From 55f0d453864382f5d925015928e5f0d2b4387a3e Mon Sep 17 00:00:00 2001 From: Kamil Marczak Date: Tue, 6 Oct 2026 12:12:10 +0200 Subject: [PATCH 01/46] feat(port): add macOS and Linux port scaffolding on wxWidgets Adds the CMake build, the .rc to C++ resource compiler, portable builds of the bundled libraries, an MFC/Win32 compatibility layer on wxWidgets (headers and first part of the implementation) and a partial OpenSSL 3 replacement for SECUDE. Application sources get portable includes and qualified message map handlers. The app does not link yet; see port/PORTING_STATUS.md. --- .gitignore | 6 + CMakeLists.txt | 35 + ChallengeResponse/ChallengeResponseDlg.cpp | 38 +- ChallengeResponse/Scenario.cpp | 2 +- CrypTool/3DVisOpt.cpp | 14 +- CrypTool/ASN1Decoder.h | 22 +- CrypTool/AlphabetTextEdit.cpp | 8 +- CrypTool/AppDocument.cpp | 4 +- CrypTool/Ber_Ell_Kurve.h | 10 +- CrypTool/BloemerMayAttack.h | 2 +- CrypTool/BruteForceHeap.cpp | 2 +- CrypTool/COMFLASH.CPP | 2 +- CrypTool/COMFLASH.H | 2 +- CrypTool/CoreCryptography.cpp | 10 +- CrypTool/CrypToolApp.cpp | 134 +- CrypTool/CrypToolApp.h | 2 +- CrypTool/CrypToolView.cpp | 12 +- CrypTool/CryptDoc.cpp | 290 +- CrypTool/CryptDoc.h | 4 +- CrypTool/Cryptography.cpp | 2 +- CrypTool/Cryptography.h | 2 +- CrypTool/DESXL.cpp | 2 +- CrypTool/DialogLaengeBin.cpp | 4 +- CrypTool/DiffieHellmanLogFile.cpp | 2 +- CrypTool/DiffieHellmanParty.h | 26 +- CrypTool/DlgADFGVX.cpp | 100 +- CrypTool/DlgADFGVX.h | 12 +- CrypTool/DlgAbout.cpp | 6 +- CrypTool/DlgAdfgvxIntro.cpp | 4 +- CrypTool/DlgAdfgvxManual.cpp | 96 +- CrypTool/DlgAdfgvxManual.h | 10 +- CrypTool/DlgAdfgvxStringBox.cpp | 4 +- CrypTool/DlgAdfgvxStringBox.h | 4 +- CrypTool/DlgAutoSubstitutionAnalysis.cpp | 8 +- CrypTool/DlgAutoSubstitutionAnalysis.h | 30 +- ...utoSubstitutionAnalysisAlgorithmChoice.cpp | 8 +- ...gAutoSubstitutionAnalysisAlgorithmChoice.h | 2 +- CrypTool/DlgBruteForceAES.cpp | 10 +- CrypTool/DlgCaesarAnalysis.cpp | 2 +- CrypTool/DlgCertificateGeneration.cpp | 8 +- CrypTool/DlgCrtAstronomy.cpp | 66 +- CrypTool/DlgCrtSecretSharing.cpp | 36 +- CrypTool/DlgCrtSecretSharingOptions.cpp | 6 +- CrypTool/DlgCrtSecretSharing_Facts.cpp | 6 +- CrypTool/DlgCrtSecretSharing_Info.cpp | 2 +- CrypTool/DlgCrtSecretSharing_Log.cpp | 6 +- CrypTool/DlgCrtTransformation.cpp | 40 +- CrypTool/DlgDemoRSAKeyGeneration.cpp | 10 +- CrypTool/DlgDiffieHellmanCreateSharedKey.cpp | 2 +- .../DlgDiffieHellmanExchangeSharedKeys.cpp | 2 +- CrypTool/DlgDiffieHellmanGenerateFinalKey.cpp | 2 +- CrypTool/DlgDiffieHellmanIntro.cpp | 2 +- CrypTool/DlgDiffieHellmanKeyInformation.cpp | 2 +- CrypTool/DlgDiffieHellmanPublicParameters.cpp | 6 +- CrypTool/DlgDiffieHellmanSecretInput.cpp | 8 +- .../DlgDiffieHellmanSetPublicParameters.cpp | 2 +- CrypTool/DlgDiffieHellmanSetSecrets.cpp | 2 +- CrypTool/DlgDiffieHellmanVisualization.cpp | 30 +- CrypTool/DlgECCDecReceiver.cpp | 8 +- CrypTool/DlgECCEncReceiver.cpp | 10 +- CrypTool/DlgECGeneration.cpp | 8 +- CrypTool/DlgECSignatureStepByStep.cpp | 2 +- CrypTool/DlgExtractSignature.cpp | 8 +- CrypTool/DlgFactorisationDemo.cpp | 12 +- CrypTool/DlgFactorisationList.cpp | 2 +- CrypTool/DlgFileProperties.cpp | 4 +- CrypTool/DlgFindAndReplace.cpp | 12 +- CrypTool/DlgFindText.cpp | 2 +- CrypTool/DlgFormatTextDocument.cpp | 12 +- CrypTool/DlgHMAC.cpp | 8 +- CrypTool/DlgHashDemo.cpp | 10 +- CrypTool/DlgHillAnalysis.cpp | 4 +- CrypTool/DlgHillOptions.cpp | 10 +- CrypTool/DlgHomophonicSubstitution.cpp | 22 +- CrypTool/DlgHybridDecryptionDemo.cpp | 4 +- CrypTool/DlgHybridEncryptionDemo.cpp | 22 +- CrypTool/DlgICGRandomParamater.cpp | 10 +- CrypTool/DlgKey.cpp | 10 +- CrypTool/DlgKeyAsym.cpp | 24 +- CrypTool/DlgKeyAsymGeneration.cpp | 20 +- CrypTool/DlgKeyFromPassword.cpp | 14 +- CrypTool/DlgKeyHex.cpp | 8 +- CrypTool/DlgKeyHexFixedLen.cpp | 8 +- CrypTool/DlgKeyHill10x10.cpp | 644 ++--- CrypTool/DlgKeyHill5x5.cpp | 186 +- CrypTool/DlgKeyHomophone.cpp | 28 +- CrypTool/DlgKeyPermutation.cpp | 18 +- CrypTool/DlgKeyPermutationInfo.cpp | 4 +- CrypTool/DlgKeyPlayfair.cpp | 26 +- CrypTool/DlgKeyPlayfair.h | 2 +- CrypTool/DlgKeySubstitution.cpp | 8 +- CrypTool/DlgKeyVigenere.cpp | 2 +- CrypTool/DlgLengthOfANumber.cpp | 10 +- CrypTool/DlgMac.cpp | 32 +- CrypTool/DlgManualSubstAnalysis.cpp | 62 +- CrypTool/DlgMonSubst.cpp | 20 +- CrypTool/DlgNGramAnalysis.cpp | 16 +- CrypTool/DlgNGramAnalysis.h | 2 +- CrypTool/DlgOptionsAnalysis.cpp | 8 +- .../DlgOptionsAutoSubstitutionAnalysis.cpp | 2 +- CrypTool/DlgOptionsRSADemo.cpp | 22 +- CrypTool/DlgOptionsSignatureAttack.cpp | 24 +- CrypTool/DlgOptionsSubstitutionAnalysis.cpp | 18 +- CrypTool/DlgPINAccess.cpp | 2 +- CrypTool/DlgPINAccess.h | 2 +- CrypTool/DlgPasswordEntropy.cpp | 18 +- CrypTool/DlgPasswordGuidelines.cpp | 6 +- CrypTool/DlgPasswordQualityMeter.cpp | 6 +- CrypTool/DlgPeriodicityAnalysis.cpp | 2 +- CrypTool/DlgPlayfairAnalysis.cpp | 16 +- CrypTool/DlgPrimeTest.cpp | 18 +- CrypTool/DlgPrimesGeneratorDemo.cpp | 24 +- CrypTool/DlgProgressFactorisation.cpp | 10 +- CrypTool/DlgRSADecryption.cpp | 8 +- CrypTool/DlgRSADemo.cpp | 32 +- CrypTool/DlgRSAEncryption.cpp | 8 +- CrypTool/DlgRandomGenerator.cpp | 14 +- CrypTool/DlgRot13Caesar.cpp | 26 +- CrypTool/DlgSecretSharingIntro.cpp | 6 +- CrypTool/DlgSecretSharingOptions.cpp | 2 +- CrypTool/DlgSecretSharingParameter.cpp | 10 +- CrypTool/DlgSecretSharingReveal.cpp | 2 +- CrypTool/DlgSecretSharingSetup.cpp | 24 +- CrypTool/DlgSelectHashFunction.cpp | 16 +- CrypTool/DlgShowHash.cpp | 2 +- CrypTool/DlgShowKey.cpp | 4 +- CrypTool/DlgShowKey.h | 2 +- CrypTool/DlgShowKeyHill10x10.cpp | 10 +- CrypTool/DlgShowKeyHill5x5.cpp | 10 +- CrypTool/DlgShowKeyParameter.cpp | 6 +- CrypTool/DlgShowParameterKeyEC.cpp | 8 +- CrypTool/DlgShowPrivateKeyEC.cpp | 6 +- CrypTool/DlgShowPublicKeyEC.cpp | 6 +- CrypTool/DlgShowPublicKeyEC.h | 2 +- .../DlgSideChannelAttackVisualizationHE.cpp | 28 +- .../DlgSideChannelAttackVisualizationHE.h | 6 +- ...ackVisualizationHEPreparationsRequest1.cpp | 8 +- ...ackVisualizationHEPreparationsRequest2.cpp | 10 +- ...ackVisualizationHEPreparationsRequest3.cpp | 10 +- ...gSideChannelAttackVisualizationHETrudy.cpp | 4 +- CrypTool/DlgSigAttModificDemo.cpp | 14 +- CrypTool/DlgSignature.cpp | 26 +- CrypTool/DlgSignature.h | 2 +- CrypTool/DlgSignatureAttack.cpp | 16 +- CrypTool/DlgSignatureDemo.cpp | 28 +- CrypTool/DlgSimpleTranspositions.cpp | 6 +- CrypTool/DlgSolitaire.cpp | 154 +- CrypTool/DlgSolitaireAnalyse.cpp | 130 +- CrypTool/DlgStatRandTests.cpp | 26 +- CrypTool/DlgStatisticsSignatureAttack.cpp | 2 +- CrypTool/DlgTextOptions.cpp | 30 +- CrypTool/DlgVerifyECSignatureStepByStep.cpp | 10 +- CrypTool/DlgVerifySignature.cpp | 28 +- CrypTool/DlgVerifySignature.h | 2 +- CrypTool/DlgVigenereAnalysisSchroedel.cpp | 10 +- CrypTool/ELLIPTIC.CPP | 2 +- CrypTool/FLASH.CPP | 4 +- CrypTool/FLASH.H | 2 + CrypTool/FPOLY.CPP | 2 +- CrypTool/FPOLY.H | 2 +- CrypTool/HashingOperations.h | 2 +- CrypTool/HexEdit.cpp | 4 +- CrypTool/HexEditBase.cpp | 20 +- CrypTool/HexEditCtrlDoc.cpp | 2 +- CrypTool/HexEditCtrlView.cpp | 36 +- CrypTool/HillAnalysis.cpp | 2 +- CrypTool/IntegerArithmetic.cpp | 2 +- CrypTool/IntegerArithmetic.h | 2 +- CrypTool/KeyRepository.cpp | 2 +- CrypTool/ListResults.cpp | 6 +- CrypTool/MONTY.CPP | 2 +- CrypTool/MONTY.H | 4 +- CrypTool/MakeZip.cpp | 6 +- CrypTool/NumberEdit.cpp | 8 +- CrypTool/OpenGLDoc.cpp | 2 +- CrypTool/OpenGLDoc.h | 2 +- CrypTool/OpenGLView.cpp | 6 +- CrypTool/OpenGLView.h | 2 +- CrypTool/POLY.CPP | 2 +- CrypTool/POLY.H | 2 +- CrypTool/POLYMOD.CPP | 2 +- CrypTool/POLYMOD.H | 8 +- CrypTool/PSEDemo.cpp | 4 +- CrypTool/PictureEx.h | 18 + CrypTool/PictureExLoop.h | 18 + CrypTool/PlayfairEdit.cpp | 2 +- CrypTool/PlotDocument.cpp | 12 +- CrypTool/PlotView.cpp | 2 +- CrypTool/RSABloemerMayDlg.cpp | 34 +- CrypTool/RSAFactorHintDlg.cpp | 46 +- CrypTool/ScintillaView.cpp | 110 +- CrypTool/ScintillaView.h | 2 +- CrypTool/SecudeLib.cpp | 10 +- CrypTool/SideChannelAttack.h | 2 +- CrypTool/SignatureAttack.h | 2 +- CrypTool/SizingDialog.cpp | 2 +- CrypTool/Solitaire.h | 3 +- CrypTool/SymEncBase.cpp | 2 +- CrypTool/adfgvx.cpp | 2 +- CrypTool/adfgvx.h | 20 +- CrypTool/base64.h | 4 + CrypTool/rsastereotypedmsgdlg.cpp | 46 +- CrypTool/stdafx.h | 4 +- CrypTool/uucode.h | 4 + NumberShark/ColoredListCtrl.cpp | 2 +- NumberShark/DlgShowPrecalculatedScores.cpp | 2 +- NumberShark/GameRules.cpp | 2 +- NumberShark/GameSummary.cpp | 6 +- NumberShark/MFC-ZahlenHaiDlg.cpp | 36 +- NumberShark/ZahlenHaiOptionen.cpp | 16 +- NumberShark/propPage1.cpp | 2 +- README.md | 2 + aestool/HexEdit1.cpp | 4 +- aestool/aestoolDlg.cpp | 22 +- cracklib-2.7/cracklib_Win32/fascist.c | 1 + cracklib-2.7/cracklib_Win32/packer.h | 24 +- cracklib-2.7/cracklib_Win32/packlib.c | 81 +- cracklib-2.7/cracklib_Win32/rules.c | 10 +- libNTL/include/NTL/mach_desc.h | 3 + libVolRen/EditorDialog.cpp | 2 +- libVolRen/StdAfx.cpp | 2 +- libVolRen/Trapezeditor.cpp | 2 +- libVolRen/VolRen.cpp | 2 +- libVolRen/VolumeRenderer.cpp | 2 +- libVolRen/VolumeUtilities.cpp | 2 +- libanalyse/Converter.cpp | 5 + libanalyse/Converter.h | 4 +- libanalyse/Default.h | 2 +- libanalyse/Format2.h | 4 +- libanalyse/MYMATH.H | 4 +- libanalyse/REGEX.H | 2 +- libanalyse/String.cpp | 12 +- libanalyse/SymbolArray.h | 2 +- libanalyse/la_ostream.h | 5 + libanalyse/la_string.h | 19 +- libanalyse/mytemplates.h | 8 +- libec/include/ECsecude.h | 6 +- libec/sources/ECsecude.c | 11 +- libmiracl/include/BIG.H | 12 +- libmiracl/include/MIRACL.H | 2 +- libmiracl/source/BIG.CPP | 4 +- libmiracl/source/MRFLASH.C | 6 +- libmiracl/source/MRFRND.C | 2 +- libmiracl/source/MRROUND.C | 2 +- port/PORTING_STATUS.md | 49 + port/app/CMakeLists.txt | 33 + port/mfcwx/CMakeLists.txt | 13 + port/mfcwx/include/afx.h | 602 ++++ port/mfcwx/include/afxadv.h | 2 + port/mfcwx/include/afxcmn.h | 319 +++ port/mfcwx/include/afxcoll.h | 2 + port/mfcwx/include/afxconv.h | 2 + port/mfcwx/include/afxcview.h | 2 + port/mfcwx/include/afxdb.h | 2 + port/mfcwx/include/afxdhtml.h | 2 + port/mfcwx/include/afxdisp.h | 3 + port/mfcwx/include/afxdlgs.h | 192 ++ port/mfcwx/include/afxdtctl.h | 3 + port/mfcwx/include/afxext.h | 41 + port/mfcwx/include/afxinet.h | 2 + port/mfcwx/include/afxmt.h | 81 + port/mfcwx/include/afxodlgs.h | 2 + port/mfcwx/include/afxole.h | 57 + port/mfcwx/include/afxpriv.h | 2 + port/mfcwx/include/afxres.h | 281 ++ port/mfcwx/include/afxrich.h | 42 + port/mfcwx/include/afxsock.h | 2 + port/mfcwx/include/afxtempl.h | 3 + port/mfcwx/include/afxwin.h | 1258 +++++++++ port/mfcwx/include/atlbase.h | 2 + port/mfcwx/include/basetsd.h | 6 + port/mfcwx/include/commctrl.h | 6 + port/mfcwx/include/commdlg.h | 6 + port/mfcwx/include/conio.h | 2 + port/mfcwx/include/crtdbg.h | 2 + port/mfcwx/include/direct.h | 2 + port/mfcwx/include/dos.h | 2 + port/mfcwx/include/gl/gl.h | 9 + port/mfcwx/include/gl/glu.h | 9 + port/mfcwx/include/htmlhelp.h | 6 + port/mfcwx/include/io.h | 2 + port/mfcwx/include/mbstring.h | 2 + port/mfcwx/include/mfcwx/codepage.h | 24 + port/mfcwx/include/mfcwx/collections.h | 451 +++ port/mfcwx/include/mfcwx/crtcompat.h | 387 +++ port/mfcwx/include/mfcwx/cstring.h | 164 ++ port/mfcwx/include/mfcwx/docview.h | 460 ++++ port/mfcwx/include/mfcwx/extras.h | 143 + port/mfcwx/include/mfcwx/fstreamcompat.h | 62 + port/mfcwx/include/mfcwx/geometry.h | 133 + port/mfcwx/include/mfcwx/msgmap.h | 292 ++ port/mfcwx/include/mfcwx/rcdata.h | 130 + port/mfcwx/include/mfcwx/regkey.h | 44 + port/mfcwx/include/mfcwx/win32.h | 325 +++ port/mfcwx/include/mfcwx/winapi_gui.h | 283 ++ port/mfcwx/include/mfcwx/winconst.h | 1935 +++++++++++++ port/mfcwx/include/mfcwx/wintypes.h | 643 +++++ port/mfcwx/include/mmsystem.h | 6 + port/mfcwx/include/objbase.h | 6 + port/mfcwx/include/ole2.h | 6 + port/mfcwx/include/process.h | 2 + port/mfcwx/include/richedit.h | 6 + port/mfcwx/include/share.h | 2 + port/mfcwx/include/shellapi.h | 6 + port/mfcwx/include/shlobj.h | 6 + port/mfcwx/include/shlwapi.h | 6 + port/mfcwx/include/tchar.h | 2 + port/mfcwx/include/winbase.h | 6 + port/mfcwx/include/wincrypt.h | 6 + port/mfcwx/include/windef.h | 6 + port/mfcwx/include/windows.h | 6 + port/mfcwx/include/wingdi.h | 6 + port/mfcwx/include/winnt.h | 6 + port/mfcwx/include/winreg.h | 6 + port/mfcwx/include/winsock.h | 6 + port/mfcwx/include/winsvc.h | 6 + port/mfcwx/include/winuser.h | 6 + port/mfcwx/src/bridge.h | 16 + port/mfcwx/src/cmdtarget.cpp | 154 ++ port/mfcwx/src/codepage.cpp | 141 + port/mfcwx/src/codepage_tables.cpp | 120 + port/mfcwx/src/controls.cpp | 2429 +++++++++++++++++ port/mfcwx/src/cstring.cpp | 701 +++++ port/mfcwx/src/globalmem.h | 40 + port/mfcwx/src/internal.h | 198 ++ port/mfcwx/src/kernel.cpp | 1131 ++++++++ port/mfcwx/src/resources.cpp | 335 +++ port/mfcwx/src/runtime.h | 67 + port/mfcwx/src/windows_impl.h | 95 + port/mfcwx/src/wnd.cpp | 1813 ++++++++++++ port/rc/rc2cpp.cmake | 126 + port/rc/rc2cpp.py | 2008 ++++++++++++++ port/rc/test/CMakeLists.txt | 25 + port/rc/test/aestool_volren_checks.cpp | 36 + port/rc/test/cryptool_checks.cpp | 169 ++ port/rc/test/numbershark_checks.cpp | 48 + port/rc/test/rc_test.cpp | 186 ++ port/rc/test/rc_test.h | 46 + port/secude/CMakeLists.txt | 106 + port/secude/README.md | 156 ++ port/secude/include/secude_compat.h | 52 + port/secude/src/alloc.cpp | 268 ++ port/secude/src/asym.cpp | 409 +++ port/secude/src/bn.hpp | 65 + port/secude/src/cadb.cpp | 171 ++ port/secude/src/cert.cpp | 581 ++++ port/secude/src/crypt_api.cpp | 307 +++ port/secude/src/data_flags.cpp | 18 + port/secude/src/der.cpp | 353 +++ port/secude/src/der.hpp | 94 + port/secude/src/dname.cpp | 333 +++ port/secude/src/dump.cpp | 298 ++ port/secude/src/encode.cpp | 861 ++++++ port/secude/src/error.cpp | 192 ++ port/secude/src/hash.cpp | 157 ++ port/secude/src/internal.hpp | 313 +++ port/secude/src/keystore.cpp | 266 ++ port/secude/src/legacy.hpp | 36 + port/secude/src/legacy_cipher.cpp | 395 +++ port/secude/src/legacy_hash.cpp | 536 ++++ port/secude/src/lnumber.cpp | 266 ++ port/secude/src/misc.cpp | 169 ++ port/secude/src/oids.cpp | 491 ++++ port/secude/src/pkcs12.cpp | 498 ++++ port/secude/src/provider.cpp | 65 + port/secude/src/pse.cpp | 743 +++++ port/secude/src/structs.cpp | 652 +++++ port/secude/src/symbols.cpp | 241 ++ port/secude/src/symcipher.cpp | 244 ++ port/secude/src/symcipher.hpp | 19 + port/secude/tests/main.cpp | 75 + port/secude/tests/test_asym.cpp | 183 ++ port/secude/tests/test_binding.cpp | 69 + port/secude/tests/test_cipher.cpp | 229 ++ port/secude/tests/test_encode.cpp | 145 + port/secude/tests/test_hash.cpp | 177 ++ port/secude/tests/test_libec_binding.c | 29 + port/secude/tests/test_lnumber.cpp | 182 ++ port/secude/tests/test_pkcs12.cpp | 177 ++ port/secude/tests/test_pse.cpp | 511 ++++ port/secude/tests/testing.hpp | 54 + port/secude/tools/mkpse.cpp | 35 + port/thirdparty/CMakeLists.txt | 55 + .../aes_candidates/ct_aes_prelude.h | 30 + port/thirdparty/aes_candidates/mars.c | 8 + port/thirdparty/aes_candidates/rc6.c | 8 + .../aes_candidates/rijndael_sizes.c | 5 + port/thirdparty/aes_candidates/serpent.c | 27 + port/thirdparty/aes_candidates/twofish.c | 5 + port/thirdparty/aes_candidates/wrapper.h.in | 13 + port/thirdparty/apfloat_compat/include/ap.h | 18 + .../thirdparty/apfloat_compat/include/apint.h | 147 + port/thirdparty/apfloat_compat/src/apint.cpp | 277 ++ port/thirdparty/cmake/aes.cmake | 102 + port/thirdparty/cmake/analyse.cmake | 43 + port/thirdparty/cmake/apfloat.cmake | 31 + port/thirdparty/cmake/cracklib.cmake | 28 + port/thirdparty/cmake/cvact.cmake | 34 + port/thirdparty/cmake/ec.cmake | 42 + port/thirdparty/cmake/gmp.cmake | 69 + port/thirdparty/cmake/helpers.cmake | 105 + port/thirdparty/cmake/miracl.cmake | 51 + port/thirdparty/cmake/ntl.cmake | 98 + port/thirdparty/cmake/openssl.cmake | 57 + port/thirdparty/compat_include/fstream.h | 8 + port/thirdparty/compat_include/iomanip.h | 8 + port/thirdparty/compat_include/iostream.h | 9 + port/thirdparty/compat_include/new.h | 8 + port/thirdparty/compat_include/strstrea.h | 8 + port/thirdparty/compat_include/strstream.h | 8 + .../cvact_compat/include/actAlgorithm.h | 57 + .../cvact_compat/include/actBasics.h | 36 + .../thirdparty/cvact_compat/include/actBlob.h | 145 + .../thirdparty/cvact_compat/include/actDate.h | 94 + .../cvact_compat/include/actDefaultRNG.h | 14 + .../cvact_compat/include/actException.h | 132 + .../cvact_compat/include/actIAlgorithm.h | 26 + .../thirdparty/cvact_compat/include/actIKey.h | 33 + .../cvact_compat/include/actIRNGAlg.h | 26 + .../thirdparty/cvact_compat/include/actInit.h | 18 + port/thirdparty/cvact_compat/include/actKey.h | 53 + .../thirdparty/cvact_compat/include/actMode.h | 213 ++ .../cvact_compat/include/actRNGKit.h | 15 + .../cvact_compat/include/actState.h | 16 + .../cvact_compat/include/actTools.h | 40 + .../thirdparty/cvact_compat/src/Algorithm.cpp | 232 ++ port/thirdparty/cvact_compat/src/Blob.cpp | 279 ++ .../cvact_compat/src/BlockCipherKey.cpp | 408 +++ port/thirdparty/cvact_compat/src/Date.cpp | 311 +++ port/thirdparty/cvact_compat/src/IESKey.cpp | 846 ++++++ port/thirdparty/cvact_compat/src/Key.cpp | 97 + port/thirdparty/cvact_compat/src/Tools.cpp | 415 +++ .../thirdparty/cvact_compat/src/actInternal.h | 85 + port/thirdparty/miracl/mirdef.h | 48 + port/thirdparty/ntl/MakeDesc.c | 1105 ++++++++ port/thirdparty/ntl/MakeDescAux.c | 23 + .../include/OpenSSL/ct_ossl_base.h | 24 + .../openssl_compat/include/OpenSSL/md2.h | 40 + .../openssl_compat/include/OpenSSL/md4.h | 35 + .../openssl_compat/include/OpenSSL/md5.h | 35 + .../openssl_compat/include/OpenSSL/ripemd.h | 35 + .../openssl_compat/include/OpenSSL/sha.h | 100 + .../openssl_compat/include_bare/crypto.h | 33 + port/thirdparty/openssl_compat/src/ct_md2.c | 108 + port/thirdparty/openssl_compat/src/ct_sha0.c | 131 + port/thirdparty/tests/test_aes_candidates.cpp | 242 ++ .../tests/test_aes_candidates_ref.inc | 135 + port/thirdparty/tests/test_analyse.cpp | 88 + port/thirdparty/tests/test_apfloat.cpp | 129 + port/thirdparty/tests/test_cracklib.cpp | 115 + port/thirdparty/tests/test_cracklib_layout.c | 20 + port/thirdparty/tests/test_cvact.cpp | 435 +++ port/thirdparty/tests/test_ec.cpp | 87 + port/thirdparty/tests/test_gmp.cpp | 102 + port/thirdparty/tests/test_miracl.cpp | 123 + port/thirdparty/tests/test_ntl.cpp | 171 ++ port/thirdparty/tests/test_openssl_compat.cpp | 178 ++ .../tests/test_openssl_compat_layout.c | 24 + port/tools/ccheck.py | 29 + port/tools/fix_includes.py | 94 + port/tools/fix_msgmaps.py | 56 + secude/secude/af.h | 5 + secude/secude/platform/platlinu.h | 5 + secude/secude/platform/platmosx.h | 6 + secude/secude/sec_conf.h | 4 + secude/secude/sec_glob.h | 5 +- 466 files changed, 40631 insertions(+), 2013 deletions(-) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 port/PORTING_STATUS.md create mode 100644 port/app/CMakeLists.txt create mode 100644 port/mfcwx/CMakeLists.txt create mode 100644 port/mfcwx/include/afx.h create mode 100644 port/mfcwx/include/afxadv.h create mode 100644 port/mfcwx/include/afxcmn.h create mode 100644 port/mfcwx/include/afxcoll.h create mode 100644 port/mfcwx/include/afxconv.h create mode 100644 port/mfcwx/include/afxcview.h create mode 100644 port/mfcwx/include/afxdb.h create mode 100644 port/mfcwx/include/afxdhtml.h create mode 100644 port/mfcwx/include/afxdisp.h create mode 100644 port/mfcwx/include/afxdlgs.h create mode 100644 port/mfcwx/include/afxdtctl.h create mode 100644 port/mfcwx/include/afxext.h create mode 100644 port/mfcwx/include/afxinet.h create mode 100644 port/mfcwx/include/afxmt.h create mode 100644 port/mfcwx/include/afxodlgs.h create mode 100644 port/mfcwx/include/afxole.h create mode 100644 port/mfcwx/include/afxpriv.h create mode 100644 port/mfcwx/include/afxres.h create mode 100644 port/mfcwx/include/afxrich.h create mode 100644 port/mfcwx/include/afxsock.h create mode 100644 port/mfcwx/include/afxtempl.h create mode 100644 port/mfcwx/include/afxwin.h create mode 100644 port/mfcwx/include/atlbase.h create mode 100644 port/mfcwx/include/basetsd.h create mode 100644 port/mfcwx/include/commctrl.h create mode 100644 port/mfcwx/include/commdlg.h create mode 100644 port/mfcwx/include/conio.h create mode 100644 port/mfcwx/include/crtdbg.h create mode 100644 port/mfcwx/include/direct.h create mode 100644 port/mfcwx/include/dos.h create mode 100644 port/mfcwx/include/gl/gl.h create mode 100644 port/mfcwx/include/gl/glu.h create mode 100644 port/mfcwx/include/htmlhelp.h create mode 100644 port/mfcwx/include/io.h create mode 100644 port/mfcwx/include/mbstring.h create mode 100644 port/mfcwx/include/mfcwx/codepage.h create mode 100644 port/mfcwx/include/mfcwx/collections.h create mode 100644 port/mfcwx/include/mfcwx/crtcompat.h create mode 100644 port/mfcwx/include/mfcwx/cstring.h create mode 100644 port/mfcwx/include/mfcwx/docview.h create mode 100644 port/mfcwx/include/mfcwx/extras.h create mode 100644 port/mfcwx/include/mfcwx/fstreamcompat.h create mode 100644 port/mfcwx/include/mfcwx/geometry.h create mode 100644 port/mfcwx/include/mfcwx/msgmap.h create mode 100644 port/mfcwx/include/mfcwx/rcdata.h create mode 100644 port/mfcwx/include/mfcwx/regkey.h create mode 100644 port/mfcwx/include/mfcwx/win32.h create mode 100644 port/mfcwx/include/mfcwx/winapi_gui.h create mode 100644 port/mfcwx/include/mfcwx/winconst.h create mode 100644 port/mfcwx/include/mfcwx/wintypes.h create mode 100644 port/mfcwx/include/mmsystem.h create mode 100644 port/mfcwx/include/objbase.h create mode 100644 port/mfcwx/include/ole2.h create mode 100644 port/mfcwx/include/process.h create mode 100644 port/mfcwx/include/richedit.h create mode 100644 port/mfcwx/include/share.h create mode 100644 port/mfcwx/include/shellapi.h create mode 100644 port/mfcwx/include/shlobj.h create mode 100644 port/mfcwx/include/shlwapi.h create mode 100644 port/mfcwx/include/tchar.h create mode 100644 port/mfcwx/include/winbase.h create mode 100644 port/mfcwx/include/wincrypt.h create mode 100644 port/mfcwx/include/windef.h create mode 100644 port/mfcwx/include/windows.h create mode 100644 port/mfcwx/include/wingdi.h create mode 100644 port/mfcwx/include/winnt.h create mode 100644 port/mfcwx/include/winreg.h create mode 100644 port/mfcwx/include/winsock.h create mode 100644 port/mfcwx/include/winsvc.h create mode 100644 port/mfcwx/include/winuser.h create mode 100644 port/mfcwx/src/bridge.h create mode 100644 port/mfcwx/src/cmdtarget.cpp create mode 100644 port/mfcwx/src/codepage.cpp create mode 100644 port/mfcwx/src/codepage_tables.cpp create mode 100644 port/mfcwx/src/controls.cpp create mode 100644 port/mfcwx/src/cstring.cpp create mode 100644 port/mfcwx/src/globalmem.h create mode 100644 port/mfcwx/src/internal.h create mode 100644 port/mfcwx/src/kernel.cpp create mode 100644 port/mfcwx/src/resources.cpp create mode 100644 port/mfcwx/src/runtime.h create mode 100644 port/mfcwx/src/windows_impl.h create mode 100644 port/mfcwx/src/wnd.cpp create mode 100644 port/rc/rc2cpp.cmake create mode 100755 port/rc/rc2cpp.py create mode 100644 port/rc/test/CMakeLists.txt create mode 100644 port/rc/test/aestool_volren_checks.cpp create mode 100644 port/rc/test/cryptool_checks.cpp create mode 100644 port/rc/test/numbershark_checks.cpp create mode 100644 port/rc/test/rc_test.cpp create mode 100644 port/rc/test/rc_test.h create mode 100644 port/secude/CMakeLists.txt create mode 100644 port/secude/README.md create mode 100644 port/secude/include/secude_compat.h create mode 100644 port/secude/src/alloc.cpp create mode 100644 port/secude/src/asym.cpp create mode 100644 port/secude/src/bn.hpp create mode 100644 port/secude/src/cadb.cpp create mode 100644 port/secude/src/cert.cpp create mode 100644 port/secude/src/crypt_api.cpp create mode 100644 port/secude/src/data_flags.cpp create mode 100644 port/secude/src/der.cpp create mode 100644 port/secude/src/der.hpp create mode 100644 port/secude/src/dname.cpp create mode 100644 port/secude/src/dump.cpp create mode 100644 port/secude/src/encode.cpp create mode 100644 port/secude/src/error.cpp create mode 100644 port/secude/src/hash.cpp create mode 100644 port/secude/src/internal.hpp create mode 100644 port/secude/src/keystore.cpp create mode 100644 port/secude/src/legacy.hpp create mode 100644 port/secude/src/legacy_cipher.cpp create mode 100644 port/secude/src/legacy_hash.cpp create mode 100644 port/secude/src/lnumber.cpp create mode 100644 port/secude/src/misc.cpp create mode 100644 port/secude/src/oids.cpp create mode 100644 port/secude/src/pkcs12.cpp create mode 100644 port/secude/src/provider.cpp create mode 100644 port/secude/src/pse.cpp create mode 100644 port/secude/src/structs.cpp create mode 100644 port/secude/src/symbols.cpp create mode 100644 port/secude/src/symcipher.cpp create mode 100644 port/secude/src/symcipher.hpp create mode 100644 port/secude/tests/main.cpp create mode 100644 port/secude/tests/test_asym.cpp create mode 100644 port/secude/tests/test_binding.cpp create mode 100644 port/secude/tests/test_cipher.cpp create mode 100644 port/secude/tests/test_encode.cpp create mode 100644 port/secude/tests/test_hash.cpp create mode 100644 port/secude/tests/test_libec_binding.c create mode 100644 port/secude/tests/test_lnumber.cpp create mode 100644 port/secude/tests/test_pkcs12.cpp create mode 100644 port/secude/tests/test_pse.cpp create mode 100644 port/secude/tests/testing.hpp create mode 100644 port/secude/tools/mkpse.cpp create mode 100644 port/thirdparty/CMakeLists.txt create mode 100644 port/thirdparty/aes_candidates/ct_aes_prelude.h create mode 100644 port/thirdparty/aes_candidates/mars.c create mode 100644 port/thirdparty/aes_candidates/rc6.c create mode 100644 port/thirdparty/aes_candidates/rijndael_sizes.c create mode 100644 port/thirdparty/aes_candidates/serpent.c create mode 100644 port/thirdparty/aes_candidates/twofish.c create mode 100644 port/thirdparty/aes_candidates/wrapper.h.in create mode 100644 port/thirdparty/apfloat_compat/include/ap.h create mode 100644 port/thirdparty/apfloat_compat/include/apint.h create mode 100644 port/thirdparty/apfloat_compat/src/apint.cpp create mode 100644 port/thirdparty/cmake/aes.cmake create mode 100644 port/thirdparty/cmake/analyse.cmake create mode 100644 port/thirdparty/cmake/apfloat.cmake create mode 100644 port/thirdparty/cmake/cracklib.cmake create mode 100644 port/thirdparty/cmake/cvact.cmake create mode 100644 port/thirdparty/cmake/ec.cmake create mode 100644 port/thirdparty/cmake/gmp.cmake create mode 100644 port/thirdparty/cmake/helpers.cmake create mode 100644 port/thirdparty/cmake/miracl.cmake create mode 100644 port/thirdparty/cmake/ntl.cmake create mode 100644 port/thirdparty/cmake/openssl.cmake create mode 100644 port/thirdparty/compat_include/fstream.h create mode 100644 port/thirdparty/compat_include/iomanip.h create mode 100644 port/thirdparty/compat_include/iostream.h create mode 100644 port/thirdparty/compat_include/new.h create mode 100644 port/thirdparty/compat_include/strstrea.h create mode 100644 port/thirdparty/compat_include/strstream.h create mode 100644 port/thirdparty/cvact_compat/include/actAlgorithm.h create mode 100644 port/thirdparty/cvact_compat/include/actBasics.h create mode 100644 port/thirdparty/cvact_compat/include/actBlob.h create mode 100644 port/thirdparty/cvact_compat/include/actDate.h create mode 100644 port/thirdparty/cvact_compat/include/actDefaultRNG.h create mode 100644 port/thirdparty/cvact_compat/include/actException.h create mode 100644 port/thirdparty/cvact_compat/include/actIAlgorithm.h create mode 100644 port/thirdparty/cvact_compat/include/actIKey.h create mode 100644 port/thirdparty/cvact_compat/include/actIRNGAlg.h create mode 100644 port/thirdparty/cvact_compat/include/actInit.h create mode 100644 port/thirdparty/cvact_compat/include/actKey.h create mode 100644 port/thirdparty/cvact_compat/include/actMode.h create mode 100644 port/thirdparty/cvact_compat/include/actRNGKit.h create mode 100644 port/thirdparty/cvact_compat/include/actState.h create mode 100644 port/thirdparty/cvact_compat/include/actTools.h create mode 100644 port/thirdparty/cvact_compat/src/Algorithm.cpp create mode 100644 port/thirdparty/cvact_compat/src/Blob.cpp create mode 100644 port/thirdparty/cvact_compat/src/BlockCipherKey.cpp create mode 100644 port/thirdparty/cvact_compat/src/Date.cpp create mode 100644 port/thirdparty/cvact_compat/src/IESKey.cpp create mode 100644 port/thirdparty/cvact_compat/src/Key.cpp create mode 100644 port/thirdparty/cvact_compat/src/Tools.cpp create mode 100644 port/thirdparty/cvact_compat/src/actInternal.h create mode 100644 port/thirdparty/miracl/mirdef.h create mode 100644 port/thirdparty/ntl/MakeDesc.c create mode 100644 port/thirdparty/ntl/MakeDescAux.c create mode 100644 port/thirdparty/openssl_compat/include/OpenSSL/ct_ossl_base.h create mode 100644 port/thirdparty/openssl_compat/include/OpenSSL/md2.h create mode 100644 port/thirdparty/openssl_compat/include/OpenSSL/md4.h create mode 100644 port/thirdparty/openssl_compat/include/OpenSSL/md5.h create mode 100644 port/thirdparty/openssl_compat/include/OpenSSL/ripemd.h create mode 100644 port/thirdparty/openssl_compat/include/OpenSSL/sha.h create mode 100644 port/thirdparty/openssl_compat/include_bare/crypto.h create mode 100644 port/thirdparty/openssl_compat/src/ct_md2.c create mode 100644 port/thirdparty/openssl_compat/src/ct_sha0.c create mode 100644 port/thirdparty/tests/test_aes_candidates.cpp create mode 100644 port/thirdparty/tests/test_aes_candidates_ref.inc create mode 100644 port/thirdparty/tests/test_analyse.cpp create mode 100644 port/thirdparty/tests/test_apfloat.cpp create mode 100644 port/thirdparty/tests/test_cracklib.cpp create mode 100644 port/thirdparty/tests/test_cracklib_layout.c create mode 100644 port/thirdparty/tests/test_cvact.cpp create mode 100644 port/thirdparty/tests/test_ec.cpp create mode 100644 port/thirdparty/tests/test_gmp.cpp create mode 100644 port/thirdparty/tests/test_miracl.cpp create mode 100644 port/thirdparty/tests/test_ntl.cpp create mode 100644 port/thirdparty/tests/test_openssl_compat.cpp create mode 100644 port/thirdparty/tests/test_openssl_compat_layout.c create mode 100644 port/tools/ccheck.py create mode 100644 port/tools/fix_includes.py create mode 100644 port/tools/fix_msgmaps.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..ec0a25e31 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +build/ +build-*/ +port/secude/build*/ +port/thirdparty/build*/ +port/rc/test/build*/ +__pycache__/ diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 000000000..bd192d63e --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,35 @@ +cmake_minimum_required(VERSION 3.20) + +project(CrypTool1 VERSION 1.4.31 LANGUAGES C CXX) + +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "Build type" FORCE) +endif() + +set(REPO_ROOT ${CMAKE_CURRENT_SOURCE_DIR}) + +if(APPLE) + foreach(prefix /opt/homebrew/opt /usr/local/opt) + if(EXISTS ${prefix}/wxwidgets@3.2/bin/wx-config-3.2 AND NOT wxWidgets_CONFIG_EXECUTABLE) + set(wxWidgets_CONFIG_EXECUTABLE ${prefix}/wxwidgets@3.2/bin/wx-config-3.2) + endif() + if(EXISTS ${prefix}/openssl@3 AND NOT OPENSSL_ROOT_DIR) + set(OPENSSL_ROOT_DIR ${prefix}/openssl@3) + endif() + endforeach() +endif() + +find_package(wxWidgets 3.2 REQUIRED COMPONENTS stc aui html gl adv xml core base) +find_package(OpenSSL 3 REQUIRED) +find_package(OpenGL REQUIRED) +find_package(Threads REQUIRED) + +add_subdirectory(port/thirdparty) +add_subdirectory(port/secude) +if(TARGET secude_compat) + # The application is C++14; the library may use a newer standard internally. + set_property(TARGET secude_compat PROPERTY INTERFACE_COMPILE_FEATURES "") +endif() +add_subdirectory(port/mfcwx) +add_subdirectory(port/app) diff --git a/ChallengeResponse/ChallengeResponseDlg.cpp b/ChallengeResponse/ChallengeResponseDlg.cpp index 0dfac2d27..d511e47f9 100644 --- a/ChallengeResponse/ChallengeResponseDlg.cpp +++ b/ChallengeResponse/ChallengeResponseDlg.cpp @@ -72,30 +72,30 @@ BEGIN_MESSAGE_MAP(CChallengeResponseDlg, CDialog) ON_WM_PAINT() ON_WM_QUERYDRAGICON() ON_WM_CREATE() - ON_BN_CLICKED(IDC_EXIT_BUTTON, OnExitButton) - ON_BN_CLICKED(IDC_START_RADIO, OnModeRadioButtons) + ON_BN_CLICKED(IDC_EXIT_BUTTON, &CChallengeResponseDlg::OnExitButton) + ON_BN_CLICKED(IDC_START_RADIO, &CChallengeResponseDlg::OnModeRadioButtons) ON_WM_MOUSEMOVE() - ON_BN_CLICKED(IDC_ACTION_ATTACK_RADIO, OnChangeAction) + ON_BN_CLICKED(IDC_ACTION_ATTACK_RADIO, &CChallengeResponseDlg::OnChangeAction) ON_WM_LBUTTONDOWN() ON_WM_LBUTTONUP() - ON_BN_CLICKED(IDC_AUTHENTICATE_CLIENT_BUTTON, OnAuthenticateClientButton) + ON_BN_CLICKED(IDC_AUTHENTICATE_CLIENT_BUTTON, &CChallengeResponseDlg::OnAuthenticateClientButton) ON_WM_DESTROY() ON_WM_TIMER() - ON_BN_CLICKED(IDC_AUTHENTICATE_ATTACKER_BUTTON, OnAuthenticateAttackerButton) - ON_BN_CLICKED(IDC_CHECK_MASK, OnCheckMask) - ON_BN_CLICKED(IDC_SCENARIO_HELP_BUTTON, OnScenarioHelpButton) - ON_BN_CLICKED(IDC_RESET_BUTTON, OnResetButton) - ON_BN_CLICKED(IDC_HELP, OnHelp) - ON_BN_CLICKED(IDC_EINFUEHRUNG_RADIO, OnModeRadioButtons) - ON_BN_CLICKED(IDC_PASSWORT_RADIO, OnModeRadioButtons) - ON_BN_CLICKED(IDC_EINMALPASSWOERTER, OnModeRadioButtons) - ON_BN_CLICKED(IDC_CHALLENGERESPONSE1_RADIO, OnModeRadioButtons) - ON_BN_CLICKED(IDC_CHALLENGERESPONSE2_RADIO, OnModeRadioButtons) - ON_BN_CLICKED(IDC_WECHSELSEITIG_RADIO, OnModeRadioButtons) - ON_BN_CLICKED(IDC_ACTION_CONNECT_RADIO, OnChangeAction) - ON_BN_CLICKED(IDC_ACTION_DISCONNECT_RADIO, OnChangeAction) - ON_BN_CLICKED(IDC_ACTION_NOTHING_RADIO, OnChangeAction) - ON_BN_CLICKED(IDC_ACTION_REMOVE_ATTACK_RADIO, OnChangeAction) + ON_BN_CLICKED(IDC_AUTHENTICATE_ATTACKER_BUTTON, &CChallengeResponseDlg::OnAuthenticateAttackerButton) + ON_BN_CLICKED(IDC_CHECK_MASK, &CChallengeResponseDlg::OnCheckMask) + ON_BN_CLICKED(IDC_SCENARIO_HELP_BUTTON, &CChallengeResponseDlg::OnScenarioHelpButton) + ON_BN_CLICKED(IDC_RESET_BUTTON, &CChallengeResponseDlg::OnResetButton) + ON_BN_CLICKED(IDC_HELP, &CChallengeResponseDlg::OnHelp) + ON_BN_CLICKED(IDC_EINFUEHRUNG_RADIO, &CChallengeResponseDlg::OnModeRadioButtons) + ON_BN_CLICKED(IDC_PASSWORT_RADIO, &CChallengeResponseDlg::OnModeRadioButtons) + ON_BN_CLICKED(IDC_EINMALPASSWOERTER, &CChallengeResponseDlg::OnModeRadioButtons) + ON_BN_CLICKED(IDC_CHALLENGERESPONSE1_RADIO, &CChallengeResponseDlg::OnModeRadioButtons) + ON_BN_CLICKED(IDC_CHALLENGERESPONSE2_RADIO, &CChallengeResponseDlg::OnModeRadioButtons) + ON_BN_CLICKED(IDC_WECHSELSEITIG_RADIO, &CChallengeResponseDlg::OnModeRadioButtons) + ON_BN_CLICKED(IDC_ACTION_CONNECT_RADIO, &CChallengeResponseDlg::OnChangeAction) + ON_BN_CLICKED(IDC_ACTION_DISCONNECT_RADIO, &CChallengeResponseDlg::OnChangeAction) + ON_BN_CLICKED(IDC_ACTION_NOTHING_RADIO, &CChallengeResponseDlg::OnChangeAction) + ON_BN_CLICKED(IDC_ACTION_REMOVE_ATTACK_RADIO, &CChallengeResponseDlg::OnChangeAction) //}}AFX_MSG_MAP END_MESSAGE_MAP() diff --git a/ChallengeResponse/Scenario.cpp b/ChallengeResponse/Scenario.cpp index ed8986040..d32fe2f8e 100644 --- a/ChallengeResponse/Scenario.cpp +++ b/ChallengeResponse/Scenario.cpp @@ -25,7 +25,7 @@ #include "stdafx.h" #include "Scenario.h" #include "ScenarioObject.h" -#include "wire.h" +#include "Wire.h" #include "resource.h" #ifdef _DEBUG diff --git a/CrypTool/3DVisOpt.cpp b/CrypTool/3DVisOpt.cpp index ac33b93ee..92c0f0569 100644 --- a/CrypTool/3DVisOpt.cpp +++ b/CrypTool/3DVisOpt.cpp @@ -81,13 +81,13 @@ void C3DVisOpt::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(C3DVisOpt, CDialog) //{{AFX_MSG_MAP(C3DVisOpt) - ON_NOTIFY(NM_RELEASEDCAPTURE, IDC_SLIDER_RESOLUTION, OnReleasedcaptureSliderResolution) - ON_NOTIFY(NM_RELEASEDCAPTURE, IDC_SLIDER_DENSITY, OnReleasedcaptureSliderDensity) - ON_NOTIFY(NM_RELEASEDCAPTURE, IDC_SLIDER_SHIFT, OnReleasedcaptureSliderShift) - ON_BN_CLICKED(IDC_RADIO_WORDSIZE_1, OnRadioWordsize1) - ON_BN_CLICKED(IDC_RADIO_WORDSIZE_2, OnRadioWordsize2) - ON_BN_CLICKED(IDC_RADIO_WORDSIZE_4, OnRadioWordsize4) - ON_BN_CLICKED(IDC_BUTTON_RESET, OnButtonReset) + ON_NOTIFY(NM_RELEASEDCAPTURE, IDC_SLIDER_RESOLUTION, &C3DVisOpt::OnReleasedcaptureSliderResolution) + ON_NOTIFY(NM_RELEASEDCAPTURE, IDC_SLIDER_DENSITY, &C3DVisOpt::OnReleasedcaptureSliderDensity) + ON_NOTIFY(NM_RELEASEDCAPTURE, IDC_SLIDER_SHIFT, &C3DVisOpt::OnReleasedcaptureSliderShift) + ON_BN_CLICKED(IDC_RADIO_WORDSIZE_1, &C3DVisOpt::OnRadioWordsize1) + ON_BN_CLICKED(IDC_RADIO_WORDSIZE_2, &C3DVisOpt::OnRadioWordsize2) + ON_BN_CLICKED(IDC_RADIO_WORDSIZE_4, &C3DVisOpt::OnRadioWordsize4) + ON_BN_CLICKED(IDC_BUTTON_RESET, &C3DVisOpt::OnButtonReset) //}}AFX_MSG_MAP END_MESSAGE_MAP() diff --git a/CrypTool/ASN1Decoder.h b/CrypTool/ASN1Decoder.h index 240c326f3..1654b2ff4 100644 --- a/CrypTool/ASN1Decoder.h +++ b/CrypTool/ASN1Decoder.h @@ -38,7 +38,16 @@ using namespace std; #endif -class ASN1Error; +// Klasse zur Ausnahmebehandlung/Fehlerbehandlung +class ASN1Error +{ +private: + long ErrorCode; +public: + ASN1Error(long e){ ErrorCode = e;}; + virtual ~ASN1Error() { }; + long GetErrorCode() const { return ErrorCode; }; +}; class ASN1Decoder { @@ -63,17 +72,6 @@ class ASN1Decoder void StoreCertDump(std::string, std::string) throw (ASN1Error); }; -// Klasse zur Ausnahmebehandlung/Fehlerbehandlung -class ASN1Error -{ -private: - long ErrorCode; -public: - ASN1Error(long e){ ErrorCode = e;}; - virtual ~ASN1Error() { }; - long GetErrorCode() const { return ErrorCode; }; -}; - // Fehlermeldungen #define E_INVALID_FILENAME 1 #define E_NO_VALID_ASN1_STRUCTURE 2 diff --git a/CrypTool/AlphabetTextEdit.cpp b/CrypTool/AlphabetTextEdit.cpp index 4a136af24..f956fd336 100644 --- a/CrypTool/AlphabetTextEdit.cpp +++ b/CrypTool/AlphabetTextEdit.cpp @@ -42,10 +42,10 @@ CAlphabetTextEdit::~CAlphabetTextEdit() { BEGIN_MESSAGE_MAP(CAlphabetTextEdit, CEdit) ON_WM_CHAR() ON_WM_KEYDOWN() - ON_COMMAND(ID_EDIT_CUT, onEditCut) - ON_COMMAND(ID_EDIT_COPY, onEditCopy) - ON_COMMAND(ID_EDIT_PASTE, onEditPaste) - ON_COMMAND(ID_EDIT_SELECT_ALL, onEditSelectAll) + ON_COMMAND(ID_EDIT_CUT, &CAlphabetTextEdit::onEditCut) + ON_COMMAND(ID_EDIT_COPY, &CAlphabetTextEdit::onEditCopy) + ON_COMMAND(ID_EDIT_PASTE, &CAlphabetTextEdit::onEditPaste) + ON_COMMAND(ID_EDIT_SELECT_ALL, &CAlphabetTextEdit::onEditSelectAll) END_MESSAGE_MAP() void CAlphabetTextEdit::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) { diff --git a/CrypTool/AppDocument.cpp b/CrypTool/AppDocument.cpp index 04fe4f32f..9fd270b69 100644 --- a/CrypTool/AppDocument.cpp +++ b/CrypTool/AppDocument.cpp @@ -57,8 +57,8 @@ CAppDocument::~CAppDocument() BEGIN_MESSAGE_MAP(CAppDocument, CDocument) //{{AFX_MSG_MAP(CAppDocument) - ON_UPDATE_COMMAND_UI(ID_GOTO_VATER, OnUpdateGotoVater) - ON_UPDATE_COMMAND_UI(ID_SHOW_KEY, OnUpdateShowKey) + ON_UPDATE_COMMAND_UI(ID_GOTO_VATER, &CAppDocument::OnUpdateGotoVater) + ON_UPDATE_COMMAND_UI(ID_SHOW_KEY, &CAppDocument::OnUpdateShowKey) //}}AFX_MSG_MAP END_MESSAGE_MAP() diff --git a/CrypTool/Ber_Ell_Kurve.h b/CrypTool/Ber_Ell_Kurve.h index 198663344..6f919eb1c 100644 --- a/CrypTool/Ber_Ell_Kurve.h +++ b/CrypTool/Ber_Ell_Kurve.h @@ -25,7 +25,7 @@ #if !defined(AFX_BER_ELL_KURVE_H__DED21344_096E_11D5_8899_00062919F268__INCLUDED_) #define AFX_BER_ELL_KURVE_H__DED21344_096E_11D5_8899_00062919F268__INCLUDED_ -#include "..\LIBMIRACL\INCLUDE\BIG.H" // Hinzugefügt von der Klassenansicht +#include "big.h" // Hinzugefügt von der Klassenansicht #if _MSC_VER > 1000 #pragma once #endif // _MSC_VER > 1000 @@ -35,10 +35,10 @@ #define RAISE '^' #include -#include "elliptic.h" -#include "comflash.h" -#include "fpoly.h" -#include "poly.h" +#include "ELLIPTIC.H" +#include "COMFLASH.H" +#include "FPOLY.H" +#include "POLY.H" #include #if defined(_MSC_VER) || _MSC_VER <= 1200 diff --git a/CrypTool/BloemerMayAttack.h b/CrypTool/BloemerMayAttack.h index 88bd8178e..0fd71043b 100644 --- a/CrypTool/BloemerMayAttack.h +++ b/CrypTool/BloemerMayAttack.h @@ -95,7 +95,7 @@ class BloemerMayAttack bool FindRoots(ZZX f, vec_ZZ& r); ZZXY *polyPowers; // represent the f(xX,yX)^k polynomials mat_ZZ Lattice; // the lattice to be reduced - ZZ BloemerMayAttack::binom(int i, int j); + ZZ binom(int i, int j); static long StopLLL(const vec_ZZ& z); diff --git a/CrypTool/BruteForceHeap.cpp b/CrypTool/BruteForceHeap.cpp index c6f959edf..b2cfc403a 100644 --- a/CrypTool/BruteForceHeap.cpp +++ b/CrypTool/BruteForceHeap.cpp @@ -18,7 +18,7 @@ **************************************************************************/ -#include ".\bruteforceheap.h" +#include "BruteForceHeap.h" diff --git a/CrypTool/COMFLASH.CPP b/CrypTool/COMFLASH.CPP index 3a50262db..e5265efd7 100644 --- a/CrypTool/COMFLASH.CPP +++ b/CrypTool/COMFLASH.CPP @@ -18,7 +18,7 @@ **************************************************************************/ -#include "comflash.h" +#include "COMFLASH.H" Complex& Complex::operator=(const Complex& a) diff --git a/CrypTool/COMFLASH.H b/CrypTool/COMFLASH.H index a7ad02dcf..8828a1010 100644 --- a/CrypTool/COMFLASH.H +++ b/CrypTool/COMFLASH.H @@ -33,7 +33,7 @@ #include using namespace std; #endif -#include "flash.h" +#include "FLASH.H" class Complex { diff --git a/CrypTool/CoreCryptography.cpp b/CrypTool/CoreCryptography.cpp index 27d0d6064..c3916c28e 100644 --- a/CrypTool/CoreCryptography.cpp +++ b/CrypTool/CoreCryptography.cpp @@ -19,11 +19,11 @@ **************************************************************************/ #include "CoreCryptography.h" -#include "..\AES\mars\mars.h" -#include "..\AES\RC6\RC6.h" -#include "..\AES\Rijndael\Rijndael-api-fst.h" -#include "..\AES\Serpent\Serpent.h" -#include "..\AES\Twofish\Twofish.h" +#include "mars.h" +#include "RC6.h" +#include "Rijndael-api-fst.h" +#include "Serpent.h" +#include "Twofish.h" #include "DESXL.h" #include "resource.h" diff --git a/CrypTool/CrypToolApp.cpp b/CrypTool/CrypToolApp.cpp index 25b7f6c08..9c2614ba3 100644 --- a/CrypTool/CrypToolApp.cpp +++ b/CrypTool/CrypToolApp.cpp @@ -89,10 +89,10 @@ #include "DlgComputeMersenneNumbers.h" // #if !defined(_MSC_VER) || _MSC_VER <= 1200 -#include "RSABloemerMayDlg.h" -#include "RSAStereotypedMSGDlg.h" -#include "RSAFactorHintDlg.h" -#include ".\cryptoolapp.h" +#include "rsabloemermaydlg.h" +#include "rsastereotypedmsgdlg.h" +#include "rsafactorhintdlg.h" +#include "CrypToolApp.h" #include "CrypToolTools.h" // #endif @@ -144,61 +144,61 @@ CCrypToolApp NEAR theApp; BEGIN_MESSAGE_MAP(CCrypToolApp, CWinApp) //{{AFX_MSG_MAP(CCrypToolApp) - ON_COMMAND(ID_APP_ABOUT, OnAppAbout) - ON_COMMAND(ID_OPTIONS_ANALYSIS, OnOptionsAnalysis) - ON_COMMAND(ID_WINDOW_CLOSE_ALL, OnWindowCloseAll) - ON_COMMAND(ID_OPTTEXT, OnOpttext) - ON_UPDATE_COMMAND_UI(ID_CRYPT_KeyGen, OnUpdateNeedSecudeTicket) - ON_COMMAND(ID_SHOW_ALL_EC_KEYS, OnShowKeys) - ON_COMMAND(ID_CRYPT_KeyGen, OnKeyGen) - ON_COMMAND(ID_EINZELVERFAHREN_TUTORIAL_PRIMZAHLENGENERIEREN, OnEinzelverfahrenTutorialPrimzahlengenerieren) - ON_COMMAND(ID_EINZELVERFAHREN_TUTORIAL_RSAALGORITHMUS, OnEinzelverfahrenTutorialRsaalgorithmus) - ON_COMMAND(ID_EINZELVERFAHREN_TUTORIALFRKLEINEZAHLEN_FAKTORISIERENIFP, OnEinzelverfahrenTutorialfrkleinezahlenFaktorisieren) - ON_COMMAND(ID_EINZELVERFAHREN_SCHLUESSELGENERIEREN, OnEinzelverfahrenSchluesselgenerieren) - ON_COMMAND(ID_ZUFALL_GENERATOREN, OnGenRandomData) - ON_COMMAND(ID_HASH_OFAFILE, OnHashOfAFile) - ON_COMMAND(ID_EINZELVERFAHREN_SIGN, OnEinzelverfahrenTutorialSignaturerzeugung) - ON_COMMAND(ID_VERENTSCHLSSELN_HYBRIDVERFAHREN_HYBRIDVERSCHLSSELUNG, OnVerentschlsselnHybridverfahrenHybridverschlsselung) - ON_COMMAND(ID_OPTIONS_STARTOPTIONS, OnOptionsStartoptions) - ON_COMMAND(ID_HILFE_INDEX, OnHilfeIndex) - ON_COMMAND(ID_HILFE_STARTSEITE, OnHilfeStartseite) - ON_COMMAND(ID_HILFE_SZENARIEN, OnHilfeSzenarien) - ON_COMMAND(ID_SIGNATUR_ATTACK, OnSignaturAttack) - ON_COMMAND(ID_EINZELVERFAHREN_DIFFIEHELLMANDEMO, OnEinzelverfahrenDiffiehellmandemo) - ON_COMMAND(ID_SIGATTMODIFICDEMO, OnSigattmodificdemo) - ON_COMMAND(ID_LOAD_README, OnLoadReadme) - ON_COMMAND(ID_SCRIPT, OnScript) - ON_COMMAND(ID_HELP_PRESENTATION, OnPresentation) - ON_COMMAND(ID_EINZELVERFAHREN_SIDECHANNELATTACK_ON_HYBRIDENCRYPTION, OnEinzelverfahrenSidechannelattackOnHybridencryption) - ON_COMMAND(ID_PASSWORDQUALITYMETER, OnPasswordQualityMeter) - ON_COMMAND(ID_PASSWORDENTROPY, OnPasswordEntropy) - ON_COMMAND(ID_LENGTHOFANUMBER, OnLengthOfANumber) - ON_COMMAND(ID_CHALLENGE_RESPONSE, OnChallengeResponse) - ON_COMMAND(ID_RSA_FACTORHINT, OnRsaFactorhint) - ON_COMMAND(ID_RSA_STEREOTYPED, OnRsaStereotyped) - ON_COMMAND(ID_RSA_BLOEMERMAY, OnRsaBloemermay) - ON_COMMAND(ID_HELP_ANIMAL_CAESAR, OnAnimalCaesar) - ON_COMMAND(ID_HELP_ANIMAL_VIGENERE, OnAnimalVigenere) - ON_COMMAND(ID_HELP_ANIMAL_NIHILIST, OnAnimalNihilist) - ON_COMMAND(ID_HELP_ANIMAL_DES, OnAnimalDes) - ON_COMMAND(ID_INDIV_CRT_PLANET, OnIndivCrtPlanet) - ON_COMMAND(ID_INDIV_CRT_DEMO, OnIndivCrtDemo) - ON_COMMAND(ID_INDIV_CRT_SECRETSHARING, OnIndivCrtSecretsharing) - ON_COMMAND(ID_NUMBERSHARK, OnNumberShark) - ON_UPDATE_COMMAND_UI(ID_NUMBERSHARK, OnUpdateNumberShark) - ON_UPDATE_COMMAND_UI(ID_SHOW_ALL_EC_KEYS, OnUpdateNeedSecudeTicket) - ON_UPDATE_COMMAND_UI(ID_CRYPT_KeyGen, OnUpdateNeedSecudeTicket) - ON_UPDATE_COMMAND_UI(ID_VERENTSCHLSSELN_HYBRIDVERFAHREN_HYBRIDVERSCHLSSELUNG, OnUpdateNeedSecudeTicket) - ON_UPDATE_COMMAND_UI(ID_VERENTSCHLSSELN_HYBRIDVERFAHREN_HYBRIDENTSCHLSSELUNG, OnUpdateNeedSecudeTicket) - ON_UPDATE_COMMAND_UI(ID_HASH_OFAFILE, OnUpdateNeedSecudeTicket) - ON_UPDATE_COMMAND_UI(ID_EINZELVERFAHREN_SIGN, OnUpdateNeedSecudeTicket) - ON_UPDATE_COMMAND_UI(ID_EINZELVERFAHREN_SCHLUESSELGENERIEREN, OnUpdateNeedSecudeTicket) - ON_COMMAND(ID_INDIVIDUAL_PROCEDURES_SECRETSHARING, OnIndividualProceduresSecretsharing) - ON_COMMAND(ID_GENERATION_TADIC_NAF_KEYS, OnGenerationTAdicNAFKeys) + ON_COMMAND(ID_APP_ABOUT, &CCrypToolApp::OnAppAbout) + ON_COMMAND(ID_OPTIONS_ANALYSIS, &CCrypToolApp::OnOptionsAnalysis) + ON_COMMAND(ID_WINDOW_CLOSE_ALL, &CCrypToolApp::OnWindowCloseAll) + ON_COMMAND(ID_OPTTEXT, &CCrypToolApp::OnOpttext) + ON_UPDATE_COMMAND_UI(ID_CRYPT_KeyGen, &CCrypToolApp::OnUpdateNeedSecudeTicket) + ON_COMMAND(ID_SHOW_ALL_EC_KEYS, &CCrypToolApp::OnShowKeys) + ON_COMMAND(ID_CRYPT_KeyGen, &CCrypToolApp::OnKeyGen) + ON_COMMAND(ID_EINZELVERFAHREN_TUTORIAL_PRIMZAHLENGENERIEREN, &CCrypToolApp::OnEinzelverfahrenTutorialPrimzahlengenerieren) + ON_COMMAND(ID_EINZELVERFAHREN_TUTORIAL_RSAALGORITHMUS, &CCrypToolApp::OnEinzelverfahrenTutorialRsaalgorithmus) + ON_COMMAND(ID_EINZELVERFAHREN_TUTORIALFRKLEINEZAHLEN_FAKTORISIERENIFP, &CCrypToolApp::OnEinzelverfahrenTutorialfrkleinezahlenFaktorisieren) + ON_COMMAND(ID_EINZELVERFAHREN_SCHLUESSELGENERIEREN, &CCrypToolApp::OnEinzelverfahrenSchluesselgenerieren) + ON_COMMAND(ID_ZUFALL_GENERATOREN, &CCrypToolApp::OnGenRandomData) + ON_COMMAND(ID_HASH_OFAFILE, &CCrypToolApp::OnHashOfAFile) + ON_COMMAND(ID_EINZELVERFAHREN_SIGN, &CCrypToolApp::OnEinzelverfahrenTutorialSignaturerzeugung) + ON_COMMAND(ID_VERENTSCHLSSELN_HYBRIDVERFAHREN_HYBRIDVERSCHLSSELUNG, &CCrypToolApp::OnVerentschlsselnHybridverfahrenHybridverschlsselung) + ON_COMMAND(ID_OPTIONS_STARTOPTIONS, &CCrypToolApp::OnOptionsStartoptions) + ON_COMMAND(ID_HILFE_INDEX, &CCrypToolApp::OnHilfeIndex) + ON_COMMAND(ID_HILFE_STARTSEITE, &CCrypToolApp::OnHilfeStartseite) + ON_COMMAND(ID_HILFE_SZENARIEN, &CCrypToolApp::OnHilfeSzenarien) + ON_COMMAND(ID_SIGNATUR_ATTACK, &CCrypToolApp::OnSignaturAttack) + ON_COMMAND(ID_EINZELVERFAHREN_DIFFIEHELLMANDEMO, &CCrypToolApp::OnEinzelverfahrenDiffiehellmandemo) + ON_COMMAND(ID_SIGATTMODIFICDEMO, &CCrypToolApp::OnSigattmodificdemo) + ON_COMMAND(ID_LOAD_README, &CCrypToolApp::OnLoadReadme) + ON_COMMAND(ID_SCRIPT, &CCrypToolApp::OnScript) + ON_COMMAND(ID_HELP_PRESENTATION, &CCrypToolApp::OnPresentation) + ON_COMMAND(ID_EINZELVERFAHREN_SIDECHANNELATTACK_ON_HYBRIDENCRYPTION, &CCrypToolApp::OnEinzelverfahrenSidechannelattackOnHybridencryption) + ON_COMMAND(ID_PASSWORDQUALITYMETER, &CCrypToolApp::OnPasswordQualityMeter) + ON_COMMAND(ID_PASSWORDENTROPY, &CCrypToolApp::OnPasswordEntropy) + ON_COMMAND(ID_LENGTHOFANUMBER, &CCrypToolApp::OnLengthOfANumber) + ON_COMMAND(ID_CHALLENGE_RESPONSE, &CCrypToolApp::OnChallengeResponse) + ON_COMMAND(ID_RSA_FACTORHINT, &CCrypToolApp::OnRsaFactorhint) + ON_COMMAND(ID_RSA_STEREOTYPED, &CCrypToolApp::OnRsaStereotyped) + ON_COMMAND(ID_RSA_BLOEMERMAY, &CCrypToolApp::OnRsaBloemermay) + ON_COMMAND(ID_HELP_ANIMAL_CAESAR, &CCrypToolApp::OnAnimalCaesar) + ON_COMMAND(ID_HELP_ANIMAL_VIGENERE, &CCrypToolApp::OnAnimalVigenere) + ON_COMMAND(ID_HELP_ANIMAL_NIHILIST, &CCrypToolApp::OnAnimalNihilist) + ON_COMMAND(ID_HELP_ANIMAL_DES, &CCrypToolApp::OnAnimalDes) + ON_COMMAND(ID_INDIV_CRT_PLANET, &CCrypToolApp::OnIndivCrtPlanet) + ON_COMMAND(ID_INDIV_CRT_DEMO, &CCrypToolApp::OnIndivCrtDemo) + ON_COMMAND(ID_INDIV_CRT_SECRETSHARING, &CCrypToolApp::OnIndivCrtSecretsharing) + ON_COMMAND(ID_NUMBERSHARK, &CCrypToolApp::OnNumberShark) + ON_UPDATE_COMMAND_UI(ID_NUMBERSHARK, &CCrypToolApp::OnUpdateNumberShark) + ON_UPDATE_COMMAND_UI(ID_SHOW_ALL_EC_KEYS, &CCrypToolApp::OnUpdateNeedSecudeTicket) + ON_UPDATE_COMMAND_UI(ID_CRYPT_KeyGen, &CCrypToolApp::OnUpdateNeedSecudeTicket) + ON_UPDATE_COMMAND_UI(ID_VERENTSCHLSSELN_HYBRIDVERFAHREN_HYBRIDVERSCHLSSELUNG, &CCrypToolApp::OnUpdateNeedSecudeTicket) + ON_UPDATE_COMMAND_UI(ID_VERENTSCHLSSELN_HYBRIDVERFAHREN_HYBRIDENTSCHLSSELUNG, &CCrypToolApp::OnUpdateNeedSecudeTicket) + ON_UPDATE_COMMAND_UI(ID_HASH_OFAFILE, &CCrypToolApp::OnUpdateNeedSecudeTicket) + ON_UPDATE_COMMAND_UI(ID_EINZELVERFAHREN_SIGN, &CCrypToolApp::OnUpdateNeedSecudeTicket) + ON_UPDATE_COMMAND_UI(ID_EINZELVERFAHREN_SCHLUESSELGENERIEREN, &CCrypToolApp::OnUpdateNeedSecudeTicket) + ON_COMMAND(ID_INDIVIDUAL_PROCEDURES_SECRETSHARING, &CCrypToolApp::OnIndividualProceduresSecretsharing) + ON_COMMAND(ID_GENERATION_TADIC_NAF_KEYS, &CCrypToolApp::OnGenerationTAdicNAFKeys) //}}AFX_MSG_MAP //ON_COMMAND(ID_VERENTSCHLSSELN_HYBRIDVERFAHREN_HYBRIDVERSCHLSSELUNG, OnVerentschlsselnHybridverfahrenHybridverschlsselung) - ON_COMMAND(ID_FILE_NEW, OnFileNew) // file commands... + ON_COMMAND(ID_FILE_NEW, &CCrypToolApp::OnFileNew) // file commands... ON_COMMAND(ID_FILE_OPEN, CWinApp::OnFileOpen) ON_COMMAND(ID_FILE_PRINT_SETUP, CWinApp::OnFilePrintSetup) // BEGINN Fuer Hilfe-Funktionalitaet eingefuegt @@ -209,14 +209,14 @@ BEGIN_MESSAGE_MAP(CCrypToolApp, CWinApp) ON_COMMAND(ID_DEFAULT_HELP, CWinApp::OnHelpFinder) // ENDE - ON_COMMAND(ID_PRIMENUMBER_TEST, OnPrimenumberTest) - ON_COMMAND(ID_AES_SELFEXTRACT, OnAesSelfextract) - ON_COMMAND(ID_INDIV_POINTADDITIONONELLIPTICCURVES, OnIndivPointadditiononellipticcurves) - ON_COMMAND(ID_COMPUTE_MERSENNE_NUMBERS, OnComputeMersenneNumbers) - ON_COMMAND(ID_FLASH_AESDEMO, OnFlashAesdemo) - ON_COMMAND(ID_FLASH_RIJNDAEL_INSPECTOR, OnFlashRijndaelInspector) - ON_COMMAND(ID_FLASH_ENIGMADEMO, OnFlashEnigmademo) - ON_COMMAND(ID_INTERACTIVE_NUMBER_THEORY, OnInteractiveNumberTheory) + ON_COMMAND(ID_PRIMENUMBER_TEST, &CCrypToolApp::OnPrimenumberTest) + ON_COMMAND(ID_AES_SELFEXTRACT, &CCrypToolApp::OnAesSelfextract) + ON_COMMAND(ID_INDIV_POINTADDITIONONELLIPTICCURVES, &CCrypToolApp::OnIndivPointadditiononellipticcurves) + ON_COMMAND(ID_COMPUTE_MERSENNE_NUMBERS, &CCrypToolApp::OnComputeMersenneNumbers) + ON_COMMAND(ID_FLASH_AESDEMO, &CCrypToolApp::OnFlashAesdemo) + ON_COMMAND(ID_FLASH_RIJNDAEL_INSPECTOR, &CCrypToolApp::OnFlashRijndaelInspector) + ON_COMMAND(ID_FLASH_ENIGMADEMO, &CCrypToolApp::OnFlashEnigmademo) + ON_COMMAND(ID_INTERACTIVE_NUMBER_THEORY, &CCrypToolApp::OnInteractiveNumberTheory) ON_COMMAND(ID_PROTOKOL_SMIME, &CCrypToolApp::OnProtokolSMIME) ON_COMMAND(ID_AES_RIJNDAEL_FLOWVISUALISATION, &CCrypToolApp::OnAesRijndaelFlowvisualisation) ON_COMMAND(ID_KNOWN_ANALYSIS_SINGLE_PERMUTATION, &CCrypToolApp::OnKnownAnalysisSinglePermutation) @@ -701,8 +701,8 @@ BOOL CCrypToolApp::InitInstance() } // Secude ptr fuer EC-Lib setzen -#define DoOneFn(a,b,c,d) ECSecudeLib.##c = SecudeLib.##c; -#define DoOneData(a,b) ECSecudeLib.##b = SecudeLib.##b; +#define DoOneFn(a,b,c,d) ECSecudeLib.c = SecudeLib.c; +#define DoOneData(a,b) ECSecudeLib.b = SecudeLib.b; DoECAll #undef DoOneFn #undef DoOneData @@ -843,7 +843,7 @@ BEGIN_MESSAGE_MAP(CMainFrame, CMDIFrameWnd) ON_WM_CREATE() ON_WM_DESTROY() ON_WM_TIMER() - ON_COMMAND(ID_OPTIONS_PLOT, OnOptionsPlot) + ON_COMMAND(ID_OPTIONS_PLOT, &CMainFrame::OnOptionsPlot) //}}AFX_MSG_MAP END_MESSAGE_MAP() diff --git a/CrypTool/CrypToolApp.h b/CrypTool/CrypToolApp.h index 0a0c096c4..9ca7075c4 100644 --- a/CrypTool/CrypToolApp.h +++ b/CrypTool/CrypToolApp.h @@ -197,7 +197,7 @@ class CCrypToolApp : public CWinApp // WinHelp set public to get access from Challenge Response Demo #if !defined(_MSC_VER) || _MSC_VER <= 1200 // HTML Help for VC++ 6.0 - virtual void CCrypToolApp::WinHelp( DWORD dwData, UINT nCmd = HELP_CONTEXT); + virtual void WinHelp( DWORD dwData, UINT nCmd = HELP_CONTEXT); #else // HTML Help for VC++ .NET virtual void WinHelpInternal( DWORD_PTR dwData, UINT nCmd = HELP_CONTEXT ); // overridden to handle F1 on menus with sub menus #endif diff --git a/CrypTool/CrypToolView.cpp b/CrypTool/CrypToolView.cpp index c6205ae28..b3b6f6872 100644 --- a/CrypTool/CrypToolView.cpp +++ b/CrypTool/CrypToolView.cpp @@ -57,12 +57,12 @@ BEGIN_MESSAGE_MAP(CCrypToolView, CView) ON_WM_SETFOCUS() #if 0 ON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint) - ON_COMMAND(ID_VIEW_ENDOFLINE, OnViewEndOfLine) - ON_UPDATE_COMMAND_UI(ID_VIEW_ENDOFLINE, OnUpdateViewEndOfLine) - ON_COMMAND(ID_VIEW_LINEWRAP, OnViewLineWrap) - ON_UPDATE_COMMAND_UI(ID_VIEW_LINEWRAP, OnUpdateViewLineWrap) - ON_COMMAND(ID_VIEW_WHITESPACE, OnViewWhitespace) - ON_UPDATE_COMMAND_UI(ID_VIEW_WHITESPACE, OnUpdateViewWhitespace) + ON_COMMAND(ID_VIEW_ENDOFLINE, &CCrypToolView::OnViewEndOfLine) + ON_UPDATE_COMMAND_UI(ID_VIEW_ENDOFLINE, &CCrypToolView::OnUpdateViewEndOfLine) + ON_COMMAND(ID_VIEW_LINEWRAP, &CCrypToolView::OnViewLineWrap) + ON_UPDATE_COMMAND_UI(ID_VIEW_LINEWRAP, &CCrypToolView::OnUpdateViewLineWrap) + ON_COMMAND(ID_VIEW_WHITESPACE, &CCrypToolView::OnViewWhitespace) + ON_UPDATE_COMMAND_UI(ID_VIEW_WHITESPACE, &CCrypToolView::OnUpdateViewWhitespace) #endif //}}AFX_MSG_MAP END_MESSAGE_MAP() diff --git a/CrypTool/CryptDoc.cpp b/CrypTool/CryptDoc.cpp index 58c4f513b..4effc7d50 100644 --- a/CrypTool/CryptDoc.cpp +++ b/CrypTool/CryptDoc.cpp @@ -64,11 +64,11 @@ #include "DlgShowKey.h" #include "DlgSolitaireAnalyse.h" #include "ECIESMain.h" -#include "bruteforceheap.h" +#include "BruteForceHeap.h" #include "DlgFormatTextDocument.h" #include "DlgSimpleTranspositions.h" #include "DlgAutomatedPermAnalysis.h" -#include "keyHillBase.h" +#include "KeyHillBase.h" extern char *CaPseDatei, *CaPseVerzeichnis, *Pfad, *PseVerzeichnis; @@ -140,145 +140,145 @@ void CCryptDoc::SymCryptBruteForce( int AlgID, cryptProvider provider, int keyle BEGIN_MESSAGE_MAP(CCryptDoc, CAppDocument) //{{AFX_MSG_MAP(CCryptDoc) - ON_COMMAND(ID_ADD_BIN, OnAddBin) - ON_COMMAND(ID_PLAYFAIR_BIN, OnPlayfairBin) - ON_COMMAND(ID_PLAYFAIR_ANALYSE, OnPlayfairAnalyse) - ON_COMMAND(ID_CAESAR_ASC, OnCaesarAsc) - ON_COMMAND(ID_VIGENERE_ASC, OnVigenereAsc) - ON_COMMAND(ID_XOR_BIN, OnXorBin) - ON_COMMAND(ID_AUTO, OnAuto) - ON_COMMAND(ID_ENTROPY, OnEntropy) - ON_COMMAND(ID_HISTOGRAM, OnHistogram) - ON_COMMAND(ID_CRYPT_IDEA, OnCryptIdea) - ON_COMMAND(ID_CRYPT_RC2, OnCryptRc2) - ON_COMMAND(ID_CRYPT_DES_DESECB, OnCryptDesDesecb) - ON_COMMAND(ID_CRYPT_HASH_MD5, OnCryptHashMd5) - ON_COMMAND(ID_CRYPT_HASH_MD2, OnCryptHashMd2) - ON_COMMAND(ID_CRYPT_HASH_MD4, OnCryptHashMd4) - ON_COMMAND(ID_CRYPT_HASH_RIPEMD160, OnCryptHashRipemd160) - ON_COMMAND(ID_CRYPT_HASH_SHA, OnCryptHashSha) - ON_COMMAND(ID_CRYPT_HASH_SHA1, OnCryptHashSha1) - ON_COMMAND(ID_CRYPT_HASH_SHA256, OnCryptHashSha256) - ON_COMMAND(ID_CRYPT_HASH_SHA512, OnCryptHashSha512) - ON_COMMAND(ID_VERNAM_BIN, OnVernamBin) - ON_COMMAND(ID_CRYPT_3DES_CBC, OnCrypt3desCbc) - ON_COMMAND(ID_CRYPT_3DES_ECB, OnCrypt3desEcb) - ON_COMMAND(ID_CRYPT_RC4, OnCryptRc4) - ON_COMMAND(ID_CRYPT_DES_DESCBC, OnCryptDesDescbc) - ON_COMMAND(ID_CAESAR_AUTO, OnCaesarAuto) - ON_COMMAND(ID_VIGENERE_AUTO, OnVigenereAuto) - ON_COMMAND(ID_ANALYSIS_VIGENERE_SCHROEDEL, OnVigenereAnalysisSchroedel) - ON_COMMAND(ID_HILL_AUTO, OnHillPlain) - ON_COMMAND(ID_ADD_AUTO, OnAddAuto) - ON_COMMAND(ID_XOR_AUTO, OnXorAuto) - ON_COMMAND(ID_HILL, OnHill) - ON_COMMAND(ID_ZIP, OnZip) - ON_COMMAND(ID_UNZIP, OnUnzip) - ON_COMMAND(ID_CRYPT_RsaDec, OnCRYPTRsaDec) - ON_COMMAND(ID_CRYPT_RsaEnc, OnCRYPTRsaEnc) - ON_COMMAND(ID_CryptMono, OnCryptMono) - ON_COMMAND(ID_CRYPT_SIGN, OnCryptSign) - ON_COMMAND(ID_CRYPT_VERIFY, OnCryptVerify) - ON_COMMAND(ID_CRYPT_EXTRACT, OnCryptExtract) - ON_COMMAND(ID_ANALYZE_SUBST, OnAnalyzeSubst) - ON_COMMAND(ID_FLOATING, OnFloating) - ON_UPDATE_COMMAND_UI(ID_CRYPT_3DES_CBC, OnUpdateNeedSecude) - ON_UPDATE_COMMAND_UI(ID_CRYPT_EXTRACT, OnUpdateNeedSecudeTicket) - ON_COMMAND(ID_ANALYSE_DESCBC, OnAnalyseDescbc) - ON_COMMAND(ID_ANALYSE_DESECB, OnAnalyseDesecb) - ON_COMMAND(ID_ANALYSE_IDEA, OnAnalyseIdea) - ON_COMMAND(ID_ANALYSE_RC2, OnAnalyseRc2) - ON_COMMAND(ID_ANALYSE_RC4, OnAnalyseRc4) - ON_COMMAND(ID_ANALYSE_TRIPLEDESCBC, OnAnalyseTripledescbc) - ON_COMMAND(ID_ANALYSE_TRIPLEDESECB, OnAnalyseTripledesecb) - ON_COMMAND(ID_CRYPT_AES_MARS, OnCryptAesMars) - ON_COMMAND(ID_CRYPT_AES_RC6, OnCryptAesRc6) - ON_COMMAND(ID_CRYPT_AES_RIJNDAEL, OnCryptAesRijndael) - ON_COMMAND(ID_CRYPT_AES_SERPENT, OnCryptAesSerpent) - ON_COMMAND(ID_CRYPT_AES_TWOFISH, OnCryptAesTwofish) - ON_COMMAND(ID_ANALYSE_AES_MARS, OnAnalyseAesMars) - ON_COMMAND(ID_ANALYSE_AES_RC6, OnAnalyseAesRc6) - ON_COMMAND(ID_ANALYSE_AES_RIJNDAEL, OnAnalyseAesRijndael) - ON_COMMAND(ID_ANALYSE_AES_SERPENT, OnAnalyseAesSerpent) - ON_COMMAND(ID_ANALYSE_AES_TWOFISH, OnAnalyseAesTwofish) - ON_COMMAND(ID_ANALYSE_VITANY, OnVitanyAnalyse) - ON_COMMAND(ID_ANALYSE_PERIOD, OnPeriod) - ON_COMMAND(ID_HOMOPHONE_ASC, OnHomophone) - ON_COMMAND(ID_ANALYSE_NGRAM, OnAnalyseNGram) - ON_COMMAND(ID_ANALYSE_NGRAM_BIN, OnAnalyseNGramBin) - ON_COMMAND(ID_PERMUTATION, OnPermutation) - ON_COMMAND(ID_AES_SELFEXTRACT, OnAesSelfextract) - ON_UPDATE_COMMAND_UI(ID_AES_SELFEXTRACT, OnUpdateAesSelfextract) - ON_COMMAND(ID_ANALYSE_ZUFALLSTESTS_FREQUENCYTEST, OnAnalyseZufallstestsFrequencytest) - ON_COMMAND(ID_ANALYSE_ZUFALLSTESTS_SERIALTEST, OnAnalyseZufallstestsSerialtest) - ON_COMMAND(ID_ANALYSE_ZUFALLSTESTS_RUNS, OnAnalyseZufallstestsRuns) - ON_COMMAND(ID_ANALYSE_ZUFALLSTESTS_POKERTEST, OnAnalyseZufallstestsPokertest) - ON_COMMAND(ID_ANALYSE_ZUFALLSTESTS_FIPSPUB1401, OnAnalyseZufallstestsFipspub1401) - ON_COMMAND(ID_EINZELVERFAHREN_HASHWERTE_HASHDEMO, OnEinzelverfahrenHashwerteHashdemo) - ON_COMMAND(ID_VERENTSCHLSSELN_HYBRIDVERFAHREN_HYBRIDVERSCHLSSELUNG, OnEinzelverfahrenHybridverfahrenHybridverschlsselung) - ON_COMMAND(ID_EINZELVERFAHREN_SIGN_DOC, OnEinzelverfahrenSignDoc) - ON_COMMAND(ID_VERENTSCHLSSELN_HYBRIDVERFAHREN_HYBRIDENTSCHLSSELUNG, OnEinzelverfahrenHybridverfahrenHybridentschlsselung) - ON_COMMAND(ID_EINZELVERFAHREN_ASN1DECODIEREN, OnEinzelverfahrenAsn1decodieren) - ON_COMMAND(ID_SIGATTMODIFICDEMO, OnSigattmodificdemo) - ON_COMMAND(ID_SIGNATUR_ATTACK, OnSignaturAttack) - ON_COMMAND(ID_EINZELVERFAHREN_SIDECHANNELATTACK_ON_HYBRIDENCRYPTION, OnEinzelverfahrenSidechannelattackOnHybridencryption) - ON_COMMAND(ID_FILEPROPERTIES, OnFileProperties) - ON_COMMAND(ID_GENERATE_MACS, OnMessageauthenticationcode) - ON_COMMAND(ID_ENCRYPT_ADFGVX, OnEncryptAdfgvx) - ON_COMMAND(ID_ANALYSE_SYMCLASSIC_ADFGVX, OnAnalyseSymmclassicAdfgvx) - ON_COMMAND(ID_CIPHERTEXT_ONLY_SUBSTITUTION, OnCiphertextOnlySubstitution) - ON_COMMAND(ID_SHOW_KEY, OnShowKey) - ON_COMMAND(ID_GOTO_VATER, OnGotoVater) - ON_COMMAND(ID_ANALYSIS_RANDOM_3D_VISUALIZATION, OnAnalysisRandom3dVisualization) - ON_COMMAND(ID_SYMCLASSIC_SOLITAIRE, OnSymclassicSolitaire) - ON_COMMAND(ID_ENCRYPTION_SIMPLE_TRANSPOSITIONS, OnEncryptionSimpleTranspositions) - ON_UPDATE_COMMAND_UI(ID_CRYPT_3DES_ECB, OnUpdateNeedSecude) - ON_UPDATE_COMMAND_UI(ID_CRYPT_DES_DESCBC, OnUpdateNeedSecude) - ON_UPDATE_COMMAND_UI(ID_CRYPT_DES_DESECB, OnUpdateNeedSecude) - ON_UPDATE_COMMAND_UI(ID_CRYPT_HASH_MD2, OnUpdateNeedSecude) - ON_UPDATE_COMMAND_UI(ID_CRYPT_HASH_MD4, OnUpdateNeedSecude) - ON_UPDATE_COMMAND_UI(ID_CRYPT_HASH_MD5, OnUpdateNeedSecude) - ON_UPDATE_COMMAND_UI(ID_CRYPT_HASH_RIPEMD160, OnUpdateNeedSecude) - ON_UPDATE_COMMAND_UI(ID_CRYPT_HASH_SHA, OnUpdateNeedSecude) - ON_UPDATE_COMMAND_UI(ID_CRYPT_HASH_SHA1, OnUpdateNeedSecude) - ON_UPDATE_COMMAND_UI(ID_CRYPT_IDEA, OnUpdateNeedSecude) - ON_UPDATE_COMMAND_UI(ID_CRYPT_RC4, OnUpdateNeedSecude) - ON_UPDATE_COMMAND_UI(ID_CRYPT_RC2, OnUpdateNeedSecude) - ON_UPDATE_COMMAND_UI(ID_CRYPT_RsaDec, OnUpdateNeedSecudeTicket) - ON_UPDATE_COMMAND_UI(ID_CRYPT_RsaEnc, OnUpdateNeedSecudeTicket) - ON_UPDATE_COMMAND_UI(ID_CRYPT_SIGN, OnUpdateNeedSecudeTicket) - ON_UPDATE_COMMAND_UI(ID_CRYPT_VERIFY, OnUpdateNeedSecudeTicket) - ON_UPDATE_COMMAND_UI(ID_ANALYSE_DESCBC, OnUpdateNeedSecudeTicket) - ON_UPDATE_COMMAND_UI(ID_ANALYSE_DESECB, OnUpdateNeedSecudeTicket) - ON_UPDATE_COMMAND_UI(ID_ANALYSE_IDEA, OnUpdateNeedSecudeTicket) - ON_UPDATE_COMMAND_UI(ID_ANALYSE_RC2, OnUpdateNeedSecudeTicket) - ON_UPDATE_COMMAND_UI(ID_ANALYSE_RC4, OnUpdateNeedSecudeTicket) - ON_UPDATE_COMMAND_UI(ID_ANALYSE_TRIPLEDESCBC, OnUpdateNeedSecudeTicket) - ON_UPDATE_COMMAND_UI(ID_ANALYSE_TRIPLEDESECB, OnUpdateNeedSecudeTicket) - ON_UPDATE_COMMAND_UI(ID_VERENTSCHLSSELN_HYBRIDVERFAHREN_HYBRIDVERSCHLSSELUNG, OnUpdateNeedSecude) - ON_UPDATE_COMMAND_UI(ID_VERENTSCHLSSELN_HYBRIDVERFAHREN_HYBRIDENTSCHLSSELUNG, OnUpdateNeedSecude) - ON_UPDATE_COMMAND_UI(ID_EINZELVERFAHREN_SIGN_DOC, OnUpdateNeedSecude) - ON_UPDATE_COMMAND_UI(ID_EINZELVERFAHREN_HASHWERTE_HASHDEMO, OnUpdateNeedSecude) - ON_COMMAND(ID_CIPHERTEXT_ONLY_SUBSTITUTION, OnCiphertextOnlySubstitution) - ON_COMMAND(ID_ROT13CAESAR_ASC, OnRot13caesarAsc) - ON_COMMAND(ID_ANALYSE_SYMCLASSIC_CIPHERTEXTONLY_SOLITAIRE, OnAnalyseSymmclassicCiphertextolySolitaire) - ON_COMMAND(ID_ENCODE_BASE64, OnIndivproceduresBase64Encode) - ON_COMMAND(ID_DECODE_BASE64, OnIndivproceduresBase64Decode) - ON_COMMAND(ID_ENCODE_UU, OnEncodeUu) - ON_COMMAND(ID_DECODE_UU, OnDecodeUu) - ON_COMMAND(ID_CRYPT_DESL, OnCryptDESL) - ON_COMMAND(ID_CRYPT_DESX, OnCryptDESX) - ON_COMMAND(ID_CRYPT_DESXL, OnCryptDESXL) - ON_COMMAND(ID_ANALYSE_DESL, OnAnalyseDESL) - ON_COMMAND(ID_ANALYSE_DESX, OnAnalyseDESX) - ON_COMMAND(ID_ANALYSE_DESXL, OnAnalyseDESXL) - ON_COMMAND(ID_FORMAT_TEXT_DOCUMENT, OnFormatTextDocument) + ON_COMMAND(ID_ADD_BIN, &CCryptDoc::OnAddBin) + ON_COMMAND(ID_PLAYFAIR_BIN, &CCryptDoc::OnPlayfairBin) + ON_COMMAND(ID_PLAYFAIR_ANALYSE, &CCryptDoc::OnPlayfairAnalyse) + ON_COMMAND(ID_CAESAR_ASC, &CCryptDoc::OnCaesarAsc) + ON_COMMAND(ID_VIGENERE_ASC, &CCryptDoc::OnVigenereAsc) + ON_COMMAND(ID_XOR_BIN, &CCryptDoc::OnXorBin) + ON_COMMAND(ID_AUTO, &CCryptDoc::OnAuto) + ON_COMMAND(ID_ENTROPY, &CCryptDoc::OnEntropy) + ON_COMMAND(ID_HISTOGRAM, &CCryptDoc::OnHistogram) + ON_COMMAND(ID_CRYPT_IDEA, &CCryptDoc::OnCryptIdea) + ON_COMMAND(ID_CRYPT_RC2, &CCryptDoc::OnCryptRc2) + ON_COMMAND(ID_CRYPT_DES_DESECB, &CCryptDoc::OnCryptDesDesecb) + ON_COMMAND(ID_CRYPT_HASH_MD5, &CCryptDoc::OnCryptHashMd5) + ON_COMMAND(ID_CRYPT_HASH_MD2, &CCryptDoc::OnCryptHashMd2) + ON_COMMAND(ID_CRYPT_HASH_MD4, &CCryptDoc::OnCryptHashMd4) + ON_COMMAND(ID_CRYPT_HASH_RIPEMD160, &CCryptDoc::OnCryptHashRipemd160) + ON_COMMAND(ID_CRYPT_HASH_SHA, &CCryptDoc::OnCryptHashSha) + ON_COMMAND(ID_CRYPT_HASH_SHA1, &CCryptDoc::OnCryptHashSha1) + ON_COMMAND(ID_CRYPT_HASH_SHA256, &CCryptDoc::OnCryptHashSha256) + ON_COMMAND(ID_CRYPT_HASH_SHA512, &CCryptDoc::OnCryptHashSha512) + ON_COMMAND(ID_VERNAM_BIN, &CCryptDoc::OnVernamBin) + ON_COMMAND(ID_CRYPT_3DES_CBC, &CCryptDoc::OnCrypt3desCbc) + ON_COMMAND(ID_CRYPT_3DES_ECB, &CCryptDoc::OnCrypt3desEcb) + ON_COMMAND(ID_CRYPT_RC4, &CCryptDoc::OnCryptRc4) + ON_COMMAND(ID_CRYPT_DES_DESCBC, &CCryptDoc::OnCryptDesDescbc) + ON_COMMAND(ID_CAESAR_AUTO, &CCryptDoc::OnCaesarAuto) + ON_COMMAND(ID_VIGENERE_AUTO, &CCryptDoc::OnVigenereAuto) + ON_COMMAND(ID_ANALYSIS_VIGENERE_SCHROEDEL, &CCryptDoc::OnVigenereAnalysisSchroedel) + ON_COMMAND(ID_HILL_AUTO, &CCryptDoc::OnHillPlain) + ON_COMMAND(ID_ADD_AUTO, &CCryptDoc::OnAddAuto) + ON_COMMAND(ID_XOR_AUTO, &CCryptDoc::OnXorAuto) + ON_COMMAND(ID_HILL, &CCryptDoc::OnHill) + ON_COMMAND(ID_ZIP, &CCryptDoc::OnZip) + ON_COMMAND(ID_UNZIP, &CCryptDoc::OnUnzip) + ON_COMMAND(ID_CRYPT_RsaDec, &CCryptDoc::OnCRYPTRsaDec) + ON_COMMAND(ID_CRYPT_RsaEnc, &CCryptDoc::OnCRYPTRsaEnc) + ON_COMMAND(ID_CryptMono, &CCryptDoc::OnCryptMono) + ON_COMMAND(ID_CRYPT_SIGN, &CCryptDoc::OnCryptSign) + ON_COMMAND(ID_CRYPT_VERIFY, &CCryptDoc::OnCryptVerify) + ON_COMMAND(ID_CRYPT_EXTRACT, &CCryptDoc::OnCryptExtract) + ON_COMMAND(ID_ANALYZE_SUBST, &CCryptDoc::OnAnalyzeSubst) + ON_COMMAND(ID_FLOATING, &CCryptDoc::OnFloating) + ON_UPDATE_COMMAND_UI(ID_CRYPT_3DES_CBC, &CCryptDoc::OnUpdateNeedSecude) + ON_UPDATE_COMMAND_UI(ID_CRYPT_EXTRACT, &CCryptDoc::OnUpdateNeedSecudeTicket) + ON_COMMAND(ID_ANALYSE_DESCBC, &CCryptDoc::OnAnalyseDescbc) + ON_COMMAND(ID_ANALYSE_DESECB, &CCryptDoc::OnAnalyseDesecb) + ON_COMMAND(ID_ANALYSE_IDEA, &CCryptDoc::OnAnalyseIdea) + ON_COMMAND(ID_ANALYSE_RC2, &CCryptDoc::OnAnalyseRc2) + ON_COMMAND(ID_ANALYSE_RC4, &CCryptDoc::OnAnalyseRc4) + ON_COMMAND(ID_ANALYSE_TRIPLEDESCBC, &CCryptDoc::OnAnalyseTripledescbc) + ON_COMMAND(ID_ANALYSE_TRIPLEDESECB, &CCryptDoc::OnAnalyseTripledesecb) + ON_COMMAND(ID_CRYPT_AES_MARS, &CCryptDoc::OnCryptAesMars) + ON_COMMAND(ID_CRYPT_AES_RC6, &CCryptDoc::OnCryptAesRc6) + ON_COMMAND(ID_CRYPT_AES_RIJNDAEL, &CCryptDoc::OnCryptAesRijndael) + ON_COMMAND(ID_CRYPT_AES_SERPENT, &CCryptDoc::OnCryptAesSerpent) + ON_COMMAND(ID_CRYPT_AES_TWOFISH, &CCryptDoc::OnCryptAesTwofish) + ON_COMMAND(ID_ANALYSE_AES_MARS, &CCryptDoc::OnAnalyseAesMars) + ON_COMMAND(ID_ANALYSE_AES_RC6, &CCryptDoc::OnAnalyseAesRc6) + ON_COMMAND(ID_ANALYSE_AES_RIJNDAEL, &CCryptDoc::OnAnalyseAesRijndael) + ON_COMMAND(ID_ANALYSE_AES_SERPENT, &CCryptDoc::OnAnalyseAesSerpent) + ON_COMMAND(ID_ANALYSE_AES_TWOFISH, &CCryptDoc::OnAnalyseAesTwofish) + ON_COMMAND(ID_ANALYSE_VITANY, &CCryptDoc::OnVitanyAnalyse) + ON_COMMAND(ID_ANALYSE_PERIOD, &CCryptDoc::OnPeriod) + ON_COMMAND(ID_HOMOPHONE_ASC, &CCryptDoc::OnHomophone) + ON_COMMAND(ID_ANALYSE_NGRAM, &CCryptDoc::OnAnalyseNGram) + ON_COMMAND(ID_ANALYSE_NGRAM_BIN, &CCryptDoc::OnAnalyseNGramBin) + ON_COMMAND(ID_PERMUTATION, &CCryptDoc::OnPermutation) + ON_COMMAND(ID_AES_SELFEXTRACT, &CCryptDoc::OnAesSelfextract) + ON_UPDATE_COMMAND_UI(ID_AES_SELFEXTRACT, &CCryptDoc::OnUpdateAesSelfextract) + ON_COMMAND(ID_ANALYSE_ZUFALLSTESTS_FREQUENCYTEST, &CCryptDoc::OnAnalyseZufallstestsFrequencytest) + ON_COMMAND(ID_ANALYSE_ZUFALLSTESTS_SERIALTEST, &CCryptDoc::OnAnalyseZufallstestsSerialtest) + ON_COMMAND(ID_ANALYSE_ZUFALLSTESTS_RUNS, &CCryptDoc::OnAnalyseZufallstestsRuns) + ON_COMMAND(ID_ANALYSE_ZUFALLSTESTS_POKERTEST, &CCryptDoc::OnAnalyseZufallstestsPokertest) + ON_COMMAND(ID_ANALYSE_ZUFALLSTESTS_FIPSPUB1401, &CCryptDoc::OnAnalyseZufallstestsFipspub1401) + ON_COMMAND(ID_EINZELVERFAHREN_HASHWERTE_HASHDEMO, &CCryptDoc::OnEinzelverfahrenHashwerteHashdemo) + ON_COMMAND(ID_VERENTSCHLSSELN_HYBRIDVERFAHREN_HYBRIDVERSCHLSSELUNG, &CCryptDoc::OnEinzelverfahrenHybridverfahrenHybridverschlsselung) + ON_COMMAND(ID_EINZELVERFAHREN_SIGN_DOC, &CCryptDoc::OnEinzelverfahrenSignDoc) + ON_COMMAND(ID_VERENTSCHLSSELN_HYBRIDVERFAHREN_HYBRIDENTSCHLSSELUNG, &CCryptDoc::OnEinzelverfahrenHybridverfahrenHybridentschlsselung) + ON_COMMAND(ID_EINZELVERFAHREN_ASN1DECODIEREN, &CCryptDoc::OnEinzelverfahrenAsn1decodieren) + ON_COMMAND(ID_SIGATTMODIFICDEMO, &CCryptDoc::OnSigattmodificdemo) + ON_COMMAND(ID_SIGNATUR_ATTACK, &CCryptDoc::OnSignaturAttack) + ON_COMMAND(ID_EINZELVERFAHREN_SIDECHANNELATTACK_ON_HYBRIDENCRYPTION, &CCryptDoc::OnEinzelverfahrenSidechannelattackOnHybridencryption) + ON_COMMAND(ID_FILEPROPERTIES, &CCryptDoc::OnFileProperties) + ON_COMMAND(ID_GENERATE_MACS, &CCryptDoc::OnMessageauthenticationcode) + ON_COMMAND(ID_ENCRYPT_ADFGVX, &CCryptDoc::OnEncryptAdfgvx) + ON_COMMAND(ID_ANALYSE_SYMCLASSIC_ADFGVX, &CCryptDoc::OnAnalyseSymmclassicAdfgvx) + ON_COMMAND(ID_CIPHERTEXT_ONLY_SUBSTITUTION, &CCryptDoc::OnCiphertextOnlySubstitution) + ON_COMMAND(ID_SHOW_KEY, &CCryptDoc::OnShowKey) + ON_COMMAND(ID_GOTO_VATER, &CCryptDoc::OnGotoVater) + ON_COMMAND(ID_ANALYSIS_RANDOM_3D_VISUALIZATION, &CCryptDoc::OnAnalysisRandom3dVisualization) + ON_COMMAND(ID_SYMCLASSIC_SOLITAIRE, &CCryptDoc::OnSymclassicSolitaire) + ON_COMMAND(ID_ENCRYPTION_SIMPLE_TRANSPOSITIONS, &CCryptDoc::OnEncryptionSimpleTranspositions) + ON_UPDATE_COMMAND_UI(ID_CRYPT_3DES_ECB, &CCryptDoc::OnUpdateNeedSecude) + ON_UPDATE_COMMAND_UI(ID_CRYPT_DES_DESCBC, &CCryptDoc::OnUpdateNeedSecude) + ON_UPDATE_COMMAND_UI(ID_CRYPT_DES_DESECB, &CCryptDoc::OnUpdateNeedSecude) + ON_UPDATE_COMMAND_UI(ID_CRYPT_HASH_MD2, &CCryptDoc::OnUpdateNeedSecude) + ON_UPDATE_COMMAND_UI(ID_CRYPT_HASH_MD4, &CCryptDoc::OnUpdateNeedSecude) + ON_UPDATE_COMMAND_UI(ID_CRYPT_HASH_MD5, &CCryptDoc::OnUpdateNeedSecude) + ON_UPDATE_COMMAND_UI(ID_CRYPT_HASH_RIPEMD160, &CCryptDoc::OnUpdateNeedSecude) + ON_UPDATE_COMMAND_UI(ID_CRYPT_HASH_SHA, &CCryptDoc::OnUpdateNeedSecude) + ON_UPDATE_COMMAND_UI(ID_CRYPT_HASH_SHA1, &CCryptDoc::OnUpdateNeedSecude) + ON_UPDATE_COMMAND_UI(ID_CRYPT_IDEA, &CCryptDoc::OnUpdateNeedSecude) + ON_UPDATE_COMMAND_UI(ID_CRYPT_RC4, &CCryptDoc::OnUpdateNeedSecude) + ON_UPDATE_COMMAND_UI(ID_CRYPT_RC2, &CCryptDoc::OnUpdateNeedSecude) + ON_UPDATE_COMMAND_UI(ID_CRYPT_RsaDec, &CCryptDoc::OnUpdateNeedSecudeTicket) + ON_UPDATE_COMMAND_UI(ID_CRYPT_RsaEnc, &CCryptDoc::OnUpdateNeedSecudeTicket) + ON_UPDATE_COMMAND_UI(ID_CRYPT_SIGN, &CCryptDoc::OnUpdateNeedSecudeTicket) + ON_UPDATE_COMMAND_UI(ID_CRYPT_VERIFY, &CCryptDoc::OnUpdateNeedSecudeTicket) + ON_UPDATE_COMMAND_UI(ID_ANALYSE_DESCBC, &CCryptDoc::OnUpdateNeedSecudeTicket) + ON_UPDATE_COMMAND_UI(ID_ANALYSE_DESECB, &CCryptDoc::OnUpdateNeedSecudeTicket) + ON_UPDATE_COMMAND_UI(ID_ANALYSE_IDEA, &CCryptDoc::OnUpdateNeedSecudeTicket) + ON_UPDATE_COMMAND_UI(ID_ANALYSE_RC2, &CCryptDoc::OnUpdateNeedSecudeTicket) + ON_UPDATE_COMMAND_UI(ID_ANALYSE_RC4, &CCryptDoc::OnUpdateNeedSecudeTicket) + ON_UPDATE_COMMAND_UI(ID_ANALYSE_TRIPLEDESCBC, &CCryptDoc::OnUpdateNeedSecudeTicket) + ON_UPDATE_COMMAND_UI(ID_ANALYSE_TRIPLEDESECB, &CCryptDoc::OnUpdateNeedSecudeTicket) + ON_UPDATE_COMMAND_UI(ID_VERENTSCHLSSELN_HYBRIDVERFAHREN_HYBRIDVERSCHLSSELUNG, &CCryptDoc::OnUpdateNeedSecude) + ON_UPDATE_COMMAND_UI(ID_VERENTSCHLSSELN_HYBRIDVERFAHREN_HYBRIDENTSCHLSSELUNG, &CCryptDoc::OnUpdateNeedSecude) + ON_UPDATE_COMMAND_UI(ID_EINZELVERFAHREN_SIGN_DOC, &CCryptDoc::OnUpdateNeedSecude) + ON_UPDATE_COMMAND_UI(ID_EINZELVERFAHREN_HASHWERTE_HASHDEMO, &CCryptDoc::OnUpdateNeedSecude) + ON_COMMAND(ID_CIPHERTEXT_ONLY_SUBSTITUTION, &CCryptDoc::OnCiphertextOnlySubstitution) + ON_COMMAND(ID_ROT13CAESAR_ASC, &CCryptDoc::OnRot13caesarAsc) + ON_COMMAND(ID_ANALYSE_SYMCLASSIC_CIPHERTEXTONLY_SOLITAIRE, &CCryptDoc::OnAnalyseSymmclassicCiphertextolySolitaire) + ON_COMMAND(ID_ENCODE_BASE64, &CCryptDoc::OnIndivproceduresBase64Encode) + ON_COMMAND(ID_DECODE_BASE64, &CCryptDoc::OnIndivproceduresBase64Decode) + ON_COMMAND(ID_ENCODE_UU, &CCryptDoc::OnEncodeUu) + ON_COMMAND(ID_DECODE_UU, &CCryptDoc::OnDecodeUu) + ON_COMMAND(ID_CRYPT_DESL, &CCryptDoc::OnCryptDESL) + ON_COMMAND(ID_CRYPT_DESX, &CCryptDoc::OnCryptDESX) + ON_COMMAND(ID_CRYPT_DESXL, &CCryptDoc::OnCryptDESXL) + ON_COMMAND(ID_ANALYSE_DESL, &CCryptDoc::OnAnalyseDESL) + ON_COMMAND(ID_ANALYSE_DESX, &CCryptDoc::OnAnalyseDESX) + ON_COMMAND(ID_ANALYSE_DESXL, &CCryptDoc::OnAnalyseDESXL) + ON_COMMAND(ID_FORMAT_TEXT_DOCUMENT, &CCryptDoc::OnFormatTextDocument) //}}AFX_MSG_MAP - ON_COMMAND(ID_HYBRID_ECCENC, OnHybridEccEnc) - ON_COMMAND(ID_HYBRID_ECCDEC, OnHybridEccDec) - ON_COMMAND(ID_EDIT_REPEAT, OnEditRepeat) - ON_UPDATE_COMMAND_UI(ID_EDIT_REPEAT, OnUpdateEditRepeat) + ON_COMMAND(ID_HYBRID_ECCENC, &CCryptDoc::OnHybridEccEnc) + ON_COMMAND(ID_HYBRID_ECCDEC, &CCryptDoc::OnHybridEccDec) + ON_COMMAND(ID_EDIT_REPEAT, &CCryptDoc::OnEditRepeat) + ON_UPDATE_COMMAND_UI(ID_EDIT_REPEAT, &CCryptDoc::OnUpdateEditRepeat) ON_COMMAND(ID_KNOWN_ANALYSIS_SINGLE_PERMUTATION, &CCryptDoc::OnKnownAnalysisSinglePermutation) END_MESSAGE_MAP() @@ -417,9 +417,9 @@ CHexDoc::~CHexDoc() BEGIN_MESSAGE_MAP(CHexDoc, CCryptDoc) //{{AFX_MSG_MAP(CHexDoc) - ON_COMMAND(ID_ENTROPY, OnEntropy) - ON_COMMAND(ID_HISTOGRAM, OnHistogram) - ON_COMMAND(ID_AUTO, OnAuto) + ON_COMMAND(ID_ENTROPY, &CHexDoc::OnEntropy) + ON_COMMAND(ID_HISTOGRAM, &CHexDoc::OnHistogram) + ON_COMMAND(ID_AUTO, &CHexDoc::OnAuto) //}}AFX_MSG_MAP END_MESSAGE_MAP() @@ -483,9 +483,9 @@ void CAscDoc::Serialize(CArchive& ar) BEGIN_MESSAGE_MAP(CAscDoc, CCryptDoc) //{{AFX_MSG_MAP(CAscDoc) - ON_COMMAND(ID_ENTROPY, OnEntropy) - ON_COMMAND(ID_HISTOGRAM, OnHistogram) - ON_COMMAND(ID_AUTO, OnAuto) + ON_COMMAND(ID_ENTROPY, &CAscDoc::OnEntropy) + ON_COMMAND(ID_HISTOGRAM, &CAscDoc::OnHistogram) + ON_COMMAND(ID_AUTO, &CAscDoc::OnAuto) //}}AFX_MSG_MAP END_MESSAGE_MAP() diff --git a/CrypTool/CryptDoc.h b/CrypTool/CryptDoc.h index 1840b4f5a..8f5753352 100644 --- a/CrypTool/CryptDoc.h +++ b/CrypTool/CryptDoc.h @@ -21,7 +21,7 @@ #ifndef _CRYPTDOC_H_ #define _CRYPTDOC_H_ -#include "symEncBase.h" +#include "SymEncBase.h" #include "CrypToolApp.h" ////////////////////////////////////////////////////////////////// @@ -207,7 +207,7 @@ class CHexDoc : public CCryptDoc // Attributes public: -BOOL CHexDoc::UpdateContent( void ); +BOOL UpdateContent( void ); // Operations public: diff --git a/CrypTool/Cryptography.cpp b/CrypTool/Cryptography.cpp index 05252356b..0c34e6331 100644 --- a/CrypTool/Cryptography.cpp +++ b/CrypTool/Cryptography.cpp @@ -75,7 +75,7 @@ using namespace std; #include "DlgKeyHexFixedLen.h" #include "DlgBruteForceAES.h" #include "ListResults.h" -#include "bruteforceheap.h" +#include "BruteForceHeap.h" #include #include diff --git a/CrypTool/Cryptography.h b/CrypTool/Cryptography.h index c0c3c5c5c..590c89430 100644 --- a/CrypTool/Cryptography.h +++ b/CrypTool/Cryptography.h @@ -23,7 +23,7 @@ #include "DlgShowProgress.h" #include "MakeNewName.h" -#include "symEncBase.h" +#include "SymEncBase.h" #define _CRT_SECURE_NO_WARNINGS diff --git a/CrypTool/DESXL.cpp b/CrypTool/DESXL.cpp index d0643102d..76a4566c2 100644 --- a/CrypTool/DESXL.cpp +++ b/CrypTool/DESXL.cpp @@ -27,7 +27,7 @@ // at the Ruhr-University Bochum (http://www.crypto.rub.de) ////////////////////////////////////////////////////////////////// -#include ".\DESXL.h" +#include "DESXL.h" DESXL::DESXL(void) { diff --git a/CrypTool/DialogLaengeBin.cpp b/CrypTool/DialogLaengeBin.cpp index de7bdd3a9..8e4ddf7c2 100644 --- a/CrypTool/DialogLaengeBin.cpp +++ b/CrypTool/DialogLaengeBin.cpp @@ -60,8 +60,8 @@ void CDlgBinKeyLength::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgBinKeyLength, CDialog) //{{AFX_MSG_MAP(CDlgBinKeyLength) - ON_EN_CHANGE(IDC_EDIT2, OnChangeEdit2) - ON_EN_CHANGE(IDC_EDIT1, OnChangeEdit2) + ON_EN_CHANGE(IDC_EDIT2, &CDlgBinKeyLength::OnChangeEdit2) + ON_EN_CHANGE(IDC_EDIT1, &CDlgBinKeyLength::OnChangeEdit2) //}}AFX_MSG_MAP END_MESSAGE_MAP() diff --git a/CrypTool/DiffieHellmanLogFile.cpp b/CrypTool/DiffieHellmanLogFile.cpp index eae0571f8..5c697fe21 100644 --- a/CrypTool/DiffieHellmanLogFile.cpp +++ b/CrypTool/DiffieHellmanLogFile.cpp @@ -21,7 +21,7 @@ // DiffieHellmanLogFile.cpp #include "stdafx.h" -#include "CryptoolApp.h" +#include "CrypToolApp.h" #include "DiffieHellmanLogFile.h" diff --git a/CrypTool/DiffieHellmanParty.h b/CrypTool/DiffieHellmanParty.h index 7ba5857a5..e6d460ab1 100644 --- a/CrypTool/DiffieHellmanParty.h +++ b/CrypTool/DiffieHellmanParty.h @@ -34,11 +34,21 @@ /*****************************************************************/ // Miracl-Bibliothek (für große Zahlen) -#include "../libmiracl/include/BIG.H" +#include "big.h" #include -class DHError; +// Klasse zur Ausnahmebehandlung/Fehlerbehandlung +class DHError +{ +private: + long ErrorCode; + +public: + DHError(long e) { ErrorCode = e; }; + + long GetErrorCode() { return ErrorCode; }; +}; class DiffieHellmanParty { @@ -85,18 +95,6 @@ class DiffieHellmanParty bool HasSecret() { return bHasSecret; }; }; -// Klasse zur Ausnahmebehandlung/Fehlerbehandlung -class DHError -{ -private: - long ErrorCode; - -public: - DHError(long e) { ErrorCode = e; }; - - long GetErrorCode() { return ErrorCode; }; -}; - // Fehlercodes #define E_GENERATOR_INVALID 1 #define E_PRIME_INVALID 2 diff --git a/CrypTool/DlgADFGVX.cpp b/CrypTool/DlgADFGVX.cpp index 3ce8f9b13..80ab64528 100644 --- a/CrypTool/DlgADFGVX.cpp +++ b/CrypTool/DlgADFGVX.cpp @@ -24,7 +24,7 @@ #include "stdafx.h" #include "CrypToolApp.h" #include "DlgADFGVX.h" -#include ".\dlgadfgvx.h" +#include "DlgADFGVX.h" #include "DlgAdfgvxStringBox.h" #include "adfgvx.h" #include "FileTools.h" @@ -157,55 +157,55 @@ void CDlgADFGVX::DoDataExchange(CDataExchange* pDX) } BEGIN_MESSAGE_MAP(CDlgADFGVX, CDialog) - ON_BN_CLICKED(IDC_BUTTON_ENCRYPT, OnBnClickedButtonEncrypt) - ON_BN_CLICKED(IDC_BUTTON_DECRYPT, OnBnClickedButtonDecrypt) - ON_BN_CLICKED(IDC_BUTTON_INSERT_KEY, OnBnClickedButtonInsertKey) - ON_BN_CLICKED(IDC_MATRIX_SHUFFLE, OnBnClickedMatrixShuffle) - ON_BN_CLICKED(IDC_MATRIX_STANDARD, OnBnClickedMatrixStandard) - ON_BN_CLICKED(IDC_CANCEL, OnBnClickedCancel) - ON_EN_CHANGE(IDC_M00, OnEnChangeM00) - ON_EN_CHANGE(IDC_M01, OnEnChangeM01) - ON_EN_CHANGE(IDC_M02, OnEnChangeM02) - ON_EN_CHANGE(IDC_M03, OnEnChangeM03) - ON_EN_CHANGE(IDC_M04, OnEnChangeM04) - ON_EN_CHANGE(IDC_M05, OnEnChangeM05) - ON_EN_CHANGE(IDC_M10, OnEnChangeM10) - ON_EN_CHANGE(IDC_M11, OnEnChangeM11) - ON_EN_CHANGE(IDC_M12, OnEnChangeM12) - ON_EN_CHANGE(IDC_M13, OnEnChangeM13) - ON_EN_CHANGE(IDC_M14, OnEnChangeM14) - ON_EN_CHANGE(IDC_M15, OnEnChangeM15) - ON_EN_CHANGE(IDC_M20, OnEnChangeM20) - ON_EN_CHANGE(IDC_M21, OnEnChangeM21) - ON_EN_CHANGE(IDC_M22, OnEnChangeM22) - ON_EN_CHANGE(IDC_M23, OnEnChangeM23) - ON_EN_CHANGE(IDC_M24, OnEnChangeM24) - ON_EN_CHANGE(IDC_M25, OnEnChangeM25) - ON_EN_CHANGE(IDC_M30, OnEnChangeM30) - ON_EN_CHANGE(IDC_M31, OnEnChangeM31) - ON_EN_CHANGE(IDC_M32, OnEnChangeM32) - ON_EN_CHANGE(IDC_M33, OnEnChangeM33) - ON_EN_CHANGE(IDC_M34, OnEnChangeM34) - ON_EN_CHANGE(IDC_M35, OnEnChangeM35) - ON_EN_CHANGE(IDC_M40, OnEnChangeM40) - ON_EN_CHANGE(IDC_M41, OnEnChangeM41) - ON_EN_CHANGE(IDC_M42, OnEnChangeM42) - ON_EN_CHANGE(IDC_M43, OnEnChangeM43) - ON_EN_CHANGE(IDC_M44, OnEnChangeM44) - ON_EN_CHANGE(IDC_M45, OnEnChangeM45) - ON_EN_CHANGE(IDC_M50, OnEnChangeM50) - ON_EN_CHANGE(IDC_M51, OnEnChangeM51) - ON_EN_CHANGE(IDC_M52, OnEnChangeM52) - ON_EN_CHANGE(IDC_M53, OnEnChangeM53) - ON_EN_CHANGE(IDC_M54, OnEnChangeM54) - ON_EN_CHANGE(IDC_M55, OnEnChangeM55) - ON_EN_CHANGE(IDC_TEXTFIELD_PASSWORD, OnChangedTranspositionPassword) - ON_BN_CLICKED(IDC_CHECK_OUTPUT, OnBnClickedCheckOutputStage1) - ON_BN_CLICKED(IDC_ERASEMATRIX, OnBnClickedErasematrix) - ON_BN_CLICKED(IDC_CHECK_BLOCK_STAGE2, OnBnClickedCheckBlockStage2) - ON_BN_CLICKED(IDC_CHECK_BLOCK_STAGE1, OnBnClickedCheckBlockStage1) - ON_BN_CLICKED(IDC_BUTTON_STRINGBOX, OnBnClickedButtonStringbox) - ON_BN_CLICKED(IDC_BUTTON_TEXT_OPTIONS, OnBnClickedButtonTextOptions) + ON_BN_CLICKED(IDC_BUTTON_ENCRYPT, &CDlgADFGVX::OnBnClickedButtonEncrypt) + ON_BN_CLICKED(IDC_BUTTON_DECRYPT, &CDlgADFGVX::OnBnClickedButtonDecrypt) + ON_BN_CLICKED(IDC_BUTTON_INSERT_KEY, &CDlgADFGVX::OnBnClickedButtonInsertKey) + ON_BN_CLICKED(IDC_MATRIX_SHUFFLE, &CDlgADFGVX::OnBnClickedMatrixShuffle) + ON_BN_CLICKED(IDC_MATRIX_STANDARD, &CDlgADFGVX::OnBnClickedMatrixStandard) + ON_BN_CLICKED(IDC_CANCEL, &CDlgADFGVX::OnBnClickedCancel) + ON_EN_CHANGE(IDC_M00, &CDlgADFGVX::OnEnChangeM00) + ON_EN_CHANGE(IDC_M01, &CDlgADFGVX::OnEnChangeM01) + ON_EN_CHANGE(IDC_M02, &CDlgADFGVX::OnEnChangeM02) + ON_EN_CHANGE(IDC_M03, &CDlgADFGVX::OnEnChangeM03) + ON_EN_CHANGE(IDC_M04, &CDlgADFGVX::OnEnChangeM04) + ON_EN_CHANGE(IDC_M05, &CDlgADFGVX::OnEnChangeM05) + ON_EN_CHANGE(IDC_M10, &CDlgADFGVX::OnEnChangeM10) + ON_EN_CHANGE(IDC_M11, &CDlgADFGVX::OnEnChangeM11) + ON_EN_CHANGE(IDC_M12, &CDlgADFGVX::OnEnChangeM12) + ON_EN_CHANGE(IDC_M13, &CDlgADFGVX::OnEnChangeM13) + ON_EN_CHANGE(IDC_M14, &CDlgADFGVX::OnEnChangeM14) + ON_EN_CHANGE(IDC_M15, &CDlgADFGVX::OnEnChangeM15) + ON_EN_CHANGE(IDC_M20, &CDlgADFGVX::OnEnChangeM20) + ON_EN_CHANGE(IDC_M21, &CDlgADFGVX::OnEnChangeM21) + ON_EN_CHANGE(IDC_M22, &CDlgADFGVX::OnEnChangeM22) + ON_EN_CHANGE(IDC_M23, &CDlgADFGVX::OnEnChangeM23) + ON_EN_CHANGE(IDC_M24, &CDlgADFGVX::OnEnChangeM24) + ON_EN_CHANGE(IDC_M25, &CDlgADFGVX::OnEnChangeM25) + ON_EN_CHANGE(IDC_M30, &CDlgADFGVX::OnEnChangeM30) + ON_EN_CHANGE(IDC_M31, &CDlgADFGVX::OnEnChangeM31) + ON_EN_CHANGE(IDC_M32, &CDlgADFGVX::OnEnChangeM32) + ON_EN_CHANGE(IDC_M33, &CDlgADFGVX::OnEnChangeM33) + ON_EN_CHANGE(IDC_M34, &CDlgADFGVX::OnEnChangeM34) + ON_EN_CHANGE(IDC_M35, &CDlgADFGVX::OnEnChangeM35) + ON_EN_CHANGE(IDC_M40, &CDlgADFGVX::OnEnChangeM40) + ON_EN_CHANGE(IDC_M41, &CDlgADFGVX::OnEnChangeM41) + ON_EN_CHANGE(IDC_M42, &CDlgADFGVX::OnEnChangeM42) + ON_EN_CHANGE(IDC_M43, &CDlgADFGVX::OnEnChangeM43) + ON_EN_CHANGE(IDC_M44, &CDlgADFGVX::OnEnChangeM44) + ON_EN_CHANGE(IDC_M45, &CDlgADFGVX::OnEnChangeM45) + ON_EN_CHANGE(IDC_M50, &CDlgADFGVX::OnEnChangeM50) + ON_EN_CHANGE(IDC_M51, &CDlgADFGVX::OnEnChangeM51) + ON_EN_CHANGE(IDC_M52, &CDlgADFGVX::OnEnChangeM52) + ON_EN_CHANGE(IDC_M53, &CDlgADFGVX::OnEnChangeM53) + ON_EN_CHANGE(IDC_M54, &CDlgADFGVX::OnEnChangeM54) + ON_EN_CHANGE(IDC_M55, &CDlgADFGVX::OnEnChangeM55) + ON_EN_CHANGE(IDC_TEXTFIELD_PASSWORD, &CDlgADFGVX::OnChangedTranspositionPassword) + ON_BN_CLICKED(IDC_CHECK_OUTPUT, &CDlgADFGVX::OnBnClickedCheckOutputStage1) + ON_BN_CLICKED(IDC_ERASEMATRIX, &CDlgADFGVX::OnBnClickedErasematrix) + ON_BN_CLICKED(IDC_CHECK_BLOCK_STAGE2, &CDlgADFGVX::OnBnClickedCheckBlockStage2) + ON_BN_CLICKED(IDC_CHECK_BLOCK_STAGE1, &CDlgADFGVX::OnBnClickedCheckBlockStage1) + ON_BN_CLICKED(IDC_BUTTON_STRINGBOX, &CDlgADFGVX::OnBnClickedButtonStringbox) + ON_BN_CLICKED(IDC_BUTTON_TEXT_OPTIONS, &CDlgADFGVX::OnBnClickedButtonTextOptions) END_MESSAGE_MAP() BOOL CDlgADFGVX::OnInitDialog() diff --git a/CrypTool/DlgADFGVX.h b/CrypTool/DlgADFGVX.h index d5417253c..c629c7b84 100644 --- a/CrypTool/DlgADFGVX.h +++ b/CrypTool/DlgADFGVX.h @@ -42,9 +42,9 @@ class CDlgADFGVX : public CDialog CString pwdString; CString matrix[6][6]; CToolTipButton buttonInsertKey; - void CDlgADFGVX::OnOK(); - void CDlgADFGVX::Decrypt(); - void CDlgADFGVX::Encrypt(); + void OnOK(); + void Decrypt(); + void Encrypt(); bool restart; BOOL printStage1; BOOL newLineStage2, newLineStage1; @@ -111,9 +111,9 @@ class CDlgADFGVX : public CDialog afx_msg void OnBnClickedErasematrix(); afx_msg void OnBnClickedCheckBlockStage2(); afx_msg void OnBnClickedCheckBlockStage1(); - BOOL CDlgADFGVX::OnInitDialog(); - CString CDlgADFGVX::CheckInput(CString oldEntry, CString input); - void CDlgADFGVX::CheckProgress(); + BOOL OnInitDialog(); + CString CheckInput(CString oldEntry, CString input); + void CheckProgress(); CString numberedPassword; BOOL boxBlockOutput2; BOOL boxBlockOutput1; diff --git a/CrypTool/DlgAbout.cpp b/CrypTool/DlgAbout.cpp index d58c7d7d0..5ab49aba7 100644 --- a/CrypTool/DlgAbout.cpp +++ b/CrypTool/DlgAbout.cpp @@ -31,7 +31,7 @@ #include "DlgTranslations.h" // für NTL Bibliothek -#include "..\libNTL\include\NTL\version.h" +#include namespace OPENSSL { #include "crypto.h" // clashes with secude.h } @@ -82,8 +82,8 @@ void CDlgAbout::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgAbout, CDialog) //{{AFX_MSG_MAP(CDlgAbout) ON_WM_PAINT() - ON_BN_CLICKED(ID_CONTRIBUTORS, OnBnClickedContributors) - ON_BN_CLICKED(ID_TRANSLATIONS, OnBnClickedTranslations) + ON_BN_CLICKED(ID_CONTRIBUTORS, &CDlgAbout::OnBnClickedContributors) + ON_BN_CLICKED(ID_TRANSLATIONS, &CDlgAbout::OnBnClickedTranslations) //}}AFX_MSG_MAP END_MESSAGE_MAP() diff --git a/CrypTool/DlgAdfgvxIntro.cpp b/CrypTool/DlgAdfgvxIntro.cpp index 3c89aa478..d0ec9871b 100644 --- a/CrypTool/DlgAdfgvxIntro.cpp +++ b/CrypTool/DlgAdfgvxIntro.cpp @@ -24,7 +24,7 @@ #include "stdafx.h" #include "CrypToolApp.h" #include "DlgAdfgvxIntro.h" -#include ".\dlgadfgvxintro.h" +#include "DlgAdfgvxIntro.h" #include "CrypToolTools.h" @@ -49,7 +49,7 @@ void DlgAdfgvxIntro::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(DlgAdfgvxIntro, CDialog) - ON_BN_CLICKED(IDC_CHECK1, OnBnClickedCheck1) + ON_BN_CLICKED(IDC_CHECK1, &DlgAdfgvxIntro::OnBnClickedCheck1) END_MESSAGE_MAP() diff --git a/CrypTool/DlgAdfgvxManual.cpp b/CrypTool/DlgAdfgvxManual.cpp index 364da4017..cbd0e0133 100644 --- a/CrypTool/DlgAdfgvxManual.cpp +++ b/CrypTool/DlgAdfgvxManual.cpp @@ -24,7 +24,7 @@ #include "stdafx.h" #include "CrypToolApp.h" #include "DlgAdfgvxManual.h" -#include ".\dlgadfgvxmanual.h" +#include "DlgAdfgvxManual.h" #include "DlgAdfgvxIntro.h" #include "DlgAdfgvxStringBox.h" #include "FileTools.h" @@ -217,54 +217,54 @@ void CDlgAdfgvxManual::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgAdfgvxManual, CDialog) //{{AFX_MSG_MAP(CDlgAdfgvxManual) - ON_BN_CLICKED(IDC_NEXT_PERMUTATION, OnBnClickedNextPermutation) - ON_EN_CHANGE(IDC_M00, OnEnChangeM00) - ON_EN_CHANGE(IDC_M01, OnEnChangeM01) - ON_EN_CHANGE(IDC_M02, OnEnChangeM02) - ON_EN_CHANGE(IDC_M03, OnEnChangeM03) - ON_EN_CHANGE(IDC_M04, OnEnChangeM04) - ON_EN_CHANGE(IDC_M05, OnEnChangeM05) - ON_EN_CHANGE(IDC_M10, OnEnChangeM10) - ON_EN_CHANGE(IDC_M11, OnEnChangeM11) - ON_EN_CHANGE(IDC_M12, OnEnChangeM12) - ON_EN_CHANGE(IDC_M13, OnEnChangeM13) - ON_EN_CHANGE(IDC_M14, OnEnChangeM14) - ON_EN_CHANGE(IDC_M15, OnEnChangeM15) - ON_EN_CHANGE(IDC_M20, OnEnChangeM20) - ON_EN_CHANGE(IDC_M21, OnEnChangeM21) - ON_EN_CHANGE(IDC_M22, OnEnChangeM22) - ON_EN_CHANGE(IDC_M23, OnEnChangeM23) - ON_EN_CHANGE(IDC_M24, OnEnChangeM24) - ON_EN_CHANGE(IDC_M25, OnEnChangeM25) - ON_EN_CHANGE(IDC_M30, OnEnChangeM30) - ON_EN_CHANGE(IDC_M31, OnEnChangeM31) - ON_EN_CHANGE(IDC_M32, OnEnChangeM32) - ON_EN_CHANGE(IDC_M33, OnEnChangeM33) - ON_EN_CHANGE(IDC_M34, OnEnChangeM34) - ON_EN_CHANGE(IDC_M35, OnEnChangeM35) - ON_EN_CHANGE(IDC_M40, OnEnChangeM40) - ON_EN_CHANGE(IDC_M41, OnEnChangeM41) - ON_EN_CHANGE(IDC_M42, OnEnChangeM42) - ON_EN_CHANGE(IDC_M43, OnEnChangeM43) - ON_EN_CHANGE(IDC_M44, OnEnChangeM44) - ON_EN_CHANGE(IDC_M45, OnEnChangeM45) - ON_EN_CHANGE(IDC_M50, OnEnChangeM50) - ON_EN_CHANGE(IDC_M51, OnEnChangeM51) - ON_EN_CHANGE(IDC_M52, OnEnChangeM52) - ON_EN_CHANGE(IDC_M53, OnEnChangeM53) - ON_EN_CHANGE(IDC_M54, OnEnChangeM54) - ON_EN_CHANGE(IDC_M55, OnEnChangeM55) - ON_BN_CLICKED(IDOK, OnBnClickedOk) - ON_CBN_SELCHANGE(IDC_MAXLENGTH, OnCbnSelchangeMaxlength) - ON_CBN_SELCHANGE(IDC_MINLENGTH, OnCbnSelchangeMinlength) - ON_BN_CLICKED(IDC_BUTTON_FORCE, OnBnClickedButtonForce) - ON_BN_CLICKED(IDC_RESET_MATRIX, OnBnClickedResetMatrix) + ON_BN_CLICKED(IDC_NEXT_PERMUTATION, &CDlgAdfgvxManual::OnBnClickedNextPermutation) + ON_EN_CHANGE(IDC_M00, &CDlgAdfgvxManual::OnEnChangeM00) + ON_EN_CHANGE(IDC_M01, &CDlgAdfgvxManual::OnEnChangeM01) + ON_EN_CHANGE(IDC_M02, &CDlgAdfgvxManual::OnEnChangeM02) + ON_EN_CHANGE(IDC_M03, &CDlgAdfgvxManual::OnEnChangeM03) + ON_EN_CHANGE(IDC_M04, &CDlgAdfgvxManual::OnEnChangeM04) + ON_EN_CHANGE(IDC_M05, &CDlgAdfgvxManual::OnEnChangeM05) + ON_EN_CHANGE(IDC_M10, &CDlgAdfgvxManual::OnEnChangeM10) + ON_EN_CHANGE(IDC_M11, &CDlgAdfgvxManual::OnEnChangeM11) + ON_EN_CHANGE(IDC_M12, &CDlgAdfgvxManual::OnEnChangeM12) + ON_EN_CHANGE(IDC_M13, &CDlgAdfgvxManual::OnEnChangeM13) + ON_EN_CHANGE(IDC_M14, &CDlgAdfgvxManual::OnEnChangeM14) + ON_EN_CHANGE(IDC_M15, &CDlgAdfgvxManual::OnEnChangeM15) + ON_EN_CHANGE(IDC_M20, &CDlgAdfgvxManual::OnEnChangeM20) + ON_EN_CHANGE(IDC_M21, &CDlgAdfgvxManual::OnEnChangeM21) + ON_EN_CHANGE(IDC_M22, &CDlgAdfgvxManual::OnEnChangeM22) + ON_EN_CHANGE(IDC_M23, &CDlgAdfgvxManual::OnEnChangeM23) + ON_EN_CHANGE(IDC_M24, &CDlgAdfgvxManual::OnEnChangeM24) + ON_EN_CHANGE(IDC_M25, &CDlgAdfgvxManual::OnEnChangeM25) + ON_EN_CHANGE(IDC_M30, &CDlgAdfgvxManual::OnEnChangeM30) + ON_EN_CHANGE(IDC_M31, &CDlgAdfgvxManual::OnEnChangeM31) + ON_EN_CHANGE(IDC_M32, &CDlgAdfgvxManual::OnEnChangeM32) + ON_EN_CHANGE(IDC_M33, &CDlgAdfgvxManual::OnEnChangeM33) + ON_EN_CHANGE(IDC_M34, &CDlgAdfgvxManual::OnEnChangeM34) + ON_EN_CHANGE(IDC_M35, &CDlgAdfgvxManual::OnEnChangeM35) + ON_EN_CHANGE(IDC_M40, &CDlgAdfgvxManual::OnEnChangeM40) + ON_EN_CHANGE(IDC_M41, &CDlgAdfgvxManual::OnEnChangeM41) + ON_EN_CHANGE(IDC_M42, &CDlgAdfgvxManual::OnEnChangeM42) + ON_EN_CHANGE(IDC_M43, &CDlgAdfgvxManual::OnEnChangeM43) + ON_EN_CHANGE(IDC_M44, &CDlgAdfgvxManual::OnEnChangeM44) + ON_EN_CHANGE(IDC_M45, &CDlgAdfgvxManual::OnEnChangeM45) + ON_EN_CHANGE(IDC_M50, &CDlgAdfgvxManual::OnEnChangeM50) + ON_EN_CHANGE(IDC_M51, &CDlgAdfgvxManual::OnEnChangeM51) + ON_EN_CHANGE(IDC_M52, &CDlgAdfgvxManual::OnEnChangeM52) + ON_EN_CHANGE(IDC_M53, &CDlgAdfgvxManual::OnEnChangeM53) + ON_EN_CHANGE(IDC_M54, &CDlgAdfgvxManual::OnEnChangeM54) + ON_EN_CHANGE(IDC_M55, &CDlgAdfgvxManual::OnEnChangeM55) + ON_BN_CLICKED(IDOK, &CDlgAdfgvxManual::OnBnClickedOk) + ON_CBN_SELCHANGE(IDC_MAXLENGTH, &CDlgAdfgvxManual::OnCbnSelchangeMaxlength) + ON_CBN_SELCHANGE(IDC_MINLENGTH, &CDlgAdfgvxManual::OnCbnSelchangeMinlength) + ON_BN_CLICKED(IDC_BUTTON_FORCE, &CDlgAdfgvxManual::OnBnClickedButtonForce) + ON_BN_CLICKED(IDC_RESET_MATRIX, &CDlgAdfgvxManual::OnBnClickedResetMatrix) //}}AFX_MSG_MAP - ON_BN_CLICKED(IDC_MATRIX_STANDARD, OnBnClickedMatrixStandard) - ON_BN_CLICKED(IDC_BUTTON_STRINGBOX, OnBnClickedButtonStringbox) - ON_EN_CHANGE(IDC_EDIT_PASSWORD, OnChangedTranspositionPassword) - ON_BN_CLICKED(IDC_BUTTON_TEXT_OPTIONS, OnBnClickedButtonTextOptions) - ON_BN_CLICKED(IDC_INSERT_KEY, OnBnClickedInsertKey) + ON_BN_CLICKED(IDC_MATRIX_STANDARD, &CDlgAdfgvxManual::OnBnClickedMatrixStandard) + ON_BN_CLICKED(IDC_BUTTON_STRINGBOX, &CDlgAdfgvxManual::OnBnClickedButtonStringbox) + ON_EN_CHANGE(IDC_EDIT_PASSWORD, &CDlgAdfgvxManual::OnChangedTranspositionPassword) + ON_BN_CLICKED(IDC_BUTTON_TEXT_OPTIONS, &CDlgAdfgvxManual::OnBnClickedButtonTextOptions) + ON_BN_CLICKED(IDC_INSERT_KEY, &CDlgAdfgvxManual::OnBnClickedInsertKey) END_MESSAGE_MAP() diff --git a/CrypTool/DlgAdfgvxManual.h b/CrypTool/DlgAdfgvxManual.h index bd434284b..998583b25 100644 --- a/CrypTool/DlgAdfgvxManual.h +++ b/CrypTool/DlgAdfgvxManual.h @@ -23,7 +23,7 @@ #include "adfgvx.h" #include "afxwin.h" #include -#include +#include "AlphabetTextEdit.h" #include "ToolTipButton.h" // CDlgAdfgvxManual-Dialogfeld @@ -34,7 +34,7 @@ class CDlgAdfgvxManual : public CDialog DECLARE_DYNAMIC(CDlgAdfgvxManual) public: - CDlgAdfgvxManual::CDlgAdfgvxManual(char* infile, CString oldTitle, CWnd* pParent); + CDlgAdfgvxManual(char* infile, CString oldTitle, CWnd* pParent); //CDlgAdfgvxManual(CWnd* pParent = NULL); // Standardkonstruktor virtual ~CDlgAdfgvxManual(); @@ -119,7 +119,7 @@ class CDlgAdfgvxManual : public CDialog CString oldTitle; CString oldEntry; basic_string ::iterator strIterTyp; - CString CDlgAdfgvxManual::CheckInput(CString oldEntry, CString input); + CString CheckInput(CString oldEntry, CString input); CString numberedPassword; bool analysed; int validCiphertext; @@ -136,8 +136,8 @@ class CDlgAdfgvxManual : public CDialog CAlphabetTextEdit editTranspositionPassword; public: - BOOL CDlgAdfgvxManual::OnInitDialog(); - void CDlgAdfgvxManual::Permutate(); + BOOL OnInitDialog(); + void Permutate(); afx_msg void OnBnClickedButtonStringbox(); diff --git a/CrypTool/DlgAdfgvxStringBox.cpp b/CrypTool/DlgAdfgvxStringBox.cpp index cc9a207cc..99d0399fc 100644 --- a/CrypTool/DlgAdfgvxStringBox.cpp +++ b/CrypTool/DlgAdfgvxStringBox.cpp @@ -24,7 +24,7 @@ #include "stdafx.h" #include "CrypToolApp.h" #include "DlgAdfgvxStringBox.h" -#include ".\dlgadfgvxstringbox.h" +#include "DlgAdfgvxStringBox.h" // DlgAdfgvxStringBox-Dialogfeld @@ -49,7 +49,7 @@ void DlgAdfgvxStringBox::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(DlgAdfgvxStringBox, CDialog) - ON_BN_CLICKED(IDOK, OnBnClickedOk) + ON_BN_CLICKED(IDOK, &DlgAdfgvxStringBox::OnBnClickedOk) END_MESSAGE_MAP() BOOL DlgAdfgvxStringBox::OnInitDialog() diff --git a/CrypTool/DlgAdfgvxStringBox.h b/CrypTool/DlgAdfgvxStringBox.h index 814806c02..2605e55c0 100644 --- a/CrypTool/DlgAdfgvxStringBox.h +++ b/CrypTool/DlgAdfgvxStringBox.h @@ -45,6 +45,6 @@ class DlgAdfgvxStringBox : public CDialog private: CString input; - void DlgAdfgvxStringBox::OnOK(); - BOOL DlgAdfgvxStringBox::OnInitDialog(); + void OnOK(); + BOOL OnInitDialog(); }; diff --git a/CrypTool/DlgAutoSubstitutionAnalysis.cpp b/CrypTool/DlgAutoSubstitutionAnalysis.cpp index 3ef3f89bf..30b960621 100644 --- a/CrypTool/DlgAutoSubstitutionAnalysis.cpp +++ b/CrypTool/DlgAutoSubstitutionAnalysis.cpp @@ -22,7 +22,7 @@ // #include "stdafx.h" -#include "cryptoolapp.h" +#include "CrypToolApp.h" #include "DlgAutoSubstitutionAnalysis.h" #ifdef _DEBUG @@ -72,10 +72,10 @@ void CDlgAutoSubstitutionAnalysis::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgAutoSubstitutionAnalysis, CDialog) //{{AFX_MSG_MAP(CDlgAutoSubstitutionAnalysis) - ON_BN_CLICKED(ID_SUBST_Cancel, OnSUBSTCancel) + ON_BN_CLICKED(ID_SUBST_Cancel, &CDlgAutoSubstitutionAnalysis::OnSUBSTCancel) ON_WM_TIMER() - ON_BN_CLICKED(IDC_COPY, OnKeyCopy) - ON_BN_CLICKED(IDC_MANUAL, OnManualAnalysis) + ON_BN_CLICKED(IDC_COPY, &CDlgAutoSubstitutionAnalysis::OnKeyCopy) + ON_BN_CLICKED(IDC_MANUAL, &CDlgAutoSubstitutionAnalysis::OnManualAnalysis) //}}AFX_MSG_MAP END_MESSAGE_MAP() diff --git a/CrypTool/DlgAutoSubstitutionAnalysis.h b/CrypTool/DlgAutoSubstitutionAnalysis.h index fc36744bd..574ff2904 100644 --- a/CrypTool/DlgAutoSubstitutionAnalysis.h +++ b/CrypTool/DlgAutoSubstitutionAnalysis.h @@ -36,21 +36,21 @@ class CDlgAutoSubstitutionAnalysis : public CDialog public: CDlgAutoSubstitutionAnalysis(CWnd* pParent = NULL); // Standardkonstruktor - void CDlgAutoSubstitutionAnalysis::setText(char *text); - CString CDlgAutoSubstitutionAnalysis::getText(); - void CDlgAutoSubstitutionAnalysis::setLanguageInfo(char *text); - void CDlgAutoSubstitutionAnalysis::setKey(char *text); - CString CDlgAutoSubstitutionAnalysis::getKey(); - void CDlgAutoSubstitutionAnalysis::setCharacterNumber(char *text); - void CDlgAutoSubstitutionAnalysis::setSpaceInAlphabet(bool value); - - void CDlgAutoSubstitutionAnalysis::setThreadFinished(bool value); - bool CDlgAutoSubstitutionAnalysis::getThreadFinished(); - void CDlgAutoSubstitutionAnalysis::setGuiFinished(bool value); - bool CDlgAutoSubstitutionAnalysis::getGuiFinished(); - void CDlgAutoSubstitutionAnalysis::setThreadFailed(bool value); - bool CDlgAutoSubstitutionAnalysis::getThreadFailed(); - bool CDlgAutoSubstitutionAnalysis::getManualAnalysis(); + void setText(char *text); + CString getText(); + void setLanguageInfo(char *text); + void setKey(char *text); + CString getKey(); + void setCharacterNumber(char *text); + void setSpaceInAlphabet(bool value); + + void setThreadFinished(bool value); + bool getThreadFinished(); + void setGuiFinished(bool value); + bool getGuiFinished(); + void setThreadFailed(bool value); + bool getThreadFailed(); + bool getManualAnalysis(); // Dialogfelddaten diff --git a/CrypTool/DlgAutoSubstitutionAnalysisAlgorithmChoice.cpp b/CrypTool/DlgAutoSubstitutionAnalysisAlgorithmChoice.cpp index 2484ce0ff..e46999d21 100644 --- a/CrypTool/DlgAutoSubstitutionAnalysisAlgorithmChoice.cpp +++ b/CrypTool/DlgAutoSubstitutionAnalysisAlgorithmChoice.cpp @@ -22,7 +22,7 @@ // #include "stdafx.h" -#include "cryptoolapp.h" +#include "CrypToolApp.h" #include "DlgAutoSubstitutionAnalysisAlgorithmChoice.h" #ifdef _DEBUG @@ -54,8 +54,8 @@ void CDlgAutoSubstitutionAnalysisAlgorithmChoice::DoDataExchange(CDataExchange* BEGIN_MESSAGE_MAP(CDlgAutoSubstitutionAnalysisAlgorithmChoice, CDialog) //{{AFX_MSG_MAP(CDlgAutoSubstitutionAnalysisAlgorithmChoice) - ON_BN_CLICKED(IDC_ALG_DIGRAM, OnAlgDigram) - ON_BN_CLICKED(IDC_ALG_WORD, OnAlgWord) + ON_BN_CLICKED(IDC_ALG_DIGRAM, &CDlgAutoSubstitutionAnalysisAlgorithmChoice::OnAlgDigram) + ON_BN_CLICKED(IDC_ALG_WORD, &CDlgAutoSubstitutionAnalysisAlgorithmChoice::OnAlgWord) //}}AFX_MSG_MAP END_MESSAGE_MAP() @@ -74,7 +74,7 @@ void CDlgAutoSubstitutionAnalysisAlgorithmChoice::OnAlgWord() CheckRadioButton (IDC_ALG_DIGRAM, IDC_ALG_WORD, IDC_ALG_WORD); } -int CDlgAutoSubstitutionAnalysisAlgorithmChoice::DoModal() +INT_PTR CDlgAutoSubstitutionAnalysisAlgorithmChoice::DoModal() { return CDialog::DoModal(); } diff --git a/CrypTool/DlgAutoSubstitutionAnalysisAlgorithmChoice.h b/CrypTool/DlgAutoSubstitutionAnalysisAlgorithmChoice.h index e32cb2bf1..3cda08074 100644 --- a/CrypTool/DlgAutoSubstitutionAnalysisAlgorithmChoice.h +++ b/CrypTool/DlgAutoSubstitutionAnalysisAlgorithmChoice.h @@ -48,7 +48,7 @@ class CDlgAutoSubstitutionAnalysisAlgorithmChoice : public CDialog // Vom Klassen-Assistenten generierte virtuelle Funktionsüberschreibungen //{{AFX_VIRTUAL(CDlgAutoSubstitutionAnalysisAlgorithmChoice) public: - virtual int DoModal(); + virtual INT_PTR DoModal(); protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV-Unterstützung //}}AFX_VIRTUAL diff --git a/CrypTool/DlgBruteForceAES.cpp b/CrypTool/DlgBruteForceAES.cpp index 15da71da2..f79451a77 100644 --- a/CrypTool/DlgBruteForceAES.cpp +++ b/CrypTool/DlgBruteForceAES.cpp @@ -29,7 +29,7 @@ #include "Cryptography.h" #include "HexEdit.h" #include "DlgBruteForceAES.h" -#include ".\dlgbruteforceaes.h" +#include "DlgBruteForceAES.h" // nibble i contains a parity bit if m_parity == CRYPT_PARITY_DES*: in low nibble (i & 1) and for DESX* in the first 64 bit only #define parity_in_nibble(i) (m_parity_check && (i & 1) && (m_parity_check != CRYPT_PARITY_DESX || i < 64/4)) @@ -67,11 +67,11 @@ void CDlgBruteForceAES::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgBruteForceAES, CDialog) //{{AFX_MSG_MAP(CDlgBruteForceAES) - ON_EN_UPDATE(IDC_EDIT1, OnUpdate) - ON_CBN_SELCHANGE(IDC_KEY_LEN, OnSelchangeKeyLen) - ON_EN_SETFOCUS(IDC_EDIT1, OnSetfocusHexEdit) + ON_EN_UPDATE(IDC_EDIT1, &CDlgBruteForceAES::OnUpdate) + ON_CBN_SELCHANGE(IDC_KEY_LEN, &CDlgBruteForceAES::OnSelchangeKeyLen) + ON_EN_SETFOCUS(IDC_EDIT1, &CDlgBruteForceAES::OnSetfocusHexEdit) //}}AFX_MSG_MAP - ON_BN_CLICKED(IDOK2, OnBnClickedOk2) + ON_BN_CLICKED(IDOK2, &CDlgBruteForceAES::OnBnClickedOk2) END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// diff --git a/CrypTool/DlgCaesarAnalysis.cpp b/CrypTool/DlgCaesarAnalysis.cpp index 9db95a338..3cd253843 100644 --- a/CrypTool/DlgCaesarAnalysis.cpp +++ b/CrypTool/DlgCaesarAnalysis.cpp @@ -61,7 +61,7 @@ void CDlgCaesarAnalysis::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgCaesarAnalysis, CDialog) //{{AFX_MSG_MAP(CDlgCaesarAnalysis) - ON_EN_UPDATE(IDC_EDIT1, OnUpdateEdit1) + ON_EN_UPDATE(IDC_EDIT1, &CDlgCaesarAnalysis::OnUpdateEdit1) //}}AFX_MSG_MAP END_MESSAGE_MAP() diff --git a/CrypTool/DlgCertificateGeneration.cpp b/CrypTool/DlgCertificateGeneration.cpp index 726a143cc..4d59e44e1 100644 --- a/CrypTool/DlgCertificateGeneration.cpp +++ b/CrypTool/DlgCertificateGeneration.cpp @@ -81,10 +81,10 @@ void CDlgCertificateGeneration::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgCertificateGeneration, CDialog) //{{AFX_MSG_MAP(CDlgCertificateGeneration) - ON_EN_CHANGE(IDC_EDIT_CERTNAME, OnChangeEdit) - ON_BN_CLICKED(IDC_PSE_IMPORT, OnPseImport) - ON_EN_CHANGE(IDC_EDIT_CERT_FIRSTNAME, OnChangeEdit) - ON_EN_CHANGE(IDC_EDIT_CERT_KEY_ID, OnChangeEdit) + ON_EN_CHANGE(IDC_EDIT_CERTNAME, &CDlgCertificateGeneration::OnChangeEdit) + ON_BN_CLICKED(IDC_PSE_IMPORT, &CDlgCertificateGeneration::OnPseImport) + ON_EN_CHANGE(IDC_EDIT_CERT_FIRSTNAME, &CDlgCertificateGeneration::OnChangeEdit) + ON_EN_CHANGE(IDC_EDIT_CERT_KEY_ID, &CDlgCertificateGeneration::OnChangeEdit) //}}AFX_MSG_MAP END_MESSAGE_MAP() diff --git a/CrypTool/DlgCrtAstronomy.cpp b/CrypTool/DlgCrtAstronomy.cpp index 99bc3d359..b0444c9fe 100644 --- a/CrypTool/DlgCrtAstronomy.cpp +++ b/CrypTool/DlgCrtAstronomy.cpp @@ -23,7 +23,7 @@ #include "stdafx.h" #include "CrypToolApp.h" #include "DlgCrtAstronomy.h" -#include ".\dlgcrtastronomy.h" +#include "DlgCrtAstronomy.h" #include "big.h" #include "crt.h" #include "IntegerArithmetic.h" @@ -120,38 +120,38 @@ void CDlgCrtAstronomy::DoDataExchange(CDataExchange* pDX) } BEGIN_MESSAGE_MAP(CDlgCrtAstronomy, CDialog) - ON_BN_CLICKED(IDC_CRT_ASTRONOMY_BUTTONSOLVE, OnBnClickedCrtAstronomyButtonsolve) - ON_BN_CLICKED(IDC_CRT_ASTRONOMY_BUTTONRESET, OnBnClickedCrtAstronomyButtonreset) - ON_BN_CLICKED(IDC_CRT_ASTRONOMY_BUTTONEND, OnBnClickedCrtAstronomyButtonend) - ON_EN_CHANGE(IDC_CRT_ASTRONOMY_EDITA0, OnEnChangeCrtAstronomyEdita0) - ON_EN_CHANGE(IDC_CRT_ASTRONOMY_EDITA1, OnEnChangeCrtAstronomyEdita1) - ON_EN_CHANGE(IDC_CRT_ASTRONOMY_EDITA2, OnEnChangeCrtAstronomyEdita2) - ON_EN_CHANGE(IDC_CRT_ASTRONOMY_EDITA3, OnEnChangeCrtAstronomyEdita3) - ON_EN_CHANGE(IDC_CRT_ASTRONOMY_EDITA4, OnEnChangeCrtAstronomyEdita4) - ON_EN_CHANGE(IDC_CRT_ASTRONOMY_EDITA5, OnEnChangeCrtAstronomyEdita5) - ON_EN_CHANGE(IDC_CRT_ASTRONOMY_EDITA6, OnEnChangeCrtAstronomyEdita6) - ON_EN_CHANGE(IDC_CRT_ASTRONOMY_EDITA7, OnEnChangeCrtAstronomyEdita7) - ON_EN_CHANGE(IDC_CRT_ASTRONOMY_EDITA8, OnEnChangeCrtAstronomyEdita8) - ON_EN_CHANGE(IDC_CRT_ASTRONOMY_EDITM0, OnEnChangeCrtAstronomyEditm0) - ON_EN_CHANGE(IDC_CRT_ASTRONOMY_EDITM1, OnEnChangeCrtAstronomyEditm1) - ON_EN_CHANGE(IDC_CRT_ASTRONOMY_EDITM2, OnEnChangeCrtAstronomyEditm2) - ON_EN_CHANGE(IDC_CRT_ASTRONOMY_EDITM3, OnEnChangeCrtAstronomyEditm3) - ON_EN_CHANGE(IDC_CRT_ASTRONOMY_EDITM4, OnEnChangeCrtAstronomyEditm4) - ON_EN_CHANGE(IDC_CRT_ASTRONOMY_EDITM5, OnEnChangeCrtAstronomyEditm5) - ON_EN_CHANGE(IDC_CRT_ASTRONOMY_EDITM6, OnEnChangeCrtAstronomyEditm6) - ON_EN_CHANGE(IDC_CRT_ASTRONOMY_EDITM7, OnEnChangeCrtAstronomyEditm7) - ON_EN_CHANGE(IDC_CRT_ASTRONOMY_EDITM8, OnEnChangeCrtAstronomyEditm8) - ON_BN_CLICKED(IDC_CRT_ASTRONOMY_CHECKMERCURY, OnBnClickedCrtAstronomyCheckmercury) - ON_BN_CLICKED(IDC_CRT_ASTRONOMY_CHECKVENUS, OnBnClickedCrtAstronomyCheckvenus) - ON_BN_CLICKED(IDC_CRT_ASTRONOMY_CHECKEARTH, OnBnClickedCrtAstronomyCheckearth) - ON_BN_CLICKED(IDC_CRT_ASTRONOMY_CHECKMARS, OnBnClickedCrtAstronomyCheckmars) - ON_BN_CLICKED(IDC_CRT_ASTRONOMY_CHECKJUPITER, OnBnClickedCrtAstronomyCheckjupiter) - ON_BN_CLICKED(IDC_CRT_ASTRONOMY_CHECKSATURN, OnBnClickedCrtAstronomyChecksaturn) - ON_BN_CLICKED(IDC_CRT_ASTRONOMY_CHECKURANUS, OnBnClickedCrtAstronomyCheckuranus) - ON_BN_CLICKED(IDC_CRT_ASTRONOMY_CHECKNEPTUN, OnBnClickedCrtAstronomyCheckneptun) - ON_BN_CLICKED(IDC_CRT_ASTRONOMY_CHECKPLUTO, OnBnClickedCrtAstronomyCheckpluto) - ON_BN_CLICKED(IDC_CRT_ASTRONOMY_BUTTONRESET2, OnBnClickedCrtAstronomyButtonreset2) - ON_STN_CLICKED(IDC_Image, OnStnClickedImage) + ON_BN_CLICKED(IDC_CRT_ASTRONOMY_BUTTONSOLVE, &CDlgCrtAstronomy::OnBnClickedCrtAstronomyButtonsolve) + ON_BN_CLICKED(IDC_CRT_ASTRONOMY_BUTTONRESET, &CDlgCrtAstronomy::OnBnClickedCrtAstronomyButtonreset) + ON_BN_CLICKED(IDC_CRT_ASTRONOMY_BUTTONEND, &CDlgCrtAstronomy::OnBnClickedCrtAstronomyButtonend) + ON_EN_CHANGE(IDC_CRT_ASTRONOMY_EDITA0, &CDlgCrtAstronomy::OnEnChangeCrtAstronomyEdita0) + ON_EN_CHANGE(IDC_CRT_ASTRONOMY_EDITA1, &CDlgCrtAstronomy::OnEnChangeCrtAstronomyEdita1) + ON_EN_CHANGE(IDC_CRT_ASTRONOMY_EDITA2, &CDlgCrtAstronomy::OnEnChangeCrtAstronomyEdita2) + ON_EN_CHANGE(IDC_CRT_ASTRONOMY_EDITA3, &CDlgCrtAstronomy::OnEnChangeCrtAstronomyEdita3) + ON_EN_CHANGE(IDC_CRT_ASTRONOMY_EDITA4, &CDlgCrtAstronomy::OnEnChangeCrtAstronomyEdita4) + ON_EN_CHANGE(IDC_CRT_ASTRONOMY_EDITA5, &CDlgCrtAstronomy::OnEnChangeCrtAstronomyEdita5) + ON_EN_CHANGE(IDC_CRT_ASTRONOMY_EDITA6, &CDlgCrtAstronomy::OnEnChangeCrtAstronomyEdita6) + ON_EN_CHANGE(IDC_CRT_ASTRONOMY_EDITA7, &CDlgCrtAstronomy::OnEnChangeCrtAstronomyEdita7) + ON_EN_CHANGE(IDC_CRT_ASTRONOMY_EDITA8, &CDlgCrtAstronomy::OnEnChangeCrtAstronomyEdita8) + ON_EN_CHANGE(IDC_CRT_ASTRONOMY_EDITM0, &CDlgCrtAstronomy::OnEnChangeCrtAstronomyEditm0) + ON_EN_CHANGE(IDC_CRT_ASTRONOMY_EDITM1, &CDlgCrtAstronomy::OnEnChangeCrtAstronomyEditm1) + ON_EN_CHANGE(IDC_CRT_ASTRONOMY_EDITM2, &CDlgCrtAstronomy::OnEnChangeCrtAstronomyEditm2) + ON_EN_CHANGE(IDC_CRT_ASTRONOMY_EDITM3, &CDlgCrtAstronomy::OnEnChangeCrtAstronomyEditm3) + ON_EN_CHANGE(IDC_CRT_ASTRONOMY_EDITM4, &CDlgCrtAstronomy::OnEnChangeCrtAstronomyEditm4) + ON_EN_CHANGE(IDC_CRT_ASTRONOMY_EDITM5, &CDlgCrtAstronomy::OnEnChangeCrtAstronomyEditm5) + ON_EN_CHANGE(IDC_CRT_ASTRONOMY_EDITM6, &CDlgCrtAstronomy::OnEnChangeCrtAstronomyEditm6) + ON_EN_CHANGE(IDC_CRT_ASTRONOMY_EDITM7, &CDlgCrtAstronomy::OnEnChangeCrtAstronomyEditm7) + ON_EN_CHANGE(IDC_CRT_ASTRONOMY_EDITM8, &CDlgCrtAstronomy::OnEnChangeCrtAstronomyEditm8) + ON_BN_CLICKED(IDC_CRT_ASTRONOMY_CHECKMERCURY, &CDlgCrtAstronomy::OnBnClickedCrtAstronomyCheckmercury) + ON_BN_CLICKED(IDC_CRT_ASTRONOMY_CHECKVENUS, &CDlgCrtAstronomy::OnBnClickedCrtAstronomyCheckvenus) + ON_BN_CLICKED(IDC_CRT_ASTRONOMY_CHECKEARTH, &CDlgCrtAstronomy::OnBnClickedCrtAstronomyCheckearth) + ON_BN_CLICKED(IDC_CRT_ASTRONOMY_CHECKMARS, &CDlgCrtAstronomy::OnBnClickedCrtAstronomyCheckmars) + ON_BN_CLICKED(IDC_CRT_ASTRONOMY_CHECKJUPITER, &CDlgCrtAstronomy::OnBnClickedCrtAstronomyCheckjupiter) + ON_BN_CLICKED(IDC_CRT_ASTRONOMY_CHECKSATURN, &CDlgCrtAstronomy::OnBnClickedCrtAstronomyChecksaturn) + ON_BN_CLICKED(IDC_CRT_ASTRONOMY_CHECKURANUS, &CDlgCrtAstronomy::OnBnClickedCrtAstronomyCheckuranus) + ON_BN_CLICKED(IDC_CRT_ASTRONOMY_CHECKNEPTUN, &CDlgCrtAstronomy::OnBnClickedCrtAstronomyCheckneptun) + ON_BN_CLICKED(IDC_CRT_ASTRONOMY_CHECKPLUTO, &CDlgCrtAstronomy::OnBnClickedCrtAstronomyCheckpluto) + ON_BN_CLICKED(IDC_CRT_ASTRONOMY_BUTTONRESET2, &CDlgCrtAstronomy::OnBnClickedCrtAstronomyButtonreset2) + ON_STN_CLICKED(IDC_Image, &CDlgCrtAstronomy::OnStnClickedImage) END_MESSAGE_MAP() diff --git a/CrypTool/DlgCrtSecretSharing.cpp b/CrypTool/DlgCrtSecretSharing.cpp index 017265685..347fc81a5 100644 --- a/CrypTool/DlgCrtSecretSharing.cpp +++ b/CrypTool/DlgCrtSecretSharing.cpp @@ -25,7 +25,7 @@ #include "stdlib.h" #include "CrypToolApp.h" #include "DlgCrtSecretSharing.h" -#include ".\dlgcrtsecretsharing.h" +#include "DlgCrtSecretSharing.h" #include "DlgCrtSecretSharingOptions.h" #include "DlgCrtSecretSharing_Facts.h" #include "DlgCrtSecretSharing_Log.h" @@ -121,23 +121,23 @@ void CDlgCrtSecretSharing::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgCrtSecretSharing, CDialog) - ON_BN_CLICKED(IDC_SECRETSHARING_BUTTON1, OnBnClickedSecretsharingShare) - ON_BN_CLICKED(IDC_SECRETSHARING_BUTTON2, OnBnClickedSecretsharingReset) - ON_BN_CLICKED(IDC_BUTTON1, OnBnClickedSecretsharingSolve) - ON_BN_CLICKED(IDC_SECRETSHARING_ADD1, OnBnClickedSecretsharingAdd1) - ON_BN_CLICKED(IDC_SECRETSHARING_ADD2, OnBnClickedSecretsharingAdd2) - ON_BN_CLICKED(IDC_SECRETSHARING_ADD3, OnBnClickedSecretsharingAdd3) - ON_BN_CLICKED(IDC_SECRETSHARING_ADD4, OnBnClickedSecretsharingAdd4) - ON_BN_CLICKED(IDC_SECRETSHARING_ADD5, OnBnClickedSecretsharingAdd5) - ON_BN_CLICKED(IDC_SECRETSHARING_ADD6, OnBnClickedSecretsharingAdd6) - ON_BN_CLICKED(IDC_SECRETSHARING_ADD7, OnBnClickedSecretsharingAdd7) - ON_BN_CLICKED(IDC_SECRETSHARING_ALGO, OnBnClickedSecretsharingEnd) - ON_BN_CLICKED(IDC_BUTTON4, OnBnClickedSecretsharingOptions) - ON_BN_CLICKED(IDC_SECRETSHARING_FACTS, OnBnClickedSecretsharingFacts) - ON_BN_CLICKED(IDC_SECRETSHARING_LOG, OnBnClickedSecretsharingLog) - ON_BN_CLICKED(IDOK, OnBnClickedOk) - ON_BN_CLICKED(IDC_CRT_Radio_AUT, OnBnClickedCrtRadioAut) - ON_BN_CLICKED(IDC_CRT_RADIO_MAN, OnBnClickedCrtRadioMan) + ON_BN_CLICKED(IDC_SECRETSHARING_BUTTON1, &CDlgCrtSecretSharing::OnBnClickedSecretsharingShare) + ON_BN_CLICKED(IDC_SECRETSHARING_BUTTON2, &CDlgCrtSecretSharing::OnBnClickedSecretsharingReset) + ON_BN_CLICKED(IDC_BUTTON1, &CDlgCrtSecretSharing::OnBnClickedSecretsharingSolve) + ON_BN_CLICKED(IDC_SECRETSHARING_ADD1, &CDlgCrtSecretSharing::OnBnClickedSecretsharingAdd1) + ON_BN_CLICKED(IDC_SECRETSHARING_ADD2, &CDlgCrtSecretSharing::OnBnClickedSecretsharingAdd2) + ON_BN_CLICKED(IDC_SECRETSHARING_ADD3, &CDlgCrtSecretSharing::OnBnClickedSecretsharingAdd3) + ON_BN_CLICKED(IDC_SECRETSHARING_ADD4, &CDlgCrtSecretSharing::OnBnClickedSecretsharingAdd4) + ON_BN_CLICKED(IDC_SECRETSHARING_ADD5, &CDlgCrtSecretSharing::OnBnClickedSecretsharingAdd5) + ON_BN_CLICKED(IDC_SECRETSHARING_ADD6, &CDlgCrtSecretSharing::OnBnClickedSecretsharingAdd6) + ON_BN_CLICKED(IDC_SECRETSHARING_ADD7, &CDlgCrtSecretSharing::OnBnClickedSecretsharingAdd7) + ON_BN_CLICKED(IDC_SECRETSHARING_ALGO, &CDlgCrtSecretSharing::OnBnClickedSecretsharingEnd) + ON_BN_CLICKED(IDC_BUTTON4, &CDlgCrtSecretSharing::OnBnClickedSecretsharingOptions) + ON_BN_CLICKED(IDC_SECRETSHARING_FACTS, &CDlgCrtSecretSharing::OnBnClickedSecretsharingFacts) + ON_BN_CLICKED(IDC_SECRETSHARING_LOG, &CDlgCrtSecretSharing::OnBnClickedSecretsharingLog) + ON_BN_CLICKED(IDOK, &CDlgCrtSecretSharing::OnBnClickedOk) + ON_BN_CLICKED(IDC_CRT_Radio_AUT, &CDlgCrtSecretSharing::OnBnClickedCrtRadioAut) + ON_BN_CLICKED(IDC_CRT_RADIO_MAN, &CDlgCrtSecretSharing::OnBnClickedCrtRadioMan) END_MESSAGE_MAP() // CDlgCrtAstronomy-Meldungshandler diff --git a/CrypTool/DlgCrtSecretSharingOptions.cpp b/CrypTool/DlgCrtSecretSharingOptions.cpp index 7b8867078..f10c00879 100644 --- a/CrypTool/DlgCrtSecretSharingOptions.cpp +++ b/CrypTool/DlgCrtSecretSharingOptions.cpp @@ -24,7 +24,7 @@ #include "stdafx.h" #include "CrypToolApp.h" #include "DlgCrtSecretSharingOptions.h" -#include ".\dlgcrtsecretsharingoptions.h" +#include "DlgCrtSecretSharingOptions.h" #include "DialogeMessage.h" #include "IntegerArithmetic.h" #include "big.h" @@ -59,8 +59,8 @@ void CDlgCrtSecretSharingOptions::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgCrtSecretSharingOptions, CDialog) - ON_BN_CLICKED(IDCANCEL, OnBnClickedCancel) - ON_BN_CLICKED(IDOK, OnBnClickedOk) + ON_BN_CLICKED(IDCANCEL, &CDlgCrtSecretSharingOptions::OnBnClickedCancel) + ON_BN_CLICKED(IDOK, &CDlgCrtSecretSharingOptions::OnBnClickedOk) END_MESSAGE_MAP() // CDlgCrtSecretSharingOptions-Meldungshandler diff --git a/CrypTool/DlgCrtSecretSharing_Facts.cpp b/CrypTool/DlgCrtSecretSharing_Facts.cpp index 3610b8fd0..8e2c4bc79 100644 --- a/CrypTool/DlgCrtSecretSharing_Facts.cpp +++ b/CrypTool/DlgCrtSecretSharing_Facts.cpp @@ -25,7 +25,7 @@ #include "stdlib.h" #include "CrypToolApp.h" #include "DlgCrtSecretSharing_Facts.h" -#include ".\dlgcrtsecretsharing_Facts.h" +#include "DlgCrtSecretSharing_Facts.h" #include "DlgCrtSecretSharing_Log.h" #include "DialogeMessage.h" @@ -66,8 +66,8 @@ void CDlgCrtSecretSharing_Facts::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgCrtSecretSharing_Facts, CDialog) - ON_BN_CLICKED(IDC_CRT_SHOWLOG, OnBnClickedCrtShowlog) - ON_BN_CLICKED(IDOK, OnBnClickedOk) + ON_BN_CLICKED(IDC_CRT_SHOWLOG, &CDlgCrtSecretSharing_Facts::OnBnClickedCrtShowlog) + ON_BN_CLICKED(IDOK, &CDlgCrtSecretSharing_Facts::OnBnClickedOk) END_MESSAGE_MAP() diff --git a/CrypTool/DlgCrtSecretSharing_Info.cpp b/CrypTool/DlgCrtSecretSharing_Info.cpp index 741c65591..1711ab0e1 100644 --- a/CrypTool/DlgCrtSecretSharing_Info.cpp +++ b/CrypTool/DlgCrtSecretSharing_Info.cpp @@ -24,7 +24,7 @@ #include "stdafx.h" #include "CrypToolApp.h" #include "DlgCrtSecretSharing_Info.h" -#include ".\dlgcrtsecretsharing_info.h" +#include "DlgCrtSecretSharing_Info.h" // Aus DH-Intro.cpp eingefügt #ifdef _DEBUG diff --git a/CrypTool/DlgCrtSecretSharing_Log.cpp b/CrypTool/DlgCrtSecretSharing_Log.cpp index 25fc4634a..962140ff1 100644 --- a/CrypTool/DlgCrtSecretSharing_Log.cpp +++ b/CrypTool/DlgCrtSecretSharing_Log.cpp @@ -27,7 +27,7 @@ #include "FileTools.h" #include "CrypToolApp.h" #include "DlgCrtSecretSharing_Log.h" -#include ".\dlgcrtsecretsharing_log.h" +#include "DlgCrtSecretSharing_Log.h" // #include // CDlgCrtSecretSharing_Log-Dialogfeld @@ -51,8 +51,8 @@ void CDlgCrtSecretSharing_Log::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgCrtSecretSharing_Log, baseCTestDlg) - ON_BN_CLICKED(IDC_BUTTON1, OnBnClickedButton1) - ON_BN_CLICKED(IDC_CRT_SecretSharing_Log_Save, OnBnClickedCrtSecretsharingLogSave) + ON_BN_CLICKED(IDC_BUTTON1, &CDlgCrtSecretSharing_Log::OnBnClickedButton1) + ON_BN_CLICKED(IDC_CRT_SecretSharing_Log_Save, &CDlgCrtSecretSharing_Log::OnBnClickedCrtSecretsharingLogSave) END_MESSAGE_MAP() diff --git a/CrypTool/DlgCrtTransformation.cpp b/CrypTool/DlgCrtTransformation.cpp index d0172d900..687ed4be2 100644 --- a/CrypTool/DlgCrtTransformation.cpp +++ b/CrypTool/DlgCrtTransformation.cpp @@ -23,7 +23,7 @@ #include "stdafx.h" #include "CrypToolApp.h" #include "DlgCrtTransformation.h" -#include ".\dlgcrttransformation.h" +#include "DlgCrtTransformation.h" #include "big.h" #include "crt.h" #include "IntegerArithmetic.h" @@ -94,25 +94,25 @@ void CDlgCrtTransformation::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgCrtTransformation, CDialog) - ON_BN_CLICKED(IDC_CRT_TRANSFORMATION_BUTTONSOLVE, OnBnClickedCrtTransformationButtonsolve) - ON_BN_CLICKED(IDC_CRT_TRANSFORMATION_BUTTONRESET2, OnBnClickedCrtTransformationButtonreset) - ON_BN_CLICKED(IDC_CRT_TRANSFORMATION_BUTTONEND2, OnBnClickedCrtTransformationButtonend) - ON_EN_CHANGE(IDC_CRT_TRANSFORMATION_EDITA0, OnEnChangeCrtTransformationEdita0) - ON_EN_CHANGE(IDC_CRT_TRANSFORMATION_EDITA1, OnEnChangeCrtTransformationEdita1) - ON_EN_CHANGE(IDC_CRT_TRANSFORMATION_EDITM0, OnEnChangeCrtTransformationEditm0) - ON_EN_CHANGE(IDC_CRT_TRANSFORMATION_EDITM1, OnEnChangeCrtTransformationEditm1) - ON_EN_CHANGE(IDC_CRT_TRANSFORMATION_EDITM2, OnEnChangeCrtTransformationEditm2) - ON_EN_CHANGE(IDC_CRT_TRANSFORMATION_EDITM3, OnEnChangeCrtTransformationEditm3) - ON_EN_CHANGE(IDC_CRT_TRANSFORMATION_EDITM4, OnEnChangeCrtTransformationEditm4) - ON_BN_CLICKED(IDC_Generate1, OnBnClickedGenerate1) - ON_BN_CLICKED(IDC_CRT_TRANSFORMATION_RADIO1, OnBnClickedCrtTransformationRadio1) - ON_BN_CLICKED(IDC_CRT_TRANSFORMATION_RADIO2, OnBnClickedCrtTransformationRadio2) - ON_BN_CLICKED(IDC_CRT_TRANSFORMATION_RADIO3, OnBnClickedCrtTransformationRadio3) - ON_EN_CHANGE(IDC_CRT_TRANSFORMATION_EDITANZAHL, OnEnChangeCrtTransformationEditanzahl) - ON_EN_CHANGE(IDC_CRT_TRANSFORMATION_EDITX, OnEnChangeCrtTransformationEditx) - ON_EN_CHANGE(IDC_CRT_TRANSFORMATION_EDITY, OnEnChangeCrtTransformationEdity) - ON_BN_CLICKED(IDC_GENERATERADIO1, OnBnClickedGenerateradio1) - ON_BN_CLICKED(IDC_GENERATERADIO2, OnBnClickedGenerateradio2) + ON_BN_CLICKED(IDC_CRT_TRANSFORMATION_BUTTONSOLVE, &CDlgCrtTransformation::OnBnClickedCrtTransformationButtonsolve) + ON_BN_CLICKED(IDC_CRT_TRANSFORMATION_BUTTONRESET2, &CDlgCrtTransformation::OnBnClickedCrtTransformationButtonreset) + ON_BN_CLICKED(IDC_CRT_TRANSFORMATION_BUTTONEND2, &CDlgCrtTransformation::OnBnClickedCrtTransformationButtonend) + ON_EN_CHANGE(IDC_CRT_TRANSFORMATION_EDITA0, &CDlgCrtTransformation::OnEnChangeCrtTransformationEdita0) + ON_EN_CHANGE(IDC_CRT_TRANSFORMATION_EDITA1, &CDlgCrtTransformation::OnEnChangeCrtTransformationEdita1) + ON_EN_CHANGE(IDC_CRT_TRANSFORMATION_EDITM0, &CDlgCrtTransformation::OnEnChangeCrtTransformationEditm0) + ON_EN_CHANGE(IDC_CRT_TRANSFORMATION_EDITM1, &CDlgCrtTransformation::OnEnChangeCrtTransformationEditm1) + ON_EN_CHANGE(IDC_CRT_TRANSFORMATION_EDITM2, &CDlgCrtTransformation::OnEnChangeCrtTransformationEditm2) + ON_EN_CHANGE(IDC_CRT_TRANSFORMATION_EDITM3, &CDlgCrtTransformation::OnEnChangeCrtTransformationEditm3) + ON_EN_CHANGE(IDC_CRT_TRANSFORMATION_EDITM4, &CDlgCrtTransformation::OnEnChangeCrtTransformationEditm4) + ON_BN_CLICKED(IDC_Generate1, &CDlgCrtTransformation::OnBnClickedGenerate1) + ON_BN_CLICKED(IDC_CRT_TRANSFORMATION_RADIO1, &CDlgCrtTransformation::OnBnClickedCrtTransformationRadio1) + ON_BN_CLICKED(IDC_CRT_TRANSFORMATION_RADIO2, &CDlgCrtTransformation::OnBnClickedCrtTransformationRadio2) + ON_BN_CLICKED(IDC_CRT_TRANSFORMATION_RADIO3, &CDlgCrtTransformation::OnBnClickedCrtTransformationRadio3) + ON_EN_CHANGE(IDC_CRT_TRANSFORMATION_EDITANZAHL, &CDlgCrtTransformation::OnEnChangeCrtTransformationEditanzahl) + ON_EN_CHANGE(IDC_CRT_TRANSFORMATION_EDITX, &CDlgCrtTransformation::OnEnChangeCrtTransformationEditx) + ON_EN_CHANGE(IDC_CRT_TRANSFORMATION_EDITY, &CDlgCrtTransformation::OnEnChangeCrtTransformationEdity) + ON_BN_CLICKED(IDC_GENERATERADIO1, &CDlgCrtTransformation::OnBnClickedGenerateradio1) + ON_BN_CLICKED(IDC_GENERATERADIO2, &CDlgCrtTransformation::OnBnClickedGenerateradio2) END_MESSAGE_MAP() diff --git a/CrypTool/DlgDemoRSAKeyGeneration.cpp b/CrypTool/DlgDemoRSAKeyGeneration.cpp index 36f268895..71dade471 100644 --- a/CrypTool/DlgDemoRSAKeyGeneration.cpp +++ b/CrypTool/DlgDemoRSAKeyGeneration.cpp @@ -81,11 +81,11 @@ void CDlgDemoRSAKeyGeneration::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgDemoRSAKeyGeneration, CDialog) //{{AFX_MSG_MAP(CDlgDemoRSAKeyGeneration) - ON_BN_CLICKED(IDC_GENERATE_PRIME, OnGeneratePrime) - ON_EN_UPDATE(IDC_EDIT_RSA_KEY_PUBLIC, OnUpdateParameter) - ON_BN_CLICKED(IDOK, OnOK) - ON_EN_UPDATE(IDC_EDIT_PRIME_P, OnUpdateParameter) - ON_EN_UPDATE(IDC_EDIT_PRIME_Q, OnUpdateParameter) + ON_BN_CLICKED(IDC_GENERATE_PRIME, &CDlgDemoRSAKeyGeneration::OnGeneratePrime) + ON_EN_UPDATE(IDC_EDIT_RSA_KEY_PUBLIC, &CDlgDemoRSAKeyGeneration::OnUpdateParameter) + ON_BN_CLICKED(IDOK, &CDlgDemoRSAKeyGeneration::OnOK) + ON_EN_UPDATE(IDC_EDIT_PRIME_P, &CDlgDemoRSAKeyGeneration::OnUpdateParameter) + ON_EN_UPDATE(IDC_EDIT_PRIME_Q, &CDlgDemoRSAKeyGeneration::OnUpdateParameter) //}}AFX_MSG_MAP END_MESSAGE_MAP() diff --git a/CrypTool/DlgDiffieHellmanCreateSharedKey.cpp b/CrypTool/DlgDiffieHellmanCreateSharedKey.cpp index 7ebfb7556..1c46f0e86 100644 --- a/CrypTool/DlgDiffieHellmanCreateSharedKey.cpp +++ b/CrypTool/DlgDiffieHellmanCreateSharedKey.cpp @@ -22,7 +22,7 @@ // #include "stdafx.h" -#include "CryptoolApp.h" +#include "CrypToolApp.h" #include "DlgDiffieHellmanCreateSharedKey.h" #ifdef _DEBUG diff --git a/CrypTool/DlgDiffieHellmanExchangeSharedKeys.cpp b/CrypTool/DlgDiffieHellmanExchangeSharedKeys.cpp index b25598a94..9b3f00055 100644 --- a/CrypTool/DlgDiffieHellmanExchangeSharedKeys.cpp +++ b/CrypTool/DlgDiffieHellmanExchangeSharedKeys.cpp @@ -22,7 +22,7 @@ // #include "stdafx.h" -#include "CryptoolApp.h" +#include "CrypToolApp.h" #include "DlgDiffieHellmanExchangeSharedKeys.h" #ifdef _DEBUG diff --git a/CrypTool/DlgDiffieHellmanGenerateFinalKey.cpp b/CrypTool/DlgDiffieHellmanGenerateFinalKey.cpp index 2034dabab..7b17bf7ee 100644 --- a/CrypTool/DlgDiffieHellmanGenerateFinalKey.cpp +++ b/CrypTool/DlgDiffieHellmanGenerateFinalKey.cpp @@ -22,7 +22,7 @@ // #include "stdafx.h" -#include "CryptoolApp.h" +#include "CrypToolApp.h" #include "DlgDiffieHellmanGenerateFinalKey.h" #ifdef _DEBUG diff --git a/CrypTool/DlgDiffieHellmanIntro.cpp b/CrypTool/DlgDiffieHellmanIntro.cpp index e8f4ba169..47158bbc4 100644 --- a/CrypTool/DlgDiffieHellmanIntro.cpp +++ b/CrypTool/DlgDiffieHellmanIntro.cpp @@ -22,7 +22,7 @@ // #include "stdafx.h" -#include "CryptoolApp.h" +#include "CrypToolApp.h" #include "DlgDiffieHellmanIntro.h" #ifdef _DEBUG diff --git a/CrypTool/DlgDiffieHellmanKeyInformation.cpp b/CrypTool/DlgDiffieHellmanKeyInformation.cpp index ed21bc9f8..cfccae969 100644 --- a/CrypTool/DlgDiffieHellmanKeyInformation.cpp +++ b/CrypTool/DlgDiffieHellmanKeyInformation.cpp @@ -99,7 +99,7 @@ void CDlgDiffieHellmanKeyInformation::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgDiffieHellmanKeyInformation, CDialog) //{{AFX_MSG_MAP(CDlgDiffieHellmanKeyInformation) - ON_BN_CLICKED(IDC_SHOWLOGTEXT, OnShowlogtext) + ON_BN_CLICKED(IDC_SHOWLOGTEXT, &CDlgDiffieHellmanKeyInformation::OnShowlogtext) //}}AFX_MSG_MAP END_MESSAGE_MAP() diff --git a/CrypTool/DlgDiffieHellmanPublicParameters.cpp b/CrypTool/DlgDiffieHellmanPublicParameters.cpp index 999ea6268..078ae9a44 100644 --- a/CrypTool/DlgDiffieHellmanPublicParameters.cpp +++ b/CrypTool/DlgDiffieHellmanPublicParameters.cpp @@ -25,7 +25,7 @@ #include "NTL/ZZ.h" #include "stdafx.h" -#include "CryptoolApp.h" +#include "CrypToolApp.h" #include "DlgDiffieHellmanPublicParameters.h" #include "DlgGenerateSavePrime.h" #include @@ -84,8 +84,8 @@ void CDlgDiffieHellmanPublicParameters::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgDiffieHellmanPublicParameters, CDialog) //{{AFX_MSG_MAP(CDlgDiffieHellmanPublicParameters) - ON_BN_CLICKED(IDC_GENERATE_PRIME, OnGeneratePrime) - ON_BN_CLICKED(IDC_GENERATE_GENERATOR, OnGenerateGenerator) + ON_BN_CLICKED(IDC_GENERATE_PRIME, &CDlgDiffieHellmanPublicParameters::OnGeneratePrime) + ON_BN_CLICKED(IDC_GENERATE_GENERATOR, &CDlgDiffieHellmanPublicParameters::OnGenerateGenerator) //}}AFX_MSG_MAP ON_EN_CHANGE(IDC_PRIME, &CDlgDiffieHellmanPublicParameters::OnEnChangePrime) END_MESSAGE_MAP() diff --git a/CrypTool/DlgDiffieHellmanSecretInput.cpp b/CrypTool/DlgDiffieHellmanSecretInput.cpp index 96c91987d..72704bb9d 100644 --- a/CrypTool/DlgDiffieHellmanSecretInput.cpp +++ b/CrypTool/DlgDiffieHellmanSecretInput.cpp @@ -22,9 +22,9 @@ // #include "stdafx.h" -#include "CryptoolApp.h" +#include "CrypToolApp.h" +#include "DlgDiffieHellmanSecretInput.h" #include "DlgDiffieHellmanSecretInput.h" -#include ".\dlgdiffiehellmansecretinput.h" #ifdef _DEBUG #define new DEBUG_NEW @@ -79,9 +79,9 @@ void CDlgDiffieHellmanSecretInput::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgDiffieHellmanSecretInput, CDialog) //{{AFX_MSG_MAP(CDlgDiffieHellmanSecretInput) - ON_BN_CLICKED(IDC_GENERATE_SECRET, OnGenerateSecret) + ON_BN_CLICKED(IDC_GENERATE_SECRET, &CDlgDiffieHellmanSecretInput::OnGenerateSecret) //}}AFX_MSG_MAP - ON_BN_CLICKED(IDOK, OnBnClickedOk) + ON_BN_CLICKED(IDOK, &CDlgDiffieHellmanSecretInput::OnBnClickedOk) END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// diff --git a/CrypTool/DlgDiffieHellmanSetPublicParameters.cpp b/CrypTool/DlgDiffieHellmanSetPublicParameters.cpp index c93c28b4a..7fb42dad4 100644 --- a/CrypTool/DlgDiffieHellmanSetPublicParameters.cpp +++ b/CrypTool/DlgDiffieHellmanSetPublicParameters.cpp @@ -22,7 +22,7 @@ // #include "stdafx.h" -#include "CryptoolApp.h" +#include "CrypToolApp.h" #include "DlgDiffieHellmanSetPublicParameters.h" #ifdef _DEBUG diff --git a/CrypTool/DlgDiffieHellmanSetSecrets.cpp b/CrypTool/DlgDiffieHellmanSetSecrets.cpp index c80571023..61f7bcedb 100644 --- a/CrypTool/DlgDiffieHellmanSetSecrets.cpp +++ b/CrypTool/DlgDiffieHellmanSetSecrets.cpp @@ -22,7 +22,7 @@ // #include "stdafx.h" -#include "CryptoolApp.h" +#include "CrypToolApp.h" #include "DlgDiffieHellmanSetSecrets.h" #ifdef _DEBUG diff --git a/CrypTool/DlgDiffieHellmanVisualization.cpp b/CrypTool/DlgDiffieHellmanVisualization.cpp index a20306150..253fd8589 100644 --- a/CrypTool/DlgDiffieHellmanVisualization.cpp +++ b/CrypTool/DlgDiffieHellmanVisualization.cpp @@ -21,7 +21,7 @@ // DlgDiffieHellmanVisualization.cpp #include "stdafx.h" -#include "CryptoolApp.h" +#include "CrypToolApp.h" #include "DlgDiffieHellmanVisualization.h" #include "DlgDiffieHellmanPublicParameters.h" @@ -111,20 +111,20 @@ void CDlgDiffieHellmanVisualization::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgDiffieHellmanVisualization, CDialog) //{{AFX_MSG_MAP(CDlgDiffieHellmanVisualization) - ON_BN_CLICKED(IDC_SETPUBLICPARAMETERS, OnSetPublicParameters) - ON_BN_CLICKED(IDC_SETSECRETS, OnSetsecrets) - ON_BN_CLICKED(IDC_CREATESHAREDKEY, OnCreatesharedkey) - ON_BN_CLICKED(IDC_EXCHANGESHAREDKEYS, OnExchangesharedkeys) - ON_BN_CLICKED(IDC_GENERATEFINALKEY, OnGeneratefinalkey) - ON_BN_CLICKED(IDC_BUTTONALICE1, OnButtonalice1) - ON_BN_CLICKED(IDC_BUTTONBOB1, OnButtonbob1) - ON_BN_CLICKED(IDC_BUTTONALICE2, OnButtonalice2) - ON_BN_CLICKED(IDC_BUTTONBOB2, OnButtonbob2) - ON_BN_CLICKED(IDC_BUTTONALICE3, OnButtonalice3) - ON_BN_CLICKED(IDC_BUTTONBOB3, OnButtonbob3) - ON_BN_CLICKED(IDC_CHECK_DISABLEHELP, OnCheckDisablehelp) - ON_BN_CLICKED(IDC_CHECK_DISABLEINTRODUCTION, OnCheckDisableIntroDialog) - ON_BN_CLICKED(IDC_KEY, OnKey) + ON_BN_CLICKED(IDC_SETPUBLICPARAMETERS, &CDlgDiffieHellmanVisualization::OnSetPublicParameters) + ON_BN_CLICKED(IDC_SETSECRETS, &CDlgDiffieHellmanVisualization::OnSetsecrets) + ON_BN_CLICKED(IDC_CREATESHAREDKEY, &CDlgDiffieHellmanVisualization::OnCreatesharedkey) + ON_BN_CLICKED(IDC_EXCHANGESHAREDKEYS, &CDlgDiffieHellmanVisualization::OnExchangesharedkeys) + ON_BN_CLICKED(IDC_GENERATEFINALKEY, &CDlgDiffieHellmanVisualization::OnGeneratefinalkey) + ON_BN_CLICKED(IDC_BUTTONALICE1, &CDlgDiffieHellmanVisualization::OnButtonalice1) + ON_BN_CLICKED(IDC_BUTTONBOB1, &CDlgDiffieHellmanVisualization::OnButtonbob1) + ON_BN_CLICKED(IDC_BUTTONALICE2, &CDlgDiffieHellmanVisualization::OnButtonalice2) + ON_BN_CLICKED(IDC_BUTTONBOB2, &CDlgDiffieHellmanVisualization::OnButtonbob2) + ON_BN_CLICKED(IDC_BUTTONALICE3, &CDlgDiffieHellmanVisualization::OnButtonalice3) + ON_BN_CLICKED(IDC_BUTTONBOB3, &CDlgDiffieHellmanVisualization::OnButtonbob3) + ON_BN_CLICKED(IDC_CHECK_DISABLEHELP, &CDlgDiffieHellmanVisualization::OnCheckDisablehelp) + ON_BN_CLICKED(IDC_CHECK_DISABLEINTRODUCTION, &CDlgDiffieHellmanVisualization::OnCheckDisableIntroDialog) + ON_BN_CLICKED(IDC_KEY, &CDlgDiffieHellmanVisualization::OnKey) ON_WM_PAINT() ON_WM_CTLCOLOR() //}}AFX_MSG_MAP diff --git a/CrypTool/DlgECCDecReceiver.cpp b/CrypTool/DlgECCDecReceiver.cpp index dd091da4f..37215d07e 100644 --- a/CrypTool/DlgECCDecReceiver.cpp +++ b/CrypTool/DlgECCDecReceiver.cpp @@ -74,10 +74,10 @@ void CDlgECCDecReceiver::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgECCDecReceiver, CDialog) //{{AFX_MSG_MAP(CDlgECCDecReceiver) - ON_NOTIFY(NM_CLICK, IDC_LIST_KEYS, OnClickListKeys) - ON_NOTIFY(HDN_ITEMCLICK, IDC_LIST_KEYS, OnItemclickListKeys) - ON_NOTIFY(LVN_COLUMNCLICK, IDC_LIST_KEYS, OnColumnclickListKeys) - ON_NOTIFY(LVN_KEYDOWN, IDC_LIST_KEYS, OnKeydownListKeys) + ON_NOTIFY(NM_CLICK, IDC_LIST_KEYS, &CDlgECCDecReceiver::OnClickListKeys) + ON_NOTIFY(HDN_ITEMCLICK, IDC_LIST_KEYS, &CDlgECCDecReceiver::OnItemclickListKeys) + ON_NOTIFY(LVN_COLUMNCLICK, IDC_LIST_KEYS, &CDlgECCDecReceiver::OnColumnclickListKeys) + ON_NOTIFY(LVN_KEYDOWN, IDC_LIST_KEYS, &CDlgECCDecReceiver::OnKeydownListKeys) //}}AFX_MSG_MAP END_MESSAGE_MAP() diff --git a/CrypTool/DlgECCEncReceiver.cpp b/CrypTool/DlgECCEncReceiver.cpp index be1dabde2..45a76b5f7 100644 --- a/CrypTool/DlgECCEncReceiver.cpp +++ b/CrypTool/DlgECCEncReceiver.cpp @@ -76,12 +76,12 @@ void CDlgECCEncReceiver::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgECCEncReceiver, CDialog) //{{AFX_MSG_MAP(CDlgECCEncReceiver) - ON_NOTIFY(NM_CLICK, IDC_LIST1, OnClickList1) - ON_NOTIFY(LVN_COLUMNCLICK, IDC_LIST1, OnColumnclickList1) - ON_NOTIFY(LVN_KEYDOWN, IDC_LIST1, OnKeydownList1) - ON_NOTIFY(HDN_ITEMCLICK, IDC_LIST1, OnItemclickList1) + ON_NOTIFY(NM_CLICK, IDC_LIST1, &CDlgECCEncReceiver::OnClickList1) + ON_NOTIFY(LVN_COLUMNCLICK, IDC_LIST1, &CDlgECCEncReceiver::OnColumnclickList1) + ON_NOTIFY(LVN_KEYDOWN, IDC_LIST1, &CDlgECCEncReceiver::OnKeydownList1) + ON_NOTIFY(HDN_ITEMCLICK, IDC_LIST1, &CDlgECCEncReceiver::OnItemclickList1) //}}AFX_MSG_MAP - ON_NOTIFY(LVN_ITEMCHANGED, IDC_LIST1, OnLvnItemchangedList1) + ON_NOTIFY(LVN_ITEMCHANGED, IDC_LIST1, &CDlgECCEncReceiver::OnLvnItemchangedList1) END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// diff --git a/CrypTool/DlgECGeneration.cpp b/CrypTool/DlgECGeneration.cpp index 2d0cbe878..99f95f210 100644 --- a/CrypTool/DlgECGeneration.cpp +++ b/CrypTool/DlgECGeneration.cpp @@ -76,10 +76,10 @@ void CDlgECGeneration::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgECGeneration, CDialog) //{{AFX_MSG_MAP(CDlgECGeneration) /* - ON_BN_CLICKED(IDC_CHECK1, OnCheck1) - ON_BN_CLICKED(IDC_BUTTON_GEN, OnButtonGen) - ON_EN_UPDATE(IDC_EDIT1, OnUpdateEdit1) - ON_EN_UPDATE(IDC_EDIT2, OnUpdateEdit2) + ON_BN_CLICKED(IDC_CHECK1, &CDlgECGeneration::OnCheck1) + ON_BN_CLICKED(IDC_BUTTON_GEN, &CDlgECGeneration::OnButtonGen) + ON_EN_UPDATE(IDC_EDIT1, &CDlgECGeneration::OnUpdateEdit1) + ON_EN_UPDATE(IDC_EDIT2, &CDlgECGeneration::OnUpdateEdit2) */ //}}AFX_MSG_MAP END_MESSAGE_MAP() diff --git a/CrypTool/DlgECSignatureStepByStep.cpp b/CrypTool/DlgECSignatureStepByStep.cpp index 3aea81165..100fb91ed 100644 --- a/CrypTool/DlgECSignatureStepByStep.cpp +++ b/CrypTool/DlgECSignatureStepByStep.cpp @@ -102,7 +102,7 @@ void CDlgECSignatureStepByStep::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgECSignatureStepByStep, CDialog) //{{AFX_MSG_MAP(CDlgECSignatureStepByStep) - ON_BN_CLICKED(IDC_BUTTON_CONTINUE, OnButtonContinue) + ON_BN_CLICKED(IDC_BUTTON_CONTINUE, &CDlgECSignatureStepByStep::OnButtonContinue) //}}AFX_MSG_MAP END_MESSAGE_MAP() diff --git a/CrypTool/DlgExtractSignature.cpp b/CrypTool/DlgExtractSignature.cpp index 8e018aff7..c9ee1852a 100644 --- a/CrypTool/DlgExtractSignature.cpp +++ b/CrypTool/DlgExtractSignature.cpp @@ -114,10 +114,10 @@ void CDlgExtractSignature::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgExtractSignature, CDialog) //{{AFX_MSG_MAP(CDlgExtractSignature) - ON_BN_CLICKED(IDC_RADIO1, OnRadioOctets) - ON_BN_CLICKED(IDC_RADIO2, OnRadioOktal) - ON_BN_CLICKED(IDC_RADIO3, OnRadioDezimal) - ON_BN_CLICKED(IDC_RADIO4, OnRadioHexadezimal) + ON_BN_CLICKED(IDC_RADIO1, &CDlgExtractSignature::OnRadioOctets) + ON_BN_CLICKED(IDC_RADIO2, &CDlgExtractSignature::OnRadioOktal) + ON_BN_CLICKED(IDC_RADIO3, &CDlgExtractSignature::OnRadioDezimal) + ON_BN_CLICKED(IDC_RADIO4, &CDlgExtractSignature::OnRadioHexadezimal) //}}AFX_MSG_MAP ON_BN_CLICKED(ID_EXTRACT_SIGNATURE_VERIFY, &CDlgExtractSignature::OnBnClickedExtractSignatureVerify) END_MESSAGE_MAP() diff --git a/CrypTool/DlgFactorisationDemo.cpp b/CrypTool/DlgFactorisationDemo.cpp index c462d7580..26beed0c2 100644 --- a/CrypTool/DlgFactorisationDemo.cpp +++ b/CrypTool/DlgFactorisationDemo.cpp @@ -99,12 +99,12 @@ void CDlgFactorisationDemo::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgFactorisationDemo, CDialog) //{{AFX_MSG_MAP(CDlgFactorisationDemo) - ON_BN_CLICKED(IDC_BUTTON_CANCEL, OnButtonEnd) - ON_BN_CLICKED(IDC_BUTTON_Faktorisieren, OnButtonFactorisation) - ON_BN_CLICKED(IDC_BUTTON_VOLLSTAENDIG_FAKTORISATION, OnButtonVollstaendigFaktorisation) - ON_EN_UPDATE(IDC_EDIT1, OnUpdateEditEingabe) - ON_BN_CLICKED(IDC_BUTTON1, OnShowFactorisationDetails) - ON_BN_CLICKED(IDC_BUTTON_LOAD_NUMBER, OnBnClickedLoadNumber) + ON_BN_CLICKED(IDC_BUTTON_CANCEL, &CDlgFactorisationDemo::OnButtonEnd) + ON_BN_CLICKED(IDC_BUTTON_Faktorisieren, &CDlgFactorisationDemo::OnButtonFactorisation) + ON_BN_CLICKED(IDC_BUTTON_VOLLSTAENDIG_FAKTORISATION, &CDlgFactorisationDemo::OnButtonVollstaendigFaktorisation) + ON_EN_UPDATE(IDC_EDIT1, &CDlgFactorisationDemo::OnUpdateEditEingabe) + ON_BN_CLICKED(IDC_BUTTON1, &CDlgFactorisationDemo::OnShowFactorisationDetails) + ON_BN_CLICKED(IDC_BUTTON_LOAD_NUMBER, &CDlgFactorisationDemo::OnBnClickedLoadNumber) //}}AFX_MSG_MAP END_MESSAGE_MAP() diff --git a/CrypTool/DlgFactorisationList.cpp b/CrypTool/DlgFactorisationList.cpp index 206669e7d..2d44f9206 100644 --- a/CrypTool/DlgFactorisationList.cpp +++ b/CrypTool/DlgFactorisationList.cpp @@ -72,7 +72,7 @@ void CDlgFactorisationList::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgFactorisationList, CDialog) //{{AFX_MSG_MAP(CDlgFactorisationList) - ON_NOTIFY(NM_DBLCLK, IDC_LIST1, OnDblclkSelect) + ON_NOTIFY(NM_DBLCLK, IDC_LIST1, &CDlgFactorisationList::OnDblclkSelect) //}}AFX_MSG_MAP END_MESSAGE_MAP() diff --git a/CrypTool/DlgFileProperties.cpp b/CrypTool/DlgFileProperties.cpp index 64b37d8bc..d074da7ea 100644 --- a/CrypTool/DlgFileProperties.cpp +++ b/CrypTool/DlgFileProperties.cpp @@ -26,7 +26,7 @@ #include "DlgFileProperties.h" #include "DlgShowKey.h" #include "CrypToolTools.h" -#include "keyHillBase.h" +#include "KeyHillBase.h" #ifdef _DEBUG #define new DEBUG_NEW @@ -70,7 +70,7 @@ void CDlgFileProperties::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgFileProperties, CDialog) //{{AFX_MSG_MAP(CDlgFileProperties) - ON_BN_CLICKED(IDC_BUTTON1, OnShowKey) + ON_BN_CLICKED(IDC_BUTTON1, &CDlgFileProperties::OnShowKey) //}}AFX_MSG_MAP END_MESSAGE_MAP() diff --git a/CrypTool/DlgFindAndReplace.cpp b/CrypTool/DlgFindAndReplace.cpp index 2f8835b4e..f668f4e80 100644 --- a/CrypTool/DlgFindAndReplace.cpp +++ b/CrypTool/DlgFindAndReplace.cpp @@ -27,7 +27,7 @@ #include "ScintillaDoc.h" #include "ScintillaView.h" #include "HexEditCtrlView.h" -#include ".\dlgfindandreplace.h" +#include "DlgFindAndReplace.h" // this function returns true if the input string (which is the output string also) // could be converted from hex to ascii; in case the hex string contains a zero byte (00), @@ -117,11 +117,11 @@ void CDlgFindAndReplace::DoDataExchange(CDataExchange* pDX) } BEGIN_MESSAGE_MAP(CDlgFindAndReplace, CDialog) - ON_BN_CLICKED(IDC_BUTTON_FIND, OnBnClickedButtonFind) - ON_BN_CLICKED(IDC_BUTTON_REPLACE, OnBnClickedButtonReplace) - ON_BN_CLICKED(IDC_BUTTON_REPLACE_ALL, OnBnClickedButtonReplaceAll) - ON_BN_CLICKED(IDC_RADIO_TEXT_MODE, OnBnClickedRadioTextMode) - ON_BN_CLICKED(IDC_RADIO_HEX_MODE, OnBnClickedRadioHexMode) + ON_BN_CLICKED(IDC_BUTTON_FIND, &CDlgFindAndReplace::OnBnClickedButtonFind) + ON_BN_CLICKED(IDC_BUTTON_REPLACE, &CDlgFindAndReplace::OnBnClickedButtonReplace) + ON_BN_CLICKED(IDC_BUTTON_REPLACE_ALL, &CDlgFindAndReplace::OnBnClickedButtonReplaceAll) + ON_BN_CLICKED(IDC_RADIO_TEXT_MODE, &CDlgFindAndReplace::OnBnClickedRadioTextMode) + ON_BN_CLICKED(IDC_RADIO_HEX_MODE, &CDlgFindAndReplace::OnBnClickedRadioHexMode) END_MESSAGE_MAP() // CDlgFindAndReplace message handlers diff --git a/CrypTool/DlgFindText.cpp b/CrypTool/DlgFindText.cpp index d14d1e2fc..7d71d8584 100644 --- a/CrypTool/DlgFindText.cpp +++ b/CrypTool/DlgFindText.cpp @@ -22,7 +22,7 @@ // #include "stdafx.h" -#include "cryptoolapp.h" +#include "CrypToolApp.h" #include "DlgFindText.h" #ifdef _DEBUG diff --git a/CrypTool/DlgFormatTextDocument.cpp b/CrypTool/DlgFormatTextDocument.cpp index 87b416665..4e4ae699b 100644 --- a/CrypTool/DlgFormatTextDocument.cpp +++ b/CrypTool/DlgFormatTextDocument.cpp @@ -24,7 +24,7 @@ #include #include -#include ".\dlgformattextdocument.h" +#include "DlgFormatTextDocument.h" IMPLEMENT_DYNAMIC(CDlgFormatTextDocument, CDialog) CDlgFormatTextDocument::CDlgFormatTextDocument(CWnd* pParent /*=NULL*/) @@ -118,11 +118,11 @@ CString CDlgFormatTextDocument::format(CString fileName) } BEGIN_MESSAGE_MAP(CDlgFormatTextDocument, CDialog) - ON_BN_CLICKED(IDOK, OnBnClickedOk) - ON_BN_CLICKED(IDC_CHECK_REMOVE_NON_ALPHABET_CHARACTERS, OnBnClickedCheckRemoveNonAlphabetCharacters) - ON_BN_CLICKED(IDC_CHECK_CONVERT_TO_UPPERCASE, OnBnClickedCheckConvertToUppercase) - ON_BN_CLICKED(IDC_CHECK_CONVERT_TO_LOWERCASE, OnBnClickedCheckConvertToLowercase) - ON_BN_CLICKED(IDC_CHECK_DIVIDE_IN_BLOCKS, OnBnClickedCheckDivideInBlocks) + ON_BN_CLICKED(IDOK, &CDlgFormatTextDocument::OnBnClickedOk) + ON_BN_CLICKED(IDC_CHECK_REMOVE_NON_ALPHABET_CHARACTERS, &CDlgFormatTextDocument::OnBnClickedCheckRemoveNonAlphabetCharacters) + ON_BN_CLICKED(IDC_CHECK_CONVERT_TO_UPPERCASE, &CDlgFormatTextDocument::OnBnClickedCheckConvertToUppercase) + ON_BN_CLICKED(IDC_CHECK_CONVERT_TO_LOWERCASE, &CDlgFormatTextDocument::OnBnClickedCheckConvertToLowercase) + ON_BN_CLICKED(IDC_CHECK_DIVIDE_IN_BLOCKS, &CDlgFormatTextDocument::OnBnClickedCheckDivideInBlocks) ON_BN_CLICKED(ID_TEXTOPTIONS, &CDlgFormatTextDocument::OnBnClickedTextoptions) END_MESSAGE_MAP() diff --git a/CrypTool/DlgHMAC.cpp b/CrypTool/DlgHMAC.cpp index 33399ad40..b6fd0fbd7 100644 --- a/CrypTool/DlgHMAC.cpp +++ b/CrypTool/DlgHMAC.cpp @@ -78,10 +78,10 @@ void CDlgHMAC::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgHMAC, CDialog) //{{AFX_MSG_MAP(CDlgHMAC) - ON_EN_CHANGE(IDC_SHOW_TEXT, OnEditText) - ON_EN_CHANGE(IDC_EDIT_KEY, OnEditKey) - ON_EN_CHANGE(IDC_EDIT_ORIGINAL_MESSAGE, OnEditOriginalMessage) - ON_EN_CHANGE(IDC_EDIT_KEY2, OnEditSecondKey) + ON_EN_CHANGE(IDC_SHOW_TEXT, &CDlgHMAC::OnEditText) + ON_EN_CHANGE(IDC_EDIT_KEY, &CDlgHMAC::OnEditKey) + ON_EN_CHANGE(IDC_EDIT_ORIGINAL_MESSAGE, &CDlgHMAC::OnEditOriginalMessage) + ON_EN_CHANGE(IDC_EDIT_KEY2, &CDlgHMAC::OnEditSecondKey) //}}AFX_MSG_MAP ON_CBN_SELENDOK(IDC_COMBO_SELECT_HASH_FUNCTION, &CDlgHMAC::OnCbnSelendokComboSelectHashFunction) ON_CBN_SELENDOK(IDC_COMBO_SELECT_HMAC_ALGORITHM, &CDlgHMAC::OnCbnSelendokComboSelectHmacAlgorithm) diff --git a/CrypTool/DlgHashDemo.cpp b/CrypTool/DlgHashDemo.cpp index 590eb72df..6701e98d9 100644 --- a/CrypTool/DlgHashDemo.cpp +++ b/CrypTool/DlgHashDemo.cpp @@ -74,11 +74,11 @@ void CDlgHashDemo::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgHashDemo, CDialog) //{{AFX_MSG_MAP(CDlgHashDemo) - ON_BN_CLICKED(IDC_RADIO_BIN, OnRadioBin) - ON_BN_CLICKED(IDC_RADIO_DEC, OnRadioDec) - ON_BN_CLICKED(IDC_RADIO_HEX, OnRadioHex) - ON_EN_CHANGE(IDC_EDIT_TEXT, OnChangeEditText) - ON_CBN_SELENDOK(IDC_COMBO_SELECT_HASH_FUNCTION, OnSelendokComboSelectHashFunction) + ON_BN_CLICKED(IDC_RADIO_BIN, &CDlgHashDemo::OnRadioBin) + ON_BN_CLICKED(IDC_RADIO_DEC, &CDlgHashDemo::OnRadioDec) + ON_BN_CLICKED(IDC_RADIO_HEX, &CDlgHashDemo::OnRadioHex) + ON_EN_CHANGE(IDC_EDIT_TEXT, &CDlgHashDemo::OnChangeEditText) + ON_CBN_SELENDOK(IDC_COMBO_SELECT_HASH_FUNCTION, &CDlgHashDemo::OnSelendokComboSelectHashFunction) //}}AFX_MSG_MAP END_MESSAGE_MAP() diff --git a/CrypTool/DlgHillAnalysis.cpp b/CrypTool/DlgHillAnalysis.cpp index 25003b4f7..15bb95f58 100644 --- a/CrypTool/DlgHillAnalysis.cpp +++ b/CrypTool/DlgHillAnalysis.cpp @@ -79,8 +79,8 @@ void CDlgHillAnaylsis::DoDataExchange(CDataExchange* pDX) } BEGIN_MESSAGE_MAP(CDlgHillAnaylsis, CDialog) - ON_CBN_SELCHANGE(IDC_DIM_VON, OnSelchangeDimVon) - ON_CBN_SELCHANGE(IDC_DIM_BIS, OnSelchangeDimBis) + ON_CBN_SELCHANGE(IDC_DIM_VON, &CDlgHillAnaylsis::OnSelchangeDimVon) + ON_CBN_SELCHANGE(IDC_DIM_BIS, &CDlgHillAnaylsis::OnSelchangeDimBis) ON_NOTIFY(TCN_SELCHANGE, IDC_TAB1, &CDlgHillAnaylsis::OnTcnSelchangeTab1) ON_BN_CLICKED(IDOK, &CDlgHillAnaylsis::OnBnClickedSearchKey) ON_BN_CLICKED(IDCANCEL, &CDlgHillAnaylsis::OnBnClickedCancel) diff --git a/CrypTool/DlgHillOptions.cpp b/CrypTool/DlgHillOptions.cpp index 351653d6f..ff28fdcad 100644 --- a/CrypTool/DlgHillOptions.cpp +++ b/CrypTool/DlgHillOptions.cpp @@ -24,7 +24,7 @@ #include "stdafx.h" #include "CrypToolApp.h" #include "DlgHillOptions.h" -#include ".\dlghilloptions.h" +#include "DlgHillOptions.h" #include "CrypToolTools.h" // DlgHillOptions dialog @@ -35,10 +35,10 @@ void DlgHillOptions::DoDataExchange(CDataExchange* pDX) } BEGIN_MESSAGE_MAP(DlgHillOptions, CDialog) - ON_BN_CLICKED(IDOK, OnBnClickedOk) - ON_BN_CLICKED(IDC_RADIO1, OnBnClickedRadio1) - ON_BN_CLICKED(IDC_RADIO2, OnBnClickedRadio2) - ON_EN_CHANGE (IDC_EDIT2, OnEnChangeEdit2) + ON_BN_CLICKED(IDOK, &DlgHillOptions::OnBnClickedOk) + ON_BN_CLICKED(IDC_RADIO1, &DlgHillOptions::OnBnClickedRadio1) + ON_BN_CLICKED(IDC_RADIO2, &DlgHillOptions::OnBnClickedRadio2) + ON_EN_CHANGE (IDC_EDIT2, &DlgHillOptions::OnEnChangeEdit2) ON_BN_CLICKED(IDC_RADIO3, &DlgHillOptions::OnBnClickedOffset0) ON_BN_CLICKED(IDC_RADIO4, &DlgHillOptions::OnBnClickedOffset1) END_MESSAGE_MAP() diff --git a/CrypTool/DlgHomophonicSubstitution.cpp b/CrypTool/DlgHomophonicSubstitution.cpp index d282653b7..e302b0e2f 100644 --- a/CrypTool/DlgHomophonicSubstitution.cpp +++ b/CrypTool/DlgHomophonicSubstitution.cpp @@ -79,16 +79,16 @@ void CDlgHomophonicSubstitution::DoDataExchange(CDataExchange *pDX) { } BEGIN_MESSAGE_MAP(CDlgHomophonicSubstitution, CDialog) - ON_BN_CLICKED(IDC_BUTTON_UPDATE_COUNT_HOMOPHONES, clickedButtonUpdateCountHomophones) - ON_BN_CLICKED(IDC_BUTTON_COPY_KEY_TO_KEYSTORE, clickedButtonCopyKeyToKeystore) - ON_BN_CLICKED(IDC_BUTTON_COPY_KEY_TO_CLIPBOARD, clickedButtonCopyKeyToClipboard) - ON_BN_CLICKED(IDC_BUTTON_PASTE_KEY_FROM_KEYSTORE, clickedButtonPasteKeyFromKeystore) - ON_BN_CLICKED(IDC_BUTTON_PASTE_KEY_FROM_CLIPBOARD, clickedButtonPasteKeyFromClipboard) - ON_NOTIFY(NM_DBLCLK, IDC_LIST_HOMOPHONES_KEY_TABLE, doubleClickedListHomophonesKeyTable) - ON_BN_CLICKED(IDC_BUTTON_ENCRYPT, clickedButtonEncrypt) - ON_BN_CLICKED(IDC_BUTTON_DECRYPT, clickedButtonDecrypt) - ON_BN_CLICKED(IDC_BUTTON_GENERATE_KEY, clickedButtonGenerateKey) - ON_BN_CLICKED(IDC_BUTTON_CANCEL, clickedButtonCancel) + ON_BN_CLICKED(IDC_BUTTON_UPDATE_COUNT_HOMOPHONES, &CDlgHomophonicSubstitution::clickedButtonUpdateCountHomophones) + ON_BN_CLICKED(IDC_BUTTON_COPY_KEY_TO_KEYSTORE, &CDlgHomophonicSubstitution::clickedButtonCopyKeyToKeystore) + ON_BN_CLICKED(IDC_BUTTON_COPY_KEY_TO_CLIPBOARD, &CDlgHomophonicSubstitution::clickedButtonCopyKeyToClipboard) + ON_BN_CLICKED(IDC_BUTTON_PASTE_KEY_FROM_KEYSTORE, &CDlgHomophonicSubstitution::clickedButtonPasteKeyFromKeystore) + ON_BN_CLICKED(IDC_BUTTON_PASTE_KEY_FROM_CLIPBOARD, &CDlgHomophonicSubstitution::clickedButtonPasteKeyFromClipboard) + ON_NOTIFY(NM_DBLCLK, IDC_LIST_HOMOPHONES_KEY_TABLE, &CDlgHomophonicSubstitution::doubleClickedListHomophonesKeyTable) + ON_BN_CLICKED(IDC_BUTTON_ENCRYPT, &CDlgHomophonicSubstitution::clickedButtonEncrypt) + ON_BN_CLICKED(IDC_BUTTON_DECRYPT, &CDlgHomophonicSubstitution::clickedButtonDecrypt) + ON_BN_CLICKED(IDC_BUTTON_GENERATE_KEY, &CDlgHomophonicSubstitution::clickedButtonGenerateKey) + ON_BN_CLICKED(IDC_BUTTON_CANCEL, &CDlgHomophonicSubstitution::clickedButtonCancel) END_MESSAGE_MAP() void CDlgHomophonicSubstitution::checkInputFile() { @@ -371,7 +371,7 @@ void CDlgHomophoneSubstitutionKeyEntryDetails::DoDataExchange(CDataExchange *pDX } BEGIN_MESSAGE_MAP(CDlgHomophoneSubstitutionKeyEntryDetails, CDialog) - ON_BN_CLICKED(IDC_BUTTON_CLOSE, clickedButtonClose) + ON_BN_CLICKED(IDC_BUTTON_CLOSE, &CDlgHomophoneSubstitutionKeyEntryDetails::clickedButtonClose) END_MESSAGE_MAP() void CDlgHomophoneSubstitutionKeyEntryDetails::clickedButtonClose() { diff --git a/CrypTool/DlgHybridDecryptionDemo.cpp b/CrypTool/DlgHybridDecryptionDemo.cpp index 87ce99818..adf6e70c6 100644 --- a/CrypTool/DlgHybridDecryptionDemo.cpp +++ b/CrypTool/DlgHybridDecryptionDemo.cpp @@ -89,8 +89,8 @@ void CDlgHybridDecryptionDemo::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgHybridDecryptionDemo, CDialog) //{{AFX_MSG_MAP(CDlgHybridDecryptionDemo) - ON_BN_CLICKED(IDC_BUTTON_CONTINUE, OnButtonContinue) - ON_BN_CLICKED(IDC_SHOW_CERTIFICATE, OnButtonShowCertificate) + ON_BN_CLICKED(IDC_BUTTON_CONTINUE, &CDlgHybridDecryptionDemo::OnButtonContinue) + ON_BN_CLICKED(IDC_SHOW_CERTIFICATE, &CDlgHybridDecryptionDemo::OnButtonShowCertificate) //}}AFX_MSG_MAP END_MESSAGE_MAP() diff --git a/CrypTool/DlgHybridEncryptionDemo.cpp b/CrypTool/DlgHybridEncryptionDemo.cpp index 093d405ba..440134c74 100644 --- a/CrypTool/DlgHybridEncryptionDemo.cpp +++ b/CrypTool/DlgHybridEncryptionDemo.cpp @@ -132,17 +132,17 @@ void CDlgHybridEncryptionDemo::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgHybridEncryptionDemo, CDialog) //{{AFX_MSG_MAP(CDlgHybridEncryptionDemo) - ON_BN_CLICKED(IDC_BUTTON1_TXT_EINFUEGEN, OnButtonGetDocument) - ON_BN_CLICKED(IDC_BUTTON_ENC_KEY_ASYM, OnButtonEncKeyAsym) - ON_BN_CLICKED(IDC_BUTTON_ENC_TXT_SYM, OnButtonEncDocumentSym) - ON_BN_CLICKED(IDC_BUTTON_GEN_SYM_KEY, OnButtonGenSymKey) - ON_BN_CLICKED(IDC_BUTTON_GET_ASYM_KEY, OnButtonGetAsymKey) - ON_BN_CLICKED(IDC_BUTTON_SHOW_SYM_KEY, OnButtonShowSymKey) - ON_BN_CLICKED(IDC_BUTTON_SHOW_ASYM_KEY, OnButtonShowAsymKey) - ON_BN_CLICKED(IDC_BUTTON_SHOWTXT, OnButtonShowDocument) - ON_BN_CLICKED(IDC_BUTTON2, OnButtonShowEncDocument) - ON_BN_CLICKED(IDC_BUTTON3, OnButtonShowEncSymKey) - ON_BN_CLICKED(IDC_BUTTON_DATENAUSGABE, OnButtonDatenausgabe) + ON_BN_CLICKED(IDC_BUTTON1_TXT_EINFUEGEN, &CDlgHybridEncryptionDemo::OnButtonGetDocument) + ON_BN_CLICKED(IDC_BUTTON_ENC_KEY_ASYM, &CDlgHybridEncryptionDemo::OnButtonEncKeyAsym) + ON_BN_CLICKED(IDC_BUTTON_ENC_TXT_SYM, &CDlgHybridEncryptionDemo::OnButtonEncDocumentSym) + ON_BN_CLICKED(IDC_BUTTON_GEN_SYM_KEY, &CDlgHybridEncryptionDemo::OnButtonGenSymKey) + ON_BN_CLICKED(IDC_BUTTON_GET_ASYM_KEY, &CDlgHybridEncryptionDemo::OnButtonGetAsymKey) + ON_BN_CLICKED(IDC_BUTTON_SHOW_SYM_KEY, &CDlgHybridEncryptionDemo::OnButtonShowSymKey) + ON_BN_CLICKED(IDC_BUTTON_SHOW_ASYM_KEY, &CDlgHybridEncryptionDemo::OnButtonShowAsymKey) + ON_BN_CLICKED(IDC_BUTTON_SHOWTXT, &CDlgHybridEncryptionDemo::OnButtonShowDocument) + ON_BN_CLICKED(IDC_BUTTON2, &CDlgHybridEncryptionDemo::OnButtonShowEncDocument) + ON_BN_CLICKED(IDC_BUTTON3, &CDlgHybridEncryptionDemo::OnButtonShowEncSymKey) + ON_BN_CLICKED(IDC_BUTTON_DATENAUSGABE, &CDlgHybridEncryptionDemo::OnButtonDatenausgabe) ON_WM_SETCURSOR() ON_WM_PAINT() //}}AFX_MSG_MAP diff --git a/CrypTool/DlgICGRandomParamater.cpp b/CrypTool/DlgICGRandomParamater.cpp index 5bba6d81c..060413a19 100644 --- a/CrypTool/DlgICGRandomParamater.cpp +++ b/CrypTool/DlgICGRandomParamater.cpp @@ -70,11 +70,11 @@ void CDlgICGRandomParamater::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgICGRandomParamater, CDialog) //{{AFX_MSG_MAP(CDlgICGRandomParamater) - ON_BN_CLICKED(IDC_STATIC_GPA, OnStaticGp_a) - ON_BN_CLICKED(IDC_PRIMBUTTON, OnPrimbutton) - ON_BN_CLICKED(IDC_STATIC_GPC, OnStaticGpc) - ON_BN_CLICKED(IDC_STATIC_GPP, OnStaticGpp) - ON_BN_CLICKED(IDC_STATIC_GPL, OnStaticGpl) + ON_BN_CLICKED(IDC_STATIC_GPA, &CDlgICGRandomParamater::OnStaticGp_a) + ON_BN_CLICKED(IDC_PRIMBUTTON, &CDlgICGRandomParamater::OnPrimbutton) + ON_BN_CLICKED(IDC_STATIC_GPC, &CDlgICGRandomParamater::OnStaticGpc) + ON_BN_CLICKED(IDC_STATIC_GPP, &CDlgICGRandomParamater::OnStaticGpp) + ON_BN_CLICKED(IDC_STATIC_GPL, &CDlgICGRandomParamater::OnStaticGpl) ON_WM_KEYDOWN() //}}AFX_MSG_MAP END_MESSAGE_MAP() diff --git a/CrypTool/DlgKey.cpp b/CrypTool/DlgKey.cpp index 16a14167f..937b70317 100644 --- a/CrypTool/DlgKey.cpp +++ b/CrypTool/DlgKey.cpp @@ -87,11 +87,11 @@ void CDlgKey::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgKey, CDialog) //{{AFX_MSG_MAP(CDlgKey) - ON_EN_UPDATE(IDC_EDIT1, OnUpdateEdit1) - ON_BN_CLICKED(IDC_BUTTON1, OnDecrypt) - ON_BN_CLICKED(IDOK, OnEncrypt) - ON_BN_CLICKED(IDC_BUTTON_TEXTOPTIONS, OnTextOptions) - ON_BN_CLICKED(IDC_BUTTON2, OnPasteKey) + ON_EN_UPDATE(IDC_EDIT1, &CDlgKey::OnUpdateEdit1) + ON_BN_CLICKED(IDC_BUTTON1, &CDlgKey::OnDecrypt) + ON_BN_CLICKED(IDOK, &CDlgKey::OnEncrypt) + ON_BN_CLICKED(IDC_BUTTON_TEXTOPTIONS, &CDlgKey::OnTextOptions) + ON_BN_CLICKED(IDC_BUTTON2, &CDlgKey::OnPasteKey) //}}AFX_MSG_MAP END_MESSAGE_MAP() diff --git a/CrypTool/DlgKeyAsym.cpp b/CrypTool/DlgKeyAsym.cpp index 1a2969ea3..058ab53ac 100644 --- a/CrypTool/DlgKeyAsym.cpp +++ b/CrypTool/DlgKeyAsym.cpp @@ -97,18 +97,18 @@ void CDlgKeyAsym::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgKeyAsym, CDialog) //{{AFX_MSG_MAP(CDlgKeyAsym) - ON_BN_CLICKED(IDC_BUTTON1, OnShowPubParamButton) - ON_BN_CLICKED(IDC_BUTTON2, OnShowAllParamButton) - ON_BN_CLICKED(IDC_BUTTON3, OnDeleteEntryButton) - ON_NOTIFY(NM_CLICK, IDC_LIST3, OnClickList3) - ON_BN_CLICKED(IDC_BUTTON4, OnButtonShowCert) - ON_BN_CLICKED(IDC_BUTTON5, OnButtonExportCert) - ON_NOTIFY(LVN_COLUMNCLICK, IDC_LIST3, OnColumnclickList3) - ON_NOTIFY(HDN_ITEMCLICK, IDC_LIST3, OnItemclickList3) - ON_NOTIFY(LVN_KEYDOWN, IDC_LIST3, OnKeydownList3) - ON_BN_CLICKED(IDC_CHECK1, OnCheckRsaKey) - ON_BN_CLICKED(IDC_CHECK2, OnCheckDsaKey) - ON_BN_CLICKED(IDC_CHECK3, OnCheckEcKey) + ON_BN_CLICKED(IDC_BUTTON1, &CDlgKeyAsym::OnShowPubParamButton) + ON_BN_CLICKED(IDC_BUTTON2, &CDlgKeyAsym::OnShowAllParamButton) + ON_BN_CLICKED(IDC_BUTTON3, &CDlgKeyAsym::OnDeleteEntryButton) + ON_NOTIFY(NM_CLICK, IDC_LIST3, &CDlgKeyAsym::OnClickList3) + ON_BN_CLICKED(IDC_BUTTON4, &CDlgKeyAsym::OnButtonShowCert) + ON_BN_CLICKED(IDC_BUTTON5, &CDlgKeyAsym::OnButtonExportCert) + ON_NOTIFY(LVN_COLUMNCLICK, IDC_LIST3, &CDlgKeyAsym::OnColumnclickList3) + ON_NOTIFY(HDN_ITEMCLICK, IDC_LIST3, &CDlgKeyAsym::OnItemclickList3) + ON_NOTIFY(LVN_KEYDOWN, IDC_LIST3, &CDlgKeyAsym::OnKeydownList3) + ON_BN_CLICKED(IDC_CHECK1, &CDlgKeyAsym::OnCheckRsaKey) + ON_BN_CLICKED(IDC_CHECK2, &CDlgKeyAsym::OnCheckDsaKey) + ON_BN_CLICKED(IDC_CHECK3, &CDlgKeyAsym::OnCheckEcKey) //}}AFX_MSG_MAP END_MESSAGE_MAP() diff --git a/CrypTool/DlgKeyAsymGeneration.cpp b/CrypTool/DlgKeyAsymGeneration.cpp index f96a341a0..33c43d201 100644 --- a/CrypTool/DlgKeyAsymGeneration.cpp +++ b/CrypTool/DlgKeyAsymGeneration.cpp @@ -146,16 +146,16 @@ void CDlgKeyAsymGeneration::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgKeyAsymGeneration, CDialog) //{{AFX_MSG_MAP(CDlgKeyAsymGeneration) - ON_CBN_SELCHANGE(IDC_COMBO3, OnSelchangeEcCombo) - ON_BN_CLICKED(IDC_RADIO1, OnRSARadio) - ON_BN_CLICKED(IDC_RADIO2, OnDSARadio) - ON_BN_CLICKED(IDC_RADIO3, OnECRadio) - ON_BN_CLICKED(IDC_RADIO5, OnDecimalRadio) - ON_BN_CLICKED(IDC_RADIO4, OnOctalRadio) - ON_BN_CLICKED(IDC_RADIO6, OnHexRadio) - ON_BN_CLICKED(IDC_BUTTON_P12IMPORT, OnButtonP12import) - ON_NOTIFY(NM_CLICK, IDC_LIST1, OnClickList) - ON_EN_KILLFOCUS(IDC_EDIT_LV, OnKillfocusEditLv) + ON_CBN_SELCHANGE(IDC_COMBO3, &CDlgKeyAsymGeneration::OnSelchangeEcCombo) + ON_BN_CLICKED(IDC_RADIO1, &CDlgKeyAsymGeneration::OnRSARadio) + ON_BN_CLICKED(IDC_RADIO2, &CDlgKeyAsymGeneration::OnDSARadio) + ON_BN_CLICKED(IDC_RADIO3, &CDlgKeyAsymGeneration::OnECRadio) + ON_BN_CLICKED(IDC_RADIO5, &CDlgKeyAsymGeneration::OnDecimalRadio) + ON_BN_CLICKED(IDC_RADIO4, &CDlgKeyAsymGeneration::OnOctalRadio) + ON_BN_CLICKED(IDC_RADIO6, &CDlgKeyAsymGeneration::OnHexRadio) + ON_BN_CLICKED(IDC_BUTTON_P12IMPORT, &CDlgKeyAsymGeneration::OnButtonP12import) + ON_NOTIFY(NM_CLICK, IDC_LIST1, &CDlgKeyAsymGeneration::OnClickList) + ON_EN_KILLFOCUS(IDC_EDIT_LV, &CDlgKeyAsymGeneration::OnKillfocusEditLv) //}}AFX_MSG_MAP ON_BN_CLICKED(IDC_BUTTON1, &CDlgKeyAsymGeneration::OnBnClickedButton1) END_MESSAGE_MAP() diff --git a/CrypTool/DlgKeyFromPassword.cpp b/CrypTool/DlgKeyFromPassword.cpp index d180d8949..af0a1cccc 100644 --- a/CrypTool/DlgKeyFromPassword.cpp +++ b/CrypTool/DlgKeyFromPassword.cpp @@ -79,13 +79,13 @@ void CDlgKeyFromPassword::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgKeyFromPassword, CDialog) //{{AFX_MSG_MAP(CDlgKeyFromPassword) - ON_BN_CLICKED(IDC_BUTTON_Generieren, OnBUTTONGenerieren) - ON_EN_UPDATE(IDC_EDIT1, OnUpdateEdit1) - ON_EN_UPDATE(IDC_EDIT2, OnUpdateEdit2) - ON_BN_CLICKED(IDC_BUTTON_CANCEL, OnBUTTONcancel) - ON_BN_CLICKED(IDC_BUTTON_Uebernehmen, OnBUTTONUebernehmen) - ON_EN_UPDATE(IDC_EDIT4, OnUpdateEdit4) - ON_EN_UPDATE(IDC_EDIT5, OnUpdateEdit5) + ON_BN_CLICKED(IDC_BUTTON_Generieren, &CDlgKeyFromPassword::OnBUTTONGenerieren) + ON_EN_UPDATE(IDC_EDIT1, &CDlgKeyFromPassword::OnUpdateEdit1) + ON_EN_UPDATE(IDC_EDIT2, &CDlgKeyFromPassword::OnUpdateEdit2) + ON_BN_CLICKED(IDC_BUTTON_CANCEL, &CDlgKeyFromPassword::OnBUTTONcancel) + ON_BN_CLICKED(IDC_BUTTON_Uebernehmen, &CDlgKeyFromPassword::OnBUTTONUebernehmen) + ON_EN_UPDATE(IDC_EDIT4, &CDlgKeyFromPassword::OnUpdateEdit4) + ON_EN_UPDATE(IDC_EDIT5, &CDlgKeyFromPassword::OnUpdateEdit5) //}}AFX_MSG_MAP ON_BN_CLICKED(IDC_RADIO1, &CDlgKeyFromPassword::OnBnClickedRadio1) ON_BN_CLICKED(IDC_RADIO2, &CDlgKeyFromPassword::OnBnClickedRadio2) diff --git a/CrypTool/DlgKeyHex.cpp b/CrypTool/DlgKeyHex.cpp index 07d7d0fa5..07add1242 100644 --- a/CrypTool/DlgKeyHex.cpp +++ b/CrypTool/DlgKeyHex.cpp @@ -112,10 +112,10 @@ void CDlgKeyHex::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgKeyHex, CDialog) //{{AFX_MSG_MAP(CDlgKeyHex) - ON_BN_CLICKED(IDC_BUTTON1, OnDecrypt) - ON_BN_CLICKED(IDOK, OnEncrypt) - ON_EN_UPDATE(IDC_EDIT1, OnUpdateKey) - ON_BN_CLICKED(IDC_BUTTON2, OnPasteHexKey) + ON_BN_CLICKED(IDC_BUTTON1, &CDlgKeyHex::OnDecrypt) + ON_BN_CLICKED(IDOK, &CDlgKeyHex::OnEncrypt) + ON_EN_UPDATE(IDC_EDIT1, &CDlgKeyHex::OnUpdateKey) + ON_BN_CLICKED(IDC_BUTTON2, &CDlgKeyHex::OnPasteHexKey) //}}AFX_MSG_MAP END_MESSAGE_MAP() diff --git a/CrypTool/DlgKeyHexFixedLen.cpp b/CrypTool/DlgKeyHexFixedLen.cpp index 6a8953d47..47a732deb 100644 --- a/CrypTool/DlgKeyHexFixedLen.cpp +++ b/CrypTool/DlgKeyHexFixedLen.cpp @@ -60,10 +60,10 @@ void CDlgKeyHexFixedLen::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgKeyHexFixedLen, CDialog) //{{AFX_MSG_MAP(CDlgKeyHexFixedLen) - ON_CBN_SELCHANGE(IDC_KEY_LEN, OnChangeKeyLen) - ON_BN_CLICKED(IDC_ENCRYPT, OnEncrypt) - ON_BN_CLICKED(IDC_DECRYPT, OnDecrypt) - ON_BN_CLICKED(IDC_PASTE_KEY, OnPasteKey) + ON_CBN_SELCHANGE(IDC_KEY_LEN, &CDlgKeyHexFixedLen::OnChangeKeyLen) + ON_BN_CLICKED(IDC_ENCRYPT, &CDlgKeyHexFixedLen::OnEncrypt) + ON_BN_CLICKED(IDC_DECRYPT, &CDlgKeyHexFixedLen::OnDecrypt) + ON_BN_CLICKED(IDC_PASTE_KEY, &CDlgKeyHexFixedLen::OnPasteKey) //}}AFX_MSG_MAP END_MESSAGE_MAP() diff --git a/CrypTool/DlgKeyHill10x10.cpp b/CrypTool/DlgKeyHill10x10.cpp index 89205c1d8..dd57d85b5 100644 --- a/CrypTool/DlgKeyHill10x10.cpp +++ b/CrypTool/DlgKeyHill10x10.cpp @@ -31,7 +31,7 @@ #include "HillEncryption.h" #include "ChrTools.h" #include "KeyRepository.h" -#include ".\dlgkeyhill10x10.h" +#include "DlgKeyHill10x10.h" #include "DlgHillOptions.h" #include "CrypToolTools.h" #include "assert.h" @@ -280,335 +280,335 @@ void CDlgKeyHill10x10::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgKeyHill10x10, CDialog) //{{AFX_MSG_MAP(CDlgKeyHill10x10) - ON_EN_UPDATE(IDC_EDIT11, OnUpdateMat11) - ON_EN_UPDATE(IDC_EDIT12, OnUpdateMat12) - ON_EN_UPDATE(IDC_EDIT13, OnUpdateMat13) - ON_EN_UPDATE(IDC_EDIT14, OnUpdateMat14) - ON_EN_UPDATE(IDC_EDIT15, OnUpdateMat15) - ON_EN_UPDATE(IDC_EDIT16, OnUpdateMat16) - ON_EN_UPDATE(IDC_EDIT17, OnUpdateMat17) - ON_EN_UPDATE(IDC_EDIT18, OnUpdateMat18) - ON_EN_UPDATE(IDC_EDIT19, OnUpdateMat19) - ON_EN_UPDATE(IDC_EDIT110, OnUpdateMat110) - ON_EN_UPDATE(IDC_EDIT21, OnUpdateMat21) - ON_EN_UPDATE(IDC_EDIT22, OnUpdateMat22) - ON_EN_UPDATE(IDC_EDIT23, OnUpdateMat23) - ON_EN_UPDATE(IDC_EDIT24, OnUpdateMat24) - ON_EN_UPDATE(IDC_EDIT25, OnUpdateMat25) - ON_EN_UPDATE(IDC_EDIT26, OnUpdateMat26) - ON_EN_UPDATE(IDC_EDIT27, OnUpdateMat27) - ON_EN_UPDATE(IDC_EDIT28, OnUpdateMat28) - ON_EN_UPDATE(IDC_EDIT29, OnUpdateMat29) - ON_EN_UPDATE(IDC_EDIT210, OnUpdateMat210) - ON_EN_UPDATE(IDC_EDIT31, OnUpdateMat31) - ON_EN_UPDATE(IDC_EDIT32, OnUpdateMat32) - ON_EN_UPDATE(IDC_EDIT33, OnUpdateMat33) - ON_EN_UPDATE(IDC_EDIT34, OnUpdateMat34) - ON_EN_UPDATE(IDC_EDIT35, OnUpdateMat35) - ON_EN_UPDATE(IDC_EDIT36, OnUpdateMat36) - ON_EN_UPDATE(IDC_EDIT37, OnUpdateMat37) - ON_EN_UPDATE(IDC_EDIT38, OnUpdateMat38) - ON_EN_UPDATE(IDC_EDIT39, OnUpdateMat39) - ON_EN_UPDATE(IDC_EDIT310, OnUpdateMat310) - ON_EN_UPDATE(IDC_EDIT41, OnUpdateMat41) - ON_EN_UPDATE(IDC_EDIT42, OnUpdateMat42) - ON_EN_UPDATE(IDC_EDIT43, OnUpdateMat43) - ON_EN_UPDATE(IDC_EDIT44, OnUpdateMat44) - ON_EN_UPDATE(IDC_EDIT45, OnUpdateMat45) - ON_EN_UPDATE(IDC_EDIT46, OnUpdateMat46) - ON_EN_UPDATE(IDC_EDIT47, OnUpdateMat47) - ON_EN_UPDATE(IDC_EDIT48, OnUpdateMat48) - ON_EN_UPDATE(IDC_EDIT49, OnUpdateMat49) - ON_EN_UPDATE(IDC_EDIT410, OnUpdateMat410) - ON_EN_UPDATE(IDC_EDIT51, OnUpdateMat51) - ON_EN_UPDATE(IDC_EDIT52, OnUpdateMat52) - ON_EN_UPDATE(IDC_EDIT53, OnUpdateMat53) - ON_EN_UPDATE(IDC_EDIT54, OnUpdateMat54) - ON_EN_UPDATE(IDC_EDIT55, OnUpdateMat55) - ON_EN_UPDATE(IDC_EDIT56, OnUpdateMat56) - ON_EN_UPDATE(IDC_EDIT57, OnUpdateMat57) - ON_EN_UPDATE(IDC_EDIT58, OnUpdateMat58) - ON_EN_UPDATE(IDC_EDIT59, OnUpdateMat59) - ON_EN_UPDATE(IDC_EDIT510, OnUpdateMat510) - ON_EN_UPDATE(IDC_EDIT61, OnUpdateMat61) - ON_EN_UPDATE(IDC_EDIT62, OnUpdateMat62) - ON_EN_UPDATE(IDC_EDIT63, OnUpdateMat63) - ON_EN_UPDATE(IDC_EDIT64, OnUpdateMat64) - ON_EN_UPDATE(IDC_EDIT65, OnUpdateMat65) - ON_EN_UPDATE(IDC_EDIT66, OnUpdateMat66) - ON_EN_UPDATE(IDC_EDIT67, OnUpdateMat67) - ON_EN_UPDATE(IDC_EDIT68, OnUpdateMat68) - ON_EN_UPDATE(IDC_EDIT69, OnUpdateMat69) - ON_EN_UPDATE(IDC_EDIT610, OnUpdateMat610) - ON_EN_UPDATE(IDC_EDIT71, OnUpdateMat71) - ON_EN_UPDATE(IDC_EDIT72, OnUpdateMat72) - ON_EN_UPDATE(IDC_EDIT73, OnUpdateMat73) - ON_EN_UPDATE(IDC_EDIT74, OnUpdateMat74) - ON_EN_UPDATE(IDC_EDIT75, OnUpdateMat75) - ON_EN_UPDATE(IDC_EDIT76, OnUpdateMat76) - ON_EN_UPDATE(IDC_EDIT77, OnUpdateMat77) - ON_EN_UPDATE(IDC_EDIT78, OnUpdateMat78) - ON_EN_UPDATE(IDC_EDIT79, OnUpdateMat79) - ON_EN_UPDATE(IDC_EDIT710, OnUpdateMat710) - ON_EN_UPDATE(IDC_EDIT81, OnUpdateMat81) - ON_EN_UPDATE(IDC_EDIT82, OnUpdateMat82) - ON_EN_UPDATE(IDC_EDIT83, OnUpdateMat83) - ON_EN_UPDATE(IDC_EDIT84, OnUpdateMat84) - ON_EN_UPDATE(IDC_EDIT85, OnUpdateMat85) - ON_EN_UPDATE(IDC_EDIT86, OnUpdateMat86) - ON_EN_UPDATE(IDC_EDIT87, OnUpdateMat87) - ON_EN_UPDATE(IDC_EDIT88, OnUpdateMat88) - ON_EN_UPDATE(IDC_EDIT89, OnUpdateMat89) - ON_EN_UPDATE(IDC_EDIT810, OnUpdateMat810) - ON_EN_UPDATE(IDC_EDIT91, OnUpdateMat91) - ON_EN_UPDATE(IDC_EDIT92, OnUpdateMat92) - ON_EN_UPDATE(IDC_EDIT93, OnUpdateMat93) - ON_EN_UPDATE(IDC_EDIT94, OnUpdateMat94) - ON_EN_UPDATE(IDC_EDIT95, OnUpdateMat95) - ON_EN_UPDATE(IDC_EDIT96, OnUpdateMat96) - ON_EN_UPDATE(IDC_EDIT97, OnUpdateMat97) - ON_EN_UPDATE(IDC_EDIT98, OnUpdateMat98) - ON_EN_UPDATE(IDC_EDIT99, OnUpdateMat99) - ON_EN_UPDATE(IDC_EDIT910, OnUpdateMat910) - ON_EN_UPDATE(IDC_EDIT101, OnUpdateMat101) - ON_EN_UPDATE(IDC_EDIT102, OnUpdateMat102) - ON_EN_UPDATE(IDC_EDIT103, OnUpdateMat103) - ON_EN_UPDATE(IDC_EDIT104, OnUpdateMat104) - ON_EN_UPDATE(IDC_EDIT105, OnUpdateMat105) - ON_EN_UPDATE(IDC_EDIT106, OnUpdateMat106) - ON_EN_UPDATE(IDC_EDIT107, OnUpdateMat107) - ON_EN_UPDATE(IDC_EDIT108, OnUpdateMat108) - ON_EN_UPDATE(IDC_EDIT109, OnUpdateMat109) - ON_EN_UPDATE(IDC_EDIT1010, OnUpdateMat1010) + ON_EN_UPDATE(IDC_EDIT11, &CDlgKeyHill10x10::OnUpdateMat11) + ON_EN_UPDATE(IDC_EDIT12, &CDlgKeyHill10x10::OnUpdateMat12) + ON_EN_UPDATE(IDC_EDIT13, &CDlgKeyHill10x10::OnUpdateMat13) + ON_EN_UPDATE(IDC_EDIT14, &CDlgKeyHill10x10::OnUpdateMat14) + ON_EN_UPDATE(IDC_EDIT15, &CDlgKeyHill10x10::OnUpdateMat15) + ON_EN_UPDATE(IDC_EDIT16, &CDlgKeyHill10x10::OnUpdateMat16) + ON_EN_UPDATE(IDC_EDIT17, &CDlgKeyHill10x10::OnUpdateMat17) + ON_EN_UPDATE(IDC_EDIT18, &CDlgKeyHill10x10::OnUpdateMat18) + ON_EN_UPDATE(IDC_EDIT19, &CDlgKeyHill10x10::OnUpdateMat19) + ON_EN_UPDATE(IDC_EDIT110, &CDlgKeyHill10x10::OnUpdateMat110) + ON_EN_UPDATE(IDC_EDIT21, &CDlgKeyHill10x10::OnUpdateMat21) + ON_EN_UPDATE(IDC_EDIT22, &CDlgKeyHill10x10::OnUpdateMat22) + ON_EN_UPDATE(IDC_EDIT23, &CDlgKeyHill10x10::OnUpdateMat23) + ON_EN_UPDATE(IDC_EDIT24, &CDlgKeyHill10x10::OnUpdateMat24) + ON_EN_UPDATE(IDC_EDIT25, &CDlgKeyHill10x10::OnUpdateMat25) + ON_EN_UPDATE(IDC_EDIT26, &CDlgKeyHill10x10::OnUpdateMat26) + ON_EN_UPDATE(IDC_EDIT27, &CDlgKeyHill10x10::OnUpdateMat27) + ON_EN_UPDATE(IDC_EDIT28, &CDlgKeyHill10x10::OnUpdateMat28) + ON_EN_UPDATE(IDC_EDIT29, &CDlgKeyHill10x10::OnUpdateMat29) + ON_EN_UPDATE(IDC_EDIT210, &CDlgKeyHill10x10::OnUpdateMat210) + ON_EN_UPDATE(IDC_EDIT31, &CDlgKeyHill10x10::OnUpdateMat31) + ON_EN_UPDATE(IDC_EDIT32, &CDlgKeyHill10x10::OnUpdateMat32) + ON_EN_UPDATE(IDC_EDIT33, &CDlgKeyHill10x10::OnUpdateMat33) + ON_EN_UPDATE(IDC_EDIT34, &CDlgKeyHill10x10::OnUpdateMat34) + ON_EN_UPDATE(IDC_EDIT35, &CDlgKeyHill10x10::OnUpdateMat35) + ON_EN_UPDATE(IDC_EDIT36, &CDlgKeyHill10x10::OnUpdateMat36) + ON_EN_UPDATE(IDC_EDIT37, &CDlgKeyHill10x10::OnUpdateMat37) + ON_EN_UPDATE(IDC_EDIT38, &CDlgKeyHill10x10::OnUpdateMat38) + ON_EN_UPDATE(IDC_EDIT39, &CDlgKeyHill10x10::OnUpdateMat39) + ON_EN_UPDATE(IDC_EDIT310, &CDlgKeyHill10x10::OnUpdateMat310) + ON_EN_UPDATE(IDC_EDIT41, &CDlgKeyHill10x10::OnUpdateMat41) + ON_EN_UPDATE(IDC_EDIT42, &CDlgKeyHill10x10::OnUpdateMat42) + ON_EN_UPDATE(IDC_EDIT43, &CDlgKeyHill10x10::OnUpdateMat43) + ON_EN_UPDATE(IDC_EDIT44, &CDlgKeyHill10x10::OnUpdateMat44) + ON_EN_UPDATE(IDC_EDIT45, &CDlgKeyHill10x10::OnUpdateMat45) + ON_EN_UPDATE(IDC_EDIT46, &CDlgKeyHill10x10::OnUpdateMat46) + ON_EN_UPDATE(IDC_EDIT47, &CDlgKeyHill10x10::OnUpdateMat47) + ON_EN_UPDATE(IDC_EDIT48, &CDlgKeyHill10x10::OnUpdateMat48) + ON_EN_UPDATE(IDC_EDIT49, &CDlgKeyHill10x10::OnUpdateMat49) + ON_EN_UPDATE(IDC_EDIT410, &CDlgKeyHill10x10::OnUpdateMat410) + ON_EN_UPDATE(IDC_EDIT51, &CDlgKeyHill10x10::OnUpdateMat51) + ON_EN_UPDATE(IDC_EDIT52, &CDlgKeyHill10x10::OnUpdateMat52) + ON_EN_UPDATE(IDC_EDIT53, &CDlgKeyHill10x10::OnUpdateMat53) + ON_EN_UPDATE(IDC_EDIT54, &CDlgKeyHill10x10::OnUpdateMat54) + ON_EN_UPDATE(IDC_EDIT55, &CDlgKeyHill10x10::OnUpdateMat55) + ON_EN_UPDATE(IDC_EDIT56, &CDlgKeyHill10x10::OnUpdateMat56) + ON_EN_UPDATE(IDC_EDIT57, &CDlgKeyHill10x10::OnUpdateMat57) + ON_EN_UPDATE(IDC_EDIT58, &CDlgKeyHill10x10::OnUpdateMat58) + ON_EN_UPDATE(IDC_EDIT59, &CDlgKeyHill10x10::OnUpdateMat59) + ON_EN_UPDATE(IDC_EDIT510, &CDlgKeyHill10x10::OnUpdateMat510) + ON_EN_UPDATE(IDC_EDIT61, &CDlgKeyHill10x10::OnUpdateMat61) + ON_EN_UPDATE(IDC_EDIT62, &CDlgKeyHill10x10::OnUpdateMat62) + ON_EN_UPDATE(IDC_EDIT63, &CDlgKeyHill10x10::OnUpdateMat63) + ON_EN_UPDATE(IDC_EDIT64, &CDlgKeyHill10x10::OnUpdateMat64) + ON_EN_UPDATE(IDC_EDIT65, &CDlgKeyHill10x10::OnUpdateMat65) + ON_EN_UPDATE(IDC_EDIT66, &CDlgKeyHill10x10::OnUpdateMat66) + ON_EN_UPDATE(IDC_EDIT67, &CDlgKeyHill10x10::OnUpdateMat67) + ON_EN_UPDATE(IDC_EDIT68, &CDlgKeyHill10x10::OnUpdateMat68) + ON_EN_UPDATE(IDC_EDIT69, &CDlgKeyHill10x10::OnUpdateMat69) + ON_EN_UPDATE(IDC_EDIT610, &CDlgKeyHill10x10::OnUpdateMat610) + ON_EN_UPDATE(IDC_EDIT71, &CDlgKeyHill10x10::OnUpdateMat71) + ON_EN_UPDATE(IDC_EDIT72, &CDlgKeyHill10x10::OnUpdateMat72) + ON_EN_UPDATE(IDC_EDIT73, &CDlgKeyHill10x10::OnUpdateMat73) + ON_EN_UPDATE(IDC_EDIT74, &CDlgKeyHill10x10::OnUpdateMat74) + ON_EN_UPDATE(IDC_EDIT75, &CDlgKeyHill10x10::OnUpdateMat75) + ON_EN_UPDATE(IDC_EDIT76, &CDlgKeyHill10x10::OnUpdateMat76) + ON_EN_UPDATE(IDC_EDIT77, &CDlgKeyHill10x10::OnUpdateMat77) + ON_EN_UPDATE(IDC_EDIT78, &CDlgKeyHill10x10::OnUpdateMat78) + ON_EN_UPDATE(IDC_EDIT79, &CDlgKeyHill10x10::OnUpdateMat79) + ON_EN_UPDATE(IDC_EDIT710, &CDlgKeyHill10x10::OnUpdateMat710) + ON_EN_UPDATE(IDC_EDIT81, &CDlgKeyHill10x10::OnUpdateMat81) + ON_EN_UPDATE(IDC_EDIT82, &CDlgKeyHill10x10::OnUpdateMat82) + ON_EN_UPDATE(IDC_EDIT83, &CDlgKeyHill10x10::OnUpdateMat83) + ON_EN_UPDATE(IDC_EDIT84, &CDlgKeyHill10x10::OnUpdateMat84) + ON_EN_UPDATE(IDC_EDIT85, &CDlgKeyHill10x10::OnUpdateMat85) + ON_EN_UPDATE(IDC_EDIT86, &CDlgKeyHill10x10::OnUpdateMat86) + ON_EN_UPDATE(IDC_EDIT87, &CDlgKeyHill10x10::OnUpdateMat87) + ON_EN_UPDATE(IDC_EDIT88, &CDlgKeyHill10x10::OnUpdateMat88) + ON_EN_UPDATE(IDC_EDIT89, &CDlgKeyHill10x10::OnUpdateMat89) + ON_EN_UPDATE(IDC_EDIT810, &CDlgKeyHill10x10::OnUpdateMat810) + ON_EN_UPDATE(IDC_EDIT91, &CDlgKeyHill10x10::OnUpdateMat91) + ON_EN_UPDATE(IDC_EDIT92, &CDlgKeyHill10x10::OnUpdateMat92) + ON_EN_UPDATE(IDC_EDIT93, &CDlgKeyHill10x10::OnUpdateMat93) + ON_EN_UPDATE(IDC_EDIT94, &CDlgKeyHill10x10::OnUpdateMat94) + ON_EN_UPDATE(IDC_EDIT95, &CDlgKeyHill10x10::OnUpdateMat95) + ON_EN_UPDATE(IDC_EDIT96, &CDlgKeyHill10x10::OnUpdateMat96) + ON_EN_UPDATE(IDC_EDIT97, &CDlgKeyHill10x10::OnUpdateMat97) + ON_EN_UPDATE(IDC_EDIT98, &CDlgKeyHill10x10::OnUpdateMat98) + ON_EN_UPDATE(IDC_EDIT99, &CDlgKeyHill10x10::OnUpdateMat99) + ON_EN_UPDATE(IDC_EDIT910, &CDlgKeyHill10x10::OnUpdateMat910) + ON_EN_UPDATE(IDC_EDIT101, &CDlgKeyHill10x10::OnUpdateMat101) + ON_EN_UPDATE(IDC_EDIT102, &CDlgKeyHill10x10::OnUpdateMat102) + ON_EN_UPDATE(IDC_EDIT103, &CDlgKeyHill10x10::OnUpdateMat103) + ON_EN_UPDATE(IDC_EDIT104, &CDlgKeyHill10x10::OnUpdateMat104) + ON_EN_UPDATE(IDC_EDIT105, &CDlgKeyHill10x10::OnUpdateMat105) + ON_EN_UPDATE(IDC_EDIT106, &CDlgKeyHill10x10::OnUpdateMat106) + ON_EN_UPDATE(IDC_EDIT107, &CDlgKeyHill10x10::OnUpdateMat107) + ON_EN_UPDATE(IDC_EDIT108, &CDlgKeyHill10x10::OnUpdateMat108) + ON_EN_UPDATE(IDC_EDIT109, &CDlgKeyHill10x10::OnUpdateMat109) + ON_EN_UPDATE(IDC_EDIT1010, &CDlgKeyHill10x10::OnUpdateMat1010) - ON_EN_UPDATE(IDC_EDIT40, OnUpdateMat40) - ON_EN_UPDATE(IDC_EDIT50, OnUpdateMat50) - ON_EN_UPDATE(IDC_EDIT20, OnUpdateMat20) - ON_EN_UPDATE(IDC_EDIT90, OnUpdateMat90) - ON_EN_UPDATE(IDC_EDIT100, OnUpdateMat100) - ON_EN_UPDATE(IDC_EDIT117, OnUpdateMat117) - ON_EN_UPDATE(IDC_EDIT118, OnUpdateMat118) - ON_EN_UPDATE(IDC_EDIT119, OnUpdateMat119) - ON_EN_UPDATE(IDC_EDIT60, OnUpdateMat60) - ON_EN_UPDATE(IDC_EDIT125, OnUpdateMat125) - ON_EN_UPDATE(IDC_EDIT127, OnUpdateMat127) - ON_EN_UPDATE(IDC_EDIT134, OnUpdateMat134) - ON_EN_UPDATE(IDC_EDIT135, OnUpdateMat135) - ON_EN_UPDATE(IDC_EDIT136, OnUpdateMat136) - ON_EN_UPDATE(IDC_EDIT70, OnUpdateMat70) - ON_EN_UPDATE(IDC_EDIT137, OnUpdateMat137) - ON_EN_UPDATE(IDC_EDIT138, OnUpdateMat138) - ON_EN_UPDATE(IDC_EDIT139, OnUpdateMat139) - ON_EN_UPDATE(IDC_EDIT80, OnUpdateMat80) - ON_EN_UPDATE(IDC_EDIT211, OnUpdateMat211) - ON_EN_UPDATE(IDC_EDIT140, OnUpdateMat140) - ON_EN_UPDATE(IDC_EDIT141, OnUpdateMat141) - ON_EN_UPDATE(IDC_EDIT142, OnUpdateMat142) - ON_EN_UPDATE(IDC_EDIT143, OnUpdateMat143) - ON_EN_UPDATE(IDC_EDIT144, OnUpdateMat144) - ON_EN_UPDATE(IDC_EDIT145, OnUpdateMat145) - ON_EN_UPDATE(IDC_EDIT146, OnUpdateMat146) - ON_EN_UPDATE(IDC_EDIT147, OnUpdateMat147) - ON_EN_UPDATE(IDC_EDIT148, OnUpdateMat148) - ON_EN_UPDATE(IDC_EDIT311, OnUpdateMat311) - ON_EN_UPDATE(IDC_EDIT149, OnUpdateMat149) - ON_EN_UPDATE(IDC_EDIT150, OnUpdateMat150) - ON_EN_UPDATE(IDC_EDIT151, OnUpdateMat151) - ON_EN_UPDATE(IDC_EDIT152, OnUpdateMat152) - ON_EN_UPDATE(IDC_EDIT153, OnUpdateMat153) - ON_EN_UPDATE(IDC_EDIT154, OnUpdateMat154) - ON_EN_UPDATE(IDC_EDIT155, OnUpdateMat155) - ON_EN_UPDATE(IDC_EDIT156, OnUpdateMat156) - ON_EN_UPDATE(IDC_EDIT157, OnUpdateMat157) - ON_EN_UPDATE(IDC_EDIT411, OnUpdateMat411) - ON_EN_UPDATE(IDC_EDIT158, OnUpdateMat158) - ON_EN_UPDATE(IDC_EDIT159, OnUpdateMat159) - ON_EN_UPDATE(IDC_EDIT113, OnUpdateMat113) - ON_EN_UPDATE(IDC_EDIT160, OnUpdateMat160) - ON_EN_UPDATE(IDC_EDIT161, OnUpdateMat161) - ON_EN_UPDATE(IDC_EDIT162, OnUpdateMat162) - ON_EN_UPDATE(IDC_EDIT163, OnUpdateMat163) - ON_EN_UPDATE(IDC_EDIT164, OnUpdateMat164) - ON_EN_UPDATE(IDC_EDIT165, OnUpdateMat165) - ON_EN_UPDATE(IDC_EDIT511, OnUpdateMat511) - ON_EN_UPDATE(IDC_EDIT166, OnUpdateMat166) - ON_EN_UPDATE(IDC_EDIT167, OnUpdateMat167) - ON_EN_UPDATE(IDC_EDIT168, OnUpdateMat168) - ON_EN_UPDATE(IDC_EDIT169, OnUpdateMat169) - ON_EN_UPDATE(IDC_EDIT170, OnUpdateMat170) - ON_EN_UPDATE(IDC_EDIT171, OnUpdateMat171) - ON_EN_UPDATE(IDC_EDIT172, OnUpdateMat172) - ON_EN_UPDATE(IDC_EDIT173, OnUpdateMat173) - ON_EN_UPDATE(IDC_EDIT174, OnUpdateMat174) - ON_EN_UPDATE(IDC_EDIT611, OnUpdateMat611) - ON_EN_UPDATE(IDC_EDIT175, OnUpdateMat175) - ON_EN_UPDATE(IDC_EDIT176, OnUpdateMat176) - ON_EN_UPDATE(IDC_EDIT177, OnUpdateMat177) - ON_EN_UPDATE(IDC_EDIT178, OnUpdateMat178) - ON_EN_UPDATE(IDC_EDIT179, OnUpdateMat179) - ON_EN_UPDATE(IDC_EDIT180, OnUpdateMat180) - ON_EN_UPDATE(IDC_EDIT181, OnUpdateMat181) - ON_EN_UPDATE(IDC_EDIT182, OnUpdateMat182) - ON_EN_UPDATE(IDC_EDIT183, OnUpdateMat183) - ON_EN_UPDATE(IDC_EDIT711, OnUpdateMat711) - ON_EN_UPDATE(IDC_EDIT184, OnUpdateMat184) - ON_EN_UPDATE(IDC_EDIT185, OnUpdateMat185) - ON_EN_UPDATE(IDC_EDIT186, OnUpdateMat186) - ON_EN_UPDATE(IDC_EDIT187, OnUpdateMat187) - ON_EN_UPDATE(IDC_EDIT188, OnUpdateMat188) - ON_EN_UPDATE(IDC_EDIT189, OnUpdateMat189) - ON_EN_UPDATE(IDC_EDIT190, OnUpdateMat190) - ON_EN_UPDATE(IDC_EDIT191, OnUpdateMat191) - ON_EN_UPDATE(IDC_EDIT192, OnUpdateMat192) - ON_EN_UPDATE(IDC_EDIT811, OnUpdateMat811) - ON_EN_UPDATE(IDC_EDIT193, OnUpdateMat193) - ON_EN_UPDATE(IDC_EDIT194, OnUpdateMat194) - ON_EN_UPDATE(IDC_EDIT195, OnUpdateMat195) - ON_EN_UPDATE(IDC_EDIT196, OnUpdateMat196) - ON_EN_UPDATE(IDC_EDIT197, OnUpdateMat197) - ON_EN_UPDATE(IDC_EDIT198, OnUpdateMat198) - ON_EN_UPDATE(IDC_EDIT199, OnUpdateMat199) - ON_EN_UPDATE(IDC_EDIT200, OnUpdateMat200) - ON_EN_UPDATE(IDC_EDIT201, OnUpdateMat201) - ON_EN_UPDATE(IDC_EDIT911, OnUpdateMat911) - ON_EN_UPDATE(IDC_EDIT202, OnUpdateMat202) - ON_EN_UPDATE(IDC_EDIT203, OnUpdateMat203) - ON_EN_UPDATE(IDC_EDIT204, OnUpdateMat204) - ON_EN_UPDATE(IDC_EDIT205, OnUpdateMat205) - ON_EN_UPDATE(IDC_EDIT206, OnUpdateMat206) - ON_EN_UPDATE(IDC_EDIT207, OnUpdateMat207) - ON_EN_UPDATE(IDC_EDIT208, OnUpdateMat208) - ON_EN_UPDATE(IDC_EDIT209, OnUpdateMat209) - ON_EN_UPDATE(IDC_EDIT212, OnUpdateMat212) - ON_EN_UPDATE(IDC_EDIT1011, OnUpdateMat1011) + ON_EN_UPDATE(IDC_EDIT40, &CDlgKeyHill10x10::OnUpdateMat40) + ON_EN_UPDATE(IDC_EDIT50, &CDlgKeyHill10x10::OnUpdateMat50) + ON_EN_UPDATE(IDC_EDIT20, &CDlgKeyHill10x10::OnUpdateMat20) + ON_EN_UPDATE(IDC_EDIT90, &CDlgKeyHill10x10::OnUpdateMat90) + ON_EN_UPDATE(IDC_EDIT100, &CDlgKeyHill10x10::OnUpdateMat100) + ON_EN_UPDATE(IDC_EDIT117, &CDlgKeyHill10x10::OnUpdateMat117) + ON_EN_UPDATE(IDC_EDIT118, &CDlgKeyHill10x10::OnUpdateMat118) + ON_EN_UPDATE(IDC_EDIT119, &CDlgKeyHill10x10::OnUpdateMat119) + ON_EN_UPDATE(IDC_EDIT60, &CDlgKeyHill10x10::OnUpdateMat60) + ON_EN_UPDATE(IDC_EDIT125, &CDlgKeyHill10x10::OnUpdateMat125) + ON_EN_UPDATE(IDC_EDIT127, &CDlgKeyHill10x10::OnUpdateMat127) + ON_EN_UPDATE(IDC_EDIT134, &CDlgKeyHill10x10::OnUpdateMat134) + ON_EN_UPDATE(IDC_EDIT135, &CDlgKeyHill10x10::OnUpdateMat135) + ON_EN_UPDATE(IDC_EDIT136, &CDlgKeyHill10x10::OnUpdateMat136) + ON_EN_UPDATE(IDC_EDIT70, &CDlgKeyHill10x10::OnUpdateMat70) + ON_EN_UPDATE(IDC_EDIT137, &CDlgKeyHill10x10::OnUpdateMat137) + ON_EN_UPDATE(IDC_EDIT138, &CDlgKeyHill10x10::OnUpdateMat138) + ON_EN_UPDATE(IDC_EDIT139, &CDlgKeyHill10x10::OnUpdateMat139) + ON_EN_UPDATE(IDC_EDIT80, &CDlgKeyHill10x10::OnUpdateMat80) + ON_EN_UPDATE(IDC_EDIT211, &CDlgKeyHill10x10::OnUpdateMat211) + ON_EN_UPDATE(IDC_EDIT140, &CDlgKeyHill10x10::OnUpdateMat140) + ON_EN_UPDATE(IDC_EDIT141, &CDlgKeyHill10x10::OnUpdateMat141) + ON_EN_UPDATE(IDC_EDIT142, &CDlgKeyHill10x10::OnUpdateMat142) + ON_EN_UPDATE(IDC_EDIT143, &CDlgKeyHill10x10::OnUpdateMat143) + ON_EN_UPDATE(IDC_EDIT144, &CDlgKeyHill10x10::OnUpdateMat144) + ON_EN_UPDATE(IDC_EDIT145, &CDlgKeyHill10x10::OnUpdateMat145) + ON_EN_UPDATE(IDC_EDIT146, &CDlgKeyHill10x10::OnUpdateMat146) + ON_EN_UPDATE(IDC_EDIT147, &CDlgKeyHill10x10::OnUpdateMat147) + ON_EN_UPDATE(IDC_EDIT148, &CDlgKeyHill10x10::OnUpdateMat148) + ON_EN_UPDATE(IDC_EDIT311, &CDlgKeyHill10x10::OnUpdateMat311) + ON_EN_UPDATE(IDC_EDIT149, &CDlgKeyHill10x10::OnUpdateMat149) + ON_EN_UPDATE(IDC_EDIT150, &CDlgKeyHill10x10::OnUpdateMat150) + ON_EN_UPDATE(IDC_EDIT151, &CDlgKeyHill10x10::OnUpdateMat151) + ON_EN_UPDATE(IDC_EDIT152, &CDlgKeyHill10x10::OnUpdateMat152) + ON_EN_UPDATE(IDC_EDIT153, &CDlgKeyHill10x10::OnUpdateMat153) + ON_EN_UPDATE(IDC_EDIT154, &CDlgKeyHill10x10::OnUpdateMat154) + ON_EN_UPDATE(IDC_EDIT155, &CDlgKeyHill10x10::OnUpdateMat155) + ON_EN_UPDATE(IDC_EDIT156, &CDlgKeyHill10x10::OnUpdateMat156) + ON_EN_UPDATE(IDC_EDIT157, &CDlgKeyHill10x10::OnUpdateMat157) + ON_EN_UPDATE(IDC_EDIT411, &CDlgKeyHill10x10::OnUpdateMat411) + ON_EN_UPDATE(IDC_EDIT158, &CDlgKeyHill10x10::OnUpdateMat158) + ON_EN_UPDATE(IDC_EDIT159, &CDlgKeyHill10x10::OnUpdateMat159) + ON_EN_UPDATE(IDC_EDIT113, &CDlgKeyHill10x10::OnUpdateMat113) + ON_EN_UPDATE(IDC_EDIT160, &CDlgKeyHill10x10::OnUpdateMat160) + ON_EN_UPDATE(IDC_EDIT161, &CDlgKeyHill10x10::OnUpdateMat161) + ON_EN_UPDATE(IDC_EDIT162, &CDlgKeyHill10x10::OnUpdateMat162) + ON_EN_UPDATE(IDC_EDIT163, &CDlgKeyHill10x10::OnUpdateMat163) + ON_EN_UPDATE(IDC_EDIT164, &CDlgKeyHill10x10::OnUpdateMat164) + ON_EN_UPDATE(IDC_EDIT165, &CDlgKeyHill10x10::OnUpdateMat165) + ON_EN_UPDATE(IDC_EDIT511, &CDlgKeyHill10x10::OnUpdateMat511) + ON_EN_UPDATE(IDC_EDIT166, &CDlgKeyHill10x10::OnUpdateMat166) + ON_EN_UPDATE(IDC_EDIT167, &CDlgKeyHill10x10::OnUpdateMat167) + ON_EN_UPDATE(IDC_EDIT168, &CDlgKeyHill10x10::OnUpdateMat168) + ON_EN_UPDATE(IDC_EDIT169, &CDlgKeyHill10x10::OnUpdateMat169) + ON_EN_UPDATE(IDC_EDIT170, &CDlgKeyHill10x10::OnUpdateMat170) + ON_EN_UPDATE(IDC_EDIT171, &CDlgKeyHill10x10::OnUpdateMat171) + ON_EN_UPDATE(IDC_EDIT172, &CDlgKeyHill10x10::OnUpdateMat172) + ON_EN_UPDATE(IDC_EDIT173, &CDlgKeyHill10x10::OnUpdateMat173) + ON_EN_UPDATE(IDC_EDIT174, &CDlgKeyHill10x10::OnUpdateMat174) + ON_EN_UPDATE(IDC_EDIT611, &CDlgKeyHill10x10::OnUpdateMat611) + ON_EN_UPDATE(IDC_EDIT175, &CDlgKeyHill10x10::OnUpdateMat175) + ON_EN_UPDATE(IDC_EDIT176, &CDlgKeyHill10x10::OnUpdateMat176) + ON_EN_UPDATE(IDC_EDIT177, &CDlgKeyHill10x10::OnUpdateMat177) + ON_EN_UPDATE(IDC_EDIT178, &CDlgKeyHill10x10::OnUpdateMat178) + ON_EN_UPDATE(IDC_EDIT179, &CDlgKeyHill10x10::OnUpdateMat179) + ON_EN_UPDATE(IDC_EDIT180, &CDlgKeyHill10x10::OnUpdateMat180) + ON_EN_UPDATE(IDC_EDIT181, &CDlgKeyHill10x10::OnUpdateMat181) + ON_EN_UPDATE(IDC_EDIT182, &CDlgKeyHill10x10::OnUpdateMat182) + ON_EN_UPDATE(IDC_EDIT183, &CDlgKeyHill10x10::OnUpdateMat183) + ON_EN_UPDATE(IDC_EDIT711, &CDlgKeyHill10x10::OnUpdateMat711) + ON_EN_UPDATE(IDC_EDIT184, &CDlgKeyHill10x10::OnUpdateMat184) + ON_EN_UPDATE(IDC_EDIT185, &CDlgKeyHill10x10::OnUpdateMat185) + ON_EN_UPDATE(IDC_EDIT186, &CDlgKeyHill10x10::OnUpdateMat186) + ON_EN_UPDATE(IDC_EDIT187, &CDlgKeyHill10x10::OnUpdateMat187) + ON_EN_UPDATE(IDC_EDIT188, &CDlgKeyHill10x10::OnUpdateMat188) + ON_EN_UPDATE(IDC_EDIT189, &CDlgKeyHill10x10::OnUpdateMat189) + ON_EN_UPDATE(IDC_EDIT190, &CDlgKeyHill10x10::OnUpdateMat190) + ON_EN_UPDATE(IDC_EDIT191, &CDlgKeyHill10x10::OnUpdateMat191) + ON_EN_UPDATE(IDC_EDIT192, &CDlgKeyHill10x10::OnUpdateMat192) + ON_EN_UPDATE(IDC_EDIT811, &CDlgKeyHill10x10::OnUpdateMat811) + ON_EN_UPDATE(IDC_EDIT193, &CDlgKeyHill10x10::OnUpdateMat193) + ON_EN_UPDATE(IDC_EDIT194, &CDlgKeyHill10x10::OnUpdateMat194) + ON_EN_UPDATE(IDC_EDIT195, &CDlgKeyHill10x10::OnUpdateMat195) + ON_EN_UPDATE(IDC_EDIT196, &CDlgKeyHill10x10::OnUpdateMat196) + ON_EN_UPDATE(IDC_EDIT197, &CDlgKeyHill10x10::OnUpdateMat197) + ON_EN_UPDATE(IDC_EDIT198, &CDlgKeyHill10x10::OnUpdateMat198) + ON_EN_UPDATE(IDC_EDIT199, &CDlgKeyHill10x10::OnUpdateMat199) + ON_EN_UPDATE(IDC_EDIT200, &CDlgKeyHill10x10::OnUpdateMat200) + ON_EN_UPDATE(IDC_EDIT201, &CDlgKeyHill10x10::OnUpdateMat201) + ON_EN_UPDATE(IDC_EDIT911, &CDlgKeyHill10x10::OnUpdateMat911) + ON_EN_UPDATE(IDC_EDIT202, &CDlgKeyHill10x10::OnUpdateMat202) + ON_EN_UPDATE(IDC_EDIT203, &CDlgKeyHill10x10::OnUpdateMat203) + ON_EN_UPDATE(IDC_EDIT204, &CDlgKeyHill10x10::OnUpdateMat204) + ON_EN_UPDATE(IDC_EDIT205, &CDlgKeyHill10x10::OnUpdateMat205) + ON_EN_UPDATE(IDC_EDIT206, &CDlgKeyHill10x10::OnUpdateMat206) + ON_EN_UPDATE(IDC_EDIT207, &CDlgKeyHill10x10::OnUpdateMat207) + ON_EN_UPDATE(IDC_EDIT208, &CDlgKeyHill10x10::OnUpdateMat208) + ON_EN_UPDATE(IDC_EDIT209, &CDlgKeyHill10x10::OnUpdateMat209) + ON_EN_UPDATE(IDC_EDIT212, &CDlgKeyHill10x10::OnUpdateMat212) + ON_EN_UPDATE(IDC_EDIT1011, &CDlgKeyHill10x10::OnUpdateMat1011) - ON_EN_KILLFOCUS(IDC_EDIT40, OnExitMat40) - ON_EN_KILLFOCUS(IDC_EDIT50, OnExitMat50) - ON_EN_KILLFOCUS(IDC_EDIT20, OnExitMat20) - ON_EN_KILLFOCUS(IDC_EDIT90, OnExitMat90) - ON_EN_KILLFOCUS(IDC_EDIT100, OnExitMat100) - ON_EN_KILLFOCUS(IDC_EDIT117, OnExitMat117) - ON_EN_KILLFOCUS(IDC_EDIT118, OnExitMat118) - ON_EN_KILLFOCUS(IDC_EDIT119, OnExitMat119) - ON_EN_KILLFOCUS(IDC_EDIT60, OnExitMat60) - ON_EN_KILLFOCUS(IDC_EDIT125, OnExitMat125) - ON_EN_KILLFOCUS(IDC_EDIT127, OnExitMat127) - ON_EN_KILLFOCUS(IDC_EDIT134, OnExitMat134) - ON_EN_KILLFOCUS(IDC_EDIT135, OnExitMat135) - ON_EN_KILLFOCUS(IDC_EDIT136, OnExitMat136) - ON_EN_KILLFOCUS(IDC_EDIT70, OnExitMat70) - ON_EN_KILLFOCUS(IDC_EDIT137, OnExitMat137) - ON_EN_KILLFOCUS(IDC_EDIT138, OnExitMat138) - ON_EN_KILLFOCUS(IDC_EDIT139, OnExitMat139) - ON_EN_KILLFOCUS(IDC_EDIT80, OnExitMat80) - ON_EN_KILLFOCUS(IDC_EDIT211, OnExitMat211) - ON_EN_KILLFOCUS(IDC_EDIT140, OnExitMat140) - ON_EN_KILLFOCUS(IDC_EDIT141, OnExitMat141) - ON_EN_KILLFOCUS(IDC_EDIT142, OnExitMat142) - ON_EN_KILLFOCUS(IDC_EDIT143, OnExitMat143) - ON_EN_KILLFOCUS(IDC_EDIT144, OnExitMat144) - ON_EN_KILLFOCUS(IDC_EDIT145, OnExitMat145) - ON_EN_KILLFOCUS(IDC_EDIT146, OnExitMat146) - ON_EN_KILLFOCUS(IDC_EDIT147, OnExitMat147) - ON_EN_KILLFOCUS(IDC_EDIT148, OnExitMat148) - ON_EN_KILLFOCUS(IDC_EDIT311, OnExitMat311) - ON_EN_KILLFOCUS(IDC_EDIT149, OnExitMat149) - ON_EN_KILLFOCUS(IDC_EDIT150, OnExitMat150) - ON_EN_KILLFOCUS(IDC_EDIT151, OnExitMat151) - ON_EN_KILLFOCUS(IDC_EDIT152, OnExitMat152) - ON_EN_KILLFOCUS(IDC_EDIT153, OnExitMat153) - ON_EN_KILLFOCUS(IDC_EDIT154, OnExitMat154) - ON_EN_KILLFOCUS(IDC_EDIT155, OnExitMat155) - ON_EN_KILLFOCUS(IDC_EDIT156, OnExitMat156) - ON_EN_KILLFOCUS(IDC_EDIT157, OnExitMat157) - ON_EN_KILLFOCUS(IDC_EDIT411, OnExitMat411) - ON_EN_KILLFOCUS(IDC_EDIT158, OnExitMat158) - ON_EN_KILLFOCUS(IDC_EDIT159, OnExitMat159) - ON_EN_KILLFOCUS(IDC_EDIT113, OnExitMat113) - ON_EN_KILLFOCUS(IDC_EDIT160, OnExitMat160) - ON_EN_KILLFOCUS(IDC_EDIT161, OnExitMat161) - ON_EN_KILLFOCUS(IDC_EDIT162, OnExitMat162) - ON_EN_KILLFOCUS(IDC_EDIT163, OnExitMat163) - ON_EN_KILLFOCUS(IDC_EDIT164, OnExitMat164) - ON_EN_KILLFOCUS(IDC_EDIT165, OnExitMat165) - ON_EN_KILLFOCUS(IDC_EDIT511, OnExitMat511) - ON_EN_KILLFOCUS(IDC_EDIT166, OnExitMat166) - ON_EN_KILLFOCUS(IDC_EDIT167, OnExitMat167) - ON_EN_KILLFOCUS(IDC_EDIT168, OnExitMat168) - ON_EN_KILLFOCUS(IDC_EDIT169, OnExitMat169) - ON_EN_KILLFOCUS(IDC_EDIT170, OnExitMat170) - ON_EN_KILLFOCUS(IDC_EDIT171, OnExitMat171) - ON_EN_KILLFOCUS(IDC_EDIT172, OnExitMat172) - ON_EN_KILLFOCUS(IDC_EDIT173, OnExitMat173) - ON_EN_KILLFOCUS(IDC_EDIT174, OnExitMat174) - ON_EN_KILLFOCUS(IDC_EDIT611, OnExitMat611) - ON_EN_KILLFOCUS(IDC_EDIT175, OnExitMat175) - ON_EN_KILLFOCUS(IDC_EDIT176, OnExitMat176) - ON_EN_KILLFOCUS(IDC_EDIT177, OnExitMat177) - ON_EN_KILLFOCUS(IDC_EDIT178, OnExitMat178) - ON_EN_KILLFOCUS(IDC_EDIT179, OnExitMat179) - ON_EN_KILLFOCUS(IDC_EDIT180, OnExitMat180) - ON_EN_KILLFOCUS(IDC_EDIT181, OnExitMat181) - ON_EN_KILLFOCUS(IDC_EDIT182, OnExitMat182) - ON_EN_KILLFOCUS(IDC_EDIT183, OnExitMat183) - ON_EN_KILLFOCUS(IDC_EDIT711, OnExitMat711) - ON_EN_KILLFOCUS(IDC_EDIT184, OnExitMat184) - ON_EN_KILLFOCUS(IDC_EDIT185, OnExitMat185) - ON_EN_KILLFOCUS(IDC_EDIT186, OnExitMat186) - ON_EN_KILLFOCUS(IDC_EDIT187, OnExitMat187) - ON_EN_KILLFOCUS(IDC_EDIT188, OnExitMat188) - ON_EN_KILLFOCUS(IDC_EDIT189, OnExitMat189) - ON_EN_KILLFOCUS(IDC_EDIT190, OnExitMat190) - ON_EN_KILLFOCUS(IDC_EDIT191, OnExitMat191) - ON_EN_KILLFOCUS(IDC_EDIT192, OnExitMat192) - ON_EN_KILLFOCUS(IDC_EDIT811, OnExitMat811) - ON_EN_KILLFOCUS(IDC_EDIT193, OnExitMat193) - ON_EN_KILLFOCUS(IDC_EDIT194, OnExitMat194) - ON_EN_KILLFOCUS(IDC_EDIT195, OnExitMat195) - ON_EN_KILLFOCUS(IDC_EDIT196, OnExitMat196) - ON_EN_KILLFOCUS(IDC_EDIT197, OnExitMat197) - ON_EN_KILLFOCUS(IDC_EDIT198, OnExitMat198) - ON_EN_KILLFOCUS(IDC_EDIT199, OnExitMat199) - ON_EN_KILLFOCUS(IDC_EDIT200, OnExitMat200) - ON_EN_KILLFOCUS(IDC_EDIT201, OnExitMat201) - ON_EN_KILLFOCUS(IDC_EDIT911, OnExitMat911) - ON_EN_KILLFOCUS(IDC_EDIT202, OnExitMat202) - ON_EN_KILLFOCUS(IDC_EDIT203, OnExitMat203) - ON_EN_KILLFOCUS(IDC_EDIT204, OnExitMat204) - ON_EN_KILLFOCUS(IDC_EDIT205, OnExitMat205) - ON_EN_KILLFOCUS(IDC_EDIT206, OnExitMat206) - ON_EN_KILLFOCUS(IDC_EDIT207, OnExitMat207) - ON_EN_KILLFOCUS(IDC_EDIT208, OnExitMat208) - ON_EN_KILLFOCUS(IDC_EDIT209, OnExitMat209) - ON_EN_KILLFOCUS(IDC_EDIT212, OnExitMat212) - ON_EN_KILLFOCUS(IDC_EDIT1011, OnExitMat1011) + ON_EN_KILLFOCUS(IDC_EDIT40, &CDlgKeyHill10x10::OnExitMat40) + ON_EN_KILLFOCUS(IDC_EDIT50, &CDlgKeyHill10x10::OnExitMat50) + ON_EN_KILLFOCUS(IDC_EDIT20, &CDlgKeyHill10x10::OnExitMat20) + ON_EN_KILLFOCUS(IDC_EDIT90, &CDlgKeyHill10x10::OnExitMat90) + ON_EN_KILLFOCUS(IDC_EDIT100, &CDlgKeyHill10x10::OnExitMat100) + ON_EN_KILLFOCUS(IDC_EDIT117, &CDlgKeyHill10x10::OnExitMat117) + ON_EN_KILLFOCUS(IDC_EDIT118, &CDlgKeyHill10x10::OnExitMat118) + ON_EN_KILLFOCUS(IDC_EDIT119, &CDlgKeyHill10x10::OnExitMat119) + ON_EN_KILLFOCUS(IDC_EDIT60, &CDlgKeyHill10x10::OnExitMat60) + ON_EN_KILLFOCUS(IDC_EDIT125, &CDlgKeyHill10x10::OnExitMat125) + ON_EN_KILLFOCUS(IDC_EDIT127, &CDlgKeyHill10x10::OnExitMat127) + ON_EN_KILLFOCUS(IDC_EDIT134, &CDlgKeyHill10x10::OnExitMat134) + ON_EN_KILLFOCUS(IDC_EDIT135, &CDlgKeyHill10x10::OnExitMat135) + ON_EN_KILLFOCUS(IDC_EDIT136, &CDlgKeyHill10x10::OnExitMat136) + ON_EN_KILLFOCUS(IDC_EDIT70, &CDlgKeyHill10x10::OnExitMat70) + ON_EN_KILLFOCUS(IDC_EDIT137, &CDlgKeyHill10x10::OnExitMat137) + ON_EN_KILLFOCUS(IDC_EDIT138, &CDlgKeyHill10x10::OnExitMat138) + ON_EN_KILLFOCUS(IDC_EDIT139, &CDlgKeyHill10x10::OnExitMat139) + ON_EN_KILLFOCUS(IDC_EDIT80, &CDlgKeyHill10x10::OnExitMat80) + ON_EN_KILLFOCUS(IDC_EDIT211, &CDlgKeyHill10x10::OnExitMat211) + ON_EN_KILLFOCUS(IDC_EDIT140, &CDlgKeyHill10x10::OnExitMat140) + ON_EN_KILLFOCUS(IDC_EDIT141, &CDlgKeyHill10x10::OnExitMat141) + ON_EN_KILLFOCUS(IDC_EDIT142, &CDlgKeyHill10x10::OnExitMat142) + ON_EN_KILLFOCUS(IDC_EDIT143, &CDlgKeyHill10x10::OnExitMat143) + ON_EN_KILLFOCUS(IDC_EDIT144, &CDlgKeyHill10x10::OnExitMat144) + ON_EN_KILLFOCUS(IDC_EDIT145, &CDlgKeyHill10x10::OnExitMat145) + ON_EN_KILLFOCUS(IDC_EDIT146, &CDlgKeyHill10x10::OnExitMat146) + ON_EN_KILLFOCUS(IDC_EDIT147, &CDlgKeyHill10x10::OnExitMat147) + ON_EN_KILLFOCUS(IDC_EDIT148, &CDlgKeyHill10x10::OnExitMat148) + ON_EN_KILLFOCUS(IDC_EDIT311, &CDlgKeyHill10x10::OnExitMat311) + ON_EN_KILLFOCUS(IDC_EDIT149, &CDlgKeyHill10x10::OnExitMat149) + ON_EN_KILLFOCUS(IDC_EDIT150, &CDlgKeyHill10x10::OnExitMat150) + ON_EN_KILLFOCUS(IDC_EDIT151, &CDlgKeyHill10x10::OnExitMat151) + ON_EN_KILLFOCUS(IDC_EDIT152, &CDlgKeyHill10x10::OnExitMat152) + ON_EN_KILLFOCUS(IDC_EDIT153, &CDlgKeyHill10x10::OnExitMat153) + ON_EN_KILLFOCUS(IDC_EDIT154, &CDlgKeyHill10x10::OnExitMat154) + ON_EN_KILLFOCUS(IDC_EDIT155, &CDlgKeyHill10x10::OnExitMat155) + ON_EN_KILLFOCUS(IDC_EDIT156, &CDlgKeyHill10x10::OnExitMat156) + ON_EN_KILLFOCUS(IDC_EDIT157, &CDlgKeyHill10x10::OnExitMat157) + ON_EN_KILLFOCUS(IDC_EDIT411, &CDlgKeyHill10x10::OnExitMat411) + ON_EN_KILLFOCUS(IDC_EDIT158, &CDlgKeyHill10x10::OnExitMat158) + ON_EN_KILLFOCUS(IDC_EDIT159, &CDlgKeyHill10x10::OnExitMat159) + ON_EN_KILLFOCUS(IDC_EDIT113, &CDlgKeyHill10x10::OnExitMat113) + ON_EN_KILLFOCUS(IDC_EDIT160, &CDlgKeyHill10x10::OnExitMat160) + ON_EN_KILLFOCUS(IDC_EDIT161, &CDlgKeyHill10x10::OnExitMat161) + ON_EN_KILLFOCUS(IDC_EDIT162, &CDlgKeyHill10x10::OnExitMat162) + ON_EN_KILLFOCUS(IDC_EDIT163, &CDlgKeyHill10x10::OnExitMat163) + ON_EN_KILLFOCUS(IDC_EDIT164, &CDlgKeyHill10x10::OnExitMat164) + ON_EN_KILLFOCUS(IDC_EDIT165, &CDlgKeyHill10x10::OnExitMat165) + ON_EN_KILLFOCUS(IDC_EDIT511, &CDlgKeyHill10x10::OnExitMat511) + ON_EN_KILLFOCUS(IDC_EDIT166, &CDlgKeyHill10x10::OnExitMat166) + ON_EN_KILLFOCUS(IDC_EDIT167, &CDlgKeyHill10x10::OnExitMat167) + ON_EN_KILLFOCUS(IDC_EDIT168, &CDlgKeyHill10x10::OnExitMat168) + ON_EN_KILLFOCUS(IDC_EDIT169, &CDlgKeyHill10x10::OnExitMat169) + ON_EN_KILLFOCUS(IDC_EDIT170, &CDlgKeyHill10x10::OnExitMat170) + ON_EN_KILLFOCUS(IDC_EDIT171, &CDlgKeyHill10x10::OnExitMat171) + ON_EN_KILLFOCUS(IDC_EDIT172, &CDlgKeyHill10x10::OnExitMat172) + ON_EN_KILLFOCUS(IDC_EDIT173, &CDlgKeyHill10x10::OnExitMat173) + ON_EN_KILLFOCUS(IDC_EDIT174, &CDlgKeyHill10x10::OnExitMat174) + ON_EN_KILLFOCUS(IDC_EDIT611, &CDlgKeyHill10x10::OnExitMat611) + ON_EN_KILLFOCUS(IDC_EDIT175, &CDlgKeyHill10x10::OnExitMat175) + ON_EN_KILLFOCUS(IDC_EDIT176, &CDlgKeyHill10x10::OnExitMat176) + ON_EN_KILLFOCUS(IDC_EDIT177, &CDlgKeyHill10x10::OnExitMat177) + ON_EN_KILLFOCUS(IDC_EDIT178, &CDlgKeyHill10x10::OnExitMat178) + ON_EN_KILLFOCUS(IDC_EDIT179, &CDlgKeyHill10x10::OnExitMat179) + ON_EN_KILLFOCUS(IDC_EDIT180, &CDlgKeyHill10x10::OnExitMat180) + ON_EN_KILLFOCUS(IDC_EDIT181, &CDlgKeyHill10x10::OnExitMat181) + ON_EN_KILLFOCUS(IDC_EDIT182, &CDlgKeyHill10x10::OnExitMat182) + ON_EN_KILLFOCUS(IDC_EDIT183, &CDlgKeyHill10x10::OnExitMat183) + ON_EN_KILLFOCUS(IDC_EDIT711, &CDlgKeyHill10x10::OnExitMat711) + ON_EN_KILLFOCUS(IDC_EDIT184, &CDlgKeyHill10x10::OnExitMat184) + ON_EN_KILLFOCUS(IDC_EDIT185, &CDlgKeyHill10x10::OnExitMat185) + ON_EN_KILLFOCUS(IDC_EDIT186, &CDlgKeyHill10x10::OnExitMat186) + ON_EN_KILLFOCUS(IDC_EDIT187, &CDlgKeyHill10x10::OnExitMat187) + ON_EN_KILLFOCUS(IDC_EDIT188, &CDlgKeyHill10x10::OnExitMat188) + ON_EN_KILLFOCUS(IDC_EDIT189, &CDlgKeyHill10x10::OnExitMat189) + ON_EN_KILLFOCUS(IDC_EDIT190, &CDlgKeyHill10x10::OnExitMat190) + ON_EN_KILLFOCUS(IDC_EDIT191, &CDlgKeyHill10x10::OnExitMat191) + ON_EN_KILLFOCUS(IDC_EDIT192, &CDlgKeyHill10x10::OnExitMat192) + ON_EN_KILLFOCUS(IDC_EDIT811, &CDlgKeyHill10x10::OnExitMat811) + ON_EN_KILLFOCUS(IDC_EDIT193, &CDlgKeyHill10x10::OnExitMat193) + ON_EN_KILLFOCUS(IDC_EDIT194, &CDlgKeyHill10x10::OnExitMat194) + ON_EN_KILLFOCUS(IDC_EDIT195, &CDlgKeyHill10x10::OnExitMat195) + ON_EN_KILLFOCUS(IDC_EDIT196, &CDlgKeyHill10x10::OnExitMat196) + ON_EN_KILLFOCUS(IDC_EDIT197, &CDlgKeyHill10x10::OnExitMat197) + ON_EN_KILLFOCUS(IDC_EDIT198, &CDlgKeyHill10x10::OnExitMat198) + ON_EN_KILLFOCUS(IDC_EDIT199, &CDlgKeyHill10x10::OnExitMat199) + ON_EN_KILLFOCUS(IDC_EDIT200, &CDlgKeyHill10x10::OnExitMat200) + ON_EN_KILLFOCUS(IDC_EDIT201, &CDlgKeyHill10x10::OnExitMat201) + ON_EN_KILLFOCUS(IDC_EDIT911, &CDlgKeyHill10x10::OnExitMat911) + ON_EN_KILLFOCUS(IDC_EDIT202, &CDlgKeyHill10x10::OnExitMat202) + ON_EN_KILLFOCUS(IDC_EDIT203, &CDlgKeyHill10x10::OnExitMat203) + ON_EN_KILLFOCUS(IDC_EDIT204, &CDlgKeyHill10x10::OnExitMat204) + ON_EN_KILLFOCUS(IDC_EDIT205, &CDlgKeyHill10x10::OnExitMat205) + ON_EN_KILLFOCUS(IDC_EDIT206, &CDlgKeyHill10x10::OnExitMat206) + ON_EN_KILLFOCUS(IDC_EDIT207, &CDlgKeyHill10x10::OnExitMat207) + ON_EN_KILLFOCUS(IDC_EDIT208, &CDlgKeyHill10x10::OnExitMat208) + ON_EN_KILLFOCUS(IDC_EDIT209, &CDlgKeyHill10x10::OnExitMat209) + ON_EN_KILLFOCUS(IDC_EDIT212, &CDlgKeyHill10x10::OnExitMat212) + ON_EN_KILLFOCUS(IDC_EDIT1011, &CDlgKeyHill10x10::OnExitMat1011) - ON_BN_CLICKED(IDC_RADIO1, OnDimension1) - ON_BN_CLICKED(IDC_RADIO2, OnDimension2) - ON_BN_CLICKED(IDC_RADIO3, OnDimension3) - ON_BN_CLICKED(IDC_RADIO4, OnDimension4) - ON_BN_CLICKED(IDC_RADIO5, OnDimension5) - ON_BN_CLICKED(IDC_RADIO6, OnDimension6) - ON_BN_CLICKED(IDC_RADIO7, OnDimension7) - ON_BN_CLICKED(IDC_RADIO8, OnDimension8) - ON_BN_CLICKED(IDC_RADIO9, OnDimension9) - ON_BN_CLICKED(IDC_RADIO10, OnDimension10) + ON_BN_CLICKED(IDC_RADIO1, &CDlgKeyHill10x10::OnDimension1) + ON_BN_CLICKED(IDC_RADIO2, &CDlgKeyHill10x10::OnDimension2) + ON_BN_CLICKED(IDC_RADIO3, &CDlgKeyHill10x10::OnDimension3) + ON_BN_CLICKED(IDC_RADIO4, &CDlgKeyHill10x10::OnDimension4) + ON_BN_CLICKED(IDC_RADIO5, &CDlgKeyHill10x10::OnDimension5) + ON_BN_CLICKED(IDC_RADIO6, &CDlgKeyHill10x10::OnDimension6) + ON_BN_CLICKED(IDC_RADIO7, &CDlgKeyHill10x10::OnDimension7) + ON_BN_CLICKED(IDC_RADIO8, &CDlgKeyHill10x10::OnDimension8) + ON_BN_CLICKED(IDC_RADIO9, &CDlgKeyHill10x10::OnDimension9) + ON_BN_CLICKED(IDC_RADIO10, &CDlgKeyHill10x10::OnDimension10) - ON_BN_CLICKED(IDC_RADIO21, OnEnableAlphCode) - ON_BN_CLICKED(IDC_RADIO22, OnDisableAlphCode) - ON_BN_CLICKED(IDC_RADIO23, OnRowVectorMatrix) - ON_BN_CLICKED(IDC_RADIO24, OnMatrixColumnVector) + ON_BN_CLICKED(IDC_RADIO21, &CDlgKeyHill10x10::OnEnableAlphCode) + ON_BN_CLICKED(IDC_RADIO22, &CDlgKeyHill10x10::OnDisableAlphCode) + ON_BN_CLICKED(IDC_RADIO23, &CDlgKeyHill10x10::OnRowVectorMatrix) + ON_BN_CLICKED(IDC_RADIO24, &CDlgKeyHill10x10::OnMatrixColumnVector) - ON_BN_CLICKED(IDC_BUTTON3, OnZufaelligerSchluessel) - ON_BN_CLICKED(IDC_BUTTON_RESETKEY, OnResetKey) - ON_BN_CLICKED(IDC_BUTTON4, OnKleinereSchluessel) - ON_BN_CLICKED(IDC_BUTTON5, OnDecrypt) - ON_BN_CLICKED(IDC_BUTTON2, OnPasteKey) + ON_BN_CLICKED(IDC_BUTTON3, &CDlgKeyHill10x10::OnZufaelligerSchluessel) + ON_BN_CLICKED(IDC_BUTTON_RESETKEY, &CDlgKeyHill10x10::OnResetKey) + ON_BN_CLICKED(IDC_BUTTON4, &CDlgKeyHill10x10::OnKleinereSchluessel) + ON_BN_CLICKED(IDC_BUTTON5, &CDlgKeyHill10x10::OnDecrypt) + ON_BN_CLICKED(IDC_BUTTON2, &CDlgKeyHill10x10::OnPasteKey) - ON_BN_CLICKED(IDC_BUTTON_TEXTOPTIONS, OnTextOptions) + ON_BN_CLICKED(IDC_BUTTON_TEXTOPTIONS, &CDlgKeyHill10x10::OnTextOptions) //}}AFX_MSG_MAP - ON_BN_CLICKED(IDC_BUTTON69, OnHillOptions) + ON_BN_CLICKED(IDC_BUTTON69, &CDlgKeyHill10x10::OnHillOptions) END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// diff --git a/CrypTool/DlgKeyHill5x5.cpp b/CrypTool/DlgKeyHill5x5.cpp index 980809ea6..d9a0cb55d 100644 --- a/CrypTool/DlgKeyHill5x5.cpp +++ b/CrypTool/DlgKeyHill5x5.cpp @@ -123,103 +123,103 @@ BEGIN_MESSAGE_MAP(CDlgKeyHill5x5, CDialog) //{{AFX_MSG_MAP(CDlgKeyHill5x5) // HillAlphMat - ON_EN_UPDATE(IDC_EDIT11, OnUpdateMat11) - ON_EN_UPDATE(IDC_EDIT12, OnUpdateMat12) - ON_EN_UPDATE(IDC_EDIT13, OnUpdateMat13) - ON_EN_UPDATE(IDC_EDIT14, OnUpdateMat14) - ON_EN_UPDATE(IDC_EDIT15, OnUpdateMat15) - ON_EN_UPDATE(IDC_EDIT21, OnUpdateMat21) - ON_EN_UPDATE(IDC_EDIT22, OnUpdateMat22) - ON_EN_UPDATE(IDC_EDIT23, OnUpdateMat23) - ON_EN_UPDATE(IDC_EDIT24, OnUpdateMat24) - ON_EN_UPDATE(IDC_EDIT25, OnUpdateMat25) - ON_EN_UPDATE(IDC_EDIT31, OnUpdateMat31) - ON_EN_UPDATE(IDC_EDIT32, OnUpdateMat32) - ON_EN_UPDATE(IDC_EDIT33, OnUpdateMat33) - ON_EN_UPDATE(IDC_EDIT34, OnUpdateMat34) - ON_EN_UPDATE(IDC_EDIT35, OnUpdateMat35) - ON_EN_UPDATE(IDC_EDIT41, OnUpdateMat41) - ON_EN_UPDATE(IDC_EDIT42, OnUpdateMat42) - ON_EN_UPDATE(IDC_EDIT43, OnUpdateMat43) - ON_EN_UPDATE(IDC_EDIT44, OnUpdateMat44) - ON_EN_UPDATE(IDC_EDIT45, OnUpdateMat45) - ON_EN_UPDATE(IDC_EDIT51, OnUpdateMat51) - ON_EN_UPDATE(IDC_EDIT52, OnUpdateMat52) - ON_EN_UPDATE(IDC_EDIT53, OnUpdateMat53) - ON_EN_UPDATE(IDC_EDIT54, OnUpdateMat54) - ON_EN_UPDATE(IDC_EDIT55, OnUpdateMat55) + ON_EN_UPDATE(IDC_EDIT11, &CDlgKeyHill5x5::OnUpdateMat11) + ON_EN_UPDATE(IDC_EDIT12, &CDlgKeyHill5x5::OnUpdateMat12) + ON_EN_UPDATE(IDC_EDIT13, &CDlgKeyHill5x5::OnUpdateMat13) + ON_EN_UPDATE(IDC_EDIT14, &CDlgKeyHill5x5::OnUpdateMat14) + ON_EN_UPDATE(IDC_EDIT15, &CDlgKeyHill5x5::OnUpdateMat15) + ON_EN_UPDATE(IDC_EDIT21, &CDlgKeyHill5x5::OnUpdateMat21) + ON_EN_UPDATE(IDC_EDIT22, &CDlgKeyHill5x5::OnUpdateMat22) + ON_EN_UPDATE(IDC_EDIT23, &CDlgKeyHill5x5::OnUpdateMat23) + ON_EN_UPDATE(IDC_EDIT24, &CDlgKeyHill5x5::OnUpdateMat24) + ON_EN_UPDATE(IDC_EDIT25, &CDlgKeyHill5x5::OnUpdateMat25) + ON_EN_UPDATE(IDC_EDIT31, &CDlgKeyHill5x5::OnUpdateMat31) + ON_EN_UPDATE(IDC_EDIT32, &CDlgKeyHill5x5::OnUpdateMat32) + ON_EN_UPDATE(IDC_EDIT33, &CDlgKeyHill5x5::OnUpdateMat33) + ON_EN_UPDATE(IDC_EDIT34, &CDlgKeyHill5x5::OnUpdateMat34) + ON_EN_UPDATE(IDC_EDIT35, &CDlgKeyHill5x5::OnUpdateMat35) + ON_EN_UPDATE(IDC_EDIT41, &CDlgKeyHill5x5::OnUpdateMat41) + ON_EN_UPDATE(IDC_EDIT42, &CDlgKeyHill5x5::OnUpdateMat42) + ON_EN_UPDATE(IDC_EDIT43, &CDlgKeyHill5x5::OnUpdateMat43) + ON_EN_UPDATE(IDC_EDIT44, &CDlgKeyHill5x5::OnUpdateMat44) + ON_EN_UPDATE(IDC_EDIT45, &CDlgKeyHill5x5::OnUpdateMat45) + ON_EN_UPDATE(IDC_EDIT51, &CDlgKeyHill5x5::OnUpdateMat51) + ON_EN_UPDATE(IDC_EDIT52, &CDlgKeyHill5x5::OnUpdateMat52) + ON_EN_UPDATE(IDC_EDIT53, &CDlgKeyHill5x5::OnUpdateMat53) + ON_EN_UPDATE(IDC_EDIT54, &CDlgKeyHill5x5::OnUpdateMat54) + ON_EN_UPDATE(IDC_EDIT55, &CDlgKeyHill5x5::OnUpdateMat55) // HillNumMat - ON_EN_UPDATE(IDC_EDIT36, OnUpdateMat36) - ON_EN_UPDATE(IDC_EDIT37, OnUpdateMat37) - ON_EN_UPDATE(IDC_EDIT38, OnUpdateMat38) - ON_EN_UPDATE(IDC_EDIT40, OnUpdateMat40) - ON_EN_UPDATE(IDC_EDIT46, OnUpdateMat46) - ON_EN_UPDATE(IDC_EDIT47, OnUpdateMat47) - ON_EN_UPDATE(IDC_EDIT48, OnUpdateMat48) - ON_EN_UPDATE(IDC_EDIT49, OnUpdateMat49) - ON_EN_UPDATE(IDC_EDIT50, OnUpdateMat50) - ON_EN_UPDATE(IDC_EDIT111, OnUpdateMat111) - ON_EN_UPDATE(IDC_EDIT57, OnUpdateMat57) - ON_EN_UPDATE(IDC_EDIT58, OnUpdateMat58) - ON_EN_UPDATE(IDC_EDIT59, OnUpdateMat59) - ON_EN_UPDATE(IDC_EDIT56, OnUpdateMat56) - ON_EN_UPDATE(IDC_EDIT115, OnUpdateMat115) - ON_EN_UPDATE(IDC_EDIT60, OnUpdateMat60) - ON_EN_UPDATE(IDC_EDIT61, OnUpdateMat61) - ON_EN_UPDATE(IDC_EDIT62, OnUpdateMat62) - ON_EN_UPDATE(IDC_EDIT116, OnUpdateMat116) - ON_EN_UPDATE(IDC_EDIT117, OnUpdateMat117) - ON_EN_UPDATE(IDC_EDIT63, OnUpdateMat63) - ON_EN_UPDATE(IDC_EDIT64, OnUpdateMat64) - ON_EN_UPDATE(IDC_EDIT69, OnUpdateMat69) - ON_EN_UPDATE(IDC_EDIT65, OnUpdateMat65) - ON_EN_UPDATE(IDC_EDIT66, OnUpdateMat66) + ON_EN_UPDATE(IDC_EDIT36, &CDlgKeyHill5x5::OnUpdateMat36) + ON_EN_UPDATE(IDC_EDIT37, &CDlgKeyHill5x5::OnUpdateMat37) + ON_EN_UPDATE(IDC_EDIT38, &CDlgKeyHill5x5::OnUpdateMat38) + ON_EN_UPDATE(IDC_EDIT40, &CDlgKeyHill5x5::OnUpdateMat40) + ON_EN_UPDATE(IDC_EDIT46, &CDlgKeyHill5x5::OnUpdateMat46) + ON_EN_UPDATE(IDC_EDIT47, &CDlgKeyHill5x5::OnUpdateMat47) + ON_EN_UPDATE(IDC_EDIT48, &CDlgKeyHill5x5::OnUpdateMat48) + ON_EN_UPDATE(IDC_EDIT49, &CDlgKeyHill5x5::OnUpdateMat49) + ON_EN_UPDATE(IDC_EDIT50, &CDlgKeyHill5x5::OnUpdateMat50) + ON_EN_UPDATE(IDC_EDIT111, &CDlgKeyHill5x5::OnUpdateMat111) + ON_EN_UPDATE(IDC_EDIT57, &CDlgKeyHill5x5::OnUpdateMat57) + ON_EN_UPDATE(IDC_EDIT58, &CDlgKeyHill5x5::OnUpdateMat58) + ON_EN_UPDATE(IDC_EDIT59, &CDlgKeyHill5x5::OnUpdateMat59) + ON_EN_UPDATE(IDC_EDIT56, &CDlgKeyHill5x5::OnUpdateMat56) + ON_EN_UPDATE(IDC_EDIT115, &CDlgKeyHill5x5::OnUpdateMat115) + ON_EN_UPDATE(IDC_EDIT60, &CDlgKeyHill5x5::OnUpdateMat60) + ON_EN_UPDATE(IDC_EDIT61, &CDlgKeyHill5x5::OnUpdateMat61) + ON_EN_UPDATE(IDC_EDIT62, &CDlgKeyHill5x5::OnUpdateMat62) + ON_EN_UPDATE(IDC_EDIT116, &CDlgKeyHill5x5::OnUpdateMat116) + ON_EN_UPDATE(IDC_EDIT117, &CDlgKeyHill5x5::OnUpdateMat117) + ON_EN_UPDATE(IDC_EDIT63, &CDlgKeyHill5x5::OnUpdateMat63) + ON_EN_UPDATE(IDC_EDIT64, &CDlgKeyHill5x5::OnUpdateMat64) + ON_EN_UPDATE(IDC_EDIT69, &CDlgKeyHill5x5::OnUpdateMat69) + ON_EN_UPDATE(IDC_EDIT65, &CDlgKeyHill5x5::OnUpdateMat65) + ON_EN_UPDATE(IDC_EDIT66, &CDlgKeyHill5x5::OnUpdateMat66) - ON_EN_KILLFOCUS(IDC_EDIT36, OnExitMat36) - ON_EN_KILLFOCUS(IDC_EDIT37, OnExitMat37) - ON_EN_KILLFOCUS(IDC_EDIT38, OnExitMat38) - ON_EN_KILLFOCUS(IDC_EDIT40, OnExitMat40) - ON_EN_KILLFOCUS(IDC_EDIT46, OnExitMat46) - ON_EN_KILLFOCUS(IDC_EDIT47, OnExitMat47) - ON_EN_KILLFOCUS(IDC_EDIT48, OnExitMat48) - ON_EN_KILLFOCUS(IDC_EDIT49, OnExitMat49) - ON_EN_KILLFOCUS(IDC_EDIT50, OnExitMat50) - ON_EN_KILLFOCUS(IDC_EDIT111, OnExitMat111) - ON_EN_KILLFOCUS(IDC_EDIT57, OnExitMat57) - ON_EN_KILLFOCUS(IDC_EDIT58, OnExitMat58) - ON_EN_KILLFOCUS(IDC_EDIT59, OnExitMat59) - ON_EN_KILLFOCUS(IDC_EDIT56, OnExitMat56) - ON_EN_KILLFOCUS(IDC_EDIT115, OnExitMat115) - ON_EN_KILLFOCUS(IDC_EDIT60, OnExitMat60) - ON_EN_KILLFOCUS(IDC_EDIT61, OnExitMat61) - ON_EN_KILLFOCUS(IDC_EDIT62, OnExitMat62) - ON_EN_KILLFOCUS(IDC_EDIT116, OnExitMat116) - ON_EN_KILLFOCUS(IDC_EDIT117, OnExitMat117) - ON_EN_KILLFOCUS(IDC_EDIT63, OnExitMat63) - ON_EN_KILLFOCUS(IDC_EDIT64, OnExitMat64) - ON_EN_KILLFOCUS(IDC_EDIT69, OnExitMat69) - ON_EN_KILLFOCUS(IDC_EDIT65, OnExitMat65) - ON_EN_KILLFOCUS(IDC_EDIT66, OnExitMat66) + ON_EN_KILLFOCUS(IDC_EDIT36, &CDlgKeyHill5x5::OnExitMat36) + ON_EN_KILLFOCUS(IDC_EDIT37, &CDlgKeyHill5x5::OnExitMat37) + ON_EN_KILLFOCUS(IDC_EDIT38, &CDlgKeyHill5x5::OnExitMat38) + ON_EN_KILLFOCUS(IDC_EDIT40, &CDlgKeyHill5x5::OnExitMat40) + ON_EN_KILLFOCUS(IDC_EDIT46, &CDlgKeyHill5x5::OnExitMat46) + ON_EN_KILLFOCUS(IDC_EDIT47, &CDlgKeyHill5x5::OnExitMat47) + ON_EN_KILLFOCUS(IDC_EDIT48, &CDlgKeyHill5x5::OnExitMat48) + ON_EN_KILLFOCUS(IDC_EDIT49, &CDlgKeyHill5x5::OnExitMat49) + ON_EN_KILLFOCUS(IDC_EDIT50, &CDlgKeyHill5x5::OnExitMat50) + ON_EN_KILLFOCUS(IDC_EDIT111, &CDlgKeyHill5x5::OnExitMat111) + ON_EN_KILLFOCUS(IDC_EDIT57, &CDlgKeyHill5x5::OnExitMat57) + ON_EN_KILLFOCUS(IDC_EDIT58, &CDlgKeyHill5x5::OnExitMat58) + ON_EN_KILLFOCUS(IDC_EDIT59, &CDlgKeyHill5x5::OnExitMat59) + ON_EN_KILLFOCUS(IDC_EDIT56, &CDlgKeyHill5x5::OnExitMat56) + ON_EN_KILLFOCUS(IDC_EDIT115, &CDlgKeyHill5x5::OnExitMat115) + ON_EN_KILLFOCUS(IDC_EDIT60, &CDlgKeyHill5x5::OnExitMat60) + ON_EN_KILLFOCUS(IDC_EDIT61, &CDlgKeyHill5x5::OnExitMat61) + ON_EN_KILLFOCUS(IDC_EDIT62, &CDlgKeyHill5x5::OnExitMat62) + ON_EN_KILLFOCUS(IDC_EDIT116, &CDlgKeyHill5x5::OnExitMat116) + ON_EN_KILLFOCUS(IDC_EDIT117, &CDlgKeyHill5x5::OnExitMat117) + ON_EN_KILLFOCUS(IDC_EDIT63, &CDlgKeyHill5x5::OnExitMat63) + ON_EN_KILLFOCUS(IDC_EDIT64, &CDlgKeyHill5x5::OnExitMat64) + ON_EN_KILLFOCUS(IDC_EDIT69, &CDlgKeyHill5x5::OnExitMat69) + ON_EN_KILLFOCUS(IDC_EDIT65, &CDlgKeyHill5x5::OnExitMat65) + ON_EN_KILLFOCUS(IDC_EDIT66, &CDlgKeyHill5x5::OnExitMat66) // Select Matrix Dimension - ON_BN_CLICKED(IDC_RADIO1, OnDimension1) - ON_BN_CLICKED(IDC_RADIO2, OnDimension2) - ON_BN_CLICKED(IDC_RADIO3, OnDimension3) - ON_BN_CLICKED(IDC_RADIO4, OnDimension4) - ON_BN_CLICKED(IDC_RADIO5, OnDimension5) - - ON_BN_CLICKED(IDC_RADIO6, OnEnableAlphCode) - ON_BN_CLICKED(IDC_RADIO7, OnDisableAlphCode) - ON_BN_CLICKED(IDC_RADIO8, OnRowVectorMatrix) - ON_BN_CLICKED(IDC_RADIO9, OnMatrixColumnVector) - - ON_BN_CLICKED(IDC_BUTTON3, OnZufaelligerSchluessel) - ON_BN_CLICKED(IDC_BUTTON_RESETKEY, OnResetKey) - ON_BN_CLICKED(IDC_BUTTON4, OnGroessereSchluessel) - ON_BN_CLICKED(IDC_BUTTON5, OnDecrypt) - ON_BN_CLICKED(IDC_BUTTON2, OnPasteKey) - ON_BN_CLICKED(IDC_BUTTON1, OnHillOptions) - ON_BN_CLICKED(IDC_BUTTON_TEXTOPTIONS, OnTextOptions) + ON_BN_CLICKED(IDC_RADIO1, &CDlgKeyHill5x5::OnDimension1) + ON_BN_CLICKED(IDC_RADIO2, &CDlgKeyHill5x5::OnDimension2) + ON_BN_CLICKED(IDC_RADIO3, &CDlgKeyHill5x5::OnDimension3) + ON_BN_CLICKED(IDC_RADIO4, &CDlgKeyHill5x5::OnDimension4) + ON_BN_CLICKED(IDC_RADIO5, &CDlgKeyHill5x5::OnDimension5) + + ON_BN_CLICKED(IDC_RADIO6, &CDlgKeyHill5x5::OnEnableAlphCode) + ON_BN_CLICKED(IDC_RADIO7, &CDlgKeyHill5x5::OnDisableAlphCode) + ON_BN_CLICKED(IDC_RADIO8, &CDlgKeyHill5x5::OnRowVectorMatrix) + ON_BN_CLICKED(IDC_RADIO9, &CDlgKeyHill5x5::OnMatrixColumnVector) + + ON_BN_CLICKED(IDC_BUTTON3, &CDlgKeyHill5x5::OnZufaelligerSchluessel) + ON_BN_CLICKED(IDC_BUTTON_RESETKEY, &CDlgKeyHill5x5::OnResetKey) + ON_BN_CLICKED(IDC_BUTTON4, &CDlgKeyHill5x5::OnGroessereSchluessel) + ON_BN_CLICKED(IDC_BUTTON5, &CDlgKeyHill5x5::OnDecrypt) + ON_BN_CLICKED(IDC_BUTTON2, &CDlgKeyHill5x5::OnPasteKey) + ON_BN_CLICKED(IDC_BUTTON1, &CDlgKeyHill5x5::OnHillOptions) + ON_BN_CLICKED(IDC_BUTTON_TEXTOPTIONS, &CDlgKeyHill5x5::OnTextOptions) //}}AFX_MSG_MAP END_MESSAGE_MAP() diff --git a/CrypTool/DlgKeyHomophone.cpp b/CrypTool/DlgKeyHomophone.cpp index 55196eb83..dcfa8667c 100644 --- a/CrypTool/DlgKeyHomophone.cpp +++ b/CrypTool/DlgKeyHomophone.cpp @@ -28,7 +28,7 @@ #include "assert.h" #include "DialogeMessage.h" #include "KeyRepository.h" -#include ".\dlgkeyhomophone.h" +#include "DlgKeyHomophone.h" #ifdef _DEBUG #define new DEBUG_NEW @@ -84,20 +84,20 @@ void CDlgKeyHomophone::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgKeyHomophone, CDialog) //{{AFX_MSG_MAP(CDlgKeyHomophone) - ON_BN_CLICKED(IDC_ERZEUGEN, OnErzeugen) - ON_BN_CLICKED(IDC_BUTTON2, OnLoadKey) - ON_BN_CLICKED(IDC_BUTTON1, OnDecrypt) - ON_BN_CLICKED(IDOK, OnEncrypt) - ON_BN_CLICKED(IDC_RADIO4, OnHex) - ON_BN_CLICKED(IDC_RADIO5, OnDecimal) - ON_BN_CLICKED(IDC_BUTTON3, OnActualizeNoOfHomophones) - ON_NOTIFY(NM_CLICK, IDC_LIST1, OnSelectList) - ON_NOTIFY(NM_DBLCLK, IDC_LIST1, OnDblclkSelect) - ON_NOTIFY(NM_RETURN, IDC_LIST1, OnReturnSelect) - ON_BN_CLICKED(IDC_CHECK1, OnSelectEncryptFormatCharacters) + ON_BN_CLICKED(IDC_ERZEUGEN, &CDlgKeyHomophone::OnErzeugen) + ON_BN_CLICKED(IDC_BUTTON2, &CDlgKeyHomophone::OnLoadKey) + ON_BN_CLICKED(IDC_BUTTON1, &CDlgKeyHomophone::OnDecrypt) + ON_BN_CLICKED(IDOK, &CDlgKeyHomophone::OnEncrypt) + ON_BN_CLICKED(IDC_RADIO4, &CDlgKeyHomophone::OnHex) + ON_BN_CLICKED(IDC_RADIO5, &CDlgKeyHomophone::OnDecimal) + ON_BN_CLICKED(IDC_BUTTON3, &CDlgKeyHomophone::OnActualizeNoOfHomophones) + ON_NOTIFY(NM_CLICK, IDC_LIST1, &CDlgKeyHomophone::OnSelectList) + ON_NOTIFY(NM_DBLCLK, IDC_LIST1, &CDlgKeyHomophone::OnDblclkSelect) + ON_NOTIFY(NM_RETURN, IDC_LIST1, &CDlgKeyHomophone::OnReturnSelect) + ON_BN_CLICKED(IDC_CHECK1, &CDlgKeyHomophone::OnSelectEncryptFormatCharacters) //}}AFX_MSG_MAP - ON_BN_CLICKED(IDC_RADIO_TEXT_INPUT, OnBnClickedRadioTextInput) - ON_BN_CLICKED(IDC_RADIO_BINARY_INPUT, OnBnClickedRadioBinaryInput) + ON_BN_CLICKED(IDC_RADIO_TEXT_INPUT, &CDlgKeyHomophone::OnBnClickedRadioTextInput) + ON_BN_CLICKED(IDC_RADIO_BINARY_INPUT, &CDlgKeyHomophone::OnBnClickedRadioBinaryInput) ON_BN_CLICKED(IDC_BUTTON_TEXTOPTIONS, &CDlgKeyHomophone::OnBnClickedButtonTextoptions) END_MESSAGE_MAP() diff --git a/CrypTool/DlgKeyPermutation.cpp b/CrypTool/DlgKeyPermutation.cpp index 10062f887..46e44a381 100644 --- a/CrypTool/DlgKeyPermutation.cpp +++ b/CrypTool/DlgKeyPermutation.cpp @@ -29,7 +29,7 @@ #include "KeyRepository.h" #include "DialogeMessage.h" -#include ".\dlgkeypermutation.h" +#include "DlgKeyPermutation.h" #include "CrypToolTools.h" #include "ChrTools.h" @@ -90,14 +90,14 @@ void CDlgKeyPermutation::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgKeyPermutation, CDialog) //{{AFX_MSG_MAP(CDlgKeyPermutation) - ON_BN_CLICKED(IDC_BUTTON1, OnDecrypt) - ON_BN_CLICKED(IDOK, OnEncrypt) - ON_BN_CLICKED(IDC_BUTTON2, OnPasteKey) - ON_BN_CLICKED(IDC_BUTTON_TxtOpt, OnTextOptions) - ON_EN_CHANGE(IDC_EDIT1, OnChangeEdit1) - ON_EN_CHANGE(IDC_EDIT2, OnChangeEdit2) - ON_BN_CLICKED(IDC_RADIO9, OnRadioButtonBinaryData) - ON_BN_CLICKED(IDC_RADIO14, OnRadioButtonText) + ON_BN_CLICKED(IDC_BUTTON1, &CDlgKeyPermutation::OnDecrypt) + ON_BN_CLICKED(IDOK, &CDlgKeyPermutation::OnEncrypt) + ON_BN_CLICKED(IDC_BUTTON2, &CDlgKeyPermutation::OnPasteKey) + ON_BN_CLICKED(IDC_BUTTON_TxtOpt, &CDlgKeyPermutation::OnTextOptions) + ON_EN_CHANGE(IDC_EDIT1, &CDlgKeyPermutation::OnChangeEdit1) + ON_EN_CHANGE(IDC_EDIT2, &CDlgKeyPermutation::OnChangeEdit2) + ON_BN_CLICKED(IDC_RADIO9, &CDlgKeyPermutation::OnRadioButtonBinaryData) + ON_BN_CLICKED(IDC_RADIO14, &CDlgKeyPermutation::OnRadioButtonText) //}}AFX_MSG_MAP END_MESSAGE_MAP() diff --git a/CrypTool/DlgKeyPermutationInfo.cpp b/CrypTool/DlgKeyPermutationInfo.cpp index 9a3621a96..effcf624b 100644 --- a/CrypTool/DlgKeyPermutationInfo.cpp +++ b/CrypTool/DlgKeyPermutationInfo.cpp @@ -24,7 +24,7 @@ #include "stdafx.h" #include "CrypToolApp.h" #include "DlgKeyPermutationInfo.h" -#include ".\dlgkeypermutationinfo.h" +#include "DlgKeyPermutationInfo.h" #ifdef _DEBUG #define new DEBUG_NEW @@ -64,7 +64,7 @@ void CDlgKeyPermutationInfo::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgKeyPermutationInfo, CDialog) //{{AFX_MSG_MAP(CDlgKeyPermutationInfo) //}}AFX_MSG_MAP - ON_BN_CLICKED(IDBACK, OnBnClickedBack) + ON_BN_CLICKED(IDBACK, &CDlgKeyPermutationInfo::OnBnClickedBack) END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// diff --git a/CrypTool/DlgKeyPlayfair.cpp b/CrypTool/DlgKeyPlayfair.cpp index 609cc774c..3582f4cba 100644 --- a/CrypTool/DlgKeyPlayfair.cpp +++ b/CrypTool/DlgKeyPlayfair.cpp @@ -138,19 +138,19 @@ void CDlgKeyPlayfair::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgKeyPlayfair, CDialog) //{{AFX_MSG_MAP(CDlgKeyPlayfair) - ON_EN_UPDATE(IDC_KEYENTER, OnUpdateEdit1) - ON_BN_CLICKED(IDC_RADIO3, OnSechs) - ON_BN_CLICKED(IDC_RADIO4, OnSechs) - ON_BN_CLICKED(IDC_BUTTON1, OnDecrypt) - ON_BN_CLICKED(IDC_BUTTON2, OnPasteKey) - ON_BN_CLICKED(IDOK, OnEncrypt) - ON_BN_CLICKED(IDC_CHECK1, OnCheck) - ON_BN_CLICKED(IDC_PREFORM, OnCheck) - ON_BN_CLICKED(IDC_CHECK_SEPARATE_LETTERS, OnCheck) - ON_EN_CHANGE(IDC_EDIT_SEPARATOR1, OnChangeSeparator) - ON_EN_CHANGE(IDC_EDIT_SEPARATOR2, OnChangeSeparator) - ON_BN_CLICKED(IDC_CHECK_SEPARATE_LETTERS_ONLY_IN_PAIRS, OnCheck) - ON_BN_CLICKED(IDC_CHECK_IGNORE_DOUBLE_CHARACTERS_IN_KEY, OnCheck) + ON_EN_UPDATE(IDC_KEYENTER, &CDlgKeyPlayfair::OnUpdateEdit1) + ON_BN_CLICKED(IDC_RADIO3, &CDlgKeyPlayfair::OnSechs) + ON_BN_CLICKED(IDC_RADIO4, &CDlgKeyPlayfair::OnSechs) + ON_BN_CLICKED(IDC_BUTTON1, &CDlgKeyPlayfair::OnDecrypt) + ON_BN_CLICKED(IDC_BUTTON2, &CDlgKeyPlayfair::OnPasteKey) + ON_BN_CLICKED(IDOK, &CDlgKeyPlayfair::OnEncrypt) + ON_BN_CLICKED(IDC_CHECK1, &CDlgKeyPlayfair::OnCheck) + ON_BN_CLICKED(IDC_PREFORM, &CDlgKeyPlayfair::OnCheck) + ON_BN_CLICKED(IDC_CHECK_SEPARATE_LETTERS, &CDlgKeyPlayfair::OnCheck) + ON_EN_CHANGE(IDC_EDIT_SEPARATOR1, &CDlgKeyPlayfair::OnChangeSeparator) + ON_EN_CHANGE(IDC_EDIT_SEPARATOR2, &CDlgKeyPlayfair::OnChangeSeparator) + ON_BN_CLICKED(IDC_CHECK_SEPARATE_LETTERS_ONLY_IN_PAIRS, &CDlgKeyPlayfair::OnCheck) + ON_BN_CLICKED(IDC_CHECK_IGNORE_DOUBLE_CHARACTERS_IN_KEY, &CDlgKeyPlayfair::OnCheck) //}}AFX_MSG_MAP END_MESSAGE_MAP() diff --git a/CrypTool/DlgKeyPlayfair.h b/CrypTool/DlgKeyPlayfair.h index 76087a394..416578ccc 100644 --- a/CrypTool/DlgKeyPlayfair.h +++ b/CrypTool/DlgKeyPlayfair.h @@ -29,7 +29,7 @@ // Copyright 1998-2001 Deutsche Bank AG, Frankfurt am Main ////////////////////////////////////////////////////////////////// -#include +#include "PlayfairAnalysis.h" #include "ToolTipButton.h" // Dlg_PlayfairKey.h : Header-Datei diff --git a/CrypTool/DlgKeySubstitution.cpp b/CrypTool/DlgKeySubstitution.cpp index 19a19cfc7..c0bfac5a8 100644 --- a/CrypTool/DlgKeySubstitution.cpp +++ b/CrypTool/DlgKeySubstitution.cpp @@ -65,10 +65,10 @@ void CDlgKeySubstitution::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgKeySubstitution, CDialog) //{{AFX_MSG_MAP(CDlgKeySubstitution) - ON_EN_CHANGE(IDC_EDIT1, OnChangeEdit1) - ON_BN_CLICKED(IDC_BUTTON1, OnDecrypt) - ON_BN_CLICKED(IDOK, OnEncrypt) - ON_BN_CLICKED(IDC_BUTTON2, OnPasteKey) + ON_EN_CHANGE(IDC_EDIT1, &CDlgKeySubstitution::OnChangeEdit1) + ON_BN_CLICKED(IDC_BUTTON1, &CDlgKeySubstitution::OnDecrypt) + ON_BN_CLICKED(IDOK, &CDlgKeySubstitution::OnEncrypt) + ON_BN_CLICKED(IDC_BUTTON2, &CDlgKeySubstitution::OnPasteKey) //}}AFX_MSG_MAP END_MESSAGE_MAP() diff --git a/CrypTool/DlgKeyVigenere.cpp b/CrypTool/DlgKeyVigenere.cpp index f00bb8c71..ab771c572 100644 --- a/CrypTool/DlgKeyVigenere.cpp +++ b/CrypTool/DlgKeyVigenere.cpp @@ -58,7 +58,7 @@ void CDlgKeyVigenereAnalyisis::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgKeyVigenereAnalyisis, CDialog) //{{AFX_MSG_MAP(CDlgKeyVigenereAnalyisis) - ON_EN_UPDATE(IDC_EDIT1, OnUpdateEdit1) + ON_EN_UPDATE(IDC_EDIT1, &CDlgKeyVigenereAnalyisis::OnUpdateEdit1) //}}AFX_MSG_MAP END_MESSAGE_MAP() diff --git a/CrypTool/DlgLengthOfANumber.cpp b/CrypTool/DlgLengthOfANumber.cpp index 265077eac..b6122513b 100644 --- a/CrypTool/DlgLengthOfANumber.cpp +++ b/CrypTool/DlgLengthOfANumber.cpp @@ -71,11 +71,11 @@ BOOL CDlgLengthOfANumber::OnInitDialog() } BEGIN_MESSAGE_MAP(CDlgLengthOfANumber, CDialog) - ON_BN_CLICKED(IDC_RADIO_BINARY, updateNumberRepresentation) - ON_BN_CLICKED(IDC_RADIO_OCTAL, updateNumberRepresentation) - ON_BN_CLICKED(IDC_RADIO_DECIMAL, updateNumberRepresentation) - ON_BN_CLICKED(IDC_RADIO_HEXADECIMAL, updateNumberRepresentation) - ON_EN_CHANGE(IDC_EDIT_NUMBER, updateNumber) + ON_BN_CLICKED(IDC_RADIO_BINARY, &CDlgLengthOfANumber::updateNumberRepresentation) + ON_BN_CLICKED(IDC_RADIO_OCTAL, &CDlgLengthOfANumber::updateNumberRepresentation) + ON_BN_CLICKED(IDC_RADIO_DECIMAL, &CDlgLengthOfANumber::updateNumberRepresentation) + ON_BN_CLICKED(IDC_RADIO_HEXADECIMAL, &CDlgLengthOfANumber::updateNumberRepresentation) + ON_EN_CHANGE(IDC_EDIT_NUMBER, &CDlgLengthOfANumber::updateNumber) END_MESSAGE_MAP() void CDlgLengthOfANumber::updateNumberRepresentation() diff --git a/CrypTool/DlgMac.cpp b/CrypTool/DlgMac.cpp index 51c9d8f21..b2e002d04 100644 --- a/CrypTool/DlgMac.cpp +++ b/CrypTool/DlgMac.cpp @@ -24,7 +24,7 @@ #include "SecudeCryptography.h" #include "CrypToolTools.h" #include "FileTools.h" -#include ".\dlgmac.h" +#include "DlgMac.h" #ifdef _DEBUG #define new DEBUG_NEW @@ -81,21 +81,21 @@ void CDlgMac::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgMac, CDialog) //{{AFX_MSG_MAP(CDlgMac) - ON_BN_CLICKED(IDC_RADIO11, OnBUTTONSecondKey) - ON_EN_CHANGE(IDC_SHOW_TEXT, OnEditText) - ON_EN_CHANGE(IDC_EDIT_KEY, OnEditKey) - ON_BN_CLICKED(IDC_RADIO7, OnBUTTONFront) - ON_BN_CLICKED(IDC_RADIO8, OnBUTTONBack) - ON_BN_CLICKED(IDC_RADIO9, OnBUTTONBoth) - ON_BN_CLICKED(IDC_RADIO10, OnBUTTONDouble) - ON_EN_CHANGE(IDC_EDIT_KEY2, OnEditSecondKey) - ON_BN_CLICKED(IDOK, OnOK) - ON_BN_CLICKED(IDC_RADIO1, OnBUTTONHashFunction) - ON_BN_CLICKED(IDC_RADIO2, OnBUTTONHashFunction) - ON_BN_CLICKED(IDC_RADIO3, OnBUTTONHashFunction) - ON_BN_CLICKED(IDC_RADIO4, OnBUTTONHashFunction) - ON_BN_CLICKED(IDC_RADIO5, OnBUTTONHashFunction) - ON_BN_CLICKED(IDC_RADIO6, OnBUTTONHashFunction) + ON_BN_CLICKED(IDC_RADIO11, &CDlgMac::OnBUTTONSecondKey) + ON_EN_CHANGE(IDC_SHOW_TEXT, &CDlgMac::OnEditText) + ON_EN_CHANGE(IDC_EDIT_KEY, &CDlgMac::OnEditKey) + ON_BN_CLICKED(IDC_RADIO7, &CDlgMac::OnBUTTONFront) + ON_BN_CLICKED(IDC_RADIO8, &CDlgMac::OnBUTTONBack) + ON_BN_CLICKED(IDC_RADIO9, &CDlgMac::OnBUTTONBoth) + ON_BN_CLICKED(IDC_RADIO10, &CDlgMac::OnBUTTONDouble) + ON_EN_CHANGE(IDC_EDIT_KEY2, &CDlgMac::OnEditSecondKey) + ON_BN_CLICKED(IDOK, &CDlgMac::OnOK) + ON_BN_CLICKED(IDC_RADIO1, &CDlgMac::OnBUTTONHashFunction) + ON_BN_CLICKED(IDC_RADIO2, &CDlgMac::OnBUTTONHashFunction) + ON_BN_CLICKED(IDC_RADIO3, &CDlgMac::OnBUTTONHashFunction) + ON_BN_CLICKED(IDC_RADIO4, &CDlgMac::OnBUTTONHashFunction) + ON_BN_CLICKED(IDC_RADIO5, &CDlgMac::OnBUTTONHashFunction) + ON_BN_CLICKED(IDC_RADIO6, &CDlgMac::OnBUTTONHashFunction) //}}AFX_MSG_MAP END_MESSAGE_MAP() diff --git a/CrypTool/DlgManualSubstAnalysis.cpp b/CrypTool/DlgManualSubstAnalysis.cpp index 5ac7e5f27..d2fcd7bef 100644 --- a/CrypTool/DlgManualSubstAnalysis.cpp +++ b/CrypTool/DlgManualSubstAnalysis.cpp @@ -159,37 +159,37 @@ void CDlgManualSubstAnalysis::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgManualSubstAnalysis, CDialog) //{{AFX_MSG_MAP(CDlgManualSubstAnalysis) - ON_BN_CLICKED(IDC_BUTTON2, OnButton2) - ON_EN_CHANGE(IDC_EDIT1, OnChangeEdit1) - ON_EN_CHANGE(IDC_EDIT2, OnChangeEdit2) - ON_EN_CHANGE(IDC_EDIT3, OnChangeEdit3) - ON_EN_CHANGE(IDC_EDIT4, OnChangeEdit4) - ON_EN_CHANGE(IDC_EDIT5, OnChangeEdit5) - ON_EN_CHANGE(IDC_EDIT6, OnChangeEdit6) - ON_EN_CHANGE(IDC_EDIT7, OnChangeEdit7) - ON_EN_CHANGE(IDC_EDIT8, OnChangeEdit8) - ON_EN_CHANGE(IDC_EDIT9, OnChangeEdit9) - ON_EN_CHANGE(IDC_EDIT10, OnChangeEdit10) - ON_EN_CHANGE(IDC_EDIT11, OnChangeEdit11) - ON_EN_CHANGE(IDC_EDIT12, OnChangeEdit12) - ON_EN_CHANGE(IDC_EDIT13, OnChangeEdit13) - ON_EN_CHANGE(IDC_EDIT14, OnChangeEdit14) - ON_EN_CHANGE(IDC_EDIT15, OnChangeEdit15) - ON_EN_CHANGE(IDC_EDIT16, OnChangeEdit16) - ON_EN_CHANGE(IDC_EDIT17, OnChangeEdit17) - ON_EN_CHANGE(IDC_EDIT18, OnChangeEdit18) - ON_EN_CHANGE(IDC_EDIT19, OnChangeEdit19) - ON_EN_CHANGE(IDC_EDIT20, OnChangeEdit20) - ON_EN_CHANGE(IDC_EDIT21, OnChangeEdit21) - ON_EN_CHANGE(IDC_EDIT22, OnChangeEdit22) - ON_EN_CHANGE(IDC_EDIT23, OnChangeEdit23) - ON_EN_CHANGE(IDC_EDIT24, OnChangeEdit24) - ON_EN_CHANGE(IDC_EDIT25, OnChangeEdit25) - ON_EN_CHANGE(IDC_EDIT26, OnChangeEdit26) - ON_EN_CHANGE(IDC_EDIT27, OnChangeEdit27) - ON_BN_CLICKED(IDC_UNDO_, OnUndo) - ON_BN_CLICKED(IDC_BUTTON1, OnCopyKey) - ON_BN_CLICKED(IDC_REDO_, OnRedo) + ON_BN_CLICKED(IDC_BUTTON2, &CDlgManualSubstAnalysis::OnButton2) + ON_EN_CHANGE(IDC_EDIT1, &CDlgManualSubstAnalysis::OnChangeEdit1) + ON_EN_CHANGE(IDC_EDIT2, &CDlgManualSubstAnalysis::OnChangeEdit2) + ON_EN_CHANGE(IDC_EDIT3, &CDlgManualSubstAnalysis::OnChangeEdit3) + ON_EN_CHANGE(IDC_EDIT4, &CDlgManualSubstAnalysis::OnChangeEdit4) + ON_EN_CHANGE(IDC_EDIT5, &CDlgManualSubstAnalysis::OnChangeEdit5) + ON_EN_CHANGE(IDC_EDIT6, &CDlgManualSubstAnalysis::OnChangeEdit6) + ON_EN_CHANGE(IDC_EDIT7, &CDlgManualSubstAnalysis::OnChangeEdit7) + ON_EN_CHANGE(IDC_EDIT8, &CDlgManualSubstAnalysis::OnChangeEdit8) + ON_EN_CHANGE(IDC_EDIT9, &CDlgManualSubstAnalysis::OnChangeEdit9) + ON_EN_CHANGE(IDC_EDIT10, &CDlgManualSubstAnalysis::OnChangeEdit10) + ON_EN_CHANGE(IDC_EDIT11, &CDlgManualSubstAnalysis::OnChangeEdit11) + ON_EN_CHANGE(IDC_EDIT12, &CDlgManualSubstAnalysis::OnChangeEdit12) + ON_EN_CHANGE(IDC_EDIT13, &CDlgManualSubstAnalysis::OnChangeEdit13) + ON_EN_CHANGE(IDC_EDIT14, &CDlgManualSubstAnalysis::OnChangeEdit14) + ON_EN_CHANGE(IDC_EDIT15, &CDlgManualSubstAnalysis::OnChangeEdit15) + ON_EN_CHANGE(IDC_EDIT16, &CDlgManualSubstAnalysis::OnChangeEdit16) + ON_EN_CHANGE(IDC_EDIT17, &CDlgManualSubstAnalysis::OnChangeEdit17) + ON_EN_CHANGE(IDC_EDIT18, &CDlgManualSubstAnalysis::OnChangeEdit18) + ON_EN_CHANGE(IDC_EDIT19, &CDlgManualSubstAnalysis::OnChangeEdit19) + ON_EN_CHANGE(IDC_EDIT20, &CDlgManualSubstAnalysis::OnChangeEdit20) + ON_EN_CHANGE(IDC_EDIT21, &CDlgManualSubstAnalysis::OnChangeEdit21) + ON_EN_CHANGE(IDC_EDIT22, &CDlgManualSubstAnalysis::OnChangeEdit22) + ON_EN_CHANGE(IDC_EDIT23, &CDlgManualSubstAnalysis::OnChangeEdit23) + ON_EN_CHANGE(IDC_EDIT24, &CDlgManualSubstAnalysis::OnChangeEdit24) + ON_EN_CHANGE(IDC_EDIT25, &CDlgManualSubstAnalysis::OnChangeEdit25) + ON_EN_CHANGE(IDC_EDIT26, &CDlgManualSubstAnalysis::OnChangeEdit26) + ON_EN_CHANGE(IDC_EDIT27, &CDlgManualSubstAnalysis::OnChangeEdit27) + ON_BN_CLICKED(IDC_UNDO_, &CDlgManualSubstAnalysis::OnUndo) + ON_BN_CLICKED(IDC_BUTTON1, &CDlgManualSubstAnalysis::OnCopyKey) + ON_BN_CLICKED(IDC_REDO_, &CDlgManualSubstAnalysis::OnRedo) //}}AFX_MSG_MAP END_MESSAGE_MAP() diff --git a/CrypTool/DlgMonSubst.cpp b/CrypTool/DlgMonSubst.cpp index c458f3e2c..fa037e06e 100644 --- a/CrypTool/DlgMonSubst.cpp +++ b/CrypTool/DlgMonSubst.cpp @@ -22,10 +22,10 @@ // #include "stdafx.h" -#include "cryptoolapp.h" +#include "CrypToolApp.h" #include "DlgMonSubst.h" #include "KeyRepository.h" -#include ".\dlgmonsubst.h" +#include "DlgMonSubst.h" #ifdef _DEBUG #define new DEBUG_NEW @@ -69,14 +69,14 @@ void CDlgMonSubst::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgMonSubst, CDialog) //{{AFX_MSG_MAP(CDlgMonSubst) - ON_EN_CHANGE(IDC_KEY, ComputeSubstKeyMapping) - ON_EN_CHANGE(IDC_KEY_OFFSET, OnChangedKeyOffset) - ON_BN_CLICKED(IDC_PASTE_KEY, OnPasteKey) - ON_BN_CLICKED(ID_ENCRYPT, OnEncrypt) - ON_BN_CLICKED(ID_DECRYPT, OnDecrypt) - ON_BN_CLICKED(IDC_RADIO1, OnBnClickedRadioSubstFillAscendingOrder) - ON_BN_CLICKED(IDC_RADIO2, OnBnClickedRadioSubstFillDescendingOrder) - ON_BN_CLICKED(IDC_RADIO3, OnBnClickedRadioAtbash) + ON_EN_CHANGE(IDC_KEY, &CDlgMonSubst::ComputeSubstKeyMapping) + ON_EN_CHANGE(IDC_KEY_OFFSET, &CDlgMonSubst::OnChangedKeyOffset) + ON_BN_CLICKED(IDC_PASTE_KEY, &CDlgMonSubst::OnPasteKey) + ON_BN_CLICKED(ID_ENCRYPT, &CDlgMonSubst::OnEncrypt) + ON_BN_CLICKED(ID_DECRYPT, &CDlgMonSubst::OnDecrypt) + ON_BN_CLICKED(IDC_RADIO1, &CDlgMonSubst::OnBnClickedRadioSubstFillAscendingOrder) + ON_BN_CLICKED(IDC_RADIO2, &CDlgMonSubst::OnBnClickedRadioSubstFillDescendingOrder) + ON_BN_CLICKED(IDC_RADIO3, &CDlgMonSubst::OnBnClickedRadioAtbash) //}}AFX_MSG_MAP ON_BN_CLICKED(IDC_BUTTON_TEXTOPTIONS, &CDlgMonSubst::OnBnClickedButtonTextoptions) END_MESSAGE_MAP() diff --git a/CrypTool/DlgNGramAnalysis.cpp b/CrypTool/DlgNGramAnalysis.cpp index 1d56f71b9..3e7ed6370 100644 --- a/CrypTool/DlgNGramAnalysis.cpp +++ b/CrypTool/DlgNGramAnalysis.cpp @@ -80,20 +80,20 @@ void CDlgNGramAnaylsis::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgNGramAnaylsis, CDialog) //{{AFX_MSG_MAP(CDlgNGramAnaylsis) - ON_BN_CLICKED(IDC_RADIO1, OnEvalNGram) - ON_BN_CLICKED(IDC_RADIO2, OnEvalNGram) - ON_BN_CLICKED(IDC_RADIO3, OnEvalNGram) - ON_BN_CLICKED(IDC_RADIO4, OnEvalNGram) - ON_BN_CLICKED(IDC_BUTTON2, OnEvalNGram) - ON_BN_CLICKED(IDOK, OnSaveNGramList) - ON_BN_CLICKED(IDC_BUTTON_TEXTOPTIONS, OnTextOptions) + ON_BN_CLICKED(IDC_RADIO1, &CDlgNGramAnaylsis::OnEvalNGram) + ON_BN_CLICKED(IDC_RADIO2, &CDlgNGramAnaylsis::OnEvalNGram) + ON_BN_CLICKED(IDC_RADIO3, &CDlgNGramAnaylsis::OnEvalNGram) + ON_BN_CLICKED(IDC_RADIO4, &CDlgNGramAnaylsis::OnEvalNGram) + ON_BN_CLICKED(IDC_BUTTON2, &CDlgNGramAnaylsis::OnEvalNGram) + ON_BN_CLICKED(IDOK, &CDlgNGramAnaylsis::OnSaveNGramList) + ON_BN_CLICKED(IDC_BUTTON_TEXTOPTIONS, &CDlgNGramAnaylsis::OnTextOptions) //}}AFX_MSG_MAP END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // Behandlungsroutinen für Nachrichten CDlgNGramAnaylsis -int CDlgNGramAnaylsis::DoModal() +INT_PTR CDlgNGramAnaylsis::DoModal() { // TODO: Speziellen Code hier einfügen und/oder Basisklasse aufrufen diff --git a/CrypTool/DlgNGramAnalysis.h b/CrypTool/DlgNGramAnalysis.h index 5cb9babff..d0811e23b 100644 --- a/CrypTool/DlgNGramAnalysis.h +++ b/CrypTool/DlgNGramAnalysis.h @@ -79,7 +79,7 @@ class CDlgNGramAnaylsis : public CDialog // Vom Klassen-Assistenten generierte virtuelle Funktionsüberschreibungen //{{AFX_VIRTUAL(CDlgNGramAnaylsis) public: - virtual int DoModal(); + virtual INT_PTR DoModal(); protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV-Unterstützung //}}AFX_VIRTUAL diff --git a/CrypTool/DlgOptionsAnalysis.cpp b/CrypTool/DlgOptionsAnalysis.cpp index 1f59fb2df..c378da8c6 100644 --- a/CrypTool/DlgOptionsAnalysis.cpp +++ b/CrypTool/DlgOptionsAnalysis.cpp @@ -93,10 +93,10 @@ void CDlgOptionsAnalysis::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgOptionsAnalysis, CDialog) //{{AFX_MSG_MAP(CDlgOptionsAnalysis) - ON_BN_CLICKED(IDC_BUTTON_SEARCH_DICTIONARY_FILE, OnButtonSearchDictionaryFile) - ON_BN_CLICKED(IDC_BUTTON_SEARCH_DIGRAMS_FILE, OnButtonSearchDigramsFile) - ON_BN_CLICKED(IDC_BUTTON_SEARCH_TRIGRAMS_FILE, OnButtonSearchTrigramsFile) - ON_BN_CLICKED(IDC_BUTTON_RESTORE_STANDARD, OnBnClickedButtonRestoreStandard) + ON_BN_CLICKED(IDC_BUTTON_SEARCH_DICTIONARY_FILE, &CDlgOptionsAnalysis::OnButtonSearchDictionaryFile) + ON_BN_CLICKED(IDC_BUTTON_SEARCH_DIGRAMS_FILE, &CDlgOptionsAnalysis::OnButtonSearchDigramsFile) + ON_BN_CLICKED(IDC_BUTTON_SEARCH_TRIGRAMS_FILE, &CDlgOptionsAnalysis::OnButtonSearchTrigramsFile) + ON_BN_CLICKED(IDC_BUTTON_RESTORE_STANDARD, &CDlgOptionsAnalysis::OnBnClickedButtonRestoreStandard) //}}AFX_MSG_MAP END_MESSAGE_MAP() diff --git a/CrypTool/DlgOptionsAutoSubstitutionAnalysis.cpp b/CrypTool/DlgOptionsAutoSubstitutionAnalysis.cpp index 51a8cc261..6d8b19626 100644 --- a/CrypTool/DlgOptionsAutoSubstitutionAnalysis.cpp +++ b/CrypTool/DlgOptionsAutoSubstitutionAnalysis.cpp @@ -22,7 +22,7 @@ // #include "stdafx.h" -#include "cryptoolapp.h" +#include "CrypToolApp.h" #include "DlgOptionsAutoSubstitutionAnalysis.h" #ifdef _DEBUG diff --git a/CrypTool/DlgOptionsRSADemo.cpp b/CrypTool/DlgOptionsRSADemo.cpp index 4159103e5..c5e6cae36 100644 --- a/CrypTool/DlgOptionsRSADemo.cpp +++ b/CrypTool/DlgOptionsRSADemo.cpp @@ -75,17 +75,17 @@ void CDlgOptionsRSADemo::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgOptionsRSADemo, CDialog) //{{AFX_MSG_MAP(CDlgOptionsRSADemo) - ON_BN_CLICKED(IDC_RADIO7, OnSelectRSA) - ON_BN_CLICKED(IDC_RADIO8, OnSelectDialougeOfSisters) - ON_EN_UPDATE(IDC_EDIT1, OnChangeAlphabet) - ON_BN_CLICKED(IDC_RADIO1, OnSelectASCII) - ON_BN_CLICKED(IDC_RADIO2, OnSelectAlphabet) - ON_BN_CLICKED(IDC_RADIO10, OnCodingNumberSystem) - ON_BN_CLICKED(IDC_RADIO9, OnCodingBAdic) - ON_BN_CLICKED(IDC_RADIO3, OnBase10) - ON_BN_CLICKED(IDC_RADIO4, OnBase2) - ON_BN_CLICKED(IDC_RADIO5, OnBase8) - ON_BN_CLICKED(IDC_RADIO6, OnBase16) + ON_BN_CLICKED(IDC_RADIO7, &CDlgOptionsRSADemo::OnSelectRSA) + ON_BN_CLICKED(IDC_RADIO8, &CDlgOptionsRSADemo::OnSelectDialougeOfSisters) + ON_EN_UPDATE(IDC_EDIT1, &CDlgOptionsRSADemo::OnChangeAlphabet) + ON_BN_CLICKED(IDC_RADIO1, &CDlgOptionsRSADemo::OnSelectASCII) + ON_BN_CLICKED(IDC_RADIO2, &CDlgOptionsRSADemo::OnSelectAlphabet) + ON_BN_CLICKED(IDC_RADIO10, &CDlgOptionsRSADemo::OnCodingNumberSystem) + ON_BN_CLICKED(IDC_RADIO9, &CDlgOptionsRSADemo::OnCodingBAdic) + ON_BN_CLICKED(IDC_RADIO3, &CDlgOptionsRSADemo::OnBase10) + ON_BN_CLICKED(IDC_RADIO4, &CDlgOptionsRSADemo::OnBase2) + ON_BN_CLICKED(IDC_RADIO5, &CDlgOptionsRSADemo::OnBase8) + ON_BN_CLICKED(IDC_RADIO6, &CDlgOptionsRSADemo::OnBase16) //}}AFX_MSG_MAP END_MESSAGE_MAP() diff --git a/CrypTool/DlgOptionsSignatureAttack.cpp b/CrypTool/DlgOptionsSignatureAttack.cpp index aa3c79e7f..c86a3ad96 100644 --- a/CrypTool/DlgOptionsSignatureAttack.cpp +++ b/CrypTool/DlgOptionsSignatureAttack.cpp @@ -72,18 +72,18 @@ void CDlgOptionsSignatureAttack::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgOptionsSignatureAttack, CDialog) //{{AFX_MSG_MAP(CDlgOptionsSignatureAttack) - ON_BN_CLICKED(IDC_RADIO1, OnMD2) - ON_BN_CLICKED(IDC_RADIO2, OnMD4) - ON_BN_CLICKED(IDC_RADIO3, OnMD5) - ON_BN_CLICKED(IDC_RADIO4, OnSHA) - ON_BN_CLICKED(IDC_RADIO5, OnSHA_1) - ON_BN_CLICKED(IDC_RADIO6, OnRIPEMD_160) - ON_BN_CLICKED(IDC_BUTTON1, OnResetToDefault) - ON_EN_UPDATE(IDC_EDIT1, OnUpdateEdit1) - ON_BN_CLICKED(IDC_RADIO7, OnRadio7) - ON_BN_CLICKED(IDC_RADIO8, OnRadio8) - ON_BN_CLICKED(IDC_RADIO9, OnRadio9) - ON_BN_CLICKED(IDC_RADIO10, OnRadio10) + ON_BN_CLICKED(IDC_RADIO1, &CDlgOptionsSignatureAttack::OnMD2) + ON_BN_CLICKED(IDC_RADIO2, &CDlgOptionsSignatureAttack::OnMD4) + ON_BN_CLICKED(IDC_RADIO3, &CDlgOptionsSignatureAttack::OnMD5) + ON_BN_CLICKED(IDC_RADIO4, &CDlgOptionsSignatureAttack::OnSHA) + ON_BN_CLICKED(IDC_RADIO5, &CDlgOptionsSignatureAttack::OnSHA_1) + ON_BN_CLICKED(IDC_RADIO6, &CDlgOptionsSignatureAttack::OnRIPEMD_160) + ON_BN_CLICKED(IDC_BUTTON1, &CDlgOptionsSignatureAttack::OnResetToDefault) + ON_EN_UPDATE(IDC_EDIT1, &CDlgOptionsSignatureAttack::OnUpdateEdit1) + ON_BN_CLICKED(IDC_RADIO7, &CDlgOptionsSignatureAttack::OnRadio7) + ON_BN_CLICKED(IDC_RADIO8, &CDlgOptionsSignatureAttack::OnRadio8) + ON_BN_CLICKED(IDC_RADIO9, &CDlgOptionsSignatureAttack::OnRadio9) + ON_BN_CLICKED(IDC_RADIO10, &CDlgOptionsSignatureAttack::OnRadio10) //}}AFX_MSG_MAP END_MESSAGE_MAP() diff --git a/CrypTool/DlgOptionsSubstitutionAnalysis.cpp b/CrypTool/DlgOptionsSubstitutionAnalysis.cpp index 6e2218b5b..7d5fe10dc 100644 --- a/CrypTool/DlgOptionsSubstitutionAnalysis.cpp +++ b/CrypTool/DlgOptionsSubstitutionAnalysis.cpp @@ -74,15 +74,15 @@ void CDlgOptionsSubstitutionAnalysis::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgOptionsSubstitutionAnalysis, CDialog) - ON_BN_CLICKED(IDC_RADIO_ANALYSIS_AUTOMATIC, OnRadioAnalysisAutomatic) - ON_BN_CLICKED(IDC_RADIO_ANALYSIS_MANUAL, OnRadioAnalysisManual) - ON_BN_CLICKED(IDC_RADIO_ANALYSIS_AUTOMATIC_GERMAN, OnRadioAnalysisAutomaticGerman) - ON_BN_CLICKED(IDC_RADIO_ANALYSIS_AUTOMATIC_ENGLISH, OnRadioAnalysisAutomaticEnglish) - ON_BN_CLICKED(IDC_BUTTON_SELECT_WORDLIST_GERMAN, OnButtonSelectWordlistGerman) - ON_BN_CLICKED(IDC_BUTTON_SELECT_WORDLIST_ENGLISH, OnButtonSelectWordlistEnglish) - ON_BN_CLICKED(IDC_CHECK_ANALYSIS_AUTOMATIC_OPTION1, OnCheckAnalysisAutomaticOption1) - ON_BN_CLICKED(IDC_CHECK_ANALYSIS_AUTOMATIC_OPTION2, OnCheckAnalysisAutomaticOption2) - ON_BN_CLICKED(IDC_CHECK_ANALYSIS_MANUAL_OPTION1, OnCheckAnalysisManualOption1) + ON_BN_CLICKED(IDC_RADIO_ANALYSIS_AUTOMATIC, &CDlgOptionsSubstitutionAnalysis::OnRadioAnalysisAutomatic) + ON_BN_CLICKED(IDC_RADIO_ANALYSIS_MANUAL, &CDlgOptionsSubstitutionAnalysis::OnRadioAnalysisManual) + ON_BN_CLICKED(IDC_RADIO_ANALYSIS_AUTOMATIC_GERMAN, &CDlgOptionsSubstitutionAnalysis::OnRadioAnalysisAutomaticGerman) + ON_BN_CLICKED(IDC_RADIO_ANALYSIS_AUTOMATIC_ENGLISH, &CDlgOptionsSubstitutionAnalysis::OnRadioAnalysisAutomaticEnglish) + ON_BN_CLICKED(IDC_BUTTON_SELECT_WORDLIST_GERMAN, &CDlgOptionsSubstitutionAnalysis::OnButtonSelectWordlistGerman) + ON_BN_CLICKED(IDC_BUTTON_SELECT_WORDLIST_ENGLISH, &CDlgOptionsSubstitutionAnalysis::OnButtonSelectWordlistEnglish) + ON_BN_CLICKED(IDC_CHECK_ANALYSIS_AUTOMATIC_OPTION1, &CDlgOptionsSubstitutionAnalysis::OnCheckAnalysisAutomaticOption1) + ON_BN_CLICKED(IDC_CHECK_ANALYSIS_AUTOMATIC_OPTION2, &CDlgOptionsSubstitutionAnalysis::OnCheckAnalysisAutomaticOption2) + ON_BN_CLICKED(IDC_CHECK_ANALYSIS_MANUAL_OPTION1, &CDlgOptionsSubstitutionAnalysis::OnCheckAnalysisManualOption1) END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// diff --git a/CrypTool/DlgPINAccess.cpp b/CrypTool/DlgPINAccess.cpp index 8acdc383d..18c95f0e0 100644 --- a/CrypTool/DlgPINAccess.cpp +++ b/CrypTool/DlgPINAccess.cpp @@ -72,7 +72,7 @@ END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // Behandlungsroutinen für Nachrichten CDlgPINAccess -int CDlgPINAccess::DoModal() +INT_PTR CDlgPINAccess::DoModal() { // TODO: Speziellen Code hier einfügen und/oder Basisklasse aufrufen diff --git a/CrypTool/DlgPINAccess.h b/CrypTool/DlgPINAccess.h index 26c70a789..63d867a47 100644 --- a/CrypTool/DlgPINAccess.h +++ b/CrypTool/DlgPINAccess.h @@ -50,7 +50,7 @@ class CDlgPINAccess : public CDialog // Vom Klassen-Assistenten generierte virtuelle Funktionsüberschreibungen //{{AFX_VIRTUAL(CDlgPINAccess) public: - virtual int DoModal(); + virtual INT_PTR DoModal(); protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV-Unterstützung //}}AFX_VIRTUAL diff --git a/CrypTool/DlgPasswordEntropy.cpp b/CrypTool/DlgPasswordEntropy.cpp index 6168ddac4..48d3a9f18 100644 --- a/CrypTool/DlgPasswordEntropy.cpp +++ b/CrypTool/DlgPasswordEntropy.cpp @@ -79,15 +79,15 @@ BOOL CDlgPasswordEntropy::OnInitDialog() } BEGIN_MESSAGE_MAP(CDlgPasswordEntropy, CDialog) - ON_BN_CLICKED(ID_GENERATEPASSWORD, OnBnClickedGeneratepassword) - ON_EN_CHANGE(IDC_EDIT_PASSWORDBITLENGTH, EditPasswordBitLengthChanged) - ON_EN_CHANGE(IDC_EDIT_PASSWORDALPHABET, EditPasswordAlphabetChanged) - ON_BN_CLICKED(IDC_CHECK_USECRYPTOOLALPHABET, CheckBoxesAlphabetChanged) - ON_BN_CLICKED(IDC_CHECK_USENONCONFUSABLECHARACTERS_WRITTENTRANSMISSION, CheckBoxesAlphabetChanged) - ON_BN_CLICKED(IDC_CHECK_USENONCONFUSABLECHARACTERS_TELEPHONICTRANSMISSION, CheckBoxesAlphabetChanged) - ON_BN_CLICKED(IDC_CHECK_USEWLANALPHABET, CheckBoxesAlphabetChanged) - ON_BN_CLICKED(ID_TEXTOPTIONS, OnBnClickedTextoptions) - ON_BN_CLICKED(ID_MEASUREPASSWORDQUALITY, OnBnClickedMeasurepasswordquality) + ON_BN_CLICKED(ID_GENERATEPASSWORD, &CDlgPasswordEntropy::OnBnClickedGeneratepassword) + ON_EN_CHANGE(IDC_EDIT_PASSWORDBITLENGTH, &CDlgPasswordEntropy::EditPasswordBitLengthChanged) + ON_EN_CHANGE(IDC_EDIT_PASSWORDALPHABET, &CDlgPasswordEntropy::EditPasswordAlphabetChanged) + ON_BN_CLICKED(IDC_CHECK_USECRYPTOOLALPHABET, &CDlgPasswordEntropy::CheckBoxesAlphabetChanged) + ON_BN_CLICKED(IDC_CHECK_USENONCONFUSABLECHARACTERS_WRITTENTRANSMISSION, &CDlgPasswordEntropy::CheckBoxesAlphabetChanged) + ON_BN_CLICKED(IDC_CHECK_USENONCONFUSABLECHARACTERS_TELEPHONICTRANSMISSION, &CDlgPasswordEntropy::CheckBoxesAlphabetChanged) + ON_BN_CLICKED(IDC_CHECK_USEWLANALPHABET, &CDlgPasswordEntropy::CheckBoxesAlphabetChanged) + ON_BN_CLICKED(ID_TEXTOPTIONS, &CDlgPasswordEntropy::OnBnClickedTextoptions) + ON_BN_CLICKED(ID_MEASUREPASSWORDQUALITY, &CDlgPasswordEntropy::OnBnClickedMeasurepasswordquality) END_MESSAGE_MAP() diff --git a/CrypTool/DlgPasswordGuidelines.cpp b/CrypTool/DlgPasswordGuidelines.cpp index 28c7215ad..daa722014 100644 --- a/CrypTool/DlgPasswordGuidelines.cpp +++ b/CrypTool/DlgPasswordGuidelines.cpp @@ -24,7 +24,7 @@ #include "CrypToolApp.h" #include "DlgPasswordGuidelines.h" #include "CrypToolTools.h" -#include ".\dlgpasswordguidelines.h" +#include "DlgPasswordGuidelines.h" #include "DialogeMessage.h" CDlgPasswordGuidelines::CDlgPasswordGuidelines(CWnd* pParent /*=NULL*/) @@ -133,8 +133,8 @@ void CDlgPasswordGuidelines::OnBnClickedOk() } BEGIN_MESSAGE_MAP(CDlgPasswordGuidelines, CDialog) - ON_BN_CLICKED(IDOK, OnBnClickedOk) - ON_BN_CLICKED(ID_DEFAULT, OnBnClickedDefault) + ON_BN_CLICKED(IDOK, &CDlgPasswordGuidelines::OnBnClickedOk) + ON_BN_CLICKED(ID_DEFAULT, &CDlgPasswordGuidelines::OnBnClickedDefault) END_MESSAGE_MAP() void CDlgPasswordGuidelines::OnBnClickedDefault() diff --git a/CrypTool/DlgPasswordQualityMeter.cpp b/CrypTool/DlgPasswordQualityMeter.cpp index 87362ed89..37060d0e3 100644 --- a/CrypTool/DlgPasswordQualityMeter.cpp +++ b/CrypTool/DlgPasswordQualityMeter.cpp @@ -237,9 +237,9 @@ void CDlgPasswordQualityMeter::UpdateUserInterface() } BEGIN_MESSAGE_MAP(CDlgPasswordQualityMeter, CDialog) - ON_EN_CHANGE(IDC_EDIT_PASSWORD, EditPasswordChanged) - ON_BN_CLICKED(IDC_CHECK_SHOWPASSWORD, OnBnClickedCheckShowpassword) - ON_BN_CLICKED(ID_CONFIGURE_PASSWORD_GUIDELINES, OnBnClickedConfigurePasswordGuidelines) + ON_EN_CHANGE(IDC_EDIT_PASSWORD, &CDlgPasswordQualityMeter::EditPasswordChanged) + ON_BN_CLICKED(IDC_CHECK_SHOWPASSWORD, &CDlgPasswordQualityMeter::OnBnClickedCheckShowpassword) + ON_BN_CLICKED(ID_CONFIGURE_PASSWORD_GUIDELINES, &CDlgPasswordQualityMeter::OnBnClickedConfigurePasswordGuidelines) END_MESSAGE_MAP() diff --git a/CrypTool/DlgPeriodicityAnalysis.cpp b/CrypTool/DlgPeriodicityAnalysis.cpp index 321649c13..bdf14d107 100644 --- a/CrypTool/DlgPeriodicityAnalysis.cpp +++ b/CrypTool/DlgPeriodicityAnalysis.cpp @@ -67,7 +67,7 @@ void CDlgPeriodicityAnalysis::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgPeriodicityAnalysis, CDialog) //{{AFX_MSG_MAP(CDlgPeriodicityAnalysis) - ON_BN_CLICKED(IDOK, OnSaveList) + ON_BN_CLICKED(IDOK, &CDlgPeriodicityAnalysis::OnSaveList) //}}AFX_MSG_MAP END_MESSAGE_MAP() diff --git a/CrypTool/DlgPlayfairAnalysis.cpp b/CrypTool/DlgPlayfairAnalysis.cpp index fac5691fe..6b75c2cba 100644 --- a/CrypTool/DlgPlayfairAnalysis.cpp +++ b/CrypTool/DlgPlayfairAnalysis.cpp @@ -217,15 +217,15 @@ void CDlgPlayfairAnalysis::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgPlayfairAnalysis, CDialog) //{{AFX_MSG_MAP(CDlgPlayfairAnalysis) - ON_BN_CLICKED(IDC_CHECK1, OnCheck) - ON_BN_CLICKED(IDC_RAD5, OnSechs) - ON_BN_CLICKED(IDC_RAD6, OnSechs) - ON_BN_CLICKED(IDC_BUTTON5, OnAnalyse) - ON_BN_CLICKED(IDC_BUTTON6, OnSynchronise) - ON_EN_UPDATE(IDC_MYTXT, OnManAnalyse) - ON_EN_UPDATE(IDC_PASSWORD, OnUpdate) + ON_BN_CLICKED(IDC_CHECK1, &CDlgPlayfairAnalysis::OnCheck) + ON_BN_CLICKED(IDC_RAD5, &CDlgPlayfairAnalysis::OnSechs) + ON_BN_CLICKED(IDC_RAD6, &CDlgPlayfairAnalysis::OnSechs) + ON_BN_CLICKED(IDC_BUTTON5, &CDlgPlayfairAnalysis::OnAnalyse) + ON_BN_CLICKED(IDC_BUTTON6, &CDlgPlayfairAnalysis::OnSynchronise) + ON_EN_UPDATE(IDC_MYTXT, &CDlgPlayfairAnalysis::OnManAnalyse) + ON_EN_UPDATE(IDC_PASSWORD, &CDlgPlayfairAnalysis::OnUpdate) ON_WM_HSCROLL() - ON_EN_CHANGE(IDC_MYTXT, OnChangeEditPlaintext) + ON_EN_CHANGE(IDC_MYTXT, &CDlgPlayfairAnalysis::OnChangeEditPlaintext) ON_WM_SIZE() //}}AFX_MSG_MAP END_MESSAGE_MAP() diff --git a/CrypTool/DlgPrimeTest.cpp b/CrypTool/DlgPrimeTest.cpp index 511eb5c0c..43cdb7434 100644 --- a/CrypTool/DlgPrimeTest.cpp +++ b/CrypTool/DlgPrimeTest.cpp @@ -25,7 +25,7 @@ #include "CrypToolApp.h" #include "DialogeMessage.h" #include "DlgPrimeTest.h" -#include ".\dlgprimetest.h" +#include "DlgPrimeTest.h" #include "DlgFactorisationDemo.h" #include "PrimeTest.h" @@ -128,14 +128,14 @@ void CDlgPrimeTest::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgPrimeTest, CDialog) - ON_BN_CLICKED(IDC_PRIMETEST_BUTTON_TEST, OnBnClickedPrimetestButtonTest) - ON_BN_CLICKED(IDC_PRIMETEST_BUTTON_CANCEL, OnBnClickedPrimetestButtonCancel) - ON_BN_CLICKED(IDC_PRIMETEST_BUTTON_LOADNUMBER, OnBnClickedPrimetestButtonLoadnumber) - ON_EN_CHANGE(IDC_PRIMETEST_EDIT_NUMBER, OnEnChangePrimetestEditNumber) - ON_BN_CLICKED(IDC_PRIMETEST_RADIO_FERMAT, OnBnClickedPrimetestRadio) - ON_BN_CLICKED(IDC_PRIMETEST_RADIO_SOLOVAY, OnBnClickedPrimetestRadio) - ON_BN_CLICKED(IDC_PRIMETEST_RADIO_MILLERRABIN, OnBnClickedPrimetestRadio) - ON_BN_CLICKED(IDC_PRIMETEST_RADIO_AKS, OnBnClickedPrimetestRadio) + ON_BN_CLICKED(IDC_PRIMETEST_BUTTON_TEST, &CDlgPrimeTest::OnBnClickedPrimetestButtonTest) + ON_BN_CLICKED(IDC_PRIMETEST_BUTTON_CANCEL, &CDlgPrimeTest::OnBnClickedPrimetestButtonCancel) + ON_BN_CLICKED(IDC_PRIMETEST_BUTTON_LOADNUMBER, &CDlgPrimeTest::OnBnClickedPrimetestButtonLoadnumber) + ON_EN_CHANGE(IDC_PRIMETEST_EDIT_NUMBER, &CDlgPrimeTest::OnEnChangePrimetestEditNumber) + ON_BN_CLICKED(IDC_PRIMETEST_RADIO_FERMAT, &CDlgPrimeTest::OnBnClickedPrimetestRadio) + ON_BN_CLICKED(IDC_PRIMETEST_RADIO_SOLOVAY, &CDlgPrimeTest::OnBnClickedPrimetestRadio) + ON_BN_CLICKED(IDC_PRIMETEST_RADIO_MILLERRABIN, &CDlgPrimeTest::OnBnClickedPrimetestRadio) + ON_BN_CLICKED(IDC_PRIMETEST_RADIO_AKS, &CDlgPrimeTest::OnBnClickedPrimetestRadio) ON_BN_CLICKED(IDC_BUTTON_JUMP_TO_FACTORIZATION, CDlgPrimeTest::OnBnClickedButtonJumpToFactorization) END_MESSAGE_MAP() diff --git a/CrypTool/DlgPrimesGeneratorDemo.cpp b/CrypTool/DlgPrimesGeneratorDemo.cpp index 735047995..99b4e87eb 100644 --- a/CrypTool/DlgPrimesGeneratorDemo.cpp +++ b/CrypTool/DlgPrimesGeneratorDemo.cpp @@ -27,7 +27,7 @@ #include #include "DlgPrimesGeneratorDemo.h" -#include "Keyrepository.h" +#include "KeyRepository.h" #include "DialogeMessage.h" #include "FileTools.h" #include "CrypToolTools.h" @@ -126,17 +126,17 @@ void CDlgPrimesGeneratorDemo::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgPrimesGeneratorDemo, CDialog) //{{AFX_MSG_MAP(CDlgPrimesGeneratorDemo) - ON_BN_CLICKED(IDC_RADIO4, OnRadio4) - ON_BN_CLICKED(IDC_RADIO5, OnRadio5) - ON_BN_CLICKED(IDC_RADIO6, OnRadio6) - ON_BN_CLICKED(IDC_RADIO7, OnRadio7) - ON_BN_CLICKED(IDC_BUTTON_GENERATE, OnButtonGenerate) - ON_BN_CLICKED(IDC_BUTTON_ACCEPT, OnButtonAccept) - ON_BN_CLICKED(IDC_ENDDIALOG, OnEndDialog) - ON_EN_UPDATE(IDC_EDIT1, OnUpdateEdit) - ON_EN_UPDATE(IDC_EDIT2, OnUpdateEdit) - ON_EN_UPDATE(IDC_EDIT3, OnUpdateEdit) - ON_EN_UPDATE(IDC_EDIT4, OnUpdateEdit) + ON_BN_CLICKED(IDC_RADIO4, &CDlgPrimesGeneratorDemo::OnRadio4) + ON_BN_CLICKED(IDC_RADIO5, &CDlgPrimesGeneratorDemo::OnRadio5) + ON_BN_CLICKED(IDC_RADIO6, &CDlgPrimesGeneratorDemo::OnRadio6) + ON_BN_CLICKED(IDC_RADIO7, &CDlgPrimesGeneratorDemo::OnRadio7) + ON_BN_CLICKED(IDC_BUTTON_GENERATE, &CDlgPrimesGeneratorDemo::OnButtonGenerate) + ON_BN_CLICKED(IDC_BUTTON_ACCEPT, &CDlgPrimesGeneratorDemo::OnButtonAccept) + ON_BN_CLICKED(IDC_ENDDIALOG, &CDlgPrimesGeneratorDemo::OnEndDialog) + ON_EN_UPDATE(IDC_EDIT1, &CDlgPrimesGeneratorDemo::OnUpdateEdit) + ON_EN_UPDATE(IDC_EDIT2, &CDlgPrimesGeneratorDemo::OnUpdateEdit) + ON_EN_UPDATE(IDC_EDIT3, &CDlgPrimesGeneratorDemo::OnUpdateEdit) + ON_EN_UPDATE(IDC_EDIT4, &CDlgPrimesGeneratorDemo::OnUpdateEdit) //}}AFX_MSG_MAP END_MESSAGE_MAP() diff --git a/CrypTool/DlgProgressFactorisation.cpp b/CrypTool/DlgProgressFactorisation.cpp index b2d662b06..89f8ecf0b 100644 --- a/CrypTool/DlgProgressFactorisation.cpp +++ b/CrypTool/DlgProgressFactorisation.cpp @@ -90,11 +90,11 @@ void CDlgProgressFactorisation::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgProgressFactorisation, CDialog) //{{AFX_MSG_MAP(CDlgProgressFactorisation) ON_WM_TIMER() - ON_BN_CLICKED(IDCANCEL2, OnCancel2) - ON_BN_CLICKED(IDCANCEL3, OnCancel3) - ON_BN_CLICKED(IDCANCEL4, OnCancel4) - ON_BN_CLICKED(IDCANCEL5, OnCancel5) - ON_BN_CLICKED(IDCANCEL6, OnCancel6) + ON_BN_CLICKED(IDCANCEL2, &CDlgProgressFactorisation::OnCancel2) + ON_BN_CLICKED(IDCANCEL3, &CDlgProgressFactorisation::OnCancel3) + ON_BN_CLICKED(IDCANCEL4, &CDlgProgressFactorisation::OnCancel4) + ON_BN_CLICKED(IDCANCEL5, &CDlgProgressFactorisation::OnCancel5) + ON_BN_CLICKED(IDCANCEL6, &CDlgProgressFactorisation::OnCancel6) //}}AFX_MSG_MAP END_MESSAGE_MAP() diff --git a/CrypTool/DlgRSADecryption.cpp b/CrypTool/DlgRSADecryption.cpp index 3041ff21b..41f5366d3 100644 --- a/CrypTool/DlgRSADecryption.cpp +++ b/CrypTool/DlgRSADecryption.cpp @@ -81,10 +81,10 @@ void CDlgRSADecryption::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgRSADecryption, CDialog) //{{AFX_MSG_MAP(CDlgRSADecryption) - ON_NOTIFY(NM_CLICK, IDC_LIST_KEYS, OnClickListKeys) - ON_NOTIFY(HDN_ITEMCLICK, IDC_LIST_KEYS, OnItemclickListKeys) - ON_NOTIFY(LVN_COLUMNCLICK, IDC_LIST_KEYS, OnColumnclickListKeys) - ON_NOTIFY(LVN_KEYDOWN, IDC_LIST_KEYS, OnKeydownListKeys) + ON_NOTIFY(NM_CLICK, IDC_LIST_KEYS, &CDlgRSADecryption::OnClickListKeys) + ON_NOTIFY(HDN_ITEMCLICK, IDC_LIST_KEYS, &CDlgRSADecryption::OnItemclickListKeys) + ON_NOTIFY(LVN_COLUMNCLICK, IDC_LIST_KEYS, &CDlgRSADecryption::OnColumnclickListKeys) + ON_NOTIFY(LVN_KEYDOWN, IDC_LIST_KEYS, &CDlgRSADecryption::OnKeydownListKeys) //}}AFX_MSG_MAP END_MESSAGE_MAP() diff --git a/CrypTool/DlgRSADemo.cpp b/CrypTool/DlgRSADemo.cpp index 0b326f06c..36ca6b992 100644 --- a/CrypTool/DlgRSADemo.cpp +++ b/CrypTool/DlgRSADemo.cpp @@ -157,21 +157,21 @@ void CDlgRSADemo::DoDataExchange(CDataExchange* pDX) //****************************************************************************** BEGIN_MESSAGE_MAP(CDlgRSADemo, CDialog) //{{AFX_MSG_MAP(CDlgRSADemo) - ON_BN_CLICKED(IDC_BUTTON_PZ_GENERIEREN, OnButtonGeneratePrimes) - ON_BN_CLICKED(IDC_BUTTON2, OnButtonUpdateRSAParameter) - ON_BN_CLICKED(IDC_OPTIONEN, OnButtonOptions) - ON_BN_CLICKED(IDC_BUTTON_VERSCHLUESSELN, OnButtonEncrypt) - ON_BN_CLICKED(IDC_BUTTON_ENTSCHLUESSELN, OnButtonDecrypt) - ON_BN_CLICKED(IDC_ENDDIALOG, OnEndDialog) - ON_EN_UPDATE(IDC_EDIT1, OnUpdatePrimeP) - ON_EN_UPDATE(IDC_EDIT2, OnUpdatePrimeQ) - ON_EN_UPDATE(IDC_EDIT3, OnUpdateModulN) - ON_EN_UPDATE(IDC_EDIT5, OnUpdatePublicKeyE) - ON_EN_UPDATE(IDC_EDIT10, OnUpdateRSAInput) - ON_BN_CLICKED(IDC_RADIO1, OnRadioRSAText) - ON_BN_CLICKED(IDC_RADIO2, OnRadioRSANumbers) - ON_BN_CLICKED(IDC_RADIO3, OnRadioRSAComplete) - ON_BN_CLICKED(IDC_RADIO4, OnRadioRSAPublicKey) + ON_BN_CLICKED(IDC_BUTTON_PZ_GENERIEREN, &CDlgRSADemo::OnButtonGeneratePrimes) + ON_BN_CLICKED(IDC_BUTTON2, &CDlgRSADemo::OnButtonUpdateRSAParameter) + ON_BN_CLICKED(IDC_OPTIONEN, &CDlgRSADemo::OnButtonOptions) + ON_BN_CLICKED(IDC_BUTTON_VERSCHLUESSELN, &CDlgRSADemo::OnButtonEncrypt) + ON_BN_CLICKED(IDC_BUTTON_ENTSCHLUESSELN, &CDlgRSADemo::OnButtonDecrypt) + ON_BN_CLICKED(IDC_ENDDIALOG, &CDlgRSADemo::OnEndDialog) + ON_EN_UPDATE(IDC_EDIT1, &CDlgRSADemo::OnUpdatePrimeP) + ON_EN_UPDATE(IDC_EDIT2, &CDlgRSADemo::OnUpdatePrimeQ) + ON_EN_UPDATE(IDC_EDIT3, &CDlgRSADemo::OnUpdateModulN) + ON_EN_UPDATE(IDC_EDIT5, &CDlgRSADemo::OnUpdatePublicKeyE) + ON_EN_UPDATE(IDC_EDIT10, &CDlgRSADemo::OnUpdateRSAInput) + ON_BN_CLICKED(IDC_RADIO1, &CDlgRSADemo::OnRadioRSAText) + ON_BN_CLICKED(IDC_RADIO2, &CDlgRSADemo::OnRadioRSANumbers) + ON_BN_CLICKED(IDC_RADIO3, &CDlgRSADemo::OnRadioRSAComplete) + ON_BN_CLICKED(IDC_RADIO4, &CDlgRSADemo::OnRadioRSAPublicKey) //}}AFX_MSG_MAP END_MESSAGE_MAP() @@ -2039,7 +2039,7 @@ CMyRSADemoEdit::~CMyRSADemoEdit() BEGIN_MESSAGE_MAP(CMyRSADemoEdit, CEdit) //{{AFX_MSG_MAP(CMyRSADemoEdit) ON_WM_CHAR() - ON_COMMAND(ID_EDIT_PASTE, OnMyPaste) + ON_COMMAND(ID_EDIT_PASTE, &CMyRSADemoEdit::OnMyPaste) //}}AFX_MSG_MAP END_MESSAGE_MAP() diff --git a/CrypTool/DlgRSAEncryption.cpp b/CrypTool/DlgRSAEncryption.cpp index 800d1ee83..05cd95248 100644 --- a/CrypTool/DlgRSAEncryption.cpp +++ b/CrypTool/DlgRSAEncryption.cpp @@ -82,10 +82,10 @@ void CDlgRSAEncryption::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgRSAEncryption, CDialog) //{{AFX_MSG_MAP(CDlgRSAEncryption) - ON_NOTIFY(NM_CLICK, IDC_LIST1, OnClickList1) - ON_NOTIFY(LVN_COLUMNCLICK, IDC_LIST1, OnColumnclickList1) - ON_NOTIFY(LVN_KEYDOWN, IDC_LIST1, OnKeydownList1) - ON_NOTIFY(HDN_ITEMCLICK, IDC_LIST1, OnItemclickList1) + ON_NOTIFY(NM_CLICK, IDC_LIST1, &CDlgRSAEncryption::OnClickList1) + ON_NOTIFY(LVN_COLUMNCLICK, IDC_LIST1, &CDlgRSAEncryption::OnColumnclickList1) + ON_NOTIFY(LVN_KEYDOWN, IDC_LIST1, &CDlgRSAEncryption::OnKeydownList1) + ON_NOTIFY(HDN_ITEMCLICK, IDC_LIST1, &CDlgRSAEncryption::OnItemclickList1) //}}AFX_MSG_MAP END_MESSAGE_MAP() diff --git a/CrypTool/DlgRandomGenerator.cpp b/CrypTool/DlgRandomGenerator.cpp index 73b3438e4..c2ae39596 100644 --- a/CrypTool/DlgRandomGenerator.cpp +++ b/CrypTool/DlgRandomGenerator.cpp @@ -38,7 +38,7 @@ using namespace std; #include "DlgPrimesGeneratorDemo.h" #include "DlgShowProgress.h" #include "CrypToolTools.h" -#include ".\dlgrandomgenerator.h" +#include "DlgRandomGenerator.h" #ifdef _DEBUG #define new DEBUG_NEW @@ -275,13 +275,13 @@ void CDlgRandomGenerator::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgRandomGenerator, CDialog) //{{AFX_MSG_MAP(CDlgRandomGenerator) - ON_BN_CLICKED(IDC_BUTTON1, OnSelGenParam) - ON_BN_CLICKED(IDOK, OnGenRandomData) + ON_BN_CLICKED(IDC_BUTTON1, &CDlgRandomGenerator::OnSelGenParam) + ON_BN_CLICKED(IDOK, &CDlgRandomGenerator::OnGenRandomData) //}}AFX_MSG_MAP - ON_BN_CLICKED(IDC_RADIO1, OnBnClickedRadio1) - ON_BN_CLICKED(IDC_RADIO2, OnBnClickedRadio2) - ON_BN_CLICKED(IDC_RADIO3, OnBnClickedRadio3) - ON_BN_CLICKED(IDC_RADIO4, OnBnClickedRadio4) + ON_BN_CLICKED(IDC_RADIO1, &CDlgRandomGenerator::OnBnClickedRadio1) + ON_BN_CLICKED(IDC_RADIO2, &CDlgRandomGenerator::OnBnClickedRadio2) + ON_BN_CLICKED(IDC_RADIO3, &CDlgRandomGenerator::OnBnClickedRadio3) + ON_BN_CLICKED(IDC_RADIO4, &CDlgRandomGenerator::OnBnClickedRadio4) END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// diff --git a/CrypTool/DlgRot13Caesar.cpp b/CrypTool/DlgRot13Caesar.cpp index 5e3695446..84bf0b42d 100644 --- a/CrypTool/DlgRot13Caesar.cpp +++ b/CrypTool/DlgRot13Caesar.cpp @@ -22,7 +22,7 @@ // #include "stdafx.h" -#include "cryptoolapp.h" +#include "CrypToolApp.h" #include "DlgRot13Caesar.h" #include "CrypToolTools.h" @@ -76,18 +76,18 @@ void CDlgRot13Caesar::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgRot13Caesar, CDialog) //{{AFX_MSG_MAP(CDlgRot13Caesar) - ON_BN_CLICKED(IDC_RADIO1, onUpdateGUI) - ON_BN_CLICKED(IDC_RADIO2, onUpdateGUI) - ON_BN_CLICKED(IDC_RADIO3, onUpdateGUI) - ON_BN_CLICKED(IDC_RADIO4, onUpdateGUI) - ON_BN_CLICKED(IDC_RADIO5, onUpdateGUI) - ON_BN_CLICKED(IDC_RADIO6, onUpdateGUI) - ON_EN_UPDATE(IDC_KEY, onUpdateGUI) - ON_EN_UPDATE(IDC_EDIT_ALPHCODE, onUpdateGUI) - ON_BN_CLICKED(IDC_PASTE_KEY, OnPasteKey) - ON_BN_CLICKED(ID_ENCRYPT, OnEncrypt) - ON_BN_CLICKED(ID_DECRYPT, OnDecrypt) - ON_BN_CLICKED(IDC_BUTTON_TxtOpt, onTextOptions) + ON_BN_CLICKED(IDC_RADIO1, &CDlgRot13Caesar::onUpdateGUI) + ON_BN_CLICKED(IDC_RADIO2, &CDlgRot13Caesar::onUpdateGUI) + ON_BN_CLICKED(IDC_RADIO3, &CDlgRot13Caesar::onUpdateGUI) + ON_BN_CLICKED(IDC_RADIO4, &CDlgRot13Caesar::onUpdateGUI) + ON_BN_CLICKED(IDC_RADIO5, &CDlgRot13Caesar::onUpdateGUI) + ON_BN_CLICKED(IDC_RADIO6, &CDlgRot13Caesar::onUpdateGUI) + ON_EN_UPDATE(IDC_KEY, &CDlgRot13Caesar::onUpdateGUI) + ON_EN_UPDATE(IDC_EDIT_ALPHCODE, &CDlgRot13Caesar::onUpdateGUI) + ON_BN_CLICKED(IDC_PASTE_KEY, &CDlgRot13Caesar::OnPasteKey) + ON_BN_CLICKED(ID_ENCRYPT, &CDlgRot13Caesar::OnEncrypt) + ON_BN_CLICKED(ID_DECRYPT, &CDlgRot13Caesar::OnDecrypt) + ON_BN_CLICKED(IDC_BUTTON_TxtOpt, &CDlgRot13Caesar::onTextOptions) //}}AFX_MSG_MAP END_MESSAGE_MAP() diff --git a/CrypTool/DlgSecretSharingIntro.cpp b/CrypTool/DlgSecretSharingIntro.cpp index 0a8af6698..af44f30f3 100644 --- a/CrypTool/DlgSecretSharingIntro.cpp +++ b/CrypTool/DlgSecretSharingIntro.cpp @@ -22,9 +22,9 @@ // #include "stdafx.h" -#include "CryptoolApp.h" +#include "CrypToolApp.h" +#include "DlgSecretSharingIntro.h" #include "DlgSecretSharingIntro.h" -#include ".\dlgsecretsharingintro.h" #include "CrypToolTools.h" @@ -54,7 +54,7 @@ void CDlgSecretSharingIntro::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgSecretSharingIntro, CDialog) //{{AFX_MSG_MAP(CDlgSecretSharingIntro) - ON_BN_CLICKED(IDOK, OnOk) + ON_BN_CLICKED(IDOK, &CDlgSecretSharingIntro::OnOk) //}}AFX_MSG_MAP END_MESSAGE_MAP() diff --git a/CrypTool/DlgSecretSharingOptions.cpp b/CrypTool/DlgSecretSharingOptions.cpp index 6eea04da2..4db5d4210 100644 --- a/CrypTool/DlgSecretSharingOptions.cpp +++ b/CrypTool/DlgSecretSharingOptions.cpp @@ -52,7 +52,7 @@ void CDlgSecretSharingOptions::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgSecretSharingOptions, CDialog) //{{AFX_MSG_MAP(CDlgSecretSharingOptions) - ON_BN_CLICKED(IDOK, OnButtonOk) + ON_BN_CLICKED(IDOK, &CDlgSecretSharingOptions::OnButtonOk) //}}AFX_MSG_MAP END_MESSAGE_MAP() diff --git a/CrypTool/DlgSecretSharingParameter.cpp b/CrypTool/DlgSecretSharingParameter.cpp index 978d8d877..cc5e4721d 100644 --- a/CrypTool/DlgSecretSharingParameter.cpp +++ b/CrypTool/DlgSecretSharingParameter.cpp @@ -25,7 +25,7 @@ #include "CrypToolApp.h" #include "DlgSecretSharingOptions.h" #include "DlgSecretSharingParameter.h" -#include ".\dlgsecretsharingparameter.h" +#include "DlgSecretSharingParameter.h" #include "CrypToolTools.h" // CDlgSecretSharingParameter-Dialogfeld @@ -66,10 +66,10 @@ void CDlgSecretSharingParameter::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgSecretSharingParameter, CDialog) - ON_BN_CLICKED(IDB_GENERATENEW, OnButtonGenerateNew) - ON_EN_UPDATE(IDC_EDIT_MYPRIME, OnEnUpdateEditMyprime) - ON_BN_CLICKED(IDOK, OnButtonClickedOk) - ON_EN_UPDATE(IDC_EDIT_MYCOEFF, OnUpdateEditMycoeff) + ON_BN_CLICKED(IDB_GENERATENEW, &CDlgSecretSharingParameter::OnButtonGenerateNew) + ON_EN_UPDATE(IDC_EDIT_MYPRIME, &CDlgSecretSharingParameter::OnEnUpdateEditMyprime) + ON_BN_CLICKED(IDOK, &CDlgSecretSharingParameter::OnButtonClickedOk) + ON_EN_UPDATE(IDC_EDIT_MYCOEFF, &CDlgSecretSharingParameter::OnUpdateEditMycoeff) END_MESSAGE_MAP() BOOL CDlgSecretSharingParameter::OnInitDialog() diff --git a/CrypTool/DlgSecretSharingReveal.cpp b/CrypTool/DlgSecretSharingReveal.cpp index a94c9bd0c..3d7b520fc 100644 --- a/CrypTool/DlgSecretSharingReveal.cpp +++ b/CrypTool/DlgSecretSharingReveal.cpp @@ -66,7 +66,7 @@ void CDlgSecretSharingReveal::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgSecretSharingReveal, CDialog) - ON_BN_CLICKED(IDC_BUTTON_LOG, OnShowLog) + ON_BN_CLICKED(IDC_BUTTON_LOG, &CDlgSecretSharingReveal::OnShowLog) END_MESSAGE_MAP() BOOL CDlgSecretSharingReveal::OnInitDialog() diff --git a/CrypTool/DlgSecretSharingSetup.cpp b/CrypTool/DlgSecretSharingSetup.cpp index a200a1819..c37ccbe2d 100644 --- a/CrypTool/DlgSecretSharingSetup.cpp +++ b/CrypTool/DlgSecretSharingSetup.cpp @@ -30,7 +30,7 @@ #include "DlgSecretSharingReveal.h" #include "DlgSecretSharingOptions.h" #include "SecretSharingLog.h" -#include ".\dlgsecretsharingsetup.h" +#include "DlgSecretSharingSetup.h" #include "CrypToolTools.h" #ifdef _DEBUG @@ -89,18 +89,18 @@ void CDlgSecretSharingSetup::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgSecretSharingSetup, CDialog) //{{AFX_MSG_MAP(CDlgSecretSharingSetup) - ON_BN_CLICKED(IDC_BUTTON_GENERATE, OnButtonGenerate) - ON_BN_CLICKED(IDC_BUTTON_UPDATE, OnButtonUpdate) - ON_BN_CLICKED(IDOK, OnButtonOk) - ON_EN_UPDATE(IDC_EDIT_THRESHOLD, OnUpdateEditThreshold) - ON_EN_UPDATE(IDC_EDIT_SECRET, OnUpdateEditSecret) - ON_EN_UPDATE(IDC_EDIT_PARTICIPANTS, OnUpdateEditParticipants) - ON_BN_CLICKED(IDC_BUTTON_OPT, OnButtonOpt) - ON_BN_CLICKED(IDC_BUTTON_CHANGE, OnButtonChange) - ON_BN_CLICKED(IDC_CHECK_SHOWINTRO, OnButtonClickedCheckShowintro) - ON_BN_CLICKED(IDC_BUTTON_EXAMPLE, OnButtonExample) + ON_BN_CLICKED(IDC_BUTTON_GENERATE, &CDlgSecretSharingSetup::OnButtonGenerate) + ON_BN_CLICKED(IDC_BUTTON_UPDATE, &CDlgSecretSharingSetup::OnButtonUpdate) + ON_BN_CLICKED(IDOK, &CDlgSecretSharingSetup::OnButtonOk) + ON_EN_UPDATE(IDC_EDIT_THRESHOLD, &CDlgSecretSharingSetup::OnUpdateEditThreshold) + ON_EN_UPDATE(IDC_EDIT_SECRET, &CDlgSecretSharingSetup::OnUpdateEditSecret) + ON_EN_UPDATE(IDC_EDIT_PARTICIPANTS, &CDlgSecretSharingSetup::OnUpdateEditParticipants) + ON_BN_CLICKED(IDC_BUTTON_OPT, &CDlgSecretSharingSetup::OnButtonOpt) + ON_BN_CLICKED(IDC_BUTTON_CHANGE, &CDlgSecretSharingSetup::OnButtonChange) + ON_BN_CLICKED(IDC_CHECK_SHOWINTRO, &CDlgSecretSharingSetup::OnButtonClickedCheckShowintro) + ON_BN_CLICKED(IDC_BUTTON_EXAMPLE, &CDlgSecretSharingSetup::OnButtonExample) //}}AFX_MSG_MAP - ON_NOTIFY(NM_CLICK, IDC_LIST_SHARES, OnNMClickListShares) + ON_NOTIFY(NM_CLICK, IDC_LIST_SHARES, &CDlgSecretSharingSetup::OnNMClickListShares) END_MESSAGE_MAP() BOOL CDlgSecretSharingSetup::OnInitDialog() diff --git a/CrypTool/DlgSelectHashFunction.cpp b/CrypTool/DlgSelectHashFunction.cpp index 249dffa5a..1facca786 100644 --- a/CrypTool/DlgSelectHashFunction.cpp +++ b/CrypTool/DlgSelectHashFunction.cpp @@ -73,14 +73,14 @@ void CDlgSelectHashFunction::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgSelectHashFunction, CDialog) //{{AFX_MSG_MAP(CDlgSelectHashFunction) - ON_BN_CLICKED(IDC_RADIO1, OnSelectedMD2) - ON_BN_CLICKED(IDC_RADIO2, OnSelectedMD4) - ON_BN_CLICKED(IDC_RADIO3, OnSelectedMD5) - ON_BN_CLICKED(IDC_RADIO4, OnSelectSHA) - ON_BN_CLICKED(IDC_RADIO5, OnSelectedSHA_1) - ON_BN_CLICKED(IDC_RADIO7, OnSelectedSHA_256) - ON_BN_CLICKED(IDC_RADIO8, OnSelectedSHA_512) - ON_BN_CLICKED(IDC_RADIO6, OnSelectedRIPEMD_160) + ON_BN_CLICKED(IDC_RADIO1, &CDlgSelectHashFunction::OnSelectedMD2) + ON_BN_CLICKED(IDC_RADIO2, &CDlgSelectHashFunction::OnSelectedMD4) + ON_BN_CLICKED(IDC_RADIO3, &CDlgSelectHashFunction::OnSelectedMD5) + ON_BN_CLICKED(IDC_RADIO4, &CDlgSelectHashFunction::OnSelectSHA) + ON_BN_CLICKED(IDC_RADIO5, &CDlgSelectHashFunction::OnSelectedSHA_1) + ON_BN_CLICKED(IDC_RADIO7, &CDlgSelectHashFunction::OnSelectedSHA_256) + ON_BN_CLICKED(IDC_RADIO8, &CDlgSelectHashFunction::OnSelectedSHA_512) + ON_BN_CLICKED(IDC_RADIO6, &CDlgSelectHashFunction::OnSelectedRIPEMD_160) //}}AFX_MSG_MAP END_MESSAGE_MAP() diff --git a/CrypTool/DlgShowHash.cpp b/CrypTool/DlgShowHash.cpp index d618e9ac4..5ac4ff699 100644 --- a/CrypTool/DlgShowHash.cpp +++ b/CrypTool/DlgShowHash.cpp @@ -56,7 +56,7 @@ void CDlgShowHash::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgShowHash, CDialog) //{{AFX_MSG_MAP(CDlgShowHash) - ON_BN_CLICKED(IDOK, OnShowHashInDesktop) + ON_BN_CLICKED(IDOK, &CDlgShowHash::OnShowHashInDesktop) //}}AFX_MSG_MAP END_MESSAGE_MAP() diff --git a/CrypTool/DlgShowKey.cpp b/CrypTool/DlgShowKey.cpp index 33b997ff6..4f183a26a 100644 --- a/CrypTool/DlgShowKey.cpp +++ b/CrypTool/DlgShowKey.cpp @@ -57,7 +57,7 @@ void CDlgShowKey::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgShowKey, CDialog) //{{AFX_MSG_MAP(CDlgShowKey) - ON_BN_CLICKED(IDC_BUTTON1, OnCopyKey) + ON_BN_CLICKED(IDC_BUTTON1, &CDlgShowKey::OnCopyKey) //}}AFX_MSG_MAP END_MESSAGE_MAP() @@ -112,7 +112,7 @@ BOOL CDlgShowKey::PreTranslateMessage(MSG *msg) { return CDialog::PreTranslateMessage(msg); } -int CDlgShowKey::DoModal() +INT_PTR CDlgShowKey::DoModal() { // TODO: Speziellen Code hier einfügen und/oder Basisklasse aufrufen return CDialog::DoModal(); diff --git a/CrypTool/DlgShowKey.h b/CrypTool/DlgShowKey.h index e425c8e45..f87d764fb 100644 --- a/CrypTool/DlgShowKey.h +++ b/CrypTool/DlgShowKey.h @@ -51,7 +51,7 @@ class CDlgShowKey : public CDialog // Vom Klassen-Assistenten generierte virtuelle Funktionsüberschreibungen //{{AFX_VIRTUAL(CDlgShowKey) public: - virtual int DoModal(); + virtual INT_PTR DoModal(); protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV-Unterstützung //}}AFX_VIRTUAL diff --git a/CrypTool/DlgShowKeyHill10x10.cpp b/CrypTool/DlgShowKeyHill10x10.cpp index cda128885..b4b8e92fa 100644 --- a/CrypTool/DlgShowKeyHill10x10.cpp +++ b/CrypTool/DlgShowKeyHill10x10.cpp @@ -266,11 +266,11 @@ void CDlgShowKeyHill10x10::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgShowKeyHill10x10, CDialog) //{{AFX_MSG_MAP(CDlgShowKeyHill10x10) - ON_BN_CLICKED(IDC_RADIO1, OnKey) - ON_BN_CLICKED(IDC_RADIO2, OnInvKey) - ON_BN_CLICKED(IDC_RADIO27, OnOfs0) - ON_BN_CLICKED(IDC_RADIO28, OnOfs1) - ON_BN_CLICKED(IDC_BUTTON1, OnCopyKey) + ON_BN_CLICKED(IDC_RADIO1, &CDlgShowKeyHill10x10::OnKey) + ON_BN_CLICKED(IDC_RADIO2, &CDlgShowKeyHill10x10::OnInvKey) + ON_BN_CLICKED(IDC_RADIO27, &CDlgShowKeyHill10x10::OnOfs0) + ON_BN_CLICKED(IDC_RADIO28, &CDlgShowKeyHill10x10::OnOfs1) + ON_BN_CLICKED(IDC_BUTTON1, &CDlgShowKeyHill10x10::OnCopyKey) //}}AFX_MSG_MAP END_MESSAGE_MAP() diff --git a/CrypTool/DlgShowKeyHill5x5.cpp b/CrypTool/DlgShowKeyHill5x5.cpp index 275d43bcd..c0f143919 100644 --- a/CrypTool/DlgShowKeyHill5x5.cpp +++ b/CrypTool/DlgShowKeyHill5x5.cpp @@ -117,11 +117,11 @@ void CDlgShowKeyHill5x5::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgShowKeyHill5x5, CDialog) //{{AFX_MSG_MAP(CDlgShowKeyHill5x5) - ON_BN_CLICKED(IDC_RADIO1, OnKey) - ON_BN_CLICKED(IDC_RADIO2, OnInvKey) - ON_BN_CLICKED(IDC_RADIO3, OnOfs0) - ON_BN_CLICKED(IDC_RADIO4, OnOfs1) - ON_BN_CLICKED(IDC_BUTTON1, OnCopyKey) + ON_BN_CLICKED(IDC_RADIO1, &CDlgShowKeyHill5x5::OnKey) + ON_BN_CLICKED(IDC_RADIO2, &CDlgShowKeyHill5x5::OnInvKey) + ON_BN_CLICKED(IDC_RADIO3, &CDlgShowKeyHill5x5::OnOfs0) + ON_BN_CLICKED(IDC_RADIO4, &CDlgShowKeyHill5x5::OnOfs1) + ON_BN_CLICKED(IDC_BUTTON1, &CDlgShowKeyHill5x5::OnCopyKey) //}}AFX_MSG_MAP END_MESSAGE_MAP() diff --git a/CrypTool/DlgShowKeyParameter.cpp b/CrypTool/DlgShowKeyParameter.cpp index d14e6c318..27243961a 100644 --- a/CrypTool/DlgShowKeyParameter.cpp +++ b/CrypTool/DlgShowKeyParameter.cpp @@ -55,9 +55,9 @@ void CDlgShowKeyParameter::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgShowKeyParameter, CDialog) //{{AFX_MSG_MAP(CDlgShowKeyParameter) - ON_BN_CLICKED(IDC_RADIO1, OnUpdate) - ON_BN_CLICKED(IDC_RADIO2, OnUpdate) - ON_BN_CLICKED(IDC_RADIO3, OnUpdate) + ON_BN_CLICKED(IDC_RADIO1, &CDlgShowKeyParameter::OnUpdate) + ON_BN_CLICKED(IDC_RADIO2, &CDlgShowKeyParameter::OnUpdate) + ON_BN_CLICKED(IDC_RADIO3, &CDlgShowKeyParameter::OnUpdate) //}}AFX_MSG_MAP END_MESSAGE_MAP() diff --git a/CrypTool/DlgShowParameterKeyEC.cpp b/CrypTool/DlgShowParameterKeyEC.cpp index dbec16250..46782a0e5 100644 --- a/CrypTool/DlgShowParameterKeyEC.cpp +++ b/CrypTool/DlgShowParameterKeyEC.cpp @@ -79,10 +79,10 @@ void CDlgShowParameterKeyEC::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgShowParameterKeyEC, CDialog) //{{AFX_MSG_MAP(CDlgShowParameterKeyEC) - ON_BN_CLICKED(IDC_RADIO1, OnOctalRadio) - ON_BN_CLICKED(IDC_RADIO2, OnDecimalRadio) - ON_BN_CLICKED(IDC_RADIO3, OnHexRadio) - ON_BN_CLICKED(IDC_BUTTON1, OnNewKeyPair) + ON_BN_CLICKED(IDC_RADIO1, &CDlgShowParameterKeyEC::OnOctalRadio) + ON_BN_CLICKED(IDC_RADIO2, &CDlgShowParameterKeyEC::OnDecimalRadio) + ON_BN_CLICKED(IDC_RADIO3, &CDlgShowParameterKeyEC::OnHexRadio) + ON_BN_CLICKED(IDC_BUTTON1, &CDlgShowParameterKeyEC::OnNewKeyPair) //}}AFX_MSG_MAP END_MESSAGE_MAP() diff --git a/CrypTool/DlgShowPrivateKeyEC.cpp b/CrypTool/DlgShowPrivateKeyEC.cpp index 495d3a9f2..8390eac81 100644 --- a/CrypTool/DlgShowPrivateKeyEC.cpp +++ b/CrypTool/DlgShowPrivateKeyEC.cpp @@ -92,9 +92,9 @@ void CDlgShowPrivateKeyEC::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgShowPrivateKeyEC, CDialog) //{{AFX_MSG_MAP(CDlgShowPrivateKeyEC) - ON_BN_CLICKED(IDC_RADIO1, OnOctalRadio) - ON_BN_CLICKED(IDC_RADIO2, OnDecimalRadio) - ON_BN_CLICKED(IDC_RADIO3, OnHexRadio) + ON_BN_CLICKED(IDC_RADIO1, &CDlgShowPrivateKeyEC::OnOctalRadio) + ON_BN_CLICKED(IDC_RADIO2, &CDlgShowPrivateKeyEC::OnDecimalRadio) + ON_BN_CLICKED(IDC_RADIO3, &CDlgShowPrivateKeyEC::OnHexRadio) //}}AFX_MSG_MAP END_MESSAGE_MAP() diff --git a/CrypTool/DlgShowPublicKeyEC.cpp b/CrypTool/DlgShowPublicKeyEC.cpp index 101d725a3..330ca4efc 100644 --- a/CrypTool/DlgShowPublicKeyEC.cpp +++ b/CrypTool/DlgShowPublicKeyEC.cpp @@ -91,9 +91,9 @@ void CDlgShowPublicKeyEC::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgShowPublicKeyEC, CDialog) //{{AFX_MSG_MAP(CDlgShowPublicKeyEC) - ON_BN_CLICKED(IDC_RADIO1, OnOctalRadio) - ON_BN_CLICKED(IDC_RADIO2, OnDecimalRadio) - ON_BN_CLICKED(IDC_RADIO3, OnHexRadio) + ON_BN_CLICKED(IDC_RADIO1, &CDlgShowPublicKeyEC::OnOctalRadio) + ON_BN_CLICKED(IDC_RADIO2, &CDlgShowPublicKeyEC::OnDecimalRadio) + ON_BN_CLICKED(IDC_RADIO3, &CDlgShowPublicKeyEC::OnHexRadio) //}}AFX_MSG_MAP END_MESSAGE_MAP() diff --git a/CrypTool/DlgShowPublicKeyEC.h b/CrypTool/DlgShowPublicKeyEC.h index 33ad72f94..4caac5c88 100644 --- a/CrypTool/DlgShowPublicKeyEC.h +++ b/CrypTool/DlgShowPublicKeyEC.h @@ -81,7 +81,7 @@ class CDlgShowPublicKeyEC : public CDialog virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV-Unterstützung //}}AFX_VIRTUAL - void CDlgShowPublicKeyEC::UpdateEcListBox(); + void UpdateEcListBox(); // Implementierung protected: diff --git a/CrypTool/DlgSideChannelAttackVisualizationHE.cpp b/CrypTool/DlgSideChannelAttackVisualizationHE.cpp index 74e82947e..e8723e89a 100644 --- a/CrypTool/DlgSideChannelAttackVisualizationHE.cpp +++ b/CrypTool/DlgSideChannelAttackVisualizationHE.cpp @@ -94,21 +94,21 @@ void CDlgSideChannelAttackVisualizationHE::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgSideChannelAttackVisualizationHE, CDialog) //{{AFX_MSG_MAP(CDlgSideChannelAttackVisualizationHE) - ON_BN_CLICKED(IDC_INTRODUCTION, OnIntroduction) - ON_BN_CLICKED(IDC_PREPARATIONS, OnPreparations) - ON_BN_CLICKED(IDC_MESSAGETRANSMISSION, OnMessagetransmission) - ON_BN_CLICKED(IDC_MESSAGEINTERCEPTION, OnMessageinterception) - ON_BN_CLICKED(IDC_ATTACKCYCLE, OnAttackcycle) - ON_BN_CLICKED(IDC_REPORT, OnReport) - ON_BN_CLICKED(IDC_ALICE, OnAlice) - ON_BN_CLICKED(IDC_BOB, OnBob) - ON_BN_CLICKED(IDC_TRUDY, OnTrudy) - ON_BN_CLICKED(IDCLOSE, OnClose) + ON_BN_CLICKED(IDC_INTRODUCTION, &CDlgSideChannelAttackVisualizationHE::OnIntroduction) + ON_BN_CLICKED(IDC_PREPARATIONS, &CDlgSideChannelAttackVisualizationHE::OnPreparations) + ON_BN_CLICKED(IDC_MESSAGETRANSMISSION, &CDlgSideChannelAttackVisualizationHE::OnMessagetransmission) + ON_BN_CLICKED(IDC_MESSAGEINTERCEPTION, &CDlgSideChannelAttackVisualizationHE::OnMessageinterception) + ON_BN_CLICKED(IDC_ATTACKCYCLE, &CDlgSideChannelAttackVisualizationHE::OnAttackcycle) + ON_BN_CLICKED(IDC_REPORT, &CDlgSideChannelAttackVisualizationHE::OnReport) + ON_BN_CLICKED(IDC_ALICE, &CDlgSideChannelAttackVisualizationHE::OnAlice) + ON_BN_CLICKED(IDC_BOB, &CDlgSideChannelAttackVisualizationHE::OnBob) + ON_BN_CLICKED(IDC_TRUDY, &CDlgSideChannelAttackVisualizationHE::OnTrudy) + ON_BN_CLICKED(IDCLOSE, &CDlgSideChannelAttackVisualizationHE::OnClose) ON_WM_TIMER() - ON_BN_CLICKED(IDC_BUTTON_NEXTSINGLESTEP, OnButtonNextsinglestep) - ON_BN_CLICKED(IDC_BUTTON_ALLREMAININGSTEPS, OnButtonAllremainingsteps) - ON_BN_CLICKED(IDC_MESSAGERECEPTION, OnMessagereception) - ON_BN_CLICKED(IDC_CHECK_DISABLEHELP, OnCheckDisablehelp) + ON_BN_CLICKED(IDC_BUTTON_NEXTSINGLESTEP, &CDlgSideChannelAttackVisualizationHE::OnButtonNextsinglestep) + ON_BN_CLICKED(IDC_BUTTON_ALLREMAININGSTEPS, &CDlgSideChannelAttackVisualizationHE::OnButtonAllremainingsteps) + ON_BN_CLICKED(IDC_MESSAGERECEPTION, &CDlgSideChannelAttackVisualizationHE::OnMessagereception) + ON_BN_CLICKED(IDC_CHECK_DISABLEHELP, &CDlgSideChannelAttackVisualizationHE::OnCheckDisablehelp) ON_WM_PAINT() ON_WM_CTLCOLOR() //}}AFX_MSG_MAP diff --git a/CrypTool/DlgSideChannelAttackVisualizationHE.h b/CrypTool/DlgSideChannelAttackVisualizationHE.h index 5f56bfbc4..5b5850311 100644 --- a/CrypTool/DlgSideChannelAttackVisualizationHE.h +++ b/CrypTool/DlgSideChannelAttackVisualizationHE.h @@ -30,9 +30,9 @@ // sub-dialogues #include "DlgSideChannelAttackVisualizationHEIntroduction.h" #include "DlgSideChannelAttackVisualizationHEPreparations.h" -#include "DlgSideChannelAttackVisualizationHEMessagetransmission.h" -#include "DlgSideChannelAttackVisualizationHEMessageinterception.h" -#include "DlgSideChannelAttackVisualizationHEAttackcycle.h" +#include "DlgSideChannelAttackVisualizationHEMessageTransmission.h" +#include "DlgSideChannelAttackVisualizationHEMessageInterception.h" +#include "DlgSideChannelAttackVisualizationHEAttackCycle.h" #include "DlgSideChannelAttackVisualizationHEReport.h" #include "DlgSideChannelAttackVisualizationHEPSEPINPrompt.h" #include "DlgHybridEncryptionDemo.h" // Hinzugefügt von der Klassenansicht diff --git a/CrypTool/DlgSideChannelAttackVisualizationHEPreparationsRequest1.cpp b/CrypTool/DlgSideChannelAttackVisualizationHEPreparationsRequest1.cpp index ede79f43a..a08ed6252 100644 --- a/CrypTool/DlgSideChannelAttackVisualizationHEPreparationsRequest1.cpp +++ b/CrypTool/DlgSideChannelAttackVisualizationHEPreparationsRequest1.cpp @@ -59,10 +59,10 @@ BOOL CDlgSideChannelAttackVisualizationHEPreparationsRequest1::OnInitDialog() } BEGIN_MESSAGE_MAP(CDlgSideChannelAttackVisualizationHEPreparationsRequest1, CDialog) - ON_BN_CLICKED(IDOK, OnBnClickedOk) - ON_BN_CLICKED(IDCANCEL, OnBnClickedCancel) - ON_BN_CLICKED(IDC_RADIO_CHOICE_1, OnBnClickedRadioChoice1) - ON_BN_CLICKED(IDC_RADIO_CHOICE_2, OnBnClickedRadioChoice2) + ON_BN_CLICKED(IDOK, &CDlgSideChannelAttackVisualizationHEPreparationsRequest1::OnBnClickedOk) + ON_BN_CLICKED(IDCANCEL, &CDlgSideChannelAttackVisualizationHEPreparationsRequest1::OnBnClickedCancel) + ON_BN_CLICKED(IDC_RADIO_CHOICE_1, &CDlgSideChannelAttackVisualizationHEPreparationsRequest1::OnBnClickedRadioChoice1) + ON_BN_CLICKED(IDC_RADIO_CHOICE_2, &CDlgSideChannelAttackVisualizationHEPreparationsRequest1::OnBnClickedRadioChoice2) END_MESSAGE_MAP() // CDlgSideChannelAttackVisualizationHEPreparationsRequest1 message handlers diff --git a/CrypTool/DlgSideChannelAttackVisualizationHEPreparationsRequest2.cpp b/CrypTool/DlgSideChannelAttackVisualizationHEPreparationsRequest2.cpp index da68d8a66..5bd8a3c57 100644 --- a/CrypTool/DlgSideChannelAttackVisualizationHEPreparationsRequest2.cpp +++ b/CrypTool/DlgSideChannelAttackVisualizationHEPreparationsRequest2.cpp @@ -62,11 +62,11 @@ BOOL CDlgSideChannelAttackVisualizationHEPreparationsRequest2::OnInitDialog() } BEGIN_MESSAGE_MAP(CDlgSideChannelAttackVisualizationHEPreparationsRequest2, CDialog) - ON_BN_CLICKED(IDOK, OnBnClickedOk) - ON_BN_CLICKED(IDCANCEL, OnBnClickedCancel) - ON_BN_CLICKED(IDC_RADIO_CHOICE_1, OnBnClickedRadioChoice1) - ON_BN_CLICKED(IDC_RADIO_CHOICE_2, OnBnClickedRadioChoice2) - ON_BN_CLICKED(IDC_RADIO_CHOICE_3, OnBnClickedRadioChoice3) + ON_BN_CLICKED(IDOK, &CDlgSideChannelAttackVisualizationHEPreparationsRequest2::OnBnClickedOk) + ON_BN_CLICKED(IDCANCEL, &CDlgSideChannelAttackVisualizationHEPreparationsRequest2::OnBnClickedCancel) + ON_BN_CLICKED(IDC_RADIO_CHOICE_1, &CDlgSideChannelAttackVisualizationHEPreparationsRequest2::OnBnClickedRadioChoice1) + ON_BN_CLICKED(IDC_RADIO_CHOICE_2, &CDlgSideChannelAttackVisualizationHEPreparationsRequest2::OnBnClickedRadioChoice2) + ON_BN_CLICKED(IDC_RADIO_CHOICE_3, &CDlgSideChannelAttackVisualizationHEPreparationsRequest2::OnBnClickedRadioChoice3) END_MESSAGE_MAP() // CDlgSideChannelAttackVisualizationHEPreparationsRequest2 message handlers diff --git a/CrypTool/DlgSideChannelAttackVisualizationHEPreparationsRequest3.cpp b/CrypTool/DlgSideChannelAttackVisualizationHEPreparationsRequest3.cpp index a99844f6f..76612905d 100644 --- a/CrypTool/DlgSideChannelAttackVisualizationHEPreparationsRequest3.cpp +++ b/CrypTool/DlgSideChannelAttackVisualizationHEPreparationsRequest3.cpp @@ -61,11 +61,11 @@ BOOL CDlgSideChannelAttackVisualizationHEPreparationsRequest3::OnInitDialog() } BEGIN_MESSAGE_MAP(CDlgSideChannelAttackVisualizationHEPreparationsRequest3, CDialog) - ON_BN_CLICKED(IDOK, OnBnClickedOk) - ON_BN_CLICKED(IDCANCEL, OnBnClickedCancel) - ON_BN_CLICKED(IDC_RADIO_CHOICE_1, OnBnClickedRadioChoice1) - ON_BN_CLICKED(IDC_RADIO_CHOICE_2, OnBnClickedRadioChoice2) - ON_BN_CLICKED(IDC_RADIO_CHOICE_3, OnBnClickedRadioChoice3) + ON_BN_CLICKED(IDOK, &CDlgSideChannelAttackVisualizationHEPreparationsRequest3::OnBnClickedOk) + ON_BN_CLICKED(IDCANCEL, &CDlgSideChannelAttackVisualizationHEPreparationsRequest3::OnBnClickedCancel) + ON_BN_CLICKED(IDC_RADIO_CHOICE_1, &CDlgSideChannelAttackVisualizationHEPreparationsRequest3::OnBnClickedRadioChoice1) + ON_BN_CLICKED(IDC_RADIO_CHOICE_2, &CDlgSideChannelAttackVisualizationHEPreparationsRequest3::OnBnClickedRadioChoice2) + ON_BN_CLICKED(IDC_RADIO_CHOICE_3, &CDlgSideChannelAttackVisualizationHEPreparationsRequest3::OnBnClickedRadioChoice3) END_MESSAGE_MAP() // CDlgSideChannelAttackVisualizationHEPreparationsRequest3 message handlers diff --git a/CrypTool/DlgSideChannelAttackVisualizationHETrudy.cpp b/CrypTool/DlgSideChannelAttackVisualizationHETrudy.cpp index 53cecc42f..a06f4d96c 100644 --- a/CrypTool/DlgSideChannelAttackVisualizationHETrudy.cpp +++ b/CrypTool/DlgSideChannelAttackVisualizationHETrudy.cpp @@ -25,7 +25,7 @@ #include "CrypToolApp.h" #include "DlgSideChannelAttackVisualizationHETrudy.h" #include "CrypToolTools.h" -#include ".\dlgsidechannelattackvisualizationhetrudy.h" +#include "DlgSideChannelAttackVisualizationHETrudy.h" #ifdef _DEBUG #define new DEBUG_NEW @@ -82,7 +82,7 @@ void CDlgSideChannelAttackVisualizationHETrudy::DoDataExchange(CDataExchange* pD BEGIN_MESSAGE_MAP(CDlgSideChannelAttackVisualizationHETrudy, CDialog) //{{AFX_MSG_MAP(CDlgSideChannelAttackVisualizationHETrudy) //}}AFX_MSG_MAP - ON_BN_CLICKED(IDOK, OnBnClickedOk) + ON_BN_CLICKED(IDOK, &CDlgSideChannelAttackVisualizationHETrudy::OnBnClickedOk) END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// diff --git a/CrypTool/DlgSigAttModificDemo.cpp b/CrypTool/DlgSigAttModificDemo.cpp index f510e7493..92c125192 100644 --- a/CrypTool/DlgSigAttModificDemo.cpp +++ b/CrypTool/DlgSigAttModificDemo.cpp @@ -78,13 +78,13 @@ void CDlgSigAttModificDemo::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgSigAttModificDemo, CDialog) //{{AFX_MSG_MAP(CDlgSigAttModificDemo) - ON_BN_CLICKED(IDOK, OnModify) - ON_BN_CLICKED(IDC_RADIO2, OnAttachment) - ON_BN_CLICKED(IDC_METHOD, OnMethod) - ON_BN_CLICKED(IDC_RADIO3, OnPrintable) - ON_BN_CLICKED(IDC_RADIO4, OnUnprintable) - ON_EN_UPDATE(IDC_HASHVALUE, OnUpdateHashvalue) - ON_EN_UPDATE(IDC_SIGBIT, OnUpdateSigbit) + ON_BN_CLICKED(IDOK, &CDlgSigAttModificDemo::OnModify) + ON_BN_CLICKED(IDC_RADIO2, &CDlgSigAttModificDemo::OnAttachment) + ON_BN_CLICKED(IDC_METHOD, &CDlgSigAttModificDemo::OnMethod) + ON_BN_CLICKED(IDC_RADIO3, &CDlgSigAttModificDemo::OnPrintable) + ON_BN_CLICKED(IDC_RADIO4, &CDlgSigAttModificDemo::OnUnprintable) + ON_EN_UPDATE(IDC_HASHVALUE, &CDlgSigAttModificDemo::OnUpdateHashvalue) + ON_EN_UPDATE(IDC_SIGBIT, &CDlgSigAttModificDemo::OnUpdateSigbit) //}}AFX_MSG_MAP END_MESSAGE_MAP() diff --git a/CrypTool/DlgSignature.cpp b/CrypTool/DlgSignature.cpp index 3d1eedc9d..ccc10cf1c 100644 --- a/CrypTool/DlgSignature.cpp +++ b/CrypTool/DlgSignature.cpp @@ -209,19 +209,19 @@ void CDlgSignature::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgSignature, CDialog) //{{AFX_MSG_MAP(CDlgSignature) - ON_NOTIFY(NM_CLICK, IDC_LIST2, OnClickList2) - ON_NOTIFY(HDN_ITEMCLICK, IDC_LIST2, OnItemclickList2) - ON_NOTIFY(LVN_COLUMNCLICK, IDC_LIST2, OnColumnclickList2) - ON_NOTIFY(LVN_KEYDOWN, IDC_LIST2, OnKeydownList2) - ON_BN_CLICKED(IDC_CHECK_DSA_KEYS, OnCheckDsaKeys) - ON_BN_CLICKED(IDC_CHECK_EC_KEYS, OnCheckEcKeys) - ON_BN_CLICKED(IDC_CHECK_RSA_KEYS, OnCheckRsaKeys) - ON_BN_CLICKED(IDC_RADIO6, OnRadioRSA) - ON_BN_CLICKED(IDC_RADIO7, OnRadioDSA) - ON_BN_CLICKED(IDC_RADIO8, OnRadioECDSA) - ON_BN_CLICKED(IDC_RADIO9, OnRadioECNR) - ON_BN_CLICKED(IDC_RADIO10, OnRadioAffinCoord) - ON_BN_CLICKED(IDC_RADIO11, OnRadioProjectCoord) + ON_NOTIFY(NM_CLICK, IDC_LIST2, &CDlgSignature::OnClickList2) + ON_NOTIFY(HDN_ITEMCLICK, IDC_LIST2, &CDlgSignature::OnItemclickList2) + ON_NOTIFY(LVN_COLUMNCLICK, IDC_LIST2, &CDlgSignature::OnColumnclickList2) + ON_NOTIFY(LVN_KEYDOWN, IDC_LIST2, &CDlgSignature::OnKeydownList2) + ON_BN_CLICKED(IDC_CHECK_DSA_KEYS, &CDlgSignature::OnCheckDsaKeys) + ON_BN_CLICKED(IDC_CHECK_EC_KEYS, &CDlgSignature::OnCheckEcKeys) + ON_BN_CLICKED(IDC_CHECK_RSA_KEYS, &CDlgSignature::OnCheckRsaKeys) + ON_BN_CLICKED(IDC_RADIO6, &CDlgSignature::OnRadioRSA) + ON_BN_CLICKED(IDC_RADIO7, &CDlgSignature::OnRadioDSA) + ON_BN_CLICKED(IDC_RADIO8, &CDlgSignature::OnRadioECDSA) + ON_BN_CLICKED(IDC_RADIO9, &CDlgSignature::OnRadioECNR) + ON_BN_CLICKED(IDC_RADIO10, &CDlgSignature::OnRadioAffinCoord) + ON_BN_CLICKED(IDC_RADIO11, &CDlgSignature::OnRadioProjectCoord) //}}AFX_MSG_MAP END_MESSAGE_MAP() diff --git a/CrypTool/DlgSignature.h b/CrypTool/DlgSignature.h index 3fac246bd..19db5d6d0 100644 --- a/CrypTool/DlgSignature.h +++ b/CrypTool/DlgSignature.h @@ -41,7 +41,7 @@ class CDlgSignature : public CDialog public: CDlgSignature(CWnd* pParent = NULL); // Standardkonstruktor - CDlgSignature::~CDlgSignature(); + ~CDlgSignature(); // Dialogfelddaten //{{AFX_DATA(CDlgSignature) diff --git a/CrypTool/DlgSignatureAttack.cpp b/CrypTool/DlgSignatureAttack.cpp index 436745c6f..05dbe7b76 100644 --- a/CrypTool/DlgSignatureAttack.cpp +++ b/CrypTool/DlgSignatureAttack.cpp @@ -47,7 +47,7 @@ using namespace std; #include "OptionsForSignatureAttack.h" #include "ErrorcodesForSignatureAttack.h" #include "DlgShowProgress.h" -#include ".\dlgsignatureattack.h" +#include "DlgSignatureAttack.h" // Verzeichnis, in dem CT gerade läuft (siehe CrypToolApp.cpp) extern char *Pfad; @@ -87,14 +87,14 @@ void CDlgSignatureAttack::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgSignatureAttack, CDialog) //{{AFX_MSG_MAP(CDlgSignatureAttack) - ON_BN_CLICKED(IDC_OPTIONS, OnOptions) - ON_BN_CLICKED(IDC_BROWSE_HARMLESS, OnBrowseOriginal) - ON_BN_CLICKED(IDC_BROWSE_DANGEROUS, OnBrowseFake) - ON_BN_CLICKED(IDC_COMPUTE, OnCompute) - ON_EN_UPDATE(IDC_FILE_DANGEROUS, OnUpdateFileFake) - ON_EN_UPDATE(IDC_FILE_HARMLESS, OnUpdateFileOriginal) + ON_BN_CLICKED(IDC_OPTIONS, &CDlgSignatureAttack::OnOptions) + ON_BN_CLICKED(IDC_BROWSE_HARMLESS, &CDlgSignatureAttack::OnBrowseOriginal) + ON_BN_CLICKED(IDC_BROWSE_DANGEROUS, &CDlgSignatureAttack::OnBrowseFake) + ON_BN_CLICKED(IDC_COMPUTE, &CDlgSignatureAttack::OnCompute) + ON_EN_UPDATE(IDC_FILE_DANGEROUS, &CDlgSignatureAttack::OnUpdateFileFake) + ON_EN_UPDATE(IDC_FILE_HARMLESS, &CDlgSignatureAttack::OnUpdateFileOriginal) //}}AFX_MSG_MAP - ON_BN_CLICKED(IDC_BUTTON_USEDEFAULTMESSAGES, OnBnClickedButtonUsedefaultmessages) + ON_BN_CLICKED(IDC_BUTTON_USEDEFAULTMESSAGES, &CDlgSignatureAttack::OnBnClickedButtonUsedefaultmessages) END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// diff --git a/CrypTool/DlgSignatureDemo.cpp b/CrypTool/DlgSignatureDemo.cpp index 211b72518..7bc476f80 100644 --- a/CrypTool/DlgSignatureDemo.cpp +++ b/CrypTool/DlgSignatureDemo.cpp @@ -87,20 +87,20 @@ void CDlgSignatureDemo::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgSignatureDemo, CDialog) //{{AFX_MSG_MAP(CDlgSignatureDemo) - ON_BN_CLICKED(IDC_SELECT_DOCUMENT, OnSelectDocument) - ON_BN_CLICKED(IDC_INFO_DOCUMENT, OnInfoDocument) - ON_BN_CLICKED(IDC_SELECT_KEY, OnSelectKey) - ON_BN_CLICKED(IDC_INFO_KEY, OnInfoKey) - ON_BN_CLICKED(IDC_SELECT_HASHALG, OnSelectHashAlg) - ON_BN_CLICKED(IDC_COMPUTE, OnCompute) - ON_BN_CLICKED(IDC_INFO_HASH, OnInfoHash) - ON_BN_CLICKED(IDC_ENCRYPT, OnEncrypt) - ON_BN_CLICKED(IDC_INFO_HASH_ENC, OnInfoHashEnc) - ON_BN_CLICKED(IDC_SELECT_CERT, OnSelectCert) - ON_BN_CLICKED(IDC_INFO_ALG, OnInfoAlg) - ON_BN_CLICKED(IDC_COMBINE, OnCombine) - ON_BN_CLICKED(IDC_INFO_CERT, OnInfoCert) - ON_BN_CLICKED(IDC_INFO_SIGN, OnInfoSign) + ON_BN_CLICKED(IDC_SELECT_DOCUMENT, &CDlgSignatureDemo::OnSelectDocument) + ON_BN_CLICKED(IDC_INFO_DOCUMENT, &CDlgSignatureDemo::OnInfoDocument) + ON_BN_CLICKED(IDC_SELECT_KEY, &CDlgSignatureDemo::OnSelectKey) + ON_BN_CLICKED(IDC_INFO_KEY, &CDlgSignatureDemo::OnInfoKey) + ON_BN_CLICKED(IDC_SELECT_HASHALG, &CDlgSignatureDemo::OnSelectHashAlg) + ON_BN_CLICKED(IDC_COMPUTE, &CDlgSignatureDemo::OnCompute) + ON_BN_CLICKED(IDC_INFO_HASH, &CDlgSignatureDemo::OnInfoHash) + ON_BN_CLICKED(IDC_ENCRYPT, &CDlgSignatureDemo::OnEncrypt) + ON_BN_CLICKED(IDC_INFO_HASH_ENC, &CDlgSignatureDemo::OnInfoHashEnc) + ON_BN_CLICKED(IDC_SELECT_CERT, &CDlgSignatureDemo::OnSelectCert) + ON_BN_CLICKED(IDC_INFO_ALG, &CDlgSignatureDemo::OnInfoAlg) + ON_BN_CLICKED(IDC_COMBINE, &CDlgSignatureDemo::OnCombine) + ON_BN_CLICKED(IDC_INFO_CERT, &CDlgSignatureDemo::OnInfoCert) + ON_BN_CLICKED(IDC_INFO_SIGN, &CDlgSignatureDemo::OnInfoSign) ON_WM_PAINT() //}}AFX_MSG_MAP END_MESSAGE_MAP() diff --git a/CrypTool/DlgSimpleTranspositions.cpp b/CrypTool/DlgSimpleTranspositions.cpp index 8595364b2..0e57ebf80 100644 --- a/CrypTool/DlgSimpleTranspositions.cpp +++ b/CrypTool/DlgSimpleTranspositions.cpp @@ -67,10 +67,10 @@ void CDlgSimpleTranspositions::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgSimpleTranspositions, CDialog) ON_BN_CLICKED(ID_ENCRYPT, &CDlgSimpleTranspositions::OnBnClickedEncrypt) ON_BN_CLICKED(ID_DECRYPT, &CDlgSimpleTranspositions::OnBnClickedDecrypt) - ON_BN_CLICKED(IDC_RADIO_SCYTALE, OnRadioScytale) - ON_BN_CLICKED(IDC_RADIO_RAIL_FENCE, OnRadioRailFence) + ON_BN_CLICKED(IDC_RADIO_SCYTALE, &CDlgSimpleTranspositions::OnRadioScytale) + ON_BN_CLICKED(IDC_RADIO_RAIL_FENCE, &CDlgSimpleTranspositions::OnRadioRailFence) ON_BN_CLICKED(ID_TEXTOPTIONS, &CDlgSimpleTranspositions::OnBnClickedTextoptions) - ON_BN_CLICKED(IDC_PASTE_KEY, OnPasteKey) + ON_BN_CLICKED(IDC_PASTE_KEY, &CDlgSimpleTranspositions::OnPasteKey) END_MESSAGE_MAP() BOOL CDlgSimpleTranspositions::OnInitDialog() diff --git a/CrypTool/DlgSolitaire.cpp b/CrypTool/DlgSolitaire.cpp index b48907242..5bc3061ce 100644 --- a/CrypTool/DlgSolitaire.cpp +++ b/CrypTool/DlgSolitaire.cpp @@ -24,7 +24,7 @@ #include "stdafx.h" #include "CrypToolApp.h" #include "DlgSolitaire.h" -#include ".\dlgsolitaire.h" +#include "DlgSolitaire.h" #include "FileTools.h" #include "CrypToolTools.h" #include "KeyRepository.h" @@ -373,82 +373,82 @@ BOOL CDlgSolitaire::OnInitDialog() } BEGIN_MESSAGE_MAP(CDlgSolitaire, CDialog) - ON_CBN_SELCHANGE(IDC_Kartenanzahl, OnCbnSelchangeKartenanzahl) - ON_CBN_SELCHANGE(IDC_COMBO2, OnCbnSelchangeCombo2) - ON_BN_CLICKED(IDC_BUTTON61, OnBnClickedButton61) - ON_BN_CLICKED(IDC_BUTTON9, OnBnClickedButton9) - ON_BN_CLICKED(IDC_BUTTON4, OnBnClickedCryptStep1) - ON_BN_CLICKED(IDC_BUTTON5, OnBnClickedCryptStep2) - ON_BN_CLICKED(IDC_BUTTON6, OnBnClickedCryptStep3) - ON_BN_CLICKED(IDC_BUTTON7, OnBnClickedCryptStep4) - ON_BN_CLICKED(IDC_BUTTON8, OnBnClickedCryptStep5) - ON_BN_CLICKED(IDC_BUTTON2, OnBnClickedDoCrypt) - ON_BN_CLICKED(IDC_BUTTON3, OnBnClickedDoCryptSteps) - ON_BN_CLICKED(IDC_BUTTON10, OnBnClickedButton10) - ON_BN_CLICKED(IDC_BUTTON11, OnBnClickedButton11) - ON_BN_CLICKED(IDC_BUTTON12, OnBnClickedButton12) - ON_BN_CLICKED(IDC_BUTTON13, OnBnClickedButton13) - ON_BN_CLICKED(IDC_BUTTON14, OnBnClickedButton14) - ON_BN_CLICKED(IDC_BUTTON15, OnBnClickedButton15) - ON_BN_CLICKED(IDC_BUTTON16, OnBnClickedButton16) - ON_BN_CLICKED(IDC_BUTTON17, OnBnClickedButton17) - ON_BN_CLICKED(IDC_BUTTON18, OnBnClickedButton18) - ON_BN_CLICKED(IDC_BUTTON19, OnBnClickedButton19) - ON_BN_CLICKED(IDC_BUTTON20, OnBnClickedButton20) - ON_BN_CLICKED(IDC_BUTTON21, OnBnClickedButton21) - ON_BN_CLICKED(IDC_BUTTON22, OnBnClickedButton22) - ON_BN_CLICKED(IDC_BUTTON23, OnBnClickedButton23) - ON_BN_CLICKED(IDC_BUTTON24, OnBnClickedButton24) - ON_BN_CLICKED(IDC_BUTTON25, OnBnClickedButton25) - ON_BN_CLICKED(IDC_BUTTON26, OnBnClickedButton26) - ON_BN_CLICKED(IDC_BUTTON27, OnBnClickedButton27) - ON_BN_CLICKED(IDC_BUTTON28, OnBnClickedButton28) - ON_BN_CLICKED(IDC_BUTTON29, OnBnClickedButton29) - ON_BN_CLICKED(IDC_BUTTON30, OnBnClickedButton30) - ON_BN_CLICKED(IDC_BUTTON31, OnBnClickedButton31) - ON_BN_CLICKED(IDC_BUTTON32, OnBnClickedButton32) - ON_BN_CLICKED(IDC_BUTTON33, OnBnClickedButton33) - ON_BN_CLICKED(IDC_BUTTON34, OnBnClickedButton34) - ON_BN_CLICKED(IDC_BUTTON35, OnBnClickedButton35) - ON_BN_CLICKED(IDC_BUTTON36, OnBnClickedButton36) - ON_BN_CLICKED(IDC_BUTTON37, OnBnClickedButton37) - ON_BN_CLICKED(IDC_BUTTON38, OnBnClickedButton38) - ON_BN_CLICKED(IDC_BUTTON39, OnBnClickedButton39) - ON_BN_CLICKED(IDC_BUTTON40, OnBnClickedButton40) - ON_BN_CLICKED(IDC_BUTTON41, OnBnClickedButton41) - ON_BN_CLICKED(IDC_BUTTON42, OnBnClickedButton42) - ON_BN_CLICKED(IDC_BUTTON43, OnBnClickedButton43) - ON_BN_CLICKED(IDC_BUTTON44, OnBnClickedButton44) - ON_BN_CLICKED(IDC_BUTTON45, OnBnClickedButton45) - ON_BN_CLICKED(IDC_BUTTON46, OnBnClickedButton46) - ON_BN_CLICKED(IDC_BUTTON47, OnBnClickedButton47) - ON_BN_CLICKED(IDC_BUTTON48, OnBnClickedButton48) - ON_BN_CLICKED(IDC_BUTTON49, OnBnClickedButton49) - ON_BN_CLICKED(IDC_BUTTON50, OnBnClickedButton50) - ON_BN_CLICKED(IDC_BUTTON51, OnBnClickedButton51) - ON_BN_CLICKED(IDC_BUTTON52, OnBnClickedButton52) - ON_BN_CLICKED(IDC_BUTTON53, OnBnClickedButton53) - ON_BN_CLICKED(IDC_BUTTON54, OnBnClickedButton54) - ON_BN_CLICKED(IDC_BUTTON55, OnBnClickedButton55) - ON_BN_CLICKED(IDC_BUTTON56, OnBnClickedButton56) - ON_BN_CLICKED(IDC_BUTTON57, OnBnClickedButton57) - ON_BN_CLICKED(IDC_BUTTON58, OnBnClickedButton58) - ON_BN_CLICKED(IDC_BUTTON59, OnBnClickedButton59) - ON_BN_CLICKED(IDC_BUTTON60, OnBnClickedButton60) - ON_BN_CLICKED(IDC_BUTTON62, OnBnClickedButton62) - ON_BN_CLICKED(IDC_BUTTON63, OnResetCardSettings) - ON_EN_CHANGE(IDC_EDIT4, OnEnChangePassword) - ON_BN_CLICKED(IDC_BUTTON1, OnBnClickedModifyDeckByPassword) - ON_BN_CLICKED(IDC_BUTTON64, OnBnClickedSaveDeck) - ON_BN_CLICKED(IDC_BUTTON65, OnBnClickedSaveFinalDeck) - ON_BN_CLICKED(IDC_BUTTON67, OnBnCancel) - ON_BN_CLICKED(IDC_BUTTON66, OnEncrypt) - ON_BN_CLICKED(IDC_BUTTON68, OnDecrypt) - ON_BN_CLICKED(IDC_PASTE_KEY, OnPasteKey) - ON_BN_CLICKED(IDC_BUTTON69, OnBnFinishCrypt) - ON_BN_CLICKED(IDC_BUTTON71, OnBnReInit) - ON_BN_CLICKED(IDC_BUTTON70, OnBnNewShuffle) - ON_BN_CLICKED(IDC_BUTTON72, OnBnSaveKeyStream) + ON_CBN_SELCHANGE(IDC_Kartenanzahl, &CDlgSolitaire::OnCbnSelchangeKartenanzahl) + ON_CBN_SELCHANGE(IDC_COMBO2, &CDlgSolitaire::OnCbnSelchangeCombo2) + ON_BN_CLICKED(IDC_BUTTON61, &CDlgSolitaire::OnBnClickedButton61) + ON_BN_CLICKED(IDC_BUTTON9, &CDlgSolitaire::OnBnClickedButton9) + ON_BN_CLICKED(IDC_BUTTON4, &CDlgSolitaire::OnBnClickedCryptStep1) + ON_BN_CLICKED(IDC_BUTTON5, &CDlgSolitaire::OnBnClickedCryptStep2) + ON_BN_CLICKED(IDC_BUTTON6, &CDlgSolitaire::OnBnClickedCryptStep3) + ON_BN_CLICKED(IDC_BUTTON7, &CDlgSolitaire::OnBnClickedCryptStep4) + ON_BN_CLICKED(IDC_BUTTON8, &CDlgSolitaire::OnBnClickedCryptStep5) + ON_BN_CLICKED(IDC_BUTTON2, &CDlgSolitaire::OnBnClickedDoCrypt) + ON_BN_CLICKED(IDC_BUTTON3, &CDlgSolitaire::OnBnClickedDoCryptSteps) + ON_BN_CLICKED(IDC_BUTTON10, &CDlgSolitaire::OnBnClickedButton10) + ON_BN_CLICKED(IDC_BUTTON11, &CDlgSolitaire::OnBnClickedButton11) + ON_BN_CLICKED(IDC_BUTTON12, &CDlgSolitaire::OnBnClickedButton12) + ON_BN_CLICKED(IDC_BUTTON13, &CDlgSolitaire::OnBnClickedButton13) + ON_BN_CLICKED(IDC_BUTTON14, &CDlgSolitaire::OnBnClickedButton14) + ON_BN_CLICKED(IDC_BUTTON15, &CDlgSolitaire::OnBnClickedButton15) + ON_BN_CLICKED(IDC_BUTTON16, &CDlgSolitaire::OnBnClickedButton16) + ON_BN_CLICKED(IDC_BUTTON17, &CDlgSolitaire::OnBnClickedButton17) + ON_BN_CLICKED(IDC_BUTTON18, &CDlgSolitaire::OnBnClickedButton18) + ON_BN_CLICKED(IDC_BUTTON19, &CDlgSolitaire::OnBnClickedButton19) + ON_BN_CLICKED(IDC_BUTTON20, &CDlgSolitaire::OnBnClickedButton20) + ON_BN_CLICKED(IDC_BUTTON21, &CDlgSolitaire::OnBnClickedButton21) + ON_BN_CLICKED(IDC_BUTTON22, &CDlgSolitaire::OnBnClickedButton22) + ON_BN_CLICKED(IDC_BUTTON23, &CDlgSolitaire::OnBnClickedButton23) + ON_BN_CLICKED(IDC_BUTTON24, &CDlgSolitaire::OnBnClickedButton24) + ON_BN_CLICKED(IDC_BUTTON25, &CDlgSolitaire::OnBnClickedButton25) + ON_BN_CLICKED(IDC_BUTTON26, &CDlgSolitaire::OnBnClickedButton26) + ON_BN_CLICKED(IDC_BUTTON27, &CDlgSolitaire::OnBnClickedButton27) + ON_BN_CLICKED(IDC_BUTTON28, &CDlgSolitaire::OnBnClickedButton28) + ON_BN_CLICKED(IDC_BUTTON29, &CDlgSolitaire::OnBnClickedButton29) + ON_BN_CLICKED(IDC_BUTTON30, &CDlgSolitaire::OnBnClickedButton30) + ON_BN_CLICKED(IDC_BUTTON31, &CDlgSolitaire::OnBnClickedButton31) + ON_BN_CLICKED(IDC_BUTTON32, &CDlgSolitaire::OnBnClickedButton32) + ON_BN_CLICKED(IDC_BUTTON33, &CDlgSolitaire::OnBnClickedButton33) + ON_BN_CLICKED(IDC_BUTTON34, &CDlgSolitaire::OnBnClickedButton34) + ON_BN_CLICKED(IDC_BUTTON35, &CDlgSolitaire::OnBnClickedButton35) + ON_BN_CLICKED(IDC_BUTTON36, &CDlgSolitaire::OnBnClickedButton36) + ON_BN_CLICKED(IDC_BUTTON37, &CDlgSolitaire::OnBnClickedButton37) + ON_BN_CLICKED(IDC_BUTTON38, &CDlgSolitaire::OnBnClickedButton38) + ON_BN_CLICKED(IDC_BUTTON39, &CDlgSolitaire::OnBnClickedButton39) + ON_BN_CLICKED(IDC_BUTTON40, &CDlgSolitaire::OnBnClickedButton40) + ON_BN_CLICKED(IDC_BUTTON41, &CDlgSolitaire::OnBnClickedButton41) + ON_BN_CLICKED(IDC_BUTTON42, &CDlgSolitaire::OnBnClickedButton42) + ON_BN_CLICKED(IDC_BUTTON43, &CDlgSolitaire::OnBnClickedButton43) + ON_BN_CLICKED(IDC_BUTTON44, &CDlgSolitaire::OnBnClickedButton44) + ON_BN_CLICKED(IDC_BUTTON45, &CDlgSolitaire::OnBnClickedButton45) + ON_BN_CLICKED(IDC_BUTTON46, &CDlgSolitaire::OnBnClickedButton46) + ON_BN_CLICKED(IDC_BUTTON47, &CDlgSolitaire::OnBnClickedButton47) + ON_BN_CLICKED(IDC_BUTTON48, &CDlgSolitaire::OnBnClickedButton48) + ON_BN_CLICKED(IDC_BUTTON49, &CDlgSolitaire::OnBnClickedButton49) + ON_BN_CLICKED(IDC_BUTTON50, &CDlgSolitaire::OnBnClickedButton50) + ON_BN_CLICKED(IDC_BUTTON51, &CDlgSolitaire::OnBnClickedButton51) + ON_BN_CLICKED(IDC_BUTTON52, &CDlgSolitaire::OnBnClickedButton52) + ON_BN_CLICKED(IDC_BUTTON53, &CDlgSolitaire::OnBnClickedButton53) + ON_BN_CLICKED(IDC_BUTTON54, &CDlgSolitaire::OnBnClickedButton54) + ON_BN_CLICKED(IDC_BUTTON55, &CDlgSolitaire::OnBnClickedButton55) + ON_BN_CLICKED(IDC_BUTTON56, &CDlgSolitaire::OnBnClickedButton56) + ON_BN_CLICKED(IDC_BUTTON57, &CDlgSolitaire::OnBnClickedButton57) + ON_BN_CLICKED(IDC_BUTTON58, &CDlgSolitaire::OnBnClickedButton58) + ON_BN_CLICKED(IDC_BUTTON59, &CDlgSolitaire::OnBnClickedButton59) + ON_BN_CLICKED(IDC_BUTTON60, &CDlgSolitaire::OnBnClickedButton60) + ON_BN_CLICKED(IDC_BUTTON62, &CDlgSolitaire::OnBnClickedButton62) + ON_BN_CLICKED(IDC_BUTTON63, &CDlgSolitaire::OnResetCardSettings) + ON_EN_CHANGE(IDC_EDIT4, &CDlgSolitaire::OnEnChangePassword) + ON_BN_CLICKED(IDC_BUTTON1, &CDlgSolitaire::OnBnClickedModifyDeckByPassword) + ON_BN_CLICKED(IDC_BUTTON64, &CDlgSolitaire::OnBnClickedSaveDeck) + ON_BN_CLICKED(IDC_BUTTON65, &CDlgSolitaire::OnBnClickedSaveFinalDeck) + ON_BN_CLICKED(IDC_BUTTON67, &CDlgSolitaire::OnBnCancel) + ON_BN_CLICKED(IDC_BUTTON66, &CDlgSolitaire::OnEncrypt) + ON_BN_CLICKED(IDC_BUTTON68, &CDlgSolitaire::OnDecrypt) + ON_BN_CLICKED(IDC_PASTE_KEY, &CDlgSolitaire::OnPasteKey) + ON_BN_CLICKED(IDC_BUTTON69, &CDlgSolitaire::OnBnFinishCrypt) + ON_BN_CLICKED(IDC_BUTTON71, &CDlgSolitaire::OnBnReInit) + ON_BN_CLICKED(IDC_BUTTON70, &CDlgSolitaire::OnBnNewShuffle) + ON_BN_CLICKED(IDC_BUTTON72, &CDlgSolitaire::OnBnSaveKeyStream) END_MESSAGE_MAP() void CDlgSolitaire::enableVorgabe(bool art) diff --git a/CrypTool/DlgSolitaireAnalyse.cpp b/CrypTool/DlgSolitaireAnalyse.cpp index 017f43d4f..2909f804d 100644 --- a/CrypTool/DlgSolitaireAnalyse.cpp +++ b/CrypTool/DlgSolitaireAnalyse.cpp @@ -24,7 +24,7 @@ #include "stdafx.h" #include "CrypToolApp.h" #include "DlgSolitaireAnalyse.h" -#include ".\dlgsolitaireanalyse.h" +#include "DlgSolitaireAnalyse.h" #include "FileTools.h" #include "DialogeMessage.h" #include "CrypToolTools.h" @@ -146,70 +146,70 @@ BOOL CDlgSolitaireAnalyse::OnInitDialog() } BEGIN_MESSAGE_MAP(CDlgSolitaireAnalyse, CDialog) - ON_CBN_SELCHANGE(IDC_Kartenanzahl, OnCbnSelchangeKartenanzahl) - ON_BN_CLICKED(IDC_BUTTON40, OnBnClickedButton40) - ON_BN_CLICKED(IDC_BUTTON9, OnBnClickedButton9) - ON_BN_CLICKED(IDC_BUTTON10, OnBnClickedButton10) - ON_BN_CLICKED(IDC_BUTTON11, OnBnClickedButton11) - ON_BN_CLICKED(IDC_BUTTON12, OnBnClickedButton12) - ON_BN_CLICKED(IDC_BUTTON13, OnBnClickedButton13) - ON_BN_CLICKED(IDC_BUTTON14, OnBnClickedButton14) - ON_BN_CLICKED(IDC_BUTTON15, OnBnClickedButton15) - ON_BN_CLICKED(IDC_BUTTON16, OnBnClickedButton16) - ON_BN_CLICKED(IDC_BUTTON17, OnBnClickedButton17) - ON_BN_CLICKED(IDC_BUTTON18, OnBnClickedButton18) - ON_BN_CLICKED(IDC_BUTTON19, OnBnClickedButton19) - ON_BN_CLICKED(IDC_BUTTON20, OnBnClickedButton20) - ON_BN_CLICKED(IDC_BUTTON21, OnBnClickedButton21) - ON_BN_CLICKED(IDC_BUTTON22, OnBnClickedButton22) - ON_BN_CLICKED(IDC_BUTTON23, OnBnClickedButton23) - ON_BN_CLICKED(IDC_BUTTON24, OnBnClickedButton24) - ON_BN_CLICKED(IDC_BUTTON25, OnBnClickedButton25) - ON_BN_CLICKED(IDC_BUTTON26, OnBnClickedButton26) - ON_BN_CLICKED(IDC_BUTTON27, OnBnClickedButton27) - ON_BN_CLICKED(IDC_BUTTON28, OnBnClickedButton28) - ON_BN_CLICKED(IDC_BUTTON29, OnBnClickedButton29) - ON_BN_CLICKED(IDC_BUTTON30, OnBnClickedButton30) - ON_BN_CLICKED(IDC_BUTTON31, OnBnClickedButton31) - ON_BN_CLICKED(IDC_BUTTON32, OnBnClickedButton32) - ON_BN_CLICKED(IDC_BUTTON33, OnBnClickedButton33) - ON_BN_CLICKED(IDC_BUTTON34, OnBnClickedButton34) - ON_BN_CLICKED(IDC_BUTTON35, OnBnClickedButton35) - ON_BN_CLICKED(IDC_BUTTON36, OnBnClickedButton36) - ON_BN_CLICKED(IDC_BUTTON37, OnBnClickedButton37) - ON_BN_CLICKED(IDC_BUTTON38, OnBnClickedButton38) - ON_BN_CLICKED(IDC_BUTTON39, OnBnClickedButton39) - ON_BN_CLICKED(IDC_BUTTON41, OnBnClickedButton41) - ON_BN_CLICKED(IDC_BUTTON42, OnBnClickedButton42) - ON_BN_CLICKED(IDC_BUTTON43, OnBnClickedButton43) - ON_BN_CLICKED(IDC_BUTTON44, OnBnClickedButton44) - ON_BN_CLICKED(IDC_BUTTON45, OnBnClickedButton45) - ON_BN_CLICKED(IDC_BUTTON46, OnBnClickedButton46) - ON_BN_CLICKED(IDC_BUTTON47, OnBnClickedButton47) - ON_BN_CLICKED(IDC_BUTTON48, OnBnClickedButton48) - ON_BN_CLICKED(IDC_BUTTON49, OnBnClickedButton49) - ON_BN_CLICKED(IDC_BUTTON50, OnBnClickedButton50) - ON_BN_CLICKED(IDC_BUTTON51, OnBnClickedButton51) - ON_BN_CLICKED(IDC_BUTTON52, OnBnClickedButton52) - ON_BN_CLICKED(IDC_BUTTON53, OnBnClickedButton53) - ON_BN_CLICKED(IDC_BUTTON54, OnBnClickedButton54) - ON_BN_CLICKED(IDC_BUTTON55, OnBnClickedButton55) - ON_BN_CLICKED(IDC_BUTTON56, OnBnClickedButton56) - ON_BN_CLICKED(IDC_BUTTON57, OnBnClickedButton57) - ON_BN_CLICKED(IDC_BUTTON58, OnBnClickedButton58) - ON_BN_CLICKED(IDC_BUTTON59, OnBnClickedButton59) - ON_BN_CLICKED(IDC_BUTTON60, OnBnClickedButton60) - ON_BN_CLICKED(IDC_BUTTON61, OnBnClickedButton61) - ON_BN_CLICKED(IDC_BUTTON62, OnBnClickedButton62) - ON_BN_CLICKED(IDC_BUTTON63, OnBnClickedButton63) - ON_BN_CLICKED(IDC_BUTTON1, OnBnClickedButton1) - ON_BN_CLICKED(IDC_BUTTON2, OnBnClickedButton2) - ON_BN_CLICKED(IDC_BUTTON3, OnBnClickedButton3) - ON_EN_CHANGE(IDC_EDIT132, OnEnChangeEdit132) - ON_BN_CLICKED(IDC_BUTTON4, OnBnClickedButton4) - ON_BN_CLICKED(IDOK, OnBnClickedOk) - ON_BN_CLICKED(IDC_BUTTON5, OnBnClickedButton5) - ON_BN_CLICKED(IDC_BUTTON6, OnBnClickedButton6) + ON_CBN_SELCHANGE(IDC_Kartenanzahl, &CDlgSolitaireAnalyse::OnCbnSelchangeKartenanzahl) + ON_BN_CLICKED(IDC_BUTTON40, &CDlgSolitaireAnalyse::OnBnClickedButton40) + ON_BN_CLICKED(IDC_BUTTON9, &CDlgSolitaireAnalyse::OnBnClickedButton9) + ON_BN_CLICKED(IDC_BUTTON10, &CDlgSolitaireAnalyse::OnBnClickedButton10) + ON_BN_CLICKED(IDC_BUTTON11, &CDlgSolitaireAnalyse::OnBnClickedButton11) + ON_BN_CLICKED(IDC_BUTTON12, &CDlgSolitaireAnalyse::OnBnClickedButton12) + ON_BN_CLICKED(IDC_BUTTON13, &CDlgSolitaireAnalyse::OnBnClickedButton13) + ON_BN_CLICKED(IDC_BUTTON14, &CDlgSolitaireAnalyse::OnBnClickedButton14) + ON_BN_CLICKED(IDC_BUTTON15, &CDlgSolitaireAnalyse::OnBnClickedButton15) + ON_BN_CLICKED(IDC_BUTTON16, &CDlgSolitaireAnalyse::OnBnClickedButton16) + ON_BN_CLICKED(IDC_BUTTON17, &CDlgSolitaireAnalyse::OnBnClickedButton17) + ON_BN_CLICKED(IDC_BUTTON18, &CDlgSolitaireAnalyse::OnBnClickedButton18) + ON_BN_CLICKED(IDC_BUTTON19, &CDlgSolitaireAnalyse::OnBnClickedButton19) + ON_BN_CLICKED(IDC_BUTTON20, &CDlgSolitaireAnalyse::OnBnClickedButton20) + ON_BN_CLICKED(IDC_BUTTON21, &CDlgSolitaireAnalyse::OnBnClickedButton21) + ON_BN_CLICKED(IDC_BUTTON22, &CDlgSolitaireAnalyse::OnBnClickedButton22) + ON_BN_CLICKED(IDC_BUTTON23, &CDlgSolitaireAnalyse::OnBnClickedButton23) + ON_BN_CLICKED(IDC_BUTTON24, &CDlgSolitaireAnalyse::OnBnClickedButton24) + ON_BN_CLICKED(IDC_BUTTON25, &CDlgSolitaireAnalyse::OnBnClickedButton25) + ON_BN_CLICKED(IDC_BUTTON26, &CDlgSolitaireAnalyse::OnBnClickedButton26) + ON_BN_CLICKED(IDC_BUTTON27, &CDlgSolitaireAnalyse::OnBnClickedButton27) + ON_BN_CLICKED(IDC_BUTTON28, &CDlgSolitaireAnalyse::OnBnClickedButton28) + ON_BN_CLICKED(IDC_BUTTON29, &CDlgSolitaireAnalyse::OnBnClickedButton29) + ON_BN_CLICKED(IDC_BUTTON30, &CDlgSolitaireAnalyse::OnBnClickedButton30) + ON_BN_CLICKED(IDC_BUTTON31, &CDlgSolitaireAnalyse::OnBnClickedButton31) + ON_BN_CLICKED(IDC_BUTTON32, &CDlgSolitaireAnalyse::OnBnClickedButton32) + ON_BN_CLICKED(IDC_BUTTON33, &CDlgSolitaireAnalyse::OnBnClickedButton33) + ON_BN_CLICKED(IDC_BUTTON34, &CDlgSolitaireAnalyse::OnBnClickedButton34) + ON_BN_CLICKED(IDC_BUTTON35, &CDlgSolitaireAnalyse::OnBnClickedButton35) + ON_BN_CLICKED(IDC_BUTTON36, &CDlgSolitaireAnalyse::OnBnClickedButton36) + ON_BN_CLICKED(IDC_BUTTON37, &CDlgSolitaireAnalyse::OnBnClickedButton37) + ON_BN_CLICKED(IDC_BUTTON38, &CDlgSolitaireAnalyse::OnBnClickedButton38) + ON_BN_CLICKED(IDC_BUTTON39, &CDlgSolitaireAnalyse::OnBnClickedButton39) + ON_BN_CLICKED(IDC_BUTTON41, &CDlgSolitaireAnalyse::OnBnClickedButton41) + ON_BN_CLICKED(IDC_BUTTON42, &CDlgSolitaireAnalyse::OnBnClickedButton42) + ON_BN_CLICKED(IDC_BUTTON43, &CDlgSolitaireAnalyse::OnBnClickedButton43) + ON_BN_CLICKED(IDC_BUTTON44, &CDlgSolitaireAnalyse::OnBnClickedButton44) + ON_BN_CLICKED(IDC_BUTTON45, &CDlgSolitaireAnalyse::OnBnClickedButton45) + ON_BN_CLICKED(IDC_BUTTON46, &CDlgSolitaireAnalyse::OnBnClickedButton46) + ON_BN_CLICKED(IDC_BUTTON47, &CDlgSolitaireAnalyse::OnBnClickedButton47) + ON_BN_CLICKED(IDC_BUTTON48, &CDlgSolitaireAnalyse::OnBnClickedButton48) + ON_BN_CLICKED(IDC_BUTTON49, &CDlgSolitaireAnalyse::OnBnClickedButton49) + ON_BN_CLICKED(IDC_BUTTON50, &CDlgSolitaireAnalyse::OnBnClickedButton50) + ON_BN_CLICKED(IDC_BUTTON51, &CDlgSolitaireAnalyse::OnBnClickedButton51) + ON_BN_CLICKED(IDC_BUTTON52, &CDlgSolitaireAnalyse::OnBnClickedButton52) + ON_BN_CLICKED(IDC_BUTTON53, &CDlgSolitaireAnalyse::OnBnClickedButton53) + ON_BN_CLICKED(IDC_BUTTON54, &CDlgSolitaireAnalyse::OnBnClickedButton54) + ON_BN_CLICKED(IDC_BUTTON55, &CDlgSolitaireAnalyse::OnBnClickedButton55) + ON_BN_CLICKED(IDC_BUTTON56, &CDlgSolitaireAnalyse::OnBnClickedButton56) + ON_BN_CLICKED(IDC_BUTTON57, &CDlgSolitaireAnalyse::OnBnClickedButton57) + ON_BN_CLICKED(IDC_BUTTON58, &CDlgSolitaireAnalyse::OnBnClickedButton58) + ON_BN_CLICKED(IDC_BUTTON59, &CDlgSolitaireAnalyse::OnBnClickedButton59) + ON_BN_CLICKED(IDC_BUTTON60, &CDlgSolitaireAnalyse::OnBnClickedButton60) + ON_BN_CLICKED(IDC_BUTTON61, &CDlgSolitaireAnalyse::OnBnClickedButton61) + ON_BN_CLICKED(IDC_BUTTON62, &CDlgSolitaireAnalyse::OnBnClickedButton62) + ON_BN_CLICKED(IDC_BUTTON63, &CDlgSolitaireAnalyse::OnBnClickedButton63) + ON_BN_CLICKED(IDC_BUTTON1, &CDlgSolitaireAnalyse::OnBnClickedButton1) + ON_BN_CLICKED(IDC_BUTTON2, &CDlgSolitaireAnalyse::OnBnClickedButton2) + ON_BN_CLICKED(IDC_BUTTON3, &CDlgSolitaireAnalyse::OnBnClickedButton3) + ON_EN_CHANGE(IDC_EDIT132, &CDlgSolitaireAnalyse::OnEnChangeEdit132) + ON_BN_CLICKED(IDC_BUTTON4, &CDlgSolitaireAnalyse::OnBnClickedButton4) + ON_BN_CLICKED(IDOK, &CDlgSolitaireAnalyse::OnBnClickedOk) + ON_BN_CLICKED(IDC_BUTTON5, &CDlgSolitaireAnalyse::OnBnClickedButton5) + ON_BN_CLICKED(IDC_BUTTON6, &CDlgSolitaireAnalyse::OnBnClickedButton6) END_MESSAGE_MAP() diff --git a/CrypTool/DlgStatRandTests.cpp b/CrypTool/DlgStatRandTests.cpp index d97ea6ada..d8b428911 100644 --- a/CrypTool/DlgStatRandTests.cpp +++ b/CrypTool/DlgStatRandTests.cpp @@ -28,7 +28,7 @@ #include "RandomTest.h" #include "ParseIniFile2.h" #include "DialogeMessage.h" -#include ".\dlgstatrandtests.h" +#include "DlgStatRandTests.h" #ifdef _DEBUG #define new DEBUG_NEW @@ -150,13 +150,13 @@ void CDlgFreqTest::init (const char *Infile_, const char *OldTitle_) BEGIN_MESSAGE_MAP(CDlgFreqTest, CDialog) //{{AFX_MSG_MAP(CDlgFreqTest) - ON_BN_CLICKED(IDC_BUTTON_FREQTEST, OnButtonFreqtest) - ON_CBN_SELCHANGE(IDC_COMBO_ALPHA, OnSelchangeComboAlpha) - ON_BN_CLICKED(IDC_CHECK1, OnCheck1) - ON_BN_CLICKED(IDC_CHECK2, OnCheck2) - ON_CBN_SELCHANGE(IDC_TUPELCOMBO, OnSelchangeTupelcombo) + ON_BN_CLICKED(IDC_BUTTON_FREQTEST, &CDlgFreqTest::OnButtonFreqtest) + ON_CBN_SELCHANGE(IDC_COMBO_ALPHA, &CDlgFreqTest::OnSelchangeComboAlpha) + ON_BN_CLICKED(IDC_CHECK1, &CDlgFreqTest::OnCheck1) + ON_BN_CLICKED(IDC_CHECK2, &CDlgFreqTest::OnCheck2) + ON_CBN_SELCHANGE(IDC_TUPELCOMBO, &CDlgFreqTest::OnSelchangeTupelcombo) //}}AFX_MSG_MAP - ON_STN_CLICKED(IDC_INFO_STATIC, OnStnClickedInfoStatic) + ON_STN_CLICKED(IDC_INFO_STATIC, &CDlgFreqTest::OnStnClickedInfoStatic) END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// @@ -405,11 +405,11 @@ void CDlgRunsTest::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgRunsTest, CDialog) //{{AFX_MSG_MAP(CDlgRunsTest) - ON_BN_CLICKED(IDC_TESTBUTTON, OnTestbutton) - ON_CBN_SELCHANGE(IDC_COMBO1, OnSelchangeCombo1) - ON_BN_CLICKED(IDC_CHECK2, OnCheck2) - ON_BN_CLICKED(IDC_CHECK3, OnCheck3) - ON_BN_CLICKED(IDC_CHECK1, OnCheck1) + ON_BN_CLICKED(IDC_TESTBUTTON, &CDlgRunsTest::OnTestbutton) + ON_CBN_SELCHANGE(IDC_COMBO1, &CDlgRunsTest::OnSelchangeCombo1) + ON_BN_CLICKED(IDC_CHECK2, &CDlgRunsTest::OnCheck2) + ON_BN_CLICKED(IDC_CHECK3, &CDlgRunsTest::OnCheck3) + ON_BN_CLICKED(IDC_CHECK1, &CDlgRunsTest::OnCheck1) //}}AFX_MSG_MAP END_MESSAGE_MAP() @@ -679,7 +679,7 @@ void CDlgFIPSTest140_1::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgFIPSTest140_1, CDialog) //{{AFX_MSG_MAP(CDlgFIPSTest140_1) - ON_BN_CLICKED(IDC_BATTERIETEST, OnBatterietest) + ON_BN_CLICKED(IDC_BATTERIETEST, &CDlgFIPSTest140_1::OnBatterietest) //}}AFX_MSG_MAP END_MESSAGE_MAP() diff --git a/CrypTool/DlgStatisticsSignatureAttack.cpp b/CrypTool/DlgStatisticsSignatureAttack.cpp index 5004385d8..bc6c843dc 100644 --- a/CrypTool/DlgStatisticsSignatureAttack.cpp +++ b/CrypTool/DlgStatisticsSignatureAttack.cpp @@ -70,7 +70,7 @@ void CDlgStatisticsSignatureAttack::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgStatisticsSignatureAttack, CDialog) //{{AFX_MSG_MAP(CDlgStatisticsSignatureAttack) - ON_BN_CLICKED(IDOK, OnPrintStatistics) + ON_BN_CLICKED(IDOK, &CDlgStatisticsSignatureAttack::OnPrintStatistics) //}}AFX_MSG_MAP END_MESSAGE_MAP() diff --git a/CrypTool/DlgTextOptions.cpp b/CrypTool/DlgTextOptions.cpp index 5279919e6..240b63ba9 100644 --- a/CrypTool/DlgTextOptions.cpp +++ b/CrypTool/DlgTextOptions.cpp @@ -176,21 +176,21 @@ void CDlgTextOptions::DoDataExchange(CDataExchange* pDX) { } BEGIN_MESSAGE_MAP(CDlgTextOptions, CDialog) - ON_BN_CLICKED(IDC_CHECK_KEEP_CHARACTERS_NOT_PRESENT_IN_ALPHABET_UNCHANGED, OnCheckKeepCharactersNotPresentInAlphabetUnchanged) - ON_BN_CLICKED(IDC_CHECK_KEEP_UPPER_LOWER_CASE_INFORMATION, OnCheckKeepUpperLowerCaseInformation) - ON_BN_CLICKED(IDC_CHECK_DISTINGUISH_UPPER_LOWER_CASE, OnCheckDistinguishUpperLowerCase) - ON_BN_CLICKED(IDC_CHECK_UPPER_CASE, OnCheckUpperCase) - ON_BN_CLICKED(IDC_CHECK_LOWER_CASE, OnCheckLowerCase) - ON_BN_CLICKED(IDC_CHECK_SPACE, OnCheckSpace) - ON_BN_CLICKED(IDC_CHECK_PUNCTUATION, OnCheckSpecial) - ON_BN_CLICKED(IDC_CHECK_DIGITS, OnCheckDigits) - ON_BN_CLICKED(IDC_CHECK_UMLAUTS, OnCheckUmlauts) - ON_EN_UPDATE(IDC_EDIT_ALPHABET, OnUpdateEditAlphabet) - ON_BN_CLICKED(IDC_BUTTON_SEARCH_REFERENCE_FILE, OnButtonSearchReferenceFile) - ON_CBN_SELENDOK(IDC_COMBO_SELECT_REFERENCE_FILE, OnComboSelectReferenceFile) - ON_BN_CLICKED(IDOK, OnButtonOK) - ON_BN_CLICKED(IDC_BUTTON_RESTORE_STANDARD, OnButtonRestoreStandard) - ON_BN_CLICKED(IDCANCEL, OnButtonCancel) + ON_BN_CLICKED(IDC_CHECK_KEEP_CHARACTERS_NOT_PRESENT_IN_ALPHABET_UNCHANGED, &CDlgTextOptions::OnCheckKeepCharactersNotPresentInAlphabetUnchanged) + ON_BN_CLICKED(IDC_CHECK_KEEP_UPPER_LOWER_CASE_INFORMATION, &CDlgTextOptions::OnCheckKeepUpperLowerCaseInformation) + ON_BN_CLICKED(IDC_CHECK_DISTINGUISH_UPPER_LOWER_CASE, &CDlgTextOptions::OnCheckDistinguishUpperLowerCase) + ON_BN_CLICKED(IDC_CHECK_UPPER_CASE, &CDlgTextOptions::OnCheckUpperCase) + ON_BN_CLICKED(IDC_CHECK_LOWER_CASE, &CDlgTextOptions::OnCheckLowerCase) + ON_BN_CLICKED(IDC_CHECK_SPACE, &CDlgTextOptions::OnCheckSpace) + ON_BN_CLICKED(IDC_CHECK_PUNCTUATION, &CDlgTextOptions::OnCheckSpecial) + ON_BN_CLICKED(IDC_CHECK_DIGITS, &CDlgTextOptions::OnCheckDigits) + ON_BN_CLICKED(IDC_CHECK_UMLAUTS, &CDlgTextOptions::OnCheckUmlauts) + ON_EN_UPDATE(IDC_EDIT_ALPHABET, &CDlgTextOptions::OnUpdateEditAlphabet) + ON_BN_CLICKED(IDC_BUTTON_SEARCH_REFERENCE_FILE, &CDlgTextOptions::OnButtonSearchReferenceFile) + ON_CBN_SELENDOK(IDC_COMBO_SELECT_REFERENCE_FILE, &CDlgTextOptions::OnComboSelectReferenceFile) + ON_BN_CLICKED(IDOK, &CDlgTextOptions::OnButtonOK) + ON_BN_CLICKED(IDC_BUTTON_RESTORE_STANDARD, &CDlgTextOptions::OnButtonRestoreStandard) + ON_BN_CLICKED(IDCANCEL, &CDlgTextOptions::OnButtonCancel) END_MESSAGE_MAP() void CDlgTextOptions::OnCheckKeepCharactersNotPresentInAlphabetUnchanged() { diff --git a/CrypTool/DlgVerifyECSignatureStepByStep.cpp b/CrypTool/DlgVerifyECSignatureStepByStep.cpp index d67f51615..c4c8905e5 100644 --- a/CrypTool/DlgVerifyECSignatureStepByStep.cpp +++ b/CrypTool/DlgVerifyECSignatureStepByStep.cpp @@ -135,11 +135,11 @@ void CDlgVerifyECSignatureStepByStep::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgVerifyECSignatureStepByStep, CDialog) //{{AFX_MSG_MAP(CDlgVerifyECSignatureStepByStep) - ON_BN_CLICKED(IDC_BUTTON_CONTINUE, OnButtonContinue) - ON_BN_CLICKED(IDC_RADIO1, OnRadioOktal) - ON_BN_CLICKED(IDC_RADIO2, OnRadioDezimal) - ON_BN_CLICKED(IDC_RADIO3, OnRadioHexadezimal) - ON_BN_CLICKED(IDC_RADIO4, OnRadioOctets) + ON_BN_CLICKED(IDC_BUTTON_CONTINUE, &CDlgVerifyECSignatureStepByStep::OnButtonContinue) + ON_BN_CLICKED(IDC_RADIO1, &CDlgVerifyECSignatureStepByStep::OnRadioOktal) + ON_BN_CLICKED(IDC_RADIO2, &CDlgVerifyECSignatureStepByStep::OnRadioDezimal) + ON_BN_CLICKED(IDC_RADIO3, &CDlgVerifyECSignatureStepByStep::OnRadioHexadezimal) + ON_BN_CLICKED(IDC_RADIO4, &CDlgVerifyECSignatureStepByStep::OnRadioOctets) //}}AFX_MSG_MAP END_MESSAGE_MAP() diff --git a/CrypTool/DlgVerifySignature.cpp b/CrypTool/DlgVerifySignature.cpp index 1a8e66902..984a11634 100644 --- a/CrypTool/DlgVerifySignature.cpp +++ b/CrypTool/DlgVerifySignature.cpp @@ -231,20 +231,20 @@ void CDlgVerifySignature::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CDlgVerifySignature, CDialog) //{{AFX_MSG_MAP(CDlgVerifySignature) - ON_NOTIFY(NM_CLICK, IDC_LIST2, OnClickList2) - ON_NOTIFY(HDN_ITEMCLICK, IDC_LIST2, OnItemclickList2) - ON_NOTIFY(LVN_COLUMNCLICK, IDC_LIST2, OnColumnclickList2) - ON_BN_CLICKED(IDC_CHECK_DSA_KEYS, OnCheckDsaKey) - ON_BN_CLICKED(IDC_CHECK_EC_KEYS, OnCheckEcKey) - ON_BN_CLICKED(IDC_CHECK_RSA_KEYS, OnCheckRsaKey) - ON_NOTIFY(LVN_KEYDOWN, IDC_LIST2, OnKeydownList2) - ON_BN_CLICKED(IDC_BUTTON1, OnButtonSearchKey) - ON_BN_CLICKED(IDC_RADIO5, OnRadioECDSA) - ON_BN_CLICKED(IDC_RADIO6, OnRadioECNR) - ON_BN_CLICKED(IDC_RADIO7, OnRadioSHA1) - ON_BN_CLICKED(IDC_RADIO8, OnRadioRIPEMD) - ON_BN_CLICKED(IDC_RADIO1, OnRadioAffineCoord) - ON_BN_CLICKED(IDC_RADIO2, OnRadioProjectCoord) + ON_NOTIFY(NM_CLICK, IDC_LIST2, &CDlgVerifySignature::OnClickList2) + ON_NOTIFY(HDN_ITEMCLICK, IDC_LIST2, &CDlgVerifySignature::OnItemclickList2) + ON_NOTIFY(LVN_COLUMNCLICK, IDC_LIST2, &CDlgVerifySignature::OnColumnclickList2) + ON_BN_CLICKED(IDC_CHECK_DSA_KEYS, &CDlgVerifySignature::OnCheckDsaKey) + ON_BN_CLICKED(IDC_CHECK_EC_KEYS, &CDlgVerifySignature::OnCheckEcKey) + ON_BN_CLICKED(IDC_CHECK_RSA_KEYS, &CDlgVerifySignature::OnCheckRsaKey) + ON_NOTIFY(LVN_KEYDOWN, IDC_LIST2, &CDlgVerifySignature::OnKeydownList2) + ON_BN_CLICKED(IDC_BUTTON1, &CDlgVerifySignature::OnButtonSearchKey) + ON_BN_CLICKED(IDC_RADIO5, &CDlgVerifySignature::OnRadioECDSA) + ON_BN_CLICKED(IDC_RADIO6, &CDlgVerifySignature::OnRadioECNR) + ON_BN_CLICKED(IDC_RADIO7, &CDlgVerifySignature::OnRadioSHA1) + ON_BN_CLICKED(IDC_RADIO8, &CDlgVerifySignature::OnRadioRIPEMD) + ON_BN_CLICKED(IDC_RADIO1, &CDlgVerifySignature::OnRadioAffineCoord) + ON_BN_CLICKED(IDC_RADIO2, &CDlgVerifySignature::OnRadioProjectCoord) //}}AFX_MSG_MAP END_MESSAGE_MAP() diff --git a/CrypTool/DlgVerifySignature.h b/CrypTool/DlgVerifySignature.h index 167b9e79b..acd4b40d8 100644 --- a/CrypTool/DlgVerifySignature.h +++ b/CrypTool/DlgVerifySignature.h @@ -43,7 +43,7 @@ class CDlgVerifySignature : public CDialog public: CDlgVerifySignature(CWnd* pParent = NULL); // Standardkonstruktor CDlgVerifySignature(char* sig_id, char* hash_id, CString sig_key, CWnd* pParent = NULL); - CDlgVerifySignature::~CDlgVerifySignature(); // Destruktor + ~CDlgVerifySignature(); // Destruktor EcDomParam_ac_ptr DomParamAc; // The Domain Parameter (affine coordinates) of the selected elliptic Curve EcDomParam_pc_ptr DomParamPc; // The Domain Parameter (projective coordinates) of the selected elliptic Curve diff --git a/CrypTool/DlgVigenereAnalysisSchroedel.cpp b/CrypTool/DlgVigenereAnalysisSchroedel.cpp index 25d074ef2..b9ff9f60d 100644 --- a/CrypTool/DlgVigenereAnalysisSchroedel.cpp +++ b/CrypTool/DlgVigenereAnalysisSchroedel.cpp @@ -22,7 +22,7 @@ // #include "stdafx.h" -#include "CryptoolApp.h" +#include "CrypToolApp.h" #include "DlgVigenereAnalysisSchroedel.h" #include "CrypToolTools.h" #include "FileTools.h" @@ -1475,9 +1475,9 @@ void CDlgVigenereAnalysisSchroedel::DoDataExchange(CDataExchange* pDX) } BEGIN_MESSAGE_MAP(CDlgVigenereAnalysisSchroedel, CDialog) - ON_BN_CLICKED(IDC_BUTTON_START_ANALYSIS, OnBnClickedStartAnalysis) - ON_BN_CLICKED(IDC_BUTTON_CANCEL_ANALYSIS, OnBnClickedCancelAnalysis) - ON_BN_CLICKED(IDC_BUTTON_SHOW_ANALYSIS_RESULTS, OnBnClickedShowAnalysisResults) + ON_BN_CLICKED(IDC_BUTTON_START_ANALYSIS, &CDlgVigenereAnalysisSchroedel::OnBnClickedStartAnalysis) + ON_BN_CLICKED(IDC_BUTTON_CANCEL_ANALYSIS, &CDlgVigenereAnalysisSchroedel::OnBnClickedCancelAnalysis) + ON_BN_CLICKED(IDC_BUTTON_SHOW_ANALYSIS_RESULTS, &CDlgVigenereAnalysisSchroedel::OnBnClickedShowAnalysisResults) ON_WM_TIMER() END_MESSAGE_MAP() @@ -1669,7 +1669,7 @@ void CDlgVigenereAnalysisSchroedelChooseLanguages::DoDataExchange(CDataExchange* } BEGIN_MESSAGE_MAP(CDlgVigenereAnalysisSchroedelChooseLanguages, CDialog) - ON_BN_CLICKED(IDOK, OnOK) + ON_BN_CLICKED(IDOK, &CDlgVigenereAnalysisSchroedelChooseLanguages::OnOK) END_MESSAGE_MAP() BOOL CDlgVigenereAnalysisSchroedelChooseLanguages::OnInitDialog() diff --git a/CrypTool/ELLIPTIC.CPP b/CrypTool/ELLIPTIC.CPP index e2fae843e..8d3702b2e 100644 --- a/CrypTool/ELLIPTIC.CPP +++ b/CrypTool/ELLIPTIC.CPP @@ -37,7 +37,7 @@ #include using namespace std; #endif -#include "elliptic.h" +#include "ELLIPTIC.H" int ECn::get(Big& x,Big& y) const {return epoint_get(p,x.getbig(),y.getbig());} diff --git a/CrypTool/FLASH.CPP b/CrypTool/FLASH.CPP index f861c128b..44068ff07 100644 --- a/CrypTool/FLASH.CPP +++ b/CrypTool/FLASH.CPP @@ -36,7 +36,7 @@ #include using namespace std; #endif -#include "flash.h" +#include "FLASH.H" #ifdef MR_FLASH @@ -52,7 +52,7 @@ return b;} Big Flash::num(void) {Big b; numer(fn,b.fn); return b;} Big Flash::den(void) {Big b; denom(fn,b.fn); return b;} -Flash operator-(const Flash& f) {Flash nf; negate(f.fn,nf.fn); return nf;} +Flash operator-(const Flash& f) {Flash nf; ::negate(f.fn,nf.fn); return nf;} Flash operator+(const Flash& f1, const Flash& f2) {Flash aff; fadd(f1.fn,f2.fn,aff.fn); return aff;} diff --git a/CrypTool/FLASH.H b/CrypTool/FLASH.H index b93ad8302..f53cca903 100644 --- a/CrypTool/FLASH.H +++ b/CrypTool/FLASH.H @@ -159,5 +159,7 @@ public: }; #endif +Flash pi(); + #endif diff --git a/CrypTool/FPOLY.CPP b/CrypTool/FPOLY.CPP index 6215a2aea..d57e17d27 100644 --- a/CrypTool/FPOLY.CPP +++ b/CrypTool/FPOLY.CPP @@ -36,7 +36,7 @@ #include using namespace std; #endif -#include "fpoly.h" +#include "FPOLY.H" FPoly::FPoly(const FPoly& p) diff --git a/CrypTool/FPOLY.H b/CrypTool/FPOLY.H index 33acef706..51bd337a2 100644 --- a/CrypTool/FPOLY.H +++ b/CrypTool/FPOLY.H @@ -33,7 +33,7 @@ #ifndef FPOLY_H #define FPOLY_H -#include "flash.h" +#include "FLASH.H" class fterm { diff --git a/CrypTool/HashingOperations.h b/CrypTool/HashingOperations.h index 116ea8305..45a833a84 100644 --- a/CrypTool/HashingOperations.h +++ b/CrypTool/HashingOperations.h @@ -48,7 +48,7 @@ struct HashAlgorithmsFP int HashOpsPerSecond; // ausgehend von einem Rechner mit ???-Prozessor und ??? MHz Taktfrequenz }; -struct HashAlgorithmsFP HAFP[]; +extern struct HashAlgorithmsFP HAFP[]; class HashingOperations { diff --git a/CrypTool/HexEdit.cpp b/CrypTool/HexEdit.cpp index 12e72a2d9..15ca7b08c 100644 --- a/CrypTool/HexEdit.cpp +++ b/CrypTool/HexEdit.cpp @@ -56,8 +56,8 @@ BEGIN_MESSAGE_MAP(CHexEdit, CEdit) //{{AFX_MSG_MAP(CHexEdit) ON_WM_CHAR() ON_WM_KEYDOWN() - ON_MESSAGE(WM_PASTE, OnPaste) - ON_CONTROL_REFLECT_EX(EN_UPDATE, OnUpdate) + ON_MESSAGE(WM_PASTE, &CHexEdit::OnPaste) + ON_CONTROL_REFLECT_EX(EN_UPDATE, &CHexEdit::OnUpdate) //}}AFX_MSG_MAP END_MESSAGE_MAP() diff --git a/CrypTool/HexEditBase.cpp b/CrypTool/HexEditBase.cpp index 62b4d8a76..35886c140 100644 --- a/CrypTool/HexEditBase.cpp +++ b/CrypTool/HexEditBase.cpp @@ -180,10 +180,10 @@ IMPLEMENT_DYNCREATE(CHexEditBase, CWnd) BEGIN_MESSAGE_MAP(CHexEditBase, CWnd) //{{AFX_MSG_MAP(CHexEditBase) - ON_MESSAGE(UM_SETSCROLRANGE, OnUmSetScrollRange) - ON_MESSAGE(WM_CHAR, OnWMChar) - ON_MESSAGE(WM_SETFONT, OnWMSetFont) - ON_MESSAGE(WM_GETFONT, OnWMGetFont) + ON_MESSAGE(UM_SETSCROLRANGE, &CHexEditBase::OnUmSetScrollRange) + ON_MESSAGE(WM_CHAR, &CHexEditBase::OnWMChar) + ON_MESSAGE(WM_SETFONT, &CHexEditBase::OnWMSetFont) + ON_MESSAGE(WM_GETFONT, &CHexEditBase::OnWMGetFont) ON_WM_DESTROY() ON_WM_TIMER() ON_WM_KILLFOCUS() @@ -201,12 +201,12 @@ BEGIN_MESSAGE_MAP(CHexEditBase, CWnd) ON_WM_KEYDOWN() ON_WM_MOUSEWHEEL() ON_WM_CONTEXTMENU() - ON_COMMAND(ID_EDIT_CUT, OnEditCut) - ON_COMMAND(ID_EDIT_COPY, OnEditCopy) - ON_COMMAND(ID_EDIT_PASTE, OnEditPaste) - ON_COMMAND(ID_EDIT_CLEAR, OnEditClear) - ON_COMMAND(ID_EDIT_SELECT_ALL, OnEditSelectAll) - ON_COMMAND(ID_TOTXT, OnToTxt) + ON_COMMAND(ID_EDIT_CUT, &CHexEditBase::OnEditCut) + ON_COMMAND(ID_EDIT_COPY, &CHexEditBase::OnEditCopy) + ON_COMMAND(ID_EDIT_PASTE, &CHexEditBase::OnEditPaste) + ON_COMMAND(ID_EDIT_CLEAR, &CHexEditBase::OnEditClear) + ON_COMMAND(ID_EDIT_SELECT_ALL, &CHexEditBase::OnEditSelectAll) + ON_COMMAND(ID_TOTXT, &CHexEditBase::OnToTxt) //}}AFX_MSG_MAP END_MESSAGE_MAP() diff --git a/CrypTool/HexEditCtrlDoc.cpp b/CrypTool/HexEditCtrlDoc.cpp index 8fb3f7ec0..3d9da06dd 100644 --- a/CrypTool/HexEditCtrlDoc.cpp +++ b/CrypTool/HexEditCtrlDoc.cpp @@ -57,7 +57,7 @@ IMPLEMENT_DYNCREATE(CHexEditCtrlDoc, CCryptDoc) BEGIN_MESSAGE_MAP(CHexEditCtrlDoc, CCryptDoc) //{{AFX_MSG_MAP(CHexEditCtrlDoc) - ON_COMMAND(ID_TOTXT, OnTotxt) + ON_COMMAND(ID_TOTXT, &CHexEditCtrlDoc::OnTotxt) //}}AFX_MSG_MAP END_MESSAGE_MAP() diff --git a/CrypTool/HexEditCtrlView.cpp b/CrypTool/HexEditCtrlView.cpp index d85e834c2..af67a4066 100644 --- a/CrypTool/HexEditCtrlView.cpp +++ b/CrypTool/HexEditCtrlView.cpp @@ -34,10 +34,10 @@ #include "CrypToolApp.h" #include "CryptDoc.h" #include "FileTools.h" -#include "cryptography.h" +#include "Cryptography.h" #include "HexEditCtrlDoc.h" #include "HexEditCtrlView.h" -#include ".\hexeditctrlview.h" +#include "HexEditCtrlView.h" ///////////////////////////////////////////////////////////////////////////// @@ -60,23 +60,23 @@ IMPLEMENT_DYNCREATE(CHexEditCtrlView, CHexEditBaseView) BEGIN_MESSAGE_MAP(CHexEditCtrlView, CHexEditBaseView) //{{AFX_MSG_MAP(CHexEditCtrlView) - ON_COMMAND(ID_EDIT_CUT, OnEditCut) - ON_COMMAND(ID_EDIT_COPY, OnEditCopy) - ON_COMMAND(ID_EDIT_PASTE, OnEditPaste) - ON_COMMAND(ID_EDIT_CLEAR, OnEditClear) - ON_COMMAND(ID_EDIT_SELECT_ALL, OnEditSelectAll) - ON_COMMAND(ID_GOTO_VATER, OnGotoVater) - ON_UPDATE_COMMAND_UI(ID_INDICATOR_LINE, OnUpdateLine) - ON_UPDATE_COMMAND_UI(ID_INDICATOR_OVR, OnUpdateInsert) - ON_COMMAND(ID_EDIT_UNDO, OnEditUndo) - ON_UPDATE_COMMAND_UI(ID_EDIT_UNDO, OnUpdateEditUndo) - ON_COMMAND(ID_EDIT_REDO, OnEditRedo) - ON_UPDATE_COMMAND_UI(ID_EDIT_REDO, OnUpdateEditRedo) + ON_COMMAND(ID_EDIT_CUT, &CHexEditCtrlView::OnEditCut) + ON_COMMAND(ID_EDIT_COPY, &CHexEditCtrlView::OnEditCopy) + ON_COMMAND(ID_EDIT_PASTE, &CHexEditCtrlView::OnEditPaste) + ON_COMMAND(ID_EDIT_CLEAR, &CHexEditCtrlView::OnEditClear) + ON_COMMAND(ID_EDIT_SELECT_ALL, &CHexEditCtrlView::OnEditSelectAll) + ON_COMMAND(ID_GOTO_VATER, &CHexEditCtrlView::OnGotoVater) + ON_UPDATE_COMMAND_UI(ID_INDICATOR_LINE, &CHexEditCtrlView::OnUpdateLine) + ON_UPDATE_COMMAND_UI(ID_INDICATOR_OVR, &CHexEditCtrlView::OnUpdateInsert) + ON_COMMAND(ID_EDIT_UNDO, &CHexEditCtrlView::OnEditUndo) + ON_UPDATE_COMMAND_UI(ID_EDIT_UNDO, &CHexEditCtrlView::OnUpdateEditUndo) + ON_COMMAND(ID_EDIT_REDO, &CHexEditCtrlView::OnEditRedo) + ON_UPDATE_COMMAND_UI(ID_EDIT_REDO, &CHexEditCtrlView::OnUpdateEditRedo) //}}AFX_MSG_MAP - ON_COMMAND(ID_ENTROPY, OnEntropy) - ON_COMMAND(ID_HISTOGRAM, OnHistogram) - ON_COMMAND(ID_TOTXT, OnTotxt) - ON_COMMAND(ID_EDIT_FIND, OnEditFind) + ON_COMMAND(ID_ENTROPY, &CHexEditCtrlView::OnEntropy) + ON_COMMAND(ID_HISTOGRAM, &CHexEditCtrlView::OnHistogram) + ON_COMMAND(ID_TOTXT, &CHexEditCtrlView::OnTotxt) + ON_COMMAND(ID_EDIT_FIND, &CHexEditCtrlView::OnEditFind) END_MESSAGE_MAP() diff --git a/CrypTool/HillAnalysis.cpp b/CrypTool/HillAnalysis.cpp index 207f22f78..fb2d39b82 100644 --- a/CrypTool/HillAnalysis.cpp +++ b/CrypTool/HillAnalysis.cpp @@ -5,7 +5,7 @@ #include "FileTools.h" #include "DialogeMessage.h" #include "DlgTextOptions.h" -#include "keyHillBase.h" +#include "KeyHillBase.h" #include "assert.h" diff --git a/CrypTool/IntegerArithmetic.cpp b/CrypTool/IntegerArithmetic.cpp index d10cdd29a..34a40da7b 100644 --- a/CrypTool/IntegerArithmetic.cpp +++ b/CrypTool/IntegerArithmetic.cpp @@ -27,7 +27,7 @@ #include "resource.h" #include "IntegerArithmetic.h" -#include +#include "MONTY.H" #include "DlgProgressFactorisation.h" #ifdef _DEBUG diff --git a/CrypTool/IntegerArithmetic.h b/CrypTool/IntegerArithmetic.h index ea3f83c5d..ce1969b55 100644 --- a/CrypTool/IntegerArithmetic.h +++ b/CrypTool/IntegerArithmetic.h @@ -37,7 +37,7 @@ extern volatile long ExitFactorisationCode; #include "BIG.H" // Hinzugefügt von der Klassenansicht -#include +#include "MONTY.H" #include "ChrTools.h" diff --git a/CrypTool/KeyRepository.cpp b/CrypTool/KeyRepository.cpp index b9ecf230f..60291dd73 100644 --- a/CrypTool/KeyRepository.cpp +++ b/CrypTool/KeyRepository.cpp @@ -25,7 +25,7 @@ // #include "KeyRepository.h" -#include "Resource.h" +#include "resource.h" ////////////////////////////////////////////////////////////////////////////// KeyData keylist[KEYDATA_TABLE_SIZE]; diff --git a/CrypTool/ListResults.cpp b/CrypTool/ListResults.cpp index f156dab29..ca27d694b 100644 --- a/CrypTool/ListResults.cpp +++ b/CrypTool/ListResults.cpp @@ -24,8 +24,8 @@ #include "stdafx.h" #include "CrypToolApp.h" #include "ListResults.h" -#include ".\listresults.h" -#include "bruteforceheap.h" +#include "ListResults.h" +#include "BruteForceHeap.h" #include "DialogeMessage.h" // CListResults dialog @@ -53,7 +53,7 @@ void CListResults::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CListResults, CDialog) - ON_BN_CLICKED(IDOK, OnBnClickedOk) + ON_BN_CLICKED(IDOK, &CListResults::OnBnClickedOk) ON_WM_SIZE() END_MESSAGE_MAP() diff --git a/CrypTool/MONTY.CPP b/CrypTool/MONTY.CPP index 11acd87e1..4918f560d 100644 --- a/CrypTool/MONTY.CPP +++ b/CrypTool/MONTY.CPP @@ -38,7 +38,7 @@ #include using namespace std; #endif -#include "monty.h" +#include "MONTY.H" BOOL ZZn::iszero() const {return fn.iszero(); } diff --git a/CrypTool/MONTY.H b/CrypTool/MONTY.H index bdaeff117..2a5e0ad04 100644 --- a/CrypTool/MONTY.H +++ b/CrypTool/MONTY.H @@ -104,9 +104,11 @@ public: friend ZZn pow( int,ZZn *,Big *); friend ZZn sqrt(const ZZn&); // only works if modulus is prime - friend ZZn luc( const ZZn&, const Big&, ZZn* b3=NULL); + friend ZZn luc( const ZZn&, const Big&, ZZn* b3); ~ZZn() { } }; +ZZn luc(const ZZn&, const Big&, ZZn* b3=NULL); + #endif diff --git a/CrypTool/MakeZip.cpp b/CrypTool/MakeZip.cpp index 972df2053..cd7d455af 100644 --- a/CrypTool/MakeZip.cpp +++ b/CrypTool/MakeZip.cpp @@ -29,9 +29,9 @@ #include "FileTools.h" #include "Cryptography.h" #include "MakeZip.h" -#include "zip\zipup.h" -#include "zip\zunzip.h" -#include "zip\zip.h" +#include "zip/zipup.h" +#include "zip/zunzip.h" +#include "zip/zip.h" #include "AppDocument.h" const char MagicZipNumber[4] = "CTZ"; diff --git a/CrypTool/NumberEdit.cpp b/CrypTool/NumberEdit.cpp index 086d17885..baeee6dcc 100644 --- a/CrypTool/NumberEdit.cpp +++ b/CrypTool/NumberEdit.cpp @@ -55,10 +55,10 @@ CNumberEdit::~CNumberEdit() { BEGIN_MESSAGE_MAP(CNumberEdit, CEdit) ON_WM_CHAR() ON_WM_KEYDOWN() - ON_COMMAND(ID_EDIT_CUT, onEditCut) - ON_COMMAND(ID_EDIT_COPY, onEditCopy) - ON_COMMAND(ID_EDIT_PASTE, onEditPaste) - ON_COMMAND(ID_EDIT_SELECT_ALL, onEditSelectAll) + ON_COMMAND(ID_EDIT_CUT, &CNumberEdit::onEditCut) + ON_COMMAND(ID_EDIT_COPY, &CNumberEdit::onEditCopy) + ON_COMMAND(ID_EDIT_PASTE, &CNumberEdit::onEditPaste) + ON_COMMAND(ID_EDIT_SELECT_ALL, &CNumberEdit::onEditSelectAll) ON_WM_CONTEXTMENU() END_MESSAGE_MAP() diff --git a/CrypTool/OpenGLDoc.cpp b/CrypTool/OpenGLDoc.cpp index be339c5ce..6ce8c0b97 100644 --- a/CrypTool/OpenGLDoc.cpp +++ b/CrypTool/OpenGLDoc.cpp @@ -22,7 +22,7 @@ // #include "stdafx.h" -#include "cryptoolapp.h" +#include "CrypToolApp.h" #include "OpenGLDoc.h" #include diff --git a/CrypTool/OpenGLDoc.h b/CrypTool/OpenGLDoc.h index bd96b9e49..e42923384 100644 --- a/CrypTool/OpenGLDoc.h +++ b/CrypTool/OpenGLDoc.h @@ -30,7 +30,7 @@ #include "VolumeRenderer.h" #include "VolumeUtilities.h" #include "StreamToVolume.h" -#include "cryptoolapp.h" +#include "CrypToolApp.h" ///////////////////////////////////////////////////////////////////////////// diff --git a/CrypTool/OpenGLView.cpp b/CrypTool/OpenGLView.cpp index 6d3fba602..c25e37dc0 100644 --- a/CrypTool/OpenGLView.cpp +++ b/CrypTool/OpenGLView.cpp @@ -30,7 +30,7 @@ #include "OpenGL.h" #include #include -#include ".\openglview.h" +#include "OpenGLView.h" #ifdef _DEBUG #define new DEBUG_NEW @@ -114,9 +114,9 @@ BEGIN_MESSAGE_MAP(COpenGLView, CView) ON_WM_LBUTTONUP() ON_WM_SIZE() ON_WM_CONTEXTMENU() - ON_COMMAND(ID_POPUP_OPENGL_SHOW_BOX, OnPopupOpenglShowBox) + ON_COMMAND(ID_POPUP_OPENGL_SHOW_BOX, &COpenGLView::OnPopupOpenglShowBox) ON_WM_DESTROY() - ON_COMMAND(ID_POPUP_OPENGL_EIGENSCHAFTEN, OnPopupOpenglEigenschaften) + ON_COMMAND(ID_POPUP_OPENGL_EIGENSCHAFTEN, &COpenGLView::OnPopupOpenglEigenschaften) ON_WM_RBUTTONDOWN() ON_WM_RBUTTONUP() //}}AFX_MSG_MAP diff --git a/CrypTool/OpenGLView.h b/CrypTool/OpenGLView.h index 110a4c03e..e65c025d2 100644 --- a/CrypTool/OpenGLView.h +++ b/CrypTool/OpenGLView.h @@ -29,7 +29,7 @@ #include "CrypToolVolRen.h" #include "OpenGLDoc.h" -#include "cryptoolapp.h" +#include "CrypToolApp.h" // we need those defines for our dynamic context menu #define CM_OPENGL_PROPERTIES 7777 diff --git a/CrypTool/POLY.CPP b/CrypTool/POLY.CPP index b3ffcebd6..1b1455881 100644 --- a/CrypTool/POLY.CPP +++ b/CrypTool/POLY.CPP @@ -31,7 +31,7 @@ * See Knuth The Art of Computer Programming Vol.2, Chapter 4.6 */ -#include "poly.h" +#include "POLY.H" Poly::Poly(const Poly& p) { diff --git a/CrypTool/POLY.H b/CrypTool/POLY.H index 3c44494a7..dbb3eb1ea 100644 --- a/CrypTool/POLY.H +++ b/CrypTool/POLY.H @@ -39,7 +39,7 @@ #include using namespace std; #endif -#include "monty.h" +#include "MONTY.H" #define FFT_BREAK_EVEN 16 diff --git a/CrypTool/POLYMOD.CPP b/CrypTool/POLYMOD.CPP index f7df009f0..d61a839f6 100644 --- a/CrypTool/POLYMOD.CPP +++ b/CrypTool/POLYMOD.CPP @@ -40,7 +40,7 @@ #include using namespace std; #endif -#include "polymod.h" +#include "POLYMOD.H" Poly modulus; diff --git a/CrypTool/POLYMOD.H b/CrypTool/POLYMOD.H index 902250201..f1091a52b 100644 --- a/CrypTool/POLYMOD.H +++ b/CrypTool/POLYMOD.H @@ -42,7 +42,7 @@ #include using namespace std; #endif -#include "poly.h" +#include "POLY.H" extern Poly modulus; @@ -52,7 +52,7 @@ public: Poly p; PolyMod() { } - PolyMod(const Poly& m) {p=m%modulus; } + PolyMod(const Poly& m) {p=m%::modulus; } PolyMod(const PolyMod& m) {p=m.p; } void clear() {p.clear();} term* addterm(const ZZn& z,int i,term *pos=NULL) {return p.addterm(z,i,pos);} @@ -62,8 +62,8 @@ public: PolyMod& operator=(int m) {p=m; return *this;} PolyMod& operator=(const PolyMod& m) {p=m.p; return *this;} PolyMod& operator=(const Poly& m) {reduce(m,*this); return *this;} - PolyMod& operator+=(const PolyMod& m) {p=(p+m.p)%modulus;return *this;} - PolyMod& operator-=(const PolyMod& m) {p=(p-m.p)%modulus;return *this;} + PolyMod& operator+=(const PolyMod& m) {p=(p+m.p)%::modulus;return *this;} + PolyMod& operator-=(const PolyMod& m) {p=(p-m.p)%::modulus;return *this;} PolyMod& operator+=(const ZZn& m) {addterm(m,0); return *this;} PolyMod& operator-=(const ZZn& m) {addterm((-m),0); return *this;} PolyMod& operator*=(const ZZn& z) {p*=z;return *this;} diff --git a/CrypTool/PSEDemo.cpp b/CrypTool/PSEDemo.cpp index b986bcf3f..e2e48905a 100644 --- a/CrypTool/PSEDemo.cpp +++ b/CrypTool/PSEDemo.cpp @@ -28,12 +28,12 @@ #include "PSEDemo.h" #include "IntegerArithmetic.h" #include "DlgKeyAsymGeneration.h" -#include "secudelib.h" +#include "SecudeLib.h" #include "secure.h" // Header-File für das SECUDE-Toolkit #include "af.h" // Header-File für den SECUDE Authentication Framework #include "CrypToolApp.h" #include "Cryptography.h" -#include "secudetools.h" +#include "SecudeTools.h" #include "DlgRSADecryption.h" #include "DialogeMessage.h" diff --git a/CrypTool/PictureEx.h b/CrypTool/PictureEx.h index a8069e646..72940587f 100644 --- a/CrypTool/PictureEx.h +++ b/CrypTool/PictureEx.h @@ -106,6 +106,10 @@ #include +#ifndef _WIN32 +class wxImage; +#endif + //#define GIF_TRACING // uncomment it if you want detailed TRACEs @@ -119,7 +123,11 @@ class CPictureEx : public CStatic struct TFrame // structure that keeps a single frame info { +#ifdef _WIN32 IPicture *m_pPicture; // pointer to the interface used for drawing +#else + wxImage *m_pPicture; +#endif SIZE m_frameSize; SIZE m_frameOffset; UINT m_nDelay; // delay (in 1/100s of a second) @@ -299,7 +307,14 @@ struct TGIFImageDescriptor // image descriptor block HBITMAP m_hOldBitmap; HANDLE m_hThread; HANDLE m_hExitEvent; +#ifdef _WIN32 IPicture * m_pPicture; +#else + // wxWidgets implementation (PictureEx_wx.cpp): images replace IPicture and the memory DCs + wxImage * m_pPicture; + wxImage * m_pCanvas; + wxImage * m_pDispCanvas; +#endif TGIFHeader * m_pGIFHeader; unsigned char * m_pRawData; TGIFLSDescriptor * m_pGIFLSDescriptor; @@ -323,6 +338,9 @@ struct TGIFImageDescriptor // image descriptor block afx_msg void OnDestroy(); afx_msg void OnPaint(); //}}AFX_MSG +#ifndef _WIN32 + afx_msg void OnTimer(UINT_PTR nIDEvent); +#endif DECLARE_MESSAGE_MAP() }; diff --git a/CrypTool/PictureExLoop.h b/CrypTool/PictureExLoop.h index a84fcec55..4e2f824d4 100644 --- a/CrypTool/PictureExLoop.h +++ b/CrypTool/PictureExLoop.h @@ -104,6 +104,10 @@ #include +#ifndef _WIN32 +class wxImage; +#endif + //#define GIF_TRACING // uncomment it if you want detailed TRACEs @@ -117,7 +121,11 @@ class CPictureExLoop : public CStatic struct TFrame // structure that keeps a single frame info { +#ifdef _WIN32 IPicture *m_pPicture; // pointer to the interface used for drawing +#else + wxImage *m_pPicture; +#endif SIZE m_frameSize; SIZE m_frameOffset; UINT m_nDelay; // delay (in 1/100s of a second) @@ -297,7 +305,14 @@ struct TGIFImageDescriptor // image descriptor block HBITMAP m_hOldBitmap; HANDLE m_hThread; HANDLE m_hExitEvent; +#ifdef _WIN32 IPicture * m_pPicture; +#else + // wxWidgets implementation (PictureEx_wx.cpp): images replace IPicture and the memory DCs + wxImage * m_pPicture; + wxImage * m_pCanvas; + wxImage * m_pDispCanvas; +#endif TGIFHeader * m_pGIFHeader; unsigned char * m_pRawData; TGIFLSDescriptor * m_pGIFLSDescriptor; @@ -321,6 +336,9 @@ struct TGIFImageDescriptor // image descriptor block afx_msg void OnDestroy(); afx_msg void OnPaint(); //}}AFX_MSG +#ifndef _WIN32 + afx_msg void OnTimer(UINT_PTR nIDEvent); +#endif DECLARE_MESSAGE_MAP() }; \ No newline at end of file diff --git a/CrypTool/PlayfairEdit.cpp b/CrypTool/PlayfairEdit.cpp index 393dc28b2..82e6c355a 100644 --- a/CrypTool/PlayfairEdit.cpp +++ b/CrypTool/PlayfairEdit.cpp @@ -49,7 +49,7 @@ CPlayfairEdit::~CPlayfairEdit() BEGIN_MESSAGE_MAP(CPlayfairEdit, CEdit) //{{AFX_MSG_MAP(CPlayfairEdit) ON_WM_CHAR() - ON_COMMAND(ID_EDIT_PASTE, OnEditPaste) + ON_COMMAND(ID_EDIT_PASTE, &CPlayfairEdit::OnEditPaste) //}}AFX_MSG_MAP END_MESSAGE_MAP() diff --git a/CrypTool/PlotDocument.cpp b/CrypTool/PlotDocument.cpp index 6b498f3b0..bfea53252 100644 --- a/CrypTool/PlotDocument.cpp +++ b/CrypTool/PlotDocument.cpp @@ -40,10 +40,10 @@ IMPLEMENT_DYNCREATE(CPlotDocument, CAppDocument) BEGIN_MESSAGE_MAP(CPlotDocument, CAppDocument) //{{AFX_MSG_MAP(CPlotDocument) - ON_COMMAND(ID_PLOTBAR, OnPlotbar) - ON_UPDATE_COMMAND_UI(ID_PLOTBAR, OnUpdatePlotbar) - ON_COMMAND(ID_BARWIDTH, OnBarwidth) - ON_UPDATE_COMMAND_UI(ID_GOTO_VATER, OnUpdateGotoVater) + ON_COMMAND(ID_PLOTBAR, &CPlotDocument::OnPlotbar) + ON_UPDATE_COMMAND_UI(ID_PLOTBAR, &CPlotDocument::OnUpdatePlotbar) + ON_COMMAND(ID_BARWIDTH, &CPlotDocument::OnBarwidth) + ON_UPDATE_COMMAND_UI(ID_GOTO_VATER, &CPlotDocument::OnUpdateGotoVater) //}}AFX_MSG_MAP END_MESSAGE_MAP() @@ -426,8 +426,8 @@ void plot_opt_dlg::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(plot_opt_dlg, CDialog) //{{AFX_MSG_MAP(plot_opt_dlg) - ON_EN_KILLFOCUS(IDC_EDIT1, OnChangeEdit1) - ON_EN_KILLFOCUS(IDC_EDIT2, OnChangeEdit2) + ON_EN_KILLFOCUS(IDC_EDIT1, &plot_opt_dlg::OnChangeEdit1) + ON_EN_KILLFOCUS(IDC_EDIT2, &plot_opt_dlg::OnChangeEdit2) //}}AFX_MSG_MAP END_MESSAGE_MAP() diff --git a/CrypTool/PlotView.cpp b/CrypTool/PlotView.cpp index 5473d2714..a8f400234 100644 --- a/CrypTool/PlotView.cpp +++ b/CrypTool/PlotView.cpp @@ -45,7 +45,7 @@ IMPLEMENT_DYNCREATE(CPlotView, CView) BEGIN_MESSAGE_MAP(CPlotView, CView) //{{AFX_MSG_MAP(CPlotView) - ON_COMMAND(ID_GOTO_VATER, OnGotoVater) + ON_COMMAND(ID_GOTO_VATER, &CPlotView::OnGotoVater) ON_WM_CONTEXTMENU() //}}AFX_MSG_MAP // Standard printing commands diff --git a/CrypTool/RSABloemerMayDlg.cpp b/CrypTool/RSABloemerMayDlg.cpp index d99c92262..f9df4181e 100644 --- a/CrypTool/RSABloemerMayDlg.cpp +++ b/CrypTool/RSABloemerMayDlg.cpp @@ -23,7 +23,7 @@ #include "stdafx.h" #include "CrypToolApp.h" -#include "RSABloemerMayDlg.h" +#include "rsabloemermaydlg.h" #include "NTLExpPars.h" #include "FileTools.h" #ifdef _DEBUG @@ -74,24 +74,24 @@ BEGIN_MESSAGE_MAP(CRSABloemerMayDlg, CDialog) //{{AFX_MSG_MAP(CRSABloemerMayDlg) ON_WM_PAINT() ON_WM_QUERYDRAGICON() - ON_BN_CLICKED(IDC_BUTTONSTART, OnStart) - ON_BN_CLICKED(IDC_RANDOM, OnRandom) + ON_BN_CLICKED(IDC_BUTTONSTART, &CRSABloemerMayDlg::OnStart) + ON_BN_CLICKED(IDC_RANDOM, &CRSABloemerMayDlg::OnRandom) ON_WM_TIMER() - ON_BN_CLICKED(IDC_CANCELATTACK, OnCancelattack) - ON_EN_CHANGE(IDC_EDITM, OnChangeEditm) - ON_EN_KILLFOCUS(IDC_EDITDELTA, OnKillfocusEditdelta) - ON_EN_CHANGE(IDC_EDITN, OnChangeEditn) - ON_EN_CHANGE(IDC_EDITE, OnChangeEdite) + ON_BN_CLICKED(IDC_CANCELATTACK, &CRSABloemerMayDlg::OnCancelattack) + ON_EN_CHANGE(IDC_EDITM, &CRSABloemerMayDlg::OnChangeEditm) + ON_EN_KILLFOCUS(IDC_EDITDELTA, &CRSABloemerMayDlg::OnKillfocusEditdelta) + ON_EN_CHANGE(IDC_EDITN, &CRSABloemerMayDlg::OnChangeEditn) + ON_EN_CHANGE(IDC_EDITE, &CRSABloemerMayDlg::OnChangeEdite) ON_WM_SETCURSOR() - ON_BN_CLICKED(IDC_BUTTONDEFAULT, OnButtondefault) - ON_BN_CLICKED(IDC_BUTTONLOG, OnButtonlog) - ON_BN_CLICKED(IDC_RADIOCHOICE, OnRadiochoice) - ON_EN_CHANGE(IDC_EDITD, OnChangeEditd) - ON_EN_CHANGE(IDC_EDITBITSOFN, OnChangeEditbitsofn) - ON_EN_CHANGE(IDC_EDITDELTA, OnChangeEditdelta) - ON_BN_CLICKED(IDC_BUTTON1, OnCancel) - ON_BN_CLICKED(IDC_RADIOCHOICE2, OnRadiochoice) - ON_BN_CLICKED(IDC_BUTTONLOG2, OnButtonlog) + ON_BN_CLICKED(IDC_BUTTONDEFAULT, &CRSABloemerMayDlg::OnButtondefault) + ON_BN_CLICKED(IDC_BUTTONLOG, &CRSABloemerMayDlg::OnButtonlog) + ON_BN_CLICKED(IDC_RADIOCHOICE, &CRSABloemerMayDlg::OnRadiochoice) + ON_EN_CHANGE(IDC_EDITD, &CRSABloemerMayDlg::OnChangeEditd) + ON_EN_CHANGE(IDC_EDITBITSOFN, &CRSABloemerMayDlg::OnChangeEditbitsofn) + ON_EN_CHANGE(IDC_EDITDELTA, &CRSABloemerMayDlg::OnChangeEditdelta) + ON_BN_CLICKED(IDC_BUTTON1, &CRSABloemerMayDlg::OnCancel) + ON_BN_CLICKED(IDC_RADIOCHOICE2, &CRSABloemerMayDlg::OnRadiochoice) + ON_BN_CLICKED(IDC_BUTTONLOG2, &CRSABloemerMayDlg::OnButtonlog) //}}AFX_MSG_MAP END_MESSAGE_MAP() diff --git a/CrypTool/RSAFactorHintDlg.cpp b/CrypTool/RSAFactorHintDlg.cpp index 6fab420d4..9110b5c18 100644 --- a/CrypTool/RSAFactorHintDlg.cpp +++ b/CrypTool/RSAFactorHintDlg.cpp @@ -23,7 +23,7 @@ #include "stdafx.h" #include "CrypToolApp.h" -#include "RSAFactorHintDlg.h" +#include "rsafactorhintdlg.h" #include "FileTools.h" #include "NTLExpPars.h" @@ -66,31 +66,31 @@ BEGIN_MESSAGE_MAP(CRSAFactorHintDlg, CDialog) //{{AFX_MSG_MAP(CRSAFactorHintDlg) ON_WM_PAINT() ON_WM_QUERYDRAGICON() - ON_BN_CLICKED(IDC_RADIONUMBER, OnRadionumber) - ON_BN_CLICKED(IDC_RADIOHEX, OnRadiohex) - ON_BN_CLICKED(IDC_RADIOBIN, OnRadio3) + ON_BN_CLICKED(IDC_RADIONUMBER, &CRSAFactorHintDlg::OnRadionumber) + ON_BN_CLICKED(IDC_RADIOHEX, &CRSAFactorHintDlg::OnRadiohex) + ON_BN_CLICKED(IDC_RADIOBIN, &CRSAFactorHintDlg::OnRadio3) ON_WM_TIMER() - ON_BN_CLICKED(IDCANCELATTACK, OnCancelattack) - ON_BN_CLICKED(IDSTART, OnStart) - ON_BN_CLICKED(IDC_R, OnRandom) - ON_EN_KILLFOCUS(IDC_EDITN, OnKillfocusEditn) - ON_EN_CHANGE(IDC_EDITBITSOFN, OnChangeEditbitsofn) + ON_BN_CLICKED(IDCANCELATTACK, &CRSAFactorHintDlg::OnCancelattack) + ON_BN_CLICKED(IDSTART, &CRSAFactorHintDlg::OnStart) + ON_BN_CLICKED(IDC_R, &CRSAFactorHintDlg::OnRandom) + ON_EN_KILLFOCUS(IDC_EDITN, &CRSAFactorHintDlg::OnKillfocusEditn) + ON_EN_CHANGE(IDC_EDITBITSOFN, &CRSAFactorHintDlg::OnChangeEditbitsofn) ON_WM_SETCURSOR() ON_WM_CLOSE() - ON_BN_CLICKED(IDC_BUTTONCLOSE, OnButtonclose) - ON_BN_CLICKED(IDC_BUTTONLOG, OnButtonlog) - ON_BN_CLICKED(IDC_RADIOCHOICE, OnRadiochoice) - ON_EN_CHANGE(IDC_EDITN, OnChangeEditn) - ON_EN_CHANGE(IDC_EDITBITSOFP, OnChangeEditbitsofp) - ON_EN_CHANGE(IDC_EDITB, OnChangeEditb) - ON_BN_CLICKED(IDC_RADIOLSB, OnRadiolsb) - ON_BN_CLICKED(IDC_BUTTONDEFAULT, OnButtondefault) - ON_EN_CHANGE(IDC_EDITGUESSP_LEFT, OnChangeGuessP) - ON_BN_CLICKED(IDC_RADIOCHOICE2, OnRadiochoice) - ON_BN_CLICKED(IDC_RADIOMSB, OnRadiolsb) - ON_EN_CHANGE(IDC_EDITGUESSP_RIGHT, OnChangeGuessP) - ON_EN_KILLFOCUS(IDC_EDITGUESSP_RIGHT, OnKillfocusGuessP) - ON_EN_KILLFOCUS(IDC_EDITGUESSP_LEFT, OnKillfocusGuessP) + ON_BN_CLICKED(IDC_BUTTONCLOSE, &CRSAFactorHintDlg::OnButtonclose) + ON_BN_CLICKED(IDC_BUTTONLOG, &CRSAFactorHintDlg::OnButtonlog) + ON_BN_CLICKED(IDC_RADIOCHOICE, &CRSAFactorHintDlg::OnRadiochoice) + ON_EN_CHANGE(IDC_EDITN, &CRSAFactorHintDlg::OnChangeEditn) + ON_EN_CHANGE(IDC_EDITBITSOFP, &CRSAFactorHintDlg::OnChangeEditbitsofp) + ON_EN_CHANGE(IDC_EDITB, &CRSAFactorHintDlg::OnChangeEditb) + ON_BN_CLICKED(IDC_RADIOLSB, &CRSAFactorHintDlg::OnRadiolsb) + ON_BN_CLICKED(IDC_BUTTONDEFAULT, &CRSAFactorHintDlg::OnButtondefault) + ON_EN_CHANGE(IDC_EDITGUESSP_LEFT, &CRSAFactorHintDlg::OnChangeGuessP) + ON_BN_CLICKED(IDC_RADIOCHOICE2, &CRSAFactorHintDlg::OnRadiochoice) + ON_BN_CLICKED(IDC_RADIOMSB, &CRSAFactorHintDlg::OnRadiolsb) + ON_EN_CHANGE(IDC_EDITGUESSP_RIGHT, &CRSAFactorHintDlg::OnChangeGuessP) + ON_EN_KILLFOCUS(IDC_EDITGUESSP_RIGHT, &CRSAFactorHintDlg::OnKillfocusGuessP) + ON_EN_KILLFOCUS(IDC_EDITGUESSP_LEFT, &CRSAFactorHintDlg::OnKillfocusGuessP) //}}AFX_MSG_MAP END_MESSAGE_MAP() diff --git a/CrypTool/ScintillaView.cpp b/CrypTool/ScintillaView.cpp index abe3d5104..e317d0eb7 100644 --- a/CrypTool/ScintillaView.cpp +++ b/CrypTool/ScintillaView.cpp @@ -39,12 +39,12 @@ #endif //#include "ScintillaDlg.h" #include "scintilla.h" -#include ".\scintillaview.h" +#include "ScintillaView.h" #include "FileTools.h" // Suchen und ersetzen #include "DlgFindAndReplace.h" -#include "cryptoolapp.h" +#include "CrypToolApp.h" #ifdef _DEBUG #define new DEBUG_NEW @@ -87,64 +87,64 @@ BEGIN_MESSAGE_MAP(CScintillaView, CCrypToolView) //{{AFX_MSG_MAP(CScintillaView) ON_WM_CREATE() ON_WM_SIZE() - ON_COMMAND(ID_EDIT_COPY, OnEditCopy) - ON_UPDATE_COMMAND_UI(ID_EDIT_COPY, OnUpdateEditCopy) - ON_COMMAND(ID_EDIT_CUT, OnEditCut) - ON_UPDATE_COMMAND_UI(ID_EDIT_CUT, OnUpdateEditCut) - ON_COMMAND(ID_EDIT_PASTE, OnEditPaste) - ON_UPDATE_COMMAND_UI(ID_EDIT_PASTE, OnUpdateEditPaste) - ON_COMMAND(ID_EDIT_UNDO, OnEditUndo) - ON_UPDATE_COMMAND_UI(ID_EDIT_UNDO, OnUpdateEditUndo) - ON_COMMAND(ID_EDIT_CLEAR, OnEditClear) - ON_COMMAND(ID_EDIT_REDO, OnEditRedo) - ON_UPDATE_COMMAND_UI(ID_EDIT_REDO, OnUpdateEditRedo) - ON_COMMAND(ID_EDIT_SELECT_ALL, OnEditSelectAll) - ON_UPDATE_COMMAND_UI(ID_INDICATOR_LINE, OnUpdateLine) - ON_UPDATE_COMMAND_UI(ID_INDICATOR_OVR, OnUpdateInsert) - ON_COMMAND(ID_VIEW_ENDOFLINE, OnViewEndOfLine) - ON_UPDATE_COMMAND_UI(ID_VIEW_ENDOFLINE, OnUpdateViewEndOfLine) - ON_COMMAND(ID_VIEW_LINEWRAP, OnViewLineWrap) - ON_UPDATE_COMMAND_UI(ID_VIEW_LINEWRAP, OnUpdateViewLineWrap) - ON_COMMAND(ID_VIEW_WHITESPACE, OnViewWhitespace) - ON_UPDATE_COMMAND_UI(ID_VIEW_WHITESPACE, OnUpdateViewWhitespace) - ON_COMMAND(ID_ZEICHENFORMAT_ARIAL08, OnViewFontArial08) - ON_COMMAND(ID_ZEICHENFORMAT_ARIAL10, OnViewFontArial10) - ON_COMMAND(ID_ZEICHENFORMAT_ARIAL12, OnViewFontArial12) - ON_COMMAND(ID_ZEICHENFORMAT_ARIAL14, OnViewFontArial14) - ON_COMMAND(ID_ZEICHENFORMAT_ARIAL16, OnViewFontArial16) - ON_COMMAND(ID_ZEICHENFORMAT_ARIAL18, OnViewFontArial18) - ON_COMMAND(ID_ZEICHENFORMAT_ARIAL20, OnViewFontArial20) - ON_COMMAND(ID_ZEICHENFORMAT_COURIER08, OnViewFontCourier08) - ON_COMMAND(ID_ZEICHENFORMAT_COURIER10, OnViewFontCourier10) - ON_COMMAND(ID_ZEICHENFORMAT_COURIER12, OnViewFontCourier12) - ON_COMMAND(ID_ZEICHENFORMAT_COURIER14, OnViewFontCourier14) - ON_COMMAND(ID_ZEICHENFORMAT_COURIER16, OnViewFontCourier16) - ON_COMMAND(ID_ZEICHENFORMAT_COURIER18, OnViewFontCourier18) - ON_COMMAND(ID_ZEICHENFORMAT_COURIER20, OnViewFontCourier20) - ON_COMMAND(ID_EDIT_FIND, OnEditFind) + ON_COMMAND(ID_EDIT_COPY, &CScintillaView::OnEditCopy) + ON_UPDATE_COMMAND_UI(ID_EDIT_COPY, &CScintillaView::OnUpdateEditCopy) + ON_COMMAND(ID_EDIT_CUT, &CScintillaView::OnEditCut) + ON_UPDATE_COMMAND_UI(ID_EDIT_CUT, &CScintillaView::OnUpdateEditCut) + ON_COMMAND(ID_EDIT_PASTE, &CScintillaView::OnEditPaste) + ON_UPDATE_COMMAND_UI(ID_EDIT_PASTE, &CScintillaView::OnUpdateEditPaste) + ON_COMMAND(ID_EDIT_UNDO, &CScintillaView::OnEditUndo) + ON_UPDATE_COMMAND_UI(ID_EDIT_UNDO, &CScintillaView::OnUpdateEditUndo) + ON_COMMAND(ID_EDIT_CLEAR, &CScintillaView::OnEditClear) + ON_COMMAND(ID_EDIT_REDO, &CScintillaView::OnEditRedo) + ON_UPDATE_COMMAND_UI(ID_EDIT_REDO, &CScintillaView::OnUpdateEditRedo) + ON_COMMAND(ID_EDIT_SELECT_ALL, &CScintillaView::OnEditSelectAll) + ON_UPDATE_COMMAND_UI(ID_INDICATOR_LINE, &CScintillaView::OnUpdateLine) + ON_UPDATE_COMMAND_UI(ID_INDICATOR_OVR, &CScintillaView::OnUpdateInsert) + ON_COMMAND(ID_VIEW_ENDOFLINE, &CScintillaView::OnViewEndOfLine) + ON_UPDATE_COMMAND_UI(ID_VIEW_ENDOFLINE, &CScintillaView::OnUpdateViewEndOfLine) + ON_COMMAND(ID_VIEW_LINEWRAP, &CScintillaView::OnViewLineWrap) + ON_UPDATE_COMMAND_UI(ID_VIEW_LINEWRAP, &CScintillaView::OnUpdateViewLineWrap) + ON_COMMAND(ID_VIEW_WHITESPACE, &CScintillaView::OnViewWhitespace) + ON_UPDATE_COMMAND_UI(ID_VIEW_WHITESPACE, &CScintillaView::OnUpdateViewWhitespace) + ON_COMMAND(ID_ZEICHENFORMAT_ARIAL08, &CScintillaView::OnViewFontArial08) + ON_COMMAND(ID_ZEICHENFORMAT_ARIAL10, &CScintillaView::OnViewFontArial10) + ON_COMMAND(ID_ZEICHENFORMAT_ARIAL12, &CScintillaView::OnViewFontArial12) + ON_COMMAND(ID_ZEICHENFORMAT_ARIAL14, &CScintillaView::OnViewFontArial14) + ON_COMMAND(ID_ZEICHENFORMAT_ARIAL16, &CScintillaView::OnViewFontArial16) + ON_COMMAND(ID_ZEICHENFORMAT_ARIAL18, &CScintillaView::OnViewFontArial18) + ON_COMMAND(ID_ZEICHENFORMAT_ARIAL20, &CScintillaView::OnViewFontArial20) + ON_COMMAND(ID_ZEICHENFORMAT_COURIER08, &CScintillaView::OnViewFontCourier08) + ON_COMMAND(ID_ZEICHENFORMAT_COURIER10, &CScintillaView::OnViewFontCourier10) + ON_COMMAND(ID_ZEICHENFORMAT_COURIER12, &CScintillaView::OnViewFontCourier12) + ON_COMMAND(ID_ZEICHENFORMAT_COURIER14, &CScintillaView::OnViewFontCourier14) + ON_COMMAND(ID_ZEICHENFORMAT_COURIER16, &CScintillaView::OnViewFontCourier16) + ON_COMMAND(ID_ZEICHENFORMAT_COURIER18, &CScintillaView::OnViewFontCourier18) + ON_COMMAND(ID_ZEICHENFORMAT_COURIER20, &CScintillaView::OnViewFontCourier20) + ON_COMMAND(ID_EDIT_FIND, &CScintillaView::OnEditFind) //}}AFX_MSG_MAP // Standard-Druckbefehle ON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint) ON_COMMAND(ID_FILE_PRINT_DIRECT, CView::OnFilePrint) ON_COMMAND(ID_FILE_PRINT_PREVIEW, CView::OnFilePrintPreview) - ON_COMMAND(ID_FILE_PAGE_SETUP, OnFilePageSetup) - ON_COMMAND(ID_TOHEX, OnTohex) - ON_UPDATE_COMMAND_UI(ID_ZEICHENFORMAT_ARIAL08, OnUpdateZeichenformatArial08) - ON_UPDATE_COMMAND_UI(ID_ZEICHENFORMAT_ARIAL10, OnUpdateZeichenformatArial10) - ON_UPDATE_COMMAND_UI(ID_ZEICHENFORMAT_ARIAL12, OnUpdateZeichenformatArial12) - ON_UPDATE_COMMAND_UI(ID_ZEICHENFORMAT_ARIAL14, OnUpdateZeichenformatArial14) - ON_UPDATE_COMMAND_UI(ID_ZEICHENFORMAT_ARIAL16, OnUpdateZeichenformatArial16) - ON_UPDATE_COMMAND_UI(ID_ZEICHENFORMAT_ARIAL18, OnUpdateZeichenformatArial18) - ON_UPDATE_COMMAND_UI(ID_ZEICHENFORMAT_ARIAL20, OnUpdateZeichenformatArial20) - ON_UPDATE_COMMAND_UI(ID_ZEICHENFORMAT_COURIER08, OnUpdateZeichenformatCourier08) - ON_UPDATE_COMMAND_UI(ID_ZEICHENFORMAT_COURIER10, OnUpdateZeichenformatCourier10) - ON_UPDATE_COMMAND_UI(ID_ZEICHENFORMAT_COURIER12, OnUpdateZeichenformatCourier12) - ON_UPDATE_COMMAND_UI(ID_ZEICHENFORMAT_COURIER14, OnUpdateZeichenformatCourier14) - ON_UPDATE_COMMAND_UI(ID_ZEICHENFORMAT_COURIER16, OnUpdateZeichenformatCourier16) - ON_UPDATE_COMMAND_UI(ID_ZEICHENFORMAT_COURIER18, OnUpdateZeichenformatCourier18) - ON_UPDATE_COMMAND_UI(ID_ZEICHENFORMAT_COURIER20, OnUpdateZeichenformatCourier20) - ON_COMMAND(ID_VIEW_ALPHABET, OnViewAlphabet) - ON_UPDATE_COMMAND_UI(ID_VIEW_ALPHABET, OnUpdateViewAlphabet) + ON_COMMAND(ID_FILE_PAGE_SETUP, &CScintillaView::OnFilePageSetup) + ON_COMMAND(ID_TOHEX, &CScintillaView::OnTohex) + ON_UPDATE_COMMAND_UI(ID_ZEICHENFORMAT_ARIAL08, &CScintillaView::OnUpdateZeichenformatArial08) + ON_UPDATE_COMMAND_UI(ID_ZEICHENFORMAT_ARIAL10, &CScintillaView::OnUpdateZeichenformatArial10) + ON_UPDATE_COMMAND_UI(ID_ZEICHENFORMAT_ARIAL12, &CScintillaView::OnUpdateZeichenformatArial12) + ON_UPDATE_COMMAND_UI(ID_ZEICHENFORMAT_ARIAL14, &CScintillaView::OnUpdateZeichenformatArial14) + ON_UPDATE_COMMAND_UI(ID_ZEICHENFORMAT_ARIAL16, &CScintillaView::OnUpdateZeichenformatArial16) + ON_UPDATE_COMMAND_UI(ID_ZEICHENFORMAT_ARIAL18, &CScintillaView::OnUpdateZeichenformatArial18) + ON_UPDATE_COMMAND_UI(ID_ZEICHENFORMAT_ARIAL20, &CScintillaView::OnUpdateZeichenformatArial20) + ON_UPDATE_COMMAND_UI(ID_ZEICHENFORMAT_COURIER08, &CScintillaView::OnUpdateZeichenformatCourier08) + ON_UPDATE_COMMAND_UI(ID_ZEICHENFORMAT_COURIER10, &CScintillaView::OnUpdateZeichenformatCourier10) + ON_UPDATE_COMMAND_UI(ID_ZEICHENFORMAT_COURIER12, &CScintillaView::OnUpdateZeichenformatCourier12) + ON_UPDATE_COMMAND_UI(ID_ZEICHENFORMAT_COURIER14, &CScintillaView::OnUpdateZeichenformatCourier14) + ON_UPDATE_COMMAND_UI(ID_ZEICHENFORMAT_COURIER16, &CScintillaView::OnUpdateZeichenformatCourier16) + ON_UPDATE_COMMAND_UI(ID_ZEICHENFORMAT_COURIER18, &CScintillaView::OnUpdateZeichenformatCourier18) + ON_UPDATE_COMMAND_UI(ID_ZEICHENFORMAT_COURIER20, &CScintillaView::OnUpdateZeichenformatCourier20) + ON_COMMAND(ID_VIEW_ALPHABET, &CScintillaView::OnViewAlphabet) + ON_UPDATE_COMMAND_UI(ID_VIEW_ALPHABET, &CScintillaView::OnUpdateViewAlphabet) END_MESSAGE_MAP() ///////////////////////////////////// diff --git a/CrypTool/ScintillaView.h b/CrypTool/ScintillaView.h index 5df66dcf5..dcac62c42 100644 --- a/CrypTool/ScintillaView.h +++ b/CrypTool/ScintillaView.h @@ -32,7 +32,7 @@ #endif // _MSC_VER > 1000 #include -#include "scintillawnd.h" +#include "ScintillaWnd.h" #include "CrypToolView.h" #ifdef SCFIND #include "FindDlg.h" diff --git a/CrypTool/SecudeLib.cpp b/CrypTool/SecudeLib.cpp index e8d68d4fd..556b6315b 100644 --- a/CrypTool/SecudeLib.cpp +++ b/CrypTool/SecudeLib.cpp @@ -35,7 +35,12 @@ static char THIS_FILE[]=__FILE__; #define new DEBUG_NEW #endif +#ifdef _WIN32 #define DYNAMICSECUDE +#else +// SECUDE.DLL is replaced by the statically linked compatibility library (port/secude) +#include "secude_compat.h" +#endif ////////////////////////////////////////////////////////////////////// // Konstruktion/Destruktion ////////////////////////////////////////////////////////////////////// @@ -89,6 +94,9 @@ int CSecudeLib::OpenSecudeLib() #ifdef DYNAMICSECUDE #define DoOneFn( a, b, c ,d) c = (c##_t) GetProcAddress(hDLL,"_"###c##d); if(c==NULL) errcnt++; #define DoOneData(a, b) b = (a *) GetProcAddress(hDLL, #b); if(b==NULL) errcnt++; +#elif !defined(_WIN32) +#define DoOneFn( a, b, c ,d) c = (c##_t) secude_compat_symbol(#c); if(c==NULL) errcnt++; +#define DoOneData(a, b) b = (a *) secude_compat_symbol(#b); if(b==NULL) errcnt++; #else #define DoOneFn(a,b,c,d) c=(c##_t) ::c; #define DoOneData(a,b) b=(a *) &::b; @@ -98,7 +106,7 @@ int CSecudeLib::OpenSecudeLib() #undef DoOneFn #undef DoOneData -#ifdef DYNAMICSECUDE +#if defined(DYNAMICSECUDE) || !defined(_WIN32) if(errcnt > 0) { // MessageBox(NULL,"SECUDE Lib konnte nicht geladen werden!","Test", MB_OK); Message(IDS_SECUDE_COULD_NOT_BE_LOADED, MB_ICONEXCLAMATION); diff --git a/CrypTool/SideChannelAttack.h b/CrypTool/SideChannelAttack.h index 95c54d7a6..7933b1c61 100644 --- a/CrypTool/SideChannelAttack.h +++ b/CrypTool/SideChannelAttack.h @@ -51,7 +51,7 @@ class SCA_Attacker; #include #include #include "AsymmetricEncryption.h" -#include "../libmiracl/include/BIG.H" +#include "big.h" #include "DlgKeyAsymGeneration.h" #include "KeyFileHandling.h" diff --git a/CrypTool/SignatureAttack.h b/CrypTool/SignatureAttack.h index 06a138748..0f1d19991 100644 --- a/CrypTool/SignatureAttack.h +++ b/CrypTool/SignatureAttack.h @@ -34,7 +34,7 @@ #include "HashingOperations.h" #include "OptionsForSignatureAttack.h" #include "ResultsOfSignatureAttack.h" -#include "sys\timeb.h" +#include class SignatureAttack { diff --git a/CrypTool/SizingDialog.cpp b/CrypTool/SizingDialog.cpp index a2aa50d7b..a0f2c00b0 100644 --- a/CrypTool/SizingDialog.cpp +++ b/CrypTool/SizingDialog.cpp @@ -35,7 +35,7 @@ #include "stdafx.h" #include "SizingDialog.h" -#include ".\SizingDialog.h" +#include "SizingDialog.h" #include "CrypToolTools.h" //////////////////////////////////////////////////////////////////////////// diff --git a/CrypTool/Solitaire.h b/CrypTool/Solitaire.h index aefb2c370..f3e1abd6e 100644 --- a/CrypTool/Solitaire.h +++ b/CrypTool/Solitaire.h @@ -41,7 +41,7 @@ class c_solitaire { unsigned char crypt_c( solitaire_action encrypt, unsigned char c ); long crypt( solitaire_action encrypt, const char *f_in, const char *f_out ); - friend long crypt_solitaire( solitaire_action encrypt, const char *f_in, const char *f_out, long cards, long ID, unsigned char *cardset, char *password = 0 ); + friend long crypt_solitaire( solitaire_action encrypt, const char *f_in, const char *f_out, long cards, long ID, unsigned char *cardset, char *password ); friend long crypt_solitaire( solitaire_action encrypt, const char *f_in, const char *f_out, CString &cardset ); public: @@ -63,6 +63,7 @@ class c_solitaire { void set_inner_state(long state) { inner_state = state; } }; +long crypt_solitaire( solitaire_action encrypt, const char *f_in, const char *f_out, long cards, long ID, unsigned char *cardset, char *password = 0 ); #if 1 class Deck diff --git a/CrypTool/SymEncBase.cpp b/CrypTool/SymEncBase.cpp index ee4a28abf..aecff142e 100644 --- a/CrypTool/SymEncBase.cpp +++ b/CrypTool/SymEncBase.cpp @@ -19,7 +19,7 @@ **************************************************************************/ #include "stdafx.h" #include "CrypToolApp.h" -#include ".\symencbase.h" +#include "SymEncBase.h" #include "SecudeCryptography.h" #include "CoreCryptography.h" #include "secure.h" // Include-File von SECUDE diff --git a/CrypTool/adfgvx.cpp b/CrypTool/adfgvx.cpp index 020f91eac..74500beed 100644 --- a/CrypTool/adfgvx.cpp +++ b/CrypTool/adfgvx.cpp @@ -28,7 +28,7 @@ using namespace std; // necessary to access the CrypTool alphabet #include "CrypToolApp.h" -#include "libanalyse\NGram.h" +#include "libanalyse/NGram.h" adfgvx::adfgvx () { codeMatrix[0][0] = 'A'; diff --git a/CrypTool/adfgvx.h b/CrypTool/adfgvx.h index 24502a8ab..1654a63ab 100644 --- a/CrypTool/adfgvx.h +++ b/CrypTool/adfgvx.h @@ -65,7 +65,7 @@ class adfgvx { char subChars[2]; pmlist permMatrix; pmlist::iterator permMatrixIterator; - int adfgvx::valueOf (char ch); + int valueOf (char ch); CString lastPasswordFound; char m_szFrequency[36]; int m_iHighestFrequency; @@ -73,7 +73,7 @@ class adfgvx { public: cblist countBigrammVector; - void adfgvx::countBigram(); + void countBigram(); float countMatrix[6][6]; adfgvx(); @@ -109,16 +109,16 @@ class adfgvx { //***** ANALYSIS FUNCTIONS *****// - CString adfgvx::nextPassword(int minlength, int maxlength, CString start); - bool adfgvx::goodenough(bool forcePassword); - void adfgvx::ForcePassword(CString pwd); - void adfgvx::SetFrequencyTable(char* szFrequency, int iHighestFrequency); + CString nextPassword(int minlength, int maxlength, CString start); + bool goodenough(bool forcePassword); + void ForcePassword(CString pwd); + void SetFrequencyTable(char* szFrequency, int iHighestFrequency); //***** INPUT VALIDATION *****// - int adfgvx::CheckPassword(int minLength, int maxLength, CString password); - CString adfgvx::CleansePassword(int errorCode, CString password); - CString adfgvx::LettersToNumbers(CString password); - int adfgvx::CheckStringBox(CString input); + int CheckPassword(int minLength, int maxLength, CString password); + CString CleansePassword(int errorCode, CString password); + CString LettersToNumbers(CString password); + int CheckStringBox(CString input); }; bool alphaSort (pmlistelem l1, pmlistelem l2); diff --git a/CrypTool/base64.h b/CrypTool/base64.h index 1494df673..a24338c3a 100644 --- a/CrypTool/base64.h +++ b/CrypTool/base64.h @@ -37,7 +37,9 @@ #ifndef _HLIB_Base64EncDec_H_ #define _HLIB_Base64EncDec_H_ 1 +#ifndef __GNUC__ #define __attribute__(x) +#endif // ERROR CODES #define B64_CODE_NOERROR 0 #define B64_DECODE_PREMATURE_END -1 @@ -54,7 +56,9 @@ extern "C" { #endif typedef unsigned int u_int32_t __attribute__((__mode__(__SI__))); +#ifdef _WIN32 typedef signed int ssize_t; +#endif /* ---ENCODE--- diff --git a/CrypTool/rsastereotypedmsgdlg.cpp b/CrypTool/rsastereotypedmsgdlg.cpp index bd5cc290b..f2220aaf0 100644 --- a/CrypTool/rsastereotypedmsgdlg.cpp +++ b/CrypTool/rsastereotypedmsgdlg.cpp @@ -23,7 +23,7 @@ #include "stdafx.h" #include "CrypToolApp.h" -#include "RSAStereotypedMSGDlg.h" +#include "rsastereotypedmsgdlg.h" #include "NTLExpPars.h" #include "FileTools.h" #ifdef _DEBUG @@ -74,31 +74,31 @@ BEGIN_MESSAGE_MAP(CRSAStereotypedMSGDlg, CDialog) //{{AFX_MSG_MAP(CRSAStereotypedMSGDlg) ON_WM_PAINT() ON_WM_QUERYDRAGICON() - ON_EN_CHANGE(IDC_EDITPLAIN, OnChangeEditplain) - ON_EN_CHANGE(IDC_EDITLENGTH, OnChangeEditlength) - ON_EN_CHANGE(IDC_EDITPOS, OnChangeEditpos) - ON_EN_CHANGE(IDC_EDITBITSOFN, OnChangeEditbitsofn) - ON_EN_CHANGE(IDC_EDITN, OnChangeEditn) - ON_EN_CHANGE(IDC_EDITE, OnChangeEdite) - ON_BN_CLICKED(IDC_BUTTONENCRYPT, OnButtonencrypt) - ON_BN_CLICKED(IDC_RADIONUMBER, OnRadionumber) - ON_BN_CLICKED(IDC_RADIOHEX, OnRadiohex) - ON_EN_KILLFOCUS(IDC_EDITCIPHER, OnKillfocusEditcipher) - ON_EN_CHANGE(IDC_EDITH, OnChangeEdith) - ON_EN_UPDATE(IDC_EDITCIPHER, OnUpdateEditcipher) - ON_BN_CLICKED(IDC_BUTTONSTART, OnButtonstart) + ON_EN_CHANGE(IDC_EDITPLAIN, &CRSAStereotypedMSGDlg::OnChangeEditplain) + ON_EN_CHANGE(IDC_EDITLENGTH, &CRSAStereotypedMSGDlg::OnChangeEditlength) + ON_EN_CHANGE(IDC_EDITPOS, &CRSAStereotypedMSGDlg::OnChangeEditpos) + ON_EN_CHANGE(IDC_EDITBITSOFN, &CRSAStereotypedMSGDlg::OnChangeEditbitsofn) + ON_EN_CHANGE(IDC_EDITN, &CRSAStereotypedMSGDlg::OnChangeEditn) + ON_EN_CHANGE(IDC_EDITE, &CRSAStereotypedMSGDlg::OnChangeEdite) + ON_BN_CLICKED(IDC_BUTTONENCRYPT, &CRSAStereotypedMSGDlg::OnButtonencrypt) + ON_BN_CLICKED(IDC_RADIONUMBER, &CRSAStereotypedMSGDlg::OnRadionumber) + ON_BN_CLICKED(IDC_RADIOHEX, &CRSAStereotypedMSGDlg::OnRadiohex) + ON_EN_KILLFOCUS(IDC_EDITCIPHER, &CRSAStereotypedMSGDlg::OnKillfocusEditcipher) + ON_EN_CHANGE(IDC_EDITH, &CRSAStereotypedMSGDlg::OnChangeEdith) + ON_EN_UPDATE(IDC_EDITCIPHER, &CRSAStereotypedMSGDlg::OnUpdateEditcipher) + ON_BN_CLICKED(IDC_BUTTONSTART, &CRSAStereotypedMSGDlg::OnButtonstart) ON_WM_TIMER() - ON_BN_CLICKED(IDC_BUTTONCANCELATTACK, OnButtoncancelattack) - ON_BN_CLICKED(IDC_RANDOM, OnRandom) + ON_BN_CLICKED(IDC_BUTTONCANCELATTACK, &CRSAStereotypedMSGDlg::OnButtoncancelattack) + ON_BN_CLICKED(IDC_RANDOM, &CRSAStereotypedMSGDlg::OnRandom) ON_WM_SETCURSOR() ON_WM_CLOSE() - ON_BN_CLICKED(IDC_BUTTONLOG, OnButtonlog) - ON_BN_CLICKED(IDC_RADIOCHOICE, OnRadiochoice) - ON_BN_CLICKED(IDC_BUTTONDELETE, OnButtondelete) - ON_EN_CHANGE(IDC_EDITPLAINFULL, OnChangeEditplainfull) - ON_EN_KILLFOCUS(IDC_EDITE, OnKillfocusEdite) - ON_BN_CLICKED(IDC_RADIOCHOICE2, OnRadiochoice) - ON_EN_KILLFOCUS(IDC_EDITN, OnKillfocusEditn) + ON_BN_CLICKED(IDC_BUTTONLOG, &CRSAStereotypedMSGDlg::OnButtonlog) + ON_BN_CLICKED(IDC_RADIOCHOICE, &CRSAStereotypedMSGDlg::OnRadiochoice) + ON_BN_CLICKED(IDC_BUTTONDELETE, &CRSAStereotypedMSGDlg::OnButtondelete) + ON_EN_CHANGE(IDC_EDITPLAINFULL, &CRSAStereotypedMSGDlg::OnChangeEditplainfull) + ON_EN_KILLFOCUS(IDC_EDITE, &CRSAStereotypedMSGDlg::OnKillfocusEdite) + ON_BN_CLICKED(IDC_RADIOCHOICE2, &CRSAStereotypedMSGDlg::OnRadiochoice) + ON_EN_KILLFOCUS(IDC_EDITN, &CRSAStereotypedMSGDlg::OnKillfocusEditn) //}}AFX_MSG_MAP END_MESSAGE_MAP() diff --git a/CrypTool/stdafx.h b/CrypTool/stdafx.h index 237500bc4..c4b01e956 100644 --- a/CrypTool/stdafx.h +++ b/CrypTool/stdafx.h @@ -75,8 +75,8 @@ #undef min #undef max -#include "libanalyse\la_string.h" -#include "libanalyse\analyse.h" +#include "libanalyse/la_string.h" +#include "libanalyse/analyse.h" #include diff --git a/CrypTool/uucode.h b/CrypTool/uucode.h index 17dac1789..aa3eed00d 100644 --- a/CrypTool/uucode.h +++ b/CrypTool/uucode.h @@ -37,7 +37,9 @@ #ifndef _HLIB_UUEncDec_H_ #define _HLIB_UUEncDec_H_ 1 +#ifndef __GNUC__ #define __attribute__(x) +#endif // ERROR CODES #define UU_CODE_NOERROR 0 #define UU_DECODE_MISSING_BEGIN -1 @@ -59,7 +61,9 @@ extern "C" { #endif typedef unsigned int u_int32_t __attribute__((__mode__(__SI__))); +#ifdef _WIN32 typedef signed int ssize_t; +#endif /* ---ENCODE--- diff --git a/NumberShark/ColoredListCtrl.cpp b/NumberShark/ColoredListCtrl.cpp index 3f69c2709..4229153c5 100644 --- a/NumberShark/ColoredListCtrl.cpp +++ b/NumberShark/ColoredListCtrl.cpp @@ -50,7 +50,7 @@ CColoredListCtrl::~CColoredListCtrl() BEGIN_MESSAGE_MAP(CColoredListCtrl, CListCtrl) //{{AFX_MSG_MAP(CColoredListCtrl) ON_WM_ERASEBKGND() - ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, OnCustomDraw) + ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, &CColoredListCtrl::OnCustomDraw) //}}AFX_MSG_MAP END_MESSAGE_MAP() diff --git a/NumberShark/DlgShowPrecalculatedScores.cpp b/NumberShark/DlgShowPrecalculatedScores.cpp index 92f243c3d..6f0f6a07d 100644 --- a/NumberShark/DlgShowPrecalculatedScores.cpp +++ b/NumberShark/DlgShowPrecalculatedScores.cpp @@ -109,7 +109,7 @@ BOOL CDlgShowPrecalculatedScores::OnInitDialog() } BEGIN_MESSAGE_MAP(CDlgShowPrecalculatedScores, CDialog) - ON_BN_CLICKED(ID_SHOW_DETAILS, OnBnClickedShowSequence) + ON_BN_CLICKED(ID_SHOW_DETAILS, &CDlgShowPrecalculatedScores::OnBnClickedShowSequence) END_MESSAGE_MAP() diff --git a/NumberShark/GameRules.cpp b/NumberShark/GameRules.cpp index 536556066..9fe9b188d 100644 --- a/NumberShark/GameRules.cpp +++ b/NumberShark/GameRules.cpp @@ -47,7 +47,7 @@ void GameRules::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(GameRules, CDialog) - ON_BN_CLICKED(IDOK, OnBnClickedOk) + ON_BN_CLICKED(IDOK, &GameRules::OnBnClickedOk) ON_WM_ACTIVATE() END_MESSAGE_MAP() diff --git a/NumberShark/GameSummary.cpp b/NumberShark/GameSummary.cpp index 34dd15a13..7d0ef5fe1 100644 --- a/NumberShark/GameSummary.cpp +++ b/NumberShark/GameSummary.cpp @@ -50,10 +50,10 @@ void CGameSummary::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CGameSummary, CDialog) - ON_BN_CLICKED(IDOK, OnBnClickedOk) + ON_BN_CLICKED(IDOK, &CGameSummary::OnBnClickedOk) ON_WM_ACTIVATE() - ON_BN_CLICKED(IDC_RADIO1, OnBnClickedRadio1) - ON_BN_CLICKED(IDC_RADIO2, OnBnClickedRadio2) + ON_BN_CLICKED(IDC_RADIO1, &CGameSummary::OnBnClickedRadio1) + ON_BN_CLICKED(IDC_RADIO2, &CGameSummary::OnBnClickedRadio2) END_MESSAGE_MAP() diff --git a/NumberShark/MFC-ZahlenHaiDlg.cpp b/NumberShark/MFC-ZahlenHaiDlg.cpp index 143db0d5f..5a835e799 100644 --- a/NumberShark/MFC-ZahlenHaiDlg.cpp +++ b/NumberShark/MFC-ZahlenHaiDlg.cpp @@ -188,26 +188,26 @@ BEGIN_MESSAGE_MAP(CMFCZahlenHaiDlg, CDialog) ON_WM_PAINT() ON_WM_QUERYDRAGICON() //}}AFX_MSG_MAP - ON_CONTROL_RANGE(BN_CLICKED, IDC_BUTTON01, IDC_BUTTON30, OnBnClickedButton) - - ON_NOTIFY(TCN_SELCHANGE, IDC_TAB1, OnTcnSelchangeTab1) - - ON_BN_CLICKED(IDC_BUTTON_OK, OnBnClickedButtonOk) - ON_BN_CLICKED(ID_BUTTON_END, OnBnClickedButtonEnd) - ON_BN_CLICKED(IDC_BUTTON_STARTNEW, OnBnClickedButtonStartnew) - ON_BN_CLICKED(ID_BUTTON_RULES, OnBnClickedButtonRules) - ON_BN_CLICKED(IDC_BUTTON_REST, OnBnClickedButtonRest) - ON_BN_CLICKED(IDC_BUTTON_OPTION, OnBnClickedButtonOption) - ON_BN_CLICKED(IDC_BUTTON_HELP, OnBnClickedButtonHelp) - ON_BN_CLICKED(IDC_BUTTON_FINISH, OnBnClickedButtonFinish) - ON_BN_CLICKED(IDC_BUTTON_UNDO, OnBnClickedButtonUndo) - ON_BN_CLICKED(IDC_BUTTON_REDO, OnBnClickedButtonRedo) - ON_BN_CLICKED(IDC_BUTTON_SWITCH_STYLE, OnBnClickedButtonSwitchStyle) + ON_CONTROL_RANGE(BN_CLICKED, IDC_BUTTON01, IDC_BUTTON30, &CMFCZahlenHaiDlg::OnBnClickedButton) + + ON_NOTIFY(TCN_SELCHANGE, IDC_TAB1, &CMFCZahlenHaiDlg::OnTcnSelchangeTab1) + + ON_BN_CLICKED(IDC_BUTTON_OK, &CMFCZahlenHaiDlg::OnBnClickedButtonOk) + ON_BN_CLICKED(ID_BUTTON_END, &CMFCZahlenHaiDlg::OnBnClickedButtonEnd) + ON_BN_CLICKED(IDC_BUTTON_STARTNEW, &CMFCZahlenHaiDlg::OnBnClickedButtonStartnew) + ON_BN_CLICKED(ID_BUTTON_RULES, &CMFCZahlenHaiDlg::OnBnClickedButtonRules) + ON_BN_CLICKED(IDC_BUTTON_REST, &CMFCZahlenHaiDlg::OnBnClickedButtonRest) + ON_BN_CLICKED(IDC_BUTTON_OPTION, &CMFCZahlenHaiDlg::OnBnClickedButtonOption) + ON_BN_CLICKED(IDC_BUTTON_HELP, &CMFCZahlenHaiDlg::OnBnClickedButtonHelp) + ON_BN_CLICKED(IDC_BUTTON_FINISH, &CMFCZahlenHaiDlg::OnBnClickedButtonFinish) + ON_BN_CLICKED(IDC_BUTTON_UNDO, &CMFCZahlenHaiDlg::OnBnClickedButtonUndo) + ON_BN_CLICKED(IDC_BUTTON_REDO, &CMFCZahlenHaiDlg::OnBnClickedButtonRedo) + ON_BN_CLICKED(IDC_BUTTON_SWITCH_STYLE, &CMFCZahlenHaiDlg::OnBnClickedButtonSwitchStyle) ON_WM_TIMER() ON_WM_SETCURSOR() - ON_NOTIFY(LVN_COLUMNCLICK, IDC_LIST2, OnLvnColumnclickList2) - ON_BN_CLICKED(IDC_BUTTON_LOAD, OnBnClickedButtonLoad) - ON_BN_CLICKED(IDC_BUTTON_SAVE, OnBnClickedButtonSave) + ON_NOTIFY(LVN_COLUMNCLICK, IDC_LIST2, &CMFCZahlenHaiDlg::OnLvnColumnclickList2) + ON_BN_CLICKED(IDC_BUTTON_LOAD, &CMFCZahlenHaiDlg::OnBnClickedButtonLoad) + ON_BN_CLICKED(IDC_BUTTON_SAVE, &CMFCZahlenHaiDlg::OnBnClickedButtonSave) END_MESSAGE_MAP() // CMFCZahlenHaiDlg Meldungshandler diff --git a/NumberShark/ZahlenHaiOptionen.cpp b/NumberShark/ZahlenHaiOptionen.cpp index 54bb44eef..53568e60c 100644 --- a/NumberShark/ZahlenHaiOptionen.cpp +++ b/NumberShark/ZahlenHaiOptionen.cpp @@ -86,16 +86,16 @@ void ZahlenHaiOptionen::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(ZahlenHaiOptionen, CDialog) - ON_BN_CLICKED(IDOK, OnBnClickedOk) - ON_BN_CLICKED(IDC_CHECK1, OnBnClickedCheck1) - ON_BN_CLICKED(IDC_CHECK2, OnBnClickedCheck2) - ON_BN_CLICKED(IDC_CHECK3, OnBnClickedCheck3) + ON_BN_CLICKED(IDOK, &ZahlenHaiOptionen::OnBnClickedOk) + ON_BN_CLICKED(IDC_CHECK1, &ZahlenHaiOptionen::OnBnClickedCheck1) + ON_BN_CLICKED(IDC_CHECK2, &ZahlenHaiOptionen::OnBnClickedCheck2) + ON_BN_CLICKED(IDC_CHECK3, &ZahlenHaiOptionen::OnBnClickedCheck3) - ON_BN_CLICKED(IDC_BUTTON_MAX, OnBnClickedButtonMax) + ON_BN_CLICKED(IDC_BUTTON_MAX, &ZahlenHaiOptionen::OnBnClickedButtonMax) ON_WM_ACTIVATE() - ON_BN_CLICKED(IDC_RADIO_MAX1, OnBnClickedRadioMax1) - ON_BN_CLICKED(IDC_RADIO_MAX2, OnBnClickedRadioMax2) - ON_NOTIFY(TCN_SELCHANGE, IDC_TAB1, OnTcnSelchangeTab1) + ON_BN_CLICKED(IDC_RADIO_MAX1, &ZahlenHaiOptionen::OnBnClickedRadioMax1) + ON_BN_CLICKED(IDC_RADIO_MAX2, &ZahlenHaiOptionen::OnBnClickedRadioMax2) + ON_NOTIFY(TCN_SELCHANGE, IDC_TAB1, &ZahlenHaiOptionen::OnTcnSelchangeTab1) END_MESSAGE_MAP() diff --git a/NumberShark/propPage1.cpp b/NumberShark/propPage1.cpp index c6d35d847..f636eea87 100644 --- a/NumberShark/propPage1.cpp +++ b/NumberShark/propPage1.cpp @@ -48,7 +48,7 @@ void propPage1::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(propPage1, CPropertyPage) - ON_NOTIFY(TCN_SELCHANGE, IDC_TAB1, OnTcnSelchangeTab1) + ON_NOTIFY(TCN_SELCHANGE, IDC_TAB1, &propPage1::OnTcnSelchangeTab1) END_MESSAGE_MAP() diff --git a/README.md b/README.md index 46f78eb6c..4706d4000 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ # CrypTool 1 + +![macOS/Linux port](https://img.shields.io/badge/macOS%2FLinux%20port-35%25-orange) — see [port/PORTING_STATUS.md](port/PORTING_STATUS.md) [CrypTool 1](https://www.cryptool.org/ct1/) (CT1) is a free Windows program for cryptography and cryptanalysis. It is part of the [CrypTool Project](https://www.cryptool.org/). diff --git a/aestool/HexEdit1.cpp b/aestool/HexEdit1.cpp index 9662f1d9b..915593f4e 100644 --- a/aestool/HexEdit1.cpp +++ b/aestool/HexEdit1.cpp @@ -56,8 +56,8 @@ BEGIN_MESSAGE_MAP(CHexEdit, CEdit) //{{AFX_MSG_MAP(CHexEdit) ON_WM_CHAR() ON_WM_KEYDOWN() - ON_MESSAGE(WM_PASTE, OnPaste) - ON_CONTROL_REFLECT_EX(EN_UPDATE, OnUpdate) + ON_MESSAGE(WM_PASTE, &CHexEdit::OnPaste) + ON_CONTROL_REFLECT_EX(EN_UPDATE, &CHexEdit::OnUpdate) //}}AFX_MSG_MAP END_MESSAGE_MAP() diff --git a/aestool/aestoolDlg.cpp b/aestool/aestoolDlg.cpp index 10a4103aa..db82699eb 100644 --- a/aestool/aestoolDlg.cpp +++ b/aestool/aestoolDlg.cpp @@ -151,17 +151,17 @@ BEGIN_MESSAGE_MAP(CAestoolDlg, CDialog) ON_WM_SYSCOMMAND() ON_WM_PAINT() ON_WM_QUERYDRAGICON() - ON_BN_CLICKED(IDC_BUTTON_SRC, OnSucheSrc) - ON_BN_CLICKED(IDC_BUTTON3, OnHelp) - ON_EN_CHANGE(IDC_EDIT_KEY_HEX, updateEncryptDecryptButtons) - ON_EN_CHANGE(IDC_EDIT_KEY_ASCII, updateEncryptDecryptButtons) - ON_EN_CHANGE(IDC_EDIT_SRC, OnChangeSrc) - ON_BN_CLICKED(IDC_CHECK_SHOW_PASSWORD, OnCheckShowPassword) - ON_BN_CLICKED(IDC_CHECK_ENTER_PASSWORD_AS_HEX, OnCheckEnterPasswordAsHex) - ON_BN_CLICKED(IDC_RADIO_EXE, OnRadioFormat) - ON_BN_CLICKED(IDC_RADIO_AES, OnRadioFormat) - ON_BN_CLICKED(IDC_BUTTON_ENCRYPT, OnOK) - ON_BN_CLICKED(IDC_BUTTON_DECRYPT, OnOK) + ON_BN_CLICKED(IDC_BUTTON_SRC, &CAestoolDlg::OnSucheSrc) + ON_BN_CLICKED(IDC_BUTTON3, &CAestoolDlg::OnHelp) + ON_EN_CHANGE(IDC_EDIT_KEY_HEX, &CAestoolDlg::updateEncryptDecryptButtons) + ON_EN_CHANGE(IDC_EDIT_KEY_ASCII, &CAestoolDlg::updateEncryptDecryptButtons) + ON_EN_CHANGE(IDC_EDIT_SRC, &CAestoolDlg::OnChangeSrc) + ON_BN_CLICKED(IDC_CHECK_SHOW_PASSWORD, &CAestoolDlg::OnCheckShowPassword) + ON_BN_CLICKED(IDC_CHECK_ENTER_PASSWORD_AS_HEX, &CAestoolDlg::OnCheckEnterPasswordAsHex) + ON_BN_CLICKED(IDC_RADIO_EXE, &CAestoolDlg::OnRadioFormat) + ON_BN_CLICKED(IDC_RADIO_AES, &CAestoolDlg::OnRadioFormat) + ON_BN_CLICKED(IDC_BUTTON_ENCRYPT, &CAestoolDlg::OnOK) + ON_BN_CLICKED(IDC_BUTTON_DECRYPT, &CAestoolDlg::OnOK) //}}AFX_MSG_MAP END_MESSAGE_MAP() diff --git a/cracklib-2.7/cracklib_Win32/fascist.c b/cracklib-2.7/cracklib_Win32/fascist.c index 0e326b12f..7b9609022 100644 --- a/cracklib-2.7/cracklib_Win32/fascist.c +++ b/cracklib-2.7/cracklib_Win32/fascist.c @@ -15,6 +15,7 @@ static char vers_id[] = "fascist.c : v2.3p3 Alec Muffett 14 dec 1997"; #include "pwd.h" #else #include +#include #endif #define ISSKIP(x) (isspace(x) || ispunct(x)) diff --git a/cracklib-2.7/cracklib_Win32/packer.h b/cracklib-2.7/cracklib_Win32/packer.h index e7df9e949..c465810dd 100644 --- a/cracklib-2.7/cracklib_Win32/packer.h +++ b/cracklib-2.7/cracklib_Win32/packer.h @@ -10,6 +10,8 @@ #include #include +#include +#include #ifdef _WIN32 #define CRACKLIB_WIN32_API __declspec(dllexport) @@ -59,17 +61,17 @@ typedef struct #define PW_WORDS(x) ((x)->header.pih_numwords) #define PIH_MAGIC 0x70775631 -extern CRACKLIB_WIN32_API PWDICT *PWOpen(); -extern CRACKLIB_WIN32_API char *Mangle(); -extern CRACKLIB_WIN32_API char *FascistCheck(); -extern CRACKLIB_WIN32_API char * FascistLook(pwp, instring); -extern CRACKLIB_WIN32_API char * Trim(string); -extern CRACKLIB_WIN32_API int PMatch(control, string); -extern CRACKLIB_WIN32_API int32 FindPW(pwp, string); -extern CRACKLIB_WIN32_API int PWClose(pwp); -extern CRACKLIB_WIN32_API int PutPW(pwp, string); -extern CRACKLIB_WIN32_API char * GetPW(pwp, number); -extern CRACKLIB_WIN32_API char Chop(string); +extern CRACKLIB_WIN32_API PWDICT *PWOpen(char *prefix, char *mode); +extern CRACKLIB_WIN32_API char *Mangle(char *input, char *control); +extern CRACKLIB_WIN32_API char *FascistCheck(char *password, char *path); +extern CRACKLIB_WIN32_API char * FascistLook(PWDICT *pwp, char *instring); +extern CRACKLIB_WIN32_API char * Trim(char *string); +extern CRACKLIB_WIN32_API int PMatch(char *control, char *string); +extern CRACKLIB_WIN32_API int32 FindPW(PWDICT *pwp, char *string); +extern CRACKLIB_WIN32_API int PWClose(PWDICT *pwp); +extern CRACKLIB_WIN32_API int PutPW(PWDICT *pwp, char *string); +extern CRACKLIB_WIN32_API char * GetPW(PWDICT *pwp, int32 number); +extern CRACKLIB_WIN32_API char Chop(char *string); #define CRACK_TOLOWER(a) (isupper(a)?tolower(a):(a)) #define CRACK_TOUPPER(a) (islower(a)?toupper(a):(a)) diff --git a/cracklib-2.7/cracklib_Win32/packlib.c b/cracklib-2.7/cracklib_Win32/packlib.c index 038a05b38..af98caee5 100644 --- a/cracklib-2.7/cracklib_Win32/packlib.c +++ b/cracklib-2.7/cracklib_Win32/packlib.c @@ -10,6 +10,68 @@ static char vers_id[] = "packlib.c : v2.3p2 Alec Muffett 18 May 1993"; +/* On-disk fields are 32-bit little-endian regardless of sizeof(int32). */ +#define PI_HEADER_DISK_SIZE 12 + +static int32 GetU32(const unsigned char *b) +{ + return (int32) b[0] | ((int32) b[1] << 8) | ((int32) b[2] << 16) | ((int32) b[3] << 24); +} + +static void PutU32(unsigned char *b, int32 v) +{ + b[0] = (unsigned char) (v & 0xff); + b[1] = (unsigned char) ((v >> 8) & 0xff); + b[2] = (unsigned char) ((v >> 16) & 0xff); + b[3] = (unsigned char) ((v >> 24) & 0xff); +} + +static size_t ReadU32(FILE *fp, int32 *out, size_t n) +{ + unsigned char b[4]; + size_t i; + for (i = 0; i < n && fread(b, 1, 4, fp) == 4; i++) + out[i] = GetU32(b); + return i; +} + +static size_t WriteU32(FILE *fp, const int32 *in, size_t n) +{ + unsigned char b[4]; + size_t i; + for (i = 0; i < n; i++) + { + PutU32(b, in[i]); + if (fwrite(b, 1, 4, fp) != 4) + break; + } + return i; +} + +static int ReadHeader(FILE *fp, struct pi_header *h) +{ + unsigned char b[PI_HEADER_DISK_SIZE]; + if (fread(b, 1, sizeof(b), fp) != sizeof(b)) + return 0; + h->pih_magic = GetU32(b); + h->pih_numwords = GetU32(b + 4); + h->pih_blocklen = (int16) (b[8] | (b[9] << 8)); + h->pih_pad = (int16) (b[10] | (b[11] << 8)); + return 1; +} + +static int WriteHeader(FILE *fp, const struct pi_header *h) +{ + unsigned char b[PI_HEADER_DISK_SIZE]; + PutU32(b, h->pih_magic); + PutU32(b + 4, h->pih_numwords); + b[8] = (unsigned char) (h->pih_blocklen & 0xff); + b[9] = (unsigned char) ((h->pih_blocklen >> 8) & 0xff); + b[10] = (unsigned char) (h->pih_pad & 0xff); + b[11] = (unsigned char) ((h->pih_pad >> 8) & 0xff); + return fwrite(b, 1, sizeof(b), fp) == sizeof(b); +} + PWDICT * PWOpen(prefix, mode) char *prefix; @@ -64,12 +126,12 @@ PWOpen(prefix, mode) pdesc.header.pih_blocklen = NUMWORDS; pdesc.header.pih_numwords = 0; - fwrite((char *) &pdesc.header, sizeof(pdesc.header), 1, ifp); + WriteHeader(ifp, &pdesc.header); } else { pdesc.flags &= ~PFOR_WRITE; - if (!fread((char *) &pdesc.header, sizeof(pdesc.header), 1, ifp)) + if (!ReadHeader(ifp, &pdesc.header)) { fprintf(stderr, "%s: error reading header\n", prefix); @@ -101,7 +163,7 @@ PWOpen(prefix, mode) if (pdesc.flags & PFOR_USEHWMS) { - if (fread(pdesc.hwms, 1, sizeof(pdesc.hwms), wfp) != sizeof(pdesc.hwms)) + if (ReadU32(wfp, pdesc.hwms, 256) != 256) { pdesc.flags &= ~PFOR_USEHWMS; } @@ -132,7 +194,7 @@ PWClose(pwp) return (-1); } - if (!fwrite((char *) &pwp->header, sizeof(pwp->header), 1, pwp->ifp)) + if (!WriteHeader(pwp->ifp, &pwp->header)) { fprintf(stderr, "index magic fwrite failed\n"); return (-1); @@ -151,7 +213,7 @@ PWClose(pwp) printf("hwm[%02x] = %d\n", i, pwp->hwms[i]); #endif } - fwrite(pwp->hwms, 1, sizeof(pwp->hwms), pwp->wfp); + WriteU32(pwp->wfp, pwp->hwms, 256); } } @@ -159,7 +221,8 @@ PWClose(pwp) fclose(pwp->dfp); // TEST - fclose(pwp->wfp); + if (pwp->wfp) + fclose(pwp->wfp); pwp->header.pih_magic = 0; @@ -199,7 +262,7 @@ PutPW(pwp, string) datum = (int32) ftell(pwp->dfp); - fwrite((char *) &datum, sizeof(datum), 1, pwp->ifp); + WriteU32(pwp->ifp, &datum, 1); fputs(pwp->data[0], pwp->dfp); putc(0, pwp->dfp); @@ -250,13 +313,13 @@ GetPW(pwp, number) { return (data[number % NUMWORDS]); } - if (fseek(pwp->ifp, sizeof(struct pi_header) + (thisblock * sizeof(int32)), 0)) + if (fseek(pwp->ifp, (long) (PI_HEADER_DISK_SIZE + thisblock * 4), 0)) { perror("(index fseek failed)"); return ((char *) 0); } - if (!fread((char *) &datum, sizeof(datum), 1, pwp->ifp)) + if (!ReadU32(pwp->ifp, &datum, 1)) { perror("(index fread failed)"); if(ferror(pwp->ifp)) diff --git a/cracklib-2.7/cracklib_Win32/rules.c b/cracklib-2.7/cracklib_Win32/rules.c index 05d46abda..77373ed97 100644 --- a/cracklib-2.7/cracklib_Win32/rules.c +++ b/cracklib-2.7/cracklib_Win32/rules.c @@ -15,13 +15,15 @@ static char vers_id[] = "rules.c : v5.0p3 Alec Muffett 20 May 1993"; #else #include "packer.h" +#include static void -Debug(val, a, b, c, d, e, f, g) - int val; - char *a, *b, *c, *d, *e, *f, *g; +Debug(int val, char *a, ...) { - fprintf(stderr, a, b, c, d, e, f); + va_list ap; + va_start(ap, a); + vfprintf(stderr, a, ap); + va_end(ap); } #endif diff --git a/libNTL/include/NTL/mach_desc.h b/libNTL/include/NTL/mach_desc.h index 8957c5719..638767cef 100644 --- a/libNTL/include/NTL/mach_desc.h +++ b/libNTL/include/NTL/mach_desc.h @@ -1,6 +1,9 @@ #ifndef NTL_mach_desc__H #define NTL_mach_desc__H +#if defined(__SIZEOF_LONG__) && (__SIZEOF_LONG__ != 4) +#error "This mach_desc.h assumes 32-bit long; put the generated NTL/mach_desc.h (port/thirdparty, target cryptool_ntl) first in the include path" +#endif #define NTL_BITS_PER_LONG (32) #define NTL_MAX_LONG (2147483647L) diff --git a/libVolRen/EditorDialog.cpp b/libVolRen/EditorDialog.cpp index 05ceb6259..022a966d2 100644 --- a/libVolRen/EditorDialog.cpp +++ b/libVolRen/EditorDialog.cpp @@ -1,7 +1,7 @@ // EditorDialog.cpp : implementation file // -#include "stdafx.h" +#include "StdAfx.h" #include "resource.h" #include "EditorDialog.h" #include "VolumeRenderer.h" diff --git a/libVolRen/StdAfx.cpp b/libVolRen/StdAfx.cpp index caeb97372..e2f27e1cb 100644 --- a/libVolRen/StdAfx.cpp +++ b/libVolRen/StdAfx.cpp @@ -2,7 +2,7 @@ // VolRen.pch will be the pre-compiled header // stdafx.obj will contain the pre-compiled type information -#include "stdafx.h" +#include "StdAfx.h" diff --git a/libVolRen/Trapezeditor.cpp b/libVolRen/Trapezeditor.cpp index 8b2ef3ef4..a6c979615 100644 --- a/libVolRen/Trapezeditor.cpp +++ b/libVolRen/Trapezeditor.cpp @@ -4,7 +4,7 @@ // Microsoft Visual C++, your modifications will be overwritten. -#include "stdafx.h" +#include "StdAfx.h" #include "Trapezeditor.h" ///////////////////////////////////////////////////////////////////////////// diff --git a/libVolRen/VolRen.cpp b/libVolRen/VolRen.cpp index e0f3a889a..3a388e212 100644 --- a/libVolRen/VolRen.cpp +++ b/libVolRen/VolRen.cpp @@ -1,7 +1,7 @@ // VolRen.cpp : Defines the initialization routines for the DLL. // -#include "stdafx.h" +#include "StdAfx.h" #include #ifdef _DEBUG diff --git a/libVolRen/VolumeRenderer.cpp b/libVolRen/VolumeRenderer.cpp index cd62a7f69..157fa2d79 100644 --- a/libVolRen/VolumeRenderer.cpp +++ b/libVolRen/VolumeRenderer.cpp @@ -6,7 +6,7 @@ #include #include #include "ArcBall.h" -#include "Volume.H" +#include "Volume.h" #include "VolumeRenderer.h" #ifdef _DEBUG diff --git a/libVolRen/VolumeUtilities.cpp b/libVolRen/VolumeUtilities.cpp index a8329ab7b..64bdbff21 100644 --- a/libVolRen/VolumeUtilities.cpp +++ b/libVolRen/VolumeUtilities.cpp @@ -2,7 +2,7 @@ // ////////////////////////////////////////////////////////////////////// -#include "stdafx.h" +#include "StdAfx.h" #include "VolumeUtilities.h" ////////////////////////////////////////////////////////////////////// diff --git a/libanalyse/Converter.cpp b/libanalyse/Converter.cpp index 963991fcd..9754a9eeb 100644 --- a/libanalyse/Converter.cpp +++ b/libanalyse/Converter.cpp @@ -29,6 +29,11 @@ // ///////////////////////////////////////////////////////////////// #include +#ifndef _MSC_VER +#include +static char* _strlwr(char* s) { for (char* p = s; *p; ++p) *p = (char)tolower((unsigned char)*p); return s; } +static char* _strupr(char* s) { for (char* p = s; *p; ++p) *p = (char)toupper((unsigned char)*p); return s; } +#endif #define IMPLEMENT_CONVERTERCLASS 1 #include "Converter.h" diff --git a/libanalyse/Converter.h b/libanalyse/Converter.h index 1cf946f16..867eca115 100644 --- a/libanalyse/Converter.h +++ b/libanalyse/Converter.h @@ -108,9 +108,9 @@ class Converter : public ConverterFormat ///////////////////////////////////////////////////////////////////// #ifdef IMPLEMENT_CONVERTERCLASS -#define CLONE(TYPE) virtual Converter* TYPE::Clone() const { return new TYPE(*this); } +#define CLONE(TYPE) virtual Converter* Clone() const { return new TYPE(*this); } #else -#define CLONE(TYPE) virtual Converter* TYPE::Clone() const; +#define CLONE(TYPE) virtual Converter* Clone() const; #endif ///////////////////////////////////////////////////////////////////// diff --git a/libanalyse/Default.h b/libanalyse/Default.h index 8a19b6f28..6d1e800be 100644 --- a/libanalyse/Default.h +++ b/libanalyse/Default.h @@ -50,7 +50,7 @@ extern OStream DefaultOStream; // GNU specific -#ifdef __GNUC__ +#if defined(__GNUC__) && __GNUC__ < 3 #define EXPLICIT explicit #define NOSTATICTAMPLES #else diff --git a/libanalyse/Format2.h b/libanalyse/Format2.h index 34bfe1846..16dc0c788 100644 --- a/libanalyse/Format2.h +++ b/libanalyse/Format2.h @@ -192,8 +192,8 @@ class NFormat : public BASE ///////////////////////////////////////////////////////////////////// // NFormat: Static Part public: - static Format::Index NewIndex() {return Index(currentindex++);} - static Format::SIndex NewString() {return SIndex(currentstringindex++);} + static Format::Index NewIndex() {return Format::Index(currentindex++);} + static Format::SIndex NewString() {return Format::SIndex(currentstringindex++);} private: static int currentindex; static int currentstringindex; diff --git a/libanalyse/MYMATH.H b/libanalyse/MYMATH.H index 60064fbaa..cbd043439 100644 --- a/libanalyse/MYMATH.H +++ b/libanalyse/MYMATH.H @@ -71,8 +71,8 @@ inline double sqr(double x) {return x*x;} inline long sqr(long x) {return x*x;} // testbit, setbit -inline int testbit(unsigned long l, int p) { return (l&(1<>(istream& s, String& x) { - if (!s.ipfx(0) || (!(s.flags() & ios::skipws) && !ws(s))) + if (!LA_IPFX(s) || (!(s.flags() & ios::skipws) && !ws(s))) { s.clear(ios::failbit|s.rdstate()); // Redundant if using GNU iostreams. return s; @@ -1225,7 +1231,7 @@ istream& operator>>(istream& s, String& x) } x.rep->s[i] = 0; x.rep->len = i; - int new_state = s.rdstate(); + ios::iostate new_state = s.rdstate(); if (i == 0) new_state |= ios::failbit; if (ch == EOF) new_state |= ios::eofbit; s.clear(new_state); @@ -1234,7 +1240,7 @@ istream& operator>>(istream& s, String& x) int readline(istream& s, String& x, char terminator, int discard) { - if (!s.ipfx(0)) + if (!LA_IPFX(s)) return 0; int ch; int i = 0; diff --git a/libanalyse/SymbolArray.h b/libanalyse/SymbolArray.h index d212b79c7..608d4afa6 100644 --- a/libanalyse/SymbolArray.h +++ b/libanalyse/SymbolArray.h @@ -147,7 +147,7 @@ class SymbolArray : public Array void Show(OStream& out=DefaultOStream) const; // Static Members - (should be put in Symbol 'class') - static int MaxBits() {return sizeof(symbol)*8;} + static int MaxBits() {return 32;} // bit-permutation width of the original 32-bit 'unsigned long' symbols static symbol MaxNum() {return 0xFFFFFFFF;} // Implementation diff --git a/libanalyse/la_ostream.h b/libanalyse/la_ostream.h index 8a5e87aaa..f052aa69c 100644 --- a/libanalyse/la_ostream.h +++ b/libanalyse/la_ostream.h @@ -105,6 +105,11 @@ friend inline OStream& sep(OStream& out) { return out.sep(); } }; +// standard C++ needs the manipulators at namespace scope +OStream& flush(OStream& out); +OStream& endl(OStream& out); +OStream& sep(OStream& out); + extern OStream DefaultOStream; ///////////////////////////////////////////////////////////////////// diff --git a/libanalyse/la_string.h b/libanalyse/la_string.h index 2c3c0dd74..d3dfcb74c 100644 --- a/libanalyse/la_string.h +++ b/libanalyse/la_string.h @@ -18,9 +18,12 @@ Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. #ifndef _String_h -#ifdef __GNUG__ +#if defined(__GNUG__) && __GNUC__ < 3 #pragma interface #endif +#if defined(__GNUG__) && __GNUC__ >= 3 && !defined(_G_NO_NRV) +#define _G_NO_NRV +#endif #define _String_h 1 #if !defined(_MSC_VER) || _MSC_VER <= 1200 @@ -323,9 +326,9 @@ class String const Regex& sep); friend String common_prefix(const String& x, const String& y, - int startpos = 0); + int startpos); friend String common_suffix(const String& x, const String& y, - int startpos = -1); + int startpos); friend String replicate(char c, int n); friend String replicate(const String& y, int n); friend String join(String src[], int n, const String& sep); @@ -365,8 +368,8 @@ class String friend istream& operator>>(istream& s, String& x); friend int readline(istream& s, String& x, - char terminator = '\n', - int discard_terminator = 1); + char terminator, + int discard_terminator); // status @@ -386,6 +389,12 @@ class String int OK() const; }; +// standard C++ needs these at namespace scope (default arguments, no ADL via String) +String common_prefix(const String& x, const String& y, int startpos = 0); +String common_suffix(const String& x, const String& y, int startpos = -1); +String replicate(char c, int n); +int readline(istream& s, String& x, char terminator = '\n', int discard_terminator = 1); + typedef String StrTmp; // for backward compatibility // other externs diff --git a/libanalyse/mytemplates.h b/libanalyse/mytemplates.h index 24f90edd0..3aa3203f4 100644 --- a/libanalyse/mytemplates.h +++ b/libanalyse/mytemplates.h @@ -32,7 +32,13 @@ #ifndef _MYTEMPLATES_H #define _MYTEMPLATES_H 1 +#ifdef _MSC_VER #include +#define LA_STDCALL __stdcall +#else +#include +#define LA_STDCALL +#endif /////////////////////////////////////////////////////////////////////////////////////// // Beginn Aenderung Jens Liebehenschel, 30.12.1998 @@ -70,7 +76,7 @@ template inline int __stdcall operator>=(const T& x, const T& y) #pragma warning( pop ) // templates defining operator +,-,*,/,^,% using +=,... and initializing constructor -template inline T __stdcall operator+(const T& x, A& y) +template inline T LA_STDCALL operator+(const T& x, A& y) { T tmp(x); tmp+=y; return tmp; } /////////////////////////////////////////////////////////////////////////////////////// // Ende Aenderung Jens Liebehenschel, 30.12.1998 diff --git a/libec/include/ECsecude.h b/libec/include/ECsecude.h index 384e62063..27a0a4d83 100644 --- a/libec/include/ECsecude.h +++ b/libec/include/ECsecude.h @@ -53,13 +53,13 @@ struct Secude_s extern struct Secude_s ECSecudeLib; -#define SECUDE(a) ECSecudeLib.##a -#define SECUDE_DATA_PTR(a) ECSecudeLib.##a +#define SECUDE(a) ECSecudeLib.a +#define SECUDE_DATA_PTR(a) ECSecudeLib.a #else #define SECUDE(a) a -#define SECUDE_DATA_PTR(a) &##a +#define SECUDE_DATA_PTR(a) &a #endif //LINK_SECUDE diff --git a/libec/sources/ECsecude.c b/libec/sources/ECsecude.c index 54a904363..a20aa6781 100644 --- a/libec/sources/ECsecude.c +++ b/libec/sources/ECsecude.c @@ -2,6 +2,15 @@ #ifndef LINK_SECUDE +#ifdef _WIN32 struct Secude_s ECSecudeLib; +#else +/* bound at link time to the SECUDE compatibility library (port/secude) */ +#define DoOneFn(a, b, c, d) (c##_t) c, +#define DoOneData(a, b) &b, +struct Secude_s ECSecudeLib = { DoECAll }; +#undef DoOneFn +#undef DoOneData +#endif -#endif \ No newline at end of file +#endif diff --git a/libmiracl/include/BIG.H b/libmiracl/include/BIG.H index 161a46b1f..0c77389ea 100644 --- a/libmiracl/include/BIG.H +++ b/libmiracl/include/BIG.H @@ -170,7 +170,7 @@ public: // x^m.y^k mod n friend Big pow(int,Big *,Big *,Big); // x[0]^m[0].x[1].m[1]... mod n - friend Big luc(const Big& ,const Big&, const Big&, Big *b4=NULL); + friend Big luc(const Big& ,const Big&, const Big&, Big *b4); friend Big inverse(const Big&, const Big&); friend Big rand(const Big&); // 0 < rand < parameter friend Big rand(int,int); // (digits,base) e.g. (1024,2) @@ -201,7 +201,7 @@ public: friend Big nres_pow(const Big&,const Big&); friend Big nres_pow2(const Big&,const Big&,const Big&,const Big&); friend Big nres_pown(int,Big *,Big *); - friend Big nres_luc(const Big&,const Big&,Big *b3=NULL); + friend Big nres_luc(const Big&,const Big&,Big *b3); friend Big nres_sqrt(const Big&); friend Big nres_modadd(const Big&,const Big&); friend Big nres_modsub(const Big&,const Big&); @@ -230,6 +230,14 @@ public: ~Big() { mirkill(fn); } }; +/* standard C++ needs these at namespace scope (no ADL via Big, default arguments) */ +Big luc(const Big&,const Big&,const Big&,Big *b4=NULL); +Big nres_luc(const Big&,const Big&,Big *b3=NULL); +Big from_binary(int,char *); +Big rand(int,int); +BOOL modulo(int,int,int,int,BOOL); +Big get_modulus(); + #endif diff --git a/libmiracl/include/MIRACL.H b/libmiracl/include/MIRACL.H index 27c6806eb..4304f1b6e 100644 --- a/libmiracl/include/MIRACL.H +++ b/libmiracl/include/MIRACL.H @@ -649,7 +649,7 @@ extern void numer(_MIPT_ flash,big); extern void denom(_MIPT_ flash,big); extern BOOL fit(big,big,int); extern void build(_MIPT_ flash,int (*)(_MIPT_ big,int)); -extern void round(_MIPT_ big,big,flash); +extern void mround(_MIPT_ big,big,flash); extern void flop(_MIPT_ flash,flash,int *,flash); extern void fmul(_MIPT_ flash,flash,flash); extern void fdiv(_MIPT_ flash,flash,flash); diff --git a/libmiracl/source/BIG.CPP b/libmiracl/source/BIG.CPP index f34514020..b1880ca75 100644 --- a/libmiracl/source/BIG.CPP +++ b/libmiracl/source/BIG.CPP @@ -23,7 +23,7 @@ big Big::getbig() const BOOL Big::iszero() const { if (size(fn)==0) return TRUE; return FALSE;} Big operator-(const Big& b) -{Big nb; negate(b.fn,nb.fn); return nb;} +{Big nb; ::negate(b.fn,nb.fn); return nb;} Big operator+(const Big& b,int i) {Big abi; incr(b.fn, i, abi.fn); return abi;} Big operator+(int i,const Big& b) @@ -34,7 +34,7 @@ Big operator+(const Big& b1, const Big& b2) Big operator-(const Big& b, int i) {Big mbi; decr(b.fn, i, mbi.fn); return mbi;} Big operator-(int i, const Big& b) -{Big mib;decr(b.fn, i, mib.fn);negate(mib.fn,mib.fn);return mib;} +{Big mib;decr(b.fn, i, mib.fn);::negate(mib.fn,mib.fn);return mib;} Big operator-(const Big& b1, const Big& b2) {Big mbb; subtract(b1.fn,b2.fn,mbb.fn); return mbb;} diff --git a/libmiracl/source/MRFLASH.C b/libmiracl/source/MRFLASH.C index d31d963cc..c49f723c2 100644 --- a/libmiracl/source/MRFLASH.C +++ b/libmiracl/source/MRFLASH.C @@ -75,7 +75,7 @@ void flop(_MIPD_ flash x,flash y,int *op,flash z) } } mr_mip->check=ON; - round(_MIPP_ mr_mip->w5,mr_mip->w6,z); + mround(_MIPP_ mr_mip->w5,mr_mip->w6,z); MR_OUT } @@ -272,7 +272,7 @@ void fpmul(_MIPD_ flash x,int n,int d,flash y) if (fit(mr_mip->w5,mr_mip->w6,mr_mip->nib)) fpack(_MIPP_ mr_mip->w5,mr_mip->w6,y); else - round(_MIPP_ mr_mip->w5,mr_mip->w6,y); + mround(_MIPP_ mr_mip->w5,mr_mip->w6,y); MR_OUT } @@ -301,7 +301,7 @@ void fincr(_MIPD_ flash x,int n,int d,flash y) if (d==1 && fit(mr_mip->w5,mr_mip->w6,mr_mip->nib)) fpack(_MIPP_ mr_mip->w5,mr_mip->w6,y); else - round(_MIPP_ mr_mip->w5,mr_mip->w6,y); + mround(_MIPP_ mr_mip->w5,mr_mip->w6,y); MR_OUT } diff --git a/libmiracl/source/MRFRND.C b/libmiracl/source/MRFRND.C index c6a06fe92..6f2316bb1 100644 --- a/libmiracl/source/MRFRND.C +++ b/libmiracl/source/MRFRND.C @@ -30,7 +30,7 @@ void frand(_MIPD_ flash x) mr_mip->check=OFF; bigrand(_MIPP_ mr_mip->w6,mr_mip->w5); mr_mip->check=ON; - round(_MIPP_ mr_mip->w5,mr_mip->w6,x); + mround(_MIPP_ mr_mip->w5,mr_mip->w6,x); MR_OUT } diff --git a/libmiracl/source/MRROUND.C b/libmiracl/source/MRROUND.C index cb88dcfb5..89ffffe60 100644 --- a/libmiracl/source/MRROUND.C +++ b/libmiracl/source/MRROUND.C @@ -169,7 +169,7 @@ middle: } -void round(_MIPD_ big num,big den,flash z) +void mround(_MIPD_ big num,big den,flash z) { /* reduces and rounds the fraction num/den into z */ int s; #ifndef MR_GENERIC_MT diff --git a/port/PORTING_STATUS.md b/port/PORTING_STATUS.md new file mode 100644 index 000000000..df84d1c1e --- /dev/null +++ b/port/PORTING_STATUS.md @@ -0,0 +1,49 @@ +# CrypTool 1 on macOS/Linux — porting status + +Work in progress on branch `xplat-port`. Nothing builds into a runnable app yet. + +**Overall progress: ~35%** (update the badge in the top-level README.md together with this table) + +| Area | Weight | Done | +|---|---|---| +| Resource compiler | 10% | 100% | +| Third-party libraries | 15% | 100% | +| mfcwx headers | 10% | 90% | +| mfcwx implementation | 30% | 25% | +| SECUDE replacement | 10% | 40% | +| App sources compile | 10% | 80% | +| Link, run, UI tuning | 10% | 0% | +| Packaging (DMG, AppImage) | 5% | 0% | + +## Approach +The original MFC sources are compiled almost unchanged against **mfcwx**, an MFC/Win32 +compatibility layer implemented on wxWidgets 3.2. Windows resources (`.rc`) are converted to +C++ tables at build time. SECUDE (proprietary, Windows-only) is replaced by an OpenSSL 3 based +implementation of the same API. + +Build: `cmake -S . -B build -G Ninja && ninja -C build -k 0 CrypTool` +(macOS: Homebrew `wxwidgets@3.2`, `openssl@3`, `gmp`, `cmake`). + +## Layout +| Path | Content | State | +|---|---|---| +| `CMakeLists.txt` | top-level build | done | +| `port/rc/` | `rc2cpp.py` resource compiler + CMake function | done, tested on all 4 .rc files | +| `port/thirdparty/` | libanalyse, MIRACL (32-bit digits), NTL 5.5.2, libec, cracklib, apfloat→GMP, cv act→OpenSSL, AES candidates, OpenSSL hash shims | done, tests pass on macOS + Linux (Docker) | +| `port/secude/` | SECUDE API on OpenSSL 3 | partial (agent stopped mid-work); `secude_compat` must also export `global_add_error` and `rabinstest` for libec | +| `port/mfcwx/include/` | MFC/Win32 headers (afx*.h, windows.h, …) | written; app compiles against them | +| `port/mfcwx/src/` | implementation | partial: CString, code pages, resources, CCmdTarget/message maps, CWnd + event bridge (`wnd.cpp`), controls (`controls.cpp`); other files may be partial from stopped agents | +| `port/tools/` | `fix_includes.py`, `fix_msgmaps.py`, `ccheck.py` (single-file syntax check) | done | + +## Key decisions +- HWND = `wxWindow*`; control ids are offset by `mfcwx::kIdOffset`; controls have no data members (temporary `CWnd` casts work as in MFC). +- `CString` is a byte string in the active ANSI code page (1252/1250/1253 by UI language); conversion happens at the wx boundary. +- Paths inside the app keep Windows form (`\\dir\\file`); every file API converts with `mfcwx::NativePath`. +- App is compiled as C++14 (C++17 `std::byte` clashes with the Windows `byte` typedef). +- Cross-thread `SendMessage` is marshalled to the main thread like on Windows. + +## Remaining work (in order) +1. mfcwx implementation still missing: non-GUI runtime (files, archives, threads, registry/INI, CRT compat), GDI (CDC on wxDC), `DefaultWindowProc` + Win32 HWND functions (winapi.cpp), dialogs + DDX (dialog.cpp), menus, CWinApp/main loop/help, doc/view + MDI frames, Scintilla proxy (ANSI↔UTF-8 positions), WGL on wxGLCanvas. +2. Finish SECUDE replacement and its tests; recreate sample PSE keys. +3. Remaining ~680 compile errors in app sources (MSVC-isms: CString through varargs, temporaries bound to non-const refs, PictureEx/IPicture, OpenGL/libVolRen NV extensions, ActiveX editor). +4. Link, run, visually tune dialog units/fonts, then packaging (.app/DMG, AppImage/.deb). diff --git a/port/app/CMakeLists.txt b/port/app/CMakeLists.txt new file mode 100644 index 000000000..8f75cb8a6 --- /dev/null +++ b/port/app/CMakeLists.txt @@ -0,0 +1,33 @@ +include(${REPO_ROOT}/port/rc/rc2cpp.cmake) + +set(CT_SRC ${REPO_ROOT}/CrypTool) +file(GLOB CT_SOURCES CONFIGURE_DEPENDS ${CT_SRC}/*.cpp ${CT_SRC}/zip/*.c) +list(FILTER CT_SOURCES EXCLUDE REGEX "/(Ber_Ell_Kurve|DlgCrypToolPRNGSeedCollector|DlgFindText|DlgHomophonicSubstitution|DlgMac|DlgSelHash|HomophonicSubstitution)\\.cpp$") +file(GLOB CT_EXTRA_SOURCES CONFIGURE_DEPENDS + ${REPO_ROOT}/libVolRen/ArcBall.cpp ${REPO_ROOT}/libVolRen/CMatrix.cpp ${REPO_ROOT}/libVolRen/CPosition.cpp + ${REPO_ROOT}/libVolRen/CQuaternion.cpp ${REPO_ROOT}/libVolRen/CVector.cpp ${REPO_ROOT}/libVolRen/Volume.cpp + ${REPO_ROOT}/libVolRen/VolumeUtilities.cpp ${REPO_ROOT}/ChallengeResponse/*.cpp) + +add_executable(CrypTool ${CT_SOURCES} ${CT_EXTRA_SOURCES}) +mfcwx_add_rc(CrypTool RC ${CT_SRC}/CrypTool.rc MODULE CrypTool HEADERS ${CT_SRC}/resource.h) + +set_target_properties(CrypTool PROPERTIES CXX_STANDARD 14 CXX_EXTENSIONS ON) +# Quoted includes only: etc. must resolve to the libraries, as on Windows. +target_compile_options(CrypTool PRIVATE "SHELL:-iquote ${CT_SRC}" "SHELL:-iquote ${REPO_ROOT}/libVolRen" + "SHELL:-iquote ${REPO_ROOT}/ChallengeResponse") +target_include_directories(CrypTool PRIVATE ${REPO_ROOT}/scintilla/include) +target_link_libraries(CrypTool PRIVATE mfcwx cryptool_thirdparty $ OpenSSL::Crypto) +target_compile_options(CrypTool PRIVATE + -Wno-inconsistent-missing-override -Wno-overloaded-virtual -Wno-deprecated-declarations + $<$:-Wno-register -Wno-writable-strings -Wno-write-strings> + -Wno-unused-variable -Wno-unused-but-set-variable -Wno-parentheses -Wno-dangling-else + -Wno-switch -Wno-unused-value -Wno-sign-compare -Wno-char-subscripts -Wno-missing-braces + -fno-delete-null-pointer-checks) +if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + target_compile_options(CrypTool PRIVATE -Wno-c++11-narrowing -Wno-reserved-user-defined-literal + -Wno-logical-op-parentheses -Wno-bitwise-op-parentheses -Wno-shift-op-parentheses + -Wno-tautological-constant-out-of-range-compare -Wno-unused-private-field -Wno-null-conversion + -Wno-undefined-bool-conversion -Wno-format-security) +else() + target_compile_options(CrypTool PRIVATE $<$:-Wno-narrowing -fpermissive>) +endif() diff --git a/port/mfcwx/CMakeLists.txt b/port/mfcwx/CMakeLists.txt new file mode 100644 index 000000000..20ad75171 --- /dev/null +++ b/port/mfcwx/CMakeLists.txt @@ -0,0 +1,13 @@ +file(GLOB MFCWX_SOURCES CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp) + +add_library(mfcwx STATIC ${MFCWX_SOURCES}) +target_include_directories(mfcwx PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include) +target_compile_features(mfcwx PUBLIC cxx_std_14) +include(${wxWidgets_USE_FILE}) +target_include_directories(mfcwx SYSTEM PUBLIC ${wxWidgets_INCLUDE_DIRS}) +target_compile_definitions(mfcwx PUBLIC ${wxWidgets_DEFINITIONS} MFCWX=1) +target_compile_options(mfcwx PUBLIC ${wxWidgets_CXX_FLAGS}) +target_link_libraries(mfcwx PUBLIC ${wxWidgets_LIBRARIES} OpenGL::GL Threads::Threads ${CMAKE_DL_LIBS}) +if(APPLE) + target_link_libraries(mfcwx PUBLIC "-framework OpenGL") +endif() diff --git a/port/mfcwx/include/afx.h b/port/mfcwx/include/afx.h new file mode 100644 index 000000000..4efb4f2ab --- /dev/null +++ b/port/mfcwx/include/afx.h @@ -0,0 +1,602 @@ +#pragma once + +// MFC core (non-GUI) subset: CObject, CString, exceptions, files, archives, time, collections. +#define __AFX_H__ + +#ifdef __cplusplus +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +#include "mfcwx/win32.h" + +#ifdef __cplusplus + +#include "mfcwx/cstring.h" +#include "mfcwx/codepage.h" + +#ifndef _DEBUG +#define ASSERT(f) ((void)0) +#define ASSERT_VALID(p) ((void)0) +#define ASSERT_KINDOF(class_name, object) ((void)0) +#define ASSERT_POINTER(p, type) ((void)0) +#define ASSERT_NULL_OR_POINTER(p, type) ((void)0) +#else +#define ASSERT(f) ((f) ? (void)0 : AfxAssertFailedLine(__FILE__, __LINE__, #f)) +#define ASSERT_VALID(p) ((void)0) +#define ASSERT_KINDOF(class_name, object) ((void)0) +#define ASSERT_POINTER(p, type) ASSERT((p) != NULL) +#define ASSERT_NULL_OR_POINTER(p, type) ((void)0) +#endif +#define VERIFY(f) ((void)(f)) +#define ENSURE(f) ((void)(f)) +#define ENSURE_ARG(f) ((void)(f)) +#define DEBUG_ONLY(f) ((void)0) +#define TRACE(...) ((void)0) +#define TRACE0(sz) ((void)0) +#define TRACE1(sz, p1) ((void)0) +#define TRACE2(sz, p1, p2) ((void)0) +#define TRACE3(sz, p1, p2, p3) ((void)0) +#define ATLTRACE(...) ((void)0) +#define ATLASSERT(f) ((void)0) +#define DEBUG_NEW new +#define THIS_FILE __FILE__ +#define AFX_MANAGE_STATE(p) +#define AfxGetStaticModuleState() nullptr +#define afx_msg +#define AFX_MSG_CALL +#define AFX_INLINE inline + +void AfxAssertFailedLine(const char* file, int line, const char* expr); + +// MFC code calls members such as GetSafeHwnd() through null pointers; hide the origin of the +// pointer from the optimizer so the null test survives. +inline bool AfxIsNullThis(const void* p) { + __asm__("" : "+r"(p)); + return p == nullptr; +} +void AfxDebugBreak(); + +class CArchive; +class CDumpContext; +class CObject; + +struct CRuntimeClass { + const char* m_lpszClassName; + int m_nObjectSize; + UINT m_wSchema; + CObject* (*m_pfnCreateObject)(); + CRuntimeClass* (*m_pfnGetBaseClass)(); + CRuntimeClass* m_pNextClass; + + CObject* CreateObject(); + BOOL IsDerivedFrom(const CRuntimeClass* pBaseClass) const; + CRuntimeClass* GetBaseClass() const { return m_pfnGetBaseClass ? m_pfnGetBaseClass() : nullptr; } + static CRuntimeClass* FromName(const char* name); + static CObject* CreateObject(const char* name); +}; + +struct AFX_CLASSINIT { + explicit AFX_CLASSINIT(CRuntimeClass* pNewClass); +}; + +class CDumpContext { +public: + CDumpContext& operator<<(const char*) { return *this; } + CDumpContext& operator<<(int) { return *this; } + CDumpContext& operator<<(unsigned) { return *this; } + CDumpContext& operator<<(long) { return *this; } + CDumpContext& operator<<(double) { return *this; } + CDumpContext& operator<<(const void*) { return *this; } + int GetDepth() const { return 0; } +}; +extern CDumpContext afxDump; + +class CObject { +public: + virtual CRuntimeClass* GetRuntimeClass() const; + virtual ~CObject() = default; + BOOL IsKindOf(const CRuntimeClass* pClass) const; + BOOL IsSerializable() const; + virtual void Serialize(CArchive& ar); + virtual void AssertValid() const {} + virtual void Dump(CDumpContext&) const {} + + static CRuntimeClass classCObject; + static CRuntimeClass* GetThisClass(); + +protected: + CObject() = default; + CObject(const CObject&) = default; + CObject& operator=(const CObject&) = default; +}; + +#define _RUNTIME_CLASS(class_name) (class_name::GetThisClass()) +#define RUNTIME_CLASS(class_name) _RUNTIME_CLASS(class_name) + +#define DECLARE_DYNAMIC(class_name) \ +public: \ + static CRuntimeClass class##class_name; \ + static CRuntimeClass* GetThisClass(); \ + CRuntimeClass* GetRuntimeClass() const override; + +#define DECLARE_DYNCREATE(class_name) \ + DECLARE_DYNAMIC(class_name) \ + static CObject* CreateObject(); + +#define DECLARE_SERIAL(class_name) \ + DECLARE_DYNCREATE(class_name) \ + friend CArchive& operator>>(CArchive& ar, class_name*& pOb); + +#define IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, wSchema, pfnNew) \ + CRuntimeClass class_name::class##class_name = {#class_name, sizeof(class class_name), wSchema, pfnNew, \ + &base_class_name::GetThisClass, nullptr}; \ + static AFX_CLASSINIT _init_##class_name(&class_name::class##class_name); \ + CRuntimeClass* class_name::GetThisClass() { return &class_name::class##class_name; } \ + CRuntimeClass* class_name::GetRuntimeClass() const { return &class_name::class##class_name; } + +#define IMPLEMENT_DYNAMIC(class_name, base_class_name) \ + IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, 0xFFFF, nullptr) + +#define IMPLEMENT_DYNCREATE(class_name, base_class_name) \ + CObject* class_name::CreateObject() { return new class_name; } \ + IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, 0xFFFF, class_name::CreateObject) + +#define IMPLEMENT_SERIAL(class_name, base_class_name, wSchema) \ + CObject* class_name::CreateObject() { return new class_name; } \ + IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, wSchema, class_name::CreateObject) \ + CArchive& operator>>(CArchive& ar, class_name*& pOb) { \ + pOb = static_cast(ar.ReadObject(RUNTIME_CLASS(class_name))); \ + return ar; \ + } + +#define VERSIONABLE_SCHEMA (0x80000000) + +// --------------------------------------------------------------------------------------------- +// Exceptions + +class CException : public CObject { + DECLARE_DYNAMIC(CException) +public: + explicit CException(BOOL bAutoDelete = TRUE) : m_bAutoDelete(bAutoDelete) {} + void Delete(); + virtual BOOL GetErrorMessage(char* lpszError, UINT nMaxError, PUINT pnHelpContext = nullptr) const; + virtual int ReportError(UINT nType = MB_OK, UINT nMessageID = 0); + BOOL m_bAutoDelete; +}; + +class CSimpleException : public CException { + DECLARE_DYNAMIC(CSimpleException) +public: + CSimpleException() = default; + explicit CSimpleException(BOOL bAutoDelete) : CException(bAutoDelete) {} +}; + +class CMemoryException : public CSimpleException { + DECLARE_DYNAMIC(CMemoryException) +public: + CMemoryException() = default; + BOOL GetErrorMessage(char* lpszError, UINT nMaxError, PUINT pnHelpContext = nullptr) const override; +}; + +class CNotSupportedException : public CSimpleException { + DECLARE_DYNAMIC(CNotSupportedException) +}; + +class CInvalidArgException : public CSimpleException { + DECLARE_DYNAMIC(CInvalidArgException) +}; + +class CUserException : public CSimpleException { + DECLARE_DYNAMIC(CUserException) +}; + +class CResourceException : public CSimpleException { + DECLARE_DYNAMIC(CResourceException) +}; + +class CArchiveException : public CException { + DECLARE_DYNAMIC(CArchiveException) +public: + enum { none, genericException, readOnly, endOfFile, writeOnly, badIndex, badClass, badSchema, bufferFull }; + explicit CArchiveException(int cause = none, const char* lpszArchiveName = nullptr) + : m_cause(cause), m_strFileName(lpszArchiveName) {} + BOOL GetErrorMessage(char* lpszError, UINT nMaxError, PUINT pnHelpContext = nullptr) const override; + int m_cause; + CString m_strFileName; +}; + +class CFileException : public CException { + DECLARE_DYNAMIC(CFileException) +public: + enum { + none, + genericException, + fileNotFound, + badPath, + tooManyOpenFiles, + accessDenied, + invalidFile, + removeCurrentDir, + directoryFull, + badSeek, + hardIO, + sharingViolation, + lockViolation, + diskFull, + endOfFile + }; + explicit CFileException(int cause = none, LONG lOsError = -1, const char* lpszArchiveName = nullptr) + : m_cause(cause), m_lOsError(lOsError), m_strFileName(lpszArchiveName) {} + BOOL GetErrorMessage(char* lpszError, UINT nMaxError, PUINT pnHelpContext = nullptr) const override; + static int OsErrorToException(LONG lOsError); + static int ErrnoToException(int nErrno); + static void ThrowOsError(LONG lOsError, const char* lpszFileName = nullptr); + static void ThrowErrno(int nErrno, const char* lpszFileName = nullptr); + int m_cause; + LONG m_lOsError; + CString m_strFileName; +}; + +[[noreturn]] void AfxThrowMemoryException(); +[[noreturn]] void AfxThrowNotSupportedException(); +[[noreturn]] void AfxThrowInvalidArgException(); +[[noreturn]] void AfxThrowUserException(); +[[noreturn]] void AfxThrowResourceException(); +[[noreturn]] void AfxThrowArchiveException(int cause, const char* lpszArchiveName = nullptr); +[[noreturn]] void AfxThrowFileException(int cause, LONG lOsError = -1, const char* lpszFileName = nullptr); +void AfxAbort(); + +#define TRY try +#define CATCH(class, e) catch (class * e) +#define AND_CATCH(class, e) catch (class * e) +#define END_CATCH +#define CATCH_ALL(e) catch (CException * e) +#define AND_CATCH_ALL(e) catch (CException * e) +#define END_CATCH_ALL +#define END_TRY +#define THROW(e) throw e +#define THROW_LAST() throw + +// --------------------------------------------------------------------------------------------- +// Files + +struct CFileStatus { + time_t m_ctime; + time_t m_mtime; + time_t m_atime; + ULONGLONG m_size; + BYTE m_attribute; + char m_szFullName[_MAX_PATH]; +}; + +class CFile : public CObject { + DECLARE_DYNAMIC(CFile) +public: + enum OpenFlags { + modeRead = 0x0000, + modeWrite = 0x0001, + modeReadWrite = 0x0002, + shareCompat = 0x0000, + shareExclusive = 0x0010, + shareDenyWrite = 0x0020, + shareDenyRead = 0x0030, + shareDenyNone = 0x0040, + modeNoInherit = 0x0080, + modeCreate = 0x1000, + modeNoTruncate = 0x2000, + typeText = 0x4000, + typeBinary = 0x8000, + osNoBuffer = 0x10000, + osWriteThrough = 0x20000, + osRandomAccess = 0x40000, + osSequentialScan = 0x80000 + }; + enum Attribute { normal = 0x00, readOnly = 0x01, hidden = 0x02, system = 0x04, volume = 0x08, directory = 0x10, archive = 0x20 }; + enum SeekPosition { begin = 0x0, current = 0x1, end = 0x2 }; + static const HANDLE hFileNull; + + CFile(); + explicit CFile(HANDLE hFile); + CFile(const char* lpszFileName, UINT nOpenFlags); + ~CFile() override; + + virtual BOOL Open(const char* lpszFileName, UINT nOpenFlags, CFileException* pError = nullptr); + virtual void Close(); + virtual void Abort(); + virtual UINT Read(void* lpBuf, UINT nCount); + virtual void Write(const void* lpBuf, UINT nCount); + virtual ULONGLONG Seek(LONGLONG lOff, UINT nFrom); + void SeekToBegin() { Seek(0, begin); } + ULONGLONG SeekToEnd() { return Seek(0, end); } + virtual ULONGLONG GetLength() const; + virtual void SetLength(ULONGLONG dwNewLen); + virtual ULONGLONG GetPosition() const; + virtual void Flush(); + virtual CString GetFileName() const; + virtual CString GetFileTitle() const; + virtual CString GetFilePath() const { return m_strFileName; } + virtual void SetFilePath(const char* lpszNewName) { m_strFileName = lpszNewName; } + BOOL GetStatus(CFileStatus& rStatus) const; + static BOOL GetStatus(const char* lpszFileName, CFileStatus& rStatus); + static void SetStatus(const char* lpszFileName, const CFileStatus& status); + static void Rename(const char* lpszOldName, const char* lpszNewName); + static void Remove(const char* lpszFileName); + virtual void LockRange(ULONGLONG, ULONGLONG) {} + virtual void UnlockRange(ULONGLONG, ULONGLONG) {} + virtual CFile* Duplicate() const; + operator HANDLE() const { return m_hFile; } + + HANDLE m_hFile; + BOOL m_bCloseOnDelete; + CString m_strFileName; + +protected: + int m_fd; +}; + +class CStdioFile : public CFile { + DECLARE_DYNAMIC(CStdioFile) +public: + CStdioFile(); + explicit CStdioFile(FILE* pOpenStream); + CStdioFile(const char* lpszFileName, UINT nOpenFlags); + ~CStdioFile() override; + + BOOL Open(const char* lpszFileName, UINT nOpenFlags, CFileException* pError = nullptr) override; + void Close() override; + void Abort() override; + UINT Read(void* lpBuf, UINT nCount) override; + void Write(const void* lpBuf, UINT nCount) override; + ULONGLONG Seek(LONGLONG lOff, UINT nFrom) override; + ULONGLONG GetLength() const override; + ULONGLONG GetPosition() const override; + void Flush() override; + virtual void WriteString(const char* lpsz); + virtual char* ReadString(char* lpsz, UINT nMax); + virtual BOOL ReadString(CString& rString); + + FILE* m_pStream; + +private: + bool m_text; +}; + +class CMemFile : public CFile { + DECLARE_DYNAMIC(CMemFile) +public: + explicit CMemFile(UINT nGrowBytes = 1024); + CMemFile(BYTE* lpBuffer, UINT nBufferSize, UINT nGrowBytes = 0); + ~CMemFile() override; + void Attach(BYTE* lpBuffer, UINT nBufferSize, UINT nGrowBytes = 0); + BYTE* Detach(); + BOOL Open(const char*, UINT, CFileException* = nullptr) override { return FALSE; } + void Close() override; + void Abort() override { Close(); } + UINT Read(void* lpBuf, UINT nCount) override; + void Write(const void* lpBuf, UINT nCount) override; + ULONGLONG Seek(LONGLONG lOff, UINT nFrom) override; + ULONGLONG GetLength() const override { return m_nFileSize; } + void SetLength(ULONGLONG dwNewLen) override; + ULONGLONG GetPosition() const override { return m_nPosition; } + void Flush() override {} + +private: + void Grow(size_t size); + BYTE* m_lpBuffer; + size_t m_nBufferSize; + size_t m_nFileSize; + size_t m_nPosition; + UINT m_nGrowBytes; + bool m_bAutoDelete; +}; + +class CFileFind : public CObject { + DECLARE_DYNAMIC(CFileFind) +public: + CFileFind(); + ~CFileFind() override; + virtual BOOL FindFile(const char* pstrName = nullptr, DWORD dwUnused = 0); + virtual BOOL FindNextFile(); + void Close(); + ULONGLONG GetLength() const; + CString GetFileName() const; + CString GetFilePath() const; + CString GetFileTitle() const; + CString GetFileURL() const; + CString GetRoot() const; + BOOL IsDots() const; + BOOL IsDirectory() const; + BOOL IsReadOnly() const { return FALSE; } + BOOL IsHidden() const; + BOOL IsArchived() const { return FALSE; } + BOOL IsNormal() const { return !IsDirectory(); } + BOOL MatchesMask(DWORD dwMask) const; + +private: + struct Impl; + Impl* m_impl; +}; + +// --------------------------------------------------------------------------------------------- +// Archives + +class CDocument; + +class CArchive { +public: + enum Mode { store = 0, load = 1, bNoFlushOnDelete = 2, bNoByteSwap = 4 }; + CArchive(CFile* pFile, UINT nMode, int nBufSize = 4096, void* lpBuf = nullptr); + ~CArchive(); + + BOOL IsLoading() const { return (m_nMode & load) != 0; } + BOOL IsStoring() const { return (m_nMode & load) == 0; } + BOOL IsByteSwapping() const { return FALSE; } + BOOL IsBufferEmpty() const; + CFile* GetFile() const { return m_pFile; } + UINT GetObjectSchema() { return m_nObjectSchema; } + void SetObjectSchema(UINT nSchema) { m_nObjectSchema = nSchema; } + void Close(); + void Abort() { m_pFile = nullptr; } + void Flush() {} + UINT Read(void* lpBuf, UINT nMax); + void Write(const void* lpBuf, UINT nMax); + BOOL ReadString(CString& rString); + char* ReadString(char* lpsz, UINT nMax); + void WriteString(const char* lpsz); + UINT ReadCount(); + void WriteCount(UINT dwCount); + CObject* ReadObject(const CRuntimeClass* pClass); + void WriteObject(const CObject* pOb); + CRuntimeClass* ReadClass(const CRuntimeClass* pClassRefRequested = nullptr, UINT* pSchema = nullptr, + DWORD* pObTag = nullptr); + void WriteClass(const CRuntimeClass* pClassRef); + void SerializeClass(const CRuntimeClass* pClassRef); + + CArchive& operator<<(BYTE by) { Write(&by, sizeof by); return *this; } + CArchive& operator<<(WORD w) { Write(&w, sizeof w); return *this; } + CArchive& operator<<(LONG l) { Write(&l, sizeof l); return *this; } + CArchive& operator<<(DWORD dw) { Write(&dw, sizeof dw); return *this; } + CArchive& operator<<(float f) { Write(&f, sizeof f); return *this; } + CArchive& operator<<(double d) { Write(&d, sizeof d); return *this; } + CArchive& operator<<(LONGLONG d) { Write(&d, sizeof d); return *this; } + CArchive& operator<<(ULONGLONG d) { Write(&d, sizeof d); return *this; } + CArchive& operator<<(short w) { return *this << static_cast(w); } + CArchive& operator<<(char ch) { return *this << static_cast(ch); } + CArchive& operator<<(bool b) { return *this << static_cast(b ? 1 : 0); } + CArchive& operator<<(long l) { return *this << static_cast(l); } + CArchive& operator<<(unsigned long l) { return *this << static_cast(l); } + CArchive& operator<<(const CString& s); + CArchive& operator<<(const CObject* pOb) { WriteObject(pOb); return *this; } + + CArchive& operator>>(BYTE& by) { Read(&by, sizeof by); return *this; } + CArchive& operator>>(WORD& w) { Read(&w, sizeof w); return *this; } + CArchive& operator>>(DWORD& dw) { Read(&dw, sizeof dw); return *this; } + CArchive& operator>>(LONG& l) { Read(&l, sizeof l); return *this; } + CArchive& operator>>(float& f) { Read(&f, sizeof f); return *this; } + CArchive& operator>>(double& d) { Read(&d, sizeof d); return *this; } + CArchive& operator>>(LONGLONG& d) { Read(&d, sizeof d); return *this; } + CArchive& operator>>(ULONGLONG& d) { Read(&d, sizeof d); return *this; } + CArchive& operator>>(short& w) { WORD v; *this >> v; w = static_cast(v); return *this; } + CArchive& operator>>(char& ch) { BYTE v; *this >> v; ch = static_cast(v); return *this; } + CArchive& operator>>(bool& b) { BYTE v; *this >> v; b = v != 0; return *this; } + CArchive& operator>>(long& l) { LONG v; *this >> v; l = v; return *this; } + CArchive& operator>>(unsigned long& l) { DWORD v; *this >> v; l = v; return *this; } + CArchive& operator>>(CString& s); + CArchive& operator>>(CObject*& pOb) { pOb = ReadObject(nullptr); return *this; } + + CDocument* m_pDocument; + CString m_strFileName; + +private: + CFile* m_pFile; + UINT m_nMode; + UINT m_nObjectSchema; + std::vector m_loadClasses; + std::vector m_loadObjects; + std::map m_storeClasses; + std::map m_storeObjects; +}; + +// --------------------------------------------------------------------------------------------- +// Time + +class CTimeSpan { +public: + CTimeSpan() : m_span(0) {} + CTimeSpan(time_t span) : m_span(span) {} + CTimeSpan(LONG lDays, int nHours, int nMins, int nSecs) + : m_span(((static_cast(lDays) * 24 + nHours) * 60 + nMins) * 60 + nSecs) {} + LONGLONG GetDays() const { return m_span / (24 * 3600); } + LONGLONG GetTotalHours() const { return m_span / 3600; } + LONG GetHours() const { return static_cast(GetTotalHours() - GetDays() * 24); } + LONGLONG GetTotalMinutes() const { return m_span / 60; } + LONG GetMinutes() const { return static_cast(GetTotalMinutes() - GetTotalHours() * 60); } + LONGLONG GetTotalSeconds() const { return m_span; } + LONG GetSeconds() const { return static_cast(GetTotalSeconds() - GetTotalMinutes() * 60); } + time_t GetTimeSpan() const { return m_span; } + CString Format(const char* pFormat) const; + CTimeSpan operator+(CTimeSpan s) const { return CTimeSpan(m_span + s.m_span); } + CTimeSpan operator-(CTimeSpan s) const { return CTimeSpan(m_span - s.m_span); } + CTimeSpan& operator+=(CTimeSpan s) { m_span += s.m_span; return *this; } + CTimeSpan& operator-=(CTimeSpan s) { m_span -= s.m_span; return *this; } + bool operator==(CTimeSpan s) const { return m_span == s.m_span; } + bool operator!=(CTimeSpan s) const { return m_span != s.m_span; } + bool operator<(CTimeSpan s) const { return m_span < s.m_span; } + bool operator>(CTimeSpan s) const { return m_span > s.m_span; } + bool operator<=(CTimeSpan s) const { return m_span <= s.m_span; } + bool operator>=(CTimeSpan s) const { return m_span >= s.m_span; } + +private: + time_t m_span; +}; + +class CTime { +public: + CTime() : m_time(0) {} + CTime(time_t t) : m_time(t) {} + CTime(int nYear, int nMonth, int nDay, int nHour, int nMin, int nSec, int nDST = -1); + CTime(const SYSTEMTIME& st, int nDST = -1); + CTime(const FILETIME& ft, int nDST = -1); + static CTime GetCurrentTime(); + time_t GetTime() const { return m_time; } + struct tm* GetLocalTm(struct tm* ptm) const; + struct tm* GetGmtTm(struct tm* ptm) const; + BOOL GetAsSystemTime(SYSTEMTIME& st) const; + int GetYear() const; + int GetMonth() const; + int GetDay() const; + int GetHour() const; + int GetMinute() const; + int GetSecond() const; + int GetDayOfWeek() const; + CString Format(const char* pFormat) const; + CString Format(UINT nFormatID) const; + CString FormatGmt(const char* pFormat) const; + CTimeSpan operator-(CTime t) const { return CTimeSpan(m_time - t.m_time); } + CTime operator-(CTimeSpan s) const { return CTime(m_time - s.GetTimeSpan()); } + CTime operator+(CTimeSpan s) const { return CTime(m_time + s.GetTimeSpan()); } + CTime& operator+=(CTimeSpan s) { m_time += s.GetTimeSpan(); return *this; } + CTime& operator-=(CTimeSpan s) { m_time -= s.GetTimeSpan(); return *this; } + bool operator==(CTime t) const { return m_time == t.m_time; } + bool operator!=(CTime t) const { return m_time != t.m_time; } + bool operator<(CTime t) const { return m_time < t.m_time; } + bool operator>(CTime t) const { return m_time > t.m_time; } + bool operator<=(CTime t) const { return m_time <= t.m_time; } + bool operator>=(CTime t) const { return m_time >= t.m_time; } + +private: + time_t m_time; +}; + +// File APIs accept Windows-style paths; see mfcwx::NativePath. +extern "C" FILE* mfcwx_fopen(const char* name, const char* mode); +extern "C" int mfcwx_rename(const char* from, const char* to); +extern "C" int mfcwx_unlink(const char* name); +inline int mfcwx_remove(const char* name) { return mfcwx_unlink(name); } +namespace std { +using ::mfcwx_fopen; +using ::mfcwx_remove; +using ::mfcwx_rename; +template +It mfcwx_remove(It first, It last, const T& value) { return (std::remove)(first, last, value); } +} +#define fopen mfcwx_fopen +#define rename mfcwx_rename +#define remove(...) mfcwx_remove(__VA_ARGS__) + +#include "mfcwx/geometry.h" +#include "mfcwx/collections.h" +#include "mfcwx/fstreamcompat.h" + +#endif // __cplusplus diff --git a/port/mfcwx/include/afxadv.h b/port/mfcwx/include/afxadv.h new file mode 100644 index 000000000..6e7e9031a --- /dev/null +++ b/port/mfcwx/include/afxadv.h @@ -0,0 +1,2 @@ +#pragma once +#include "afxwin.h" diff --git a/port/mfcwx/include/afxcmn.h b/port/mfcwx/include/afxcmn.h new file mode 100644 index 000000000..7c7375fe6 --- /dev/null +++ b/port/mfcwx/include/afxcmn.h @@ -0,0 +1,319 @@ +#pragma once +#define __AFXCMN_H__ + +// MFC common controls. Like the basic controls these classes carry no data and operate on m_hWnd. + +#include "afxwin.h" + +class CImageList : public CObject { + DECLARE_DYNCREATE(CImageList) +public: + CImageList(); + ~CImageList() override; + HIMAGELIST m_hImageList; + operator HIMAGELIST() const { return AfxIsNullThis(this) ? nullptr : m_hImageList; } + BOOL Create(int cx, int cy, UINT nFlags, int nInitial, int nGrow); + BOOL Create(UINT nBitmapID, int cx, int nGrow, COLORREF crMask); + BOOL DeleteImageList(); + int Add(CBitmap* pbmImage, CBitmap* pbmMask); + int Add(CBitmap* pbmImage, COLORREF crMask); + int Add(HICON hIcon); + int GetImageCount() const; + BOOL Draw(CDC* pDC, int nImage, POINT pt, UINT nStyle); +}; +#define ILC_MASK 0x0001 +#define ILC_COLOR 0x0000 +#define ILC_COLOR4 0x0004 +#define ILC_COLOR8 0x0008 +#define ILC_COLOR16 0x0010 +#define ILC_COLOR24 0x0018 +#define ILC_COLOR32 0x0020 +#define ILD_NORMAL 0x0000 +#define ILD_TRANSPARENT 0x0001 + +class CProgressCtrl : public CWnd { + DECLARE_DYNAMIC(CProgressCtrl) +public: + CProgressCtrl() = default; + BOOL Create(DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID); + void SetRange(short nLower, short nUpper); + void SetRange32(int nLower, int nUpper); + void GetRange(int& nLower, int& nUpper) const; + int GetPos() const; + int SetPos(int nPos); + int OffsetPos(int nPos); + int SetStep(int nStep); + int StepIt(); + COLORREF SetBkColor(COLORREF clrNew); + COLORREF SetBarColor(COLORREF clrBar); + BOOL SetMarquee(BOOL fMarqueeMode, int nInterval); +}; + +class CSliderCtrl : public CWnd { + DECLARE_DYNAMIC(CSliderCtrl) +public: + CSliderCtrl() = default; + BOOL Create(DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID); + int GetLineSize() const; + int SetLineSize(int nSize); + int GetPageSize() const; + int SetPageSize(int nSize); + int GetRangeMax() const; + int GetRangeMin() const; + void GetRange(int& nMin, int& nMax) const; + void SetRangeMin(int nMin, BOOL bRedraw = FALSE); + void SetRangeMax(int nMax, BOOL bRedraw = FALSE); + void SetRange(int nMin, int nMax, BOOL bRedraw = FALSE); + void GetSelection(int& nMin, int& nMax) const; + void SetSelection(int nMin, int nMax); + void GetChannelRect(LPRECT lprc) const; + void GetThumbRect(LPRECT lprc) const; + int GetPos() const; + void SetPos(int nPos); + UINT GetNumTics() const; + int GetTic(int nTic) const; + BOOL SetTic(int nTic); + void SetTicFreq(int nFreq); + void ClearSel(BOOL bRedraw = FALSE); + void ClearTics(BOOL bRedraw = FALSE); +}; + +struct UDACCEL { + UINT nSec; + UINT nInc; +}; + +class CSpinButtonCtrl : public CWnd { + DECLARE_DYNAMIC(CSpinButtonCtrl) +public: + CSpinButtonCtrl() = default; + BOOL Create(DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID); + BOOL SetAccel(int nAccel, UDACCEL* pAccel); + UINT GetAccel(int nAccel, UDACCEL* pAccel) const; + int SetBase(int nBase); + UINT GetBase() const; + CWnd* SetBuddy(CWnd* pWndBuddy); + CWnd* GetBuddy() const; + int SetPos(int nPos); + int GetPos() const; + int SetPos32(int nPos) { return SetPos(nPos); } + int GetPos32(BOOL* lpbError = nullptr) const; + void SetRange(short nLower, short nUpper); + void SetRange32(int nLower, int nUpper); + DWORD GetRange() const; + void GetRange(int& lower, int& upper) const; + void GetRange32(int& lower, int& upper) const { GetRange(lower, upper); } +}; + +class CHeaderCtrl : public CWnd { + DECLARE_DYNAMIC(CHeaderCtrl) +public: + CHeaderCtrl() = default; + int GetItemCount() const; +}; + +class CListCtrl : public CWnd { + DECLARE_DYNAMIC(CListCtrl) +public: + CListCtrl() = default; + BOOL Create(DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID); + COLORREF GetBkColor() const; + BOOL SetBkColor(COLORREF cr); + COLORREF GetTextColor() const; + BOOL SetTextColor(COLORREF cr); + COLORREF GetTextBkColor() const; + BOOL SetTextBkColor(COLORREF cr); + CImageList* GetImageList(int nImageList) const; + CImageList* SetImageList(CImageList* pImageList, int nImageListType); + int GetItemCount() const; + BOOL GetItem(LVITEM* pItem) const; + BOOL SetItem(const LVITEM* pItem); + BOOL SetItem(int nItem, int nSubItem, UINT nMask, const char* lpszItem, int nImage, UINT nState, UINT nStateMask, + LPARAM lParam); + UINT GetCallbackMask() const { return 0; } + BOOL SetCallbackMask(UINT) { return TRUE; } + int GetNextItem(int nItem, int nFlags) const; + POSITION GetFirstSelectedItemPosition() const; + int GetNextSelectedItem(POSITION& pos) const; + BOOL GetItemRect(int nItem, LPRECT lpRect, UINT nCode) const; + BOOL GetSubItemRect(int iItem, int iSubItem, int nArea, CRect& ref); + BOOL SetItemPosition(int, POINT) { return TRUE; } + BOOL GetItemPosition(int nItem, LPPOINT lpPoint) const; + int GetStringWidth(const char* lpsz) const; + CEdit* GetEditControl() const { return nullptr; } + BOOL GetColumn(int nCol, LVCOLUMN* pColumn) const; + BOOL SetColumn(int nCol, const LVCOLUMN* pColumn); + int GetColumnWidth(int nCol) const; + BOOL SetColumnWidth(int nCol, int cx); + BOOL GetViewRect(LPRECT lpRect) const; + int GetTopIndex() const; + int GetCountPerPage() const; + BOOL GetOrigin(LPPOINT lpPoint) const; + BOOL SetItemState(int nItem, LVITEM* pItem); + BOOL SetItemState(int nItem, UINT nState, UINT nMask); + UINT GetItemState(int nItem, UINT nMask) const; + CString GetItemText(int nItem, int nSubItem) const; + int GetItemText(int nItem, int nSubItem, char* lpszText, int nLen) const; + BOOL SetItemText(int nItem, int nSubItem, const char* lpszText); + void SetItemCount(int nItems); + BOOL SetItemData(int nItem, DWORD_PTR dwData); + DWORD_PTR GetItemData(int nItem) const; + UINT GetSelectedCount() const; + int GetSelectionMark() const; + int SetSelectionMark(int iIndex); + DWORD GetExtendedStyle() const; + DWORD SetExtendedStyle(DWORD dwNewStyle); + CHeaderCtrl* GetHeaderCtrl() const; + int GetHotItem() const { return -1; } + int InsertItem(const LVITEM* pItem); + int InsertItem(int nItem, const char* lpszItem); + int InsertItem(int nItem, const char* lpszItem, int nImage); + int InsertItem(UINT nMask, int nItem, const char* lpszItem, UINT nState, UINT nStateMask, int nImage, + LPARAM lParam); + BOOL DeleteItem(int nItem); + BOOL DeleteAllItems(); + int FindItem(LVFINDINFO* pFindInfo, int nStart = -1) const; + int HitTest(LVHITTESTINFO* pHitTestInfo) const; + int HitTest(CPoint pt, UINT* pFlags = nullptr) const; + int SubItemHitTest(LVHITTESTINFO* pInfo); + BOOL EnsureVisible(int nItem, BOOL bPartialOK); + BOOL Scroll(CSize size); + BOOL RedrawItems(int nFirst, int nLast); + BOOL Arrange(UINT) { return TRUE; } + CEdit* EditLabel(int) { return nullptr; } + int InsertColumn(int nCol, const LVCOLUMN* pColumn); + int InsertColumn(int nCol, const char* lpszColumnHeading, int nFormat = LVCFMT_LEFT, int nWidth = -1, + int nSubItem = -1); + BOOL DeleteColumn(int nCol); + BOOL Update(int nItem); + BOOL SortItems(int (*pfnCompare)(LPARAM, LPARAM, LPARAM), DWORD_PTR dwData); + BOOL GetCheck(int nItem) const; + BOOL SetCheck(int nItem, BOOL fCheck = TRUE); + virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct); +}; + +class CTreeCtrl : public CWnd { + DECLARE_DYNAMIC(CTreeCtrl) +public: + CTreeCtrl() = default; +}; + +class CTabCtrl : public CWnd { + DECLARE_DYNAMIC(CTabCtrl) +public: + CTabCtrl() = default; + BOOL Create(DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID); + CImageList* GetImageList() const { return nullptr; } + CImageList* SetImageList(CImageList*) { return nullptr; } + int GetItemCount() const; + BOOL GetItem(int nItem, TCITEM* pTabCtrlItem) const; + BOOL SetItem(int nItem, TCITEM* pTabCtrlItem); + BOOL GetItemRect(int nItem, LPRECT lpRect) const; + int GetCurSel() const; + int SetCurSel(int nItem); + void SetCurFocus(int nItem); + CSize SetItemSize(CSize size); + void SetPadding(CSize) {} + int GetRowCount() const { return 1; } + int GetCurFocus() const { return GetCurSel(); } + LONG InsertItem(int nItem, TCITEM* pTabCtrlItem); + LONG InsertItem(int nItem, const char* lpszItem); + LONG InsertItem(int nItem, const char* lpszItem, int nImage); + BOOL DeleteItem(int nItem); + BOOL DeleteAllItems(); + void AdjustRect(BOOL bLarger, LPRECT lpRect); + DWORD GetExtendedStyle() { return 0; } + DWORD SetExtendedStyle(DWORD, DWORD = 0) { return 0; } + int HitTest(void*) const { return -1; } +}; + +#define LPSTR_TEXTCALLBACK ((LPSTR)-1L) + +class CToolTipCtrl : public CWnd { + DECLARE_DYNAMIC(CToolTipCtrl) +public: + CToolTipCtrl() = default; + BOOL Create(CWnd* pParentWnd, DWORD dwStyle = 0); + BOOL AddTool(CWnd* pWnd, UINT nIDText, LPCRECT lpRectTool = nullptr, UINT_PTR nIDTool = 0); + BOOL AddTool(CWnd* pWnd, const char* lpszText = LPSTR_TEXTCALLBACK, LPCRECT lpRectTool = nullptr, + UINT_PTR nIDTool = 0); + void DelTool(CWnd* pWnd, UINT_PTR nIDTool = 0); + void UpdateTipText(const char* lpszText, CWnd* pWnd, UINT_PTR nIDTool = 0); + void UpdateTipText(UINT nIDText, CWnd* pWnd, UINT_PTR nIDTool = 0); + void RelayEvent(LPMSG lpMsg); + void Activate(BOOL bActivate); + void SetDelayTime(UINT nDelay); + void SetDelayTime(DWORD dwDuration, int iTime); + int SetMaxTipWidth(int iWidth); + void SetTipBkColor(COLORREF) {} + void SetTipTextColor(COLORREF) {} + int GetToolCount() const; + void Pop() {} + void Update() {} +}; +#define TTDT_AUTOMATIC 0 +#define TTDT_RESHOW 1 +#define TTDT_AUTOPOP 2 +#define TTDT_INITIAL 3 +#define TTF_IDISHWND 0x0001 +#define TTF_CENTERTIP 0x0002 +#define TTF_SUBCLASS 0x0010 + +class CRichEditCtrl : public CWnd { + DECLARE_DYNAMIC(CRichEditCtrl) +public: + CRichEditCtrl() = default; + BOOL Create(DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID); + BOOL CanUndo() const; + BOOL CanRedo() const; + int GetLineCount() const; + BOOL GetModify() const; + void SetModify(BOOL bModified = TRUE); + void GetRect(LPRECT lpRect) const; + CPoint GetCharPos(long lChar) const; + void SetOptions(WORD, DWORD) {} + int GetLine(int nIndex, char* lpszBuffer) const; + int GetLine(int nIndex, char* lpszBuffer, int nMaxLength) const; + BOOL CanPaste(UINT nFormat = 0) const; + void GetSel(long& nStartChar, long& nEndChar) const; + void GetSel(CHARRANGE& cr) const; + void LimitText(long nChars = 0); + long LineFromChar(long nIndex) const; + void SetSel(long nStartChar, long nEndChar); + void SetSel(CHARRANGE& cr); + DWORD GetDefaultCharFormat(CHARFORMAT& cf) const; + DWORD GetSelectionCharFormat(CHARFORMAT& cf) const; + long GetEventMask() const { return 0; } + long GetLimitText() const; + DWORD GetParaFormat(PARAFORMAT& pf) const; + long GetSelText(char* lpBuf) const; + CString GetSelText() const; + WORD GetSelectionType() const; + COLORREF SetBackgroundColor(BOOL bSysColor, COLORREF cr); + BOOL SetDefaultCharFormat(CHARFORMAT& cf); + BOOL SetSelectionCharFormat(CHARFORMAT& cf); + BOOL SetWordCharFormat(CHARFORMAT& cf); + DWORD SetEventMask(DWORD dwEventMask) { return dwEventMask; } + BOOL SetParaFormat(PARAFORMAT& pf); + BOOL SetReadOnly(BOOL bReadOnly = TRUE); + void SetTargetDevice(HDC, long) {} + long GetTextLength() const; + long GetTextLengthEx(DWORD, UINT = 1200) const { return GetTextLength(); } + BOOL SetAutoURLDetect(BOOL = TRUE) { return TRUE; } + void EmptyUndoBuffer(); + int LineIndex(int nLine = -1) const; + int LineLength(int nLine = -1) const; + void LineScroll(int nLines, int nChars = 0); + void ReplaceSel(const char* lpszNewText, BOOL bCanUndo = FALSE); + long StreamIn(int nFormat, EDITSTREAM& es); + long StreamOut(int nFormat, EDITSTREAM& es); + long FindText(DWORD dwFlags, FINDTEXTEX* pFindText) const; + BOOL Undo(); + BOOL Redo(); + void Clear(); + void Copy(); + void Cut(); + void Paste(); + int GetFirstVisibleLine() const; + void HideSelection(BOOL, BOOL) {} +}; diff --git a/port/mfcwx/include/afxcoll.h b/port/mfcwx/include/afxcoll.h new file mode 100644 index 000000000..05e081c1b --- /dev/null +++ b/port/mfcwx/include/afxcoll.h @@ -0,0 +1,2 @@ +#pragma once +#include "afx.h" diff --git a/port/mfcwx/include/afxconv.h b/port/mfcwx/include/afxconv.h new file mode 100644 index 000000000..6e7e9031a --- /dev/null +++ b/port/mfcwx/include/afxconv.h @@ -0,0 +1,2 @@ +#pragma once +#include "afxwin.h" diff --git a/port/mfcwx/include/afxcview.h b/port/mfcwx/include/afxcview.h new file mode 100644 index 000000000..6e7e9031a --- /dev/null +++ b/port/mfcwx/include/afxcview.h @@ -0,0 +1,2 @@ +#pragma once +#include "afxwin.h" diff --git a/port/mfcwx/include/afxdb.h b/port/mfcwx/include/afxdb.h new file mode 100644 index 000000000..6e7e9031a --- /dev/null +++ b/port/mfcwx/include/afxdb.h @@ -0,0 +1,2 @@ +#pragma once +#include "afxwin.h" diff --git a/port/mfcwx/include/afxdhtml.h b/port/mfcwx/include/afxdhtml.h new file mode 100644 index 000000000..6e7e9031a --- /dev/null +++ b/port/mfcwx/include/afxdhtml.h @@ -0,0 +1,2 @@ +#pragma once +#include "afxwin.h" diff --git a/port/mfcwx/include/afxdisp.h b/port/mfcwx/include/afxdisp.h new file mode 100644 index 000000000..17582187c --- /dev/null +++ b/port/mfcwx/include/afxdisp.h @@ -0,0 +1,3 @@ +#pragma once +#define __AFXDISP_H__ +#include "afxwin.h" diff --git a/port/mfcwx/include/afxdlgs.h b/port/mfcwx/include/afxdlgs.h new file mode 100644 index 000000000..61a643c8f --- /dev/null +++ b/port/mfcwx/include/afxdlgs.h @@ -0,0 +1,192 @@ +#pragma once +#define __AFXDLGS_H__ + +#include "afxwin.h" + +#include + +class CCommonDialog : public CDialog { + DECLARE_DYNAMIC(CCommonDialog) +public: + explicit CCommonDialog(CWnd* pParentWnd) : CDialog(static_cast(0), pParentWnd) {} +}; + +class CFileDialog : public CCommonDialog { + DECLARE_DYNAMIC(CFileDialog) +public: + explicit CFileDialog(BOOL bOpenFileDialog, const char* lpszDefExt = nullptr, const char* lpszFileName = nullptr, + DWORD dwFlags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, const char* lpszFilter = nullptr, + CWnd* pParentWnd = nullptr, DWORD dwSize = 0, BOOL bVistaStyle = TRUE); + INT_PTR DoModal() override; + CString GetPathName() const; + CString GetFileName() const; + CString GetFileExt() const; + CString GetFileTitle() const; + CString GetFolderPath() const; + BOOL GetReadOnlyPref() const { return FALSE; } + POSITION GetStartPosition() const; + CString GetNextPathName(POSITION& pos) const; + OPENFILENAME& GetOFN() { return m_ofn; } + + OPENFILENAME m_ofn; + BOOL m_bOpenFileDialog; + CString m_strFilter; + CString m_strDefExt; + CString m_strTitle; + CString m_strInitialDir; + char m_szFileName[4096]; + char m_szFileTitle[260]; + std::vector m_paths; +}; + +class CColorDialog : public CCommonDialog { + DECLARE_DYNAMIC(CColorDialog) +public: + explicit CColorDialog(COLORREF clrInit = 0, DWORD dwFlags = 0, CWnd* pParentWnd = nullptr); + INT_PTR DoModal() override; + COLORREF GetColor() const { return m_cc.rgbResult; } + void SetCurrentColor(COLORREF clr) { m_cc.rgbResult = clr; } + CHOOSECOLOR m_cc; +}; + +typedef struct tagCHOOSEFONTA { + DWORD lStructSize; + HWND hwndOwner; + HDC hDC; + LPLOGFONT lpLogFont; + INT iPointSize; + DWORD Flags; + COLORREF rgbColors; + LPARAM lCustData; + LPVOID lpfnHook; + LPCSTR lpTemplateName; + HINSTANCE hInstance; + LPSTR lpszStyle; + WORD nFontType; + INT nSizeMin; + INT nSizeMax; +} CHOOSEFONT, *LPCHOOSEFONT; +#define CF_SCREENFONTS 0x00000001 +#define CF_PRINTERFONTS 0x00000002 +#define CF_BOTH (CF_SCREENFONTS | CF_PRINTERFONTS) +#define CF_EFFECTS 0x00000100L +#define CF_INITTOLOGFONTSTRUCT 0x00000040L +#define CF_FIXEDPITCHONLY 0x00004000L +#define CF_NOVERTFONTS 0x01000000L + +class CFontDialog : public CCommonDialog { + DECLARE_DYNAMIC(CFontDialog) +public: + explicit CFontDialog(LPLOGFONT lplfInitial = nullptr, DWORD dwFlags = CF_EFFECTS | CF_SCREENFONTS, + CDC* pdcPrinter = nullptr, CWnd* pParentWnd = nullptr); + INT_PTR DoModal() override; + void GetCurrentFont(LPLOGFONT lplf); + CString GetFaceName() const; + int GetSize() const; + COLORREF GetColor() const { return m_cf.rgbColors; } + int GetWeight() const { return m_lf.lfWeight; } + BOOL IsItalic() const { return m_lf.lfItalic; } + BOOL IsUnderline() const { return m_lf.lfUnderline; } + BOOL IsStrikeOut() const { return m_lf.lfStrikeOut; } + BOOL IsBold() const { return m_lf.lfWeight >= FW_SEMIBOLD; } + CHOOSEFONT m_cf; + LOGFONT m_lf; +}; + +typedef struct tagPSDA { + DWORD lStructSize; + HWND hwndOwner; + HGLOBAL hDevMode, hDevNames; + DWORD Flags; + POINT ptPaperSize; + RECT rtMinMargin, rtMargin; +} PAGESETUPDLG; +#define PSD_INTHOUSANDTHSOFINCHES 0x00000004 +#define PSD_INHUNDREDTHSOFMILLIMETERS 0x00000008 +#define PSD_MARGINS 0x00000002 + +class CPageSetupDialog : public CCommonDialog { + DECLARE_DYNAMIC(CPageSetupDialog) +public: + explicit CPageSetupDialog(DWORD dwFlags = 0, CWnd* pParentWnd = nullptr); + INT_PTR DoModal() override; + CSize GetPaperSize() const; + void GetMargins(LPRECT lpRectMargins, LPRECT lpRectMinMargins) const; + PAGESETUPDLG m_psd; +}; + +class CPrintDialog : public CCommonDialog { + DECLARE_DYNAMIC(CPrintDialog) +public: + explicit CPrintDialog(BOOL bPrintSetupOnly, DWORD dwFlags = 0, CWnd* pParentWnd = nullptr); + INT_PTR DoModal() override; + PRINTDLG m_pd; + HDC GetPrinterDC() const { return nullptr; } + HDC CreatePrinterDC() { return nullptr; } + CString GetDeviceName() const { return CString(); } +}; + +#define FR_DOWN 0x00000001 +#define FR_WHOLEWORD 0x00000002 +#define FR_MATCHCASE 0x00000004 +#define FR_FINDNEXT 0x00000008 +#define FR_REPLACE 0x00000010 +#define FR_REPLACEALL 0x00000020 +#define FR_DIALOGTERM 0x00000040 +#define FINDMSGSTRING "commdlg_FindReplace" + +class CFindReplaceDialog : public CCommonDialog { + DECLARE_DYNAMIC(CFindReplaceDialog) +public: + CFindReplaceDialog(); + BOOL Create(BOOL bFindDialogOnly, const char* lpszFindWhat, const char* lpszReplaceWith = nullptr, + DWORD dwFlags = FR_DOWN, CWnd* pParentWnd = nullptr); + static CFindReplaceDialog* GetNotifier(LPARAM lParam); + CString GetReplaceString() const { return m_replace; } + CString GetFindString() const { return m_find; } + BOOL SearchDown() const { return (m_flags & FR_DOWN) != 0; } + BOOL FindNext() const { return (m_flags & FR_FINDNEXT) != 0; } + BOOL MatchCase() const { return (m_flags & FR_MATCHCASE) != 0; } + BOOL MatchWholeWord() const { return (m_flags & FR_WHOLEWORD) != 0; } + BOOL ReplaceCurrent() const { return (m_flags & FR_REPLACE) != 0; } + BOOL ReplaceAll() const { return (m_flags & FR_REPLACEALL) != 0; } + BOOL IsTerminating() const { return (m_flags & FR_DIALOGTERM) != 0; } + CString m_find; + CString m_replace; + DWORD m_flags; +}; + +class CPropertyPage : public CDialog { + DECLARE_DYNAMIC(CPropertyPage) +public: + CPropertyPage(); + explicit CPropertyPage(UINT nIDTemplate, UINT nIDCaption = 0, DWORD dwSize = 0); + void SetModified(BOOL = TRUE) {} + virtual BOOL OnSetActive() { return TRUE; } + virtual BOOL OnKillActive() { return UpdateData(TRUE); } + virtual BOOL OnApply() { return TRUE; } + virtual void OnReset() {} + void OnOK() override {} + void OnCancel() override {} + CString m_strCaption; +}; + +class CPropertySheet : public CWnd { + DECLARE_DYNAMIC(CPropertySheet) +public: + CPropertySheet(); + explicit CPropertySheet(UINT nIDCaption, CWnd* pParentWnd = nullptr, UINT iSelectPage = 0); + explicit CPropertySheet(const char* pszCaption, CWnd* pParentWnd = nullptr, UINT iSelectPage = 0); + void AddPage(CPropertyPage* pPage); + void RemovePage(CPropertyPage* pPage); + int GetPageCount() const { return static_cast(m_pages.size()); } + CPropertyPage* GetPage(int nPage) const { return m_pages[static_cast(nPage)]; } + int GetActiveIndex() const { return m_active; } + BOOL SetActivePage(int nPage); + void SetTitle(const char* lpszText, UINT = 0) { m_strCaption = lpszText; } + virtual INT_PTR DoModal(); + std::vector m_pages; + CString m_strCaption; + CWnd* m_pParentWnd; + int m_active; +}; diff --git a/port/mfcwx/include/afxdtctl.h b/port/mfcwx/include/afxdtctl.h new file mode 100644 index 000000000..cf6cec0e8 --- /dev/null +++ b/port/mfcwx/include/afxdtctl.h @@ -0,0 +1,3 @@ +#pragma once +#define __AFXDTCTL_H__ +#include "afxwin.h" diff --git a/port/mfcwx/include/afxext.h b/port/mfcwx/include/afxext.h new file mode 100644 index 000000000..7d76748d6 --- /dev/null +++ b/port/mfcwx/include/afxext.h @@ -0,0 +1,41 @@ +#pragma once +#define __AFXEXT_H__ + +#include "afxwin.h" +#include "afxdlgs.h" + +class CBitmapButton : public CButton { + DECLARE_DYNAMIC(CBitmapButton) +public: + CBitmapButton() = default; + CBitmapButton(const CBitmapButton&) = delete; + BOOL LoadBitmaps(const char* lpszBitmapResource, const char* lpszBitmapResourceSel = nullptr, + const char* lpszBitmapResourceFocus = nullptr, const char* lpszBitmapResourceDisabled = nullptr); + BOOL LoadBitmaps(UINT nIDBitmapResource, UINT nIDBitmapResourceSel = 0, UINT nIDBitmapResourceFocus = 0, + UINT nIDBitmapResourceDisabled = 0); + BOOL AutoLoad(UINT nID, CWnd* pParent); + void SizeToContent(); + void DrawItem(LPDRAWITEMSTRUCT lpDIS) override; + + CBitmap m_bitmap; + CBitmap m_bitmapSel; + CBitmap m_bitmapFocus; + CBitmap m_bitmapDisabled; +}; + +class CFormView : public CScrollView { + DECLARE_DYNAMIC(CFormView) +public: + explicit CFormView(UINT nIDTemplate); + explicit CFormView(const char* lpszTemplateName); + void OnDraw(CDC*) override {} + void OnInitialUpdate() override; + UINT m_nIDTemplate; +}; + +class CSplitterWnd : public CWnd { +public: + BOOL CreateStatic(CWnd*, int, int, DWORD = WS_CHILD | WS_VISIBLE, UINT = AFX_IDW_PANE_FIRST) { return FALSE; } + BOOL CreateView(int, int, CRuntimeClass*, SIZE, CCreateContext*) { return FALSE; } + CWnd* GetPane(int, int) const { return nullptr; } +}; diff --git a/port/mfcwx/include/afxinet.h b/port/mfcwx/include/afxinet.h new file mode 100644 index 000000000..6e7e9031a --- /dev/null +++ b/port/mfcwx/include/afxinet.h @@ -0,0 +1,2 @@ +#pragma once +#include "afxwin.h" diff --git a/port/mfcwx/include/afxmt.h b/port/mfcwx/include/afxmt.h new file mode 100644 index 000000000..bf98e059f --- /dev/null +++ b/port/mfcwx/include/afxmt.h @@ -0,0 +1,81 @@ +#pragma once +#define __AFXMT_H__ + +#include "afxwin.h" + +class CSyncObject : public CObject { + DECLARE_DYNAMIC(CSyncObject) +public: + explicit CSyncObject(const char* pstrName = nullptr); + ~CSyncObject() override; + virtual BOOL Lock(DWORD dwTimeout = INFINITE); + virtual BOOL Unlock() = 0; + virtual BOOL Unlock(LONG, LPLONG = nullptr) { return TRUE; } + operator HANDLE() const { return m_hObject; } + HANDLE m_hObject; +}; + +class CCriticalSection : public CSyncObject { + DECLARE_DYNAMIC(CCriticalSection) +public: + CCriticalSection(); + ~CCriticalSection() override; + BOOL Lock() { return Lock(INFINITE); } + BOOL Lock(DWORD dwTimeout) override; + BOOL Unlock() override; + operator CRITICAL_SECTION*() { return &m_sect; } + CRITICAL_SECTION m_sect; +}; + +class CMutex : public CSyncObject { + DECLARE_DYNAMIC(CMutex) +public: + explicit CMutex(BOOL bInitiallyOwn = FALSE, const char* lpszName = nullptr, LPSECURITY_ATTRIBUTES lpsaAttribute = nullptr); + BOOL Unlock() override; +}; + +class CSemaphore : public CSyncObject { + DECLARE_DYNAMIC(CSemaphore) +public: + explicit CSemaphore(LONG lInitialCount = 1, LONG lMaxCount = 1, const char* pstrName = nullptr, + LPSECURITY_ATTRIBUTES lpsaAttributes = nullptr); + BOOL Unlock() override; + BOOL Unlock(LONG lCount, LPLONG lprevCount = nullptr) override; +}; + +class CEvent : public CSyncObject { + DECLARE_DYNAMIC(CEvent) +public: + explicit CEvent(BOOL bInitiallyOwn = FALSE, BOOL bManualReset = FALSE, const char* lpszName = nullptr, + LPSECURITY_ATTRIBUTES lpsaAttribute = nullptr); + BOOL SetEvent(); + BOOL PulseEvent(); + BOOL ResetEvent(); + BOOL Unlock() override { return TRUE; } +}; + +class CSingleLock { +public: + explicit CSingleLock(CSyncObject* pObject, BOOL bInitialLock = FALSE); + ~CSingleLock(); + BOOL Lock(DWORD dwTimeOut = INFINITE); + BOOL Unlock(); + BOOL Unlock(LONG lCount, LPLONG lPrevCount = nullptr); + BOOL IsLocked() { return m_bAcquired; } + +protected: + CSyncObject* m_pObject; + BOOL m_bAcquired; +}; + +class CMultiLock { +public: + CMultiLock(CSyncObject* ppObjects[], DWORD dwCount, BOOL bInitialLock = FALSE); + ~CMultiLock(); + DWORD Lock(DWORD dwTimeOut = INFINITE, BOOL bWaitForAll = TRUE, DWORD dwWakeMask = 0); + BOOL Unlock(); + +protected: + std::vector m_objects; + std::vector m_locked; +}; diff --git a/port/mfcwx/include/afxodlgs.h b/port/mfcwx/include/afxodlgs.h new file mode 100644 index 000000000..be0574af7 --- /dev/null +++ b/port/mfcwx/include/afxodlgs.h @@ -0,0 +1,2 @@ +#pragma once +#include "afxdlgs.h" diff --git a/port/mfcwx/include/afxole.h b/port/mfcwx/include/afxole.h new file mode 100644 index 000000000..5a4ef9756 --- /dev/null +++ b/port/mfcwx/include/afxole.h @@ -0,0 +1,57 @@ +#pragma once +#define __AFXOLE_H__ + +#include "afxwin.h" + +#define DROPEFFECT_NONE 0 +#define DROPEFFECT_COPY 1 +#define DROPEFFECT_MOVE 2 +#define DROPEFFECT_LINK 4 +#define DROPEFFECT_SCROLL 0x80000000 +typedef DWORD DROPEFFECT; +typedef WORD CLIPFORMAT; + +class COleDataObject { +public: + COleDataObject() = default; + BOOL IsDataAvailable(CLIPFORMAT cfFormat, void* = nullptr) const; + HGLOBAL GetGlobalData(CLIPFORMAT cfFormat, void* = nullptr) const; + BOOL AttachClipboard(); + void Release() {} +}; + +class COleDataSource : public CCmdTarget { +public: + void CacheGlobalData(CLIPFORMAT cfFormat, HGLOBAL hGlobal, void* = nullptr); + DROPEFFECT DoDragDrop(DWORD = DROPEFFECT_COPY | DROPEFFECT_MOVE, LPCRECT = nullptr, void* = nullptr); + void SetClipboard(); + HGLOBAL m_data = nullptr; + CLIPFORMAT m_format = 0; +}; + +class COleDropTarget : public CCmdTarget { +public: + BOOL Register(CWnd*) { return TRUE; } + void Revoke() {} +}; + +class COleDateTime { +public: + COleDateTime() : m_dt(0), m_status(valid) {} + COleDateTime(int nYear, int nMonth, int nDay, int nHour, int nMin, int nSec); + static COleDateTime GetCurrentTime(); + int GetYear() const; + int GetMonth() const; + int GetDay() const; + int GetHour() const; + int GetMinute() const; + int GetSecond() const; + int GetDayOfWeek() const; + CString Format(const char* pFormat) const; + CString Format(DWORD dwFlags = 0, LCID lcid = 0) const; + enum DateTimeStatus { error = -1, valid = 0, invalid = 1, null = 2 }; + double m_dt; + DateTimeStatus m_status; +}; + +BOOL AFXAPI AfxOleInit(); diff --git a/port/mfcwx/include/afxpriv.h b/port/mfcwx/include/afxpriv.h new file mode 100644 index 000000000..6e7e9031a --- /dev/null +++ b/port/mfcwx/include/afxpriv.h @@ -0,0 +1,2 @@ +#pragma once +#include "afxwin.h" diff --git a/port/mfcwx/include/afxres.h b/port/mfcwx/include/afxres.h new file mode 100644 index 000000000..e0912e953 --- /dev/null +++ b/port/mfcwx/include/afxres.h @@ -0,0 +1,281 @@ +#pragma once + +/* Standard MFC resource IDs. Parsed by port/rc/rc2cpp.py. */ + +#include "mfcwx/winconst.h" + +#define IDC_STATIC (-1) + +#define ID_SEPARATOR 0 + +#define AFX_IDS_APP_TITLE 0xE000 +#define AFX_IDS_IDLEMESSAGE 0xE001 +#define AFX_IDS_HELPMODEMESSAGE 0xE002 +#define AFX_IDS_APP_TITLE_EMBEDDING 0xE003 +#define AFX_IDS_COMPANY_NAME 0xE004 +#define AFX_IDS_OBJ_TITLE_INPLACE 0xE005 + +#define ID_FILE_NEW 0xE100 +#define ID_FILE_OPEN 0xE101 +#define ID_FILE_CLOSE 0xE102 +#define ID_FILE_SAVE 0xE103 +#define ID_FILE_SAVE_AS 0xE104 +#define ID_FILE_PAGE_SETUP 0xE105 +#define ID_FILE_PRINT_SETUP 0xE106 +#define ID_FILE_PRINT 0xE107 +#define ID_FILE_PRINT_DIRECT 0xE108 +#define ID_FILE_PRINT_PREVIEW 0xE109 +#define ID_FILE_UPDATE 0xE10A +#define ID_FILE_SAVE_COPY_AS 0xE10B +#define ID_FILE_SEND_MAIL 0xE10C +#define ID_FILE_NEW_FRAME 0xE10D +#define ID_FILE_MRU_FIRST 0xE110 +#define ID_FILE_MRU_FILE1 0xE110 +#define ID_FILE_MRU_FILE2 0xE111 +#define ID_FILE_MRU_FILE3 0xE112 +#define ID_FILE_MRU_FILE4 0xE113 +#define ID_FILE_MRU_FILE5 0xE114 +#define ID_FILE_MRU_FILE6 0xE115 +#define ID_FILE_MRU_FILE7 0xE116 +#define ID_FILE_MRU_FILE8 0xE117 +#define ID_FILE_MRU_FILE9 0xE118 +#define ID_FILE_MRU_FILE10 0xE119 +#define ID_FILE_MRU_FILE11 0xE11A +#define ID_FILE_MRU_FILE12 0xE11B +#define ID_FILE_MRU_FILE13 0xE11C +#define ID_FILE_MRU_FILE14 0xE11D +#define ID_FILE_MRU_FILE15 0xE11E +#define ID_FILE_MRU_FILE16 0xE11F +#define ID_FILE_MRU_LAST 0xE11F + +#define ID_EDIT_CLEAR 0xE120 +#define ID_EDIT_CLEAR_ALL 0xE121 +#define ID_EDIT_COPY 0xE122 +#define ID_EDIT_CUT 0xE123 +#define ID_EDIT_FIND 0xE124 +#define ID_EDIT_PASTE 0xE125 +#define ID_EDIT_PASTE_LINK 0xE126 +#define ID_EDIT_PASTE_SPECIAL 0xE127 +#define ID_EDIT_REPEAT 0xE128 +#define ID_EDIT_REPLACE 0xE129 +#define ID_EDIT_SELECT_ALL 0xE12A +#define ID_EDIT_UNDO 0xE12B +#define ID_EDIT_REDO 0xE12C + +#define ID_WINDOW_NEW 0xE130 +#define ID_WINDOW_ARRANGE 0xE131 +#define ID_WINDOW_CASCADE 0xE132 +#define ID_WINDOW_TILE_HORZ 0xE133 +#define ID_WINDOW_TILE_VERT 0xE134 +#define ID_WINDOW_SPLIT 0xE135 +#define AFX_IDM_WINDOW_FIRST 0xE130 +#define AFX_IDM_WINDOW_LAST 0xE13F +#define AFX_IDM_FIRST_MDICHILD 0xFF00 + +#define ID_APP_ABOUT 0xE140 +#define ID_APP_EXIT 0xE141 +#define ID_HELP_INDEX 0xE142 +#define ID_HELP_FINDER 0xE143 +#define ID_HELP_USING 0xE144 +#define ID_CONTEXT_HELP 0xE145 +#define ID_HELP 0xE146 +#define ID_DEFAULT_HELP 0xE147 + +#define ID_NEXT_PANE 0xE150 +#define ID_PREV_PANE 0xE151 +#define ID_FORMAT_FONT 0xE160 + +#define ID_OLE_INSERT_NEW 0xE200 +#define ID_OLE_EDIT_LINKS 0xE201 +#define ID_OLE_EDIT_CONVERT 0xE202 +#define ID_OLE_EDIT_CHANGE_ICON 0xE203 +#define ID_OLE_EDIT_PROPERTIES 0xE204 +#define ID_OLE_VERB_FIRST 0xE210 + +#define AFX_ID_PREVIEW_CLOSE 0xE300 +#define AFX_ID_PREVIEW_NUMPAGE 0xE301 +#define AFX_ID_PREVIEW_NEXT 0xE302 +#define AFX_ID_PREVIEW_PREV 0xE303 +#define AFX_ID_PREVIEW_PRINT 0xE304 +#define AFX_ID_PREVIEW_ZOOMIN 0xE305 +#define AFX_ID_PREVIEW_ZOOMOUT 0xE306 + +#define ID_INDICATOR_EXT 0xE700 +#define ID_INDICATOR_CAPS 0xE701 +#define ID_INDICATOR_NUM 0xE702 +#define ID_INDICATOR_SCRL 0xE703 +#define ID_INDICATOR_OVR 0xE704 +#define ID_INDICATOR_REC 0xE705 +#define ID_INDICATOR_KANA 0xE706 + +#define ID_VIEW_TOOLBAR 0xE800 +#define ID_VIEW_STATUS_BAR 0xE801 +#define ID_VIEW_REBAR 0xE804 +#define ID_VIEW_AUTOARRANGE 0xE805 +#define ID_VIEW_SMALLICON 0xE810 +#define ID_VIEW_LARGEICON 0xE811 +#define ID_VIEW_LIST 0xE812 +#define ID_VIEW_DETAILS 0xE813 +#define ID_VIEW_LINEUP 0xE814 +#define ID_VIEW_BYNAME 0xE815 + +#define ID_RECORD_FIRST 0xE900 +#define ID_RECORD_LAST 0xE901 +#define ID_RECORD_NEXT 0xE902 +#define ID_RECORD_PREV 0xE903 + +#define AFX_IDW_CONTROLBAR_FIRST 0xE800 +#define AFX_IDW_CONTROLBAR_LAST 0xE8FF +#define AFX_IDW_TOOLBAR 0xE800 +#define AFX_IDW_STATUS_BAR 0xE801 +#define AFX_IDW_PREVIEW_BAR 0xE802 +#define AFX_IDW_RESIZE_BAR 0xE803 +#define AFX_IDW_REBAR 0xE804 +#define AFX_IDW_DIALOGBAR 0xE805 +#define AFX_IDW_DOCKBAR_TOP 0xE81B +#define AFX_IDW_DOCKBAR_LEFT 0xE81C +#define AFX_IDW_DOCKBAR_RIGHT 0xE81D +#define AFX_IDW_DOCKBAR_BOTTOM 0xE81E +#define AFX_IDW_DOCKBAR_FLOAT 0xE81F +#define AFX_IDW_PANE_FIRST 0xE900 +#define AFX_IDW_PANE_LAST 0xE9FF +#define AFX_IDW_HSCROLL_FIRST 0xEA00 +#define AFX_IDW_VSCROLL_FIRST 0xEA10 +#define AFX_IDW_SIZE_BOX 0xEA20 +#define AFX_IDW_PANE_SAVE 0xEA21 + +#define AFX_IDS_SCFIRST 0xEF00 +#define AFX_IDS_SCSIZE 0xEF00 +#define AFX_IDS_SCMOVE 0xEF01 +#define AFX_IDS_SCMINIMIZE 0xEF02 +#define AFX_IDS_SCMAXIMIZE 0xEF03 +#define AFX_IDS_SCNEXTWINDOW 0xEF04 +#define AFX_IDS_SCPREVWINDOW 0xEF05 +#define AFX_IDS_SCCLOSE 0xEF06 +#define AFX_IDS_SCRESTORE 0xEF12 +#define AFX_IDS_SCTASKLIST 0xEF13 +#define AFX_IDS_MDICHILD 0xEF1F +#define AFX_IDS_DESKACCESSORY 0xEFDA +#define AFX_IDS_OPENFILE 0xF000 +#define AFX_IDS_SAVEFILE 0xF001 +#define AFX_IDS_ALLFILTER 0xF002 +#define AFX_IDS_UNTITLED 0xF003 +#define AFX_IDS_SAVEFILECOPY 0xF004 +#define AFX_IDS_PREVIEW_CLOSE 0xF005 +#define AFX_IDS_UNNAMED_FILE 0xF006 +#define AFX_IDS_HIDE 0xF011 + +#define AFX_IDP_INVALID_FILENAME 0xF100 +#define AFX_IDP_FAILED_TO_OPEN_DOC 0xF101 +#define AFX_IDP_FAILED_TO_SAVE_DOC 0xF102 +#define AFX_IDP_ASK_TO_SAVE 0xF103 +#define AFX_IDP_FAILED_TO_CREATE_DOC 0xF104 +#define AFX_IDP_FILE_TOO_LARGE 0xF105 +#define AFX_IDP_FAILED_TO_START_PRINT 0xF106 +#define AFX_IDP_FAILED_TO_LAUNCH_HELP 0xF107 +#define AFX_IDP_INTERNAL_FAILURE 0xF108 +#define AFX_IDP_COMMAND_FAILURE 0xF109 +#define AFX_IDP_FAILED_MEMORY_ALLOC 0xF10A +#define AFX_IDP_UNREG_DONE 0xF10B +#define AFX_IDP_UNREG_FAILURE 0xF10C +#define AFX_IDP_DLL_LOAD_FAILED 0xF10D +#define AFX_IDP_DLL_BAD_VERSION 0xF10E +#define AFX_IDP_PARSE_INT 0xF110 +#define AFX_IDP_PARSE_REAL 0xF111 +#define AFX_IDP_PARSE_INT_RANGE 0xF112 +#define AFX_IDP_PARSE_REAL_RANGE 0xF113 +#define AFX_IDP_PARSE_STRING_SIZE 0xF114 +#define AFX_IDP_PARSE_RADIO_BUTTON 0xF115 +#define AFX_IDP_PARSE_BYTE 0xF116 +#define AFX_IDP_PARSE_UINT 0xF117 +#define AFX_IDP_PARSE_DATETIME 0xF118 +#define AFX_IDP_PARSE_CURRENCY 0xF119 +#define AFX_IDP_FAILED_INVALID_FORMAT 0xF120 +#define AFX_IDP_FAILED_INVALID_PATH 0xF121 +#define AFX_IDP_FAILED_DISK_FULL 0xF122 +#define AFX_IDP_FAILED_ACCESS_READ 0xF123 +#define AFX_IDP_FAILED_ACCESS_WRITE 0xF124 +#define AFX_IDP_FAILED_IO_ERROR_READ 0xF125 +#define AFX_IDP_FAILED_IO_ERROR_WRITE 0xF126 + +#define AFX_IDC_LISTBOX 100 +#define AFX_IDC_CHANGE 101 +#define AFX_IDD_FILEOPEN 28676 +#define AFX_IDD_FILESAVE 28677 +#define AFX_IDD_FONT 28678 +#define AFX_IDD_COLOR 28679 +#define AFX_IDD_PRINT 28680 +#define AFX_IDD_PRINTSETUP 28681 +#define AFX_IDD_FIND 28682 +#define AFX_IDD_REPLACE 28683 +#define AFX_IDD_NEWTYPEDLG 30721 +#define AFX_IDD_PRINTDLG 30722 +#define AFX_IDD_PREVIEW_TOOLBAR 30723 + +#define AFX_IDS_MEMORY_EXCEPTION 0xF007 +#define AFX_IDS_NOT_SUPPORTED_EXCEPTION 0xF008 +#define AFX_IDS_RESOURCE_EXCEPTION 0xF009 +#define AFX_IDS_USER_EXCEPTION 0xF00A +#define AFX_IDS_PRINTONPORT 0xF040 +#define AFX_IDS_ONEPAGE 0xF041 +#define AFX_IDS_TWOPAGE 0xF042 +#define AFX_IDS_PRINTPAGENUM 0xF043 +#define AFX_IDS_PREVIEWPAGEDESC 0xF044 +#define AFX_IDS_PRINTDEFAULTEXT 0xF045 +#define AFX_IDS_PRINTDEFAULT 0xF046 +#define AFX_IDS_PRINTFILTER 0xF047 +#define AFX_IDS_PRINTCAPTION 0xF048 +#define AFX_IDS_PRINTTOFILE 0xF049 +#define AFX_IDS_PASTELINKEDTYPE 0xF074 +#define AFX_IDS_UNKNOWNTYPE 0xF076 +#define AFX_IDS_NOT_DOCOBJECT 0xF184 +#define AFX_IDS_OCC_SCALEUNITS_PIXELS 0xF1C0 +#define AFX_IDP_NO_ERROR_AVAILABLE 0xF020 +#define AFX_IDP_FAILED_TO_NOTIFY 0xF185 +#define AFX_IDP_GET_NOT_SUPPORTED 0xF18C +#define AFX_IDP_SET_NOT_SUPPORTED 0xF18D +#define AFX_IDP_FAILED_MAPI_LOAD 0xF190 +#define AFX_IDP_INVALID_MAPI_DLL 0xF191 +#define AFX_IDP_FAILED_MAPI_SEND 0xF192 +#define AFX_IDP_FILE_NONE 0xF1A0 +#define AFX_IDP_FILE_GENERIC 0xF1A1 +#define AFX_IDP_FILE_NOT_FOUND 0xF1A2 +#define AFX_IDP_FILE_BAD_PATH 0xF1A3 +#define AFX_IDP_FILE_TOO_MANY_OPEN 0xF1A4 +#define AFX_IDP_FILE_ACCESS_DENIED 0xF1A5 +#define AFX_IDP_FILE_INVALID_FILE 0xF1A6 +#define AFX_IDP_FILE_REMOVE_CURRENT 0xF1A7 +#define AFX_IDP_FILE_DIR_FULL 0xF1A8 +#define AFX_IDP_FILE_BAD_SEEK 0xF1A9 +#define AFX_IDP_FILE_HARD_IO 0xF1AA +#define AFX_IDP_FILE_SHARING 0xF1AB +#define AFX_IDP_FILE_LOCKING 0xF1AC +#define AFX_IDP_FILE_DISKFULL 0xF1AD +#define AFX_IDP_FILE_EOF 0xF1AE +#define AFX_IDP_ARCH_NONE 0xF1B0 +#define AFX_IDP_ARCH_GENERIC 0xF1B1 +#define AFX_IDP_ARCH_READONLY 0xF1B2 +#define AFX_IDP_ARCH_ENDOFFILE 0xF1B3 +#define AFX_IDP_ARCH_WRITEONLY 0xF1B4 +#define AFX_IDP_ARCH_BADINDEX 0xF1B5 +#define AFX_IDP_ARCH_BADCLASS 0xF1B6 +#define AFX_IDP_ARCH_BADSCHEMA 0xF1B7 +#define AFX_IDC_PRINT_DOCNAME 201 +#define AFX_IDC_PRINT_PRINTERNAME 202 +#define AFX_IDC_PRINT_PORTNAME 203 +#define AFX_IDC_PRINT_PAGENUM 204 +#define AFX_IDC_CONTEXTHELP 30977 +#define AFX_IDC_MAGNIFY 30978 +#define AFX_IDC_SMALLARROWS 30979 +#define AFX_IDC_HSPLITBAR 30980 +#define AFX_IDC_VSPLITBAR 30981 +#define AFX_IDC_NODROPCRSR 30982 +#define AFX_IDC_TRACKNWSE 30983 +#define AFX_IDC_TRACKNESW 30984 +#define AFX_IDC_TRACKNS 30985 +#define AFX_IDC_TRACKWE 30986 +#define AFX_IDC_TRACK4WAY 30988 +#define AFX_IDC_MOVE4WAY 30989 +#define AFX_IDB_MINIFRAME_MENU 30994 +#define AFX_IDB_CHECKLISTBOX_NT 30995 +#define AFX_IDB_CHECKLISTBOX_95 30996 diff --git a/port/mfcwx/include/afxrich.h b/port/mfcwx/include/afxrich.h new file mode 100644 index 000000000..37b5330df --- /dev/null +++ b/port/mfcwx/include/afxrich.h @@ -0,0 +1,42 @@ +#pragma once +#define __AFXRICH_H__ + +#include "afxwin.h" +#include "afxcmn.h" +#include "afxole.h" + +class CRichEditCntrItem : public CObject { +public: + CRichEditCntrItem() = default; +}; + +class CRichEditDoc : public CDocument { + DECLARE_DYNAMIC(CRichEditDoc) +public: + CRichEditDoc(); + virtual CRichEditCntrItem* CreateClientItem(void* preo = nullptr) const; + class CRichEditView* GetView() const; + BOOL m_bRTF; + void Serialize(CArchive& ar) override; +}; + +class CRichEditView : public CView { + DECLARE_DYNCREATE(CRichEditView) +public: + CRichEditView(); + CRichEditCtrl& GetRichEditCtrl() const { return *reinterpret_cast(const_cast(this)); } + CRichEditDoc* GetDocument() const { return reinterpret_cast(m_pDocument); } + void OnDraw(CDC*) override {} + void OnInitialUpdate() override; + void Serialize(CArchive& ar) override; + long GetTextLength() const; + void SetCharFormat(CHARFORMAT2 cf); + CHARFORMAT2& GetCharFormatSelection(); + void WrapChanged() {} + afx_msg int OnCreate(LPCREATESTRUCT lpcs); + int m_nWordWrap; + enum WordWrapType { WrapNone = 0, WrapToWindow = 1, WrapToTargetDevice = 2 }; + +protected: + DECLARE_MESSAGE_MAP() +}; diff --git a/port/mfcwx/include/afxsock.h b/port/mfcwx/include/afxsock.h new file mode 100644 index 000000000..6e7e9031a --- /dev/null +++ b/port/mfcwx/include/afxsock.h @@ -0,0 +1,2 @@ +#pragma once +#include "afxwin.h" diff --git a/port/mfcwx/include/afxtempl.h b/port/mfcwx/include/afxtempl.h new file mode 100644 index 000000000..e60a537cd --- /dev/null +++ b/port/mfcwx/include/afxtempl.h @@ -0,0 +1,3 @@ +#pragma once +#define __AFXTEMPL_H__ +#include "afx.h" diff --git a/port/mfcwx/include/afxwin.h b/port/mfcwx/include/afxwin.h new file mode 100644 index 000000000..305e0d603 --- /dev/null +++ b/port/mfcwx/include/afxwin.h @@ -0,0 +1,1258 @@ +#pragma once + +// MFC GUI subset on top of wxWidgets. +#define __AFXWIN_H__ + +#include "afx.h" +#include "afxres.h" +#include "mfcwx/msgmap.h" + +#include + +class wxWindow; +class wxDC; +class wxMenu; +class wxMenuBar; +class wxBitmap; +class wxFrame; + +class CCmdTarget; +class CWnd; +class CDC; +class CMenu; +class CCmdUI; +class CDocument; +class CView; +class CFrameWnd; +class CMDIFrameWnd; +class CMDIChildWnd; +class CDocTemplate; +class CWinApp; +class CWinThread; +class CDataExchange; +class CScrollBar; +class CGdiObject; +class CPen; +class CBrush; +class CFont; +class CBitmap; +class CRgn; +class CPalette; +class CImageList; +class CToolTipCtrl; +class CPrintInfo; +class CCreateContext; +class CPrintDialog; +class CStatusBar; +class CToolBar; +class CControlBar; +class CDialog; + +namespace mfcwx { +struct WindowState; +struct DCState; +struct GdiObjectImpl; +} + +// --------------------------------------------------------------------------------------------- +// Command targets + +class CCmdTarget : public CObject { + DECLARE_DYNAMIC(CCmdTarget) +public: + CCmdTarget() = default; + virtual BOOL OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo); + void BeginWaitCursor(); + void EndWaitCursor(); + void RestoreWaitCursor(); + void EnableAutomation() {} + virtual void OnFinalRelease() { delete this; } + +protected: + static const AFX_MSGMAP* GetThisMessageMap(); + virtual const AFX_MSGMAP* GetMessageMap() const; + +public: + // Locates the handler for a message map entry search; used by the dispatcher. + const AFX_MSGMAP_ENTRY* FindMessageEntry(UINT nMessage, UINT nCode, UINT nID) const; + BOOL DispatchCommand(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo); +}; + +class CCmdUI { +public: + CCmdUI(); + virtual ~CCmdUI() = default; + virtual void Enable(BOOL bOn = TRUE); + virtual void SetCheck(int nCheck = 1); + virtual void SetRadio(BOOL bOn = TRUE); + virtual void SetText(const char* lpszText); + void ContinueRouting() { m_bContinueRouting = TRUE; } + BOOL DoUpdate(CCmdTarget* pTarget, BOOL bDisableIfNoHndler); + + UINT m_nID; + UINT m_nIndex; + CMenu* m_pMenu; + CMenu* m_pSubMenu; + CMenu* m_pParentMenu; + CWnd* m_pOther; + BOOL m_bEnableChanged; + BOOL m_bContinueRouting; + UINT m_nIndexMax; + + // results collected for the wx update event + bool m_enabled; + int m_check; + bool m_checkSet; + bool m_textSet; + CString m_text; +}; + +// --------------------------------------------------------------------------------------------- +// Windows + +class CWnd : public CCmdTarget { + DECLARE_DYNCREATE(CWnd) +public: + CWnd(); + ~CWnd() override; + + HWND m_hWnd; + HWND GetSafeHwnd() const { return AfxIsNullThis(this) ? nullptr : m_hWnd; } + operator HWND() const { return AfxIsNullThis(this) ? nullptr : m_hWnd; } + bool operator==(const CWnd& wnd) const { return m_hWnd == wnd.m_hWnd; } + bool operator!=(const CWnd& wnd) const { return m_hWnd != wnd.m_hWnd; } + + static CWnd* FromHandle(HWND hWnd); + static CWnd* FromHandlePermanent(HWND hWnd); + static void DeleteTempMap(); + BOOL Attach(HWND hWndNew); + HWND Detach(); + BOOL SubclassWindow(HWND hWnd); + BOOL SubclassDlgItem(UINT nID, CWnd* pParent); + HWND UnsubclassWindow(); + + // creation + virtual BOOL Create(const char* lpszClassName, const char* lpszWindowName, DWORD dwStyle, const RECT& rect, + CWnd* pParentWnd, UINT nID, CCreateContext* pContext = nullptr); + virtual BOOL CreateEx(DWORD dwExStyle, const char* lpszClassName, const char* lpszWindowName, DWORD dwStyle, + int x, int y, int nWidth, int nHeight, HWND hWndParent, HMENU nIDorHMenu, + LPVOID lpParam = nullptr); + virtual BOOL CreateEx(DWORD dwExStyle, const char* lpszClassName, const char* lpszWindowName, DWORD dwStyle, + const RECT& rect, CWnd* pParentWnd, UINT nID, LPVOID lpParam = nullptr); + virtual BOOL DestroyWindow(); + virtual BOOL PreCreateWindow(CREATESTRUCT& cs); + virtual void PreSubclassWindow() {} + virtual void PostNcDestroy() {} + + // message handling + LRESULT SendMessage(UINT message, WPARAM wParam = 0, LPARAM lParam = 0); + BOOL PostMessage(UINT message, WPARAM wParam = 0, LPARAM lParam = 0); + BOOL SendNotifyMessage(UINT message, WPARAM wParam, LPARAM lParam); + void SendMessageToDescendants(UINT message, WPARAM wParam = 0, LPARAM lParam = 0, BOOL bDeep = TRUE, + BOOL bOnlyPerm = FALSE); + virtual LRESULT WindowProc(UINT message, WPARAM wParam, LPARAM lParam); + virtual BOOL OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult); + virtual LRESULT DefWindowProc(UINT message, WPARAM wParam, LPARAM lParam); + LRESULT Default(); + virtual BOOL OnCommand(WPARAM wParam, LPARAM lParam); + virtual BOOL OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult); + virtual BOOL OnChildNotify(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult); + BOOL ReflectChildNotify(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult); + virtual BOOL PreTranslateMessage(MSG* pMsg); + static const MSG* GetCurrentMessage(); + BOOL ExecuteDlgInit(const char* lpszResourceName); + BOOL ExecuteDlgInit(UINT nIDTemplate); + + // state + DWORD GetStyle() const; + DWORD GetExStyle() const; + BOOL ModifyStyle(DWORD dwRemove, DWORD dwAdd, UINT nFlags = 0); + BOOL ModifyStyleEx(DWORD dwRemove, DWORD dwAdd, UINT nFlags = 0); + int GetDlgCtrlID() const; + int SetDlgCtrlID(int nID); + CWnd* GetParent() const; + CWnd* SetParent(CWnd* pWndNewParent); + CWnd* GetOwner() const; + void SetOwner(CWnd* pOwnerWnd); + CWnd* GetParentOwner() const; + CFrameWnd* GetParentFrame() const; + CFrameWnd* GetTopLevelFrame() const; + CWnd* GetTopLevelParent() const; + CWnd* GetTopLevelOwner() const; + CWnd* GetTopWindow() const; + CWnd* GetWindow(UINT nCmd) const; + CWnd* GetNextWindow(UINT nFlag = GW_HWNDNEXT) const; + CWnd* GetLastActivePopup() const; + CWnd* GetDescendantWindow(int nID, BOOL bOnlyPerm = FALSE) const; + CWnd* ChildWindowFromPoint(POINT point) const; + static CWnd* WindowFromPoint(POINT point); + static CWnd* GetActiveWindow(); + CWnd* SetActiveWindow(); + static CWnd* GetForegroundWindow(); + BOOL SetForegroundWindow(); + static CWnd* GetFocus(); + CWnd* SetFocus(); + static CWnd* GetDesktopWindow(); + static CWnd* GetCapture(); + CWnd* SetCapture(); + static CWnd* FindWindow(const char* lpszClassName, const char* lpszWindowName); + BOOL IsChild(const CWnd* pWnd) const; + BOOL IsWindowEnabled() const; + BOOL EnableWindow(BOOL bEnable = TRUE); + BOOL IsWindowVisible() const; + BOOL ShowWindow(int nCmdShow); + BOOL IsIconic() const; + BOOL IsZoomed() const; + BOOL FlashWindow(BOOL) { return TRUE; } + void DragAcceptFiles(BOOL bAccept = TRUE); + BOOL IsDialogMessage(LPMSG) { return FALSE; } + + // text + void SetWindowText(const char* lpszString); + void SetWindowTextA(const char* lpszString) { SetWindowText(lpszString); } + int GetWindowText(char* lpszStringBuf, int nMaxCount) const; + void GetWindowText(CString& rString) const; + int GetWindowTextA(char* lpszStringBuf, int nMaxCount) const { return GetWindowText(lpszStringBuf, nMaxCount); } + void GetWindowTextA(CString& rString) const { GetWindowText(rString); } + int GetWindowTextLength() const; + void SetFont(CFont* pFont, BOOL bRedraw = TRUE); + CFont* GetFont() const; + + // geometry + void GetWindowRect(LPRECT lpRect) const; + void GetClientRect(LPRECT lpRect) const; + void ClientToScreen(LPPOINT lpPoint) const; + void ClientToScreen(LPRECT lpRect) const; + void ScreenToClient(LPPOINT lpPoint) const; + void ScreenToClient(LPRECT lpRect) const; + void MapWindowPoints(CWnd* pwndTo, LPPOINT lpPoint, UINT nCount) const; + void MapWindowPoints(CWnd* pwndTo, LPRECT lpRect) const; + void MoveWindow(int x, int y, int nWidth, int nHeight, BOOL bRepaint = TRUE); + void MoveWindow(LPCRECT lpRect, BOOL bRepaint = TRUE); + BOOL SetWindowPos(const CWnd* pWndInsertAfter, int x, int y, int cx, int cy, UINT nFlags); + BOOL GetWindowPlacement(WINDOWPLACEMENT* lpwndpl) const; + BOOL SetWindowPlacement(const WINDOWPLACEMENT* lpwndpl); + void CenterWindow(CWnd* pAlternateOwner = nullptr); + void BringWindowToTop(); + void CalcWindowRect(LPRECT lpClientRect, UINT nAdjustType = 0); + void RepositionBars(UINT, UINT, UINT, UINT = 0, LPRECT = nullptr, LPCRECT = nullptr, BOOL = TRUE) {} + + // painting + CDC* GetDC(); + CDC* GetWindowDC(); + int ReleaseDC(CDC* pDC); + void Invalidate(BOOL bErase = TRUE); + void InvalidateRect(LPCRECT lpRect, BOOL bErase = TRUE); + void InvalidateRgn(CRgn* pRgn, BOOL bErase = TRUE); + void ValidateRect(LPCRECT) {} + void UpdateWindow(); + BOOL RedrawWindow(LPCRECT lpRectUpdate = nullptr, CRgn* prgnUpdate = nullptr, UINT flags = 0); + void SetRedraw(BOOL bRedraw = TRUE); + BOOL GetUpdateRect(LPRECT lpRect, BOOL bErase = FALSE); + CDC* BeginPaint(LPPAINTSTRUCT lpPaint); + void EndPaint(LPPAINTSTRUCT lpPaint); + void ScrollWindow(int xAmount, int yAmount, LPCRECT lpRect = nullptr, LPCRECT lpClipRect = nullptr); + void ShowCaret() {} + void HideCaret() {} + BOOL CreateCaret(CBitmap*) { return TRUE; } + void CreateSolidCaret(int, int) {} + void SetCaretPos(POINT) {} + static CPoint GetCaretPos(); + + // scrolling + int GetScrollPos(int nBar) const; + int SetScrollPos(int nBar, int nPos, BOOL bRedraw = TRUE); + void GetScrollRange(int nBar, LPINT lpMinPos, LPINT lpMaxPos) const; + void SetScrollRange(int nBar, int nMinPos, int nMaxPos, BOOL bRedraw = TRUE); + BOOL SetScrollInfo(int nBar, LPSCROLLINFO lpScrollInfo, BOOL bRedraw = TRUE); + BOOL GetScrollInfo(int nBar, LPSCROLLINFO lpScrollInfo, UINT nMask = SIF_ALL); + int GetScrollLimit(int nBar); + void ShowScrollBar(UINT nBar, BOOL bShow = TRUE); + BOOL EnableScrollBar(int nSBFlags, UINT nArrowFlags = ESB_ENABLE_BOTH); + virtual CScrollBar* GetScrollBarCtrl(int nBar) const; + + // timers + UINT_PTR SetTimer(UINT_PTR nIDEvent, UINT nElapse, void (*lpfnTimer)(HWND, UINT, UINT_PTR, DWORD)); + BOOL KillTimer(UINT_PTR nIDEvent); + + // dialog items + CWnd* GetDlgItem(int nID) const; + void GetDlgItem(int nID, HWND* phWnd) const; + UINT GetDlgItemInt(int nID, BOOL* lpTrans = nullptr, BOOL bSigned = TRUE) const; + void SetDlgItemInt(int nID, UINT nValue, BOOL bSigned = TRUE); + int GetDlgItemText(int nID, char* lpStr, int nMaxCount) const; + int GetDlgItemText(int nID, CString& rString) const; + void SetDlgItemText(int nID, const char* lpszString); + UINT IsDlgButtonChecked(int nIDButton) const; + void CheckDlgButton(int nIDButton, UINT nCheck); + void CheckRadioButton(int nIDFirstButton, int nIDLastButton, int nIDCheckButton); + int GetCheckedRadioButton(int nIDFirstButton, int nIDLastButton); + LRESULT SendDlgItemMessage(int nID, UINT message, WPARAM wParam = 0, LPARAM lParam = 0); + CWnd* GetNextDlgGroupItem(CWnd* pWndCtl, BOOL bPrevious = FALSE) const; + CWnd* GetNextDlgTabItem(CWnd* pWndCtl, BOOL bPrevious = FALSE) const; + BOOL UpdateData(BOOL bSaveAndValidate = TRUE); + virtual void DoDataExchange(CDataExchange* pDX); + + // menus + CMenu* GetMenu() const; + BOOL SetMenu(CMenu* pMenu); + void DrawMenuBar(); + CMenu* GetSystemMenu(BOOL bRevert) const; + + // help, icons, misc + virtual void WinHelp(DWORD_PTR dwData, UINT nCmd = HELP_CONTEXT); + virtual void HtmlHelp(DWORD_PTR dwData, UINT nCmd = 0x000F); + HICON SetIcon(HICON hIcon, BOOL bBigIcon); + HICON GetIcon(BOOL bBigIcon) const; + BOOL EnableToolTips(BOOL bEnable = TRUE); + void SetWindowContextHelpId(DWORD) {} + int MessageBox(const char* lpszText, const char* lpszCaption = nullptr, UINT nType = MB_OK); + int MessageBoxA(const char* lpszText, const char* lpszCaption = nullptr, UINT nType = MB_OK) { return MessageBox(lpszText, lpszCaption, nType); } + BOOL OpenClipboard(); + BOOL LockWindowUpdate(); + void UnlockWindowUpdate(); + LONG_PTR GetWindowLongPtr(int nIndex) const; + LONG_PTR SetWindowLongPtr(int nIndex, LONG_PTR dwNewLong); + + // default handlers + afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); + afx_msg void OnDestroy(); + afx_msg void OnNcDestroy(); + afx_msg void OnClose(); + afx_msg void OnPaint(); + afx_msg BOOL OnEraseBkgnd(CDC* pDC); + afx_msg void OnSize(UINT nType, int cx, int cy); + afx_msg void OnMove(int x, int y); + afx_msg void OnTimer(UINT_PTR nIDEvent); + afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags); + afx_msg void OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags); + afx_msg void OnChar(UINT nChar, UINT nRepCnt, UINT nFlags); + afx_msg void OnSysKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags); + afx_msg void OnSysKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags); + afx_msg void OnSysChar(UINT nChar, UINT nRepCnt, UINT nFlags); + afx_msg void OnMouseMove(UINT nFlags, CPoint point); + afx_msg void OnLButtonDown(UINT nFlags, CPoint point); + afx_msg void OnLButtonUp(UINT nFlags, CPoint point); + afx_msg void OnLButtonDblClk(UINT nFlags, CPoint point); + afx_msg void OnRButtonDown(UINT nFlags, CPoint point); + afx_msg void OnRButtonUp(UINT nFlags, CPoint point); + afx_msg void OnRButtonDblClk(UINT nFlags, CPoint point); + afx_msg void OnMButtonDown(UINT nFlags, CPoint point); + afx_msg void OnMButtonUp(UINT nFlags, CPoint point); + afx_msg void OnNcLButtonDown(UINT nHitTest, CPoint point); + afx_msg BOOL OnMouseWheel(UINT nFlags, short zDelta, CPoint pt); + afx_msg void OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar); + afx_msg void OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar); + afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor); + afx_msg void OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct); + afx_msg void OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpMeasureItemStruct); + afx_msg void OnSetFocus(CWnd* pOldWnd); + afx_msg void OnKillFocus(CWnd* pNewWnd); + afx_msg void OnContextMenu(CWnd* pWnd, CPoint pos); + afx_msg BOOL OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message); + afx_msg void OnShowWindow(BOOL bShow, UINT nStatus); + afx_msg void OnSysCommand(UINT nID, LPARAM lParam); + afx_msg HCURSOR OnQueryDragIcon(); + afx_msg BOOL OnHelpInfo(HELPINFO* pHelpInfo); + afx_msg void OnGetMinMaxInfo(MINMAXINFO* lpMMI); + afx_msg void OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized); + afx_msg UINT OnGetDlgCode(); + afx_msg void OnDropFiles(HDROP hDropInfo); + afx_msg void OnInitMenuPopup(CMenu* pPopupMenu, UINT nIndex, BOOL bSysMenu); + afx_msg void OnMDIActivate(BOOL bActivate, CWnd* pActivateWnd, CWnd* pDeactivateWnd); + afx_msg void OnEnable(BOOL bEnable); + afx_msg BOOL OnNcActivate(BOOL bActive); + afx_msg void OnMenuSelect(UINT nItemID, UINT nFlags, HMENU hSysMenu); + afx_msg void OnSizing(UINT fwSide, LPRECT pRect); + + // implementation + wxWindow* GetWx() const { return reinterpret_cast(m_hWnd); } + static CWnd* FromWx(wxWindow* window); + void AttachWx(wxWindow* window); + +protected: + DECLARE_MESSAGE_MAP() +}; + +extern const CWnd wndTop; +extern const CWnd wndBottom; +extern const CWnd wndTopMost; +extern const CWnd wndNoTopMost; + +// --------------------------------------------------------------------------------------------- +// GDI + +class CGdiObject : public CObject { + DECLARE_DYNCREATE(CGdiObject) +public: + CGdiObject(); + ~CGdiObject() override; + HGDIOBJ m_hObject; + operator HGDIOBJ() const { return AfxIsNullThis(this) ? nullptr : m_hObject; } + HGDIOBJ GetSafeHandle() const { return AfxIsNullThis(this) ? nullptr : m_hObject; } + static CGdiObject* FromHandle(HGDIOBJ hObject); + BOOL Attach(HGDIOBJ hObject); + HGDIOBJ Detach(); + BOOL DeleteObject(); + int GetObject(int nCount, LPVOID lpObject) const; + BOOL CreateStockObject(int nIndex); + BOOL UnrealizeObject() { return TRUE; } +}; + +class CPen : public CGdiObject { + DECLARE_DYNAMIC(CPen) +public: + CPen() = default; + CPen(int nPenStyle, int nWidth, COLORREF crColor); + CPen(int nPenStyle, int nWidth, const LOGBRUSH* pLogBrush, int nStyleCount = 0, const DWORD* lpStyle = nullptr); + BOOL CreatePen(int nPenStyle, int nWidth, COLORREF crColor); + BOOL CreatePen(int nPenStyle, int nWidth, const LOGBRUSH* pLogBrush, int nStyleCount = 0, + const DWORD* lpStyle = nullptr); + BOOL CreatePenIndirect(LPLOGPEN lpLogPen); + int GetLogPen(LOGPEN* pLogPen); + static CPen* FromHandle(HPEN hPen) { return static_cast(CGdiObject::FromHandle(hPen)); } + operator HPEN() const { return m_hObject; } +}; + +class CBrush : public CGdiObject { + DECLARE_DYNAMIC(CBrush) +public: + CBrush() = default; + explicit CBrush(COLORREF crColor); + CBrush(int nIndex, COLORREF crColor); + explicit CBrush(CBitmap* pBitmap); + BOOL CreateSolidBrush(COLORREF crColor); + BOOL CreateHatchBrush(int nIndex, COLORREF crColor); + BOOL CreatePatternBrush(CBitmap* pBitmap); + BOOL CreateSysColorBrush(int nIndex); + BOOL CreateBrushIndirect(const LOGBRUSH* lpLogBrush); + int GetLogBrush(LOGBRUSH* pLogBrush); + static CBrush* FromHandle(HBRUSH hBrush) { return static_cast(CGdiObject::FromHandle(hBrush)); } + operator HBRUSH() const { return m_hObject; } +}; + +class CFont : public CGdiObject { + DECLARE_DYNAMIC(CFont) +public: + CFont() = default; + BOOL CreateFontIndirect(const LOGFONT* lpLogFont); + BOOL CreateFont(int nHeight, int nWidth, int nEscapement, int nOrientation, int nWeight, BYTE bItalic, + BYTE bUnderline, BYTE cStrikeOut, BYTE nCharSet, BYTE nOutPrecision, BYTE nClipPrecision, + BYTE nQuality, BYTE nPitchAndFamily, const char* lpszFacename); + BOOL CreateFontA(int nHeight, int nWidth, int nEscapement, int nOrientation, int nWeight, BYTE bItalic, + BYTE bUnderline, BYTE cStrikeOut, BYTE nCharSet, BYTE nOutPrecision, BYTE nClipPrecision, + BYTE nQuality, BYTE nPitchAndFamily, const char* lpszFacename) { + return CreateFont(nHeight, nWidth, nEscapement, nOrientation, nWeight, bItalic, bUnderline, cStrikeOut, + nCharSet, nOutPrecision, nClipPrecision, nQuality, nPitchAndFamily, lpszFacename); + } + BOOL CreatePointFont(int nPointSize, const char* lpszFaceName, CDC* pDC = nullptr); + BOOL CreatePointFontIndirect(const LOGFONT* lpLogFont, CDC* pDC = nullptr); + int GetLogFont(LOGFONT* pLogFont); + static CFont* FromHandle(HFONT hFont) { return static_cast(CGdiObject::FromHandle(hFont)); } + operator HFONT() const { return m_hObject; } +}; + +class CBitmap : public CGdiObject { + DECLARE_DYNAMIC(CBitmap) +public: + CBitmap() = default; + BOOL LoadBitmap(const char* lpszResourceName); + BOOL LoadBitmap(UINT nIDResource); + BOOL LoadBitmapA(const char* lpszResourceName) { return LoadBitmap(lpszResourceName); } + BOOL LoadBitmapA(UINT nIDResource) { return LoadBitmap(nIDResource); } + BOOL LoadOEMBitmap(UINT nIDBitmap); + BOOL LoadMappedBitmap(UINT nIDBitmap, UINT nFlags = 0, void* lpColorMap = nullptr, int nMapSize = 0); + BOOL CreateBitmap(int nWidth, int nHeight, UINT nPlanes, UINT nBitcount, const void* lpBits); + BOOL CreateBitmapIndirect(LPBITMAP lpBitmap); + BOOL CreateCompatibleBitmap(CDC* pDC, int nWidth, int nHeight); + BOOL CreateDiscardableBitmap(CDC* pDC, int nWidth, int nHeight); + int GetBitmap(BITMAP* pBitMap); + DWORD SetBitmapBits(DWORD dwCount, const void* lpBits); + DWORD GetBitmapBits(DWORD dwCount, LPVOID lpBits) const; + CSize SetBitmapDimension(int nWidth, int nHeight); + CSize GetBitmapDimension() const; + static CBitmap* FromHandle(HBITMAP hBitmap) { return static_cast(CGdiObject::FromHandle(hBitmap)); } + operator HBITMAP() const { return m_hObject; } +}; + +class CRgn : public CGdiObject { + DECLARE_DYNAMIC(CRgn) +public: + CRgn() = default; + BOOL CreateRectRgn(int x1, int y1, int x2, int y2); + BOOL CreateRectRgnIndirect(LPCRECT lpRect); + BOOL CreateEllipticRgn(int x1, int y1, int x2, int y2); + BOOL CreateEllipticRgnIndirect(LPCRECT lpRect); + BOOL CreatePolygonRgn(LPPOINT lpPoints, int nCount, int nMode); + BOOL CreateRoundRectRgn(int x1, int y1, int x2, int y2, int x3, int y3); + int CombineRgn(CRgn* pRgn1, CRgn* pRgn2, int nCombineMode); + int CopyRgn(CRgn* pRgnSrc); + BOOL PtInRegion(int x, int y) const; + BOOL PtInRegion(POINT point) const { return PtInRegion(point.x, point.y); } + BOOL RectInRegion(LPCRECT lpRect) const; + int GetRgnBox(LPRECT lpRect) const; + void SetRectRgn(int x1, int y1, int x2, int y2); + int OffsetRgn(int x, int y); + static CRgn* FromHandle(HRGN hRgn) { return static_cast(CGdiObject::FromHandle(hRgn)); } + operator HRGN() const { return m_hObject; } +}; + +typedef struct tagLOGPALETTE* LPLOGPALETTE; +class CPalette : public CGdiObject { + DECLARE_DYNAMIC(CPalette) +public: + CPalette() = default; + BOOL CreatePalette(LPLOGPALETTE) { return TRUE; } + BOOL CreateHalftonePalette(CDC*) { return TRUE; } + operator HPALETTE() const { return m_hObject; } +}; + +class CDC : public CObject { + DECLARE_DYNCREATE(CDC) +public: + CDC(); + ~CDC() override; + + HDC m_hDC; + HDC m_hAttribDC; + operator HDC() const { return AfxIsNullThis(this) ? nullptr : m_hDC; } + HDC GetSafeHdc() const { return AfxIsNullThis(this) ? nullptr : m_hDC; } + static CDC* FromHandle(HDC hDC); + static void DeleteTempMap(); + BOOL Attach(HDC hDC); + HDC Detach(); + BOOL CreateCompatibleDC(CDC* pDC); + BOOL CreateDC(const char*, const char*, const char*, const void*) { return FALSE; } + BOOL CreateIC(const char*, const char*, const char*, const void*) { return FALSE; } + BOOL DeleteDC(); + CWnd* GetWindow() const; + BOOL IsPrinting() const { return m_bPrinting; } + BOOL m_bPrinting; + + int SaveDC(); + BOOL RestoreDC(int nSavedDC); + CGdiObject* SelectStockObject(int nIndex); + CPen* SelectObject(CPen* pPen); + CBrush* SelectObject(CBrush* pBrush); + CFont* SelectObject(CFont* pFont); + CBitmap* SelectObject(CBitmap* pBitmap); + int SelectObject(CRgn* pRgn); + CGdiObject* SelectObject(CGdiObject* pObject); + HGDIOBJ SelectObject(HGDIOBJ hObject); + CPalette* SelectPalette(CPalette* pPalette, BOOL bForceBackground); + UINT RealizePalette() { return 0; } + CPen* GetCurrentPen() const; + CBrush* GetCurrentBrush() const; + CFont* GetCurrentFont() const; + CBitmap* GetCurrentBitmap() const; + int GetDeviceCaps(int nIndex) const; + + COLORREF GetBkColor() const; + COLORREF SetBkColor(COLORREF crColor); + int GetBkMode() const; + int SetBkMode(int nBkMode); + COLORREF GetTextColor() const; + COLORREF SetTextColor(COLORREF crColor); + int GetROP2() const; + int SetROP2(int nDrawMode); + int SetStretchBltMode(int nStretchMode); + int GetStretchBltMode() const; + UINT SetTextAlign(UINT nFlags); + UINT GetTextAlign() const; + int SetTextCharacterExtra(int) { return 0; } + int SetMapMode(int nMapMode); + int GetMapMode() const; + CPoint SetViewportOrg(int x, int y); + CPoint SetViewportOrg(POINT point) { return SetViewportOrg(point.x, point.y); } + CPoint GetViewportOrg() const; + CPoint OffsetViewportOrg(int nWidth, int nHeight); + CSize SetViewportExt(int cx, int cy); + CSize SetViewportExt(SIZE size) { return SetViewportExt(size.cx, size.cy); } + CSize GetViewportExt() const; + CPoint SetWindowOrg(int x, int y); + CPoint SetWindowOrg(POINT point) { return SetWindowOrg(point.x, point.y); } + CPoint GetWindowOrg() const; + CPoint OffsetWindowOrg(int nWidth, int nHeight); + CSize SetWindowExt(int cx, int cy); + CSize SetWindowExt(SIZE size) { return SetWindowExt(size.cx, size.cy); } + CSize GetWindowExt() const; + void DPtoLP(LPPOINT lpPoints, int nCount = 1) const; + void DPtoLP(LPRECT lpRect) const; + void DPtoLP(LPSIZE lpSize) const; + void LPtoDP(LPPOINT lpPoints, int nCount = 1) const; + void LPtoDP(LPRECT lpRect) const; + void LPtoDP(LPSIZE lpSize) const; + + int GetClipBox(LPRECT lpRect) const; + int SelectClipRgn(CRgn* pRgn); + int IntersectClipRect(int x1, int y1, int x2, int y2); + int IntersectClipRect(LPCRECT lpRect); + int ExcludeClipRect(int, int, int, int) { return SIMPLEREGION; } + int ExcludeClipRect(LPCRECT) { return SIMPLEREGION; } + BOOL PtVisible(int, int) const { return TRUE; } + BOOL RectVisible(LPCRECT) const { return TRUE; } + + CPoint GetCurrentPosition() const; + CPoint MoveTo(int x, int y); + CPoint MoveTo(POINT point) { return MoveTo(point.x, point.y); } + BOOL LineTo(int x, int y); + BOOL LineTo(POINT point) { return LineTo(point.x, point.y); } + BOOL Arc(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4); + BOOL Arc(LPCRECT lpRect, POINT ptStart, POINT ptEnd); + BOOL Polyline(const POINT* lpPoints, int nCount); + BOOL PolyBezier(const POINT* lpPoints, int nCount); + BOOL PolylineTo(const POINT* lpPoints, int nCount); + void FillRect(LPCRECT lpRect, CBrush* pBrush); + void FrameRect(LPCRECT lpRect, CBrush* pBrush); + void InvertRect(LPCRECT lpRect); + BOOL DrawIcon(int x, int y, HICON hIcon); + BOOL DrawIcon(POINT point, HICON hIcon) { return DrawIcon(point.x, point.y, hIcon); } + BOOL DrawEdge(LPRECT lpRect, UINT nEdge, UINT nFlags); + BOOL DrawFrameControl(LPRECT lpRect, UINT nType, UINT nState); + void DrawFocusRect(LPCRECT lpRect); + BOOL Chord(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4); + BOOL Ellipse(int x1, int y1, int x2, int y2); + BOOL Ellipse(LPCRECT lpRect) { return Ellipse(lpRect->left, lpRect->top, lpRect->right, lpRect->bottom); } + BOOL Pie(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4); + BOOL Pie(LPCRECT lpRect, POINT ptStart, POINT ptEnd); + BOOL Polygon(const POINT* lpPoints, int nCount); + BOOL PolyPolygon(const POINT* lpPoints, const INT* lpPolyCounts, int nCount); + BOOL Rectangle(int x1, int y1, int x2, int y2); + BOOL Rectangle(LPCRECT lpRect) { return Rectangle(lpRect->left, lpRect->top, lpRect->right, lpRect->bottom); } + BOOL RoundRect(int x1, int y1, int x2, int y2, int x3, int y3); + BOOL RoundRect(LPCRECT lpRect, POINT point); + void FillSolidRect(LPCRECT lpRect, COLORREF clr); + void FillSolidRect(int x, int y, int cx, int cy, COLORREF clr); + void Draw3dRect(LPCRECT lpRect, COLORREF clrTopLeft, COLORREF clrBottomRight); + void Draw3dRect(int x, int y, int cx, int cy, COLORREF clrTopLeft, COLORREF clrBottomRight); + BOOL PatBlt(int x, int y, int nWidth, int nHeight, DWORD dwRop); + BOOL BitBlt(int x, int y, int nWidth, int nHeight, CDC* pSrcDC, int xSrc, int ySrc, DWORD dwRop); + BOOL StretchBlt(int x, int y, int nWidth, int nHeight, CDC* pSrcDC, int xSrc, int ySrc, int nSrcWidth, + int nSrcHeight, DWORD dwRop); + BOOL TransparentBlt(int xDest, int yDest, int nDestWidth, int nDestHeight, CDC* pSrcDC, int xSrc, int ySrc, + int nSrcWidth, int nSrcHeight, UINT clrTransparent); + COLORREF GetPixel(int x, int y) const; + COLORREF GetPixel(POINT point) const { return GetPixel(point.x, point.y); } + COLORREF SetPixel(int x, int y, COLORREF crColor); + COLORREF SetPixel(POINT point, COLORREF crColor) { return SetPixel(point.x, point.y, crColor); } + BOOL SetPixelV(int x, int y, COLORREF crColor) { SetPixel(x, y, crColor); return TRUE; } + BOOL FloodFill(int x, int y, COLORREF crColor); + BOOL ExtFloodFill(int x, int y, COLORREF crColor, UINT nFillType); + BOOL FillRgn(CRgn* pRgn, CBrush* pBrush); + BOOL FrameRgn(CRgn* pRgn, CBrush* pBrush, int nWidth, int nHeight); + BOOL PaintRgn(CRgn* pRgn); + + BOOL TextOut(int x, int y, const char* lpszString, int nCount); + BOOL TextOut(int x, int y, const CString& str); + BOOL ExtTextOut(int x, int y, UINT nOptions, LPCRECT lpRect, const char* lpszString, UINT nCount, + LPINT lpDxWidths); + BOOL ExtTextOut(int x, int y, UINT nOptions, LPCRECT lpRect, const CString& str, LPINT lpDxWidths); + CSize TabbedTextOut(int x, int y, const char* lpszString, int nCount, int nTabPositions, LPINT lpnTabStopPositions, + int nTabOrigin); + int DrawText(const char* lpszString, int nCount, LPRECT lpRect, UINT nFormat); + int DrawText(const CString& str, LPRECT lpRect, UINT nFormat); + CSize GetTextExtent(const char* lpszString, int nCount) const; + CSize GetTextExtent(const CString& str) const; + CSize GetOutputTextExtent(const char* lpszString, int nCount) const { return GetTextExtent(lpszString, nCount); } + CSize GetOutputTextExtent(const CString& str) const { return GetTextExtent(str); } + CSize GetTabbedTextExtent(const char* lpszString, int nCount, int nTabPositions, LPINT lpnTabStopPositions) const; + BOOL GetTextExtentExPoint(const char* lpszString, int nCount, int nMaxExtent, LPINT lpnFit, LPINT alpDx, + LPSIZE lpSize) const; + BOOL GetTextMetrics(LPTEXTMETRIC lpMetrics) const; + BOOL GetOutputTextMetrics(LPTEXTMETRIC lpMetrics) const { return GetTextMetrics(lpMetrics); } + int GetTextFace(int nCount, char* lpszFacename) const; + int GetTextFace(CString& rString) const; + BOOL GetCharWidth(UINT nFirstChar, UINT nLastChar, LPINT lpBuffer) const; + int SetTextJustification(int, int) { return 1; } + + int StartDoc(const char*) { return 0; } + int EndDoc() { return 0; } + int StartPage() { return 0; } + int EndPage() { return 0; } + int AbortDoc() { return 0; } + + // implementation: m_hDC points to an mfcwx::DCState owned by this CDC (or by the creator of a + // temporary wrapper); GetWx() returns the wx device context it draws on. + wxDC* GetWx() const; +}; + +class CPaintDC : public CDC { + DECLARE_DYNAMIC(CPaintDC) +public: + explicit CPaintDC(CWnd* pWnd); + ~CPaintDC() override; + PAINTSTRUCT m_ps; + +protected: + HWND m_hWnd; +}; + +class CClientDC : public CDC { + DECLARE_DYNAMIC(CClientDC) +public: + explicit CClientDC(CWnd* pWnd); + ~CClientDC() override; + +protected: + HWND m_hWnd; +}; + +class CWindowDC : public CDC { + DECLARE_DYNAMIC(CWindowDC) +public: + explicit CWindowDC(CWnd* pWnd); + ~CWindowDC() override; + +protected: + HWND m_hWnd; +}; + +// --------------------------------------------------------------------------------------------- +// Menus + +class CMenu : public CObject { + DECLARE_DYNCREATE(CMenu) +public: + CMenu(); + ~CMenu() override; + HMENU m_hMenu; + operator HMENU() const { return AfxIsNullThis(this) ? nullptr : m_hMenu; } + HMENU GetSafeHmenu() const { return AfxIsNullThis(this) ? nullptr : m_hMenu; } + static CMenu* FromHandle(HMENU hMenu); + BOOL Attach(HMENU hMenu); + HMENU Detach(); + BOOL CreateMenu(); + BOOL CreatePopupMenu(); + BOOL LoadMenu(const char* lpszResourceName); + BOOL LoadMenu(UINT nIDResource); + BOOL DestroyMenu(); + BOOL DeleteMenu(UINT nPosition, UINT nFlags); + BOOL RemoveMenu(UINT nPosition, UINT nFlags); + BOOL AppendMenu(UINT nFlags, UINT_PTR nIDNewItem = 0, const char* lpszNewItem = nullptr); + BOOL InsertMenu(UINT nPosition, UINT nFlags, UINT_PTR nIDNewItem = 0, const char* lpszNewItem = nullptr); + BOOL ModifyMenu(UINT nPosition, UINT nFlags, UINT_PTR nIDNewItem = 0, const char* lpszNewItem = nullptr); + UINT CheckMenuItem(UINT nIDCheckItem, UINT nCheck); + BOOL CheckMenuRadioItem(UINT nIDFirst, UINT nIDLast, UINT nIDItem, UINT nFlags); + UINT EnableMenuItem(UINT nIDEnableItem, UINT nEnable); + UINT GetMenuItemCount() const; + UINT GetMenuItemID(int nPos) const; + UINT GetMenuState(UINT nID, UINT nFlags) const; + int GetMenuString(UINT nIDItem, char* lpString, int nMaxCount, UINT nFlags) const; + int GetMenuString(UINT nIDItem, CString& rString, UINT nFlags) const; + CMenu* GetSubMenu(int nPos) const; + BOOL SetDefaultItem(UINT, BOOL = FALSE) { return TRUE; } + BOOL TrackPopupMenu(UINT nFlags, int x, int y, CWnd* pWnd, LPCRECT lpRect = nullptr); + BOOL SetMenuItemBitmaps(UINT, UINT, const CBitmap*, const CBitmap*) { return TRUE; } + + // implementation + wxMenu* GetWxMenu() const; + wxMenuBar* GetWxMenuBar() const; +}; + +// --------------------------------------------------------------------------------------------- +// Controls (no data members: methods operate on m_hWnd so temporary CWnd wrappers can be cast) + +class CStatic : public CWnd { + DECLARE_DYNAMIC(CStatic) +public: + CStatic() = default; + BOOL Create(const char* lpszText, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID = 0xffff); + HICON SetIcon(HICON hIcon); + HICON GetIcon() const; + HBITMAP SetBitmap(HBITMAP hBitmap); + HBITMAP GetBitmap() const; + HCURSOR SetCursor(HCURSOR hCursor); + HCURSOR GetCursor(); + virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct); +}; + +class CButton : public CWnd { + DECLARE_DYNAMIC(CButton) +public: + CButton() = default; + BOOL Create(const char* lpszCaption, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID); + UINT GetState() const; + void SetState(BOOL bHighlight); + int GetCheck() const; + void SetCheck(int nCheck); + UINT GetButtonStyle() const; + void SetButtonStyle(UINT nStyle, BOOL bRedraw = TRUE); + HICON SetIcon(HICON hIcon); + HICON GetIcon() const; + HBITMAP SetBitmap(HBITMAP hBitmap); + HBITMAP GetBitmap() const; + HCURSOR SetCursor(HCURSOR hCursor); + HCURSOR GetCursor(); + virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct); +}; + +class CEdit : public CWnd { + DECLARE_DYNAMIC(CEdit) +public: + CEdit() = default; + BOOL Create(DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID); + BOOL CanUndo() const; + int GetLineCount() const; + BOOL GetModify() const; + void SetModify(BOOL bModified = TRUE); + void GetRect(LPRECT lpRect) const; + DWORD GetSel() const; + void GetSel(int& nStartChar, int& nEndChar) const; + int GetLine(int nIndex, char* lpszBuffer) const; + int GetLine(int nIndex, char* lpszBuffer, int nMaxLength) const; + void EmptyUndoBuffer(); + BOOL FmtLines(BOOL) { return TRUE; } + void LimitText(int nChars = 0); + int LineFromChar(int nIndex = -1) const; + int LineIndex(int nLine = -1) const; + int LineLength(int nLine = -1) const; + void LineScroll(int nLines, int nChars = 0); + void ReplaceSel(const char* lpszNewText, BOOL bCanUndo = FALSE); + void SetPasswordChar(char ch); + void SetRect(LPCRECT) {} + void SetRectNP(LPCRECT) {} + void SetSel(DWORD dwSelection, BOOL bNoScroll = FALSE); + void SetSel(int nStartChar, int nEndChar, BOOL bNoScroll = FALSE); + BOOL SetTabStops(int nTabStops, LPINT rgTabStops); + void SetTabStops() {} + BOOL SetTabStops(const int&) { return TRUE; } + BOOL Undo(); + void Clear(); + void Copy(); + void Cut(); + void Paste(); + BOOL SetReadOnly(BOOL bReadOnly = TRUE); + int GetFirstVisibleLine() const; + char GetPasswordChar() const; + void SetMargins(UINT, UINT) {} + DWORD GetMargins() const { return 0; } + void SetLimitText(UINT nMax); + UINT GetLimitText() const; + CPoint PosFromChar(UINT nChar) const; + int CharFromPos(CPoint pt) const; +}; + +class CListBox : public CWnd { + DECLARE_DYNAMIC(CListBox) +public: + CListBox() = default; + BOOL Create(DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID); + int GetCount() const; + int GetHorizontalExtent() const { return 0; } + void SetHorizontalExtent(int) {} + int GetTopIndex() const; + int SetTopIndex(int nIndex); + DWORD_PTR GetItemData(int nIndex) const; + int SetItemData(int nIndex, DWORD_PTR dwItemData); + void* GetItemDataPtr(int nIndex) const { return reinterpret_cast(GetItemData(nIndex)); } + int SetItemDataPtr(int nIndex, void* pData) { return SetItemData(nIndex, reinterpret_cast(pData)); } + int GetItemRect(int nIndex, LPRECT lpRect) const; + int GetSel(int nIndex) const; + int GetText(int nIndex, char* lpszBuffer) const; + void GetText(int nIndex, CString& rString) const; + int GetTextLen(int nIndex) const; + void SetColumnWidth(int) {} + BOOL SetTabStops(int, LPINT) { return TRUE; } + void SetTabStops() {} + BOOL SetTabStops(const int&) { return TRUE; } + int SetItemHeight(int, UINT) { return 0; } + int GetItemHeight(int) const { return 16; } + int FindStringExact(int nIndexStart, const char* lpszFind) const; + int GetCaretIndex() const; + int SetCaretIndex(int nIndex, BOOL bScroll = TRUE); + int GetCurSel() const; + int SetCurSel(int nSelect); + int SetSel(int nIndex, BOOL bSelect = TRUE); + int GetSelCount() const; + int GetSelItems(int nMaxItems, LPINT rgIndex) const; + int SelItemRange(BOOL bSelect, int nFirstItem, int nLastItem); + int AddString(const char* lpszItem); + int DeleteString(UINT nIndex); + int InsertString(int nIndex, const char* lpszItem); + void ResetContent(); + int Dir(UINT, const char*) { return LB_ERR; } + int FindString(int nStartAfter, const char* lpszItem) const; + int SelectString(int nStartAfter, const char* lpszItem); + int GetAnchorIndex() const { return GetCaretIndex(); } + void SetAnchorIndex(int) {} + UINT ItemFromPoint(CPoint pt, BOOL& bOutside) const; +}; + +class CCheckListBox : public CListBox { + DECLARE_DYNAMIC(CCheckListBox) +public: + CCheckListBox() = default; + void SetCheckStyle(UINT) {} + UINT GetCheckStyle() { return BS_AUTOCHECKBOX; } + void SetCheck(int nIndex, int nCheck); + int GetCheck(int nIndex); + void Enable(int nIndex, BOOL bEnabled = TRUE); + BOOL IsEnabled(int nIndex); +}; + +class CComboBox : public CWnd { + DECLARE_DYNAMIC(CComboBox) +public: + CComboBox() = default; + BOOL Create(DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID); + int GetCount() const; + int GetCurSel() const; + int SetCurSel(int nSelect); + DWORD GetEditSel() const; + BOOL LimitText(int nMaxChars); + BOOL SetEditSel(int nStartChar, int nEndChar); + DWORD_PTR GetItemData(int nIndex) const; + int SetItemData(int nIndex, DWORD_PTR dwItemData); + void* GetItemDataPtr(int nIndex) const { return reinterpret_cast(GetItemData(nIndex)); } + int SetItemDataPtr(int nIndex, void* pData) { return SetItemData(nIndex, reinterpret_cast(pData)); } + int GetLBText(int nIndex, char* lpszText) const; + void GetLBText(int nIndex, CString& rString) const; + int GetLBTextLen(int nIndex) const; + int SetItemHeight(int, UINT) { return 0; } + int GetItemHeight(int) const { return 16; } + int FindStringExact(int nIndexStart, const char* lpszFind) const; + int SetExtendedUI(BOOL = TRUE) { return CB_OKAY; } + BOOL GetExtendedUI() const { return FALSE; } + void GetDroppedControlRect(LPRECT lprect) const; + BOOL GetDroppedState() const { return FALSE; } + int GetDroppedWidth() const { return 0; } + int SetDroppedWidth(UINT) { return 0; } + int GetTopIndex() const { return 0; } + int SetTopIndex(int) { return 0; } + void ShowDropDown(BOOL bShowIt = TRUE); + int AddString(const char* lpszString); + int DeleteString(UINT nIndex); + int InsertString(int nIndex, const char* lpszString); + void ResetContent(); + int Dir(UINT, const char*) { return CB_ERR; } + int FindString(int nStartAfter, const char* lpszString) const; + int SelectString(int nStartAfter, const char* lpszString); + void Clear(); + void Copy(); + void Cut(); + void Paste(); +}; + +class CScrollBar : public CWnd { + DECLARE_DYNAMIC(CScrollBar) +public: + CScrollBar() = default; + BOOL Create(DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID); + int GetScrollPos() const; + int SetScrollPos(int nPos, BOOL bRedraw = TRUE); + void GetScrollRange(LPINT lpMinPos, LPINT lpMaxPos) const; + void SetScrollRange(int nMinPos, int nMaxPos, BOOL bRedraw = TRUE); + void ShowScrollBar(BOOL bShow = TRUE); + BOOL EnableScrollBar(UINT nArrowFlags = ESB_ENABLE_BOTH); + BOOL SetScrollInfo(LPSCROLLINFO lpScrollInfo, BOOL bRedraw = TRUE); + BOOL GetScrollInfo(LPSCROLLINFO lpScrollInfo, UINT nMask = SIF_ALL); + int GetScrollLimit(); +}; + +// --------------------------------------------------------------------------------------------- +// Dialogs and data exchange + +class CDataExchange { +public: + CDataExchange(CWnd* pDlgWnd, BOOL bSaveAndValidate); + BOOL m_bSaveAndValidate; + CWnd* m_pDlgWnd; + HWND m_hWndLastControl; + BOOL m_bEditLastControl; + HWND PrepareCtrl(int nIDC); + HWND PrepareEditCtrl(int nIDC); + void Fail(); +}; + +class CDialog : public CWnd { + DECLARE_DYNAMIC(CDialog) +public: + CDialog(); + explicit CDialog(const char* lpszTemplateName, CWnd* pParentWnd = nullptr); + explicit CDialog(UINT nIDTemplate, CWnd* pParentWnd = nullptr); + ~CDialog() override; + + virtual BOOL Create(const char* lpszTemplateName, CWnd* pParentWnd = nullptr); + virtual BOOL Create(UINT nIDTemplate, CWnd* pParentWnd = nullptr); + BOOL CreateIndirect(const void* lpDialogTemplate, CWnd* pParentWnd = nullptr, void* lpDialogInit = nullptr); + BOOL InitModalIndirect(const void* lpDialogTemplate, CWnd* pParentWnd = nullptr, void* lpDialogInit = nullptr); + virtual INT_PTR DoModal(); + void EndDialog(int nResult); + void MapDialogRect(LPRECT lpRect) const; + void SetHelpID(UINT nIDR) { m_nIDHelp = nIDR; } + void SetDefID(UINT nID); + DWORD GetDefID() const; + void NextDlgCtrl() const; + void PrevDlgCtrl() const; + void GotoDlgCtrl(CWnd* pWndCtrl); + BOOL CheckAutoCenter() { return TRUE; } + virtual BOOL OnInitDialog(); + virtual void OnSetFont(CFont*) {} + BOOL ContinueModal() { return m_bModalRunning; } + BOOL DestroyWindow() override; + + UINT m_nIDHelp; + const char* m_lpszTemplateName; + UINT m_nIDTemplate; + CWnd* m_pParentWnd; + BOOL m_bModalRunning; + int m_nModalResult; + +protected: + virtual void OnOK(); + virtual void OnCancel(); + afx_msg void OnHelp(); + afx_msg LRESULT HandleInitDialog(WPARAM, LPARAM); + BOOL CreateFromTemplate(BOOL bModal); + friend struct mfcwx::WindowState; + + DECLARE_MESSAGE_MAP() +}; + +void AFXAPI DDX_Control(CDataExchange* pDX, int nIDC, CWnd& rControl); +void AFXAPI DDX_Text(CDataExchange* pDX, int nIDC, BYTE& value); +void AFXAPI DDX_Text(CDataExchange* pDX, int nIDC, short& value); +void AFXAPI DDX_Text(CDataExchange* pDX, int nIDC, int& value); +void AFXAPI DDX_Text(CDataExchange* pDX, int nIDC, UINT& value); +void AFXAPI DDX_Text(CDataExchange* pDX, int nIDC, long& value); +void AFXAPI DDX_Text(CDataExchange* pDX, int nIDC, unsigned long& value); +void AFXAPI DDX_Text(CDataExchange* pDX, int nIDC, LONG& value); +void AFXAPI DDX_Text(CDataExchange* pDX, int nIDC, DWORD& value); +void AFXAPI DDX_Text(CDataExchange* pDX, int nIDC, LONGLONG& value); +void AFXAPI DDX_Text(CDataExchange* pDX, int nIDC, ULONGLONG& value); +void AFXAPI DDX_Text(CDataExchange* pDX, int nIDC, CString& value); +void AFXAPI DDX_Text(CDataExchange* pDX, int nIDC, char* value, int nMaxLen); +void AFXAPI DDX_Text(CDataExchange* pDX, int nIDC, float& value); +void AFXAPI DDX_Text(CDataExchange* pDX, int nIDC, double& value); +void AFXAPI DDX_Check(CDataExchange* pDX, int nIDC, int& value); +void AFXAPI DDX_Check(CDataExchange* pDX, int nIDC, bool& value); +void AFXAPI DDX_Radio(CDataExchange* pDX, int nIDC, int& value); +void AFXAPI DDX_LBString(CDataExchange* pDX, int nIDC, CString& value); +void AFXAPI DDX_CBString(CDataExchange* pDX, int nIDC, CString& value); +void AFXAPI DDX_LBIndex(CDataExchange* pDX, int nIDC, int& index); +void AFXAPI DDX_CBIndex(CDataExchange* pDX, int nIDC, int& index); +void AFXAPI DDX_LBStringExact(CDataExchange* pDX, int nIDC, CString& value); +void AFXAPI DDX_CBStringExact(CDataExchange* pDX, int nIDC, CString& value); +void AFXAPI DDX_Scroll(CDataExchange* pDX, int nIDC, int& value); +void AFXAPI DDX_Slider(CDataExchange* pDX, int nIDC, int& value); + +void AFXAPI DDV_MaxChars(CDataExchange* pDX, const CString& value, int nChars); +void AFXAPI DDV_MinMaxByte(CDataExchange* pDX, BYTE value, BYTE minVal, BYTE maxVal); +void AFXAPI DDV_MinMaxShort(CDataExchange* pDX, short value, short minVal, short maxVal); +void AFXAPI DDV_MinMaxInt(CDataExchange* pDX, int value, int minVal, int maxVal); +void AFXAPI DDV_MinMaxLong(CDataExchange* pDX, long value, long minVal, long maxVal); +void AFXAPI DDV_MinMaxUInt(CDataExchange* pDX, UINT value, UINT minVal, UINT maxVal); +void AFXAPI DDV_MinMaxDWord(CDataExchange* pDX, DWORD value, DWORD minVal, DWORD maxVal); +void AFXAPI DDV_MinMaxLongLong(CDataExchange* pDX, LONGLONG value, LONGLONG minVal, LONGLONG maxVal); +void AFXAPI DDV_MinMaxULongLong(CDataExchange* pDX, ULONGLONG value, ULONGLONG minVal, ULONGLONG maxVal); +void AFXAPI DDV_MinMaxFloat(CDataExchange* pDX, float const& value, float minVal, float maxVal); +void AFXAPI DDV_MinMaxDouble(CDataExchange* pDX, double const& value, double minVal, double maxVal); +void AFXAPI DDV_MinMaxSlider(CDataExchange* pDX, DWORD value, DWORD minVal, DWORD maxVal); + +// --------------------------------------------------------------------------------------------- +// Threads and application + +typedef UINT (*AFX_THREADPROC)(LPVOID); + +class CWinThread : public CCmdTarget { + DECLARE_DYNAMIC(CWinThread) +public: + CWinThread(); + CWinThread(AFX_THREADPROC pfnThreadProc, LPVOID pParam); + ~CWinThread() override; + BOOL CreateThread(DWORD dwCreateFlags = 0, UINT nStackSize = 0, LPSECURITY_ATTRIBUTES lpSecurityAttrs = nullptr); + int GetThreadPriority(); + BOOL SetThreadPriority(int nPriority); + DWORD SuspendThread(); + DWORD ResumeThread(); + BOOL PostThreadMessage(UINT message, WPARAM wParam, LPARAM lParam); + virtual BOOL InitInstance(); + virtual int ExitInstance(); + virtual int Run(); + virtual BOOL PreTranslateMessage(MSG* pMsg); + virtual BOOL PumpMessage(); + virtual BOOL OnIdle(LONG lCount); + virtual BOOL IsIdleMessage(MSG*) { return TRUE; } + virtual CWnd* GetMainWnd(); + operator HANDLE() const { return m_hThread; } + + CWnd* m_pMainWnd; + CWnd* m_pActiveWnd; + BOOL m_bAutoDelete; + HANDLE m_hThread; + DWORD m_nThreadID; + AFX_THREADPROC m_pfnThreadProc; + LPVOID m_pThreadParams; + MSG m_msgCur; +}; + +CWinThread* AFXAPI AfxBeginThread(AFX_THREADPROC pfnThreadProc, LPVOID pParam, int nPriority = THREAD_PRIORITY_NORMAL, + UINT nStackSize = 0, DWORD dwCreateFlags = 0, + LPSECURITY_ATTRIBUTES lpSecurityAttrs = nullptr); +CWinThread* AFXAPI AfxBeginThread(CRuntimeClass* pThreadClass, int nPriority = THREAD_PRIORITY_NORMAL, + UINT nStackSize = 0, DWORD dwCreateFlags = 0, + LPSECURITY_ATTRIBUTES lpSecurityAttrs = nullptr); +void AFXAPI AfxEndThread(UINT nExitCode, BOOL bDelete = TRUE); +CWinThread* AFXAPI AfxGetThread(); + +class CRecentFileList { +public: + CRecentFileList(UINT nStart, const char* lpszSection, const char* lpszEntryFormat, int nSize, + int nMaxDispLen = 30); + virtual ~CRecentFileList() = default; + int GetSize() const { return m_nSize; } + CString& operator[](int nIndex) { return m_arrNames[nIndex]; } + virtual void Remove(int nIndex); + virtual void Add(const char* lpszPathName); + virtual BOOL GetDisplayName(CString& strName, int nIndex, const char* lpszCurDir, int nCurDir, + BOOL bAtLeastName = TRUE) const; + virtual void UpdateMenu(CCmdUI* pCmdUI); + virtual void ReadList(); + virtual void WriteList(); + + int m_nSize; + std::vector m_arrNames; + CString m_strSectionName; + CString m_strEntryFormat; + UINT m_nStart; + int m_nMaxDisplayLength; +}; + +class CWinApp : public CWinThread { + DECLARE_DYNAMIC(CWinApp) +public: + explicit CWinApp(const char* lpszAppName = nullptr); + ~CWinApp() override; + + HINSTANCE m_hInstance; + HINSTANCE m_hPrevInstance; + char* m_lpCmdLine; + int m_nCmdShow; + const char* m_pszAppName; + const char* m_pszRegistryKey; + const char* m_pszExeName; + const char* m_pszHelpFilePath; + const char* m_pszProfileName; + CRecentFileList* m_pRecentFileList; + BOOL m_bHelpMode; + WORD m_nWaitCursorCount; + HCURSOR m_hcurWaitCursorRestore; + int m_eHelpType; + + BOOL InitInstance() override; + int ExitInstance() override; + int Run() override; + BOOL OnIdle(LONG lCount) override; + virtual BOOL InitApplication(); + virtual CDocument* OpenDocumentFile(const char* lpszFileName); + virtual void AddToRecentFileList(const char* lpszPathName); + virtual BOOL SaveAllModified(); + virtual void CloseAllDocuments(BOOL bEndSession); + virtual int DoMessageBox(const char* lpszPrompt, UINT nType, UINT nIDPrompt); + virtual void DoWaitCursor(int nCode); + virtual void WinHelp(DWORD_PTR dwData, UINT nCmd = HELP_CONTEXT); + virtual void WinHelpInternal(DWORD_PTR dwData, UINT nCmd = HELP_CONTEXT); + virtual void HtmlHelp(DWORD_PTR dwData, UINT nCmd = 0x000F); + virtual BOOL ProcessShellCommand(class CCommandLineInfo& rCmdInfo); + virtual void ParseCommandLine(class CCommandLineInfo& rCmdInfo); + virtual BOOL DoPromptFileName(CString& fileName, UINT nIDSTitle, DWORD lFlags, BOOL bOpenFileDialog, + CDocTemplate* pTemplate); + + void AddDocTemplate(CDocTemplate* pTemplate); + POSITION GetFirstDocTemplatePosition() const; + CDocTemplate* GetNextDocTemplate(POSITION& pos) const; + HCURSOR LoadCursor(const char* lpszResourceName) const; + HCURSOR LoadCursor(UINT nIDResource) const; + HCURSOR LoadStandardCursor(const char* lpszCursorName) const; + HCURSOR LoadOEMCursor(UINT nIDCursor) const; + HICON LoadIcon(const char* lpszResourceName) const; + HICON LoadIcon(UINT nIDResource) const; + HICON LoadStandardIcon(const char* lpszIconName) const; + HICON LoadOEMIcon(UINT nIDIcon) const; + UINT GetProfileInt(const char* lpszSection, const char* lpszEntry, int nDefault); + BOOL WriteProfileInt(const char* lpszSection, const char* lpszEntry, int nValue); + CString GetProfileString(const char* lpszSection, const char* lpszEntry, const char* lpszDefault = nullptr); + BOOL WriteProfileString(const char* lpszSection, const char* lpszEntry, const char* lpszValue); + BOOL GetProfileBinary(const char* lpszSection, const char* lpszEntry, LPBYTE* ppData, UINT* pBytes); + BOOL WriteProfileBinary(const char* lpszSection, const char* lpszEntry, LPBYTE pData, UINT nBytes); + void SetRegistryKey(const char* lpszRegistryKey); + void SetRegistryKey(UINT nIDRegistryKey); + void LoadStdProfileSettings(UINT nMaxMRU = 4); + void EnableShellOpen() {} + void RegisterShellFileTypes(BOOL = FALSE) {} + void UnregisterShellFileTypes() {} + void Enable3dControls() {} + void Enable3dControlsStatic() {} + void SetDialogBkColor(COLORREF = 0, COLORREF = 0) {} + void EnableHtmlHelp() {} + void HideApplication(); + BOOL GetPrinterDeviceDefaults(void*) { return FALSE; } + void SelectPrinter(HANDLE, HANDLE, BOOL = TRUE) {} + CWnd* GetMainWnd() override { return m_pMainWnd; } + + afx_msg void OnFileNew(); + afx_msg void OnFileOpen(); + afx_msg void OnFilePrintSetup(); + afx_msg void OnAppExit(); + afx_msg void OnHelp(); + afx_msg void OnHelpFinder(); + afx_msg void OnHelpIndex(); + afx_msg void OnContextHelp(); + afx_msg void OnHelpUsing(); + afx_msg BOOL OnOpenRecentFile(UINT nID); + afx_msg void OnUpdateRecentFileMenu(CCmdUI* pCmdUI); + + std::vector m_templates; + +protected: + DECLARE_MESSAGE_MAP() +}; + +class CCommandLineInfo : public CObject { +public: + CCommandLineInfo(); + virtual void ParseParam(const char* pszParam, BOOL bFlag, BOOL bLast); + BOOL m_bShowSplash; + BOOL m_bRunEmbedded; + BOOL m_bRunAutomated; + enum { FileNew, FileOpen, FilePrint, FilePrintTo, FileDDE, AppRegister, AppUnregister, FileNothing = -1 } m_nShellCommand; + CString m_strFileName; + CString m_strPrinterName; + CString m_strDriverName; + CString m_strPortName; +}; + +CWinApp* AFXAPI AfxGetApp(); +CWnd* AFXAPI AfxGetMainWnd(); +HINSTANCE AFXAPI AfxGetInstanceHandle(); +HINSTANCE AFXAPI AfxGetResourceHandle(); +void AFXAPI AfxSetResourceHandle(HINSTANCE hInstResource); +HINSTANCE AFXAPI AfxFindResourceHandle(const char* lpszName, const char* lpszType); +const char* AFXAPI AfxGetAppName(); +int AFXAPI AfxMessageBox(const char* lpszText, UINT nType = MB_OK, UINT nIDHelp = 0); +int AFXAPI AfxMessageBox(UINT nIDPrompt, UINT nType = MB_OK, UINT nIDHelp = (UINT)-1); +void AFXAPI AfxFormatString1(CString& rString, UINT nIDS, const char* lpsz1); +void AFXAPI AfxFormatString2(CString& rString, UINT nIDS, const char* lpsz1, const char* lpsz2); +BOOL AFXAPI AfxExtractSubString(CString& rString, const char* lpszFullString, int iSubString, char chSep = '\n'); +const char* AFXAPI AfxRegisterWndClass(UINT nClassStyle, HCURSOR hCursor = nullptr, HBRUSH hbrBackground = nullptr, + HICON hIcon = nullptr); +BOOL AFXAPI AfxRegisterClass(WNDCLASS* lpWndClass); +BOOL AFXAPI AfxInitRichEdit(); +BOOL AFXAPI AfxInitRichEdit2(); +BOOL AFXAPI AfxOleInit(); +void AFXAPI AfxEnableControlContainer(); +BOOL AFXAPI AfxSocketInit(); +void AFXAPI AfxInitCommonControls(); +BOOL AFXAPI AfxCheckMemory(); +void AFXAPI AfxGetModuleShortFileName(HINSTANCE hInst, CString& strShortName); +LRESULT AFXAPI AfxCallWndProc(CWnd* pWnd, HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam); + +#include "mfcwx/winapi_gui.h" +#include "mfcwx/regkey.h" +#include "mfcwx/extras.h" + +template +inline typename std::common_type::type min(A a, B b) { return b < a ? b : a; } +template +inline typename std::common_type::type max(A a, B b) { return a < b ? b : a; } +#include "mfcwx/docview.h" diff --git a/port/mfcwx/include/atlbase.h b/port/mfcwx/include/atlbase.h new file mode 100644 index 000000000..05e081c1b --- /dev/null +++ b/port/mfcwx/include/atlbase.h @@ -0,0 +1,2 @@ +#pragma once +#include "afx.h" diff --git a/port/mfcwx/include/basetsd.h b/port/mfcwx/include/basetsd.h new file mode 100644 index 000000000..17bdd6cc2 --- /dev/null +++ b/port/mfcwx/include/basetsd.h @@ -0,0 +1,6 @@ +#pragma once +#ifdef __cplusplus +#include "afx.h" +#else +#include "mfcwx/win32.h" +#endif diff --git a/port/mfcwx/include/commctrl.h b/port/mfcwx/include/commctrl.h new file mode 100644 index 000000000..38b8341c6 --- /dev/null +++ b/port/mfcwx/include/commctrl.h @@ -0,0 +1,6 @@ +#pragma once +#ifdef __cplusplus +#include "afxwin.h" +#else +#include "mfcwx/win32.h" +#endif diff --git a/port/mfcwx/include/commdlg.h b/port/mfcwx/include/commdlg.h new file mode 100644 index 000000000..38b8341c6 --- /dev/null +++ b/port/mfcwx/include/commdlg.h @@ -0,0 +1,6 @@ +#pragma once +#ifdef __cplusplus +#include "afxwin.h" +#else +#include "mfcwx/win32.h" +#endif diff --git a/port/mfcwx/include/conio.h b/port/mfcwx/include/conio.h new file mode 100644 index 000000000..fbffeb452 --- /dev/null +++ b/port/mfcwx/include/conio.h @@ -0,0 +1,2 @@ +#pragma once +#include "mfcwx/crtcompat.h" diff --git a/port/mfcwx/include/crtdbg.h b/port/mfcwx/include/crtdbg.h new file mode 100644 index 000000000..fbffeb452 --- /dev/null +++ b/port/mfcwx/include/crtdbg.h @@ -0,0 +1,2 @@ +#pragma once +#include "mfcwx/crtcompat.h" diff --git a/port/mfcwx/include/direct.h b/port/mfcwx/include/direct.h new file mode 100644 index 000000000..fbffeb452 --- /dev/null +++ b/port/mfcwx/include/direct.h @@ -0,0 +1,2 @@ +#pragma once +#include "mfcwx/crtcompat.h" diff --git a/port/mfcwx/include/dos.h b/port/mfcwx/include/dos.h new file mode 100644 index 000000000..fbffeb452 --- /dev/null +++ b/port/mfcwx/include/dos.h @@ -0,0 +1,2 @@ +#pragma once +#include "mfcwx/crtcompat.h" diff --git a/port/mfcwx/include/gl/gl.h b/port/mfcwx/include/gl/gl.h new file mode 100644 index 000000000..f60013dd4 --- /dev/null +++ b/port/mfcwx/include/gl/gl.h @@ -0,0 +1,9 @@ +#pragma once +#ifdef __APPLE__ +#ifndef GL_SILENCE_DEPRECATION +#define GL_SILENCE_DEPRECATION +#endif +#include +#else +#include +#endif diff --git a/port/mfcwx/include/gl/glu.h b/port/mfcwx/include/gl/glu.h new file mode 100644 index 000000000..5fc1f9c1c --- /dev/null +++ b/port/mfcwx/include/gl/glu.h @@ -0,0 +1,9 @@ +#pragma once +#ifdef __APPLE__ +#ifndef GL_SILENCE_DEPRECATION +#define GL_SILENCE_DEPRECATION +#endif +#include +#else +#include +#endif diff --git a/port/mfcwx/include/htmlhelp.h b/port/mfcwx/include/htmlhelp.h new file mode 100644 index 000000000..85c4f63d3 --- /dev/null +++ b/port/mfcwx/include/htmlhelp.h @@ -0,0 +1,6 @@ +#pragma once +#include "mfcwx/win32.h" +#ifdef __cplusplus +HWND HtmlHelp(HWND hwndCaller, const char* pszFile, UINT uCommand, DWORD_PTR dwData); +HWND HtmlHelpA(HWND hwndCaller, const char* pszFile, UINT uCommand, DWORD_PTR dwData); +#endif diff --git a/port/mfcwx/include/io.h b/port/mfcwx/include/io.h new file mode 100644 index 000000000..fbffeb452 --- /dev/null +++ b/port/mfcwx/include/io.h @@ -0,0 +1,2 @@ +#pragma once +#include "mfcwx/crtcompat.h" diff --git a/port/mfcwx/include/mbstring.h b/port/mfcwx/include/mbstring.h new file mode 100644 index 000000000..fbffeb452 --- /dev/null +++ b/port/mfcwx/include/mbstring.h @@ -0,0 +1,2 @@ +#pragma once +#include "mfcwx/crtcompat.h" diff --git a/port/mfcwx/include/mfcwx/codepage.h b/port/mfcwx/include/mfcwx/codepage.h new file mode 100644 index 000000000..8ac2799c9 --- /dev/null +++ b/port/mfcwx/include/mfcwx/codepage.h @@ -0,0 +1,24 @@ +#pragma once + +#include + +// The ANSI code page plays the role of the Windows system code page: CString, char* APIs and +// documents hold bytes in this encoding; mfcwx converts at every boundary to the toolkit. +namespace mfcwx { + +void SetAnsiCodePage(int cp); +int GetAnsiCodePage(); + +wchar_t AnsiToUnicode(unsigned char ch); +// Returns false if ch has no representation; out is set to '?'. +bool UnicodeToAnsi(wchar_t ch, char& out); + +std::string AnsiToUtf8(const char* s, int length = -1); +std::string Utf8ToAnsi(const char* utf8); + +// A path as used by the Windows code (ANSI bytes, '\\' or '/' separators) to a native UTF-8 path. +std::string NativePath(const char* path); +// A native UTF-8 path to the form the Windows code expects. +std::string AppPath(const char* nativePath); + +} // namespace mfcwx diff --git a/port/mfcwx/include/mfcwx/collections.h b/port/mfcwx/include/mfcwx/collections.h new file mode 100644 index 000000000..2cf2ba9db --- /dev/null +++ b/port/mfcwx/include/mfcwx/collections.h @@ -0,0 +1,451 @@ +#pragma once + +// MFC collection classes (afxtempl.h / afxcoll.h). + +#include +#include +#include + +struct __POSITION {}; +typedef __POSITION* POSITION; +#define BEFORE_START_POSITION ((POSITION)(intptr_t)-1) + +template +inline void CopyElements(TYPE* pDest, const TYPE* pSrc, INT_PTR nCount) { + for (INT_PTR i = 0; i < nCount; ++i) + pDest[i] = pSrc[i]; +} + +template +class CArray : public CObject { +public: + CArray() = default; + CArray(const CArray& other) : CObject(), m_data(other.m_data) {} + CArray& operator=(const CArray& other) { m_data = other.m_data; return *this; } + + INT_PTR GetSize() const { return static_cast(m_data.size()); } + INT_PTR GetCount() const { return GetSize(); } + BOOL IsEmpty() const { return m_data.empty(); } + INT_PTR GetUpperBound() const { return GetSize() - 1; } + void SetSize(INT_PTR nNewSize, INT_PTR nGrowBy = -1) { + if (nGrowBy > 0) + m_data.reserve(static_cast(nNewSize + nGrowBy)); + m_data.resize(static_cast(nNewSize < 0 ? 0 : nNewSize)); + } + void FreeExtra() { m_data.shrink_to_fit(); } + void RemoveAll() { m_data.clear(); } + const TYPE& GetAt(INT_PTR nIndex) const { return m_data[static_cast(nIndex)]; } + TYPE& GetAt(INT_PTR nIndex) { return m_data[static_cast(nIndex)]; } + void SetAt(INT_PTR nIndex, ARG_TYPE newElement) { m_data[static_cast(nIndex)] = newElement; } + const TYPE& ElementAt(INT_PTR nIndex) const { return m_data[static_cast(nIndex)]; } + TYPE& ElementAt(INT_PTR nIndex) { return m_data[static_cast(nIndex)]; } + const TYPE* GetData() const { return m_data.empty() ? nullptr : m_data.data(); } + TYPE* GetData() { return m_data.empty() ? nullptr : m_data.data(); } + void SetAtGrow(INT_PTR nIndex, ARG_TYPE newElement) { + if (nIndex >= GetSize()) + m_data.resize(static_cast(nIndex + 1)); + m_data[static_cast(nIndex)] = newElement; + } + INT_PTR Add(ARG_TYPE newElement) { + m_data.push_back(newElement); + return GetSize() - 1; + } + INT_PTR Append(const CArray& src) { + INT_PTR old = GetSize(); + m_data.insert(m_data.end(), src.m_data.begin(), src.m_data.end()); + return old; + } + void Copy(const CArray& src) { m_data = src.m_data; } + const TYPE& operator[](INT_PTR nIndex) const { return m_data[static_cast(nIndex)]; } + TYPE& operator[](INT_PTR nIndex) { return m_data[static_cast(nIndex)]; } + void InsertAt(INT_PTR nIndex, ARG_TYPE newElement, INT_PTR nCount = 1) { + if (nIndex > GetSize()) + m_data.resize(static_cast(nIndex)); + m_data.insert(m_data.begin() + nIndex, static_cast(nCount), newElement); + } + void InsertAt(INT_PTR nStartIndex, CArray* pNewArray) { + if (nStartIndex > GetSize()) + m_data.resize(static_cast(nStartIndex)); + m_data.insert(m_data.begin() + nStartIndex, pNewArray->m_data.begin(), pNewArray->m_data.end()); + } + void RemoveAt(INT_PTR nIndex, INT_PTR nCount = 1) { + m_data.erase(m_data.begin() + nIndex, m_data.begin() + nIndex + nCount); + } + void Serialize(CArchive& ar) override; + +protected: + std::vector m_data; +}; + +template +class CTypedPtrArray : public BASE_CLASS { +public: + TYPE GetAt(INT_PTR nIndex) const { return static_cast(BASE_CLASS::GetAt(nIndex)); } + TYPE& ElementAt(INT_PTR nIndex) { return reinterpret_cast(BASE_CLASS::ElementAt(nIndex)); } + void SetAt(INT_PTR nIndex, TYPE ptr) { BASE_CLASS::SetAt(nIndex, ptr); } + void SetAtGrow(INT_PTR nIndex, TYPE newElement) { BASE_CLASS::SetAtGrow(nIndex, newElement); } + INT_PTR Add(TYPE newElement) { return BASE_CLASS::Add(newElement); } + void InsertAt(INT_PTR nIndex, TYPE newElement, INT_PTR nCount = 1) { BASE_CLASS::InsertAt(nIndex, newElement, nCount); } + TYPE operator[](INT_PTR nIndex) const { return GetAt(nIndex); } + TYPE& operator[](INT_PTR nIndex) { return ElementAt(nIndex); } +}; + +template +class CList : public CObject { + struct CNode { + CNode* pNext; + CNode* pPrev; + TYPE data; + }; + +public: + explicit CList(INT_PTR = 10) {} + CList(const CList&) = delete; + CList& operator=(const CList&) = delete; + ~CList() override { RemoveAll(); } + + INT_PTR GetCount() const { return m_nCount; } + INT_PTR GetSize() const { return m_nCount; } + BOOL IsEmpty() const { return m_nCount == 0; } + TYPE& GetHead() { return m_pHead->data; } + const TYPE& GetHead() const { return m_pHead->data; } + TYPE& GetTail() { return m_pTail->data; } + const TYPE& GetTail() const { return m_pTail->data; } + TYPE RemoveHead() { + CNode* node = m_pHead; + TYPE value = node->data; + Unlink(node); + return value; + } + TYPE RemoveTail() { + CNode* node = m_pTail; + TYPE value = node->data; + Unlink(node); + return value; + } + POSITION AddHead(ARG_TYPE newElement) { return InsertBefore(reinterpret_cast(m_pHead), newElement); } + POSITION AddTail(ARG_TYPE newElement) { return InsertAfter(reinterpret_cast(m_pTail), newElement); } + void AddHead(CList* pNewList) { + for (CNode* n = pNewList->m_pTail; n; n = n->pPrev) + AddHead(n->data); + } + void AddTail(CList* pNewList) { + for (CNode* n = pNewList->m_pHead; n; n = n->pNext) + AddTail(n->data); + } + void RemoveAll() { + while (m_pHead) + Unlink(m_pHead); + } + POSITION GetHeadPosition() const { return reinterpret_cast(m_pHead); } + POSITION GetTailPosition() const { return reinterpret_cast(m_pTail); } + TYPE& GetNext(POSITION& rPosition) { + CNode* node = reinterpret_cast(rPosition); + rPosition = reinterpret_cast(node->pNext); + return node->data; + } + const TYPE& GetNext(POSITION& rPosition) const { + CNode* node = reinterpret_cast(rPosition); + rPosition = reinterpret_cast(node->pNext); + return node->data; + } + TYPE& GetPrev(POSITION& rPosition) { + CNode* node = reinterpret_cast(rPosition); + rPosition = reinterpret_cast(node->pPrev); + return node->data; + } + const TYPE& GetPrev(POSITION& rPosition) const { + CNode* node = reinterpret_cast(rPosition); + rPosition = reinterpret_cast(node->pPrev); + return node->data; + } + TYPE& GetAt(POSITION position) { return reinterpret_cast(position)->data; } + const TYPE& GetAt(POSITION position) const { return reinterpret_cast(position)->data; } + void SetAt(POSITION pos, ARG_TYPE newElement) { reinterpret_cast(pos)->data = newElement; } + void RemoveAt(POSITION position) { Unlink(reinterpret_cast(position)); } + POSITION InsertBefore(POSITION position, ARG_TYPE newElement) { + CNode* at = reinterpret_cast(position); + CNode* node = new CNode{at, at ? at->pPrev : m_pTail, newElement}; + if (!at) { + node->pNext = m_pHead; + node->pPrev = nullptr; + } + Link(node); + return reinterpret_cast(node); + } + POSITION InsertAfter(POSITION position, ARG_TYPE newElement) { + CNode* at = reinterpret_cast(position); + CNode* node = new CNode{at ? at->pNext : m_pHead, at, newElement}; + if (!at) { + node->pNext = nullptr; + node->pPrev = m_pTail; + } + Link(node); + return reinterpret_cast(node); + } + POSITION Find(ARG_TYPE searchValue, POSITION startAfter = nullptr) const { + CNode* n = startAfter ? reinterpret_cast(startAfter)->pNext : m_pHead; + for (; n; n = n->pNext) + if (n->data == searchValue) + return reinterpret_cast(n); + return nullptr; + } + POSITION FindIndex(INT_PTR nIndex) const { + if (nIndex < 0 || nIndex >= m_nCount) + return nullptr; + CNode* n = m_pHead; + while (nIndex-- > 0) + n = n->pNext; + return reinterpret_cast(n); + } + void Serialize(CArchive& ar) override; + +private: + void Link(CNode* node) { + if (node->pPrev) + node->pPrev->pNext = node; + else + m_pHead = node; + if (node->pNext) + node->pNext->pPrev = node; + else + m_pTail = node; + ++m_nCount; + } + void Unlink(CNode* node) { + if (node->pPrev) + node->pPrev->pNext = node->pNext; + else + m_pHead = node->pNext; + if (node->pNext) + node->pNext->pPrev = node->pPrev; + else + m_pTail = node->pPrev; + --m_nCount; + delete node; + } + + CNode* m_pHead = nullptr; + CNode* m_pTail = nullptr; + INT_PTR m_nCount = 0; +}; + +template +class CTypedPtrList : public BASE_CLASS { +public: + explicit CTypedPtrList(INT_PTR nBlockSize = 10) : BASE_CLASS(nBlockSize) {} + TYPE& GetHead() { return reinterpret_cast(BASE_CLASS::GetHead()); } + TYPE GetHead() const { return static_cast(BASE_CLASS::GetHead()); } + TYPE& GetTail() { return reinterpret_cast(BASE_CLASS::GetTail()); } + TYPE GetTail() const { return static_cast(BASE_CLASS::GetTail()); } + TYPE RemoveHead() { return static_cast(BASE_CLASS::RemoveHead()); } + TYPE RemoveTail() { return static_cast(BASE_CLASS::RemoveTail()); } + TYPE& GetNext(POSITION& rPosition) { return reinterpret_cast(BASE_CLASS::GetNext(rPosition)); } + TYPE GetNext(POSITION& rPosition) const { return static_cast(BASE_CLASS::GetNext(rPosition)); } + TYPE& GetPrev(POSITION& rPosition) { return reinterpret_cast(BASE_CLASS::GetPrev(rPosition)); } + TYPE GetPrev(POSITION& rPosition) const { return static_cast(BASE_CLASS::GetPrev(rPosition)); } + TYPE& GetAt(POSITION position) { return reinterpret_cast(BASE_CLASS::GetAt(position)); } + TYPE GetAt(POSITION position) const { return static_cast(BASE_CLASS::GetAt(position)); } + void SetAt(POSITION pos, TYPE newElement) { BASE_CLASS::SetAt(pos, newElement); } + POSITION AddHead(TYPE newElement) { return BASE_CLASS::AddHead(newElement); } + POSITION AddTail(TYPE newElement) { return BASE_CLASS::AddTail(newElement); } +}; + +template +struct CMapKeyLess { + bool operator()(const KEY& a, const KEY& b) const { return a < b; } +}; + +template +class CMap : public CObject { + struct CAssoc { + CAssoc* pNext; + CAssoc* pPrev; + KEY key; + VALUE value; + }; + +public: + explicit CMap(INT_PTR = 10) {} + CMap(const CMap&) = delete; + CMap& operator=(const CMap&) = delete; + ~CMap() override { RemoveAll(); } + + INT_PTR GetCount() const { return static_cast(m_index.size()); } + INT_PTR GetSize() const { return GetCount(); } + BOOL IsEmpty() const { return m_index.empty(); } + BOOL Lookup(ARG_KEY key, VALUE& rValue) const { + auto it = m_index.find(KEY(key)); + if (it == m_index.end()) + return FALSE; + rValue = it->second->value; + return TRUE; + } + VALUE& operator[](ARG_KEY key) { + KEY k(key); + auto it = m_index.find(k); + if (it != m_index.end()) + return it->second->value; + CAssoc* a = new CAssoc{nullptr, m_pTail, k, VALUE()}; + if (m_pTail) + m_pTail->pNext = a; + else + m_pHead = a; + m_pTail = a; + m_index.emplace(k, a); + return a->value; + } + void SetAt(ARG_KEY key, ARG_VALUE newValue) { (*this)[key] = newValue; } + BOOL RemoveKey(ARG_KEY key) { + auto it = m_index.find(KEY(key)); + if (it == m_index.end()) + return FALSE; + CAssoc* a = it->second; + m_index.erase(it); + if (a->pPrev) + a->pPrev->pNext = a->pNext; + else + m_pHead = a->pNext; + if (a->pNext) + a->pNext->pPrev = a->pPrev; + else + m_pTail = a->pPrev; + delete a; + return TRUE; + } + void RemoveAll() { + while (m_pHead) { + CAssoc* next = m_pHead->pNext; + delete m_pHead; + m_pHead = next; + } + m_pTail = nullptr; + m_index.clear(); + } + POSITION GetStartPosition() const { return reinterpret_cast(m_pHead); } + void GetNextAssoc(POSITION& rNextPosition, KEY& rKey, VALUE& rValue) const { + CAssoc* a = reinterpret_cast(rNextPosition); + rKey = a->key; + rValue = a->value; + rNextPosition = reinterpret_cast(a->pNext); + } + void InitHashTable(UINT, BOOL = TRUE) {} + UINT GetHashTableSize() const { return 17; } + +private: + CAssoc* m_pHead = nullptr; + CAssoc* m_pTail = nullptr; + std::map> m_index; +}; + +template +class CTypedPtrMap : public BASE_CLASS { +public: + BOOL Lookup(typename BASE_CLASS::BASE_ARG_KEY key, VALUE& rValue) const { + typename BASE_CLASS::BASE_VALUE v; + BOOL found = BASE_CLASS::Lookup(key, v); + rValue = static_cast(v); + return found; + } + VALUE& operator[](typename BASE_CLASS::BASE_ARG_KEY key) { return reinterpret_cast(BASE_CLASS::operator[](key)); } + void SetAt(KEY key, VALUE newValue) { BASE_CLASS::SetAt(key, newValue); } + BOOL RemoveKey(KEY key) { return BASE_CLASS::RemoveKey(key); } + void GetNextAssoc(POSITION& rPosition, KEY& rKey, VALUE& rValue) const { + typename BASE_CLASS::BASE_KEY k; + typename BASE_CLASS::BASE_VALUE v; + BASE_CLASS::GetNextAssoc(rPosition, k, v); + rKey = static_cast(k); + rValue = static_cast(v); + } +}; + +#define MFCWX_DECLARE_ARRAY(name, TYPE, ARG) \ + class name : public CArray { \ + DECLARE_SERIAL(name) \ + public: \ + name() = default; \ + name(const name& o) : CArray(o) {} \ + name& operator=(const name& o) { CArray::operator=(o); return *this; } \ + }; + +MFCWX_DECLARE_ARRAY(CStringArray, CString, const char*) +MFCWX_DECLARE_ARRAY(CPtrArray, void*, void*) +MFCWX_DECLARE_ARRAY(CObArray, CObject*, CObject*) +MFCWX_DECLARE_ARRAY(CDWordArray, DWORD, DWORD) +MFCWX_DECLARE_ARRAY(CUIntArray, UINT, UINT) +MFCWX_DECLARE_ARRAY(CWordArray, WORD, WORD) +MFCWX_DECLARE_ARRAY(CByteArray, BYTE, BYTE) + +#define MFCWX_DECLARE_LIST(name, TYPE, ARG) \ + class name : public CList { \ + DECLARE_SERIAL(name) \ + public: \ + explicit name(INT_PTR nBlockSize = 10) : CList(nBlockSize) {} \ + }; + +MFCWX_DECLARE_LIST(CStringList, CString, const char*) +MFCWX_DECLARE_LIST(CPtrList, void*, void*) +MFCWX_DECLARE_LIST(CObList, CObject*, CObject*) + +#define MFCWX_DECLARE_MAP(name, K, AK, V, AV) \ + class name : public CMap { \ + DECLARE_SERIAL(name) \ + public: \ + typedef K BASE_KEY; \ + typedef AK BASE_ARG_KEY; \ + typedef V BASE_VALUE; \ + typedef AV BASE_ARG_VALUE; \ + explicit name(INT_PTR nBlockSize = 10) : CMap(nBlockSize) {} \ + }; + +MFCWX_DECLARE_MAP(CMapStringToString, CString, const char*, CString, const char*) +MFCWX_DECLARE_MAP(CMapStringToPtr, CString, const char*, void*, void*) +MFCWX_DECLARE_MAP(CMapStringToOb, CString, const char*, CObject*, CObject*) +MFCWX_DECLARE_MAP(CMapPtrToPtr, void*, void*, void*, void*) +MFCWX_DECLARE_MAP(CMapPtrToWord, void*, void*, WORD, WORD) +MFCWX_DECLARE_MAP(CMapWordToPtr, WORD, WORD, void*, void*) +MFCWX_DECLARE_MAP(CMapWordToOb, WORD, WORD, CObject*, CObject*) + +template +void SerializeElements(CArchive& ar, TYPE* pElements, INT_PTR nCount) { + if (ar.IsStoring()) + ar.Write(pElements, static_cast(nCount * sizeof(TYPE))); + else + ar.Read(pElements, static_cast(nCount * sizeof(TYPE))); +} + +inline void SerializeElements(CArchive& ar, CString* pElements, INT_PTR nCount) { + for (INT_PTR i = 0; i < nCount; ++i) { + if (ar.IsStoring()) + ar << pElements[i]; + else + ar >> pElements[i]; + } +} + +template +void CArray::Serialize(CArchive& ar) { + if (ar.IsStoring()) { + ar.WriteCount(static_cast(GetSize())); + } else { + SetSize(ar.ReadCount()); + } + if (!m_data.empty()) + SerializeElements(ar, m_data.data(), GetSize()); +} + +template +void CList::Serialize(CArchive& ar) { + if (ar.IsStoring()) { + ar.WriteCount(static_cast(m_nCount)); + for (CNode* n = m_pHead; n; n = n->pNext) + SerializeElements(ar, &n->data, 1); + } else { + UINT count = ar.ReadCount(); + while (count--) { + TYPE value{}; + SerializeElements(ar, &value, 1); + AddTail(value); + } + } +} diff --git a/port/mfcwx/include/mfcwx/crtcompat.h b/port/mfcwx/include/mfcwx/crtcompat.h new file mode 100644 index 000000000..8f715ef5b --- /dev/null +++ b/port/mfcwx/include/mfcwx/crtcompat.h @@ -0,0 +1,387 @@ +#pragma once + +/* Microsoft C runtime extensions on top of the POSIX C library. C compatible. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _MFCWX_CRTCOMPAT +#define _MFCWX_CRTCOMPAT + +#define _CRT_SECURE_NO_WARNINGS +#define _MAX_PATH 260 +#define _MAX_DRIVE 3 +#define _MAX_DIR 256 +#define _MAX_FNAME 256 +#define _MAX_EXT 256 + +#define _O_RDONLY O_RDONLY +#define _O_WRONLY O_WRONLY +#define _O_RDWR O_RDWR +#define _O_APPEND O_APPEND +#define _O_CREAT O_CREAT +#define _O_TRUNC O_TRUNC +#define _O_EXCL O_EXCL +#define _O_BINARY 0 +#define _O_TEXT 0 +#define O_BINARY 0 +#define O_TEXT 0 +#define _S_IREAD S_IRUSR +#define _S_IWRITE S_IWUSR +#define _S_IFDIR S_IFDIR +#define _S_IFREG S_IFREG +#define _S_IFMT S_IFMT +#define _SH_DENYNO 0x40 +#define _SH_DENYRW 0x10 +#define _SH_DENYWR 0x20 +#define _SH_DENYRD 0x30 + +#define _stat stat +#define _stati64 stat +#define _fstat fstat +#define _fstati64 fstat +#define _off_t off_t +#define _fsize_t unsigned long +#define _timeb timeb +#define _ftime(t) ftime(t) +#define _fseeki64 fseeko +#define _ftelli64 ftello +#define _memicmp mfcwx_memicmp +#define memicmp mfcwx_memicmp +#define __time64_t time_t +#define _time64 time +#define _localtime64 localtime +#define _gmtime64 gmtime +#define _mktime64 mktime +#define _ctime64 ctime +#define _difftime64 difftime + +#define _open mfcwx_open +#define _tell(fd) lseek((fd), 0, SEEK_CUR) +#define _telli64(fd) lseek((fd), 0, SEEK_CUR) +#define _lseeki64 _lseek +#define _stricmp strcasecmp +#define _strnicmp strncasecmp +#define _strcmpi strcasecmp +#define stricmp strcasecmp +#define strnicmp strncasecmp +#define strcmpi strcasecmp +#define _mbscmp(a, b) strcmp((const char*)(a), (const char*)(b)) +#define _mbsicmp(a, b) strcasecmp((const char*)(a), (const char*)(b)) +#define _mbslen(a) strlen((const char*)(a)) +#define _mbschr(a, c) ((unsigned char*)strchr((const char*)(a), (c))) +#define _mbsrchr(a, c) ((unsigned char*)strrchr((const char*)(a), (c))) +#define _mbsstr(a, b) ((unsigned char*)strstr((const char*)(a), (const char*)(b))) +#define _mbsinc(p) ((p) + 1) +#define _mbsdec(s, p) ((p) - 1) +#define _ismbblead(c) 0 +#define _snprintf snprintf +#define _vsnprintf vsnprintf +#define _vscprintf(fmt, args) vsnprintf(NULL, 0, (fmt), (args)) +#define _scprintf(...) snprintf(NULL, 0, __VA_ARGS__) +#define _snscanf sscanf +#define _strtoi64 strtoll +#define _strtoui64 strtoull +#define _atoi64 atoll +#define _wtoi(s) ((int)wcstol((s), NULL, 10)) +#define _isnan isnan +#define _finite isfinite +#define _hypot hypot +#define _copysign copysign +#define _chgsign(x) (-(x)) +#define _logb logb +#define _nextafter nextafter +#define _j0 j0 +#define _j1 j1 +#define _y0 y0 +#define _y1 y1 +#define _fpclass(x) fpclassify(x) +#define _CrtSetDbgFlag(x) 0 +#define _CrtDumpMemoryLeaks() 0 +#define _CrtCheckMemory() 1 +#define _CrtSetBreakAlloc(x) 0 +#define _CrtSetReportMode(a, b) 0 +#define _CRTDBG_ALLOC_MEM_DF 0x01 +#define _CRTDBG_LEAK_CHECK_DF 0x20 +#define _CRTDBG_REPORT_FLAG (-1) +#define _CRT_WARN 0 +#define _CRT_ERROR 1 +#define _CRT_ASSERT 2 +#define _CRTDBG_MODE_DEBUG 0x2 +#define _ASSERTE(expr) ((void)0) +#define _ASSERT(expr) ((void)0) +#define _RPT0(rptno, msg) ((void)0) +#define _RPT1(rptno, msg, a1) ((void)0) +#define _RPT2(rptno, msg, a1, a2) ((void)0) +#define _RPT3(rptno, msg, a1, a2, a3) ((void)0) +#define _RPTF0(rptno, msg) ((void)0) +#define _RPTF1(rptno, msg, a1) ((void)0) +#define _alloca alloca +#define _msize(p) mfcwx_msize(p) +#define _heapchk() (-2) +#define _HEAPOK (-2) +#define __min(a, b) (((a) < (b)) ? (a) : (b)) +#define __max(a, b) (((a) > (b)) ? (a) : (b)) +#define _rotl(v, s) mfcwx_rotl((v), (s)) +#define _rotr(v, s) mfcwx_rotr((v), (s)) +#define _lrotl(v, s) mfcwx_rotl((v), (s)) +#define _lrotr(v, s) mfcwx_rotr((v), (s)) +#define _byteswap_ulong(x) __builtin_bswap32(x) +#define _byteswap_ushort(x) __builtin_bswap16(x) +#define _byteswap_uint64(x) __builtin_bswap64(x) +#define _getch getchar +#define _getche getchar +#define getch getchar +#define _putch putchar +#define _kbhit() 0 +#define kbhit() 0 +#define _flushall() fflush(NULL) +#define _fcloseall() 0 +#define _fsopen(name, mode, sh) mfcwx_fopen((name), (mode)) +#define _wfopen(name, mode) NULL +#define _set_errno(e) (errno = (e), 0) +#define _get_errno(p) (*(p) = errno, 0) +#define _beginthread mfcwx_beginthread +#define _beginthreadex mfcwx_beginthreadex +#define _endthread() mfcwx_endthread(0) +#define _endthreadex(c) mfcwx_endthread(c) +#define _sleep(ms) mfcwx_sleep_ms(ms) +#define _clearfp() 0 +#define _controlfp(a, b) 0 +#define _control87(a, b) 0 +#define _MCW_EM 0x0008001f +#define _EM_INVALID 0x00000010 +#define _EM_ZERODIVIDE 0x00000008 +#define _EM_OVERFLOW 0x00000004 +#define _EM_UNDERFLOW 0x00000002 +#define _EM_INEXACT 0x00000001 +#define _EM_DENORMAL 0x00080000 +#define _PC_24 0x00020000 +#define _PC_53 0x00010000 +#define _PC_64 0x00000000 +#define _MCW_PC 0x00030000 + +#define _tcslen strlen +#define _tcscpy strcpy +#define _tcsncpy strncpy +#define _tcscat strcat +#define _tcsncat strncat +#define _tcscmp strcmp +#define _tcsncmp strncmp +#define _tcsicmp strcasecmp +#define _tcsnicmp strncasecmp +#define _tcschr strchr +#define _tcsrchr strrchr +#define _tcsstr strstr +#define _tcsspn strspn +#define _tcscspn strcspn +#define _tcspbrk strpbrk +#define _tcstok strtok +#define _tcsdup strdup +#define _tcsupr _strupr +#define _tcslwr _strlwr +#define _tcsrev _strrev +#define _tcstol strtol +#define _tcstoul strtoul +#define _tcstod strtod +#define _tcsftime strftime +#define _tcsinc(p) ((p) + 1) +#define _tcsdec(s, p) ((p) - 1) +#define _tcsnextc(p) ((unsigned int)(unsigned char)*(p)) +#define _tclen(p) 1 +#define _tccpy(d, s) (*(d) = *(s)) +#define _ttoi atoi +#define _ttol atol +#define _ttof atof +#define _tstoi atoi +#define _tstof atof +#define _ttoi64 atoll +#define _itot _itoa +#define _ltot _ltoa +#define _ultot _ultoa +#define _stprintf sprintf +#define _sntprintf snprintf +#define _vstprintf vsprintf +#define _vsntprintf vsnprintf +#define _tprintf printf +#define _ftprintf fprintf +#define _stscanf sscanf +#define _tfopen mfcwx_fopen +#define _tremove mfcwx_unlink +#define _trename mfcwx_rename +#define _tunlink mfcwx_unlink +#define _taccess mfcwx_access +#define _tmkdir _mkdir +#define _tgetcwd getcwd +#define _tchdir mfcwx_chdir +#define _tsplitpath _splitpath +#define _tmakepath _makepath +#define _tfullpath _fullpath +#define _tsetlocale setlocale +#define _tgetenv getenv +#define _tsystem system +#define _istalpha isalpha +#define _istdigit isdigit +#define _istxdigit isxdigit +#define _istspace isspace +#define _istalnum isalnum +#define _istupper isupper +#define _istlower islower +#define _istprint isprint +#define _istpunct ispunct +#define _istcntrl iscntrl +#define _istgraph isgraph +#define _totupper toupper +#define _totlower tolower +#define _tcsclen strlen +#define _tcsnccpy strncpy +#define _tcsncpy_s(d, n, s, c) strncpy_s((d), (n), (s), (c)) +#define _tcscpy_s strcpy_s +#define _tcscat_s strcat_s +#define _stprintf_s sprintf_s +#define _tmain main +#define _tWinMain WinMain + +int mfcwx_open(const char* name, int flags, ...); +int mfcwx_memicmp(const void* a, const void* b, size_t n); +int mfcwx_unlink(const char* name); +int mfcwx_rmdir(const char* name); +int mfcwx_chdir(const char* name); +int mfcwx_access(const char* name, int mode); +int mfcwx_chmod(const char* name, int mode); +int mfcwx_rename(const char* from, const char* to); +FILE* mfcwx_fopen(const char* name, const char* mode); +size_t mfcwx_msize(void* p); +unsigned int mfcwx_rotl(unsigned int v, int s); +unsigned int mfcwx_rotr(unsigned int v, int s); +void mfcwx_sleep_ms(unsigned long ms); +uintptr_t mfcwx_beginthread(void (*fn)(void*), unsigned stack, void* arg); +uintptr_t mfcwx_beginthreadex(void* security, unsigned stack, unsigned (*fn)(void*), void* arg, unsigned flags, + unsigned* threadId); +void mfcwx_endthread(unsigned code); +void mfcwx_normalize_path(char* path); + +int _mkdir(const char* path); + +static inline int _close(int fd) { return close(fd); } +static inline int _read(int fd, void* buf, unsigned int n) { return (int)read(fd, buf, n); } +static inline int _write(int fd, const void* buf, unsigned int n) { return (int)write(fd, buf, n); } +static inline long _lseek(int fd, long off, int whence) { return (long)lseek(fd, off, whence); } +static inline int _dup(int fd) { return dup(fd); } +static inline int _dup2(int a, int b) { return dup2(a, b); } +static inline int _fileno(FILE* f) { return fileno(f); } +static inline FILE* _fdopen(int fd, const char* mode) { return fdopen(fd, mode); } +static inline int _unlink(const char* name) { return mfcwx_unlink(name); } +static inline int _rmdir(const char* name) { return mfcwx_rmdir(name); } +static inline int _chdir(const char* name) { return mfcwx_chdir(name); } +static inline char* _getcwd(char* buf, int size) { return getcwd(buf, (size_t)size); } +static inline int _access(const char* name, int mode) { return mfcwx_access(name, mode); } +static inline int _chmod(const char* name, int mode) { return mfcwx_chmod(name, mode); } +static inline int _getpid(void) { return (int)getpid(); } +static inline int _isatty(int fd) { return isatty(fd); } +static inline int _umask(int mask) { return (int)umask((mode_t)mask); } +static inline char* _strdup(const char* s) { return strdup(s); } + +char* _strupr(char* s); +char* _strlwr(char* s); +char* _strrev(char* s); +char* strupr(char* s); +char* strlwr(char* s); +char* strrev(char* s); +char* _strset(char* s, int c); +char* _strnset(char* s, int c, size_t n); +char* _itoa(int value, char* buf, int radix); +char* _ltoa(long value, char* buf, int radix); +char* _ultoa(unsigned long value, char* buf, int radix); +char* _i64toa(long long value, char* buf, int radix); +char* _ui64toa(unsigned long long value, char* buf, int radix); +char* itoa(int value, char* buf, int radix); +char* ltoa(long value, char* buf, int radix); +char* ultoa(unsigned long value, char* buf, int radix); +char* _gcvt(double value, int digits, char* buf); +char* gcvt(double value, int digits, char* buf); +char* _ecvt(double value, int digits, int* dec, int* sign); +char* _fcvt(double value, int digits, int* dec, int* sign); +long _filelength(int fd); +long filelength(int fd); +long long _filelengthi64(int fd); +int _chsize(int fd, long size); +int _eof(int fd); +int _setmode(int fd, int mode); +void _splitpath(const char* path, char* drive, char* dir, char* fname, char* ext); +void _makepath(char* path, const char* drive, const char* dir, const char* fname, const char* ext); +char* _fullpath(char* abs, const char* rel, size_t size); +int _getdrive(void); +int _chdrive(int drive); +char* _mktemp(char* templ); +char* _tempnam(const char* dir, const char* prefix); +int _vsnprintf_s(char* buf, size_t size, size_t count, const char* fmt, va_list args); +int sprintf_s(char* buf, size_t size, const char* fmt, ...); +int _snprintf_s(char* buf, size_t size, size_t count, const char* fmt, ...); +int vsprintf_s(char* buf, size_t size, const char* fmt, va_list args); +int strcpy_s(char* dst, size_t size, const char* src); +int strncpy_s(char* dst, size_t size, const char* src, size_t count); +int strcat_s(char* dst, size_t size, const char* src); +int strncat_s(char* dst, size_t size, const char* src, size_t count); +char* strtok_s(char* s, const char* delim, char** ctx); +int _strupr_s(char* s, size_t size); +int _strlwr_s(char* s, size_t size); +int _itoa_s(int value, char* buf, size_t size, int radix); +int _ltoa_s(long value, char* buf, size_t size, int radix); +int _i64toa_s(long long value, char* buf, size_t size, int radix); +int fopen_s(FILE** f, const char* name, const char* mode); +int memcpy_s(void* dst, size_t dstSize, const void* src, size_t count); +int memmove_s(void* dst, size_t dstSize, const void* src, size_t count); +int localtime_s(struct tm* out, const time_t* t); +int gmtime_s(struct tm* out, const time_t* t); +int ctime_s(char* buf, size_t size, const time_t* t); +int asctime_s(char* buf, size_t size, const struct tm* t); +int _dupenv_s(char** buf, size_t* len, const char* name); +char* _strdate(char* buf); +char* _strtime(char* buf); +void _tzset(void); + +struct _finddata_t { + unsigned attrib; + time_t time_create; + time_t time_access; + time_t time_write; + unsigned long size; + char name[260]; +}; +#define _finddatai64_t _finddata_t +#define _finddata32_t _finddata_t +#define _A_NORMAL 0x00 +#define _A_RDONLY 0x01 +#define _A_HIDDEN 0x02 +#define _A_SYSTEM 0x04 +#define _A_SUBDIR 0x10 +#define _A_ARCH 0x20 +intptr_t _findfirst(const char* pattern, struct _finddata_t* data); +int _findnext(intptr_t handle, struct _finddata_t* data); +int _findclose(intptr_t handle); +#define _findfirsti64 _findfirst +#define _findnexti64 _findnext + +#endif /* _MFCWX_CRTCOMPAT */ + +#ifdef __cplusplus +} +#endif diff --git a/port/mfcwx/include/mfcwx/cstring.h b/port/mfcwx/include/mfcwx/cstring.h new file mode 100644 index 000000000..6f418ccb4 --- /dev/null +++ b/port/mfcwx/include/mfcwx/cstring.h @@ -0,0 +1,164 @@ +#pragma once + +#include "mfcwx/win32.h" + +#include + +// MBCS CString: a byte string in the application's ANSI code page (see mfcwx/codepage.h). +class CString { +public: + CString() noexcept; + CString(const CString& s); + CString(CString&& s) noexcept; + CString(const char* s); + CString(const unsigned char* s); + CString(const char* s, int length); + CString(char ch, int repeat = 1); + CString(const wchar_t* s); + ~CString(); + + CString& operator=(const CString& s); + CString& operator=(CString&& s) noexcept; + CString& operator=(const char* s); + CString& operator=(const unsigned char* s); + CString& operator=(const wchar_t* s); + CString& operator=(char ch); + + int GetLength() const { return m_len; } + int GetAllocLength() const { return m_cap; } + bool IsEmpty() const { return m_len == 0; } + void Empty(); + char GetAt(int i) const { return m_p[i]; } + void SetAt(int i, char ch) { m_p[i] = ch; } + char operator[](int i) const { return m_p[i]; } + operator const char*() const { return m_p; } + const char* GetString() const { return m_p; } + + char* GetBuffer(); + char* GetBuffer(int minLength); + char* GetBufferSetLength(int length); + void ReleaseBuffer(int newLength = -1); + void ReleaseBufferSetLength(int newLength) { ReleaseBuffer(newLength); } + char* LockBuffer() { return GetBuffer(); } + void UnlockBuffer() {} + void FreeExtra() {} + void Preallocate(int length) { Reserve(length); } + void Truncate(int length); + void SetString(const char* s); + void SetString(const char* s, int length); + + CString& operator+=(const CString& s); + CString& operator+=(const char* s); + CString& operator+=(const unsigned char* s) { return *this += reinterpret_cast(s); } + CString& operator+=(char ch); + CString& operator+=(unsigned char ch) { return *this += static_cast(ch); } + CString& operator+=(const wchar_t* s); + void Append(const char* s); + void Append(const char* s, int length); + void Append(const CString& s) { *this += s; } + void AppendChar(char ch) { *this += ch; } + + int Compare(const char* s) const; + int CompareNoCase(const char* s) const; + int Collate(const char* s) const { return Compare(s); } + int CollateNoCase(const char* s) const { return CompareNoCase(s); } + + CString Mid(int first) const; + CString Mid(int first, int count) const; + CString Left(int count) const; + CString Right(int count) const; + CString SpanIncluding(const char* charSet) const; + CString SpanExcluding(const char* charSet) const; + CString Tokenize(const char* tokens, int& start) const; + + CString& MakeUpper(); + CString& MakeLower(); + CString& MakeReverse(); + int Replace(char oldCh, char newCh); + int Replace(const char* oldStr, const char* newStr); + int Remove(char ch); + int Insert(int index, char ch); + int Insert(int index, const char* s); + int Delete(int index, int count = 1); + + CString& TrimLeft(); + CString& TrimLeft(char target); + CString& TrimLeft(const char* targets); + CString& TrimRight(); + CString& TrimRight(char target); + CString& TrimRight(const char* targets); + CString& Trim() { TrimRight(); return TrimLeft(); } + CString& Trim(char target) { TrimRight(target); return TrimLeft(target); } + CString& Trim(const char* targets) { TrimRight(targets); return TrimLeft(targets); } + + int Find(char ch, int start = 0) const; + int Find(const char* sub, int start = 0) const; + int ReverseFind(char ch) const; + int FindOneOf(const char* charSet) const; + + void Format(const char* fmt, ...) __attribute__((format(printf, 2, 3))); + void Format(UINT formatId, ...); + void FormatV(const char* fmt, va_list args); + void AppendFormat(const char* fmt, ...) __attribute__((format(printf, 2, 3))); + void AppendFormat(UINT formatId, ...); + void AppendFormatV(const char* fmt, va_list args); + void FormatMessage(const char* fmt, ...); + void FormatMessage(UINT formatId, ...); + + BOOL LoadString(UINT id); + BOOL LoadStringA(UINT id) { return LoadString(id); } + BSTR AllocSysString() const; + BSTR SetSysString(BSTR* bstr) const; + void AnsiToOem() {} + void OemToAnsi() {} + + static int StringLength(const char* s) { return s ? static_cast(strlen(s)) : 0; } + + friend void swap(CString& a, CString& b) noexcept { + std::swap(a.m_p, b.m_p); + std::swap(a.m_len, b.m_len); + std::swap(a.m_cap, b.m_cap); + } + +private: + void Reserve(int capacity); + void Assign(const char* s, int length); + + char* m_p; + int m_len; + int m_cap; +}; + +CString operator+(const CString& a, const CString& b); +CString operator+(const CString& a, const char* b); +CString operator+(const char* a, const CString& b); +CString operator+(const CString& a, char b); +CString operator+(char a, const CString& b); +CString operator+(const CString& a, const wchar_t* b); + +inline bool operator==(const CString& a, const CString& b) { return a.Compare(b) == 0; } +inline bool operator==(const CString& a, const char* b) { return a.Compare(b) == 0; } +inline bool operator==(const char* a, const CString& b) { return b.Compare(a) == 0; } +inline bool operator==(const CString& a, char b) { return a.GetLength() == 1 && a[0] == b; } +inline bool operator!=(const CString& a, const CString& b) { return a.Compare(b) != 0; } +inline bool operator!=(const CString& a, const char* b) { return a.Compare(b) != 0; } +inline bool operator!=(const char* a, const CString& b) { return b.Compare(a) != 0; } +inline bool operator!=(const CString& a, char b) { return !(a == b); } +inline bool operator<(const CString& a, const CString& b) { return a.Compare(b) < 0; } +inline bool operator<(const CString& a, const char* b) { return a.Compare(b) < 0; } +inline bool operator<(const char* a, const CString& b) { return b.Compare(a) > 0; } +inline bool operator>(const CString& a, const CString& b) { return a.Compare(b) > 0; } +inline bool operator>(const CString& a, const char* b) { return a.Compare(b) > 0; } +inline bool operator>(const char* a, const CString& b) { return b.Compare(a) < 0; } +inline bool operator<=(const CString& a, const CString& b) { return a.Compare(b) <= 0; } +inline bool operator<=(const CString& a, const char* b) { return a.Compare(b) <= 0; } +inline bool operator<=(const char* a, const CString& b) { return b.Compare(a) >= 0; } +inline bool operator>=(const CString& a, const CString& b) { return a.Compare(b) >= 0; } +inline bool operator>=(const CString& a, const char* b) { return a.Compare(b) >= 0; } +inline bool operator>=(const char* a, const CString& b) { return b.Compare(a) <= 0; } + +typedef CString CStringA; +typedef CString CStringT; + +// Translates Microsoft-specific printf conversions (%I64d, %I32u, ...) to their C99 forms. +const char* AfxTranslateFormat(const char* fmt, char* buffer, size_t size); diff --git a/port/mfcwx/include/mfcwx/docview.h b/port/mfcwx/include/mfcwx/docview.h new file mode 100644 index 000000000..acf71f23e --- /dev/null +++ b/port/mfcwx/include/mfcwx/docview.h @@ -0,0 +1,460 @@ +#pragma once + +// Document/view architecture, frames and control bars. + +class CCreateContext { +public: + CCreateContext() = default; + CRuntimeClass* m_pNewViewClass = nullptr; + CDocument* m_pCurrentDoc = nullptr; + CDocTemplate* m_pNewDocTemplate = nullptr; + CView* m_pLastView = nullptr; + CFrameWnd* m_pCurrentFrame = nullptr; +}; + +class CPrintDialog; + +class CPrintInfo { +public: + CPrintInfo(); + ~CPrintInfo(); + void SetMinPage(UINT nMinPage) { m_nMinPage = nMinPage; } + void SetMaxPage(UINT nMaxPage) { m_nMaxPage = nMaxPage; } + UINT GetMinPage() const { return m_nMinPage; } + UINT GetMaxPage() const { return m_nMaxPage; } + UINT GetFromPage() const { return m_nMinPage; } + UINT GetToPage() const { return m_nMaxPage; } + UINT GetOffsetPage() const { return 0; } + CPrintDialog* m_pPD; + BOOL m_bDirect; + BOOL m_bPreview; + BOOL m_bContinuePrinting; + UINT m_nCurPage; + UINT m_nNumPreviewPages; + CString m_strPageDesc; + LPVOID m_lpUserData; + CRect m_rectDraw; + UINT m_nMinPage; + UINT m_nMaxPage; +}; + +class CDocument : public CCmdTarget { + DECLARE_DYNAMIC(CDocument) +public: + CDocument(); + ~CDocument() override; + + const CString& GetTitle() const { return m_strTitle; } + virtual void SetTitle(const char* lpszTitle); + const CString& GetPathName() const { return m_strPathName; } + virtual void SetPathName(const char* lpszPathName, BOOL bAddToMRU = TRUE); + CDocTemplate* GetDocTemplate() const { return m_pDocTemplate; } + virtual BOOL IsModified() { return m_bModified; } + virtual void SetModifiedFlag(BOOL bModified = TRUE) { m_bModified = bModified; } + virtual POSITION GetFirstViewPosition() const; + virtual CView* GetNextView(POSITION& rPosition) const; + void UpdateAllViews(CView* pSender, LPARAM lHint = 0, CObject* pHint = nullptr); + void AddView(CView* pView); + void RemoveView(CView* pView); + virtual void DeleteContents() {} + virtual BOOL OnNewDocument(); + virtual BOOL OnOpenDocument(const char* lpszPathName); + virtual BOOL OnSaveDocument(const char* lpszPathName); + virtual void OnCloseDocument(); + virtual void ReportSaveLoadException(const char* lpszPathName, CException* e, BOOL bSaving, UINT nIDPDefault); + virtual CFile* GetFile(const char* lpszFileName, UINT nOpenFlags, CFileException* pError); + virtual void ReleaseFile(CFile* pFile, BOOL bAbort); + virtual BOOL CanCloseFrame(CFrameWnd* pFrame); + virtual BOOL SaveModified(); + virtual void PreCloseFrame(CFrameWnd* pFrame) {} + virtual BOOL DoSave(const char* lpszPathName, BOOL bReplace = TRUE); + virtual BOOL DoFileSave(); + virtual void OnChangedViewList(); + virtual void SetModifiedFlagNoUpdate(BOOL b) { m_bModified = b; } + virtual void OnIdle() {} + void SendInitialUpdate(); + BOOL OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo) override; + + afx_msg void OnFileClose(); + afx_msg void OnFileSave(); + afx_msg void OnFileSaveAs(); + + CString m_strTitle; + CString m_strPathName; + CDocTemplate* m_pDocTemplate; + std::vector m_viewList; + BOOL m_bModified; + BOOL m_bAutoDelete; + BOOL m_bEmbedded; + +protected: + DECLARE_MESSAGE_MAP() +}; + +class CView : public CWnd { + DECLARE_DYNAMIC(CView) +public: + CView(); + ~CView() override; + + CDocument* GetDocument() const { return m_pDocument; } + BOOL PreCreateWindow(CREATESTRUCT& cs) override; + BOOL OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo) override; + virtual void OnInitialUpdate(); + virtual void OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint); + virtual void OnDraw(CDC* pDC) = 0; + virtual void OnPrepareDC(CDC* pDC, CPrintInfo* pInfo = nullptr); + virtual void OnActivateView(BOOL bActivate, CView* pActivateView, CView* pDeactiveView); + virtual void OnActivateFrame(UINT, CFrameWnd*) {} + virtual BOOL OnPreparePrinting(CPrintInfo* pInfo); + virtual void OnBeginPrinting(CDC* pDC, CPrintInfo* pInfo); + virtual void OnPrint(CDC* pDC, CPrintInfo* pInfo); + virtual void OnEndPrinting(CDC* pDC, CPrintInfo* pInfo); + virtual void OnEndPrintPreview(CDC*, CPrintInfo*, POINT, void*) {} + virtual BOOL IsSelected(const CObject*) const { return FALSE; } + virtual BOOL OnScrollBy(CSize, BOOL = TRUE) { return FALSE; } + virtual BOOL OnScroll(UINT, UINT, BOOL = TRUE) { return FALSE; } + BOOL DoPreparePrinting(CPrintInfo* pInfo); + void PostNcDestroy() override; + + afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); + afx_msg void OnDestroy(); + afx_msg void OnPaint(); + afx_msg void OnFilePrint(); + afx_msg void OnFilePrintPreview(); + + CDocument* m_pDocument; + +protected: + DECLARE_MESSAGE_MAP() +}; + +class CScrollView : public CView { + DECLARE_DYNAMIC(CScrollView) +public: + CScrollView(); + static const SIZE sizeDefault; + void SetScrollSizes(int nMapMode, SIZE sizeTotal, const SIZE& sizePage = sizeDefault, + const SIZE& sizeLine = sizeDefault); + void SetScaleToFitSize(SIZE sizeTotal); + CPoint GetScrollPosition() const; + CPoint GetDeviceScrollPosition() const; + CSize GetTotalSize() const { return m_totalLog; } + void ScrollToPosition(POINT pt); + void FillOutsideRect(CDC* pDC, CBrush* pBrush); + void ResizeParentToFit(BOOL bShrinkOnly = TRUE); + void OnPrepareDC(CDC* pDC, CPrintInfo* pInfo = nullptr) override; + + int m_nMapMode; + CSize m_totalLog; + CSize m_totalDev; + CSize m_pageDev; + CSize m_lineDev; +}; + +class CFrameWnd : public CWnd { + DECLARE_DYNCREATE(CFrameWnd) +public: + static const CRect rectDefault; + CFrameWnd(); + ~CFrameWnd() override; + + virtual BOOL Create(const char* lpszClassName, const char* lpszWindowName, DWORD dwStyle = WS_OVERLAPPEDWINDOW, + const RECT& rect = rectDefault, CWnd* pParentWnd = nullptr, const char* lpszMenuName = nullptr, + DWORD dwExStyle = 0, CCreateContext* pContext = nullptr); + virtual BOOL LoadFrame(UINT nIDResource, DWORD dwDefaultStyle = WS_OVERLAPPEDWINDOW | 0x00008000L, + CWnd* pParentWnd = nullptr, CCreateContext* pContext = nullptr); + BOOL PreCreateWindow(CREATESTRUCT& cs) override; + BOOL OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo) override; + BOOL OnCommand(WPARAM wParam, LPARAM lParam) override; + BOOL PreTranslateMessage(MSG* pMsg) override; + void PostNcDestroy() override; + + virtual CFrameWnd* GetActiveFrame(); + CView* GetActiveView() const; + void SetActiveView(CView* pViewNew, BOOL bNotify = TRUE); + virtual CDocument* GetActiveDocument(); + virtual void InitialUpdateFrame(CDocument* pDoc, BOOL bMakeVisible); + virtual void ActivateFrame(int nCmdShow = -1); + virtual void OnUpdateFrameTitle(BOOL bAddToTitle); + virtual void OnUpdateFrameMenu(HMENU hMenuAlt); + virtual void RecalcLayout(BOOL bNotify = TRUE); + virtual void GetMessageString(UINT nID, CString& rMessage) const; + virtual BOOL OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext); + virtual void SetMessageText(const char* lpszText); + void SetMessageText(UINT nID); + CWnd* CreateView(CCreateContext* pContext, UINT nID = AFX_IDW_PANE_FIRST); + void SetTitle(const char* lpszTitle) { m_strTitle = lpszTitle; } + CString GetTitle() const { return m_strTitle; } + void EnableDocking(DWORD dwDockStyle); + void DockControlBar(CControlBar* pBar, UINT nDockBarID = 0, LPCRECT lpRect = nullptr); + void FloatControlBar(CControlBar*, CPoint, DWORD = CBRS_ALIGN_TOP) {} + void ShowControlBar(CControlBar* pBar, BOOL bShow, BOOL bDelay); + CControlBar* GetControlBar(UINT nID); + BOOL IsTracking() const { return FALSE; } + void BeginModalState() {} + void EndModalState() {} + BOOL InModalState() const { return FALSE; } + void SetDockState(const void*) {} + void LoadBarState(const char*) {} + void SaveBarState(const char*) const {} + void OnSetPreviewMode(BOOL, void*) {} + void UpdateFrameTitleForDocument(const char* lpszDocName); + void NotifyFloatingWindows(DWORD) {} + BOOL IsFrameWnd() const { return TRUE; } + HMENU GetDefaultMenu() { return m_hMenuDefault; } + + afx_msg int OnCreate(LPCREATESTRUCT lpcs); + afx_msg void OnClose(); + afx_msg void OnDestroy(); + afx_msg void OnSize(UINT nType, int cx, int cy); + afx_msg void OnSetFocus(CWnd* pOldWnd); + afx_msg void OnUpdateControlBarMenu(CCmdUI* pCmdUI); + afx_msg BOOL OnBarCheck(UINT nID); + afx_msg void OnHelp(); + afx_msg void OnContextHelp(); + + CString m_strTitle; + UINT m_nIDHelp; + HMENU m_hMenuDefault; + HACCEL m_hAccelTable; + BOOL m_bAutoMenuEnable; + UINT m_nIdleFlags; + CView* m_pViewActive; + std::vector m_listControlBars; + CStatusBar* m_pStatusBar; + +protected: + DECLARE_MESSAGE_MAP() +}; + +class CMDIChildWnd; + +class CMDIFrameWnd : public CFrameWnd { + DECLARE_DYNAMIC(CMDIFrameWnd) +public: + CMDIFrameWnd(); + BOOL LoadFrame(UINT nIDResource, DWORD dwDefaultStyle = WS_OVERLAPPEDWINDOW | 0x00008000L, + CWnd* pParentWnd = nullptr, CCreateContext* pContext = nullptr) override; + BOOL OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo) override; + CFrameWnd* GetActiveFrame() override; + void OnUpdateFrameTitle(BOOL bAddToTitle) override; + void OnUpdateFrameMenu(HMENU hMenuAlt) override; + BOOL OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext) override; + void RecalcLayout(BOOL bNotify = TRUE) override; + + void MDIActivate(CWnd* pWndActivate); + CMDIChildWnd* MDIGetActive(BOOL* pbMaximized = nullptr) const; + void MDIIconArrange() {} + void MDIMaximize(CWnd* pWnd); + void MDINext(); + void MDIPrev(); + void MDIRestore(CWnd* pWnd); + CMenu* MDISetMenu(CMenu* pFrameMenu, CMenu* pWindowMenu); + void MDITile(); + void MDICascade(); + void MDITile(int) { MDITile(); } + void MDICascade(int) { MDICascade(); } + CMDIChildWnd* CreateNewChild(CRuntimeClass* pClass, UINT nResource, HMENU hMenu = nullptr, HACCEL hAccel = nullptr); + HWND m_hWndMDIClient; + + afx_msg void OnWindowNew(); + afx_msg void OnWindowCascade(); + afx_msg void OnWindowTile(); + afx_msg void OnUpdateMDIWindowCmd(CCmdUI* pCmdUI); + + // implementation: all MDI children in creation order + std::vector m_children; + +protected: + DECLARE_MESSAGE_MAP() +}; + +class CMDIChildWnd : public CFrameWnd { + DECLARE_DYNCREATE(CMDIChildWnd) +public: + CMDIChildWnd(); + ~CMDIChildWnd() override; + virtual BOOL Create(const char* lpszClassName, const char* lpszWindowName, + DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_OVERLAPPEDWINDOW, const RECT& rect = rectDefault, + CMDIFrameWnd* pParentWnd = nullptr, CCreateContext* pContext = nullptr); + BOOL LoadFrame(UINT nIDResource, DWORD dwDefaultStyle = WS_CHILD | WS_VISIBLE | WS_OVERLAPPEDWINDOW | 0x00008000L, + CWnd* pParentWnd = nullptr, CCreateContext* pContext = nullptr) override; + BOOL DestroyWindow() override; + BOOL PreCreateWindow(CREATESTRUCT& cs) override; + void ActivateFrame(int nCmdShow = -1) override; + void OnUpdateFrameMenu(BOOL bActive, CWnd* pActivateWnd, HMENU hMenuAlt); + void OnUpdateFrameTitle(BOOL bAddToTitle) override; + CMDIFrameWnd* GetMDIFrame() const; + void MDIDestroy(); + void MDIActivate(); + void MDIMaximize(); + void MDIRestore(); + void SetHandles(HMENU hMenu, HACCEL hAccel) { m_hMenuShared = hMenu; m_hAccelTable = hAccel; } + BOOL PreTranslateMessage(MSG* pMsg) override; + + HMENU m_hMenuShared; + CMDIFrameWnd* m_pMDIFrame; + +protected: + DECLARE_MESSAGE_MAP() +}; + +class CDocTemplate : public CCmdTarget { + DECLARE_DYNAMIC(CDocTemplate) +public: + enum DocStringIndex { windowTitle, docName, fileNewName, filterName, filterExt, regFileTypeId, regFileTypeName }; + enum Confidence { noAttempt, maybeAttemptForeign, maybeAttemptNative, yesAttemptForeign, yesAttemptNative, yesAlreadyOpen }; + + CDocTemplate(UINT nIDResource, CRuntimeClass* pDocClass, CRuntimeClass* pFrameClass, CRuntimeClass* pViewClass); + ~CDocTemplate() override; + + virtual void LoadTemplate(); + virtual POSITION GetFirstDocPosition() const = 0; + virtual CDocument* GetNextDoc(POSITION& rPos) const = 0; + virtual void AddDocument(CDocument* pDoc); + virtual void RemoveDocument(CDocument* pDoc); + virtual BOOL GetDocString(CString& rString, enum DocStringIndex index) const; + virtual CDocument* CreateNewDocument(); + virtual CFrameWnd* CreateNewFrame(CDocument* pDoc, CFrameWnd* pOther); + virtual void InitialUpdateFrame(CFrameWnd* pFrame, CDocument* pDoc, BOOL bMakeVisible = TRUE); + virtual BOOL SaveAllModified(); + virtual void CloseAllDocuments(BOOL bEndSession); + virtual CDocument* OpenDocumentFile(const char* lpszPathName, BOOL bMakeVisible = TRUE) = 0; + virtual void SetDefaultTitle(CDocument* pDocument) = 0; + virtual Confidence MatchDocType(const char* lpszPathName, CDocument*& rpDocMatch); + void SetContainerInfo(UINT) {} + void SetServerInfo(UINT, UINT = 0, CRuntimeClass* = nullptr) {} + + UINT m_nIDResource; + CRuntimeClass* m_pDocClass; + CRuntimeClass* m_pFrameClass; + CRuntimeClass* m_pViewClass; + CString m_strDocStrings; + BOOL m_bAutoDelete; +}; + +class CMultiDocTemplate : public CDocTemplate { + DECLARE_DYNAMIC(CMultiDocTemplate) +public: + CMultiDocTemplate(UINT nIDResource, CRuntimeClass* pDocClass, CRuntimeClass* pFrameClass, + CRuntimeClass* pViewClass); + ~CMultiDocTemplate() override; + void LoadTemplate() override; + POSITION GetFirstDocPosition() const override; + CDocument* GetNextDoc(POSITION& rPos) const override; + void AddDocument(CDocument* pDoc) override; + void RemoveDocument(CDocument* pDoc) override; + CDocument* OpenDocumentFile(const char* lpszPathName, BOOL bMakeVisible = TRUE) override; + void SetDefaultTitle(CDocument* pDocument) override; + + HMENU m_hMenuShared; + HACCEL m_hAccelTable; + std::vector m_docList; + UINT m_nUntitledCount; +}; + +class CSingleDocTemplate : public CDocTemplate { + DECLARE_DYNAMIC(CSingleDocTemplate) +public: + CSingleDocTemplate(UINT nIDResource, CRuntimeClass* pDocClass, CRuntimeClass* pFrameClass, + CRuntimeClass* pViewClass); + POSITION GetFirstDocPosition() const override; + CDocument* GetNextDoc(POSITION& rPos) const override; + void AddDocument(CDocument* pDoc) override; + void RemoveDocument(CDocument* pDoc) override; + CDocument* OpenDocumentFile(const char* lpszPathName, BOOL bMakeVisible = TRUE) override; + void SetDefaultTitle(CDocument* pDocument) override; + CDocument* m_pOnlyDoc; +}; + +// --------------------------------------------------------------------------------------------- +// Control bars + +class CControlBar : public CWnd { + DECLARE_DYNAMIC(CControlBar) +public: + CControlBar(); + DWORD GetBarStyle() { return m_dwStyle; } + void SetBarStyle(DWORD dwStyle) { m_dwStyle = dwStyle; } + void EnableDocking(DWORD dwDockStyle) { m_dwDockStyle = dwDockStyle; } + BOOL IsFloating() const { return FALSE; } + CFrameWnd* GetDockingFrame() const { return GetParentFrame(); } + virtual CSize CalcFixedLayout(BOOL bStretch, BOOL bHorz); + DWORD m_dwStyle; + DWORD m_dwDockStyle; +}; + +#define TBBS_BUTTON 0x0000 +#define TBBS_SEPARATOR 0x0001 +#define TBBS_CHECKBOX 0x0002 +#define TBBS_GROUP 0x0004 +#define TBBS_CHECKGROUP (TBBS_GROUP | TBBS_CHECKBOX) +#define TBBS_CHECKED 0x0400 +#define TBBS_PRESSED 0x0200 +#define TBBS_DISABLED 0x0100 +#define TBBS_INDETERMINATE 0x0800 +#define TBBS_HIDDEN 0x1000 + +class CToolBar : public CControlBar { + DECLARE_DYNAMIC(CToolBar) +public: + CToolBar(); + BOOL Create(CWnd* pParentWnd, DWORD dwStyle = WS_CHILD | WS_VISIBLE | CBRS_TOP, UINT nID = AFX_IDW_TOOLBAR); + BOOL CreateEx(CWnd* pParentWnd, DWORD dwCtrlStyle = TBSTYLE_FLAT, + DWORD dwStyle = WS_CHILD | WS_VISIBLE | CBRS_ALIGN_TOP, CRect rcBorders = CRect(0, 0, 0, 0), + UINT nID = AFX_IDW_TOOLBAR); + BOOL LoadToolBar(const char* lpszResourceName); + BOOL LoadToolBar(UINT nIDResource); + BOOL LoadBitmap(const char* lpszResourceName); + BOOL LoadBitmap(UINT nIDResource); + BOOL SetButtons(const UINT* lpIDArray, int nIDCount); + void SetSizes(SIZE sizeButton, SIZE sizeImage); + int CommandToIndex(UINT nIDFind) const; + UINT GetItemID(int nIndex) const; + void GetItemRect(int nIndex, LPRECT lpRect) const; + UINT GetButtonStyle(int nIndex) const; + void SetButtonStyle(int nIndex, UINT nStyle); + int GetCount() const; + void SetButtonText(int, const char*) {} + void SetHeight(int) {} + + std::vector m_buttonIDs; + UINT m_nResourceID; +}; + +#define SBPS_NORMAL 0x0000 +#define SBPS_NOBORDERS 0x0100 +#define SBPS_POPOUT 0x0200 +#define SBPS_OWNERDRAW 0x1000 +#define SBPS_DISABLED 0x04000000 +#define SBPS_STRETCH 0x08000000 + +class CStatusBar : public CControlBar { + DECLARE_DYNAMIC(CStatusBar) +public: + CStatusBar(); + BOOL Create(CWnd* pParentWnd, DWORD dwStyle = WS_CHILD | WS_VISIBLE | CBRS_BOTTOM, UINT nID = AFX_IDW_STATUS_BAR); + BOOL CreateEx(CWnd* pParentWnd, DWORD dwCtrlStyle = 0, DWORD dwStyle = WS_CHILD | WS_VISIBLE | CBRS_BOTTOM, + UINT nID = AFX_IDW_STATUS_BAR); + BOOL SetIndicators(const UINT* lpIDArray, int nIDCount); + int CommandToIndex(UINT nIDFind) const; + UINT GetItemID(int nIndex) const; + void GetItemRect(int nIndex, LPRECT lpRect) const; + CString GetPaneText(int nIndex) const; + void GetPaneText(int nIndex, CString& rString) const; + BOOL SetPaneText(int nIndex, const char* lpszNewText, BOOL bUpdate = TRUE); + void GetPaneInfo(int nIndex, UINT& nID, UINT& nStyle, int& cxWidth) const; + void SetPaneInfo(int nIndex, UINT nID, UINT nStyle, int cxWidth); + UINT GetPaneStyle(int nIndex) const; + void SetPaneStyle(int nIndex, UINT nStyle); + CString GetText() const { return GetPaneText(0); } + void SetText(const char* lpsz) { SetPaneText(0, lpsz); } + + std::vector m_indicators; +}; + +class CDialogBar : public CControlBar { +public: + BOOL Create(CWnd*, UINT, UINT, UINT) { return FALSE; } +}; diff --git a/port/mfcwx/include/mfcwx/extras.h b/port/mfcwx/include/mfcwx/extras.h new file mode 100644 index 000000000..15ba7dcd3 --- /dev/null +++ b/port/mfcwx/include/mfcwx/extras.h @@ -0,0 +1,143 @@ +#pragma once + +// Assorted Win32/MFC declarations used by the application that do not fit elsewhere. + +#define CS_VREDRAW 0x0001 +#define CS_HREDRAW 0x0002 +#define CS_DBLCLKS 0x0008 +#define CS_OWNDC 0x0020 +#define CS_CLASSDC 0x0040 +#define CS_PARENTDC 0x0080 +#define CS_NOCLOSE 0x0200 +#define CS_SAVEBITS 0x0800 +#define CS_BYTEALIGNCLIENT 0x1000 +#define CS_GLOBALCLASS 0x4000 +#define GCL_HCURSOR (-12) +#define GCL_HICON (-14) +#define GCL_HBRBACKGROUND (-10) +#define GCLP_HCURSOR (-12) +#define GCLP_HICON (-14) +#define SM_CXDRAG 68 +#define SM_CYDRAG 69 +#define TCS_EX_FLATSEPARATORS 0x00000001 +#define TCS_EX_REGISTERDROP 0x00000002 +#define LOCALE_IMEASURE 0x0000000D +#define LOCALE_SDECIMAL 0x0000000E +#define LOCALE_STHOUSAND 0x0000000F +#define LOCALE_SLANGUAGE 0x00000002 +#define LOCALE_SENGLANGUAGE 0x00001001 +#define LOCALE_SABBREVLANGNAME 0x00000003 +#define LOCALE_IDEFAULTANSICODEPAGE 0x00001004 +#define UNUSED(x) (void)(x) +#define UNUSED_ALWAYS(x) (void)(x) + +LONG GetClassLong(HWND hWnd, int nIndex); +LONG SetClassLong(HWND hWnd, int nIndex, LONG dwNewLong); +ULONG_PTR SetClassLongPtr(HWND hWnd, int nIndex, LONG_PTR dwNewLong); +BOOL GetClassInfo(HINSTANCE hInstance, const char* lpClassName, WNDCLASS* lpWndClass); +ATOM RegisterClass(const WNDCLASS* lpWndClass); +BOOL UnregisterClass(const char* lpClassName, HINSTANCE hInstance); +HWND CreateWindowEx(DWORD dwExStyle, const char* lpClassName, const char* lpWindowName, DWORD dwStyle, int X, int Y, + int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, HINSTANCE hInstance, LPVOID lpParam); +HWND CreateWindow(const char* lpClassName, const char* lpWindowName, DWORD dwStyle, int X, int Y, int nWidth, + int nHeight, HWND hWndParent, HMENU hMenu, HINSTANCE hInstance, LPVOID lpParam); +HWND GetNextWindow(HWND hWnd, UINT wCmd); +BOOL DestroyCaret(); +BOOL CreateCaret(HWND hWnd, HBITMAP hBitmap, int nWidth, int nHeight); +BOOL ShowCaret(HWND hWnd); +BOOL HideCaret(HWND hWnd); +BOOL SetCaretPos(int X, int Y); +int GetLocaleInfo(LCID Locale, DWORD LCType, char* lpLCData, int cchData); +int GetLocaleInfoA(LCID Locale, DWORD LCType, char* lpLCData, int cchData); +BOOL GetOpenFileName(OPENFILENAME* lpofn); +BOOL GetSaveFileName(OPENFILENAME* lpofn); +BOOL GetOpenFileNameA(OPENFILENAME* lpofn); +BOOL GetSaveFileNameA(OPENFILENAME* lpofn); +BOOL ChooseColor(CHOOSECOLOR* lpcc); +BOOL MessageBeep(UINT uType); +BOOL FlashWindow(HWND hWnd, BOOL bInvert); + +#define PD_ALLPAGES 0x00000000 +#define PD_SELECTION 0x00000001 +#define PD_PAGENUMS 0x00000002 +#define PD_NOSELECTION 0x00000004 +#define PD_NOPAGENUMS 0x00000008 +#define PD_COLLATE 0x00000010 +#define PD_PRINTTOFILE 0x00000020 +#define PD_PRINTSETUP 0x00000040 +#define PD_RETURNDC 0x00000100 +#define PD_USEDEVMODECOPIES 0x00040000 +#define PD_DISABLEPRINTTOFILE 0x00080000 +#define PD_HIDEPRINTTOFILE 0x00100000 +typedef struct tagPDA { + DWORD lStructSize; + HWND hwndOwner; + HGLOBAL hDevMode, hDevNames; + HDC hDC; + DWORD Flags; + WORD nFromPage, nToPage, nMinPage, nMaxPage, nCopies; + HINSTANCE hInstance; + LPARAM lCustData; +} PRINTDLG, *LPPRINTDLG; +typedef struct _devicemodeA { char dmDeviceName[32]; WORD dmSpecVersion, dmDriverVersion, dmSize, dmDriverExtra; DWORD dmFields; short dmOrientation, dmPaperSize, dmPaperLength, dmPaperWidth; } DEVMODE, *LPDEVMODE; +typedef struct tagDEVNAMES { WORD wDriverOffset, wDeviceOffset, wOutputOffset, wDefault; } DEVNAMES, *LPDEVNAMES; +#define DMORIENT_PORTRAIT 1 +#define DMORIENT_LANDSCAPE 2 + +typedef struct tagPALETTEENTRY { BYTE peRed, peGreen, peBlue, peFlags; } PALETTEENTRY, *LPPALETTEENTRY; +typedef struct tagLOGPALETTE { WORD palVersion, palNumEntries; PALETTEENTRY palPalEntry[1]; } LOGPALETTE; +#define PC_RESERVED 0x01 +#define PC_EXPLICIT 0x02 +#define PC_NOCOLLAPSE 0x04 + +#define ListView_GetSubItemRect(hwnd, iItem, iSubItem, code, prc) \ + (BOOL)SendMessage((hwnd), LVM_GETSUBITEMRECT, (WPARAM)(int)(iItem), \ + ((prc) ? ((((LPRECT)(prc))->top = (iSubItem)), (((LPRECT)(prc))->left = (code)), (LPARAM)(prc)) : (LPARAM)(LPRECT)NULL)) +#define ListView_GetTopIndex(hwnd) (int)SendMessage((hwnd), LVM_GETTOPINDEX, 0, 0) +#define ListView_GetItemText(hwndLV, i, iSubItem_, pszText_, cchTextMax_) \ + { LVITEM _lvi; _lvi.iSubItem = (iSubItem_); _lvi.cchTextMax = (cchTextMax_); _lvi.pszText = (pszText_); \ + SendMessage((hwndLV), LVM_GETITEMTEXT, (WPARAM)(i), (LPARAM)&_lvi); } +#define ListView_SetExtendedListViewStyle(hwnd, style) SendMessage((hwnd), LVM_SETEXTENDEDLISTVIEWSTYLE, 0, (style)) +#define ListView_GetItemCount(hwnd) (int)SendMessage((hwnd), LVM_GETITEMCOUNT, 0, 0) + +class CWaitCursor { +public: + CWaitCursor(); + ~CWaitCursor(); + void Restore() {} +}; + +// WGL: OpenGL rendering into a window. mfcwx backs the window's client area with a wxGLCanvas. +typedef struct tagPIXELFORMATDESCRIPTOR { + WORD nSize, nVersion; + DWORD dwFlags; + BYTE iPixelType, cColorBits, cRedBits, cRedShift, cGreenBits, cGreenShift, cBlueBits, cBlueShift, cAlphaBits, + cAlphaShift, cAccumBits, cAccumRedBits, cAccumGreenBits, cAccumBlueBits, cAccumAlphaBits, cDepthBits, + cStencilBits, cAuxBuffers, iLayerType, bReserved; + DWORD dwLayerMask, dwVisibleMask, dwDamageMask; +} PIXELFORMATDESCRIPTOR, *PPIXELFORMATDESCRIPTOR, *LPPIXELFORMATDESCRIPTOR; +#define PFD_TYPE_RGBA 0 +#define PFD_TYPE_COLORINDEX 1 +#define PFD_MAIN_PLANE 0 +#define PFD_DOUBLEBUFFER 0x00000001 +#define PFD_STEREO 0x00000002 +#define PFD_DRAW_TO_WINDOW 0x00000004 +#define PFD_DRAW_TO_BITMAP 0x00000008 +#define PFD_SUPPORT_GDI 0x00000010 +#define PFD_SUPPORT_OPENGL 0x00000020 +#define PFD_GENERIC_FORMAT 0x00000040 +#define PFD_NEED_PALETTE 0x00000080 +#define PFD_NEED_SYSTEM_PALETTE 0x00000100 +#define PFD_SWAP_EXCHANGE 0x00000200 +#define PFD_GENERIC_ACCELERATED 0x00001000 +int ChoosePixelFormat(HDC hdc, const PIXELFORMATDESCRIPTOR* ppfd); +BOOL SetPixelFormat(HDC hdc, int format, const PIXELFORMATDESCRIPTOR* ppfd); +int GetPixelFormat(HDC hdc); +int DescribePixelFormat(HDC hdc, int iPixelFormat, UINT nBytes, LPPIXELFORMATDESCRIPTOR ppfd); +BOOL SwapBuffers(HDC hdc); +HGLRC wglCreateContext(HDC hdc); +BOOL wglDeleteContext(HGLRC hglrc); +BOOL wglMakeCurrent(HDC hdc, HGLRC hglrc); +HGLRC wglGetCurrentContext(); +HDC wglGetCurrentDC(); +void* wglGetProcAddress(const char* name); diff --git a/port/mfcwx/include/mfcwx/fstreamcompat.h b/port/mfcwx/include/mfcwx/fstreamcompat.h new file mode 100644 index 000000000..95b99181d --- /dev/null +++ b/port/mfcwx/include/mfcwx/fstreamcompat.h @@ -0,0 +1,62 @@ +#pragma once + +// File streams that accept Windows-style paths ("dir\\file.txt") and are constructible +// from const char* by copy-initialization, as the pre-standard allowed. + +#include +#include + +#include "mfcwx/codepage.h" + +namespace std { + +class mfcwx_ifstream : public basic_ifstream { +public: + mfcwx_ifstream() = default; + mfcwx_ifstream(const char* path, ios_base::openmode mode = ios_base::in) + : basic_ifstream(::mfcwx::NativePath(path), mode) {} + mfcwx_ifstream(const string& path, ios_base::openmode mode = ios_base::in) + : mfcwx_ifstream(path.c_str(), mode) {} + void open(const char* path, ios_base::openmode mode = ios_base::in) { + basic_ifstream::open(::mfcwx::NativePath(path), mode); + } + void open(const string& path, ios_base::openmode mode = ios_base::in) { open(path.c_str(), mode); } +}; + +class mfcwx_ofstream : public basic_ofstream { +public: + mfcwx_ofstream() = default; + mfcwx_ofstream(const char* path, ios_base::openmode mode = ios_base::out) + : basic_ofstream(::mfcwx::NativePath(path), mode) {} + mfcwx_ofstream(const string& path, ios_base::openmode mode = ios_base::out) + : mfcwx_ofstream(path.c_str(), mode) {} + void open(const char* path, ios_base::openmode mode = ios_base::out) { + basic_ofstream::open(::mfcwx::NativePath(path), mode); + } + void open(const string& path, ios_base::openmode mode = ios_base::out) { open(path.c_str(), mode); } +}; + +class mfcwx_fstream : public basic_fstream { +public: + mfcwx_fstream() = default; + mfcwx_fstream(const char* path, ios_base::openmode mode = ios_base::in | ios_base::out) + : basic_fstream(::mfcwx::NativePath(path), mode) {} + mfcwx_fstream(const string& path, ios_base::openmode mode = ios_base::in | ios_base::out) + : mfcwx_fstream(path.c_str(), mode) {} + void open(const char* path, ios_base::openmode mode = ios_base::in | ios_base::out) { + basic_fstream::open(::mfcwx::NativePath(path), mode); + } + void open(const string& path, ios_base::openmode mode = ios_base::in | ios_base::out) { + open(path.c_str(), mode); + } +}; + +} // namespace std + +using std::mfcwx_fstream; +using std::mfcwx_ifstream; +using std::mfcwx_ofstream; + +#define ifstream mfcwx_ifstream +#define ofstream mfcwx_ofstream +#define fstream mfcwx_fstream diff --git a/port/mfcwx/include/mfcwx/geometry.h b/port/mfcwx/include/mfcwx/geometry.h new file mode 100644 index 000000000..a82b4b858 --- /dev/null +++ b/port/mfcwx/include/mfcwx/geometry.h @@ -0,0 +1,133 @@ +#pragma once + +#include "mfcwx/wintypes.h" + +class CPoint; +class CRect; + +class CSize : public tagSIZE { +public: + CSize() { cx = cy = 0; } + CSize(int initCX, int initCY) { cx = initCX; cy = initCY; } + CSize(SIZE initSize) { cx = initSize.cx; cy = initSize.cy; } + CSize(POINT initPt) { cx = initPt.x; cy = initPt.y; } + CSize(DWORD dwSize) { cx = static_cast(dwSize & 0xffff); cy = static_cast(dwSize >> 16); } + bool operator==(SIZE s) const { return cx == s.cx && cy == s.cy; } + bool operator!=(SIZE s) const { return !(*this == s); } + void operator+=(SIZE s) { cx += s.cx; cy += s.cy; } + void operator-=(SIZE s) { cx -= s.cx; cy -= s.cy; } + void SetSize(int CX, int CY) { cx = CX; cy = CY; } + CSize operator+(SIZE s) const { return CSize(cx + s.cx, cy + s.cy); } + CSize operator-(SIZE s) const { return CSize(cx - s.cx, cy - s.cy); } + CSize operator-() const { return CSize(-cx, -cy); } + CPoint operator+(POINT p) const; + CPoint operator-(POINT p) const; +}; + +class CPoint : public tagPOINT { +public: + CPoint() { x = y = 0; } + CPoint(int initX, int initY) { x = initX; y = initY; } + CPoint(POINT initPt) { x = initPt.x; y = initPt.y; } + CPoint(SIZE initSize) { x = initSize.cx; y = initSize.cy; } + CPoint(LPARAM dwPoint) { x = static_cast(dwPoint & 0xffff); y = static_cast((dwPoint >> 16) & 0xffff); } + void Offset(int xOffset, int yOffset) { x += xOffset; y += yOffset; } + void Offset(POINT point) { x += point.x; y += point.y; } + void Offset(SIZE size) { x += size.cx; y += size.cy; } + void SetPoint(int X, int Y) { x = X; y = Y; } + bool operator==(POINT p) const { return x == p.x && y == p.y; } + bool operator!=(POINT p) const { return !(*this == p); } + void operator+=(SIZE s) { x += s.cx; y += s.cy; } + void operator-=(SIZE s) { x -= s.cx; y -= s.cy; } + void operator+=(POINT p) { x += p.x; y += p.y; } + void operator-=(POINT p) { x -= p.x; y -= p.y; } + CPoint operator+(SIZE s) const { return CPoint(x + s.cx, y + s.cy); } + CPoint operator-(SIZE s) const { return CPoint(x - s.cx, y - s.cy); } + CPoint operator-() const { return CPoint(-x, -y); } + CPoint operator+(POINT p) const { return CPoint(x + p.x, y + p.y); } + CSize operator-(POINT p) const { return CSize(x - p.x, y - p.y); } +}; + +inline CPoint CSize::operator+(POINT p) const { return CPoint(cx + p.x, cy + p.y); } +inline CPoint CSize::operator-(POINT p) const { return CPoint(cx - p.x, cy - p.y); } + +class CRect : public tagRECT { +public: + CRect() { left = top = right = bottom = 0; } + CRect(int l, int t, int r, int b) { left = l; top = t; right = r; bottom = b; } + CRect(const RECT& srcRect) { left = srcRect.left; top = srcRect.top; right = srcRect.right; bottom = srcRect.bottom; } + CRect(LPCRECT lpSrcRect) { *this = *lpSrcRect; } + CRect(POINT point, SIZE size) { left = point.x; top = point.y; right = left + size.cx; bottom = top + size.cy; } + CRect(POINT topLeft, POINT bottomRight) { left = topLeft.x; top = topLeft.y; right = bottomRight.x; bottom = bottomRight.y; } + CRect& operator=(const RECT& r) { left = r.left; top = r.top; right = r.right; bottom = r.bottom; return *this; } + + int Width() const { return right - left; } + int Height() const { return bottom - top; } + CSize Size() const { return CSize(Width(), Height()); } + CPoint& TopLeft() { return *reinterpret_cast(this); } + CPoint& BottomRight() { return *(reinterpret_cast(this) + 1); } + const CPoint& TopLeft() const { return *reinterpret_cast(this); } + const CPoint& BottomRight() const { return *(reinterpret_cast(this) + 1); } + CPoint CenterPoint() const { return CPoint((left + right) / 2, (top + bottom) / 2); } + operator LPRECT() { return this; } + operator LPCRECT() const { return this; } + BOOL IsRectEmpty() const { return left >= right || top >= bottom; } + BOOL IsRectNull() const { return left == 0 && right == 0 && top == 0 && bottom == 0; } + BOOL PtInRect(POINT point) const { return point.x >= left && point.x < right && point.y >= top && point.y < bottom; } + void SetRect(int x1, int y1, int x2, int y2) { left = x1; top = y1; right = x2; bottom = y2; } + void SetRect(POINT topLeft, POINT bottomRight) { SetRect(topLeft.x, topLeft.y, bottomRight.x, bottomRight.y); } + void SetRectEmpty() { left = top = right = bottom = 0; } + void CopyRect(LPCRECT lpSrcRect) { *this = *lpSrcRect; } + BOOL EqualRect(LPCRECT r) const { return left == r->left && top == r->top && right == r->right && bottom == r->bottom; } + void InflateRect(int x, int y) { left -= x; top -= y; right += x; bottom += y; } + void InflateRect(SIZE size) { InflateRect(size.cx, size.cy); } + void InflateRect(LPCRECT r) { left -= r->left; top -= r->top; right += r->right; bottom += r->bottom; } + void InflateRect(int l, int t, int r, int b) { left -= l; top -= t; right += r; bottom += b; } + void DeflateRect(int x, int y) { InflateRect(-x, -y); } + void DeflateRect(SIZE size) { InflateRect(-size.cx, -size.cy); } + void DeflateRect(LPCRECT r) { left += r->left; top += r->top; right -= r->right; bottom -= r->bottom; } + void DeflateRect(int l, int t, int r, int b) { left += l; top += t; right -= r; bottom -= b; } + void OffsetRect(int x, int y) { left += x; right += x; top += y; bottom += y; } + void OffsetRect(POINT point) { OffsetRect(point.x, point.y); } + void OffsetRect(SIZE size) { OffsetRect(size.cx, size.cy); } + void MoveToY(int y) { bottom = Height() + y; top = y; } + void MoveToX(int x) { right = Width() + x; left = x; } + void MoveToXY(int x, int y) { MoveToX(x); MoveToY(y); } + void MoveToXY(POINT p) { MoveToXY(p.x, p.y); } + void NormalizeRect() { + if (left > right) { LONG t = left; left = right; right = t; } + if (top > bottom) { LONG t = top; top = bottom; bottom = t; } + } + BOOL IntersectRect(LPCRECT r1, LPCRECT r2) { + left = r1->left > r2->left ? r1->left : r2->left; + top = r1->top > r2->top ? r1->top : r2->top; + right = r1->right < r2->right ? r1->right : r2->right; + bottom = r1->bottom < r2->bottom ? r1->bottom : r2->bottom; + if (IsRectEmpty()) { SetRectEmpty(); return FALSE; } + return TRUE; + } + BOOL UnionRect(LPCRECT r1, LPCRECT r2) { + CRect a(r1), b(r2); + if (a.IsRectEmpty()) { *this = b; return !IsRectEmpty(); } + if (b.IsRectEmpty()) { *this = a; return TRUE; } + left = a.left < b.left ? a.left : b.left; + top = a.top < b.top ? a.top : b.top; + right = a.right > b.right ? a.right : b.right; + bottom = a.bottom > b.bottom ? a.bottom : b.bottom; + return TRUE; + } + bool operator==(const RECT& r) const { return EqualRect(&r); } + bool operator!=(const RECT& r) const { return !EqualRect(&r); } + void operator+=(POINT p) { OffsetRect(p); } + void operator+=(SIZE s) { OffsetRect(s); } + void operator-=(POINT p) { OffsetRect(-p.x, -p.y); } + void operator-=(SIZE s) { OffsetRect(-s.cx, -s.cy); } + void operator&=(const RECT& r) { IntersectRect(this, &r); } + void operator|=(const RECT& r) { UnionRect(this, &r); } + CRect operator+(POINT p) const { CRect r(*this); r.OffsetRect(p); return r; } + CRect operator-(POINT p) const { CRect r(*this); r.OffsetRect(-p.x, -p.y); return r; } + CRect operator+(SIZE s) const { CRect r(*this); r.OffsetRect(s); return r; } + CRect operator-(SIZE s) const { CRect r(*this); r.OffsetRect(-s.cx, -s.cy); return r; } + CRect operator&(const RECT& r2) const { CRect r; r.IntersectRect(this, &r2); return r; } + CRect operator|(const RECT& r2) const { CRect r; r.UnionRect(this, &r2); return r; } +}; diff --git a/port/mfcwx/include/mfcwx/msgmap.h b/port/mfcwx/include/mfcwx/msgmap.h new file mode 100644 index 000000000..ef7dc58f8 --- /dev/null +++ b/port/mfcwx/include/mfcwx/msgmap.h @@ -0,0 +1,292 @@ +#pragma once + +#include + +// MFC message map machinery. Handlers are stored as CCmdTarget member pointers together with a +// signature code; CCmdTarget/CWnd dispatch converts them back to the exact handler type. + +class CCmdTarget; +class CWnd; +class CDC; +class CCmdUI; +class CScrollBar; +class CMenu; + +typedef void (CCmdTarget::*AFX_PMSG)(void); +typedef void (CWnd::*AFX_PMSGW)(void); + +enum AfxSig { + AfxSig_end = 0, + AfxSigCmd_v, // void () + AfxSigCmd_b, // BOOL () + AfxSigCmd_RANGE, // void (UINT) + AfxSigCmd_EX, // BOOL (UINT) + AfxSigCmdUI, // void (CCmdUI*) + AfxSigCmdUI_RANGE, // void (CCmdUI*, UINT) + AfxSigNotify_v, // void (NMHDR*, LRESULT*) + AfxSigNotify_b, // BOOL (NMHDR*, LRESULT*) + AfxSigNotify_RANGE, // void (UINT, NMHDR*, LRESULT*) + AfxSigNotify_EX, // BOOL (UINT, NMHDR*, LRESULT*) + AfxSig_vv, // void () + AfxSig_bv, // BOOL () + AfxSig_lwl, // LRESULT (WPARAM, LPARAM) + AfxSig_is, // int (LPCREATESTRUCT) + AfxSig_bD, // BOOL (CDC*) + AfxSig_vwii, // void (UINT, int, int) WM_SIZE + AfxSig_vwwx, // void (UINT, UINT, UINT) WM_KEYDOWN/WM_CHAR... + AfxSig_vwp, // void (UINT, CPoint) mouse + AfxSig_bwsp, // BOOL (UINT, short, CPoint) WM_MOUSEWHEEL + AfxSig_vwwW, // void (UINT, UINT, CScrollBar*) WM_HSCROLL/VSCROLL + AfxSig_hDWw, // HBRUSH (CDC*, CWnd*, UINT) WM_CTLCOLOR + AfxSig_hDw, // HBRUSH (CDC*, UINT) WM_CTLCOLOR reflect + AfxSig_vOWNER, // void (int, LPDRAWITEMSTRUCT) WM_DRAWITEM + AfxSig_vMEASURE, // void (int, LPMEASUREITEMSTRUCT) WM_MEASUREITEM + AfxSig_vW, // void (CWnd*) WM_SETFOCUS/KILLFOCUS + AfxSig_vWp, // void (CWnd*, CPoint) WM_CONTEXTMENU + AfxSig_bWww, // BOOL (CWnd*, UINT, UINT) WM_SETCURSOR + AfxSig_vbw, // void (BOOL, UINT) WM_SHOWWINDOW + AfxSig_vwl, // void (UINT, LPARAM) WM_SYSCOMMAND + AfxSig_vw_ptr, // void (UINT_PTR) WM_TIMER + AfxSig_vw_uint, // void (UINT) WM_TIMER, pre-VS2005 handlers + AfxSig_hv, // HCURSOR () WM_QUERYDRAGICON + AfxSig_bHELPINFO, // BOOL (HELPINFO*) WM_HELP + AfxSig_vPOS, // void (MINMAXINFO*) WM_GETMINMAXINFO + AfxSig_vwWb, // void (UINT, CWnd*, BOOL) WM_ACTIVATE + AfxSig_wv, // UINT () WM_GETDLGCODE + AfxSig_vHDROP, // void (HDROP) WM_DROPFILES + AfxSig_vMwb, // void (CMenu*, UINT, BOOL) WM_INITMENUPOPUP + AfxSig_vii, // void (int, int) WM_MOVE + AfxSig_vwW, // void (UINT, CWnd*) + AfxSig_vbWW, // void (BOOL, CWnd*, CWnd*) WM_MDIACTIVATE + AfxSig_bb, // BOOL (BOOL) WM_NCACTIVATE + AfxSig_vb, // void (BOOL) WM_ENABLE + AfxSig_vwwh, // void (UINT, UINT, HMENU) WM_MENUSELECT + AfxSig_vRECT, // void (UINT, LPRECT) WM_SIZING/MOVING + AfxSig_vv_reflect, // void () reflected control notifications + AfxSig_bv_reflect, // BOOL () ON_CONTROL_REFLECT_EX + AfxSig_vNMHDRpl, // void (NMHDR*, LRESULT*) ON_NOTIFY_REFLECT + AfxSig_bNMHDRpl, // BOOL (NMHDR*, LRESULT*) ON_NOTIFY_REFLECT_EX + AfxSig_vv_event, // OLE event sink, unused +}; + +#define CN_COMMAND 0 +#define CN_UPDATE_COMMAND_UI ((UINT)(-1)) +#define CN_EVENT ((UINT)(-2)) +#define CN_OLEEVENT ((UINT)(-3)) +#define WM_REFLECT_BASE 0xBC00 + +struct AFX_MSGMAP_ENTRY { + UINT nMessage; + UINT nCode; + UINT nID; + UINT nLastID; + UINT_PTR nSig; + AFX_PMSG pfn; +}; + +struct AFX_MSGMAP { + const AFX_MSGMAP* (*pfnGetBaseMap)(); + const AFX_MSGMAP_ENTRY* lpEntries; +}; + +struct AFX_CMDHANDLERINFO { + CCmdTarget* pTarget; + AFX_PMSG pmf; +}; + +// Old code declares OnTimer(UINT), newer code OnTimer(UINT_PTR); both are accepted. +template +constexpr UINT_PTR AfxSigTimer(void (C::*)(UINT_PTR)) { return AfxSig_vw_ptr; } +template ::value>::type> +constexpr UINT_PTR AfxSigTimer(void (C::*)(UINT)) { return AfxSig_vw_uint; } +template +AFX_PMSG AfxPmsgTimer(void (C::*f)(UINT_PTR)) { return (AFX_PMSG)(AFX_PMSGW)static_cast(f); } +template ::value>::type> +AFX_PMSG AfxPmsgTimer(void (C::*f)(UINT)) { return (AFX_PMSG)(AFX_PMSGW)(void (CWnd::*)(UINT))static_cast(f); } + +#define DECLARE_MESSAGE_MAP() \ +protected: \ + static const AFX_MSGMAP* GetThisMessageMap(); \ + const AFX_MSGMAP* GetMessageMap() const override; + +#define BEGIN_MESSAGE_MAP(theClass, baseClass) \ + const AFX_MSGMAP* theClass::GetMessageMap() const { return GetThisMessageMap(); } \ + const AFX_MSGMAP* theClass::GetThisMessageMap() { \ + typedef theClass ThisClass; \ + typedef baseClass TheBaseClass; \ + (void)sizeof(ThisClass*); \ + static const AFX_MSGMAP_ENTRY _messageEntries[] = { + +#define BEGIN_TEMPLATE_MESSAGE_MAP(theClass, type_name, baseClass) \ + template \ + const AFX_MSGMAP* theClass::GetMessageMap() const { return GetThisMessageMap(); } \ + template \ + const AFX_MSGMAP* theClass::GetThisMessageMap() { \ + typedef theClass ThisClass; \ + typedef baseClass TheBaseClass; \ + static const AFX_MSGMAP_ENTRY _messageEntries[] = { + +#define END_MESSAGE_MAP() \ + {0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0}}; \ + static const AFX_MSGMAP messageMap = {&TheBaseClass::GetThisMessageMap, &_messageEntries[0]}; \ + return &messageMap; \ + } + +#define AFX_MFN(sig, fn) ((AFX_PMSG)(static_cast(fn))) +#define AFX_MFNW(sig, fn) ((AFX_PMSG)(AFX_PMSGW)(static_cast(fn))) + +// command and notification handlers +#define ON_COMMAND(id, memberFxn) \ + {WM_COMMAND, CN_COMMAND, (UINT)(WORD)(id), (UINT)(WORD)(id), AfxSigCmd_v, AFX_MFN(void (CCmdTarget::*)(void), memberFxn)}, +#define ON_COMMAND_EX(id, memberFxn) \ + {WM_COMMAND, CN_COMMAND, (UINT)(WORD)(id), (UINT)(WORD)(id), AfxSigCmd_EX, AFX_MFN(BOOL (CCmdTarget::*)(UINT), memberFxn)}, +#define ON_COMMAND_RANGE(id, idLast, memberFxn) \ + {WM_COMMAND, CN_COMMAND, (UINT)(WORD)(id), (UINT)(WORD)(idLast), AfxSigCmd_RANGE, AFX_MFN(void (CCmdTarget::*)(UINT), memberFxn)}, +#define ON_COMMAND_EX_RANGE(id, idLast, memberFxn) \ + {WM_COMMAND, CN_COMMAND, (UINT)(WORD)(id), (UINT)(WORD)(idLast), AfxSigCmd_EX, AFX_MFN(BOOL (CCmdTarget::*)(UINT), memberFxn)}, +#define ON_UPDATE_COMMAND_UI(id, memberFxn) \ + {WM_COMMAND, CN_UPDATE_COMMAND_UI, (UINT)(WORD)(id), (UINT)(WORD)(id), AfxSigCmdUI, AFX_MFN(void (CCmdTarget::*)(CCmdUI*), memberFxn)}, +#define ON_UPDATE_COMMAND_UI_RANGE(id, idLast, memberFxn) \ + {WM_COMMAND, CN_UPDATE_COMMAND_UI, (UINT)(WORD)(id), (UINT)(WORD)(idLast), AfxSigCmdUI, AFX_MFN(void (CCmdTarget::*)(CCmdUI*), memberFxn)}, +#define ON_CONTROL(wNotifyCode, id, memberFxn) \ + {WM_COMMAND, (UINT)(WORD)(wNotifyCode), (UINT)(WORD)(id), (UINT)(WORD)(id), AfxSigCmd_v, AFX_MFN(void (CCmdTarget::*)(void), memberFxn)}, +#define ON_CONTROL_RANGE(wNotifyCode, id, idLast, memberFxn) \ + {WM_COMMAND, (UINT)(WORD)(wNotifyCode), (UINT)(WORD)(id), (UINT)(WORD)(idLast), AfxSigCmd_RANGE, AFX_MFN(void (CCmdTarget::*)(UINT), memberFxn)}, +#define ON_NOTIFY(wNotifyCode, id, memberFxn) \ + {WM_NOTIFY, (UINT)(wNotifyCode), (UINT)(WORD)(id), (UINT)(WORD)(id), AfxSigNotify_v, AFX_MFN(void (CCmdTarget::*)(NMHDR*, LRESULT*), memberFxn)}, +#define ON_NOTIFY_RANGE(wNotifyCode, id, idLast, memberFxn) \ + {WM_NOTIFY, (UINT)(wNotifyCode), (UINT)(WORD)(id), (UINT)(WORD)(idLast), AfxSigNotify_RANGE, AFX_MFN(void (CCmdTarget::*)(UINT, NMHDR*, LRESULT*), memberFxn)}, +#define ON_NOTIFY_EX(wNotifyCode, id, memberFxn) \ + {WM_NOTIFY, (UINT)(wNotifyCode), (UINT)(WORD)(id), (UINT)(WORD)(id), AfxSigNotify_EX, AFX_MFN(BOOL (CCmdTarget::*)(UINT, NMHDR*, LRESULT*), memberFxn)}, +#define ON_NOTIFY_EX_RANGE(wNotifyCode, id, idLast, memberFxn) \ + {WM_NOTIFY, (UINT)(wNotifyCode), (UINT)(WORD)(id), (UINT)(WORD)(idLast), AfxSigNotify_EX, AFX_MFN(BOOL (CCmdTarget::*)(UINT, NMHDR*, LRESULT*), memberFxn)}, + +#define ON_BN_CLICKED(id, memberFxn) ON_CONTROL(BN_CLICKED, id, memberFxn) +#define ON_BN_DOUBLECLICKED(id, memberFxn) ON_CONTROL(BN_DOUBLECLICKED, id, memberFxn) +#define ON_BN_SETFOCUS(id, memberFxn) ON_CONTROL(BN_SETFOCUS, id, memberFxn) +#define ON_BN_KILLFOCUS(id, memberFxn) ON_CONTROL(BN_KILLFOCUS, id, memberFxn) +#define ON_BN_HILITE(id, memberFxn) ON_CONTROL(BN_HILITE, id, memberFxn) +#define ON_BN_UNHILITE(id, memberFxn) ON_CONTROL(BN_UNHILITE, id, memberFxn) +#define ON_BN_PUSHED(id, memberFxn) ON_CONTROL(BN_PUSHED, id, memberFxn) +#define ON_BN_UNPUSHED(id, memberFxn) ON_CONTROL(BN_UNPUSHED, id, memberFxn) +#define ON_EN_SETFOCUS(id, memberFxn) ON_CONTROL(EN_SETFOCUS, id, memberFxn) +#define ON_EN_KILLFOCUS(id, memberFxn) ON_CONTROL(EN_KILLFOCUS, id, memberFxn) +#define ON_EN_CHANGE(id, memberFxn) ON_CONTROL(EN_CHANGE, id, memberFxn) +#define ON_EN_UPDATE(id, memberFxn) ON_CONTROL(EN_UPDATE, id, memberFxn) +#define ON_EN_ERRSPACE(id, memberFxn) ON_CONTROL(EN_ERRSPACE, id, memberFxn) +#define ON_EN_MAXTEXT(id, memberFxn) ON_CONTROL(EN_MAXTEXT, id, memberFxn) +#define ON_EN_HSCROLL(id, memberFxn) ON_CONTROL(EN_HSCROLL, id, memberFxn) +#define ON_EN_VSCROLL(id, memberFxn) ON_CONTROL(EN_VSCROLL, id, memberFxn) +#define ON_STN_CLICKED(id, memberFxn) ON_CONTROL(STN_CLICKED, id, memberFxn) +#define ON_STN_DBLCLK(id, memberFxn) ON_CONTROL(STN_DBLCLK, id, memberFxn) +#define ON_STN_ENABLE(id, memberFxn) ON_CONTROL(STN_ENABLE, id, memberFxn) +#define ON_STN_DISABLE(id, memberFxn) ON_CONTROL(STN_DISABLE, id, memberFxn) +#define ON_CBN_ERRSPACE(id, memberFxn) ON_CONTROL(CBN_ERRSPACE, id, memberFxn) +#define ON_CBN_SELCHANGE(id, memberFxn) ON_CONTROL(CBN_SELCHANGE, id, memberFxn) +#define ON_CBN_DBLCLK(id, memberFxn) ON_CONTROL(CBN_DBLCLK, id, memberFxn) +#define ON_CBN_SETFOCUS(id, memberFxn) ON_CONTROL(CBN_SETFOCUS, id, memberFxn) +#define ON_CBN_KILLFOCUS(id, memberFxn) ON_CONTROL(CBN_KILLFOCUS, id, memberFxn) +#define ON_CBN_EDITCHANGE(id, memberFxn) ON_CONTROL(CBN_EDITCHANGE, id, memberFxn) +#define ON_CBN_EDITUPDATE(id, memberFxn) ON_CONTROL(CBN_EDITUPDATE, id, memberFxn) +#define ON_CBN_DROPDOWN(id, memberFxn) ON_CONTROL(CBN_DROPDOWN, id, memberFxn) +#define ON_CBN_CLOSEUP(id, memberFxn) ON_CONTROL(CBN_CLOSEUP, id, memberFxn) +#define ON_CBN_SELENDOK(id, memberFxn) ON_CONTROL(CBN_SELENDOK, id, memberFxn) +#define ON_CBN_SELENDCANCEL(id, memberFxn) ON_CONTROL(CBN_SELENDCANCEL, id, memberFxn) +#define ON_LBN_ERRSPACE(id, memberFxn) ON_CONTROL(LBN_ERRSPACE, id, memberFxn) +#define ON_LBN_SELCHANGE(id, memberFxn) ON_CONTROL(LBN_SELCHANGE, id, memberFxn) +#define ON_LBN_DBLCLK(id, memberFxn) ON_CONTROL(LBN_DBLCLK, id, memberFxn) +#define ON_LBN_SELCANCEL(id, memberFxn) ON_CONTROL(LBN_SELCANCEL, id, memberFxn) +#define ON_LBN_SETFOCUS(id, memberFxn) ON_CONTROL(LBN_SETFOCUS, id, memberFxn) +#define ON_LBN_KILLFOCUS(id, memberFxn) ON_CONTROL(LBN_KILLFOCUS, id, memberFxn) + +// reflected notifications (handled by the control itself) +#define ON_CONTROL_REFLECT(wNotifyCode, memberFxn) \ + {WM_COMMAND + WM_REFLECT_BASE, (UINT)(WORD)(wNotifyCode), 0, 0, AfxSig_vv_reflect, AFX_MFN(void (CCmdTarget::*)(void), memberFxn)}, +#define ON_CONTROL_REFLECT_EX(wNotifyCode, memberFxn) \ + {WM_COMMAND + WM_REFLECT_BASE, (UINT)(WORD)(wNotifyCode), 0, 0, AfxSig_bv_reflect, AFX_MFN(BOOL (CCmdTarget::*)(void), memberFxn)}, +#define ON_NOTIFY_REFLECT(wNotifyCode, memberFxn) \ + {WM_NOTIFY + WM_REFLECT_BASE, (UINT)(wNotifyCode), 0, 0, AfxSig_vNMHDRpl, AFX_MFN(void (CCmdTarget::*)(NMHDR*, LRESULT*), memberFxn)}, +#define ON_NOTIFY_REFLECT_EX(wNotifyCode, memberFxn) \ + {WM_NOTIFY + WM_REFLECT_BASE, (UINT)(wNotifyCode), 0, 0, AfxSig_bNMHDRpl, AFX_MFN(BOOL (CCmdTarget::*)(NMHDR*, LRESULT*), memberFxn)}, +#define ON_EN_CHANGE_REFLECT(memberFxn) ON_CONTROL_REFLECT(EN_CHANGE, memberFxn) +#define ON_WM_CTLCOLOR_REFLECT() \ + {WM_CTLCOLOR + WM_REFLECT_BASE, 0, 0, 0, AfxSig_hDw, AFX_MFNW(HBRUSH (CWnd::*)(CDC*, UINT), &ThisClass::CtlColor)}, +#define ON_WM_DRAWITEM_REFLECT() \ + {WM_DRAWITEM + WM_REFLECT_BASE, 0, 0, 0, AfxSig_vOWNER, AFX_MFNW(void (CWnd::*)(LPDRAWITEMSTRUCT), &ThisClass::DrawItem)}, +#define ON_WM_HSCROLL_REFLECT() \ + {WM_HSCROLL + WM_REFLECT_BASE, 0, 0, 0, AfxSig_vwwW, AFX_MFNW(void (CWnd::*)(UINT, UINT), &ThisClass::HScroll)}, +#define ON_WM_VSCROLL_REFLECT() \ + {WM_VSCROLL + WM_REFLECT_BASE, 0, 0, 0, AfxSig_vwwW, AFX_MFNW(void (CWnd::*)(UINT, UINT), &ThisClass::VScroll)}, + +// generic messages +#define ON_MESSAGE(message, memberFxn) \ + {(UINT)(message), 0, 0, 0, AfxSig_lwl, AFX_MFNW(LRESULT (CWnd::*)(WPARAM, LPARAM), memberFxn)}, +#define ON_REGISTERED_MESSAGE(nMessageVariable, memberFxn) \ + {0xC000, 0, 0, 0, (UINT_PTR)(UINT*)(&nMessageVariable), AFX_MFNW(LRESULT (CWnd::*)(WPARAM, LPARAM), memberFxn)}, +#define ON_THREAD_MESSAGE(message, memberFxn) \ + {(UINT)(message), 0, 0, 0, AfxSig_lwl, (AFX_PMSG)(static_cast(memberFxn))}, +#define ON_EVENT(theClass, id, dispid, pfnHandler, vtsParams) + +// window messages with fixed handler names +#define ON_WM_CREATE() {WM_CREATE, 0, 0, 0, AfxSig_is, AFX_MFNW(int (CWnd::*)(LPCREATESTRUCT), &ThisClass::OnCreate)}, +#define ON_WM_DESTROY() {WM_DESTROY, 0, 0, 0, AfxSig_vv, AFX_MFNW(void (CWnd::*)(void), &ThisClass::OnDestroy)}, +#define ON_WM_NCDESTROY() {WM_NCDESTROY, 0, 0, 0, AfxSig_vv, AFX_MFNW(void (CWnd::*)(void), &ThisClass::OnNcDestroy)}, +#define ON_WM_CLOSE() {WM_CLOSE, 0, 0, 0, AfxSig_vv, AFX_MFNW(void (CWnd::*)(void), &ThisClass::OnClose)}, +#define ON_WM_PAINT() {WM_PAINT, 0, 0, 0, AfxSig_vv, AFX_MFNW(void (CWnd::*)(void), &ThisClass::OnPaint)}, +#define ON_WM_ERASEBKGND() {WM_ERASEBKGND, 0, 0, 0, AfxSig_bD, AFX_MFNW(BOOL (CWnd::*)(CDC*), &ThisClass::OnEraseBkgnd)}, +#define ON_WM_SIZE() {WM_SIZE, 0, 0, 0, AfxSig_vwii, AFX_MFNW(void (CWnd::*)(UINT, int, int), &ThisClass::OnSize)}, +#define ON_WM_MOVE() {WM_MOVE, 0, 0, 0, AfxSig_vii, AFX_MFNW(void (CWnd::*)(int, int), &ThisClass::OnMove)}, +#define ON_WM_TIMER() {WM_TIMER, 0, 0, 0, AfxSigTimer(&ThisClass::OnTimer), AfxPmsgTimer(&ThisClass::OnTimer)}, +#define ON_WM_KEYDOWN() {WM_KEYDOWN, 0, 0, 0, AfxSig_vwwx, AFX_MFNW(void (CWnd::*)(UINT, UINT, UINT), &ThisClass::OnKeyDown)}, +#define ON_WM_KEYUP() {WM_KEYUP, 0, 0, 0, AfxSig_vwwx, AFX_MFNW(void (CWnd::*)(UINT, UINT, UINT), &ThisClass::OnKeyUp)}, +#define ON_WM_CHAR() {WM_CHAR, 0, 0, 0, AfxSig_vwwx, AFX_MFNW(void (CWnd::*)(UINT, UINT, UINT), &ThisClass::OnChar)}, +#define ON_WM_SYSKEYDOWN() {WM_SYSKEYDOWN, 0, 0, 0, AfxSig_vwwx, AFX_MFNW(void (CWnd::*)(UINT, UINT, UINT), &ThisClass::OnSysKeyDown)}, +#define ON_WM_SYSKEYUP() {WM_SYSKEYUP, 0, 0, 0, AfxSig_vwwx, AFX_MFNW(void (CWnd::*)(UINT, UINT, UINT), &ThisClass::OnSysKeyUp)}, +#define ON_WM_SYSCHAR() {WM_SYSCHAR, 0, 0, 0, AfxSig_vwwx, AFX_MFNW(void (CWnd::*)(UINT, UINT, UINT), &ThisClass::OnSysChar)}, +#define ON_WM_MOUSEMOVE() {WM_MOUSEMOVE, 0, 0, 0, AfxSig_vwp, AFX_MFNW(void (CWnd::*)(UINT, CPoint), &ThisClass::OnMouseMove)}, +#define ON_WM_LBUTTONDOWN() {WM_LBUTTONDOWN, 0, 0, 0, AfxSig_vwp, AFX_MFNW(void (CWnd::*)(UINT, CPoint), &ThisClass::OnLButtonDown)}, +#define ON_WM_LBUTTONUP() {WM_LBUTTONUP, 0, 0, 0, AfxSig_vwp, AFX_MFNW(void (CWnd::*)(UINT, CPoint), &ThisClass::OnLButtonUp)}, +#define ON_WM_LBUTTONDBLCLK() {WM_LBUTTONDBLCLK, 0, 0, 0, AfxSig_vwp, AFX_MFNW(void (CWnd::*)(UINT, CPoint), &ThisClass::OnLButtonDblClk)}, +#define ON_WM_RBUTTONDOWN() {WM_RBUTTONDOWN, 0, 0, 0, AfxSig_vwp, AFX_MFNW(void (CWnd::*)(UINT, CPoint), &ThisClass::OnRButtonDown)}, +#define ON_WM_RBUTTONUP() {WM_RBUTTONUP, 0, 0, 0, AfxSig_vwp, AFX_MFNW(void (CWnd::*)(UINT, CPoint), &ThisClass::OnRButtonUp)}, +#define ON_WM_RBUTTONDBLCLK() {WM_RBUTTONDBLCLK, 0, 0, 0, AfxSig_vwp, AFX_MFNW(void (CWnd::*)(UINT, CPoint), &ThisClass::OnRButtonDblClk)}, +#define ON_WM_MBUTTONDOWN() {WM_MBUTTONDOWN, 0, 0, 0, AfxSig_vwp, AFX_MFNW(void (CWnd::*)(UINT, CPoint), &ThisClass::OnMButtonDown)}, +#define ON_WM_MBUTTONUP() {WM_MBUTTONUP, 0, 0, 0, AfxSig_vwp, AFX_MFNW(void (CWnd::*)(UINT, CPoint), &ThisClass::OnMButtonUp)}, +#define ON_WM_MOUSEWHEEL() {WM_MOUSEWHEEL, 0, 0, 0, AfxSig_bwsp, AFX_MFNW(BOOL (CWnd::*)(UINT, short, CPoint), &ThisClass::OnMouseWheel)}, +#define ON_WM_HSCROLL() {WM_HSCROLL, 0, 0, 0, AfxSig_vwwW, AFX_MFNW(void (CWnd::*)(UINT, UINT, CScrollBar*), &ThisClass::OnHScroll)}, +#define ON_WM_VSCROLL() {WM_VSCROLL, 0, 0, 0, AfxSig_vwwW, AFX_MFNW(void (CWnd::*)(UINT, UINT, CScrollBar*), &ThisClass::OnVScroll)}, +#define ON_WM_CTLCOLOR() {WM_CTLCOLOR, 0, 0, 0, AfxSig_hDWw, AFX_MFNW(HBRUSH (CWnd::*)(CDC*, CWnd*, UINT), &ThisClass::OnCtlColor)}, +#define ON_WM_DRAWITEM() {WM_DRAWITEM, 0, 0, 0, AfxSig_vOWNER, AFX_MFNW(void (CWnd::*)(int, LPDRAWITEMSTRUCT), &ThisClass::OnDrawItem)}, +#define ON_WM_MEASUREITEM() {WM_MEASUREITEM, 0, 0, 0, AfxSig_vMEASURE, AFX_MFNW(void (CWnd::*)(int, LPMEASUREITEMSTRUCT), &ThisClass::OnMeasureItem)}, +#define ON_WM_SETFOCUS() {WM_SETFOCUS, 0, 0, 0, AfxSig_vW, AFX_MFNW(void (CWnd::*)(CWnd*), &ThisClass::OnSetFocus)}, +#define ON_WM_KILLFOCUS() {WM_KILLFOCUS, 0, 0, 0, AfxSig_vW, AFX_MFNW(void (CWnd::*)(CWnd*), &ThisClass::OnKillFocus)}, +#define ON_WM_CONTEXTMENU() {WM_CONTEXTMENU, 0, 0, 0, AfxSig_vWp, AFX_MFNW(void (CWnd::*)(CWnd*, CPoint), &ThisClass::OnContextMenu)}, +#define ON_WM_SETCURSOR() {WM_SETCURSOR, 0, 0, 0, AfxSig_bWww, AFX_MFNW(BOOL (CWnd::*)(CWnd*, UINT, UINT), &ThisClass::OnSetCursor)}, +#define ON_WM_SHOWWINDOW() {WM_SHOWWINDOW, 0, 0, 0, AfxSig_vbw, AFX_MFNW(void (CWnd::*)(BOOL, UINT), &ThisClass::OnShowWindow)}, +#define ON_WM_SYSCOMMAND() {WM_SYSCOMMAND, 0, 0, 0, AfxSig_vwl, AFX_MFNW(void (CWnd::*)(UINT, LPARAM), &ThisClass::OnSysCommand)}, +#define ON_WM_QUERYDRAGICON() {WM_QUERYDRAGICON, 0, 0, 0, AfxSig_hv, AFX_MFNW(HCURSOR (CWnd::*)(void), &ThisClass::OnQueryDragIcon)}, +#define ON_WM_HELPINFO() {WM_HELP, 0, 0, 0, AfxSig_bHELPINFO, AFX_MFNW(BOOL (CWnd::*)(HELPINFO*), &ThisClass::OnHelpInfo)}, +#define ON_WM_GETMINMAXINFO() {WM_GETMINMAXINFO, 0, 0, 0, AfxSig_vPOS, AFX_MFNW(void (CWnd::*)(MINMAXINFO*), &ThisClass::OnGetMinMaxInfo)}, +#define ON_WM_ACTIVATE() {WM_ACTIVATE, 0, 0, 0, AfxSig_vwWb, AFX_MFNW(void (CWnd::*)(UINT, CWnd*, BOOL), &ThisClass::OnActivate)}, +#define ON_WM_GETDLGCODE() {WM_GETDLGCODE, 0, 0, 0, AfxSig_wv, AFX_MFNW(UINT (CWnd::*)(void), &ThisClass::OnGetDlgCode)}, +#define ON_WM_DROPFILES() {WM_DROPFILES, 0, 0, 0, AfxSig_vHDROP, AFX_MFNW(void (CWnd::*)(HDROP), &ThisClass::OnDropFiles)}, +#define ON_WM_INITMENUPOPUP() {WM_INITMENUPOPUP, 0, 0, 0, AfxSig_vMwb, AFX_MFNW(void (CWnd::*)(CMenu*, UINT, BOOL), &ThisClass::OnInitMenuPopup)}, +#define ON_WM_MDIACTIVATE() {WM_MDIACTIVATE, 0, 0, 0, AfxSig_vbWW, AFX_MFNW(void (CWnd::*)(BOOL, CWnd*, CWnd*), &ThisClass::OnMDIActivate)}, +#define ON_WM_ENABLE() {WM_ENABLE, 0, 0, 0, AfxSig_vb, AFX_MFNW(void (CWnd::*)(BOOL), &ThisClass::OnEnable)}, +#define ON_WM_NCACTIVATE() {WM_NCACTIVATE, 0, 0, 0, AfxSig_bb, AFX_MFNW(BOOL (CWnd::*)(BOOL), &ThisClass::OnNcActivate)}, +#define ON_WM_MENUSELECT() {WM_MENUSELECT, 0, 0, 0, AfxSig_vwwh, AFX_MFNW(void (CWnd::*)(UINT, UINT, HMENU), &ThisClass::OnMenuSelect)}, +#define ON_WM_SIZING() {WM_SIZING, 0, 0, 0, AfxSig_vRECT, AFX_MFNW(void (CWnd::*)(UINT, LPRECT), &ThisClass::OnSizing)}, +#define ON_WM_NCLBUTTONDOWN() {WM_NCLBUTTONDOWN, 0, 0, 0, AfxSig_vwp, AFX_MFNW(void (CWnd::*)(UINT, CPoint), &ThisClass::OnNcLButtonDown)}, +#define ON_WM_NCHITTEST() +#define ON_WM_NCPAINT() +#define ON_WM_NCCALCSIZE() +#define ON_WM_WINDOWPOSCHANGED() +#define ON_WM_WINDOWPOSCHANGING() +#define ON_WM_SETTINGCHANGE() +#define ON_WM_SYSCOLORCHANGE() +#define ON_WM_PALETTECHANGED() +#define ON_WM_QUERYNEWPALETTE() +#define ON_WM_CAPTURECHANGED() +#define ON_WM_MOUSEACTIVATE() +#define ON_WM_ACTIVATEAPP() +#define ON_WM_ENTERIDLE() diff --git a/port/mfcwx/include/mfcwx/rcdata.h b/port/mfcwx/include/mfcwx/rcdata.h new file mode 100644 index 000000000..bcbb1f1f0 --- /dev/null +++ b/port/mfcwx/include/mfcwx/rcdata.h @@ -0,0 +1,130 @@ +#pragma once + +#include + +// Resource tables generated from .rc files by port/rc/rc2cpp.py. +// All strings are UTF-8. A resource is identified either by a numeric id (name == nullptr) +// or by a string name (id == 0). +namespace mfcwx { +namespace rc { + +struct Control { + const char* cls; // window class as written in the .rc ("Button", "Edit", "Static", "ComboBox", + // "ListBox", "ScrollBar", "msctls_progress32", "msctls_trackbar32", + // "msctls_updown32", "SysListView32", "SysTreeView32", "SysTabControl32", + // "RICHEDIT", "RichEdit20A", "SysDateTimePick32", "SysAnimate32", ...) + const char* text; // caption, or resource name for SS_BITMAP/SS_ICON/BS_BITMAP ("#123" for numeric ids) + int id; // control id, -1 for IDC_STATIC + int16_t x, y, cx, cy; // dialog units + uint32_t style; // full style including the implied bits of the statement keyword + uint32_t exStyle; +}; + +struct Dialog { + int id; + const char* name; + int16_t x, y, cx, cy; + uint32_t style; + uint32_t exStyle; + const char* caption; + const char* fontName; // nullptr if DS_SETFONT not given + int16_t fontSize; + int16_t fontWeight; + uint8_t fontItalic; + const char* menuName; // nullptr if none + int menuId; + const Control* controls; + int controlCount; +}; + +struct MenuItem { + int16_t depth; // 0 = top level of the menu resource + int id; // command id, 0 for popups and separators + uint32_t flags; // MF_POPUP, MF_SEPARATOR, MF_GRAYED, MF_DISABLED, MF_CHECKED, MF_MENUBARBREAK, MF_MENUBREAK, MF_HELP + const char* text; // with '&' mnemonics and "\t" accelerator hints as in the .rc +}; + +struct Menu { + int id; + const char* name; + const MenuItem* items; + int itemCount; +}; + +struct StringEntry { + int id; + const char* text; +}; + +struct Accel { + int key; // virtual key code if (flags & FVIRTKEY), else the character code + int id; + uint32_t flags; // FVIRTKEY | FSHIFT | FCONTROL | FALT | FNOINVERT +}; + +struct AccelTable { + int id; + const char* name; + const Accel* items; + int itemCount; +}; + +struct Toolbar { + int id; + const char* name; + int16_t buttonWidth, buttonHeight; + const int* buttons; // command ids, 0 = separator + int buttonCount; +}; + +// BITMAP, ICON, CURSOR, GIF, AVI, HTML, WAVE and other file-backed resources. +struct FileResource { + const char* type; // "BITMAP", "ICON", "CURSOR", or the custom type name as in the .rc + int id; + const char* name; + const char* path; // relative to the directory of the .rc file, with '/' separators +}; + +// DLGINIT: initial list entries of combo boxes. +struct DlgInitEntry { + int dialogId; + int controlId; + const char* text; +}; + +struct VersionInfo { + uint16_t fileVersion[4]; + uint16_t productVersion[4]; + const char* const* keys; // alternating key/value pairs from the first StringFileInfo block + int keyCount; // number of pairs +}; + +struct Language { + const char* code; // "de", "en", "fr", "es", "pl", "el", "rs", ... + int langId; // MAKELANGID(primary, sub) + int codepage; // code page of the original .rc section + const Dialog* dialogs; + int dialogCount; + const Menu* menus; + int menuCount; + const StringEntry* strings; // sorted by id + int stringCount; + const AccelTable* accels; + int accelCount; + const Toolbar* toolbars; + int toolbarCount; + const FileResource* files; + int fileCount; + const DlgInitEntry* dlgInits; + int dlgInitCount; + const VersionInfo* version; // nullptr if none +}; + +struct Module { + const char* name; // e.g. "CrypTool" + const Language* const* languages; + int languageCount; +}; + +} // namespace rc +} // namespace mfcwx diff --git a/port/mfcwx/include/mfcwx/regkey.h b/port/mfcwx/include/mfcwx/regkey.h new file mode 100644 index 000000000..80c85f1bc --- /dev/null +++ b/port/mfcwx/include/mfcwx/regkey.h @@ -0,0 +1,44 @@ +#pragma once + +// ATL CRegKey, backed by the registry emulation of mfcwx (wxConfig). +class CRegKey { +public: + CRegKey() : m_hKey(nullptr) {} + ~CRegKey() { Close(); } + operator HKEY() const { return m_hKey; } + LONG Create(HKEY hKeyParent, const char* lpszKeyName, LPSTR lpszClass = nullptr, + DWORD dwOptions = REG_OPTION_NON_VOLATILE, REGSAM samDesired = KEY_READ | KEY_WRITE, + LPSECURITY_ATTRIBUTES lpSecAttr = nullptr, LPDWORD lpdwDisposition = nullptr); + LONG Open(HKEY hKeyParent, const char* lpszKeyName, REGSAM samDesired = KEY_READ | KEY_WRITE); + LONG Close(); + LONG QueryDWORDValue(const char* pszValueName, DWORD& dwValue); + LONG QueryStringValue(const char* pszValueName, LPSTR pszValue, ULONG* pnChars); + LONG QueryValue(DWORD& dwValue, const char* lpszValueName) { return QueryDWORDValue(lpszValueName, dwValue); } + LONG QueryValue(unsigned long& dwValue, const char* lpszValueName) { + DWORD v = 0; + LONG r = QueryDWORDValue(lpszValueName, v); + if (r == ERROR_SUCCESS) + dwValue = v; + return r; + } + LONG QueryValue(LPSTR szValue, const char* lpszValueName, unsigned long* pdwCount) { + DWORD n = pdwCount ? static_cast(*pdwCount) : 0; + LONG r = QueryValue(szValue, lpszValueName, &n); + if (pdwCount) + *pdwCount = n; + return r; + } + LONG QueryValue(LPSTR szValue, const char* lpszValueName, DWORD* pdwCount); + LONG QueryBinaryValue(const char* pszValueName, void* pValue, ULONG* pnBytes); + LONG SetDWORDValue(const char* pszValueName, DWORD dwValue); + LONG SetStringValue(const char* pszValueName, const char* pszValue, DWORD dwType = REG_SZ); + LONG SetValue(DWORD dwValue, const char* lpszValueName) { return SetDWORDValue(lpszValueName, dwValue); } + LONG SetValue(unsigned long dwValue, const char* lpszValueName) { return SetDWORDValue(lpszValueName, static_cast(dwValue)); } + LONG SetValue(int dwValue, const char* lpszValueName) { return SetDWORDValue(lpszValueName, static_cast(dwValue)); } + LONG SetValue(const char* lpszValue, const char* lpszValueName = nullptr) { return SetStringValue(lpszValueName, lpszValue); } + LONG SetBinaryValue(const char* pszValueName, const void* pValue, ULONG nBytes); + LONG DeleteValue(const char* lpszValue); + LONG DeleteSubKey(const char* lpszSubKey); + LONG RecurseDeleteKey(const char* lpszKey) { return DeleteSubKey(lpszKey); } + HKEY m_hKey; +}; diff --git a/port/mfcwx/include/mfcwx/win32.h b/port/mfcwx/include/mfcwx/win32.h new file mode 100644 index 000000000..33f863a9c --- /dev/null +++ b/port/mfcwx/include/mfcwx/win32.h @@ -0,0 +1,325 @@ +#pragma once + +/* Win32 API subset implemented on top of wxWidgets / POSIX. C compatible declarations; + functions taking or returning C++ types live in the afx headers. */ + +#include "mfcwx/wintypes.h" +#include "mfcwx/winconst.h" +#include "mfcwx/crtcompat.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define MAKEWORD(a, b) ((WORD)(((BYTE)((DWORD_PTR)(a) & 0xff)) | ((WORD)((BYTE)((DWORD_PTR)(b) & 0xff))) << 8)) +#define MAKELONG(a, b) ((LONG)(((WORD)((DWORD_PTR)(a) & 0xffff)) | ((DWORD)((WORD)((DWORD_PTR)(b) & 0xffff))) << 16)) +#define LOWORD(l) ((WORD)((DWORD_PTR)(l) & 0xffff)) +#define HIWORD(l) ((WORD)((DWORD_PTR)(l) >> 16)) +#define LOBYTE(w) ((BYTE)((DWORD_PTR)(w) & 0xff)) +#define HIBYTE(w) ((BYTE)((DWORD_PTR)(w) >> 8)) +#define MAKEWPARAM(l, h) ((WPARAM)(DWORD)MAKELONG(l, h)) +#define MAKELPARAM(l, h) ((LPARAM)(DWORD)MAKELONG(l, h)) +#define MAKELRESULT(l, h) ((LRESULT)(DWORD)MAKELONG(l, h)) +#define GET_X_LPARAM(lp) ((int)(short)LOWORD(lp)) +#define GET_Y_LPARAM(lp) ((int)(short)HIWORD(lp)) +#define GET_WHEEL_DELTA_WPARAM(wParam) ((short)HIWORD(wParam)) +#define RGB(r, g, b) ((COLORREF)(((BYTE)(r) | ((WORD)((BYTE)(g)) << 8)) | (((DWORD)(BYTE)(b)) << 16))) +#define PALETTERGB(r, g, b) (0x02000000 | RGB(r, g, b)) +#define GetRValue(rgb) (LOBYTE(rgb)) +#define GetGValue(rgb) (LOBYTE(((WORD)(rgb)) >> 8)) +#define GetBValue(rgb) (LOBYTE((rgb) >> 16)) +#define MAKEINTRESOURCEA(i) ((LPSTR)((ULONG_PTR)((WORD)(i)))) +#define MAKEINTRESOURCE(i) MAKEINTRESOURCEA(i) +#define IS_INTRESOURCE(r) ((((ULONG_PTR)(r)) >> 16) == 0) +#define MAKELANGID(p, s) ((((WORD)(s)) << 10) | (WORD)(p)) +#define PRIMARYLANGID(lgid) ((WORD)(lgid) & 0x3ff) +#define SUBLANGID(lgid) ((WORD)(lgid) >> 10) +#define MAKELCID(lgid, srtid) ((DWORD)((((DWORD)((WORD)(srtid))) << 16) | ((DWORD)((WORD)(lgid))))) +#define LANGIDFROMLCID(lcid) ((WORD)(lcid)) +#define SUCCEEDED(hr) (((HRESULT)(hr)) >= 0) +#define FAILED(hr) (((HRESULT)(hr)) < 0) +#define S_OK ((HRESULT)0L) +#define S_FALSE ((HRESULT)1L) +#define E_FAIL ((HRESULT)0x80004005L) +#define E_NOTIMPL ((HRESULT)0x80004001L) +#define E_OUTOFMEMORY ((HRESULT)0x8007000EL) +#define E_INVALIDARG ((HRESULT)0x80070057L) +#define UNREFERENCED_PARAMETER(P) (void)(P) +#define DBG_UNREFERENCED_PARAMETER(P) (void)(P) +#define DBG_UNREFERENCED_LOCAL_VARIABLE(V) (void)(V) +#define ZeroMemory(d, l) memset((d), 0, (l)) +#define SecureZeroMemory(d, l) memset((d), 0, (l)) +#define FillMemory(d, l, f) memset((d), (f), (l)) +#define CopyMemory(d, s, l) memcpy((d), (s), (l)) +#define MoveMemory(d, s, l) memmove((d), (s), (l)) +#define RtlZeroMemory ZeroMemory +#define RtlCopyMemory CopyMemory +#define RtlMoveMemory MoveMemory +#define RtlFillMemory FillMemory +#define _countof(a) (sizeof(a) / sizeof((a)[0])) +#define ARRAYSIZE(a) _countof(a) +#undef _T +#undef _TEXT +#undef TEXT +#define TEXT(s) s +#define __TEXT(s) s +#define _T(s) s +#define _TEXT(s) s +#define MAKEPOINTS(l) (*((POINTS*)&(l))) + +/* threads / timing / process */ +DWORD GetTickCount(void); +ULONGLONG GetTickCount64(void); +void Sleep(DWORD ms); +DWORD GetLastError(void); +void SetLastError(DWORD err); +DWORD GetCurrentThreadId(void); +DWORD GetCurrentProcessId(void); +HANDLE GetCurrentProcess(void); +HANDLE GetCurrentThread(void); +BOOL QueryPerformanceCounter(LARGE_INTEGER* c); +BOOL QueryPerformanceFrequency(LARGE_INTEGER* f); +void GetSystemTime(LPSYSTEMTIME st); +void GetLocalTime(LPSYSTEMTIME st); +void GetSystemTimeAsFileTime(LPFILETIME ft); +BOOL SystemTimeToFileTime(const SYSTEMTIME* st, LPFILETIME ft); +BOOL FileTimeToSystemTime(const FILETIME* ft, LPSYSTEMTIME st); +BOOL FileTimeToLocalFileTime(const FILETIME* ft, LPFILETIME lft); +void GetSystemInfo(LPSYSTEM_INFO si); +BOOL GetVersionEx(LPOSVERSIONINFO vi); +DWORD GetVersion(void); +void GlobalMemoryStatus(LPMEMORYSTATUS ms); +BOOL SetThreadPriority(HANDLE thread, int priority); +int GetThreadPriority(HANDLE thread); +BOOL SetPriorityClass(HANDLE process, DWORD cls); +DWORD WaitForSingleObject(HANDLE h, DWORD ms); +DWORD WaitForMultipleObjects(DWORD count, const HANDLE* handles, BOOL waitAll, DWORD ms); +BOOL CloseHandle(HANDLE h); +BOOL GetExitCodeThread(HANDLE thread, LPDWORD code); +BOOL GetExitCodeProcess(HANDLE process, LPDWORD code); +BOOL TerminateThread(HANDLE thread, DWORD code); +BOOL TerminateProcess(HANDLE process, UINT code); +HANDLE CreateThread(LPSECURITY_ATTRIBUTES sa, SIZE_T stack, LPTHREAD_START_ROUTINE fn, LPVOID param, DWORD flags, + LPDWORD threadId); +DWORD ResumeThread(HANDLE thread); +DWORD SuspendThread(HANDLE thread); +void ExitThread(DWORD code); +HANDLE CreateEventA(LPSECURITY_ATTRIBUTES sa, BOOL manualReset, BOOL initialState, LPCSTR name); +#define CreateEvent CreateEventA +BOOL SetEvent(HANDLE h); +BOOL ResetEvent(HANDLE h); +BOOL PulseEvent(HANDLE h); +HANDLE CreateMutexA(LPSECURITY_ATTRIBUTES sa, BOOL initialOwner, LPCSTR name); +#define CreateMutex CreateMutexA +BOOL ReleaseMutex(HANDLE h); +HANDLE CreateSemaphoreA(LPSECURITY_ATTRIBUTES sa, LONG initial, LONG max, LPCSTR name); +#define CreateSemaphore CreateSemaphoreA +BOOL ReleaseSemaphore(HANDLE h, LONG count, LPLONG previous); +void InitializeCriticalSection(LPCRITICAL_SECTION cs); +void DeleteCriticalSection(LPCRITICAL_SECTION cs); +void EnterCriticalSection(LPCRITICAL_SECTION cs); +void LeaveCriticalSection(LPCRITICAL_SECTION cs); +BOOL TryEnterCriticalSection(LPCRITICAL_SECTION cs); +LONG InterlockedIncrement(LONG volatile* p); +LONG InterlockedDecrement(LONG volatile* p); +LONG InterlockedExchange(LONG volatile* p, LONG v); +LONG InterlockedExchangeAdd(LONG volatile* p, LONG v); +LONG InterlockedCompareExchange(LONG volatile* p, LONG exchange, LONG comparand); +BOOL CreateProcessA(LPCSTR app, LPSTR cmdLine, LPSECURITY_ATTRIBUTES pa, LPSECURITY_ATTRIBUTES ta, BOOL inherit, + DWORD flags, LPVOID env, LPCSTR curDir, LPSTARTUPINFO si, LPPROCESS_INFORMATION pi); +#define CreateProcess CreateProcessA +UINT WinExec(LPCSTR cmdLine, UINT show); +HINSTANCE ShellExecuteA(HWND hwnd, LPCSTR op, LPCSTR file, LPCSTR params, LPCSTR dir, int show); +#define ShellExecute ShellExecuteA +void ExitProcess(UINT code); +DWORD FormatMessageA(DWORD flags, LPCVOID source, DWORD msgId, DWORD langId, LPSTR buffer, DWORD size, va_list* args); +#define FormatMessage FormatMessageA +HLOCAL LocalAlloc(UINT flags, SIZE_T bytes); +HLOCAL LocalFree(HLOCAL mem); +HGLOBAL GlobalAlloc(UINT flags, SIZE_T bytes); +HGLOBAL GlobalFree(HGLOBAL mem); +LPVOID GlobalLock(HGLOBAL mem); +BOOL GlobalUnlock(HGLOBAL mem); +SIZE_T GlobalSize(HGLOBAL mem); +HGLOBAL GlobalReAlloc(HGLOBAL mem, SIZE_T bytes, UINT flags); +DWORD GetEnvironmentVariableA(LPCSTR name, LPSTR buffer, DWORD size); +#define GetEnvironmentVariable GetEnvironmentVariableA +BOOL SetEnvironmentVariableA(LPCSTR name, LPCSTR value); +#define SetEnvironmentVariable SetEnvironmentVariableA +DWORD ExpandEnvironmentStringsA(LPCSTR src, LPSTR dst, DWORD size); +#define ExpandEnvironmentStrings ExpandEnvironmentStringsA +LPSTR GetCommandLineA(void); +#define GetCommandLine GetCommandLineA +UINT SetErrorMode(UINT mode); +void OutputDebugStringA(LPCSTR s); +#define OutputDebugString OutputDebugStringA +void DebugBreak(void); +BOOL Beep(DWORD freq, DWORD duration); +BOOL MessageBeep(UINT type); +int MulDiv(int number, int numerator, int denominator); +BOOL IsBadReadPtr(const void* p, UINT_PTR cb); +BOOL IsBadWritePtr(LPVOID p, UINT_PTR cb); +DWORD GetUserDefaultLangID(void); +LCID GetUserDefaultLCID(void); +LCID GetSystemDefaultLCID(void); +LCID GetThreadLocale(void); +BOOL SetThreadLocale(LCID lcid); +UINT GetACP(void); +UINT GetOEMCP(void); +int MultiByteToWideChar(UINT cp, DWORD flags, LPCSTR src, int srcLen, LPWSTR dst, int dstLen); +int WideCharToMultiByte(UINT cp, DWORD flags, LPCWSTR src, int srcLen, LPSTR dst, int dstLen, LPCSTR defChar, + LPBOOL usedDef); +BOOL GetUserNameA(LPSTR buffer, LPDWORD size); +#define GetUserName GetUserNameA +BOOL GetComputerNameA(LPSTR buffer, LPDWORD size); +#define GetComputerName GetComputerNameA + +/* modules */ +HMODULE LoadLibraryA(LPCSTR name); +#define LoadLibrary LoadLibraryA +HMODULE LoadLibraryExA(LPCSTR name, HANDLE file, DWORD flags); +#define LoadLibraryEx LoadLibraryExA +BOOL FreeLibrary(HMODULE mod); +FARPROC GetProcAddress(HMODULE mod, LPCSTR name); +HMODULE GetModuleHandleA(LPCSTR name); +#define GetModuleHandle GetModuleHandleA +DWORD GetModuleFileNameA(HMODULE mod, LPSTR buffer, DWORD size); +#define GetModuleFileName GetModuleFileNameA + +/* files and directories */ +HANDLE CreateFileA(LPCSTR name, DWORD access, DWORD share, LPSECURITY_ATTRIBUTES sa, DWORD disposition, DWORD flags, + HANDLE templ); +#define CreateFile CreateFileA +BOOL ReadFile(HANDLE f, LPVOID buffer, DWORD toRead, LPDWORD read, LPOVERLAPPED ov); +BOOL WriteFile(HANDLE f, LPCVOID buffer, DWORD toWrite, LPDWORD written, LPOVERLAPPED ov); +DWORD SetFilePointer(HANDLE f, LONG distance, PLONG distanceHigh, DWORD method); +BOOL SetEndOfFile(HANDLE f); +DWORD GetFileSize(HANDLE f, LPDWORD high); +BOOL GetFileSizeEx(HANDLE f, PLARGE_INTEGER size); +BOOL FlushFileBuffers(HANDLE f); +BOOL GetFileTime(HANDLE f, LPFILETIME creation, LPFILETIME access, LPFILETIME write); +BOOL DeleteFileA(LPCSTR name); +#define DeleteFile DeleteFileA +BOOL CopyFileA(LPCSTR from, LPCSTR to, BOOL failIfExists); +#define CopyFile CopyFileA +BOOL MoveFileA(LPCSTR from, LPCSTR to); +#define MoveFile MoveFileA +BOOL MoveFileExA(LPCSTR from, LPCSTR to, DWORD flags); +#define MoveFileEx MoveFileExA +#define MOVEFILE_REPLACE_EXISTING 0x00000001 +#define MOVEFILE_COPY_ALLOWED 0x00000002 +BOOL CreateDirectoryA(LPCSTR path, LPSECURITY_ATTRIBUTES sa); +#define CreateDirectory CreateDirectoryA +BOOL RemoveDirectoryA(LPCSTR path); +#define RemoveDirectory RemoveDirectoryA +BOOL SetCurrentDirectoryA(LPCSTR path); +#define SetCurrentDirectory SetCurrentDirectoryA +DWORD GetCurrentDirectoryA(DWORD size, LPSTR buffer); +#define GetCurrentDirectory GetCurrentDirectoryA +DWORD GetTempPathA(DWORD size, LPSTR buffer); +#define GetTempPath GetTempPathA +UINT GetTempFileNameA(LPCSTR path, LPCSTR prefix, UINT unique, LPSTR buffer); +#define GetTempFileName GetTempFileNameA +DWORD GetFileAttributesA(LPCSTR name); +#define GetFileAttributes GetFileAttributesA +BOOL SetFileAttributesA(LPCSTR name, DWORD attrs); +#define SetFileAttributes SetFileAttributesA +DWORD GetFullPathNameA(LPCSTR name, DWORD size, LPSTR buffer, LPSTR* filePart); +#define GetFullPathName GetFullPathNameA +DWORD GetShortPathNameA(LPCSTR longPath, LPSTR shortPath, DWORD size); +#define GetShortPathName GetShortPathNameA +DWORD GetLongPathNameA(LPCSTR shortPath, LPSTR longPath, DWORD size); +#define GetLongPathName GetLongPathNameA +HANDLE FindFirstFileA(LPCSTR pattern, LPWIN32_FIND_DATA data); +#define FindFirstFile FindFirstFileA +BOOL FindNextFileA(HANDLE find, LPWIN32_FIND_DATA data); +#define FindNextFile FindNextFileA +BOOL FindClose(HANDLE find); +UINT GetWindowsDirectoryA(LPSTR buffer, UINT size); +#define GetWindowsDirectory GetWindowsDirectoryA +UINT GetSystemDirectoryA(LPSTR buffer, UINT size); +#define GetSystemDirectory GetSystemDirectoryA +UINT GetDriveTypeA(LPCSTR root); +#define GetDriveType GetDriveTypeA +BOOL GetDiskFreeSpaceExA(LPCSTR dir, PULARGE_INTEGER freeToCaller, PULARGE_INTEGER total, PULARGE_INTEGER totalFree); +#define GetDiskFreeSpaceEx GetDiskFreeSpaceExA + +/* ini files, mapped onto simple INI parsing */ +DWORD GetPrivateProfileStringA(LPCSTR app, LPCSTR key, LPCSTR def, LPSTR ret, DWORD size, LPCSTR file); +#define GetPrivateProfileString GetPrivateProfileStringA +UINT GetPrivateProfileIntA(LPCSTR app, LPCSTR key, INT def, LPCSTR file); +#define GetPrivateProfileInt GetPrivateProfileIntA +BOOL WritePrivateProfileStringA(LPCSTR app, LPCSTR key, LPCSTR value, LPCSTR file); +#define WritePrivateProfileString WritePrivateProfileStringA +DWORD GetPrivateProfileSectionA(LPCSTR app, LPSTR ret, DWORD size, LPCSTR file); +#define GetPrivateProfileSection GetPrivateProfileSectionA + +/* registry, mapped onto wxConfig */ +LONG RegOpenKeyExA(HKEY key, LPCSTR sub, DWORD options, REGSAM sam, PHKEY result); +#define RegOpenKeyEx RegOpenKeyExA +LONG RegOpenKeyA(HKEY key, LPCSTR sub, PHKEY result); +#define RegOpenKey RegOpenKeyA +LONG RegCreateKeyExA(HKEY key, LPCSTR sub, DWORD reserved, LPSTR cls, DWORD options, REGSAM sam, + LPSECURITY_ATTRIBUTES sa, PHKEY result, LPDWORD disposition); +#define RegCreateKeyEx RegCreateKeyExA +LONG RegCreateKeyA(HKEY key, LPCSTR sub, PHKEY result); +#define RegCreateKey RegCreateKeyA +LONG RegCloseKey(HKEY key); +LONG RegQueryValueExA(HKEY key, LPCSTR name, LPDWORD reserved, LPDWORD type, LPBYTE data, LPDWORD size); +#define RegQueryValueEx RegQueryValueExA +LONG RegSetValueExA(HKEY key, LPCSTR name, DWORD reserved, DWORD type, const BYTE* data, DWORD size); +#define RegSetValueEx RegSetValueExA +LONG RegDeleteValueA(HKEY key, LPCSTR name); +#define RegDeleteValue RegDeleteValueA +LONG RegDeleteKeyA(HKEY key, LPCSTR sub); +#define RegDeleteKey RegDeleteKeyA +LONG RegEnumKeyExA(HKEY key, DWORD index, LPSTR name, LPDWORD nameSize, LPDWORD reserved, LPSTR cls, LPDWORD clsSize, + PFILETIME lastWrite); +#define RegEnumKeyEx RegEnumKeyExA + +/* strings */ +int lstrlenA(LPCSTR s); +#define lstrlen lstrlenA +LPSTR lstrcpyA(LPSTR d, LPCSTR s); +#define lstrcpy lstrcpyA +LPSTR lstrcpynA(LPSTR d, LPCSTR s, int n); +#define lstrcpyn lstrcpynA +LPSTR lstrcatA(LPSTR d, LPCSTR s); +#define lstrcat lstrcatA +int lstrcmpA(LPCSTR a, LPCSTR b); +#define lstrcmp lstrcmpA +int lstrcmpiA(LPCSTR a, LPCSTR b); +#define lstrcmpi lstrcmpiA +int wsprintfA(LPSTR buf, LPCSTR fmt, ...); +#define wsprintf wsprintfA +int wvsprintfA(LPSTR buf, LPCSTR fmt, va_list args); +#define wvsprintf wvsprintfA +LPSTR CharUpperA(LPSTR s); +#define CharUpper CharUpperA +LPSTR CharLowerA(LPSTR s); +#define CharLower CharLowerA +DWORD CharUpperBuffA(LPSTR s, DWORD len); +#define CharUpperBuff CharUpperBuffA +DWORD CharLowerBuffA(LPSTR s, DWORD len); +#define CharLowerBuff CharLowerBuffA +LPSTR CharNextA(LPCSTR s); +#define CharNext CharNextA +LPSTR CharPrevA(LPCSTR start, LPCSTR s); +#define CharPrev CharPrevA +BOOL IsCharAlphaA(CHAR c); +#define IsCharAlpha IsCharAlphaA +BOOL IsCharAlphaNumericA(CHAR c); +#define IsCharAlphaNumeric IsCharAlphaNumericA +BOOL IsCharUpperA(CHAR c); +#define IsCharUpper IsCharUpperA +BOOL IsCharLowerA(CHAR c); +#define IsCharLower IsCharLowerA +BOOL IsDBCSLeadByte(BYTE c); +BOOL OemToCharA(LPCSTR src, LPSTR dst); +#define OemToChar OemToCharA +BOOL CharToOemA(LPCSTR src, LPSTR dst); +#define CharToOem CharToOemA + +#ifdef __cplusplus +} +#endif diff --git a/port/mfcwx/include/mfcwx/winapi_gui.h b/port/mfcwx/include/mfcwx/winapi_gui.h new file mode 100644 index 000000000..bff9d3913 --- /dev/null +++ b/port/mfcwx/include/mfcwx/winapi_gui.h @@ -0,0 +1,283 @@ +#pragma once + +// Win32 user/gdi functions operating on HWND/HDC handles (HWND is a wxWindow*, HDC a CDC*). + +LRESULT SendMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam); +LRESULT SendMessageA(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam); +BOOL PostMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam); +BOOL PostMessageA(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam); +BOOL SendNotifyMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam); +BOOL PostThreadMessage(DWORD idThread, UINT Msg, WPARAM wParam, LPARAM lParam); +void PostQuitMessage(int nExitCode); +UINT RegisterWindowMessage(const char* lpString); +UINT RegisterWindowMessageA(const char* lpString); +BOOL PeekMessage(LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax, UINT wRemoveMsg); +BOOL GetMessage(LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax); +BOOL TranslateMessage(const MSG* lpMsg); +LRESULT DispatchMessage(const MSG* lpMsg); +BOOL WaitMessage(); +DWORD MsgWaitForMultipleObjects(DWORD nCount, const HANDLE* pHandles, BOOL fWaitAll, DWORD dwMilliseconds, + DWORD dwWakeMask); +LRESULT DefWindowProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam); +LRESULT CallWindowProc(WNDPROC lpPrevWndFunc, HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam); + +BOOL IsWindow(HWND hWnd); +BOOL IsWindowVisible(HWND hWnd); +BOOL IsWindowEnabled(HWND hWnd); +BOOL EnableWindow(HWND hWnd, BOOL bEnable); +BOOL ShowWindow(HWND hWnd, int nCmdShow); +BOOL IsIconic(HWND hWnd); +BOOL IsZoomed(HWND hWnd); +BOOL IsChild(HWND hWndParent, HWND hWnd); +HWND GetParent(HWND hWnd); +HWND SetParent(HWND hWndChild, HWND hWndNewParent); +HWND GetWindow(HWND hWnd, UINT uCmd); +HWND GetTopWindow(HWND hWnd); +HWND GetDesktopWindow(); +HWND GetActiveWindow(); +HWND SetActiveWindow(HWND hWnd); +HWND GetForegroundWindow(); +BOOL SetForegroundWindow(HWND hWnd); +HWND GetFocus(); +HWND SetFocus(HWND hWnd); +HWND GetCapture(); +HWND SetCapture(HWND hWnd); +BOOL ReleaseCapture(); +HWND FindWindow(const char* lpClassName, const char* lpWindowName); +HWND FindWindowA(const char* lpClassName, const char* lpWindowName); +HWND WindowFromPoint(POINT Point); +HWND ChildWindowFromPoint(HWND hWndParent, POINT Point); +BOOL DestroyWindow(HWND hWnd); +BOOL CloseWindow(HWND hWnd); +BOOL BringWindowToTop(HWND hWnd); +BOOL SetWindowText(HWND hWnd, const char* lpString); +BOOL SetWindowTextA(HWND hWnd, const char* lpString); +int GetWindowText(HWND hWnd, char* lpString, int nMaxCount); +int GetWindowTextA(HWND hWnd, char* lpString, int nMaxCount); +int GetWindowTextLength(HWND hWnd); +int GetClassName(HWND hWnd, char* lpClassName, int nMaxCount); +LONG GetWindowLong(HWND hWnd, int nIndex); +LONG SetWindowLong(HWND hWnd, int nIndex, LONG dwNewLong); +LONG_PTR GetWindowLongPtr(HWND hWnd, int nIndex); +LONG_PTR SetWindowLongPtr(HWND hWnd, int nIndex, LONG_PTR dwNewLong); +BOOL GetWindowRect(HWND hWnd, LPRECT lpRect); +BOOL GetClientRect(HWND hWnd, LPRECT lpRect); +BOOL ClientToScreen(HWND hWnd, LPPOINT lpPoint); +BOOL ScreenToClient(HWND hWnd, LPPOINT lpPoint); +int MapWindowPoints(HWND hWndFrom, HWND hWndTo, LPPOINT lpPoints, UINT cPoints); +BOOL MoveWindow(HWND hWnd, int X, int Y, int nWidth, int nHeight, BOOL bRepaint); +BOOL SetWindowPos(HWND hWnd, HWND hWndInsertAfter, int X, int Y, int cx, int cy, UINT uFlags); +BOOL InvalidateRect(HWND hWnd, const RECT* lpRect, BOOL bErase); +BOOL ValidateRect(HWND hWnd, const RECT* lpRect); +BOOL UpdateWindow(HWND hWnd); +BOOL RedrawWindow(HWND hWnd, const RECT* lprcUpdate, HRGN hrgnUpdate, UINT flags); +HDC GetDC(HWND hWnd); +HDC GetWindowDC(HWND hWnd); +int ReleaseDC(HWND hWnd, HDC hDC); +HDC BeginPaint(HWND hWnd, LPPAINTSTRUCT lpPaint); +BOOL EndPaint(HWND hWnd, const PAINTSTRUCT* lpPaint); +UINT_PTR SetTimer(HWND hWnd, UINT_PTR nIDEvent, UINT uElapse, TIMERPROC lpTimerFunc); +BOOL KillTimer(HWND hWnd, UINT_PTR uIDEvent); + +HWND GetDlgItem(HWND hDlg, int nIDDlgItem); +int GetDlgCtrlID(HWND hWnd); +UINT GetDlgItemInt(HWND hDlg, int nIDDlgItem, BOOL* lpTranslated, BOOL bSigned); +BOOL SetDlgItemInt(HWND hDlg, int nIDDlgItem, UINT uValue, BOOL bSigned); +UINT GetDlgItemText(HWND hDlg, int nIDDlgItem, char* lpString, int cchMax); +UINT GetDlgItemTextA(HWND hDlg, int nIDDlgItem, char* lpString, int cchMax); +BOOL SetDlgItemText(HWND hDlg, int nIDDlgItem, const char* lpString); +BOOL SetDlgItemTextA(HWND hDlg, int nIDDlgItem, const char* lpString); +BOOL CheckDlgButton(HWND hDlg, int nIDButton, UINT uCheck); +UINT IsDlgButtonChecked(HWND hDlg, int nIDButton); +BOOL CheckRadioButton(HWND hDlg, int nIDFirstButton, int nIDLastButton, int nIDCheckButton); +LRESULT SendDlgItemMessage(HWND hDlg, int nIDDlgItem, UINT Msg, WPARAM wParam, LPARAM lParam); +BOOL EndDialog(HWND hDlg, INT_PTR nResult); +HWND GetNextDlgTabItem(HWND hDlg, HWND hCtl, BOOL bPrevious); +HWND GetNextDlgGroupItem(HWND hDlg, HWND hCtl, BOOL bPrevious); +BOOL MapDialogRect(HWND hDlg, LPRECT lpRect); +LONG GetDialogBaseUnits(); + +int MessageBox(HWND hWnd, const char* lpText, const char* lpCaption, UINT uType); +int MessageBoxA(HWND hWnd, const char* lpText, const char* lpCaption, UINT uType); +BOOL WinHelp(HWND hWndMain, const char* lpszHelp, UINT uCommand, ULONG_PTR dwData); +BOOL WinHelpA(HWND hWndMain, const char* lpszHelp, UINT uCommand, ULONG_PTR dwData); + +int GetSystemMetrics(int nIndex); +DWORD GetSysColor(int nIndex); +HBRUSH GetSysColorBrush(int nIndex); +BOOL SystemParametersInfo(UINT uiAction, UINT uiParam, PVOID pvParam, UINT fWinIni); +BOOL SystemParametersInfoA(UINT uiAction, UINT uiParam, PVOID pvParam, UINT fWinIni); +SHORT GetKeyState(int nVirtKey); +SHORT GetAsyncKeyState(int vKey); +BOOL GetKeyboardState(BYTE* lpKeyState); +BOOL GetCursorPos(LPPOINT lpPoint); +BOOL SetCursorPos(int X, int Y); +HCURSOR SetCursor(HCURSOR hCursor); +HCURSOR GetCursor(); +int ShowCursor(BOOL bShow); +HCURSOR LoadCursor(HINSTANCE hInstance, const char* lpCursorName); +HCURSOR LoadCursorA(HINSTANCE hInstance, const char* lpCursorName); +HCURSOR LoadCursorFromFile(const char* lpFileName); +HICON LoadIcon(HINSTANCE hInstance, const char* lpIconName); +HICON LoadIconA(HINSTANCE hInstance, const char* lpIconName); +BOOL DestroyIcon(HICON hIcon); +BOOL DestroyCursor(HCURSOR hCursor); +BOOL DrawIcon(HDC hDC, int X, int Y, HICON hIcon); +HANDLE LoadImage(HINSTANCE hInst, const char* name, UINT type, int cx, int cy, UINT fuLoad); +HANDLE LoadImageA(HINSTANCE hInst, const char* name, UINT type, int cx, int cy, UINT fuLoad); +HBITMAP LoadBitmap(HINSTANCE hInstance, const char* lpBitmapName); +HBITMAP LoadBitmapA(HINSTANCE hInstance, const char* lpBitmapName); +int LoadString(HINSTANCE hInstance, UINT uID, char* lpBuffer, int cchBufferMax); +int LoadStringA(HINSTANCE hInstance, UINT uID, char* lpBuffer, int cchBufferMax); +HMENU LoadMenu(HINSTANCE hInstance, const char* lpMenuName); +HACCEL LoadAccelerators(HINSTANCE hInstance, const char* lpTableName); +int TranslateAccelerator(HWND hWnd, HACCEL hAccTable, LPMSG lpMsg); +HRSRC FindResource(HMODULE hModule, const char* lpName, const char* lpType); +HRSRC FindResourceA(HMODULE hModule, const char* lpName, const char* lpType); +HGLOBAL LoadResource(HMODULE hModule, HRSRC hResInfo); +LPVOID LockResource(HGLOBAL hResData); +DWORD SizeofResource(HMODULE hModule, HRSRC hResInfo); +BOOL FreeResource(HGLOBAL hResData); +#define RT_CURSOR MAKEINTRESOURCE(1) +#define RT_BITMAP MAKEINTRESOURCE(2) +#define RT_ICON MAKEINTRESOURCE(3) +#define RT_MENU MAKEINTRESOURCE(4) +#define RT_DIALOG MAKEINTRESOURCE(5) +#define RT_STRING MAKEINTRESOURCE(6) +#define RT_ACCELERATOR MAKEINTRESOURCE(9) +#define RT_RCDATA MAKEINTRESOURCE(10) +#define RT_GROUP_CURSOR MAKEINTRESOURCE(12) +#define RT_GROUP_ICON MAKEINTRESOURCE(14) +#define RT_VERSION MAKEINTRESOURCE(16) +#define RT_DLGINIT MAKEINTRESOURCE(240) +#define RT_HTML MAKEINTRESOURCE(23) + +#define IDC_ARROW MAKEINTRESOURCE(32512) +#define IDC_IBEAM MAKEINTRESOURCE(32513) +#define IDC_WAIT MAKEINTRESOURCE(32514) +#define IDC_CROSS MAKEINTRESOURCE(32515) +#define IDC_UPARROW MAKEINTRESOURCE(32516) +#define IDC_SIZE MAKEINTRESOURCE(32640) +#define IDC_ICON MAKEINTRESOURCE(32641) +#define IDC_SIZENWSE MAKEINTRESOURCE(32642) +#define IDC_SIZENESW MAKEINTRESOURCE(32643) +#define IDC_SIZEWE MAKEINTRESOURCE(32644) +#define IDC_SIZENS MAKEINTRESOURCE(32645) +#define IDC_SIZEALL MAKEINTRESOURCE(32646) +#define IDC_NO MAKEINTRESOURCE(32648) +#define IDC_HAND MAKEINTRESOURCE(32649) +#define IDC_APPSTARTING MAKEINTRESOURCE(32650) +#define IDC_HELP MAKEINTRESOURCE(32651) +#define IDI_APPLICATION MAKEINTRESOURCE(32512) +#define IDI_HAND MAKEINTRESOURCE(32513) +#define IDI_QUESTION MAKEINTRESOURCE(32514) +#define IDI_EXCLAMATION MAKEINTRESOURCE(32515) +#define IDI_ASTERISK MAKEINTRESOURCE(32516) +#define IDI_WINLOGO MAKEINTRESOURCE(32517) +#define IDI_WARNING IDI_EXCLAMATION +#define IDI_ERROR IDI_HAND +#define IDI_INFORMATION IDI_ASTERISK + +HMENU GetMenu(HWND hWnd); +BOOL SetMenu(HWND hWnd, HMENU hMenu); +HMENU GetSubMenu(HMENU hMenu, int nPos); +HMENU GetSystemMenu(HWND hWnd, BOOL bRevert); +HMENU CreateMenu(); +HMENU CreatePopupMenu(); +BOOL DestroyMenu(HMENU hMenu); +BOOL AppendMenu(HMENU hMenu, UINT uFlags, UINT_PTR uIDNewItem, const char* lpNewItem); +BOOL AppendMenuA(HMENU hMenu, UINT uFlags, UINT_PTR uIDNewItem, const char* lpNewItem); +BOOL InsertMenu(HMENU hMenu, UINT uPosition, UINT uFlags, UINT_PTR uIDNewItem, const char* lpNewItem); +BOOL DeleteMenu(HMENU hMenu, UINT uPosition, UINT uFlags); +BOOL RemoveMenu(HMENU hMenu, UINT uPosition, UINT uFlags); +BOOL ModifyMenu(HMENU hMnu, UINT uPosition, UINT uFlags, UINT_PTR uIDNewItem, const char* lpNewItem); +DWORD CheckMenuItem(HMENU hMenu, UINT uIDCheckItem, UINT uCheck); +BOOL EnableMenuItem(HMENU hMenu, UINT uIDEnableItem, UINT uEnable); +int GetMenuItemCount(HMENU hMenu); +UINT GetMenuItemID(HMENU hMenu, int nPos); +UINT GetMenuState(HMENU hMenu, UINT uId, UINT uFlags); +int GetMenuString(HMENU hMenu, UINT uIDItem, char* lpString, int cchMax, UINT flags); +BOOL TrackPopupMenu(HMENU hMenu, UINT uFlags, int x, int y, int nReserved, HWND hWnd, const RECT* prcRect); +BOOL DrawMenuBar(HWND hWnd); + +BOOL OpenClipboard(HWND hWndNewOwner); +BOOL CloseClipboard(); +BOOL EmptyClipboard(); +HANDLE GetClipboardData(UINT uFormat); +HANDLE SetClipboardData(UINT uFormat, HANDLE hMem); +BOOL IsClipboardFormatAvailable(UINT format); +UINT RegisterClipboardFormat(const char* lpszFormat); +UINT RegisterClipboardFormatA(const char* lpszFormat); +UINT DragQueryFile(HDROP hDrop, UINT iFile, char* lpszFile, UINT cch); +UINT DragQueryFileA(HDROP hDrop, UINT iFile, char* lpszFile, UINT cch); +void DragFinish(HDROP hDrop); +void DragAcceptFiles(HWND hWnd, BOOL fAccept); + +HGDIOBJ GetStockObject(int i); +BOOL DeleteObject(HGDIOBJ ho); +HGDIOBJ SelectObject(HDC hdc, HGDIOBJ h); +int GetObject(HANDLE h, int c, LPVOID pv); +int GetObjectA(HANDLE h, int c, LPVOID pv); +HBRUSH CreateSolidBrush(COLORREF color); +HBRUSH CreateHatchBrush(int iHatch, COLORREF color); +HBRUSH CreatePatternBrush(HBITMAP hbm); +HPEN CreatePen(int iStyle, int cWidth, COLORREF color); +HFONT CreateFont(int cHeight, int cWidth, int cEscapement, int cOrientation, int cWeight, DWORD bItalic, + DWORD bUnderline, DWORD bStrikeOut, DWORD iCharSet, DWORD iOutPrecision, DWORD iClipPrecision, + DWORD iQuality, DWORD iPitchAndFamily, const char* pszFaceName); +HFONT CreateFontA(int cHeight, int cWidth, int cEscapement, int cOrientation, int cWeight, DWORD bItalic, + DWORD bUnderline, DWORD bStrikeOut, DWORD iCharSet, DWORD iOutPrecision, DWORD iClipPrecision, + DWORD iQuality, DWORD iPitchAndFamily, const char* pszFaceName); +HFONT CreateFontIndirect(const LOGFONT* lplf); +HFONT CreateFontIndirectA(const LOGFONT* lplf); +HBITMAP CreateCompatibleBitmap(HDC hdc, int cx, int cy); +HBITMAP CreateBitmap(int nWidth, int nHeight, UINT nPlanes, UINT nBitCount, const void* lpBits); +HDC CreateCompatibleDC(HDC hdc); +BOOL DeleteDC(HDC hdc); +COLORREF SetTextColor(HDC hdc, COLORREF color); +COLORREF SetBkColor(HDC hdc, COLORREF color); +int SetBkMode(HDC hdc, int mode); +BOOL TextOut(HDC hdc, int x, int y, const char* lpString, int c); +BOOL TextOutA(HDC hdc, int x, int y, const char* lpString, int c); +int DrawText(HDC hdc, const char* lpchText, int cchText, LPRECT lprc, UINT format); +int DrawTextA(HDC hdc, const char* lpchText, int cchText, LPRECT lprc, UINT format); +BOOL GetTextExtentPoint32(HDC hdc, const char* lpString, int c, LPSIZE psizl); +BOOL GetTextExtentPoint32A(HDC hdc, const char* lpString, int c, LPSIZE psizl); +BOOL GetTextMetrics(HDC hdc, LPTEXTMETRIC lptm); +BOOL GetTextMetricsA(HDC hdc, LPTEXTMETRIC lptm); +int GetDeviceCaps(HDC hdc, int index); +BOOL BitBlt(HDC hdc, int x, int y, int cx, int cy, HDC hdcSrc, int x1, int y1, DWORD rop); +BOOL StretchBlt(HDC hdcDest, int xDest, int yDest, int wDest, int hDest, HDC hdcSrc, int xSrc, int ySrc, int wSrc, + int hSrc, DWORD rop); +BOOL MoveToEx(HDC hdc, int x, int y, LPPOINT lppt); +BOOL LineTo(HDC hdc, int x, int y); +BOOL Rectangle(HDC hdc, int left, int top, int right, int bottom); +BOOL Ellipse(HDC hdc, int left, int top, int right, int bottom); +BOOL Polygon(HDC hdc, const POINT* apt, int cpt); +BOOL Polyline(HDC hdc, const POINT* apt, int cpt); +int FillRect(HDC hDC, const RECT* lprc, HBRUSH hbr); +int FrameRect(HDC hDC, const RECT* lprc, HBRUSH hbr); +COLORREF SetPixel(HDC hdc, int x, int y, COLORREF color); +COLORREF GetPixel(HDC hdc, int x, int y); +BOOL DrawEdge(HDC hdc, LPRECT qrc, UINT edge, UINT grfFlags); +BOOL DrawFocusRect(HDC hDC, const RECT* lprc); +int SaveDC(HDC hdc); +BOOL RestoreDC(HDC hdc, int nSavedDC); +HRGN CreateRectRgn(int x1, int y1, int x2, int y2); +HRGN CreateEllipticRgn(int x1, int y1, int x2, int y2); +int SelectClipRgn(HDC hdc, HRGN hrgn); +int SetDIBitsToDevice(HDC hdc, int xDest, int yDest, DWORD w, DWORD h, int xSrc, int ySrc, UINT StartScan, + UINT cLines, const void* lpvBits, const BITMAPINFO* lpbmi, UINT ColorUse); +int StretchDIBits(HDC hdc, int xDest, int yDest, int DestWidth, int DestHeight, int xSrc, int ySrc, int SrcWidth, + int SrcHeight, const void* lpBits, const BITMAPINFO* lpbmi, UINT iUsage, DWORD rop); + +BOOL SetRect(LPRECT lprc, int xLeft, int yTop, int xRight, int yBottom); +BOOL SetRectEmpty(LPRECT lprc); +BOOL CopyRect(LPRECT lprcDst, const RECT* lprcSrc); +BOOL InflateRect(LPRECT lprc, int dx, int dy); +BOOL OffsetRect(LPRECT lprc, int dx, int dy); +BOOL IntersectRect(LPRECT lprcDst, const RECT* lprcSrc1, const RECT* lprcSrc2); +BOOL UnionRect(LPRECT lprcDst, const RECT* lprcSrc1, const RECT* lprcSrc2); +BOOL IsRectEmpty(const RECT* lprc); +BOOL PtInRect(const RECT* lprc, POINT pt); +BOOL EqualRect(const RECT* lprc1, const RECT* lprc2); diff --git a/port/mfcwx/include/mfcwx/winconst.h b/port/mfcwx/include/mfcwx/winconst.h new file mode 100644 index 000000000..6e48af10b --- /dev/null +++ b/port/mfcwx/include/mfcwx/winconst.h @@ -0,0 +1,1935 @@ +#pragma once + +/* Win32 constants with their original numeric values. Parsed by port/rc/rc2cpp.py. */ + +#define FALSE 0 +#define TRUE 1 +#define MAX_PATH 260 +#define _MAX_PATH 260 +#define _MAX_DRIVE 3 +#define _MAX_DIR 256 +#define _MAX_FNAME 256 +#define _MAX_EXT 256 +#define INFINITE 0xFFFFFFFF +#define VS_VERSION_INFO 1 + +/* window styles */ +#define WS_OVERLAPPED 0x00000000L +#define WS_POPUP 0x80000000L +#define WS_CHILD 0x40000000L +#define WS_MINIMIZE 0x20000000L +#define WS_VISIBLE 0x10000000L +#define WS_DISABLED 0x08000000L +#define WS_CLIPSIBLINGS 0x04000000L +#define WS_CLIPCHILDREN 0x02000000L +#define WS_MAXIMIZE 0x01000000L +#define WS_CAPTION 0x00C00000L +#define WS_BORDER 0x00800000L +#define WS_DLGFRAME 0x00400000L +#define WS_VSCROLL 0x00200000L +#define WS_HSCROLL 0x00100000L +#define WS_SYSMENU 0x00080000L +#define WS_THICKFRAME 0x00040000L +#define WS_GROUP 0x00020000L +#define WS_TABSTOP 0x00010000L +#define WS_MINIMIZEBOX 0x00020000L +#define WS_MAXIMIZEBOX 0x00010000L +#define WS_TILED WS_OVERLAPPED +#define WS_ICONIC WS_MINIMIZE +#define WS_SIZEBOX WS_THICKFRAME +#define WS_CHILDWINDOW WS_CHILD +#define WS_OVERLAPPEDWINDOW (WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX) +#define WS_TILEDWINDOW WS_OVERLAPPEDWINDOW +#define WS_POPUPWINDOW (WS_POPUP | WS_BORDER | WS_SYSMENU) + +#define WS_EX_DLGMODALFRAME 0x00000001L +#define WS_EX_NOPARENTNOTIFY 0x00000004L +#define WS_EX_TOPMOST 0x00000008L +#define WS_EX_ACCEPTFILES 0x00000010L +#define WS_EX_TRANSPARENT 0x00000020L +#define WS_EX_MDICHILD 0x00000040L +#define WS_EX_TOOLWINDOW 0x00000080L +#define WS_EX_WINDOWEDGE 0x00000100L +#define WS_EX_CLIENTEDGE 0x00000200L +#define WS_EX_CONTEXTHELP 0x00000400L +#define WS_EX_RIGHT 0x00001000L +#define WS_EX_LEFT 0x00000000L +#define WS_EX_RTLREADING 0x00002000L +#define WS_EX_LTRREADING 0x00000000L +#define WS_EX_LEFTSCROLLBAR 0x00004000L +#define WS_EX_RIGHTSCROLLBAR 0x00000000L +#define WS_EX_CONTROLPARENT 0x00010000L +#define WS_EX_STATICEDGE 0x00020000L +#define WS_EX_APPWINDOW 0x00040000L +#define WS_EX_LAYERED 0x00080000L +#define WS_EX_NOINHERITLAYOUT 0x00100000L +#define WS_EX_LAYOUTRTL 0x00400000L +#define WS_EX_COMPOSITED 0x02000000L +#define WS_EX_NOACTIVATE 0x08000000L +#define WS_EX_OVERLAPPEDWINDOW (WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE) +#define WS_EX_PALETTEWINDOW (WS_EX_WINDOWEDGE | WS_EX_TOOLWINDOW | WS_EX_TOPMOST) + +/* dialog styles */ +#define DS_ABSALIGN 0x01L +#define DS_SYSMODAL 0x02L +#define DS_3DLOOK 0x04L +#define DS_FIXEDSYS 0x08L +#define DS_NOFAILCREATE 0x10L +#define DS_LOCALEDIT 0x20L +#define DS_SETFONT 0x40L +#define DS_MODALFRAME 0x80L +#define DS_NOIDLEMSG 0x100L +#define DS_SETFOREGROUND 0x200L +#define DS_CONTROL 0x0400L +#define DS_CENTER 0x0800L +#define DS_CENTERMOUSE 0x1000L +#define DS_CONTEXTHELP 0x2000L +#define DS_SHELLFONT (DS_SETFONT | DS_FIXEDSYS) + +/* edit styles */ +#define ES_LEFT 0x0000L +#define ES_CENTER 0x0001L +#define ES_RIGHT 0x0002L +#define ES_MULTILINE 0x0004L +#define ES_UPPERCASE 0x0008L +#define ES_LOWERCASE 0x0010L +#define ES_PASSWORD 0x0020L +#define ES_AUTOVSCROLL 0x0040L +#define ES_AUTOHSCROLL 0x0080L +#define ES_NOHIDESEL 0x0100L +#define ES_OEMCONVERT 0x0400L +#define ES_READONLY 0x0800L +#define ES_WANTRETURN 0x1000L +#define ES_NUMBER 0x2000L +#define ES_DISABLENOSCROLL 0x00002000L +#define ES_SUNKEN 0x00004000L +#define ES_SAVESEL 0x00008000L +#define ES_SELFIME 0x00040000L +#define ES_NOIME 0x00080000L +#define ES_VERTICAL 0x00400000L +#define ES_SELECTIONBAR 0x01000000L +#define ES_EX_NOCALLOLEINIT 0x00000000L + +/* button styles */ +#define BS_PUSHBUTTON 0x00000000L +#define BS_DEFPUSHBUTTON 0x00000001L +#define BS_CHECKBOX 0x00000002L +#define BS_AUTOCHECKBOX 0x00000003L +#define BS_RADIOBUTTON 0x00000004L +#define BS_3STATE 0x00000005L +#define BS_AUTO3STATE 0x00000006L +#define BS_GROUPBOX 0x00000007L +#define BS_USERBUTTON 0x00000008L +#define BS_AUTORADIOBUTTON 0x00000009L +#define BS_PUSHBOX 0x0000000AL +#define BS_OWNERDRAW 0x0000000BL +#define BS_SPLITBUTTON 0x0000000CL +#define BS_DEFSPLITBUTTON 0x0000000DL +#define BS_COMMANDLINK 0x0000000EL +#define BS_DEFCOMMANDLINK 0x0000000FL +#define BS_TYPEMASK 0x0000000FL +#define BS_LEFTTEXT 0x00000020L +#define BS_TEXT 0x00000000L +#define BS_ICON 0x00000040L +#define BS_BITMAP 0x00000080L +#define BS_LEFT 0x00000100L +#define BS_RIGHT 0x00000200L +#define BS_CENTER 0x00000300L +#define BS_TOP 0x00000400L +#define BS_BOTTOM 0x00000800L +#define BS_VCENTER 0x00000C00L +#define BS_PUSHLIKE 0x00001000L +#define BS_MULTILINE 0x00002000L +#define BS_NOTIFY 0x00004000L +#define BS_FLAT 0x00008000L +#define BS_RIGHTBUTTON BS_LEFTTEXT + +/* static styles */ +#define SS_LEFT 0x00000000L +#define SS_CENTER 0x00000001L +#define SS_RIGHT 0x00000002L +#define SS_ICON 0x00000003L +#define SS_BLACKRECT 0x00000004L +#define SS_GRAYRECT 0x00000005L +#define SS_WHITERECT 0x00000006L +#define SS_BLACKFRAME 0x00000007L +#define SS_GRAYFRAME 0x00000008L +#define SS_WHITEFRAME 0x00000009L +#define SS_USERITEM 0x0000000AL +#define SS_SIMPLE 0x0000000BL +#define SS_LEFTNOWORDWRAP 0x0000000CL +#define SS_OWNERDRAW 0x0000000DL +#define SS_BITMAP 0x0000000EL +#define SS_ENHMETAFILE 0x0000000FL +#define SS_ETCHEDHORZ 0x00000010L +#define SS_ETCHEDVERT 0x00000011L +#define SS_ETCHEDFRAME 0x00000012L +#define SS_TYPEMASK 0x0000001FL +#define SS_REALSIZECONTROL 0x00000040L +#define SS_NOPREFIX 0x00000080L +#define SS_NOTIFY 0x00000100L +#define SS_CENTERIMAGE 0x00000200L +#define SS_RIGHTJUST 0x00000400L +#define SS_REALSIZEIMAGE 0x00000800L +#define SS_SUNKEN 0x00001000L +#define SS_EDITCONTROL 0x00002000L +#define SS_ENDELLIPSIS 0x00004000L +#define SS_PATHELLIPSIS 0x00008000L +#define SS_WORDELLIPSIS 0x0000C000L +#define SS_ELLIPSISMASK 0x0000C000L + +/* combo box styles */ +#define CBS_SIMPLE 0x0001L +#define CBS_DROPDOWN 0x0002L +#define CBS_DROPDOWNLIST 0x0003L +#define CBS_OWNERDRAWFIXED 0x0010L +#define CBS_OWNERDRAWVARIABLE 0x0020L +#define CBS_AUTOHSCROLL 0x0040L +#define CBS_OEMCONVERT 0x0080L +#define CBS_SORT 0x0100L +#define CBS_HASSTRINGS 0x0200L +#define CBS_NOINTEGRALHEIGHT 0x0400L +#define CBS_DISABLENOSCROLL 0x0800L +#define CBS_UPPERCASE 0x2000L +#define CBS_LOWERCASE 0x4000L + +/* list box styles */ +#define LBS_NOTIFY 0x0001L +#define LBS_SORT 0x0002L +#define LBS_NOREDRAW 0x0004L +#define LBS_MULTIPLESEL 0x0008L +#define LBS_OWNERDRAWFIXED 0x0010L +#define LBS_OWNERDRAWVARIABLE 0x0020L +#define LBS_HASSTRINGS 0x0040L +#define LBS_USETABSTOPS 0x0080L +#define LBS_NOINTEGRALHEIGHT 0x0100L +#define LBS_MULTICOLUMN 0x0200L +#define LBS_WANTKEYBOARDINPUT 0x0400L +#define LBS_EXTENDEDSEL 0x0800L +#define LBS_DISABLENOSCROLL 0x1000L +#define LBS_NODATA 0x2000L +#define LBS_NOSEL 0x4000L +#define LBS_COMBOBOX 0x8000L +#define LBS_STANDARD (LBS_NOTIFY | LBS_SORT | WS_VSCROLL | WS_BORDER) + +/* scroll bar styles */ +#define SBS_HORZ 0x0000L +#define SBS_VERT 0x0001L +#define SBS_TOPALIGN 0x0002L +#define SBS_LEFTALIGN 0x0002L +#define SBS_BOTTOMALIGN 0x0004L +#define SBS_RIGHTALIGN 0x0004L +#define SBS_SIZEBOX 0x0008L +#define SBS_SIZEGRIP 0x0010L + +/* common control styles */ +#define CCS_TOP 0x00000001L +#define CCS_NOMOVEY 0x00000002L +#define CCS_BOTTOM 0x00000003L +#define CCS_NORESIZE 0x00000004L +#define CCS_NOPARENTALIGN 0x00000008L +#define CCS_ADJUSTABLE 0x00000020L +#define CCS_NODIVIDER 0x00000040L +#define CCS_VERT 0x00000080L + +#define TBS_AUTOTICKS 0x0001 +#define TBS_VERT 0x0002 +#define TBS_HORZ 0x0000 +#define TBS_TOP 0x0004 +#define TBS_BOTTOM 0x0000 +#define TBS_LEFT 0x0004 +#define TBS_RIGHT 0x0000 +#define TBS_BOTH 0x0008 +#define TBS_NOTICKS 0x0010 +#define TBS_ENABLESELRANGE 0x0020 +#define TBS_FIXEDLENGTH 0x0040 +#define TBS_NOTHUMB 0x0080 +#define TBS_TOOLTIPS 0x0100 +#define TBS_REVERSED 0x0200 + +#define UDS_WRAP 0x0001 +#define UDS_SETBUDDYINT 0x0002 +#define UDS_ALIGNRIGHT 0x0004 +#define UDS_ALIGNLEFT 0x0008 +#define UDS_AUTOBUDDY 0x0010 +#define UDS_ARROWKEYS 0x0020 +#define UDS_HORZ 0x0040 +#define UDS_NOTHOUSANDS 0x0080 +#define UDS_HOTTRACK 0x0100 + +#define PBS_SMOOTH 0x01 +#define PBS_VERTICAL 0x04 +#define PBS_MARQUEE 0x08 + +#define LVS_ICON 0x0000 +#define LVS_REPORT 0x0001 +#define LVS_SMALLICON 0x0002 +#define LVS_LIST 0x0003 +#define LVS_TYPEMASK 0x0003 +#define LVS_SINGLESEL 0x0004 +#define LVS_SHOWSELALWAYS 0x0008 +#define LVS_SORTASCENDING 0x0010 +#define LVS_SORTDESCENDING 0x0020 +#define LVS_SHAREIMAGELISTS 0x0040 +#define LVS_NOLABELWRAP 0x0080 +#define LVS_AUTOARRANGE 0x0100 +#define LVS_EDITLABELS 0x0200 +#define LVS_OWNERDRAWFIXED 0x0400 +#define LVS_ALIGNTOP 0x0000 +#define LVS_ALIGNLEFT 0x0800 +#define LVS_OWNERDATA 0x1000 +#define LVS_NOSCROLL 0x2000 +#define LVS_NOCOLUMNHEADER 0x4000 +#define LVS_NOSORTHEADER 0x8000 + +#define LVS_EX_GRIDLINES 0x00000001 +#define LVS_EX_SUBITEMIMAGES 0x00000002 +#define LVS_EX_CHECKBOXES 0x00000004 +#define LVS_EX_TRACKSELECT 0x00000008 +#define LVS_EX_HEADERDRAGDROP 0x00000010 +#define LVS_EX_FULLROWSELECT 0x00000020 +#define LVS_EX_ONECLICKACTIVATE 0x00000040 +#define LVS_EX_TWOCLICKACTIVATE 0x00000080 +#define LVS_EX_FLATSB 0x00000100 +#define LVS_EX_REGIONAL 0x00000200 +#define LVS_EX_INFOTIP 0x00000400 +#define LVS_EX_UNDERLINEHOT 0x00000800 +#define LVS_EX_UNDERLINECOLD 0x00001000 +#define LVS_EX_MULTIWORKAREAS 0x00002000 +#define LVS_EX_LABELTIP 0x00004000 +#define LVS_EX_BORDERSELECT 0x00008000 +#define LVS_EX_DOUBLEBUFFER 0x00010000 + +#define TVS_HASBUTTONS 0x0001 +#define TVS_HASLINES 0x0002 +#define TVS_LINESATROOT 0x0004 +#define TVS_EDITLABELS 0x0008 +#define TVS_DISABLEDRAGDROP 0x0010 +#define TVS_SHOWSELALWAYS 0x0020 +#define TVS_RTLREADING 0x0040 +#define TVS_NOTOOLTIPS 0x0080 +#define TVS_CHECKBOXES 0x0100 +#define TVS_TRACKSELECT 0x0200 +#define TVS_SINGLEEXPAND 0x0400 +#define TVS_INFOTIP 0x0800 +#define TVS_FULLROWSELECT 0x1000 +#define TVS_NOSCROLL 0x2000 +#define TVS_NONEVENHEIGHT 0x4000 + +#define TCS_SCROLLOPPOSITE 0x0001 +#define TCS_BOTTOM 0x0002 +#define TCS_RIGHT 0x0002 +#define TCS_MULTISELECT 0x0004 +#define TCS_FLATBUTTONS 0x0008 +#define TCS_FORCEICONLEFT 0x0010 +#define TCS_FORCELABELLEFT 0x0020 +#define TCS_HOTTRACK 0x0040 +#define TCS_VERTICAL 0x0080 +#define TCS_TABS 0x0000 +#define TCS_BUTTONS 0x0100 +#define TCS_SINGLELINE 0x0000 +#define TCS_MULTILINE 0x0200 +#define TCS_RIGHTJUSTIFY 0x0000 +#define TCS_FIXEDWIDTH 0x0400 +#define TCS_RAGGEDRIGHT 0x0800 +#define TCS_FOCUSONBUTTONDOWN 0x1000 +#define TCS_OWNERDRAWFIXED 0x2000 +#define TCS_TOOLTIPS 0x4000 +#define TCS_FOCUSNEVER 0x8000 + +#define ACS_CENTER 0x0001 +#define ACS_TRANSPARENT 0x0002 +#define ACS_AUTOPLAY 0x0004 +#define ACS_TIMER 0x0008 + +#define DTS_UPDOWN 0x0001 +#define DTS_SHOWNONE 0x0002 +#define DTS_SHORTDATEFORMAT 0x0000 +#define DTS_LONGDATEFORMAT 0x0004 +#define DTS_TIMEFORMAT 0x0009 +#define DTS_APPCANPARSE 0x0010 +#define DTS_RIGHTALIGN 0x0020 + +#define HDS_HORZ 0x0000 +#define HDN_BEGINTRACK (HDN_FIRST - 6) +#define HDN_ENDTRACK (HDN_FIRST - 7) +#define HDN_TRACK (HDN_FIRST - 8) +#define HDN_ITEMCHANGING (HDN_FIRST - 0) +#define HDN_ITEMCHANGED (HDN_FIRST - 1) +#define HDN_DIVIDERDBLCLICK (HDN_FIRST - 5) +#define HDI_WIDTH 0x0001 +#define HDI_TEXT 0x0002 +#define HDI_FORMAT 0x0004 +#define HDI_LPARAM 0x0008 +#define HDS_BUTTONS 0x0002 +#define HDS_HOTTRACK 0x0004 + +#define TTS_ALWAYSTIP 0x01 +#define TTS_NOPREFIX 0x02 +#define TTS_BALLOON 0x40 + +#define SBARS_SIZEGRIP 0x0100 + +#define TBSTYLE_BUTTON 0x0000 +#define TBSTYLE_SEP 0x0001 +#define TBSTYLE_CHECK 0x0002 +#define TBSTYLE_GROUP 0x0004 +#define TBSTYLE_CHECKGROUP (TBSTYLE_GROUP | TBSTYLE_CHECK) +#define TBSTYLE_DROPDOWN 0x0008 +#define TBSTYLE_AUTOSIZE 0x0010 +#define TBSTYLE_NOPREFIX 0x0020 +#define TBSTYLE_TOOLTIPS 0x0100 +#define TBSTYLE_WRAPABLE 0x0200 +#define TBSTYLE_ALTDRAG 0x0400 +#define TBSTYLE_FLAT 0x0800 +#define TBSTYLE_LIST 0x1000 +#define TBSTYLE_TRANSPARENT 0x8000 + +/* MFC control bar styles */ +#define CBRS_ALIGN_LEFT 0x1000L +#define CBRS_ALIGN_TOP 0x2000L +#define CBRS_ALIGN_RIGHT 0x4000L +#define CBRS_ALIGN_BOTTOM 0x8000L +#define CBRS_ALIGN_ANY 0xF000L +#define CBRS_BORDER_LEFT 0x0100L +#define CBRS_BORDER_TOP 0x0200L +#define CBRS_BORDER_RIGHT 0x0400L +#define CBRS_BORDER_BOTTOM 0x0800L +#define CBRS_BORDER_ANY 0x0F00L +#define CBRS_TOOLTIPS 0x0010L +#define CBRS_FLYBY 0x0020L +#define CBRS_FLOAT_MULTI 0x0040L +#define CBRS_BORDER_3D 0x0080L +#define CBRS_HIDE_INPLACE 0x0008L +#define CBRS_SIZE_DYNAMIC 0x0004L +#define CBRS_SIZE_FIXED 0x0002L +#define CBRS_FLOATING 0x0001L +#define CBRS_GRIPPER 0x00400000L +#define CBRS_ORIENT_HORZ (CBRS_ALIGN_TOP | CBRS_ALIGN_BOTTOM) +#define CBRS_ORIENT_VERT (CBRS_ALIGN_LEFT | CBRS_ALIGN_RIGHT) +#define CBRS_ORIENT_ANY (CBRS_ORIENT_HORZ | CBRS_ORIENT_VERT) +#define CBRS_ALL 0x0040FFFFL +#define CBRS_NOALIGN 0x00000000L +#define CBRS_LEFT (CBRS_ALIGN_LEFT | CBRS_BORDER_RIGHT) +#define CBRS_TOP (CBRS_ALIGN_TOP | CBRS_BORDER_BOTTOM) +#define CBRS_RIGHT (CBRS_ALIGN_RIGHT | CBRS_BORDER_LEFT) +#define CBRS_BOTTOM (CBRS_ALIGN_BOTTOM | CBRS_BORDER_TOP) + +/* window messages */ +#define WM_NULL 0x0000 +#define WM_CREATE 0x0001 +#define WM_DESTROY 0x0002 +#define WM_MOVE 0x0003 +#define WM_SIZE 0x0005 +#define WM_ACTIVATE 0x0006 +#define WM_SETFOCUS 0x0007 +#define WM_KILLFOCUS 0x0008 +#define WM_ENABLE 0x000A +#define WM_SETREDRAW 0x000B +#define WM_SETTEXT 0x000C +#define WM_GETTEXT 0x000D +#define WM_GETTEXTLENGTH 0x000E +#define WM_PAINT 0x000F +#define WM_CLOSE 0x0010 +#define WM_QUERYENDSESSION 0x0011 +#define WM_QUIT 0x0012 +#define WM_QUERYOPEN 0x0013 +#define WM_ERASEBKGND 0x0014 +#define WM_SYSCOLORCHANGE 0x0015 +#define WM_ENDSESSION 0x0016 +#define WM_SHOWWINDOW 0x0018 +#define WM_CTLCOLOR 0x0019 +#define WM_SETTINGCHANGE 0x001A +#define WM_WININICHANGE 0x001A +#define WM_ACTIVATEAPP 0x001C +#define WM_FONTCHANGE 0x001D +#define WM_TIMECHANGE 0x001E +#define WM_CANCELMODE 0x001F +#define WM_SETCURSOR 0x0020 +#define WM_MOUSEACTIVATE 0x0021 +#define WM_CHILDACTIVATE 0x0022 +#define WM_GETMINMAXINFO 0x0024 +#define WM_DRAWITEM 0x002B +#define WM_MEASUREITEM 0x002C +#define WM_DELETEITEM 0x002D +#define WM_VKEYTOITEM 0x002E +#define WM_CHARTOITEM 0x002F +#define WM_SETFONT 0x0030 +#define WM_GETFONT 0x0031 +#define WM_QUERYDRAGICON 0x0037 +#define WM_COMPAREITEM 0x0039 +#define WM_WINDOWPOSCHANGING 0x0046 +#define WM_WINDOWPOSCHANGED 0x0047 +#define WM_NOTIFY 0x004E +#define WM_HELP 0x0053 +#define WM_CONTEXTMENU 0x007B +#define WM_STYLECHANGING 0x007C +#define WM_STYLECHANGED 0x007D +#define WM_DISPLAYCHANGE 0x007E +#define WM_GETICON 0x007F +#define WM_SETICON 0x0080 +#define WM_NCCREATE 0x0081 +#define WM_NCDESTROY 0x0082 +#define WM_NCCALCSIZE 0x0083 +#define WM_NCHITTEST 0x0084 +#define WM_NCPAINT 0x0085 +#define WM_NCACTIVATE 0x0086 +#define WM_GETDLGCODE 0x0087 +#define WM_NCMOUSEMOVE 0x00A0 +#define WM_NCLBUTTONDOWN 0x00A1 +#define WM_NCLBUTTONUP 0x00A2 +#define WM_NCLBUTTONDBLCLK 0x00A3 +#define WM_KEYFIRST 0x0100 +#define WM_KEYDOWN 0x0100 +#define WM_KEYUP 0x0101 +#define WM_CHAR 0x0102 +#define WM_DEADCHAR 0x0103 +#define WM_SYSKEYDOWN 0x0104 +#define WM_SYSKEYUP 0x0105 +#define WM_SYSCHAR 0x0106 +#define WM_SYSDEADCHAR 0x0107 +#define WM_KEYLAST 0x0108 +#define WM_INITDIALOG 0x0110 +#define WM_COMMAND 0x0111 +#define WM_SYSCOMMAND 0x0112 +#define WM_TIMER 0x0113 +#define WM_HSCROLL 0x0114 +#define WM_VSCROLL 0x0115 +#define WM_INITMENU 0x0116 +#define WM_INITMENUPOPUP 0x0117 +#define WM_MENUSELECT 0x011F +#define WM_MENUCHAR 0x0120 +#define WM_ENTERIDLE 0x0121 +#define WM_CTLCOLORMSGBOX 0x0132 +#define WM_CTLCOLOREDIT 0x0133 +#define WM_CTLCOLORLISTBOX 0x0134 +#define WM_CTLCOLORBTN 0x0135 +#define WM_CTLCOLORDLG 0x0136 +#define WM_CTLCOLORSCROLLBAR 0x0137 +#define WM_CTLCOLORSTATIC 0x0138 +#define WM_MOUSEFIRST 0x0200 +#define WM_MOUSEMOVE 0x0200 +#define WM_LBUTTONDOWN 0x0201 +#define WM_LBUTTONUP 0x0202 +#define WM_LBUTTONDBLCLK 0x0203 +#define WM_RBUTTONDOWN 0x0204 +#define WM_RBUTTONUP 0x0205 +#define WM_RBUTTONDBLCLK 0x0206 +#define WM_MBUTTONDOWN 0x0207 +#define WM_MBUTTONUP 0x0208 +#define WM_MBUTTONDBLCLK 0x0209 +#define WM_MOUSEWHEEL 0x020A +#define WM_MOUSELAST 0x020A +#define WM_PARENTNOTIFY 0x0210 +#define WM_ENTERMENULOOP 0x0211 +#define WM_EXITMENULOOP 0x0212 +#define WM_SIZING 0x0214 +#define WM_CAPTURECHANGED 0x0215 +#define WM_MOVING 0x0216 +#define WM_MDICREATE 0x0220 +#define WM_MDIDESTROY 0x0221 +#define WM_MDIACTIVATE 0x0222 +#define WM_MDIRESTORE 0x0223 +#define WM_MDINEXT 0x0224 +#define WM_MDIMAXIMIZE 0x0225 +#define WM_MDITILE 0x0226 +#define WM_MDICASCADE 0x0227 +#define WM_MDIICONARRANGE 0x0228 +#define WM_MDIGETACTIVE 0x0229 +#define WM_MDISETMENU 0x0230 +#define WM_ENTERSIZEMOVE 0x0231 +#define WM_EXITSIZEMOVE 0x0232 +#define WM_DROPFILES 0x0233 +#define WM_MOUSEHOVER 0x02A1 +#define WM_MOUSELEAVE 0x02A3 +#define WM_CUT 0x0300 +#define WM_COPY 0x0301 +#define WM_PASTE 0x0302 +#define WM_CLEAR 0x0303 +#define WM_UNDO 0x0304 +#define WM_RENDERFORMAT 0x0305 +#define WM_DESTROYCLIPBOARD 0x0307 +#define WM_PAINTCLIPBOARD 0x0309 +#define WM_QUERYNEWPALETTE 0x030F +#define WM_PALETTECHANGED 0x0311 +#define WM_HOTKEY 0x0312 +#define WM_PRINT 0x0317 +#define WM_PRINTCLIENT 0x0318 +#define WM_APPCOMMAND 0x0319 +#define WM_THEMECHANGED 0x031A +#define WM_USER 0x0400 +#define WM_APP 0x8000 + +/* MFC private messages */ +#define WM_QUERYAFXWNDPROC 0x0360 +#define WM_SIZEPARENT 0x0361 +#define WM_SETMESSAGESTRING 0x0362 +#define WM_IDLEUPDATECMDUI 0x0363 +#define WM_INITIALUPDATE 0x0364 +#define WM_COMMANDHELP 0x0365 +#define WM_HELPHITTEST 0x0366 +#define WM_EXITHELPMODE 0x0367 +#define WM_RECALCPARENT 0x0368 +#define WM_SIZECHILD 0x0369 +#define WM_KICKIDLE 0x036A +#define WM_QUERYCENTERWND 0x036B +#define WM_DISABLEMODAL 0x036C +#define WM_FLOATSTATUS 0x036D +#define WM_ACTIVATETOPLEVEL 0x036E + +/* edit control messages and notifications */ +#define EM_GETSEL 0x00B0 +#define EM_SETSEL 0x00B1 +#define EM_GETRECT 0x00B2 +#define EM_SETRECT 0x00B3 +#define EM_SETRECTNP 0x00B4 +#define EM_SCROLL 0x00B5 +#define EM_LINESCROLL 0x00B6 +#define EM_SCROLLCARET 0x00B7 +#define EM_GETMODIFY 0x00B8 +#define EM_SETMODIFY 0x00B9 +#define EM_GETLINECOUNT 0x00BA +#define EM_LINEINDEX 0x00BB +#define EM_SETHANDLE 0x00BC +#define EM_GETHANDLE 0x00BD +#define EM_GETTHUMB 0x00BE +#define EM_LINELENGTH 0x00C1 +#define EM_REPLACESEL 0x00C2 +#define EM_GETLINE 0x00C4 +#define EM_LIMITTEXT 0x00C5 +#define EM_SETLIMITTEXT EM_LIMITTEXT +#define EM_CANUNDO 0x00C6 +#define EM_UNDO 0x00C7 +#define EM_FMTLINES 0x00C8 +#define EM_LINEFROMCHAR 0x00C9 +#define EM_SETTABSTOPS 0x00CB +#define EM_SETPASSWORDCHAR 0x00CC +#define EM_EMPTYUNDOBUFFER 0x00CD +#define EM_GETFIRSTVISIBLELINE 0x00CE +#define EM_SETREADONLY 0x00CF +#define EM_SETWORDBREAKPROC 0x00D0 +#define EM_GETWORDBREAKPROC 0x00D1 +#define EM_GETPASSWORDCHAR 0x00D2 +#define EM_SETMARGINS 0x00D3 +#define EM_GETMARGINS 0x00D4 +#define EM_GETLIMITTEXT 0x00D5 +#define EM_POSFROMCHAR 0x00D6 +#define EM_CHARFROMPOS 0x00D7 +#define EC_LEFTMARGIN 0x0001 +#define EC_RIGHTMARGIN 0x0002 +#define EC_USEFONTINFO 0xffff + +#define EN_SETFOCUS 0x0100 +#define EN_KILLFOCUS 0x0200 +#define EN_CHANGE 0x0300 +#define EN_UPDATE 0x0400 +#define EN_ERRSPACE 0x0500 +#define EN_MAXTEXT 0x0501 +#define EN_HSCROLL 0x0601 +#define EN_VSCROLL 0x0602 +#define EN_MSGFILTER 0x0700 +#define EN_REQUESTRESIZE 0x0701 +#define EN_SELCHANGE 0x0702 +#define EN_DROPFILES 0x0703 +#define EN_PROTECTED 0x0704 +#define EN_LINK 0x070b + +/* rich edit messages */ +#define EM_CANPASTE (WM_USER + 50) +#define EM_DISPLAYBAND (WM_USER + 51) +#define EM_EXGETSEL (WM_USER + 52) +#define EM_EXLIMITTEXT (WM_USER + 53) +#define EM_EXLINEFROMCHAR (WM_USER + 54) +#define EM_EXSETSEL (WM_USER + 55) +#define EM_FINDTEXT (WM_USER + 56) +#define EM_FORMATRANGE (WM_USER + 57) +#define EM_GETCHARFORMAT (WM_USER + 58) +#define EM_GETEVENTMASK (WM_USER + 59) +#define EM_GETOLEINTERFACE (WM_USER + 60) +#define EM_GETPARAFORMAT (WM_USER + 61) +#define EM_GETSELTEXT (WM_USER + 62) +#define EM_HIDESELECTION (WM_USER + 63) +#define EM_PASTESPECIAL (WM_USER + 64) +#define EM_REQUESTRESIZE (WM_USER + 65) +#define EM_SELECTIONTYPE (WM_USER + 66) +#define EM_SETBKGNDCOLOR (WM_USER + 67) +#define EM_SETCHARFORMAT (WM_USER + 68) +#define EM_SETEVENTMASK (WM_USER + 69) +#define EM_SETOLECALLBACK (WM_USER + 70) +#define EM_SETPARAFORMAT (WM_USER + 71) +#define EM_SETTARGETDEVICE (WM_USER + 72) +#define EM_STREAMIN (WM_USER + 73) +#define EM_STREAMOUT (WM_USER + 74) +#define EM_GETTEXTRANGE (WM_USER + 75) +#define EM_FINDWORDBREAK (WM_USER + 76) +#define EM_SETOPTIONS (WM_USER + 77) +#define EM_GETOPTIONS (WM_USER + 78) +#define EM_FINDTEXTEX (WM_USER + 79) +#define EM_SETUNDOLIMIT (WM_USER + 82) +#define EM_REDO (WM_USER + 84) +#define EM_CANREDO (WM_USER + 85) +#define EM_SETTEXTMODE (WM_USER + 89) +#define EM_GETTEXTMODE (WM_USER + 90) +#define EM_AUTOURLDETECT (WM_USER + 91) +#define EM_GETTEXTEX (WM_USER + 94) +#define EM_GETTEXTLENGTHEX (WM_USER + 95) +#define EM_SETTEXTEX (WM_USER + 97) +#define ENM_NONE 0x00000000 +#define ENM_CHANGE 0x00000001 +#define ENM_UPDATE 0x00000002 +#define ENM_SCROLL 0x00000004 +#define ENM_KEYEVENTS 0x00010000 +#define ENM_MOUSEEVENTS 0x00020000 +#define ENM_REQUESTRESIZE 0x00040000 +#define ENM_SELCHANGE 0x00080000 +#define ENM_LINK 0x04000000 +#define SF_TEXT 0x0001 +#define SF_RTF 0x0002 +#define SFF_SELECTION 0x8000 +#define CFM_BOLD 0x00000001 +#define CFM_ITALIC 0x00000002 +#define CFM_UNDERLINE 0x00000004 +#define CFM_STRIKEOUT 0x00000008 +#define CFM_PROTECTED 0x00000010 +#define CFM_LINK 0x00000020 +#define CFM_SIZE 0x80000000 +#define CFM_COLOR 0x40000000 +#define CFM_FACE 0x20000000 +#define CFM_OFFSET 0x10000000 +#define CFM_CHARSET 0x08000000 +#define CFE_BOLD 0x0001 +#define CFE_ITALIC 0x0002 +#define CFE_UNDERLINE 0x0004 +#define CFE_STRIKEOUT 0x0008 +#define CFE_PROTECTED 0x0010 +#define CFE_LINK 0x0020 +#define CFE_AUTOCOLOR 0x40000000 +#define SCF_SELECTION 0x0001 +#define SCF_WORD 0x0002 +#define SCF_DEFAULT 0x0000 +#define SCF_ALL 0x0004 + +/* button messages and notifications */ +#define BM_GETCHECK 0x00F0 +#define BM_SETCHECK 0x00F1 +#define BM_GETSTATE 0x00F2 +#define BM_SETSTATE 0x00F3 +#define BM_SETSTYLE 0x00F4 +#define BM_CLICK 0x00F5 +#define BM_GETIMAGE 0x00F6 +#define BM_SETIMAGE 0x00F7 +#define BST_UNCHECKED 0x0000 +#define BST_CHECKED 0x0001 +#define BST_INDETERMINATE 0x0002 +#define BST_PUSHED 0x0004 +#define BST_FOCUS 0x0008 +#define BN_CLICKED 0 +#define BN_PAINT 1 +#define BN_HILITE 2 +#define BN_UNHILITE 3 +#define BN_DISABLE 4 +#define BN_DOUBLECLICKED 5 +#define BN_PUSHED BN_HILITE +#define BN_UNPUSHED BN_UNHILITE +#define BN_DBLCLK BN_DOUBLECLICKED +#define BN_SETFOCUS 6 +#define BN_KILLFOCUS 7 + +/* static messages and notifications */ +#define STM_SETICON 0x0170 +#define STM_GETICON 0x0171 +#define STM_SETIMAGE 0x0172 +#define STM_GETIMAGE 0x0173 +#define STN_CLICKED 0 +#define STN_DBLCLK 1 +#define STN_ENABLE 2 +#define STN_DISABLE 3 +#define IMAGE_BITMAP 0 +#define IMAGE_ICON 1 +#define IMAGE_CURSOR 2 +#define IMAGE_ENHMETAFILE 3 +#define LR_DEFAULTCOLOR 0x0000 +#define LR_MONOCHROME 0x0001 +#define LR_LOADFROMFILE 0x0010 +#define LR_LOADTRANSPARENT 0x0020 +#define LR_DEFAULTSIZE 0x0040 +#define LR_LOADMAP3DCOLORS 0x1000 +#define LR_CREATEDIBSECTION 0x2000 +#define LR_SHARED 0x8000 + +/* combo box messages and notifications */ +#define CB_OKAY 0 +#define CB_ERR (-1) +#define CB_ERRSPACE (-2) +#define CBN_ERRSPACE (-1) +#define CBN_SELCHANGE 1 +#define CBN_DBLCLK 2 +#define CBN_SETFOCUS 3 +#define CBN_KILLFOCUS 4 +#define CBN_EDITCHANGE 5 +#define CBN_EDITUPDATE 6 +#define CBN_DROPDOWN 7 +#define CBN_CLOSEUP 8 +#define CBN_SELENDOK 9 +#define CBN_SELENDCANCEL 10 +#define CB_GETEDITSEL 0x0140 +#define CB_LIMITTEXT 0x0141 +#define CB_SETEDITSEL 0x0142 +#define CB_ADDSTRING 0x0143 +#define CB_DELETESTRING 0x0144 +#define CB_DIR 0x0145 +#define CB_GETCOUNT 0x0146 +#define CB_GETCURSEL 0x0147 +#define CB_GETLBTEXT 0x0148 +#define CB_GETLBTEXTLEN 0x0149 +#define CB_INSERTSTRING 0x014A +#define CB_RESETCONTENT 0x014B +#define CB_FINDSTRING 0x014C +#define CB_SELECTSTRING 0x014D +#define CB_SETCURSEL 0x014E +#define CB_SHOWDROPDOWN 0x014F +#define CB_GETITEMDATA 0x0150 +#define CB_SETITEMDATA 0x0151 +#define CB_GETDROPPEDCONTROLRECT 0x0152 +#define CB_SETITEMHEIGHT 0x0153 +#define CB_GETITEMHEIGHT 0x0154 +#define CB_SETEXTENDEDUI 0x0155 +#define CB_GETEXTENDEDUI 0x0156 +#define CB_GETDROPPEDSTATE 0x0157 +#define CB_FINDSTRINGEXACT 0x0158 +#define CB_SETLOCALE 0x0159 +#define CB_GETLOCALE 0x015A +#define CB_GETTOPINDEX 0x015B +#define CB_SETTOPINDEX 0x015C +#define CB_GETHORIZONTALEXTENT 0x015D +#define CB_SETHORIZONTALEXTENT 0x015E +#define CB_GETDROPPEDWIDTH 0x015F +#define CB_SETDROPPEDWIDTH 0x0160 +#define CB_INITSTORAGE 0x0161 + +/* list box messages and notifications */ +#define LB_OKAY 0 +#define LB_ERR (-1) +#define LB_ERRSPACE (-2) +#define LBN_ERRSPACE (-2) +#define LBN_SELCHANGE 1 +#define LBN_DBLCLK 2 +#define LBN_SELCANCEL 3 +#define LBN_SETFOCUS 4 +#define LBN_KILLFOCUS 5 +#define LB_ADDSTRING 0x0180 +#define LB_INSERTSTRING 0x0181 +#define LB_DELETESTRING 0x0182 +#define LB_SELITEMRANGEEX 0x0183 +#define LB_RESETCONTENT 0x0184 +#define LB_SETSEL 0x0185 +#define LB_SETCURSEL 0x0186 +#define LB_GETSEL 0x0187 +#define LB_GETCURSEL 0x0188 +#define LB_GETTEXT 0x0189 +#define LB_GETTEXTLEN 0x018A +#define LB_GETCOUNT 0x018B +#define LB_SELECTSTRING 0x018C +#define LB_DIR 0x018D +#define LB_GETTOPINDEX 0x018E +#define LB_FINDSTRING 0x018F +#define LB_GETSELCOUNT 0x0190 +#define LB_GETSELITEMS 0x0191 +#define LB_SETTABSTOPS 0x0192 +#define LB_GETHORIZONTALEXTENT 0x0193 +#define LB_SETHORIZONTALEXTENT 0x0194 +#define LB_SETCOLUMNWIDTH 0x0195 +#define LB_ADDFILE 0x0196 +#define LB_SETTOPINDEX 0x0197 +#define LB_GETITEMRECT 0x0198 +#define LB_GETITEMDATA 0x0199 +#define LB_SETITEMDATA 0x019A +#define LB_SELITEMRANGE 0x019B +#define LB_SETANCHORINDEX 0x019C +#define LB_GETANCHORINDEX 0x019D +#define LB_SETCARETINDEX 0x019E +#define LB_GETCARETINDEX 0x019F +#define LB_SETITEMHEIGHT 0x01A0 +#define LB_GETITEMHEIGHT 0x01A1 +#define LB_FINDSTRINGEXACT 0x01A2 + +/* scroll bar */ +#define SB_LINEUP 0 +#define SB_LINELEFT 0 +#define SB_LINEDOWN 1 +#define SB_LINERIGHT 1 +#define SB_PAGEUP 2 +#define SB_PAGELEFT 2 +#define SB_PAGEDOWN 3 +#define SB_PAGERIGHT 3 +#define SB_THUMBPOSITION 4 +#define SB_THUMBTRACK 5 +#define SB_TOP 6 +#define SB_LEFT 6 +#define SB_BOTTOM 7 +#define SB_RIGHT 7 +#define SB_ENDSCROLL 8 +#define SB_HORZ 0 +#define SB_VERT 1 +#define SB_CTL 2 +#define SB_BOTH 3 +#define SIF_RANGE 0x0001 +#define SIF_PAGE 0x0002 +#define SIF_POS 0x0004 +#define SIF_DISABLENOSCROLL 0x0008 +#define SIF_TRACKPOS 0x0010 +#define SIF_ALL (SIF_RANGE | SIF_PAGE | SIF_POS | SIF_TRACKPOS) +#define ESB_ENABLE_BOTH 0x0000 +#define ESB_DISABLE_BOTH 0x0003 +#define SBM_SETPOS 0x00E0 +#define SBM_GETPOS 0x00E1 +#define SBM_SETRANGE 0x00E2 +#define SBM_GETRANGE 0x00E3 + +/* trackbar */ +#define TBM_GETPOS (WM_USER) +#define TBM_GETRANGEMIN (WM_USER + 1) +#define TBM_GETRANGEMAX (WM_USER + 2) +#define TBM_GETTIC (WM_USER + 3) +#define TBM_SETTIC (WM_USER + 4) +#define TBM_SETPOS (WM_USER + 5) +#define TBM_SETRANGE (WM_USER + 6) +#define TBM_SETRANGEMIN (WM_USER + 7) +#define TBM_SETRANGEMAX (WM_USER + 8) +#define TBM_CLEARTICS (WM_USER + 9) +#define TBM_SETSEL (WM_USER + 10) +#define TBM_SETSELSTART (WM_USER + 11) +#define TBM_SETSELEND (WM_USER + 12) +#define TBM_GETPTICS (WM_USER + 14) +#define TBM_GETTICPOS (WM_USER + 15) +#define TBM_GETNUMTICS (WM_USER + 16) +#define TBM_GETSELSTART (WM_USER + 17) +#define TBM_GETSELEND (WM_USER + 18) +#define TBM_CLEARSEL (WM_USER + 19) +#define TBM_SETTICFREQ (WM_USER + 20) +#define TBM_SETPAGESIZE (WM_USER + 21) +#define TBM_GETPAGESIZE (WM_USER + 22) +#define TBM_SETLINESIZE (WM_USER + 23) +#define TBM_GETLINESIZE (WM_USER + 24) +#define TB_LINEUP 0 +#define TB_LINEDOWN 1 +#define TB_PAGEUP 2 +#define TB_PAGEDOWN 3 +#define TB_THUMBPOSITION 4 +#define TB_THUMBTRACK 5 +#define TB_TOP 6 +#define TB_BOTTOM 7 +#define TB_ENDTRACK 8 + +/* progress bar */ +#define PBM_SETRANGE (WM_USER + 1) +#define PBM_SETPOS (WM_USER + 2) +#define PBM_DELTAPOS (WM_USER + 3) +#define PBM_SETSTEP (WM_USER + 4) +#define PBM_STEPIT (WM_USER + 5) +#define PBM_SETRANGE32 (WM_USER + 6) +#define PBM_GETRANGE (WM_USER + 7) +#define PBM_GETPOS (WM_USER + 8) +#define PBM_SETBARCOLOR (WM_USER + 9) +#define PBM_SETMARQUEE (WM_USER + 10) +#define CCM_FIRST 0x2000 +#define CCM_SETBKCOLOR (CCM_FIRST + 1) +#define PBM_SETBKCOLOR CCM_SETBKCOLOR + +/* up-down */ +#define UDM_SETRANGE (WM_USER + 101) +#define UDM_GETRANGE (WM_USER + 102) +#define UDM_SETPOS (WM_USER + 103) +#define UDM_GETPOS (WM_USER + 104) +#define UDM_SETBUDDY (WM_USER + 105) +#define UDM_GETBUDDY (WM_USER + 106) +#define UDM_SETACCEL (WM_USER + 107) +#define UDM_GETACCEL (WM_USER + 108) +#define UDM_SETBASE (WM_USER + 109) +#define UDM_GETBASE (WM_USER + 110) +#define UDM_SETRANGE32 (WM_USER + 111) +#define UDM_GETRANGE32 (WM_USER + 112) +#define UDM_SETPOS32 (WM_USER + 113) +#define UDM_GETPOS32 (WM_USER + 114) +#define UD_MAXVAL 0x7fff +#define UD_MINVAL (-UD_MAXVAL) + +/* WM_NOTIFY codes */ +#define NM_FIRST (0U - 0U) +#define NM_OUTOFMEMORY (NM_FIRST - 1) +#define NM_CLICK (NM_FIRST - 2) +#define NM_DBLCLK (NM_FIRST - 3) +#define NM_RETURN (NM_FIRST - 4) +#define NM_RCLICK (NM_FIRST - 5) +#define NM_RDBLCLK (NM_FIRST - 6) +#define NM_SETFOCUS (NM_FIRST - 7) +#define NM_KILLFOCUS (NM_FIRST - 8) +#define NM_CUSTOMDRAW (NM_FIRST - 12) +#define NM_HOVER (NM_FIRST - 13) +#define NM_NCHITTEST (NM_FIRST - 14) +#define NM_KEYDOWN (NM_FIRST - 15) +#define NM_RELEASEDCAPTURE (NM_FIRST - 16) +#define NM_SETCURSOR (NM_FIRST - 17) +#define NM_CHAR (NM_FIRST - 18) +#define LVN_FIRST (0U - 100U) +#define LVN_ITEMCHANGING (LVN_FIRST - 0) +#define LVN_ITEMCHANGED (LVN_FIRST - 1) +#define LVN_INSERTITEM (LVN_FIRST - 2) +#define LVN_DELETEITEM (LVN_FIRST - 3) +#define LVN_DELETEALLITEMS (LVN_FIRST - 4) +#define LVN_BEGINLABELEDIT (LVN_FIRST - 5) +#define LVN_ENDLABELEDIT (LVN_FIRST - 6) +#define LVN_COLUMNCLICK (LVN_FIRST - 8) +#define LVN_BEGINDRAG (LVN_FIRST - 9) +#define LVN_BEGINRDRAG (LVN_FIRST - 11) +#define LVN_ODCACHEHINT (LVN_FIRST - 13) +#define LVN_ITEMACTIVATE (LVN_FIRST - 14) +#define LVN_ODSTATECHANGED (LVN_FIRST - 15) +#define LVN_HOTTRACK (LVN_FIRST - 21) +#define LVN_GETDISPINFO (LVN_FIRST - 50) +#define LVN_SETDISPINFO (LVN_FIRST - 51) +#define LVN_ODFINDITEM (LVN_FIRST - 52) +#define LVN_KEYDOWN (LVN_FIRST - 55) +#define LVN_GETINFOTIP (LVN_FIRST - 57) +#define HDN_FIRST (0U - 300U) +#define HDN_ITEMCLICK (HDN_FIRST - 2) +#define TVN_FIRST (0U - 400U) +#define TVN_SELCHANGING (TVN_FIRST - 1) +#define TVN_SELCHANGED (TVN_FIRST - 2) +#define TVN_GETDISPINFO (TVN_FIRST - 3) +#define TVN_SETDISPINFO (TVN_FIRST - 4) +#define TVN_ITEMEXPANDING (TVN_FIRST - 5) +#define TVN_ITEMEXPANDED (TVN_FIRST - 6) +#define TVN_BEGINDRAG (TVN_FIRST - 7) +#define TVN_KEYDOWN (TVN_FIRST - 12) +#define TTN_FIRST (0U - 520U) +#define TTN_GETDISPINFO (TTN_FIRST - 0) +#define TTN_NEEDTEXT TTN_GETDISPINFO +#define TTN_SHOW (TTN_FIRST - 1) +#define TTN_POP (TTN_FIRST - 2) +#define TCN_FIRST (0U - 550U) +#define TCN_KEYDOWN (TCN_FIRST - 0) +#define TCN_SELCHANGE (TCN_FIRST - 1) +#define TCN_SELCHANGING (TCN_FIRST - 2) +#define UDN_FIRST (0U - 721U) +#define UDN_DELTAPOS (UDN_FIRST - 1) +#define DTN_FIRST (0U - 760U) +#define DTN_DATETIMECHANGE (DTN_FIRST + 1) +#define MCN_FIRST (0U - 750U) + +/* list view */ +#define LVM_FIRST 0x1000 +#define LVM_GETITEMCOUNT (LVM_FIRST + 4) +#define LVM_GETITEMRECT (LVM_FIRST + 14) +#define LVM_GETTOPINDEX (LVM_FIRST + 39) +#define LVM_GETITEMTEXT (LVM_FIRST + 45) +#define LVM_SETEXTENDEDLISTVIEWSTYLE (LVM_FIRST + 54) +#define LVM_GETEXTENDEDLISTVIEWSTYLE (LVM_FIRST + 55) +#define LVM_GETSUBITEMRECT (LVM_FIRST + 56) +#define LVM_SUBITEMHITTEST (LVM_FIRST + 57) +#define LVCF_FMT 0x0001 +#define LVCF_WIDTH 0x0002 +#define LVCF_TEXT 0x0004 +#define LVCF_SUBITEM 0x0008 +#define LVCF_IMAGE 0x0010 +#define LVCF_ORDER 0x0020 +#define LVCFMT_LEFT 0x0000 +#define LVCFMT_RIGHT 0x0001 +#define LVCFMT_CENTER 0x0002 +#define LVCFMT_JUSTIFYMASK 0x0003 +#define LVIF_TEXT 0x0001 +#define LVIF_IMAGE 0x0002 +#define LVIF_PARAM 0x0004 +#define LVIF_STATE 0x0008 +#define LVIF_INDENT 0x0010 +#define LVIF_NORECOMPUTE 0x0800 +#define LVIS_FOCUSED 0x0001 +#define LVIS_SELECTED 0x0002 +#define LVIS_CUT 0x0004 +#define LVIS_DROPHILITED 0x0008 +#define LVIS_OVERLAYMASK 0x0F00 +#define LVIS_STATEIMAGEMASK 0xF000 +#define LVNI_ALL 0x0000 +#define LVNI_FOCUSED 0x0001 +#define LVNI_SELECTED 0x0002 +#define LVNI_CUT 0x0004 +#define LVNI_DROPHILITED 0x0008 +#define LVNI_ABOVE 0x0100 +#define LVNI_BELOW 0x0200 +#define LVNI_TOLEFT 0x0400 +#define LVNI_TORIGHT 0x0800 +#define LVSCW_AUTOSIZE (-1) +#define LVSCW_AUTOSIZE_USEHEADER (-2) +#define LVIR_BOUNDS 0 +#define LVIR_ICON 1 +#define LVIR_LABEL 2 +#define LVIR_SELECTBOUNDS 3 +#define LVHT_NOWHERE 0x0001 +#define LVHT_ONITEMICON 0x0002 +#define LVHT_ONITEMLABEL 0x0004 +#define LVHT_ONITEMSTATEICON 0x0008 +#define LVHT_ONITEM (LVHT_ONITEMICON | LVHT_ONITEMLABEL | LVHT_ONITEMSTATEICON) +#define LVSIL_NORMAL 0 +#define LVSIL_SMALL 1 +#define LVSIL_STATE 2 +#define LVFI_PARAM 0x0001 +#define LVFI_STRING 0x0002 +#define LVFI_PARTIAL 0x0008 +#define LVFI_WRAP 0x0020 +#define LVFI_NEARESTXY 0x0040 +#define LVA_DEFAULT 0x0000 + +/* tree view */ +#define TVI_ROOT ((HTREEITEM)(intptr_t)-0x10000) +#define TVI_FIRST ((HTREEITEM)(intptr_t)-0x0FFFF) +#define TVI_LAST ((HTREEITEM)(intptr_t)-0x0FFFE) +#define TVI_SORT ((HTREEITEM)(intptr_t)-0x0FFFD) +#define TVIF_TEXT 0x0001 +#define TVIF_IMAGE 0x0002 +#define TVIF_PARAM 0x0004 +#define TVIF_STATE 0x0008 +#define TVIF_HANDLE 0x0010 +#define TVIF_SELECTEDIMAGE 0x0020 +#define TVIF_CHILDREN 0x0040 +#define TVIS_SELECTED 0x0002 +#define TVIS_BOLD 0x0010 +#define TVIS_EXPANDED 0x0020 +#define TVE_COLLAPSE 0x0001 +#define TVE_EXPAND 0x0002 +#define TVE_TOGGLE 0x0003 +#define TVGN_ROOT 0x0000 +#define TVGN_NEXT 0x0001 +#define TVGN_PREVIOUS 0x0002 +#define TVGN_PARENT 0x0003 +#define TVGN_CHILD 0x0004 +#define TVGN_FIRSTVISIBLE 0x0005 +#define TVGN_NEXTVISIBLE 0x0006 +#define TVGN_PREVIOUSVISIBLE 0x0007 +#define TVGN_DROPHILITE 0x0008 +#define TVGN_CARET 0x0009 + +/* tab control */ +#define TCIF_TEXT 0x0001 +#define TCIF_IMAGE 0x0002 +#define TCIF_RTLREADING 0x0004 +#define TCIF_PARAM 0x0008 +#define TCIF_STATE 0x0010 + +/* virtual keys */ +#define VK_LBUTTON 0x01 +#define VK_RBUTTON 0x02 +#define VK_CANCEL 0x03 +#define VK_MBUTTON 0x04 +#define VK_BACK 0x08 +#define VK_TAB 0x09 +#define VK_CLEAR 0x0C +#define VK_RETURN 0x0D +#define VK_SHIFT 0x10 +#define VK_CONTROL 0x11 +#define VK_MENU 0x12 +#define VK_PAUSE 0x13 +#define VK_CAPITAL 0x14 +#define VK_ESCAPE 0x1B +#define VK_SPACE 0x20 +#define VK_PRIOR 0x21 +#define VK_NEXT 0x22 +#define VK_END 0x23 +#define VK_HOME 0x24 +#define VK_LEFT 0x25 +#define VK_UP 0x26 +#define VK_RIGHT 0x27 +#define VK_DOWN 0x28 +#define VK_SELECT 0x29 +#define VK_PRINT 0x2A +#define VK_EXECUTE 0x2B +#define VK_SNAPSHOT 0x2C +#define VK_INSERT 0x2D +#define VK_DELETE 0x2E +#define VK_HELP 0x2F +#define VK_LWIN 0x5B +#define VK_RWIN 0x5C +#define VK_APPS 0x5D +#define VK_NUMPAD0 0x60 +#define VK_NUMPAD1 0x61 +#define VK_NUMPAD2 0x62 +#define VK_NUMPAD3 0x63 +#define VK_NUMPAD4 0x64 +#define VK_NUMPAD5 0x65 +#define VK_NUMPAD6 0x66 +#define VK_NUMPAD7 0x67 +#define VK_NUMPAD8 0x68 +#define VK_NUMPAD9 0x69 +#define VK_MULTIPLY 0x6A +#define VK_ADD 0x6B +#define VK_SEPARATOR 0x6C +#define VK_SUBTRACT 0x6D +#define VK_DECIMAL 0x6E +#define VK_DIVIDE 0x6F +#define VK_F1 0x70 +#define VK_F2 0x71 +#define VK_F3 0x72 +#define VK_F4 0x73 +#define VK_F5 0x74 +#define VK_F6 0x75 +#define VK_F7 0x76 +#define VK_F8 0x77 +#define VK_F9 0x78 +#define VK_F10 0x79 +#define VK_F11 0x7A +#define VK_F12 0x7B +#define VK_F13 0x7C +#define VK_F14 0x7D +#define VK_F15 0x7E +#define VK_F16 0x7F +#define VK_NUMLOCK 0x90 +#define VK_SCROLL 0x91 +#define VK_LSHIFT 0xA0 +#define VK_RSHIFT 0xA1 +#define VK_LCONTROL 0xA2 +#define VK_RCONTROL 0xA3 +#define VK_LMENU 0xA4 +#define VK_RMENU 0xA5 +#define VK_OEM_1 0xBA +#define VK_OEM_PLUS 0xBB +#define VK_OEM_COMMA 0xBC +#define VK_OEM_MINUS 0xBD +#define VK_OEM_PERIOD 0xBE +#define VK_OEM_2 0xBF +#define VK_OEM_3 0xC0 +#define VK_OEM_4 0xDB +#define VK_OEM_5 0xDC +#define VK_OEM_6 0xDD +#define VK_OEM_7 0xDE + +/* accelerators */ +#define FVIRTKEY 0x01 +#define FNOINVERT 0x02 +#define FSHIFT 0x04 +#define FCONTROL 0x08 +#define FALT 0x10 + +/* menus */ +#define MF_INSERT 0x00000000L +#define MF_CHANGE 0x00000080L +#define MF_APPEND 0x00000100L +#define MF_DELETE 0x00000200L +#define MF_REMOVE 0x00001000L +#define MF_BYCOMMAND 0x00000000L +#define MF_BYPOSITION 0x00000400L +#define MF_SEPARATOR 0x00000800L +#define MF_ENABLED 0x00000000L +#define MF_GRAYED 0x00000001L +#define MF_DISABLED 0x00000002L +#define MF_UNCHECKED 0x00000000L +#define MF_CHECKED 0x00000008L +#define MF_USECHECKBITMAPS 0x00000200L +#define MF_STRING 0x00000000L +#define MF_BITMAP 0x00000004L +#define MF_OWNERDRAW 0x00000100L +#define MF_POPUP 0x00000010L +#define MF_MENUBARBREAK 0x00000020L +#define MF_MENUBREAK 0x00000040L +#define MF_UNHILITE 0x00000000L +#define MF_HILITE 0x00000080L +#define MF_DEFAULT 0x00001000L +#define MF_SYSMENU 0x00002000L +#define MF_HELP 0x00004000L +#define MF_RIGHTJUSTIFY 0x00004000L +#define MF_MOUSESELECT 0x00008000L +#define MF_END 0x00000080L +#define MFT_STRING MF_STRING +#define MFT_SEPARATOR MF_SEPARATOR +#define MFT_RADIOCHECK 0x00000200L +#define MFS_GRAYED 0x00000003L +#define MFS_DISABLED MFS_GRAYED +#define MFS_CHECKED MF_CHECKED +#define MFS_ENABLED MF_ENABLED +#define MFS_DEFAULT MF_DEFAULT +#define TPM_LEFTBUTTON 0x0000L +#define TPM_RIGHTBUTTON 0x0002L +#define TPM_LEFTALIGN 0x0000L +#define TPM_CENTERALIGN 0x0004L +#define TPM_RIGHTALIGN 0x0008L +#define TPM_TOPALIGN 0x0000L +#define TPM_VCENTERALIGN 0x0010L +#define TPM_BOTTOMALIGN 0x0020L +#define TPM_NONOTIFY 0x0080L +#define TPM_RETURNCMD 0x0100L + +/* message boxes */ +#define MB_OK 0x00000000L +#define MB_OKCANCEL 0x00000001L +#define MB_ABORTRETRYIGNORE 0x00000002L +#define MB_YESNOCANCEL 0x00000003L +#define MB_YESNO 0x00000004L +#define MB_RETRYCANCEL 0x00000005L +#define MB_CANCELTRYCONTINUE 0x00000006L +#define MB_ICONHAND 0x00000010L +#define MB_ICONQUESTION 0x00000020L +#define MB_ICONEXCLAMATION 0x00000030L +#define MB_ICONASTERISK 0x00000040L +#define MB_USERICON 0x00000080L +#define MB_ICONWARNING MB_ICONEXCLAMATION +#define MB_ICONERROR MB_ICONHAND +#define MB_ICONINFORMATION MB_ICONASTERISK +#define MB_ICONSTOP MB_ICONHAND +#define MB_DEFBUTTON1 0x00000000L +#define MB_DEFBUTTON2 0x00000100L +#define MB_DEFBUTTON3 0x00000200L +#define MB_DEFBUTTON4 0x00000300L +#define MB_APPLMODAL 0x00000000L +#define MB_SYSTEMMODAL 0x00001000L +#define MB_TASKMODAL 0x00002000L +#define MB_HELP 0x00004000L +#define MB_NOFOCUS 0x00008000L +#define MB_SETFOREGROUND 0x00010000L +#define MB_DEFAULT_DESKTOP_ONLY 0x00020000L +#define MB_TOPMOST 0x00040000L +#define MB_RIGHT 0x00080000L +#define MB_RTLREADING 0x00100000L +#define MB_TYPEMASK 0x0000000FL +#define MB_ICONMASK 0x000000F0L +#define MB_DEFMASK 0x00000F00L +#define MB_MODEMASK 0x00003000L +#define MB_MISCMASK 0x0000C000L +#define IDOK 1 +#define IDCANCEL 2 +#define IDABORT 3 +#define IDRETRY 4 +#define IDIGNORE 5 +#define IDYES 6 +#define IDNO 7 +#define IDCLOSE 8 +#define IDHELP 9 +#define IDTRYAGAIN 10 +#define IDCONTINUE 11 + +/* ShowWindow / SetWindowPos */ +#define SW_HIDE 0 +#define SW_SHOWNORMAL 1 +#define SW_NORMAL 1 +#define SW_SHOWMINIMIZED 2 +#define SW_SHOWMAXIMIZED 3 +#define SW_MAXIMIZE 3 +#define SW_SHOWNOACTIVATE 4 +#define SW_SHOW 5 +#define SW_MINIMIZE 6 +#define SW_SHOWMINNOACTIVE 7 +#define SW_SHOWNA 8 +#define SW_RESTORE 9 +#define SW_SHOWDEFAULT 10 +#define SW_FORCEMINIMIZE 11 +#define SWP_NOSIZE 0x0001 +#define SWP_NOMOVE 0x0002 +#define SWP_NOZORDER 0x0004 +#define SWP_NOREDRAW 0x0008 +#define SWP_NOACTIVATE 0x0010 +#define SWP_FRAMECHANGED 0x0020 +#define SWP_SHOWWINDOW 0x0040 +#define SWP_HIDEWINDOW 0x0080 +#define SWP_NOCOPYBITS 0x0100 +#define SWP_NOOWNERZORDER 0x0200 +#define SWP_NOSENDCHANGING 0x0400 +#define SWP_DRAWFRAME SWP_FRAMECHANGED +#define SWP_NOREPOSITION SWP_NOOWNERZORDER +#define SIZE_RESTORED 0 +#define SIZE_MINIMIZED 1 +#define SIZE_MAXIMIZED 2 +#define SIZE_MAXSHOW 3 +#define SIZE_MAXHIDE 4 +#define WA_INACTIVE 0 +#define WA_ACTIVE 1 +#define WA_CLICKACTIVE 2 + +#define GWL_WNDPROC (-4) +#define GWL_HINSTANCE (-6) +#define GWL_HWNDPARENT (-8) +#define GWL_STYLE (-16) +#define GWL_EXSTYLE (-20) +#define GWL_USERDATA (-21) +#define GWL_ID (-12) +#define GWLP_WNDPROC (-4) +#define GWLP_HINSTANCE (-6) +#define GWLP_HWNDPARENT (-8) +#define GWLP_USERDATA (-21) +#define GWLP_ID (-12) +#define GW_HWNDFIRST 0 +#define GW_HWNDLAST 1 +#define GW_HWNDNEXT 2 +#define GW_HWNDPREV 3 +#define GW_OWNER 4 +#define GW_CHILD 5 + +#define SC_SIZE 0xF000 +#define SC_MOVE 0xF010 +#define SC_MINIMIZE 0xF020 +#define SC_MAXIMIZE 0xF030 +#define SC_NEXTWINDOW 0xF040 +#define SC_PREVWINDOW 0xF050 +#define SC_CLOSE 0xF060 +#define SC_VSCROLL 0xF070 +#define SC_HSCROLL 0xF080 +#define SC_MOUSEMENU 0xF090 +#define SC_KEYMENU 0xF100 +#define SC_RESTORE 0xF120 +#define SC_TASKLIST 0xF130 +#define SC_CONTEXTHELP 0xF180 + +#define DLGC_WANTARROWS 0x0001 +#define DLGC_WANTTAB 0x0002 +#define DLGC_WANTALLKEYS 0x0004 +#define DLGC_WANTMESSAGE 0x0004 +#define DLGC_HASSETSEL 0x0008 +#define DLGC_DEFPUSHBUTTON 0x0010 +#define DLGC_UNDEFPUSHBUTTON 0x0020 +#define DLGC_RADIOBUTTON 0x0040 +#define DLGC_WANTCHARS 0x0080 +#define DLGC_STATIC 0x0100 +#define DLGC_BUTTON 0x2000 +#define DM_GETDEFID (WM_USER + 0) +#define DM_SETDEFID (WM_USER + 1) +#define DC_HASDEFID 0x534B + +#define MK_LBUTTON 0x0001 +#define MK_RBUTTON 0x0002 +#define MK_SHIFT 0x0004 +#define MK_CONTROL 0x0008 +#define MK_MBUTTON 0x0010 +#define WHEEL_DELTA 120 + +#define HTERROR (-2) +#define HTTRANSPARENT (-1) +#define HTNOWHERE 0 +#define HTCLIENT 1 +#define HTCAPTION 2 +#define HTSYSMENU 3 + +#define CTLCOLOR_MSGBOX 0 +#define CTLCOLOR_EDIT 1 +#define CTLCOLOR_LISTBOX 2 +#define CTLCOLOR_BTN 3 +#define CTLCOLOR_DLG 4 +#define CTLCOLOR_SCROLLBAR 5 +#define CTLCOLOR_STATIC 6 + +#define ODT_MENU 1 +#define ODT_LISTBOX 2 +#define ODT_COMBOBOX 3 +#define ODT_BUTTON 4 +#define ODT_STATIC 5 +#define ODA_DRAWENTIRE 0x0001 +#define ODA_SELECT 0x0002 +#define ODA_FOCUS 0x0004 +#define ODS_SELECTED 0x0001 +#define ODS_GRAYED 0x0002 +#define ODS_DISABLED 0x0004 +#define ODS_CHECKED 0x0008 +#define ODS_FOCUS 0x0010 +#define ODS_DEFAULT 0x0020 +#define ODS_HOTLIGHT 0x0040 +#define ODS_INACTIVE 0x0080 +#define ODS_NOACCEL 0x0100 +#define ODS_NOFOCUSRECT 0x0200 + +/* GDI */ +#define DT_TOP 0x00000000 +#define DT_LEFT 0x00000000 +#define DT_CENTER 0x00000001 +#define DT_RIGHT 0x00000002 +#define DT_VCENTER 0x00000004 +#define DT_BOTTOM 0x00000008 +#define DT_WORDBREAK 0x00000010 +#define DT_SINGLELINE 0x00000020 +#define DT_EXPANDTABS 0x00000040 +#define DT_TABSTOP 0x00000080 +#define DT_NOCLIP 0x00000100 +#define DT_EXTERNALLEADING 0x00000200 +#define DT_CALCRECT 0x00000400 +#define DT_NOPREFIX 0x00000800 +#define DT_INTERNAL 0x00001000 +#define DT_EDITCONTROL 0x00002000 +#define DT_PATH_ELLIPSIS 0x00004000 +#define DT_END_ELLIPSIS 0x00008000 +#define DT_MODIFYSTRING 0x00010000 +#define DT_RTLREADING 0x00020000 +#define DT_WORD_ELLIPSIS 0x00040000 +#define TA_NOUPDATECP 0 +#define TA_UPDATECP 1 +#define TA_LEFT 0 +#define TA_RIGHT 2 +#define TA_CENTER 6 +#define TA_TOP 0 +#define TA_BOTTOM 8 +#define TA_BASELINE 24 +#define TRANSPARENT 1 +#define OPAQUE 2 +#define ETO_OPAQUE 0x0002 +#define ETO_CLIPPED 0x0004 +#define PS_SOLID 0 +#define PS_DASH 1 +#define PS_DOT 2 +#define PS_DASHDOT 3 +#define PS_DASHDOTDOT 4 +#define PS_NULL 5 +#define PS_INSIDEFRAME 6 +#define PS_USERSTYLE 7 +#define PS_ALTERNATE 8 +#define PS_STYLE_MASK 0x0000000F +#define PS_ENDCAP_ROUND 0x00000000 +#define PS_ENDCAP_SQUARE 0x00000100 +#define PS_ENDCAP_FLAT 0x00000200 +#define PS_JOIN_ROUND 0x00000000 +#define PS_JOIN_BEVEL 0x00001000 +#define PS_JOIN_MITER 0x00002000 +#define PS_COSMETIC 0x00000000 +#define PS_GEOMETRIC 0x00010000 +#define BS_SOLID 0 +#define BS_NULL 1 +#define BS_HOLLOW BS_NULL +#define BS_HATCHED 2 +#define BS_PATTERN 3 +#define HS_HORIZONTAL 0 +#define HS_VERTICAL 1 +#define HS_FDIAGONAL 2 +#define HS_BDIAGONAL 3 +#define HS_CROSS 4 +#define HS_DIAGCROSS 5 +#define WHITE_BRUSH 0 +#define LTGRAY_BRUSH 1 +#define GRAY_BRUSH 2 +#define DKGRAY_BRUSH 3 +#define BLACK_BRUSH 4 +#define NULL_BRUSH 5 +#define HOLLOW_BRUSH NULL_BRUSH +#define WHITE_PEN 6 +#define BLACK_PEN 7 +#define NULL_PEN 8 +#define OEM_FIXED_FONT 10 +#define ANSI_FIXED_FONT 11 +#define ANSI_VAR_FONT 12 +#define SYSTEM_FONT 13 +#define DEVICE_DEFAULT_FONT 14 +#define DEFAULT_PALETTE 15 +#define SYSTEM_FIXED_FONT 16 +#define DEFAULT_GUI_FONT 17 +#define DC_BRUSH 18 +#define DC_PEN 19 +#define SRCCOPY 0x00CC0020UL +#define SRCPAINT 0x00EE0086UL +#define SRCAND 0x008800C6UL +#define SRCINVERT 0x00660046UL +#define SRCERASE 0x00440328UL +#define NOTSRCCOPY 0x00330008UL +#define NOTSRCERASE 0x001100A6UL +#define MERGECOPY 0x00C000CAUL +#define MERGEPAINT 0x00BB0226UL +#define PATCOPY 0x00F00021UL +#define PATPAINT 0x00FB0A09UL +#define PATINVERT 0x005A0049UL +#define DSTINVERT 0x00550009UL +#define BLACKNESS 0x00000042UL +#define WHITENESS 0x00FF0062UL +#define R2_BLACK 1 +#define R2_NOTMERGEPEN 2 +#define R2_MASKNOTPEN 3 +#define R2_NOTCOPYPEN 4 +#define R2_MASKPENNOT 5 +#define R2_NOT 6 +#define R2_XORPEN 7 +#define R2_NOTMASKPEN 8 +#define R2_MASKPEN 9 +#define R2_NOTXORPEN 10 +#define R2_NOP 11 +#define R2_MERGENOTPEN 12 +#define R2_COPYPEN 13 +#define R2_MERGEPENNOT 14 +#define R2_MERGEPEN 15 +#define R2_WHITE 16 +#define MM_TEXT 1 +#define MM_LOMETRIC 2 +#define MM_HIMETRIC 3 +#define MM_LOENGLISH 4 +#define MM_HIENGLISH 5 +#define MM_TWIPS 6 +#define MM_ISOTROPIC 7 +#define MM_ANISOTROPIC 8 +#define ALTERNATE 1 +#define WINDING 2 +#define BLACKONWHITE 1 +#define WHITEONBLACK 2 +#define COLORONCOLOR 3 +#define HALFTONE 4 +#define STRETCH_DELETESCANS COLORONCOLOR +#define LOGPIXELSX 88 +#define LOGPIXELSY 90 +#define HORZRES 8 +#define VERTRES 10 +#define BITSPIXEL 12 +#define PLANES 14 +#define NUMCOLORS 24 +#define RASTERCAPS 38 +#define HORZSIZE 4 +#define VERTSIZE 6 +#define DIB_RGB_COLORS 0 +#define DIB_PAL_COLORS 1 +#define BI_RGB 0L +#define CLR_INVALID 0xFFFFFFFF +#define CLR_NONE 0xFFFFFFFFL +#define CLR_DEFAULT 0xFF000000L +#define BDR_RAISEDOUTER 0x0001 +#define BDR_SUNKENOUTER 0x0002 +#define BDR_RAISEDINNER 0x0004 +#define BDR_SUNKENINNER 0x0008 +#define EDGE_RAISED (BDR_RAISEDOUTER | BDR_RAISEDINNER) +#define EDGE_SUNKEN (BDR_SUNKENOUTER | BDR_SUNKENINNER) +#define EDGE_ETCHED (BDR_SUNKENOUTER | BDR_RAISEDINNER) +#define EDGE_BUMP (BDR_RAISEDOUTER | BDR_SUNKENINNER) +#define BF_LEFT 0x0001 +#define BF_TOP 0x0002 +#define BF_RIGHT 0x0004 +#define BF_BOTTOM 0x0008 +#define BF_RECT (BF_LEFT | BF_TOP | BF_RIGHT | BF_BOTTOM) +#define BF_MIDDLE 0x0800 +#define BF_SOFT 0x1000 +#define BF_ADJUST 0x2000 +#define BF_FLAT 0x4000 +#define BF_MONO 0x8000 +#define DFC_CAPTION 1 +#define DFC_MENU 2 +#define DFC_SCROLL 3 +#define DFC_BUTTON 4 +#define DFCS_BUTTONCHECK 0x0000 +#define DFCS_BUTTONRADIO 0x0004 +#define DFCS_BUTTONPUSH 0x0010 +#define DFCS_INACTIVE 0x0100 +#define DFCS_PUSHED 0x0200 +#define DFCS_CHECKED 0x0400 +#define DFCS_FLAT 0x4000 +#define RGN_AND 1 +#define RGN_OR 2 +#define RGN_XOR 3 +#define RGN_DIFF 4 +#define RGN_COPY 5 +#define NULLREGION 1 +#define SIMPLEREGION 2 +#define COMPLEXREGION 3 +#define ERROR 0 + +#define FW_DONTCARE 0 +#define FW_THIN 100 +#define FW_EXTRALIGHT 200 +#define FW_LIGHT 300 +#define FW_NORMAL 400 +#define FW_REGULAR 400 +#define FW_MEDIUM 500 +#define FW_SEMIBOLD 600 +#define FW_DEMIBOLD 600 +#define FW_BOLD 700 +#define FW_EXTRABOLD 800 +#define FW_ULTRABOLD 800 +#define FW_HEAVY 900 +#define FW_BLACK 900 +#define ANSI_CHARSET 0 +#define DEFAULT_CHARSET 1 +#define SYMBOL_CHARSET 2 +#define SHIFTJIS_CHARSET 128 +#define GREEK_CHARSET 161 +#define TURKISH_CHARSET 162 +#define BALTIC_CHARSET 186 +#define RUSSIAN_CHARSET 204 +#define EASTEUROPE_CHARSET 238 +#define OEM_CHARSET 255 +#define OUT_DEFAULT_PRECIS 0 +#define OUT_STRING_PRECIS 1 +#define OUT_CHARACTER_PRECIS 2 +#define OUT_STROKE_PRECIS 3 +#define OUT_TT_PRECIS 4 +#define OUT_DEVICE_PRECIS 5 +#define OUT_RASTER_PRECIS 6 +#define OUT_TT_ONLY_PRECIS 7 +#define OUT_OUTLINE_PRECIS 8 +#define CLIP_DEFAULT_PRECIS 0 +#define CLIP_CHARACTER_PRECIS 1 +#define CLIP_STROKE_PRECIS 2 +#define DEFAULT_QUALITY 0 +#define DRAFT_QUALITY 1 +#define PROOF_QUALITY 2 +#define NONANTIALIASED_QUALITY 3 +#define ANTIALIASED_QUALITY 4 +#define CLEARTYPE_QUALITY 5 +#define DEFAULT_PITCH 0 +#define FIXED_PITCH 1 +#define VARIABLE_PITCH 2 +#define FF_DONTCARE (0 << 4) +#define FF_ROMAN (1 << 4) +#define FF_SWISS (2 << 4) +#define FF_MODERN (3 << 4) +#define FF_SCRIPT (4 << 4) +#define FF_DECORATIVE (5 << 4) +#define LF_FACESIZE 32 + +#define COLOR_SCROLLBAR 0 +#define COLOR_BACKGROUND 1 +#define COLOR_ACTIVECAPTION 2 +#define COLOR_INACTIVECAPTION 3 +#define COLOR_MENU 4 +#define COLOR_WINDOW 5 +#define COLOR_WINDOWFRAME 6 +#define COLOR_MENUTEXT 7 +#define COLOR_WINDOWTEXT 8 +#define COLOR_CAPTIONTEXT 9 +#define COLOR_ACTIVEBORDER 10 +#define COLOR_INACTIVEBORDER 11 +#define COLOR_APPWORKSPACE 12 +#define COLOR_HIGHLIGHT 13 +#define COLOR_HIGHLIGHTTEXT 14 +#define COLOR_BTNFACE 15 +#define COLOR_BTNSHADOW 16 +#define COLOR_GRAYTEXT 17 +#define COLOR_BTNTEXT 18 +#define COLOR_INACTIVECAPTIONTEXT 19 +#define COLOR_BTNHIGHLIGHT 20 +#define COLOR_3DDKSHADOW 21 +#define COLOR_3DLIGHT 22 +#define COLOR_INFOTEXT 23 +#define COLOR_INFOBK 24 +#define COLOR_HOTLIGHT 26 +#define COLOR_DESKTOP COLOR_BACKGROUND +#define COLOR_3DFACE COLOR_BTNFACE +#define COLOR_3DSHADOW COLOR_BTNSHADOW +#define COLOR_3DHIGHLIGHT COLOR_BTNHIGHLIGHT +#define COLOR_3DHILIGHT COLOR_BTNHIGHLIGHT +#define COLOR_BTNHILIGHT COLOR_BTNHIGHLIGHT + +#define SM_CXSCREEN 0 +#define SM_CYSCREEN 1 +#define SM_CXVSCROLL 2 +#define SM_CYHSCROLL 3 +#define SM_CYCAPTION 4 +#define SM_CXBORDER 5 +#define SM_CYBORDER 6 +#define SM_CXDLGFRAME 7 +#define SM_CYDLGFRAME 8 +#define SM_CYVTHUMB 9 +#define SM_CXHTHUMB 10 +#define SM_CXICON 11 +#define SM_CYICON 12 +#define SM_CXCURSOR 13 +#define SM_CYCURSOR 14 +#define SM_CYMENU 15 +#define SM_CXFULLSCREEN 16 +#define SM_CYFULLSCREEN 17 +#define SM_CYVSCROLL 20 +#define SM_CXHSCROLL 21 +#define SM_CXMIN 28 +#define SM_CYMIN 29 +#define SM_CXFRAME 32 +#define SM_CYFRAME 33 +#define SM_CXFIXEDFRAME SM_CXDLGFRAME +#define SM_CYFIXEDFRAME SM_CYDLGFRAME +#define SM_CXEDGE 45 +#define SM_CYEDGE 46 +#define SM_CXSMICON 49 +#define SM_CYSMICON 50 +#define SM_CXMAXIMIZED 61 +#define SM_CYMAXIMIZED 62 +#define SM_CXVIRTUALSCREEN 78 +#define SM_CYVIRTUALSCREEN 79 + +#define CF_TEXT 1 +#define CF_BITMAP 2 +#define CF_OEMTEXT 7 +#define CF_DIB 8 +#define CF_UNICODETEXT 13 +#define CF_HDROP 15 +#define GMEM_FIXED 0x0000 +#define GMEM_MOVEABLE 0x0002 +#define GMEM_ZEROINIT 0x0040 +#define GMEM_DDESHARE 0x2000 +#define GMEM_SHARE 0x2000 +#define GHND (GMEM_MOVEABLE | GMEM_ZEROINIT) +#define GPTR (GMEM_FIXED | GMEM_ZEROINIT) + +/* help */ +#define HELP_CONTEXT 0x0001L +#define HELP_QUIT 0x0002L +#define HELP_INDEX 0x0003L +#define HELP_CONTENTS 0x0003L +#define HELP_HELPONHELP 0x0004L +#define HELP_SETINDEX 0x0005L +#define HELP_CONTEXTPOPUP 0x0008L +#define HELP_FINDER 0x000bL +#define HELP_WM_HELP 0x000cL +#define HELP_CONTEXTMENU 0x000aL +#define HELP_KEY 0x0101L +#define HELP_PARTIALKEY 0x0105L +#define HELPINFO_WINDOW 0x0001 +#define HELPINFO_MENUITEM 0x0002 +#define HH_DISPLAY_TOPIC 0x0000 +#define HH_HELP_FINDER 0x0000 +#define HH_DISPLAY_TOC 0x0001 +#define HH_DISPLAY_INDEX 0x0002 +#define HH_DISPLAY_SEARCH 0x0003 +#define HH_KEYWORD_LOOKUP 0x000D +#define HH_DISPLAY_TEXT_POPUP 0x000E +#define HH_HELP_CONTEXT 0x000F +#define HH_TP_HELP_CONTEXTMENU 0x0010 +#define HH_TP_HELP_WM_HELP 0x0011 +#define HH_CLOSE_ALL 0x0012 +#define HH_ALINK_LOOKUP 0x0013 +#define HH_INITIALIZE 0x001C +#define HH_UNINITIALIZE 0x001D + +/* cursor / icon ids (MAKEINTRESOURCE values) */ +#define IDC_ARROW_ID 32512 +#define IDC_IBEAM_ID 32513 +#define IDC_WAIT_ID 32514 +#define IDC_CROSS_ID 32515 +#define IDC_UPARROW_ID 32516 +#define IDC_SIZE_ID 32640 +#define IDC_ICON_ID 32641 +#define IDC_SIZENWSE_ID 32642 +#define IDC_SIZENESW_ID 32643 +#define IDC_SIZEWE_ID 32644 +#define IDC_SIZENS_ID 32645 +#define IDC_SIZEALL_ID 32646 +#define IDC_NO_ID 32648 +#define IDC_HAND_ID 32649 +#define IDC_APPSTARTING_ID 32650 +#define IDC_HELP_ID 32651 +#define IDI_APPLICATION_ID 32512 +#define IDI_HAND_ID 32513 +#define IDI_QUESTION_ID 32514 +#define IDI_EXCLAMATION_ID 32515 +#define IDI_ASTERISK_ID 32516 +#define IDI_WINLOGO_ID 32517 + +/* file API */ +#define GENERIC_READ 0x80000000L +#define GENERIC_WRITE 0x40000000L +#define GENERIC_EXECUTE 0x20000000L +#define GENERIC_ALL 0x10000000L +#define FILE_READ_DATA 0x0001 +#define FILE_WRITE_DATA 0x0002 +#define FILE_APPEND_DATA 0x0004 +#define FILE_READ_ATTRIBUTES 0x0080 +#define FILE_WRITE_ATTRIBUTES 0x0100 +#define FILE_SHARE_READ 0x00000001 +#define FILE_SHARE_WRITE 0x00000002 +#define FILE_SHARE_DELETE 0x00000004 +#define CREATE_NEW 1 +#define CREATE_ALWAYS 2 +#define OPEN_EXISTING 3 +#define OPEN_ALWAYS 4 +#define TRUNCATE_EXISTING 5 +#define FILE_ATTRIBUTE_READONLY 0x00000001 +#define FILE_ATTRIBUTE_HIDDEN 0x00000002 +#define FILE_ATTRIBUTE_SYSTEM 0x00000004 +#define FILE_ATTRIBUTE_DIRECTORY 0x00000010 +#define FILE_ATTRIBUTE_ARCHIVE 0x00000020 +#define FILE_ATTRIBUTE_NORMAL 0x00000080 +#define FILE_ATTRIBUTE_TEMPORARY 0x00000100 +#define INVALID_FILE_ATTRIBUTES 0xFFFFFFFF +#define FILE_BEGIN 0 +#define FILE_CURRENT 1 +#define FILE_END 2 +#define INVALID_FILE_SIZE 0xFFFFFFFF +#define INVALID_SET_FILE_POINTER 0xFFFFFFFF + +/* errors */ +#define NO_ERROR 0L +#define ERROR_SUCCESS 0L +#define ERROR_INVALID_FUNCTION 1L +#define ERROR_FILE_NOT_FOUND 2L +#define ERROR_PATH_NOT_FOUND 3L +#define ERROR_ACCESS_DENIED 5L +#define ERROR_INVALID_HANDLE 6L +#define ERROR_NOT_ENOUGH_MEMORY 8L +#define ERROR_ALREADY_EXISTS 183L +#define ERROR_MORE_DATA 234L +#define ERROR_NO_MORE_FILES 18L +#define ERROR_CANCELLED 1223L + +/* threads and sync */ +#define WAIT_OBJECT_0 0x00000000L +#define WAIT_ABANDONED 0x00000080L +#define WAIT_TIMEOUT 258L +#define WAIT_FAILED 0xFFFFFFFF +#define STILL_ACTIVE 0x00000103L +#define THREAD_PRIORITY_LOWEST (-2) +#define THREAD_PRIORITY_BELOW_NORMAL (-1) +#define THREAD_PRIORITY_NORMAL 0 +#define THREAD_PRIORITY_HIGHEST 2 +#define THREAD_PRIORITY_ABOVE_NORMAL 1 +#define THREAD_PRIORITY_IDLE (-15) +#define THREAD_PRIORITY_TIME_CRITICAL 15 +#define CREATE_SUSPENDED 0x00000004 +#define CREATE_NO_WINDOW 0x08000000 +#define CREATE_NEW_CONSOLE 0x00000010 +#define NORMAL_PRIORITY_CLASS 0x00000020 +#define STARTF_USESHOWWINDOW 0x00000001 +#define STARTF_USESTDHANDLES 0x00000100 + +/* registry */ +#define KEY_QUERY_VALUE 0x0001 +#define KEY_SET_VALUE 0x0002 +#define KEY_CREATE_SUB_KEY 0x0004 +#define KEY_ENUMERATE_SUB_KEYS 0x0008 +#define KEY_NOTIFY 0x0010 +#define KEY_READ 0x20019 +#define KEY_WRITE 0x20006 +#define KEY_ALL_ACCESS 0xF003F +#define REG_NONE 0 +#define REG_SZ 1 +#define REG_EXPAND_SZ 2 +#define REG_BINARY 3 +#define REG_DWORD 4 +#define REG_OPTION_NON_VOLATILE 0x00000000L +#define REG_CREATED_NEW_KEY 0x00000001L +#define REG_OPENED_EXISTING_KEY 0x00000002L + +/* code pages / locale */ +#define CP_ACP 0 +#define CP_OEMCP 1 +#define CP_UTF8 65001 +#define LANG_NEUTRAL 0x00 +#define LANG_ENGLISH 0x09 +#define LANG_GERMAN 0x07 +#define LANG_FRENCH 0x0c +#define LANG_SPANISH 0x0a +#define LANG_POLISH 0x15 +#define LANG_GREEK 0x08 +#define LANG_SERBIAN 0x1a +#define SUBLANG_NEUTRAL 0x00 +#define SUBLANG_DEFAULT 0x01 +#define SUBLANG_SYS_DEFAULT 0x02 +#define SUBLANG_ENGLISH_US 0x01 +#define SUBLANG_ENGLISH_UK 0x02 +#define SUBLANG_GERMAN 0x01 +#define SUBLANG_GERMAN_SWISS 0x02 +#define SUBLANG_FRENCH 0x01 +#define SUBLANG_SPANISH 0x01 +#define SUBLANG_SPANISH_MODERN 0x03 +#define SUBLANG_SERBIAN_LATIN 0x02 +#define LOCALE_USER_DEFAULT 0x0400 +#define LOCALE_SYSTEM_DEFAULT 0x0800 +#define FORMAT_MESSAGE_ALLOCATE_BUFFER 0x00000100 +#define FORMAT_MESSAGE_IGNORE_INSERTS 0x00000200 +#define FORMAT_MESSAGE_FROM_STRING 0x00000400 +#define FORMAT_MESSAGE_FROM_HMODULE 0x00000800 +#define FORMAT_MESSAGE_FROM_SYSTEM 0x00001000 + +/* misc */ +#define PM_NOREMOVE 0x0000 +#define PM_REMOVE 0x0001 +#define PM_NOYIELD 0x0002 +#define QS_ALLINPUT 0x04FF +#define MWMO_INPUTAVAILABLE 0x0004 +#define MB_PRECOMPOSED 0x00000001 +#define WC_COMPOSITECHECK 0x00000200 +#define SPI_GETWORKAREA 0x0030 +#define SPI_GETNONCLIENTMETRICS 0x0029 +#define CW_USEDEFAULT ((int)0x80000000) +#define USER_TIMER_MINIMUM 0x0000000A +#define SEM_FAILCRITICALERRORS 0x0001 +#define SEM_NOOPENFILEERRORBOX 0x8000 diff --git a/port/mfcwx/include/mfcwx/wintypes.h b/port/mfcwx/include/mfcwx/wintypes.h new file mode 100644 index 000000000..7d7d28cf1 --- /dev/null +++ b/port/mfcwx/include/mfcwx/wintypes.h @@ -0,0 +1,643 @@ +#pragma once + +/* Win32 base types. C compatible. 32-bit widths follow the Windows LLP64 model. */ + +#include +#include +#include + +#ifndef _MFCWX_WINTYPES +#define _MFCWX_WINTYPES + +#define WINAPI +#define WINAPIV +#define APIENTRY +#define CALLBACK +#define PASCAL +#define _pascal +#define __pascal +#define FAR +#define NEAR +#define far +#define near +#define _far +#define _near +#define __far +#define __near +#define __stdcall +#define _stdcall +#define __cdecl +#define _cdecl +#define __fastcall +#define _fastcall +#define __declspec(x) +#define __forceinline inline +#define _inline inline +#define __inline inline +#define __w64 +#define __int8 char +#define __int16 short +#define __int32 int +#define __int64 long long +#define CONST const +#define VOID void +#define IN +#define OUT +#define OPTIONAL +#define UNALIGNED +#define AFXAPI +#define AFX_CDECL +#define AFX_STATIC static +#define AFX_STATIC_DATA static +#define AFX_INLINE inline +#define AFX_EXT_CLASS +#define AFX_EXT_API +#define AFX_EXT_DATA +#define AFX_CORE_DATA +#define AFX_DATA +#define AFX_DATADEF +#define AFX_COMDAT +#define AFX_NOVTABLE +#define AFX_IMPORT_DATA +#define AFX_API_EXPORT +#define DECLSPEC_NOVTABLE +#define DECLSPEC_IMPORT +#define DECLSPEC_EXPORT + +typedef int BOOL; +typedef unsigned char BOOLEAN; +typedef unsigned char BYTE; +typedef unsigned char byte; +typedef unsigned short WORD; +typedef uint32_t DWORD; +typedef unsigned int UINT; +typedef int INT; +typedef int32_t LONG; +typedef uint32_t ULONG; +typedef short SHORT; +typedef unsigned short USHORT; +typedef char CHAR; +typedef unsigned char UCHAR; +typedef char CCHAR; +typedef float FLOAT; +typedef double DOUBLE; +typedef long long LONGLONG; +typedef unsigned long long ULONGLONG; +typedef long long INT64; +typedef unsigned long long UINT64; +typedef int32_t INT32; +typedef uint32_t UINT32; +typedef int16_t INT16; +typedef uint16_t UINT16; +typedef int8_t INT8; +typedef uint8_t UINT8; +typedef uint32_t DWORD32; +typedef uint64_t DWORD64; +typedef uint64_t QWORD; +typedef intptr_t INT_PTR; +typedef uintptr_t UINT_PTR; +typedef intptr_t LONG_PTR; +typedef uintptr_t ULONG_PTR; +typedef uintptr_t DWORD_PTR; +typedef size_t SIZE_T; +typedef ptrdiff_t SSIZE_T; +typedef uintptr_t WPARAM; +typedef intptr_t LPARAM; +typedef intptr_t LRESULT; +typedef WORD ATOM; +typedef DWORD COLORREF; +typedef DWORD LCID; +typedef WORD LANGID; +typedef long HRESULT; +typedef DWORD ACCESS_MASK; + +typedef char TCHAR; +typedef unsigned char _TUCHAR; +typedef char _TCHAR; +typedef char _TSCHAR; +typedef wchar_t WCHAR; +typedef char* LPSTR; +typedef char* PSTR; +typedef char* NPSTR; +typedef const char* LPCSTR; +typedef const char* PCSTR; +typedef char* LPTSTR; +typedef char* PTSTR; +typedef const char* LPCTSTR; +typedef const char* PCTSTR; +typedef wchar_t* LPWSTR; +typedef wchar_t* PWSTR; +typedef const wchar_t* LPCWSTR; +typedef const wchar_t* PCWSTR; +typedef wchar_t OLECHAR; +typedef wchar_t* LPOLESTR; +typedef const wchar_t* LPCOLESTR; +typedef wchar_t* BSTR; +typedef unsigned char* LPBYTE; +typedef unsigned char* PBYTE; +typedef BOOL* LPBOOL; +typedef BOOL* PBOOL; +typedef WORD* LPWORD; +typedef WORD* PWORD; +typedef DWORD* LPDWORD; +typedef DWORD* PDWORD; +typedef LONG* LPLONG; +typedef LONG* PLONG; +typedef int* LPINT; +typedef int* PINT; +typedef UINT* LPUINT; +typedef UINT* PUINT; +typedef void* LPVOID; +typedef void* PVOID; +typedef const void* LPCVOID; +typedef COLORREF* LPCOLORREF; + +#define DECLARE_HANDLE(name) struct name##__ { int unused; }; typedef struct name##__* name +typedef void* HANDLE; +typedef HANDLE* PHANDLE; +typedef HANDLE* LPHANDLE; +DECLARE_HANDLE(HWND); +DECLARE_HANDLE(HINSTANCE); +DECLARE_HANDLE(HDC); +DECLARE_HANDLE(HGDIOBJ); +DECLARE_HANDLE(HMENU); +DECLARE_HANDLE(HACCEL); +DECLARE_HANDLE(HICON); +DECLARE_HANDLE(HKEY); +DECLARE_HANDLE(HTREEITEM); +DECLARE_HANDLE(HIMAGELIST); +DECLARE_HANDLE(HDROP); +DECLARE_HANDLE(HGLRC); +DECLARE_HANDLE(HDESK); +DECLARE_HANDLE(HHOOK); +DECLARE_HANDLE(HMONITOR); +DECLARE_HANDLE(HENHMETAFILE); +DECLARE_HANDLE(HMETAFILE); +DECLARE_HANDLE(HRSRC); +DECLARE_HANDLE(HTASK); +typedef HINSTANCE HMODULE; +typedef HICON HCURSOR; +typedef HGDIOBJ HBITMAP; +typedef HGDIOBJ HBRUSH; +typedef HGDIOBJ HPEN; +typedef HGDIOBJ HFONT; +typedef HGDIOBJ HRGN; +typedef HGDIOBJ HPALETTE; +typedef HANDLE HGLOBAL; +typedef HANDLE HLOCAL; +typedef HANDLE GLOBALHANDLE; +typedef HANDLE LOCALHANDLE; +typedef int HFILE; +typedef HKEY* PHKEY; +typedef LONG LSTATUS; +typedef DWORD REGSAM; + +#define INVALID_HANDLE_VALUE ((HANDLE)(intptr_t)-1) +#define HFILE_ERROR ((HFILE)-1) +#define HKEY_CLASSES_ROOT ((HKEY)(uintptr_t)0x80000000) +#define HKEY_CURRENT_USER ((HKEY)(uintptr_t)0x80000001) +#define HKEY_LOCAL_MACHINE ((HKEY)(uintptr_t)0x80000002) +#define HKEY_USERS ((HKEY)(uintptr_t)0x80000003) +#define HWND_TOP ((HWND)0) +#define HWND_BOTTOM ((HWND)1) +#define HWND_TOPMOST ((HWND)(intptr_t)-1) +#define HWND_NOTOPMOST ((HWND)(intptr_t)-2) +#define HWND_DESKTOP ((HWND)0) + +typedef LRESULT (*WNDPROC)(HWND, UINT, WPARAM, LPARAM); +typedef INT_PTR (*DLGPROC)(HWND, UINT, WPARAM, LPARAM); +typedef void (*TIMERPROC)(HWND, UINT, UINT_PTR, DWORD); +typedef INT_PTR (*FARPROC)(void); +typedef INT_PTR (*PROC)(void); +typedef DWORD (*LPTHREAD_START_ROUTINE)(LPVOID); + +typedef struct tagRECT { LONG left, top, right, bottom; } RECT, *PRECT, *LPRECT, *NPRECT; +typedef const RECT* LPCRECT; +typedef struct tagPOINT { LONG x, y; } POINT, *PPOINT, *LPPOINT, *NPPOINT; +typedef struct tagSIZE { LONG cx, cy; } SIZE, *PSIZE, *LPSIZE, SIZEL, *PSIZEL, *LPSIZEL; +typedef struct tagPOINTS { SHORT x, y; } POINTS, *PPOINTS, *LPPOINTS; + +typedef struct tagMSG { + HWND hwnd; + UINT message; + WPARAM wParam; + LPARAM lParam; + DWORD time; + POINT pt; +} MSG, *PMSG, *LPMSG; + +typedef struct _FILETIME { DWORD dwLowDateTime, dwHighDateTime; } FILETIME, *PFILETIME, *LPFILETIME; +typedef struct _SYSTEMTIME { + WORD wYear, wMonth, wDayOfWeek, wDay, wHour, wMinute, wSecond, wMilliseconds; +} SYSTEMTIME, *PSYSTEMTIME, *LPSYSTEMTIME; + +typedef union _LARGE_INTEGER { + struct { DWORD LowPart; LONG HighPart; } u; + struct { DWORD LowPart; LONG HighPart; }; + LONGLONG QuadPart; +} LARGE_INTEGER, *PLARGE_INTEGER; +typedef union _ULARGE_INTEGER { + struct { DWORD LowPart; DWORD HighPart; } u; + struct { DWORD LowPart; DWORD HighPart; }; + ULONGLONG QuadPart; +} ULARGE_INTEGER, *PULARGE_INTEGER; + +typedef struct _SECURITY_ATTRIBUTES { + DWORD nLength; + LPVOID lpSecurityDescriptor; + BOOL bInheritHandle; +} SECURITY_ATTRIBUTES, *PSECURITY_ATTRIBUTES, *LPSECURITY_ATTRIBUTES; + +typedef struct _OVERLAPPED { + ULONG_PTR Internal, InternalHigh; + DWORD Offset, OffsetHigh; + HANDLE hEvent; +} OVERLAPPED, *LPOVERLAPPED; + +typedef struct _WIN32_FIND_DATAA { + DWORD dwFileAttributes; + FILETIME ftCreationTime, ftLastAccessTime, ftLastWriteTime; + DWORD nFileSizeHigh, nFileSizeLow; + DWORD dwReserved0, dwReserved1; + CHAR cFileName[260]; + CHAR cAlternateFileName[14]; +} WIN32_FIND_DATAA, WIN32_FIND_DATA, *PWIN32_FIND_DATA, *LPWIN32_FIND_DATA; + +typedef struct _STARTUPINFOA { + DWORD cb; + LPSTR lpReserved, lpDesktop, lpTitle; + DWORD dwX, dwY, dwXSize, dwYSize, dwXCountChars, dwYCountChars, dwFillAttribute, dwFlags; + WORD wShowWindow, cbReserved2; + LPBYTE lpReserved2; + HANDLE hStdInput, hStdOutput, hStdError; +} STARTUPINFOA, STARTUPINFO, *LPSTARTUPINFO; + +typedef struct _PROCESS_INFORMATION { + HANDLE hProcess, hThread; + DWORD dwProcessId, dwThreadId; +} PROCESS_INFORMATION, *PPROCESS_INFORMATION, *LPPROCESS_INFORMATION; + +typedef struct tagLOGFONTA { + LONG lfHeight, lfWidth, lfEscapement, lfOrientation, lfWeight; + BYTE lfItalic, lfUnderline, lfStrikeOut, lfCharSet, lfOutPrecision, lfClipPrecision, lfQuality, lfPitchAndFamily; + CHAR lfFaceName[32]; +} LOGFONTA, LOGFONT, *PLOGFONT, *LPLOGFONT; + +typedef struct tagTEXTMETRICA { + LONG tmHeight, tmAscent, tmDescent, tmInternalLeading, tmExternalLeading, tmAveCharWidth, tmMaxCharWidth, + tmWeight, tmOverhang, tmDigitizedAspectX, tmDigitizedAspectY; + BYTE tmFirstChar, tmLastChar, tmDefaultChar, tmBreakChar, tmItalic, tmUnderlined, tmStruckOut, tmPitchAndFamily, + tmCharSet; +} TEXTMETRICA, TEXTMETRIC, *PTEXTMETRIC, *LPTEXTMETRIC; + +typedef struct tagLOGPEN { UINT lopnStyle; POINT lopnWidth; COLORREF lopnColor; } LOGPEN, *PLOGPEN, *LPLOGPEN; +typedef struct tagLOGBRUSH { UINT lbStyle; COLORREF lbColor; ULONG_PTR lbHatch; } LOGBRUSH, *PLOGBRUSH, *LPLOGBRUSH; + +typedef struct tagBITMAP { + LONG bmType, bmWidth, bmHeight, bmWidthBytes; + WORD bmPlanes, bmBitsPixel; + LPVOID bmBits; +} BITMAP, *PBITMAP, *LPBITMAP; + +#pragma pack(push, 2) +typedef struct tagBITMAPFILEHEADER { + WORD bfType; + DWORD bfSize; + WORD bfReserved1, bfReserved2; + DWORD bfOffBits; +} BITMAPFILEHEADER, *LPBITMAPFILEHEADER, *PBITMAPFILEHEADER; +#pragma pack(pop) + +typedef struct tagBITMAPINFOHEADER { + DWORD biSize; + LONG biWidth, biHeight; + WORD biPlanes, biBitCount; + DWORD biCompression, biSizeImage; + LONG biXPelsPerMeter, biYPelsPerMeter; + DWORD biClrUsed, biClrImportant; +} BITMAPINFOHEADER, *LPBITMAPINFOHEADER, *PBITMAPINFOHEADER; +typedef struct tagRGBQUAD { BYTE rgbBlue, rgbGreen, rgbRed, rgbReserved; } RGBQUAD, *LPRGBQUAD; +typedef struct tagBITMAPINFO { BITMAPINFOHEADER bmiHeader; RGBQUAD bmiColors[1]; } BITMAPINFO, *LPBITMAPINFO, *PBITMAPINFO; + +typedef struct tagPAINTSTRUCT { + HDC hdc; + BOOL fErase; + RECT rcPaint; + BOOL fRestore, fIncUpdate; + BYTE rgbReserved[32]; +} PAINTSTRUCT, *PPAINTSTRUCT, *LPPAINTSTRUCT; + +typedef struct tagDRAWITEMSTRUCT { + UINT CtlType, CtlID, itemID, itemAction, itemState; + HWND hwndItem; + HDC hDC; + RECT rcItem; + ULONG_PTR itemData; +} DRAWITEMSTRUCT, *PDRAWITEMSTRUCT, *LPDRAWITEMSTRUCT; + +typedef struct tagMEASUREITEMSTRUCT { + UINT CtlType, CtlID, itemID, itemWidth, itemHeight; + ULONG_PTR itemData; +} MEASUREITEMSTRUCT, *PMEASUREITEMSTRUCT, *LPMEASUREITEMSTRUCT; + +typedef struct tagCOMPAREITEMSTRUCT { + UINT CtlType, CtlID; + HWND hwndItem; + UINT itemID1; + ULONG_PTR itemData1; + UINT itemID2; + ULONG_PTR itemData2; + DWORD dwLocaleId; +} COMPAREITEMSTRUCT, *LPCOMPAREITEMSTRUCT; + +typedef struct tagDELETEITEMSTRUCT { + UINT CtlType, CtlID, itemID; + HWND hwndItem; + ULONG_PTR itemData; +} DELETEITEMSTRUCT, *LPDELETEITEMSTRUCT; + +typedef struct tagNMHDR { HWND hwndFrom; UINT_PTR idFrom; UINT code; } NMHDR, *LPNMHDR; +typedef struct _HD_ITEMA { + UINT mask; + int cxy; + LPSTR pszText; + HBITMAP hbm; + int cchTextMax, fmt; + LPARAM lParam; + int iImage, iOrder; +} HDITEM, HD_ITEM, *LPHDITEM; +typedef struct tagNMHEADERA { NMHDR hdr; int iItem; int iButton; HDITEM* pitem; } NMHEADER, HD_NOTIFY, *LPNMHEADER; + +typedef struct tagHELPINFO { + UINT cbSize; + int iContextType; + int iCtrlId; + HANDLE hItemHandle; + DWORD_PTR dwContextId; + POINT MousePos; +} HELPINFO, *LPHELPINFO; + +typedef struct tagMINMAXINFO { + POINT ptReserved, ptMaxSize, ptMaxPosition, ptMinTrackSize, ptMaxTrackSize; +} MINMAXINFO, *PMINMAXINFO, *LPMINMAXINFO; + +typedef struct tagCREATESTRUCTA { + LPVOID lpCreateParams; + HINSTANCE hInstance; + HMENU hMenu; + HWND hwndParent; + int cy, cx, y, x; + LONG style; + LPCSTR lpszName, lpszClass; + DWORD dwExStyle; +} CREATESTRUCTA, CREATESTRUCT, *LPCREATESTRUCT; + +typedef struct tagWINDOWPLACEMENT { + UINT length, flags, showCmd; + POINT ptMinPosition, ptMaxPosition; + RECT rcNormalPosition; +} WINDOWPLACEMENT, *PWINDOWPLACEMENT, *LPWINDOWPLACEMENT; + +typedef struct tagWINDOWPOS { + HWND hwnd, hwndInsertAfter; + int x, y, cx, cy; + UINT flags; +} WINDOWPOS, *LPWINDOWPOS, *PWINDOWPOS; + +typedef struct tagSCROLLINFO { + UINT cbSize, fMask; + int nMin, nMax; + UINT nPage; + int nPos, nTrackPos; +} SCROLLINFO, *LPSCROLLINFO; +typedef const SCROLLINFO* LPCSCROLLINFO; + +typedef struct tagACCEL { BYTE fVirt; WORD key; WORD cmd; } ACCEL, *LPACCEL; + +typedef struct tagWNDCLASSA { + UINT style; + WNDPROC lpfnWndProc; + int cbClsExtra, cbWndExtra; + HINSTANCE hInstance; + HICON hIcon; + HCURSOR hCursor; + HBRUSH hbrBackground; + LPCSTR lpszMenuName, lpszClassName; +} WNDCLASSA, WNDCLASS, *LPWNDCLASS; + +typedef struct tagNMUPDOWN { NMHDR hdr; int iPos; int iDelta; } NMUPDOWN, *LPNMUPDOWN, NM_UPDOWN; + +typedef struct tagLVITEMA { + UINT mask; + int iItem, iSubItem; + UINT state, stateMask; + LPSTR pszText; + int cchTextMax, iImage; + LPARAM lParam; + int iIndent; +} LVITEMA, LVITEM, LV_ITEM, *LPLVITEM; + +typedef struct tagLVCOLUMNA { + UINT mask; + int fmt, cx; + LPSTR pszText; + int cchTextMax, iSubItem, iImage, iOrder; +} LVCOLUMNA, LVCOLUMN, LV_COLUMN, *LPLVCOLUMN; + +typedef struct tagNMLISTVIEW { + NMHDR hdr; + int iItem, iSubItem; + UINT uNewState, uOldState, uChanged; + POINT ptAction; + LPARAM lParam; +} NMLISTVIEW, NM_LISTVIEW, *LPNMLISTVIEW; + +typedef struct tagNMITEMACTIVATE { + NMHDR hdr; + int iItem, iSubItem; + UINT uNewState, uOldState, uChanged; + POINT ptAction; + LPARAM lParam; + UINT uKeyFlags; +} NMITEMACTIVATE, *LPNMITEMACTIVATE; + +typedef struct tagLVDISPINFO { NMHDR hdr; LVITEMA item; } NMLVDISPINFO, LV_DISPINFO, *LPNMLVDISPINFO; +typedef struct tagLVKEYDOWN { NMHDR hdr; WORD wVKey; UINT flags; } NMLVKEYDOWN, LV_KEYDOWN, *LPNMLVKEYDOWN; +typedef struct tagLVHITTESTINFO { POINT pt; UINT flags; int iItem, iSubItem, iGroup; } LVHITTESTINFO, *LPLVHITTESTINFO; +typedef struct tagLVFINDINFOA { UINT flags; LPCSTR psz; LPARAM lParam; POINT pt; UINT vkDirection; } LVFINDINFO, *LPFINDINFO; + +typedef struct tagTVITEMA { + UINT mask; + HTREEITEM hItem; + UINT state, stateMask; + LPSTR pszText; + int cchTextMax, iImage, iSelectedImage, cChildren; + LPARAM lParam; +} TVITEMA, TVITEM, TV_ITEM, *LPTVITEM; +typedef struct tagTVINSERTSTRUCTA { HTREEITEM hParent, hInsertAfter; TVITEMA item; } TVINSERTSTRUCT, *LPTVINSERTSTRUCT; +typedef struct tagNMTREEVIEWA { + NMHDR hdr; + UINT action; + TVITEMA itemOld, itemNew; + POINT ptDrag; +} NMTREEVIEW, NM_TREEVIEW, *LPNMTREEVIEW; + +typedef struct tagTCITEMA { + UINT mask; + DWORD dwState, dwStateMask; + LPSTR pszText; + int cchTextMax, iImage; + LPARAM lParam; +} TCITEMA, TCITEM, TC_ITEM, *LPTCITEM; + +typedef struct tagTOOLINFOA { + UINT cbSize, uFlags; + HWND hwnd; + UINT_PTR uId; + RECT rect; + HINSTANCE hinst; + LPSTR lpszText; + LPARAM lParam; +} TOOLINFOA, TOOLINFO, *LPTOOLINFO; + +typedef struct tagNMTTDISPINFOA { + NMHDR hdr; + LPSTR lpszText; + char szText[80]; + HINSTANCE hinst; + UINT uFlags; + LPARAM lParam; +} NMTTDISPINFO, TOOLTIPTEXT, TOOLTIPTEXTA, *LPNMTTDISPINFO, *LPTOOLTIPTEXT; + +typedef struct _charrange { LONG cpMin, cpMax; } CHARRANGE; +typedef struct _textrange { CHARRANGE chrg; LPSTR lpstrText; } TEXTRANGE; +typedef struct _findtextexa { CHARRANGE chrg; LPCSTR lpstrText; CHARRANGE chrgText; } FINDTEXTEX; +typedef struct _findtext { CHARRANGE chrg; LPCSTR lpstrText; } FINDTEXT; +typedef DWORD (*EDITSTREAMCALLBACK)(DWORD_PTR dwCookie, LPBYTE pbBuff, LONG cb, LONG* pcb); +typedef struct _editstream { DWORD_PTR dwCookie; DWORD dwError; EDITSTREAMCALLBACK pfnCallback; } EDITSTREAM; +typedef struct _charformat { + UINT cbSize; + DWORD dwMask, dwEffects; + LONG yHeight, yOffset; + COLORREF crTextColor; + BYTE bCharSet, bPitchAndFamily; + char szFaceName[32]; +} CHARFORMAT, CHARFORMATA; +typedef struct _charformat2 { + UINT cbSize; + DWORD dwMask, dwEffects; + LONG yHeight, yOffset; + COLORREF crTextColor; + BYTE bCharSet, bPitchAndFamily; + char szFaceName[32]; + WORD wWeight; + SHORT sSpacing; + COLORREF crBackColor; + LCID lcid; + DWORD dwReserved; + SHORT sStyle; + WORD wKerning; + BYTE bUnderlineType, bAnimation, bRevAuthor, bReserved1; +} CHARFORMAT2, CHARFORMAT2A; +typedef struct _paraformat { + UINT cbSize; + DWORD dwMask; + WORD wNumbering, wReserved; + LONG dxStartIndent, dxRightIndent, dxOffset; + WORD wAlignment; + SHORT cTabCount; + LONG rgxTabs[32]; +} PARAFORMAT; +typedef struct _msgfilter { NMHDR nmhdr; UINT msg; WPARAM wParam; LPARAM lParam; } MSGFILTER; +typedef struct _selchange { NMHDR nmhdr; CHARRANGE chrg; WORD seltyp; } SELCHANGE; +typedef struct _enlink { NMHDR nmhdr; UINT msg; WPARAM wParam; LPARAM lParam; CHARRANGE chrg; } ENLINK; + +typedef struct tagOFNA { + DWORD lStructSize; + HWND hwndOwner; + HINSTANCE hInstance; + LPCSTR lpstrFilter; + LPSTR lpstrCustomFilter; + DWORD nMaxCustFilter, nFilterIndex; + LPSTR lpstrFile; + DWORD nMaxFile; + LPSTR lpstrFileTitle; + DWORD nMaxFileTitle; + LPCSTR lpstrInitialDir, lpstrTitle; + DWORD Flags; + WORD nFileOffset, nFileExtension; + LPCSTR lpstrDefExt; + LPARAM lCustData; + LPVOID lpfnHook; + LPCSTR lpTemplateName; +} OPENFILENAMEA, OPENFILENAME, *LPOPENFILENAME; + +#define OFN_READONLY 0x00000001 +#define OFN_OVERWRITEPROMPT 0x00000002 +#define OFN_HIDEREADONLY 0x00000004 +#define OFN_NOCHANGEDIR 0x00000008 +#define OFN_SHOWHELP 0x00000010 +#define OFN_ENABLEHOOK 0x00000020 +#define OFN_ENABLETEMPLATE 0x00000040 +#define OFN_NOVALIDATE 0x00000100 +#define OFN_ALLOWMULTISELECT 0x00000200 +#define OFN_EXTENSIONDIFFERENT 0x00000400 +#define OFN_PATHMUSTEXIST 0x00000800 +#define OFN_FILEMUSTEXIST 0x00001000 +#define OFN_CREATEPROMPT 0x00002000 +#define OFN_SHAREAWARE 0x00004000 +#define OFN_NOREADONLYRETURN 0x00008000 +#define OFN_NOTESTFILECREATE 0x00010000 +#define OFN_EXPLORER 0x00080000 +#define OFN_NODEREFERENCELINKS 0x00100000 +#define OFN_LONGNAMES 0x00200000 +#define OFN_ENABLESIZING 0x00800000 + +typedef struct tagCHOOSECOLORA { + DWORD lStructSize; + HWND hwndOwner; + HWND hInstance; + COLORREF rgbResult; + COLORREF* lpCustColors; + DWORD Flags; + LPARAM lCustData; + LPVOID lpfnHook; + LPCSTR lpTemplateName; +} CHOOSECOLOR, *LPCHOOSECOLOR; +#define CC_RGBINIT 0x00000001 +#define CC_FULLOPEN 0x00000002 + +typedef struct tagNONCLIENTMETRICSA { + UINT cbSize; + int iBorderWidth, iScrollWidth, iScrollHeight, iCaptionWidth, iCaptionHeight; + LOGFONTA lfCaptionFont; + int iSmCaptionWidth, iSmCaptionHeight; + LOGFONTA lfSmCaptionFont; + int iMenuWidth, iMenuHeight; + LOGFONTA lfMenuFont, lfStatusFont, lfMessageFont; +} NONCLIENTMETRICS; + +typedef struct _MEMORYSTATUS { + DWORD dwLength, dwMemoryLoad; + SIZE_T dwTotalPhys, dwAvailPhys, dwTotalPageFile, dwAvailPageFile, dwTotalVirtual, dwAvailVirtual; +} MEMORYSTATUS, *LPMEMORYSTATUS; + +typedef struct _SYSTEM_INFO { + WORD wProcessorArchitecture, wReserved; + DWORD dwPageSize; + LPVOID lpMinimumApplicationAddress, lpMaximumApplicationAddress; + DWORD_PTR dwActiveProcessorMask; + DWORD dwNumberOfProcessors, dwProcessorType, dwAllocationGranularity; + WORD wProcessorLevel, wProcessorRevision; +} SYSTEM_INFO, *LPSYSTEM_INFO; + +typedef struct _OSVERSIONINFOA { + DWORD dwOSVersionInfoSize, dwMajorVersion, dwMinorVersion, dwBuildNumber, dwPlatformId; + CHAR szCSDVersion[128]; +} OSVERSIONINFO, OSVERSIONINFOA, *LPOSVERSIONINFO; + +typedef struct _CRITICAL_SECTION { void* impl; } CRITICAL_SECTION, *LPCRITICAL_SECTION; + +typedef struct _GUID { DWORD Data1; WORD Data2, Data3; BYTE Data4[8]; } GUID, IID, CLSID; + +#endif /* _MFCWX_WINTYPES */ diff --git a/port/mfcwx/include/mmsystem.h b/port/mfcwx/include/mmsystem.h new file mode 100644 index 000000000..38b8341c6 --- /dev/null +++ b/port/mfcwx/include/mmsystem.h @@ -0,0 +1,6 @@ +#pragma once +#ifdef __cplusplus +#include "afxwin.h" +#else +#include "mfcwx/win32.h" +#endif diff --git a/port/mfcwx/include/objbase.h b/port/mfcwx/include/objbase.h new file mode 100644 index 000000000..38b8341c6 --- /dev/null +++ b/port/mfcwx/include/objbase.h @@ -0,0 +1,6 @@ +#pragma once +#ifdef __cplusplus +#include "afxwin.h" +#else +#include "mfcwx/win32.h" +#endif diff --git a/port/mfcwx/include/ole2.h b/port/mfcwx/include/ole2.h new file mode 100644 index 000000000..38b8341c6 --- /dev/null +++ b/port/mfcwx/include/ole2.h @@ -0,0 +1,6 @@ +#pragma once +#ifdef __cplusplus +#include "afxwin.h" +#else +#include "mfcwx/win32.h" +#endif diff --git a/port/mfcwx/include/process.h b/port/mfcwx/include/process.h new file mode 100644 index 000000000..fbffeb452 --- /dev/null +++ b/port/mfcwx/include/process.h @@ -0,0 +1,2 @@ +#pragma once +#include "mfcwx/crtcompat.h" diff --git a/port/mfcwx/include/richedit.h b/port/mfcwx/include/richedit.h new file mode 100644 index 000000000..38b8341c6 --- /dev/null +++ b/port/mfcwx/include/richedit.h @@ -0,0 +1,6 @@ +#pragma once +#ifdef __cplusplus +#include "afxwin.h" +#else +#include "mfcwx/win32.h" +#endif diff --git a/port/mfcwx/include/share.h b/port/mfcwx/include/share.h new file mode 100644 index 000000000..fbffeb452 --- /dev/null +++ b/port/mfcwx/include/share.h @@ -0,0 +1,2 @@ +#pragma once +#include "mfcwx/crtcompat.h" diff --git a/port/mfcwx/include/shellapi.h b/port/mfcwx/include/shellapi.h new file mode 100644 index 000000000..38b8341c6 --- /dev/null +++ b/port/mfcwx/include/shellapi.h @@ -0,0 +1,6 @@ +#pragma once +#ifdef __cplusplus +#include "afxwin.h" +#else +#include "mfcwx/win32.h" +#endif diff --git a/port/mfcwx/include/shlobj.h b/port/mfcwx/include/shlobj.h new file mode 100644 index 000000000..38b8341c6 --- /dev/null +++ b/port/mfcwx/include/shlobj.h @@ -0,0 +1,6 @@ +#pragma once +#ifdef __cplusplus +#include "afxwin.h" +#else +#include "mfcwx/win32.h" +#endif diff --git a/port/mfcwx/include/shlwapi.h b/port/mfcwx/include/shlwapi.h new file mode 100644 index 000000000..38b8341c6 --- /dev/null +++ b/port/mfcwx/include/shlwapi.h @@ -0,0 +1,6 @@ +#pragma once +#ifdef __cplusplus +#include "afxwin.h" +#else +#include "mfcwx/win32.h" +#endif diff --git a/port/mfcwx/include/tchar.h b/port/mfcwx/include/tchar.h new file mode 100644 index 000000000..deb747432 --- /dev/null +++ b/port/mfcwx/include/tchar.h @@ -0,0 +1,2 @@ +#pragma once +#include "mfcwx/win32.h" diff --git a/port/mfcwx/include/winbase.h b/port/mfcwx/include/winbase.h new file mode 100644 index 000000000..17bdd6cc2 --- /dev/null +++ b/port/mfcwx/include/winbase.h @@ -0,0 +1,6 @@ +#pragma once +#ifdef __cplusplus +#include "afx.h" +#else +#include "mfcwx/win32.h" +#endif diff --git a/port/mfcwx/include/wincrypt.h b/port/mfcwx/include/wincrypt.h new file mode 100644 index 000000000..38b8341c6 --- /dev/null +++ b/port/mfcwx/include/wincrypt.h @@ -0,0 +1,6 @@ +#pragma once +#ifdef __cplusplus +#include "afxwin.h" +#else +#include "mfcwx/win32.h" +#endif diff --git a/port/mfcwx/include/windef.h b/port/mfcwx/include/windef.h new file mode 100644 index 000000000..17bdd6cc2 --- /dev/null +++ b/port/mfcwx/include/windef.h @@ -0,0 +1,6 @@ +#pragma once +#ifdef __cplusplus +#include "afx.h" +#else +#include "mfcwx/win32.h" +#endif diff --git a/port/mfcwx/include/windows.h b/port/mfcwx/include/windows.h new file mode 100644 index 000000000..17bdd6cc2 --- /dev/null +++ b/port/mfcwx/include/windows.h @@ -0,0 +1,6 @@ +#pragma once +#ifdef __cplusplus +#include "afx.h" +#else +#include "mfcwx/win32.h" +#endif diff --git a/port/mfcwx/include/wingdi.h b/port/mfcwx/include/wingdi.h new file mode 100644 index 000000000..17bdd6cc2 --- /dev/null +++ b/port/mfcwx/include/wingdi.h @@ -0,0 +1,6 @@ +#pragma once +#ifdef __cplusplus +#include "afx.h" +#else +#include "mfcwx/win32.h" +#endif diff --git a/port/mfcwx/include/winnt.h b/port/mfcwx/include/winnt.h new file mode 100644 index 000000000..17bdd6cc2 --- /dev/null +++ b/port/mfcwx/include/winnt.h @@ -0,0 +1,6 @@ +#pragma once +#ifdef __cplusplus +#include "afx.h" +#else +#include "mfcwx/win32.h" +#endif diff --git a/port/mfcwx/include/winreg.h b/port/mfcwx/include/winreg.h new file mode 100644 index 000000000..17bdd6cc2 --- /dev/null +++ b/port/mfcwx/include/winreg.h @@ -0,0 +1,6 @@ +#pragma once +#ifdef __cplusplus +#include "afx.h" +#else +#include "mfcwx/win32.h" +#endif diff --git a/port/mfcwx/include/winsock.h b/port/mfcwx/include/winsock.h new file mode 100644 index 000000000..17bdd6cc2 --- /dev/null +++ b/port/mfcwx/include/winsock.h @@ -0,0 +1,6 @@ +#pragma once +#ifdef __cplusplus +#include "afx.h" +#else +#include "mfcwx/win32.h" +#endif diff --git a/port/mfcwx/include/winsvc.h b/port/mfcwx/include/winsvc.h new file mode 100644 index 000000000..38b8341c6 --- /dev/null +++ b/port/mfcwx/include/winsvc.h @@ -0,0 +1,6 @@ +#pragma once +#ifdef __cplusplus +#include "afxwin.h" +#else +#include "mfcwx/win32.h" +#endif diff --git a/port/mfcwx/include/winuser.h b/port/mfcwx/include/winuser.h new file mode 100644 index 000000000..17bdd6cc2 --- /dev/null +++ b/port/mfcwx/include/winuser.h @@ -0,0 +1,6 @@ +#pragma once +#ifdef __cplusplus +#include "afx.h" +#else +#include "mfcwx/win32.h" +#endif diff --git a/port/mfcwx/src/bridge.h b/port/mfcwx/src/bridge.h new file mode 100644 index 000000000..8911be22b --- /dev/null +++ b/port/mfcwx/src/bridge.h @@ -0,0 +1,16 @@ +#pragma once + +#include + +#include "afxwin.h" +#include "mfcwx/codepage.h" + +namespace mfcwx { + +wxString ToWx(const char* s, int length = -1); +inline wxString ToWx(const CString& s) { return ToWx(s.GetString(), s.GetLength()); } +std::string FromWx(const wxString& s); +inline CString CStr(const wxString& s) { std::string a = FromWx(s); return CString(a.data(), static_cast(a.size())); } +inline wxString NativePathWx(const char* path) { return wxString::FromUTF8(NativePath(path)); } + +} // namespace mfcwx diff --git a/port/mfcwx/src/cmdtarget.cpp b/port/mfcwx/src/cmdtarget.cpp new file mode 100644 index 000000000..501b20546 --- /dev/null +++ b/port/mfcwx/src/cmdtarget.cpp @@ -0,0 +1,154 @@ +#include "internal.h" + +// --------------------------------------------------------------------------------------------- +// CCmdTarget and message map lookup + +CRuntimeClass CCmdTarget::classCCmdTarget = {"CCmdTarget", sizeof(CCmdTarget), 0xFFFF, nullptr, + &CObject::GetThisClass, nullptr}; +static AFX_CLASSINIT _init_CCmdTarget(&CCmdTarget::classCCmdTarget); +CRuntimeClass* CCmdTarget::GetThisClass() { return &CCmdTarget::classCCmdTarget; } +CRuntimeClass* CCmdTarget::GetRuntimeClass() const { return &CCmdTarget::classCCmdTarget; } + +const AFX_MSGMAP* CCmdTarget::GetThisMessageMap() { + static const AFX_MSGMAP_ENTRY entries[] = {{0, 0, 0, 0, AfxSig_end, nullptr}}; + static const AFX_MSGMAP map = {nullptr, &entries[0]}; + return ↦ +} + +const AFX_MSGMAP* CCmdTarget::GetMessageMap() const { return GetThisMessageMap(); } + +const AFX_MSGMAP_ENTRY* CCmdTarget::FindMessageEntry(UINT nMessage, UINT nCode, UINT nID) const { + for (const AFX_MSGMAP* map = GetMessageMap(); map; map = map->pfnGetBaseMap ? map->pfnGetBaseMap() : nullptr) { + for (const AFX_MSGMAP_ENTRY* e = map->lpEntries; e->nSig != AfxSig_end; ++e) { + if (e->nMessage == 0xC000) { + if (nMessage >= 0xC000 && *reinterpret_cast(e->nSig) == nMessage) + return e; + continue; + } + if (e->nMessage != nMessage || e->nCode != nCode) + continue; + if (nMessage == WM_COMMAND || nMessage == WM_NOTIFY) { + if (nID >= e->nID && nID <= e->nLastID) + return e; + } else { + return e; + } + } + if (map->pfnGetBaseMap == nullptr || map->pfnGetBaseMap() == map) + break; + } + return nullptr; +} + +struct AFX_NOTIFY { + LRESULT* pResult; + NMHDR* pNMHDR; +}; + +BOOL CCmdTarget::DispatchCommand(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo) { + UINT message = WM_COMMAND; + UINT code = static_cast(nCode); + if (static_cast(HIWORD(static_cast(nCode))) == WM_NOTIFY && nCode != static_cast(CN_UPDATE_COMMAND_UI)) { + message = WM_NOTIFY; + code = static_cast(static_cast(LOWORD(static_cast(nCode)))); + } + const AFX_MSGMAP_ENTRY* e = FindMessageEntry(message, code, nID); + if (!e && message == WM_NOTIFY) + e = FindMessageEntry(message, static_cast(LOWORD(static_cast(nCode))), nID); + if (!e) + return FALSE; + if (pHandlerInfo) { + pHandlerInfo->pTarget = this; + pHandlerInfo->pmf = e->pfn; + return TRUE; + } + switch (e->nSig) { + case AfxSigCmd_v: + (this->*e->pfn)(); + return TRUE; + case AfxSigCmd_b: + return (this->*reinterpret_cast(e->pfn))(); + case AfxSigCmd_RANGE: + (this->*reinterpret_cast(e->pfn))(nID); + return TRUE; + case AfxSigCmd_EX: + return (this->*reinterpret_cast(e->pfn))(nID); + case AfxSigCmdUI: { + auto* pCmdUI = static_cast(pExtra); + pCmdUI->m_bContinueRouting = FALSE; + (this->*reinterpret_cast(e->pfn))(pCmdUI); + return !pCmdUI->m_bContinueRouting; + } + case AfxSigNotify_v: { + auto* notify = static_cast(pExtra); + (this->*reinterpret_cast(e->pfn))(notify->pNMHDR, notify->pResult); + return TRUE; + } + case AfxSigNotify_b: { + auto* notify = static_cast(pExtra); + return (this->*reinterpret_cast(e->pfn))(notify->pNMHDR, + notify->pResult); + } + case AfxSigNotify_RANGE: { + auto* notify = static_cast(pExtra); + (this->*reinterpret_cast(e->pfn))(nID, notify->pNMHDR, + notify->pResult); + return TRUE; + } + case AfxSigNotify_EX: { + auto* notify = static_cast(pExtra); + return (this->*reinterpret_cast(e->pfn))( + nID, notify->pNMHDR, notify->pResult); + } + default: + return FALSE; + } +} + +BOOL CCmdTarget::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo) { + return DispatchCommand(nID, nCode, pExtra, pHandlerInfo); +} + +void CCmdTarget::BeginWaitCursor() { wxBeginBusyCursor(); } +void CCmdTarget::EndWaitCursor() { + if (wxIsBusy()) + wxEndBusyCursor(); +} +void CCmdTarget::RestoreWaitCursor() {} + +// --------------------------------------------------------------------------------------------- +// CCmdUI + +CCmdUI::CCmdUI() + : m_nID(0), m_nIndex(0), m_pMenu(nullptr), m_pSubMenu(nullptr), m_pParentMenu(nullptr), m_pOther(nullptr), + m_bEnableChanged(FALSE), m_bContinueRouting(FALSE), m_nIndexMax(0), m_enabled(true), m_check(0), + m_checkSet(false), m_textSet(false) {} + +void CCmdUI::Enable(BOOL bOn) { + m_enabled = bOn != FALSE; + m_bEnableChanged = TRUE; +} + +void CCmdUI::SetCheck(int nCheck) { + m_check = nCheck; + m_checkSet = true; +} + +void CCmdUI::SetRadio(BOOL bOn) { SetCheck(bOn ? 1 : 0); } + +void CCmdUI::SetText(const char* lpszText) { + m_text = lpszText; + m_textSet = true; +} + +BOOL CCmdUI::DoUpdate(CCmdTarget* pTarget, BOOL bDisableIfNoHndler) { + m_bEnableChanged = FALSE; + BOOL handled = pTarget->OnCmdMsg(m_nID, static_cast(CN_UPDATE_COMMAND_UI), this, nullptr); + if (bDisableIfNoHndler && !m_bEnableChanged) { + AFX_CMDHANDLERINFO info; + info.pTarget = nullptr; + BOOL hasHandler = pTarget->OnCmdMsg(m_nID, CN_COMMAND, this, &info); + Enable(hasHandler); + } + return handled; +} diff --git a/port/mfcwx/src/codepage.cpp b/port/mfcwx/src/codepage.cpp new file mode 100644 index 000000000..0f4b16938 --- /dev/null +++ b/port/mfcwx/src/codepage.cpp @@ -0,0 +1,141 @@ +#include "bridge.h" + +#include +#include +#include + +namespace mfcwx { + +extern const uint16_t kCp1250High[128]; +extern const uint16_t kCp1251High[128]; +extern const uint16_t kCp1252High[128]; +extern const uint16_t kCp1253High[128]; +extern const uint16_t kCp1254High[128]; +extern const uint16_t kCp1257High[128]; + +namespace { + +struct CodePage { + int cp; + const uint16_t* high; + std::unordered_map reverse; +}; + +CodePage* MakeCodePage(int cp, const uint16_t* high) { + auto* page = new CodePage{cp, high, {}}; + for (int i = 0; i < 128; ++i) + page->reverse.emplace(static_cast(high[i]), static_cast(0x80 + i)); + return page; +} + +CodePage* LookupCodePage(int cp) { + static CodePage* const pages[] = { + MakeCodePage(1250, kCp1250High), MakeCodePage(1251, kCp1251High), MakeCodePage(1252, kCp1252High), + MakeCodePage(1253, kCp1253High), MakeCodePage(1254, kCp1254High), MakeCodePage(1257, kCp1257High), + }; + for (CodePage* page : pages) + if (page->cp == cp) + return page; + return pages[2]; +} + +std::atomic g_page{nullptr}; + +CodePage* Current() { + CodePage* page = g_page.load(std::memory_order_acquire); + if (!page) { + page = LookupCodePage(1252); + g_page.store(page, std::memory_order_release); + } + return page; +} + +} // namespace + +void SetAnsiCodePage(int cp) { g_page.store(LookupCodePage(cp), std::memory_order_release); } + +int GetAnsiCodePage() { return Current()->cp; } + +wchar_t AnsiToUnicode(unsigned char ch) { + if (ch < 0x80) + return ch; + return static_cast(Current()->high[ch - 0x80]); +} + +bool UnicodeToAnsi(wchar_t ch, char& out) { + if (static_cast(ch) < 0x80) { + out = static_cast(ch); + return true; + } + const auto& reverse = Current()->reverse; + auto it = reverse.find(ch); + if (it == reverse.end()) { + out = '?'; + return false; + } + out = it->second; + return true; +} + +wxString ToWx(const char* s, int length) { + if (!s) + return wxString(); + if (length < 0) + length = static_cast(strlen(s)); + std::wstring w; + w.reserve(length); + for (int i = 0; i < length; ++i) + w.push_back(AnsiToUnicode(static_cast(s[i]))); + return wxString(w); +} + +std::string FromWx(const wxString& s) { + std::string out; + out.reserve(s.length()); + for (wxString::const_iterator it = s.begin(); it != s.end(); ++it) { + char ch; + UnicodeToAnsi(static_cast((*it).GetValue()), ch); + out.push_back(ch); + } + return out; +} + +std::string AnsiToUtf8(const char* s, int length) { return std::string(ToWx(s, length).utf8_str()); } + +std::string AppPath(const char* nativePath) { + if (!nativePath) + return std::string(); + std::string p = Utf8ToAnsi(nativePath); + for (char& ch : p) + if (ch == '/') + ch = '\\'; + return p; +} + +std::string Utf8ToAnsi(const char* utf8) { + if (!utf8) + return std::string(); + return FromWx(wxString::FromUTF8(utf8)); +} + +std::string NativePath(const char* path) { + if (!path) + return std::string(); + std::string p(path); + for (char& ch : p) + if (ch == '\\') + ch = '/'; + if (p.size() >= 2 && p[1] == ':' && ((p[0] >= 'A' && p[0] <= 'Z') || (p[0] >= 'a' && p[0] <= 'z'))) + p.erase(0, 2); + bool ascii = true; + for (unsigned char ch : p) + if (ch >= 0x80) { + ascii = false; + break; + } + if (ascii) + return p; + return std::string(ToWx(p.c_str()).utf8_str()); +} + +} // namespace mfcwx diff --git a/port/mfcwx/src/codepage_tables.cpp b/port/mfcwx/src/codepage_tables.cpp new file mode 100644 index 000000000..77293ded9 --- /dev/null +++ b/port/mfcwx/src/codepage_tables.cpp @@ -0,0 +1,120 @@ +// Generated from Python codecs: upper half (0x80-0xFF) of Windows code pages. +#include + +namespace mfcwx { + +const uint16_t kCp1250High[128] = { + 0x20AC, 0x0081, 0x201A, 0x0083, 0x201E, 0x2026, 0x2020, 0x2021, + 0x0088, 0x2030, 0x0160, 0x2039, 0x015A, 0x0164, 0x017D, 0x0179, + 0x0090, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, + 0x0098, 0x2122, 0x0161, 0x203A, 0x015B, 0x0165, 0x017E, 0x017A, + 0x00A0, 0x02C7, 0x02D8, 0x0141, 0x00A4, 0x0104, 0x00A6, 0x00A7, + 0x00A8, 0x00A9, 0x015E, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x017B, + 0x00B0, 0x00B1, 0x02DB, 0x0142, 0x00B4, 0x00B5, 0x00B6, 0x00B7, + 0x00B8, 0x0105, 0x015F, 0x00BB, 0x013D, 0x02DD, 0x013E, 0x017C, + 0x0154, 0x00C1, 0x00C2, 0x0102, 0x00C4, 0x0139, 0x0106, 0x00C7, + 0x010C, 0x00C9, 0x0118, 0x00CB, 0x011A, 0x00CD, 0x00CE, 0x010E, + 0x0110, 0x0143, 0x0147, 0x00D3, 0x00D4, 0x0150, 0x00D6, 0x00D7, + 0x0158, 0x016E, 0x00DA, 0x0170, 0x00DC, 0x00DD, 0x0162, 0x00DF, + 0x0155, 0x00E1, 0x00E2, 0x0103, 0x00E4, 0x013A, 0x0107, 0x00E7, + 0x010D, 0x00E9, 0x0119, 0x00EB, 0x011B, 0x00ED, 0x00EE, 0x010F, + 0x0111, 0x0144, 0x0148, 0x00F3, 0x00F4, 0x0151, 0x00F6, 0x00F7, + 0x0159, 0x016F, 0x00FA, 0x0171, 0x00FC, 0x00FD, 0x0163, 0x02D9, +}; + +const uint16_t kCp1251High[128] = { + 0x0402, 0x0403, 0x201A, 0x0453, 0x201E, 0x2026, 0x2020, 0x2021, + 0x20AC, 0x2030, 0x0409, 0x2039, 0x040A, 0x040C, 0x040B, 0x040F, + 0x0452, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, + 0x0098, 0x2122, 0x0459, 0x203A, 0x045A, 0x045C, 0x045B, 0x045F, + 0x00A0, 0x040E, 0x045E, 0x0408, 0x00A4, 0x0490, 0x00A6, 0x00A7, + 0x0401, 0x00A9, 0x0404, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x0407, + 0x00B0, 0x00B1, 0x0406, 0x0456, 0x0491, 0x00B5, 0x00B6, 0x00B7, + 0x0451, 0x2116, 0x0454, 0x00BB, 0x0458, 0x0405, 0x0455, 0x0457, + 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, + 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F, + 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, + 0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F, + 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, + 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F, + 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, + 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F, +}; + +const uint16_t kCp1252High[128] = { + 0x20AC, 0x0081, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, + 0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, 0x008D, 0x017D, 0x008F, + 0x0090, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, + 0x02DC, 0x2122, 0x0161, 0x203A, 0x0153, 0x009D, 0x017E, 0x0178, + 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, + 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, + 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, + 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, + 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, + 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, + 0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, + 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF, + 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, + 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, + 0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, + 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF, +}; + +const uint16_t kCp1253High[128] = { + 0x20AC, 0x0081, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, + 0x0088, 0x2030, 0x008A, 0x2039, 0x008C, 0x008D, 0x008E, 0x008F, + 0x0090, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, + 0x0098, 0x2122, 0x009A, 0x203A, 0x009C, 0x009D, 0x009E, 0x009F, + 0x00A0, 0x0385, 0x0386, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, + 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x2015, + 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x0384, 0x00B5, 0x00B6, 0x00B7, + 0x0388, 0x0389, 0x038A, 0x00BB, 0x038C, 0x00BD, 0x038E, 0x038F, + 0x0390, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, + 0x0398, 0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F, + 0x03A0, 0x03A1, 0x00D2, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7, + 0x03A8, 0x03A9, 0x03AA, 0x03AB, 0x03AC, 0x03AD, 0x03AE, 0x03AF, + 0x03B0, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, + 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, + 0x03C0, 0x03C1, 0x03C2, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7, + 0x03C8, 0x03C9, 0x03CA, 0x03CB, 0x03CC, 0x03CD, 0x03CE, 0x00FF, +}; + +const uint16_t kCp1254High[128] = { + 0x20AC, 0x0081, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, + 0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, 0x008D, 0x008E, 0x008F, + 0x0090, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, + 0x02DC, 0x2122, 0x0161, 0x203A, 0x0153, 0x009D, 0x009E, 0x0178, + 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, + 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, + 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, + 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, + 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, + 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, + 0x011E, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, + 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x0130, 0x015E, 0x00DF, + 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, + 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, + 0x011F, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, + 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x0131, 0x015F, 0x00FF, +}; + +const uint16_t kCp1257High[128] = { + 0x20AC, 0x0081, 0x201A, 0x0083, 0x201E, 0x2026, 0x2020, 0x2021, + 0x0088, 0x2030, 0x008A, 0x2039, 0x008C, 0x00A8, 0x02C7, 0x00B8, + 0x0090, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, + 0x0098, 0x2122, 0x009A, 0x203A, 0x009C, 0x00AF, 0x02DB, 0x009F, + 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, + 0x00D8, 0x00A9, 0x0156, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00C6, + 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, + 0x00F8, 0x00B9, 0x0157, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00E6, + 0x0104, 0x012E, 0x0100, 0x0106, 0x00C4, 0x00C5, 0x0118, 0x0112, + 0x010C, 0x00C9, 0x0179, 0x0116, 0x0122, 0x0136, 0x012A, 0x013B, + 0x0160, 0x0143, 0x0145, 0x00D3, 0x014C, 0x00D5, 0x00D6, 0x00D7, + 0x0172, 0x0141, 0x015A, 0x016A, 0x00DC, 0x017B, 0x017D, 0x00DF, + 0x0105, 0x012F, 0x0101, 0x0107, 0x00E4, 0x00E5, 0x0119, 0x0113, + 0x010D, 0x00E9, 0x017A, 0x0117, 0x0123, 0x0137, 0x012B, 0x013C, + 0x0161, 0x0144, 0x0146, 0x00F3, 0x014D, 0x00F5, 0x00F6, 0x00F7, + 0x0173, 0x0142, 0x015B, 0x016B, 0x00FC, 0x017C, 0x017E, 0x02D9, +}; + +} // namespace mfcwx diff --git a/port/mfcwx/src/controls.cpp b/port/mfcwx/src/controls.cpp new file mode 100644 index 000000000..af06a5010 --- /dev/null +++ b/port/mfcwx/src/controls.cpp @@ -0,0 +1,2429 @@ +#include "windows_impl.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace mfcwx { + +// --------------------------------------------------------------------------------------------- +// Custom-drawn window used for views, owner-drawn controls, unknown window classes and statics +// that the application paints itself. + +class GenericWindow : public wxWindow { +public: + GenericWindow(wxWindow* parent, int id, const wxPoint& pos, const wxSize& size, long style) + : wxWindow(parent, id, pos, size, style | wxFULL_REPAINT_ON_RESIZE) {} + bool AcceptsFocus() const override { + WindowState* st = GetState(const_cast(this)); + return st && (st->style & WS_TABSTOP) != 0 ? wxWindow::AcceptsFocus() : HasFlag(wxWANTS_CHARS); + } + bool AcceptsFocusFromKeyboard() const override { + WindowState* st = GetState(const_cast(this)); + return st && (st->style & WS_TABSTOP) != 0; + } +}; + +namespace { + +struct EditExtra { + int limit = 0; + bool modified = false; + char passwordChar = 0; +}; + +struct RangeExtra { + int minValue = 0; + int maxValue = 100; + int step = 10; + int pos = 0; + bool inverted = false; +}; + +struct ButtonExtra { + bool pushed = false; + bool hover = false; + HBITMAP image = nullptr; + HICON icon = nullptr; +}; + +struct TabExtra { + std::vector> items; + int current = -1; +}; + +template +T& Extra(WindowState& st) { + if (!st.extra) { + st.extra = new T(); + st.deleteExtra = [&st] { delete static_cast(st.extra); st.extra = nullptr; }; + } + return *static_cast(st.extra); +} + +wxString AnsiText(const char* s) { return ToWx(s ? s : ""); } + +bool IsMultilineEdit(const WindowState& st) { + return (st.kind == ControlKind::Edit || st.kind == ControlKind::RichEdit) && (st.style & ES_MULTILINE); +} + +wxTextCtrl* AsText(wxWindow* w) { return wxDynamicCast(w, wxTextCtrl); } + +// Windows multiline edits count "\r\n" as two characters; wx counts one. +long WinToWxPos(wxTextCtrl* t, const WindowState& st, long winPos) { + if (!IsMultilineEdit(st) || winPos <= 0) + return winPos; + wxString v = t->GetValue(); + long wxPos = 0; + long win = 0; + for (wxString::const_iterator it = v.begin(); it != v.end() && win < winPos; ++it) { + win += *it == '\n' ? 2 : 1; + if (win <= winPos) + ++wxPos; + } + return wxPos; +} + +long WxToWinPos(wxTextCtrl* t, const WindowState& st, long wxPos) { + if (!IsMultilineEdit(st) || wxPos <= 0) + return wxPos; + wxString v = t->GetValue(); + long win = 0; + long i = 0; + for (wxString::const_iterator it = v.begin(); it != v.end() && i < wxPos; ++it, ++i) + win += *it == '\n' ? 2 : 1; + return win; +} + +wxString ToEditText(const WindowState& st, const wxString& s) { + if (!IsMultilineEdit(st)) + return s; + wxString r = s; + r.Replace("\r\n", "\n"); + r.Replace("\r", "\n"); + return r; +} + +wxString FromEditText(const WindowState& st, const wxString& s) { + if (!IsMultilineEdit(st)) + return s; + wxString r = s; + r.Replace("\n", "\r\n"); + return r; +} + +wxString StripMnemonicForNoPrefix(const char* text, DWORD style) { + wxString s = AnsiText(text); + if (style & SS_NOPREFIX) + s.Replace("&", "&&"); + return s; +} + +int ControlId(wxWindow* w) { + WindowState* st = GetState(w); + return st ? st->winId : 0; +} + +void WrapStatic(wxWindow* w, WindowState& st) { + auto* text = wxDynamicCast(w, wxStaticText); + if (!text) + return; + DWORD type = st.style & SS_TYPEMASK; + wxString label = (st.style & SS_NOPREFIX) ? wxString(st.text).Clone() : st.text; + if (st.style & SS_NOPREFIX) + label.Replace("&", "&&"); + st.settingText = true; + text->SetLabel(label); + if (type != SS_LEFTNOWORDWRAP && type != SS_SIMPLE && w->GetSize().x > 0) + text->Wrap(w->GetSize().x); + st.settingText = false; +} + +} // namespace + +wxString GetControlText(wxWindow* w) { + WindowState* st = GetState(w); + if (!st) + return w->GetLabel(); + switch (st->kind) { + case ControlKind::Edit: + case ControlKind::RichEdit: + if (auto* t = AsText(w)) + return FromEditText(*st, t->GetValue()); + break; + case ControlKind::ComboBox: + if (auto* c = wxDynamicCast(w, wxComboBox)) + return c->GetValue(); + break; + case ControlKind::ComboList: + if (auto* c = wxDynamicCast(w, wxChoice)) + return c->GetStringSelection(); + break; + case ControlKind::Scintilla: + if (auto* s = wxDynamicCast(w, wxStyledTextCtrl)) + return s->GetText(); + break; + case ControlKind::Static: + case ControlKind::Generic: + case ControlKind::OwnerDrawButton: + case ControlKind::StaticBitmap: + case ControlKind::StaticFrame: + return st->text; + case ControlKind::Dialog: + case ControlKind::Frame: + case ControlKind::MDIChild: + return st->text.empty() ? w->GetLabel() : st->text; + default: + break; + } + return w->GetLabel(); +} + +void SetControlText(wxWindow* w, const wxString& text) { + WindowState* st = GetState(w); + if (!st) { + w->SetLabel(text); + return; + } + switch (st->kind) { + case ControlKind::Edit: + case ControlKind::RichEdit: + if (auto* t = AsText(w)) { + wxString v = ToEditText(*st, text); + if (t->GetValue() != v) + t->SetValue(v); + else + t->SetValue(v); + Extra(*st).modified = false; + } + return; + case ControlKind::ComboBox: + if (auto* c = wxDynamicCast(w, wxComboBox)) { + st->settingText = true; + c->ChangeValue(text); + st->settingText = false; + } + return; + case ControlKind::ComboList: + if (auto* c = wxDynamicCast(w, wxChoice)) + c->SetStringSelection(text); + return; + case ControlKind::Scintilla: + if (auto* s = wxDynamicCast(w, wxStyledTextCtrl)) + s->SetText(text); + return; + case ControlKind::Static: + st->text = text; + WrapStatic(w, *st); + return; + case ControlKind::Generic: + case ControlKind::OwnerDrawButton: + case ControlKind::StaticBitmap: + case ControlKind::StaticFrame: + st->text = text; + w->Refresh(); + return; + case ControlKind::Dialog: + case ControlKind::Frame: + case ControlKind::MDIChild: + st->text = text; + w->SetLabel(text); + return; + default: + w->SetLabel(text); + return; + } +} + +DWORD CurrentStyle(wxWindow* w) { + WindowState* st = GetState(w); + DWORD style = st ? st->style : 0; + style = w->IsShown() ? (style | WS_VISIBLE) : (style & ~WS_VISIBLE); + style = w->IsThisEnabled() ? (style & ~WS_DISABLED) : (style | WS_DISABLED); + if (st && (st->kind == ControlKind::Edit || st->kind == ControlKind::RichEdit)) { + if (auto* t = AsText(w)) + style = t->IsEditable() ? (style & ~ES_READONLY) : (style | ES_READONLY); + } + if (auto* tlw = wxDynamicCast(w, wxTopLevelWindow)) { + if (tlw->IsMaximized()) + style |= WS_MAXIMIZE; + if (tlw->IsIconized()) + style |= WS_MINIMIZE; + } + return style; +} + +void ApplyStyle(wxWindow* w, DWORD oldStyle, DWORD newStyle) { + WindowState& st = EnsureState(w); + st.style = newStyle; + if ((oldStyle ^ newStyle) & WS_VISIBLE) + w->Show((newStyle & WS_VISIBLE) != 0); + if ((oldStyle ^ newStyle) & WS_DISABLED) + w->Enable((newStyle & WS_DISABLED) == 0); + if (st.kind == ControlKind::Edit || st.kind == ControlKind::RichEdit) { + if (auto* t = AsText(w)) { + if ((oldStyle ^ newStyle) & ES_READONLY) + t->SetEditable((newStyle & ES_READONLY) == 0); + } + } + if (st.kind == ControlKind::OwnerDrawButton || st.kind == ControlKind::Generic || st.kind == ControlKind::StaticFrame) + w->Refresh(); +} + +// --------------------------------------------------------------------------------------------- +// Notifications to the parent + +void NotifyParent(wxWindow* control, UINT code) { + wxWindow* parent = LogicalParent(control); + if (!parent || !IsManagedWindow(parent)) + return; + DispatchMessageTo(ToHwnd(parent), WM_COMMAND, MAKEWPARAM(ControlId(control), code), + reinterpret_cast(ToHwnd(control))); +} + +LRESULT NotifyParentNM(wxWindow* control, NMHDR* hdr) { + wxWindow* parent = LogicalParent(control); + if (!parent || !IsManagedWindow(parent)) + return 0; + hdr->hwndFrom = ToHwnd(control); + hdr->idFrom = static_cast(ControlId(control)); + return DispatchMessageTo(ToHwnd(parent), WM_NOTIFY, hdr->idFrom, reinterpret_cast(hdr)); +} + +void NotifyFocusChange(wxWindow* w, WindowState& st, bool gained) { + switch (st.kind) { + case ControlKind::Edit: + case ControlKind::RichEdit: + NotifyParent(w, gained ? EN_SETFOCUS : EN_KILLFOCUS); + break; + case ControlKind::ComboBox: + case ControlKind::ComboList: + NotifyParent(w, gained ? CBN_SETFOCUS : CBN_KILLFOCUS); + break; + case ControlKind::ListBox: + if (st.style & LBS_NOTIFY) + NotifyParent(w, gained ? LBN_SETFOCUS : LBN_KILLFOCUS); + break; + case ControlKind::PushButton: + case ControlKind::CheckBox: + case ControlKind::RadioButton: + case ControlKind::OwnerDrawButton: + if (st.style & BS_NOTIFY) + NotifyParent(w, gained ? BN_SETFOCUS : BN_KILLFOCUS); + if (st.kind == ControlKind::OwnerDrawButton) + w->Refresh(); + break; + case ControlKind::ListView: { + NMHDR hdr; + hdr.code = gained ? NM_SETFOCUS : NM_KILLFOCUS; + NotifyParentNM(w, &hdr); + break; + } + default: + break; + } +} + +bool FilterEditChar(wxWindow* w, WindowState& st, UINT& ch, wxKeyEvent& e) { + if (st.kind != ControlKind::Edit && st.kind != ControlKind::ComboBox) + return false; + if (ch >= 32) { + if ((st.style & ES_NUMBER) && st.kind == ControlKind::Edit && !(ch >= '0' && ch <= '9')) { + wxBell(); + return true; + } + if (st.style & ES_UPPERCASE) + ch = static_cast(toupper(static_cast(ch))); + else if (st.style & ES_LOWERCASE) + ch = static_cast(tolower(static_cast(ch))); + if (st.kind == ControlKind::Edit) { + EditExtra& ex = Extra(st); + auto* t = AsText(w); + if (t && ex.limit > 0) { + long from, to; + t->GetSelection(&from, &to); + long len = static_cast(FromEditText(st, t->GetValue()).length()); + if (len - (to - from) >= ex.limit) { + NotifyParent(w, EN_MAXTEXT); + return true; + } + } + } + if ((st.style & (ES_UPPERCASE | ES_LOWERCASE)) && !HasMessageHandler(st.permanent, WM_CHAR)) { + if (auto* t = AsText(w)) { + char a = static_cast(ch); + t->WriteText(ToWx(&a, 1)); + return true; + } + } + } + (void)e; + return false; +} + +// --------------------------------------------------------------------------------------------- +// Owner-drawn buttons and statics + +namespace { + +void SendDrawItem(wxWindow* w, WindowState& st, wxDC& dc, UINT ctlType) { + ButtonExtra& bx = Extra(st); + DRAWITEMSTRUCT dis; + memset(&dis, 0, sizeof dis); + dis.CtlType = ctlType; + dis.CtlID = static_cast(st.winId); + dis.itemID = 0; + dis.itemAction = ODA_DRAWENTIRE; + if (bx.pushed) + dis.itemState |= ODS_SELECTED; + if (!w->IsEnabled()) + dis.itemState |= ODS_DISABLED | ODS_GRAYED; + if (w->HasFocus()) + dis.itemState |= ODS_FOCUS; + dis.hwndItem = ToHwnd(w); + CDC* cdc = WrapDC(&dc, w); + dis.hDC = cdc->m_hDC; + wxSize sz = w->GetClientSize(); + dis.rcItem.right = sz.x; + dis.rcItem.bottom = sz.y; + wxWindow* parent = LogicalParent(w); + if (parent && IsManagedWindow(parent)) + DispatchMessageTo(ToHwnd(parent), WM_DRAWITEM, static_cast(st.winId), reinterpret_cast(&dis)); + UnwrapDC(cdc); +} + +void PaintOwnerDrawButton(wxWindow* w, wxPaintEvent&) { + wxPaintDC dc(w); + WindowState* st = GetState(w); + if (!st) + return; + SendDrawItem(w, *st, dc, ODT_BUTTON); +} + +void PaintStaticFrame(wxWindow* w, wxPaintEvent&) { + wxPaintDC dc(w); + WindowState* st = GetState(w); + if (!st) + return; + wxSize sz = w->GetClientSize(); + DWORD type = st->style & SS_TYPEMASK; + wxColour shadow = wxSystemSettings::GetColour(wxSYS_COLOUR_3DSHADOW); + wxColour light = wxSystemSettings::GetColour(wxSYS_COLOUR_3DHIGHLIGHT); + wxColour colour; + switch (type) { + case SS_BLACKRECT: case SS_BLACKFRAME: colour = wxColour(0, 0, 0); break; + case SS_GRAYRECT: case SS_GRAYFRAME: colour = shadow; break; + case SS_WHITERECT: case SS_WHITEFRAME: colour = wxColour(255, 255, 255); break; + default: colour = shadow; break; + } + switch (type) { + case SS_BLACKRECT: + case SS_GRAYRECT: + case SS_WHITERECT: + dc.SetPen(*wxTRANSPARENT_PEN); + dc.SetBrush(wxBrush(colour)); + dc.DrawRectangle(0, 0, sz.x, sz.y); + break; + case SS_ETCHEDHORZ: + dc.SetPen(wxPen(shadow)); + dc.DrawLine(0, 0, sz.x, 0); + dc.SetPen(wxPen(light)); + dc.DrawLine(0, 1, sz.x, 1); + break; + case SS_ETCHEDVERT: + dc.SetPen(wxPen(shadow)); + dc.DrawLine(0, 0, 0, sz.y); + dc.SetPen(wxPen(light)); + dc.DrawLine(1, 0, 1, sz.y); + break; + case SS_ETCHEDFRAME: + dc.SetBrush(*wxTRANSPARENT_BRUSH); + dc.SetPen(wxPen(shadow)); + dc.DrawRectangle(0, 0, sz.x - 1, sz.y - 1); + dc.SetPen(wxPen(light)); + dc.DrawRectangle(1, 1, sz.x - 1, sz.y - 1); + break; + default: + dc.SetBrush(*wxTRANSPARENT_BRUSH); + dc.SetPen(wxPen(colour)); + dc.DrawRectangle(0, 0, sz.x, sz.y); + break; + } +} + +void PaintGenericStatic(wxWindow* w, wxPaintEvent&) { + wxPaintDC dc(w); + WindowState* st = GetState(w); + if (!st) + return; + if ((st->style & SS_TYPEMASK) == SS_OWNERDRAW) { + SendDrawItem(w, *st, dc, ODT_STATIC); + return; + } + wxString text = st->text; + if (!(st->style & SS_NOPREFIX)) + text = wxStripMenuCodes(text, wxStrip_Mnemonics); + dc.SetFont(w->GetFont()); + dc.SetTextForeground(w->GetForegroundColour()); + wxSize sz = w->GetClientSize(); + int align = wxALIGN_LEFT; + if ((st->style & SS_TYPEMASK) == SS_CENTER) + align = wxALIGN_CENTER_HORIZONTAL; + else if ((st->style & SS_TYPEMASK) == SS_RIGHT) + align = wxALIGN_RIGHT; + if (st->style & SS_CENTERIMAGE) + align |= wxALIGN_CENTER_VERTICAL; + dc.DrawLabel(text, wxRect(0, 0, sz.x, sz.y), align); +} + +void BindOwnerDrawButton(wxWindow* w) { + w->Bind(wxEVT_LEFT_DOWN, [w](wxMouseEvent& e) { + WindowState* st = GetState(w); + if (st && w->IsEnabled()) { + Extra(*st).pushed = true; + w->CaptureMouse(); + w->Refresh(); + if (st->style & WS_TABSTOP) + w->SetFocus(); + } + e.Skip(); + }); + w->Bind(wxEVT_LEFT_DCLICK, [w](wxMouseEvent& e) { + WindowState* st = GetState(w); + if (st && w->IsEnabled()) { + Extra(*st).pushed = true; + if (!w->HasCapture()) + w->CaptureMouse(); + w->Refresh(); + } + e.Skip(); + }); + w->Bind(wxEVT_LEFT_UP, [w](wxMouseEvent& e) { + WindowState* st = GetState(w); + if (w->HasCapture()) + w->ReleaseMouse(); + if (st) { + ButtonExtra& bx = Extra(*st); + bool wasPushed = bx.pushed; + bx.pushed = false; + w->Refresh(); + if (wasPushed && wxRect(w->GetClientSize()).Contains(e.GetPosition())) + NotifyParent(w, BN_CLICKED); + } + e.Skip(); + }); + w->Bind(wxEVT_MOUSE_CAPTURE_LOST, [w](wxMouseCaptureLostEvent&) { + WindowState* st = GetState(w); + if (st) { + Extra(*st).pushed = false; + w->Refresh(); + } + }); + w->Bind(wxEVT_KEY_UP, [w](wxKeyEvent& e) { + if (e.GetKeyCode() == WXK_SPACE) + NotifyParent(w, BN_CLICKED); + else + e.Skip(); + }); +} + +void BindStaticNotify(wxWindow* w) { + w->Bind(wxEVT_LEFT_DOWN, [w](wxMouseEvent& e) { + WindowState* st = GetState(w); + if (st && (st->style & SS_NOTIFY)) + NotifyParent(w, STN_CLICKED); + e.Skip(); + }); + w->Bind(wxEVT_LEFT_DCLICK, [w](wxMouseEvent& e) { + WindowState* st = GetState(w); + if (st && (st->style & SS_NOTIFY)) + NotifyParent(w, STN_DBLCLK); + e.Skip(); + }); +} + +// --------------------------------------------------------------------------------------------- +// Tab control (header only, as in Windows) + +void PaintTabs(wxWindow* w, wxPaintEvent&) { + wxPaintDC dc(w); + WindowState* st = GetState(w); + if (!st) + return; + TabExtra& tx = Extra(*st); + dc.SetFont(w->GetFont()); + wxSize sz = w->GetClientSize(); + int tabH = dc.GetCharHeight() + 8; + wxColour shadow = wxSystemSettings::GetColour(wxSYS_COLOUR_3DSHADOW); + dc.SetPen(wxPen(shadow)); + dc.SetBrush(*wxTRANSPARENT_BRUSH); + dc.DrawRectangle(0, tabH - 1, sz.x, sz.y - tabH + 1); + int x = 2; + for (size_t i = 0; i < tx.items.size(); ++i) { + wxString label = wxStripMenuCodes(tx.items[i].first, wxStrip_Mnemonics); + int tw = dc.GetTextExtent(label).x + 16; + bool sel = static_cast(i) == tx.current; + wxRect r(x, sel ? 0 : 2, tw, tabH - (sel ? 0 : 2)); + dc.SetBrush(sel ? wxBrush(w->GetBackgroundColour()) : wxBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE))); + dc.SetPen(wxPen(shadow)); + dc.DrawRectangle(r); + if (sel) { + dc.SetPen(wxPen(w->GetBackgroundColour())); + dc.DrawLine(r.x + 1, r.GetBottom(), r.GetRight(), r.GetBottom()); + } + dc.SetTextForeground(w->GetForegroundColour()); + dc.DrawLabel(label, r, wxALIGN_CENTER); + x += tw; + } +} + +int TabHitTest(wxWindow* w, const wxPoint& p) { + WindowState* st = GetState(w); + if (!st) + return -1; + TabExtra& tx = Extra(*st); + wxClientDC dc(w); + dc.SetFont(w->GetFont()); + int tabH = dc.GetCharHeight() + 8; + if (p.y > tabH) + return -1; + int x = 2; + for (size_t i = 0; i < tx.items.size(); ++i) { + int tw = dc.GetTextExtent(wxStripMenuCodes(tx.items[i].first, wxStrip_Mnemonics)).x + 16; + if (p.x >= x && p.x < x + tw) + return static_cast(i); + x += tw; + } + return -1; +} + +void BindTabs(wxWindow* w) { + w->Bind(wxEVT_LEFT_DOWN, [w](wxMouseEvent& e) { + int hit = TabHitTest(w, e.GetPosition()); + WindowState* st = GetState(w); + if (hit >= 0 && st && hit != Extra(*st).current) { + NMHDR hdr; + hdr.code = TCN_SELCHANGING; + if (!NotifyParentNM(w, &hdr)) { + Extra(*st).current = hit; + w->Refresh(); + NMHDR hdr2; + hdr2.code = TCN_SELCHANGE; + NotifyParentNM(w, &hdr2); + } + } + e.Skip(); + }); +} + +// --------------------------------------------------------------------------------------------- +// Spin buttons + +RangeExtra& SpinRange(WindowState& st) { return Extra(st); } + +void UpdateSpinBuddy(wxWindow* w, WindowState& st) { + if (!(st.style & UDS_SETBUDDYINT) || !st.buddy || !st.buddy->m_hWnd) + return; + RangeExtra& r = SpinRange(st); + wxString text = wxString::Format("%d", r.pos); + if (!(st.style & UDS_NOTHOUSANDS) && std::abs(r.pos) >= 1000) { + text = wxString::Format("%d", r.pos); + } + SetControlText(st.buddy->GetWx(), text); + (void)w; +} + +int ReadSpinBuddy(WindowState& st) { + RangeExtra& r = SpinRange(st); + if ((st.style & UDS_SETBUDDYINT) && st.buddy && st.buddy->m_hWnd) { + long v; + if (GetControlText(st.buddy->GetWx()).ToLong(&v)) + r.pos = static_cast(v); + } + return r.pos; +} + +void SpinStep(wxWindow* w, int direction) { + WindowState* st = GetState(w); + if (!st) + return; + RangeExtra& r = SpinRange(*st); + int pos = ReadSpinBuddy(*st); + int lo = std::min(r.minValue, r.maxValue); + int hi = std::max(r.minValue, r.maxValue); + int delta = r.minValue > r.maxValue ? -direction : direction; + NMUPDOWN nm; + memset(&nm, 0, sizeof nm); + nm.hdr.code = UDN_DELTAPOS; + nm.iPos = pos; + nm.iDelta = delta; + if (NotifyParentNM(w, &nm.hdr)) + return; + int next = pos + nm.iDelta; + if (next > hi) + next = (st->style & UDS_WRAP) ? lo : hi; + if (next < lo) + next = (st->style & UDS_WRAP) ? hi : lo; + r.pos = next; + UpdateSpinBuddy(w, *st); + wxWindow* parent = LogicalParent(w); + if (parent && IsManagedWindow(parent)) { + UINT msg = (st->style & UDS_HORZ) ? WM_HSCROLL : WM_VSCROLL; + DispatchMessageTo(ToHwnd(parent), msg, MAKEWPARAM(SB_THUMBPOSITION, static_cast(next)), + reinterpret_cast(ToHwnd(w))); + DispatchMessageTo(ToHwnd(parent), msg, MAKEWPARAM(SB_ENDSCROLL, static_cast(next)), + reinterpret_cast(ToHwnd(w))); + } +} + +// --------------------------------------------------------------------------------------------- +// Scroll bars and sliders + +UINT ScrollCodeFromEvent(const wxScrollEvent& e) { + wxEventType t = e.GetEventType(); + if (t == wxEVT_SCROLL_LINEUP) + return SB_LINEUP; + if (t == wxEVT_SCROLL_LINEDOWN) + return SB_LINEDOWN; + if (t == wxEVT_SCROLL_PAGEUP) + return SB_PAGEUP; + if (t == wxEVT_SCROLL_PAGEDOWN) + return SB_PAGEDOWN; + if (t == wxEVT_SCROLL_THUMBTRACK) + return SB_THUMBTRACK; + if (t == wxEVT_SCROLL_THUMBRELEASE) + return SB_THUMBPOSITION; + if (t == wxEVT_SCROLL_TOP) + return SB_TOP; + if (t == wxEVT_SCROLL_BOTTOM) + return SB_BOTTOM; + return SB_ENDSCROLL; +} + +void ForwardScroll(wxScrollEvent& e) { + wxWindow* w = static_cast(e.GetEventObject()); + WindowState* st = GetState(w); + wxWindow* parent = LogicalParent(w); + if (!st || !parent || !IsManagedWindow(parent)) + return; + bool vertical = e.GetOrientation() == wxVERTICAL; + UINT msg = vertical ? WM_VSCROLL : WM_HSCROLL; + UINT code = ScrollCodeFromEvent(e); + int pos = e.GetPosition(); + if (st->kind == ControlKind::Slider) { + RangeExtra& r = Extra(*st); + r.pos = pos; + } + DispatchMessageTo(ToHwnd(parent), msg, MAKEWPARAM(code, static_cast(pos)), reinterpret_cast(ToHwnd(w))); + if (e.GetEventType() == wxEVT_SCROLL_CHANGED || e.GetEventType() == wxEVT_SCROLL_THUMBRELEASE) + DispatchMessageTo(ToHwnd(parent), msg, MAKEWPARAM(SB_ENDSCROLL, static_cast(pos)), + reinterpret_cast(ToHwnd(w))); +} + +// --------------------------------------------------------------------------------------------- +// List view notifications + +void ListNotify(wxListEvent& e, UINT code, bool selected) { + wxWindow* w = static_cast(e.GetEventObject()); + NMLISTVIEW nm; + memset(&nm, 0, sizeof nm); + nm.hdr.code = code; + nm.iItem = static_cast(e.GetIndex()); + nm.iSubItem = e.GetColumn() < 0 ? 0 : e.GetColumn(); + if (code == LVN_ITEMCHANGED) { + nm.uChanged = LVIF_STATE; + nm.uNewState = selected ? (LVIS_SELECTED | LVIS_FOCUSED) : 0; + nm.uOldState = selected ? 0 : LVIS_SELECTED; + } + if (code == LVN_COLUMNCLICK) + nm.iItem = -1; + wxPoint p = e.GetPoint(); + nm.ptAction.x = p.x; + nm.ptAction.y = p.y; + if (auto* lc = wxDynamicCast(w, wxListCtrl)) + if (nm.iItem >= 0) + nm.lParam = static_cast(lc->GetItemData(nm.iItem)); + NotifyParentNM(w, &nm.hdr); +} + +} // namespace + +// --------------------------------------------------------------------------------------------- +// Event binding for standard controls + +void BindControlEvents(wxWindow* w, WindowState& st) { + switch (st.kind) { + case ControlKind::PushButton: + w->Bind(wxEVT_BUTTON, [w](wxCommandEvent&) { NotifyParent(w, BN_CLICKED); }); + w->Bind(wxEVT_TOGGLEBUTTON, [w](wxCommandEvent&) { NotifyParent(w, BN_CLICKED); }); + break; + case ControlKind::CheckBox: + w->Bind(wxEVT_CHECKBOX, [w](wxCommandEvent&) { + WindowState* s = GetState(w); + if (s) { + DWORD type = s->style & BS_TYPEMASK; + if (type == BS_CHECKBOX || type == BS_3STATE) { + // Non-automatic check boxes keep their state; the application changes it. + auto* cb = wxDynamicCast(w, wxCheckBox); + if (cb) + cb->Set3StateValue(s->checkState == 2 ? wxCHK_UNDETERMINED + : (s->checkState ? wxCHK_CHECKED : wxCHK_UNCHECKED)); + } else if (auto* cb = wxDynamicCast(w, wxCheckBox)) { + s->checkState = cb->Get3StateValue() == wxCHK_UNDETERMINED ? 2 : (cb->GetValue() ? 1 : 0); + } + } + NotifyParent(w, BN_CLICKED); + }); + w->Bind(wxEVT_TOGGLEBUTTON, [w](wxCommandEvent&) { NotifyParent(w, BN_CLICKED); }); + break; + case ControlKind::RadioButton: + w->Bind(wxEVT_RADIOBUTTON, [w](wxCommandEvent&) { + WindowState* s = GetState(w); + if (s && (s->style & BS_TYPEMASK) == BS_RADIOBUTTON) { + if (auto* rb = wxDynamicCast(w, wxRadioButton)) + if (!s->checkState) + rb->SetValue(false); + } + NotifyParent(w, BN_CLICKED); + }); + break; + case ControlKind::OwnerDrawButton: + st.customPaint = [w](wxPaintEvent& e) { PaintOwnerDrawButton(w, e); }; + BindOwnerDrawButton(w); + break; + case ControlKind::StaticFrame: + st.customPaint = [w](wxPaintEvent& e) { PaintStaticFrame(w, e); }; + BindStaticNotify(w); + break; + case ControlKind::Static: + case ControlKind::StaticBitmap: + BindStaticNotify(w); + break; + case ControlKind::Edit: + case ControlKind::RichEdit: + w->Bind(wxEVT_TEXT, [w](wxCommandEvent& e) { + WindowState* s = GetState(w); + if (s) { + Extra(*s).modified = true; + NotifyParent(w, EN_UPDATE); + NotifyParent(w, EN_CHANGE); + } + e.Skip(false); + }); + w->Bind(wxEVT_TEXT_MAXLEN, [w](wxCommandEvent&) { NotifyParent(w, EN_MAXTEXT); }); + break; + case ControlKind::ComboBox: + w->Bind(wxEVT_TEXT, [w](wxCommandEvent&) { + WindowState* s = GetState(w); + if (s && !s->settingText) { + NotifyParent(w, CBN_EDITUPDATE); + NotifyParent(w, CBN_EDITCHANGE); + } + }); + w->Bind(wxEVT_COMBOBOX, [w](wxCommandEvent&) { + NotifyParent(w, CBN_SELCHANGE); + NotifyParent(w, CBN_SELENDOK); + }); + w->Bind(wxEVT_COMBOBOX_DROPDOWN, [w](wxCommandEvent&) { NotifyParent(w, CBN_DROPDOWN); }); + w->Bind(wxEVT_COMBOBOX_CLOSEUP, [w](wxCommandEvent&) { NotifyParent(w, CBN_CLOSEUP); }); + break; + case ControlKind::ComboList: + w->Bind(wxEVT_CHOICE, [w](wxCommandEvent&) { + NotifyParent(w, CBN_SELCHANGE); + NotifyParent(w, CBN_SELENDOK); + NotifyParent(w, CBN_CLOSEUP); + }); + break; + case ControlKind::ListBox: + w->Bind(wxEVT_LISTBOX, [w](wxCommandEvent&) { + WindowState* s = GetState(w); + if (s && (s->style & LBS_NOTIFY)) + NotifyParent(w, LBN_SELCHANGE); + }); + w->Bind(wxEVT_LISTBOX_DCLICK, [w](wxCommandEvent&) { + WindowState* s = GetState(w); + if (s && (s->style & LBS_NOTIFY)) + NotifyParent(w, LBN_DBLCLK); + }); + break; + case ControlKind::ScrollBar: + case ControlKind::Slider: + w->Bind(wxEVT_SCROLL_TOP, &ForwardScroll); + w->Bind(wxEVT_SCROLL_BOTTOM, &ForwardScroll); + w->Bind(wxEVT_SCROLL_LINEUP, &ForwardScroll); + w->Bind(wxEVT_SCROLL_LINEDOWN, &ForwardScroll); + w->Bind(wxEVT_SCROLL_PAGEUP, &ForwardScroll); + w->Bind(wxEVT_SCROLL_PAGEDOWN, &ForwardScroll); + w->Bind(wxEVT_SCROLL_THUMBTRACK, &ForwardScroll); + w->Bind(wxEVT_SCROLL_THUMBRELEASE, &ForwardScroll); + w->Bind(wxEVT_SCROLL_CHANGED, &ForwardScroll); + if (st.kind == ControlKind::Slider) + w->Bind(wxEVT_LEFT_UP, [w](wxMouseEvent& e) { + NMHDR hdr; + hdr.code = NM_RELEASEDCAPTURE; + NotifyParentNM(w, &hdr); + e.Skip(); + }); + break; + case ControlKind::Spin: + w->Bind(wxEVT_SPIN_UP, [w](wxSpinEvent& e) { + SpinStep(w, +1); + e.Veto(); + }); + w->Bind(wxEVT_SPIN_DOWN, [w](wxSpinEvent& e) { + SpinStep(w, -1); + e.Veto(); + }); + break; + case ControlKind::ListView: + w->Bind(wxEVT_LIST_ITEM_SELECTED, [](wxListEvent& e) { ListNotify(e, LVN_ITEMCHANGED, true); }); + w->Bind(wxEVT_LIST_ITEM_DESELECTED, [](wxListEvent& e) { ListNotify(e, LVN_ITEMCHANGED, false); }); + w->Bind(wxEVT_LIST_ITEM_ACTIVATED, [](wxListEvent& e) { ListNotify(e, NM_DBLCLK, true); }); + w->Bind(wxEVT_LIST_ITEM_RIGHT_CLICK, [](wxListEvent& e) { ListNotify(e, NM_RCLICK, true); }); + w->Bind(wxEVT_LIST_COL_CLICK, [](wxListEvent& e) { ListNotify(e, LVN_COLUMNCLICK, false); }); + w->Bind(wxEVT_LIST_ITEM_CHECKED, [](wxListEvent& e) { ListNotify(e, LVN_ITEMCHANGED, true); }); + w->Bind(wxEVT_LIST_ITEM_UNCHECKED, [](wxListEvent& e) { ListNotify(e, LVN_ITEMCHANGED, true); }); + w->Bind(wxEVT_LIST_KEY_DOWN, [w](wxListEvent& e) { + NMLVKEYDOWN nm; + memset(&nm, 0, sizeof nm); + nm.hdr.code = LVN_KEYDOWN; + nm.wVKey = static_cast(VirtualKeyFromWx(e.GetKeyCode())); + NotifyParentNM(w, &nm.hdr); + e.Skip(); + }); + w->Bind(wxEVT_LEFT_UP, [w](wxMouseEvent& e) { + NMITEMACTIVATE nm; + memset(&nm, 0, sizeof nm); + nm.hdr.code = NM_CLICK; + int flags = 0; + long sub = 0; + if (auto* lc = wxDynamicCast(w, wxListCtrl)) + nm.iItem = static_cast(lc->HitTest(e.GetPosition(), flags, &sub)); + nm.iSubItem = static_cast(sub); + nm.ptAction.x = e.GetX(); + nm.ptAction.y = e.GetY(); + NotifyParentNM(w, &nm.hdr); + e.Skip(); + }); + break; + case ControlKind::Tab: + st.customPaint = [w](wxPaintEvent& e) { PaintTabs(w, e); }; + BindTabs(w); + break; + case ControlKind::Scintilla: + BindScintillaEvents(w); + break; + default: + break; + } +} + +// --------------------------------------------------------------------------------------------- +// Creation + +namespace { + +wxFont DialogFont(); + +bool ClassIs(const char* cls, const char* name) { return cls && strcasecmp(cls, name) == 0; } + +long BorderFlags(DWORD style, DWORD exStyle, bool defaultBorder) { + if (exStyle & (WS_EX_CLIENTEDGE | WS_EX_STATICEDGE)) + return wxBORDER_SUNKEN; + if (style & WS_BORDER) + return wxBORDER_SIMPLE; + return defaultBorder ? wxBORDER_DEFAULT : wxBORDER_NONE; +} + +} // namespace + +wxWindow* CreateControl(wxWindow* parent, const char* cls, const char* text, int winId, const wxRect& rect, + DWORD style, DWORD exStyle) { + const int id = ToWxId(winId); + const wxPoint pos = rect.GetPosition(); + const wxSize size = rect.GetSize(); + wxWindow* w = nullptr; + ControlKind kind = ControlKind::Generic; + wxString label = AnsiText(text); + + if (ClassIs(cls, "Button")) { + DWORD type = style & BS_TYPEMASK; + if (type == BS_GROUPBOX) { + w = new wxStaticBox(parent, id, label, pos, size); + kind = ControlKind::GroupBox; + } else if (type == BS_OWNERDRAW) { + w = new GenericWindow(parent, id, pos, size, wxBORDER_NONE); + kind = ControlKind::OwnerDrawButton; + } else if (type == BS_CHECKBOX || type == BS_AUTOCHECKBOX || type == BS_3STATE || type == BS_AUTO3STATE) { + if (style & BS_PUSHLIKE) { + w = new wxToggleButton(parent, id, label, pos, size); + } else { + long f = (type == BS_3STATE || type == BS_AUTO3STATE) ? wxCHK_3STATE | wxCHK_ALLOW_3RD_STATE_FOR_USER + : wxCHK_2STATE; + if (style & BS_LEFTTEXT) + f |= wxALIGN_RIGHT; + w = new wxCheckBox(parent, id, label, pos, size, f); + } + kind = ControlKind::CheckBox; + } else if (type == BS_RADIOBUTTON || type == BS_AUTORADIOBUTTON) { + long f = 0; + if (style & WS_GROUP) + f |= wxRB_GROUP; + else { + wxWindowList& siblings = parent->GetChildren(); + for (auto it = siblings.rbegin(); it != siblings.rend(); ++it) { + WindowState* s = GetState(*it); + if (!s) + continue; + if (s->kind == ControlKind::RadioButton) + break; + if (s->style & WS_GROUP) { + f |= wxRB_GROUP; + break; + } + } + } + w = new wxRadioButton(parent, id, label, pos, size, f); + kind = ControlKind::RadioButton; + } else { + long f = 0; + if ((style & BS_CENTER) == BS_LEFT) + f |= wxBU_LEFT; + else if ((style & BS_CENTER) == BS_RIGHT) + f |= wxBU_RIGHT; + if ((style & (BS_BITMAP | BS_ICON)) && label.empty()) + f |= wxBU_EXACTFIT | wxBU_NOTEXT; + w = new wxButton(parent, id, label, pos, size, f); + kind = ControlKind::PushButton; + } + } else if (ClassIs(cls, "Edit") || ClassIs(cls, "RICHEDIT") || ClassIs(cls, "RichEdit20A") || + ClassIs(cls, "RichEdit20W") || ClassIs(cls, "RICHEDIT50W")) { + bool rich = !ClassIs(cls, "Edit"); + long f = BorderFlags(style, exStyle, false); + if ((style & ES_MULTILINE) || rich) { + f |= wxTE_MULTILINE; + if (style & ES_AUTOHSCROLL || style & WS_HSCROLL) + f |= wxTE_DONTWRAP | wxHSCROLL; + else + f |= wxTE_WORDWRAP; + if (!(style & WS_VSCROLL) && !rich) + f |= wxTE_NO_VSCROLL; + } + if (rich) + f |= wxTE_RICH2; + if (style & ES_READONLY) + f |= wxTE_READONLY; + if (style & ES_PASSWORD) + f |= wxTE_PASSWORD; + if ((style & 3) == ES_CENTER) + f |= wxTE_CENTRE; + else if ((style & 3) == ES_RIGHT) + f |= wxTE_RIGHT; + if (style & ES_NOHIDESEL) + f |= wxTE_NOHIDESEL; + w = new wxTextCtrl(parent, id, wxString(), pos, size, f); + kind = rich ? ControlKind::RichEdit : ControlKind::Edit; + if (!label.empty()) { + static_cast(w)->ChangeValue(label); + } + } else if (ClassIs(cls, "Static")) { + DWORD type = style & SS_TYPEMASK; + if (type == SS_BITMAP || type == SS_ICON) { + wxBitmap bmp; + if (type == SS_BITMAP) + bmp = LoadBitmapResource(ResRef::From(text)); + else { + wxIcon icon = LoadIconResource(ResRef::From(text)); + if (icon.IsOk()) + bmp.CopyFromIcon(icon); + } + wxSize s = size; + if (bmp.IsOk() && !(style & SS_REALSIZECONTROL) && !(style & SS_CENTERIMAGE)) + s = bmp.GetSize(); + w = new wxStaticBitmap(parent, id, bmp.IsOk() ? bmp : wxBitmap(1, 1), pos, s); + kind = ControlKind::StaticBitmap; + } else if ((type >= SS_BLACKRECT && type <= SS_WHITEFRAME) || type == SS_ETCHEDHORZ || type == SS_ETCHEDVERT || + type == SS_ETCHEDFRAME) { + w = new GenericWindow(parent, id, pos, size, wxBORDER_NONE); + kind = ControlKind::StaticFrame; + } else if (type == SS_OWNERDRAW || type == SS_USERITEM) { + w = new GenericWindow(parent, id, pos, size, wxBORDER_NONE); + kind = ControlKind::Generic; + EnsureState(w).customPaint = [w](wxPaintEvent& e) { PaintGenericStatic(w, e); }; + } else { + long f = wxST_NO_AUTORESIZE | BorderFlags(style, exStyle, false); + if (style & SS_SUNKEN) + f |= wxBORDER_SUNKEN; + if (type == SS_CENTER) + f |= wxALIGN_CENTRE_HORIZONTAL; + else if (type == SS_RIGHT) + f |= wxALIGN_RIGHT; + if (style & SS_ENDELLIPSIS) + f |= wxST_ELLIPSIZE_END; + w = new wxStaticText(parent, id, wxString(), pos, size, f); + kind = ControlKind::Static; + } + } else if (ClassIs(cls, "ComboBox")) { + DWORD type = style & 3; + long f = (style & CBS_SORT) ? wxCB_SORT : 0; + if (type == CBS_DROPDOWNLIST) { + w = new wxChoice(parent, id, pos, wxSize(size.x, -1), 0, nullptr, f); + kind = ControlKind::ComboList; + } else { + w = new wxComboBox(parent, id, wxString(), pos, wxSize(size.x, -1), 0, nullptr, f); + kind = ControlKind::ComboBox; + } + } else if (ClassIs(cls, "ListBox")) { + long f = BorderFlags(style, exStyle, true); + if (style & LBS_MULTIPLESEL) + f |= wxLB_MULTIPLE; + else if (style & LBS_EXTENDEDSEL) + f |= wxLB_EXTENDED; + else + f |= wxLB_SINGLE; + if (style & LBS_SORT) + f |= wxLB_SORT; + if (style & WS_HSCROLL) + f |= wxLB_HSCROLL; + if (style & LBS_DISABLENOSCROLL) + f |= wxLB_ALWAYS_SB; + w = new wxListBox(parent, id, pos, size, 0, nullptr, f); + kind = ControlKind::ListBox; + } else if (ClassIs(cls, "ScrollBar")) { + w = new wxScrollBar(parent, id, pos, size, (style & SBS_VERT) ? wxSB_VERTICAL : wxSB_HORIZONTAL); + kind = ControlKind::ScrollBar; + } else if (ClassIs(cls, "msctls_progress32")) { + w = new wxGauge(parent, id, 100, pos, size, (style & PBS_VERTICAL) ? wxGA_VERTICAL : wxGA_HORIZONTAL); + kind = ControlKind::Progress; + } else if (ClassIs(cls, "msctls_trackbar32")) { + long f = (style & TBS_VERT) ? wxSL_VERTICAL : wxSL_HORIZONTAL; + if (style & TBS_AUTOTICKS) + f |= wxSL_AUTOTICKS; + if (style & TBS_BOTH) + f |= wxSL_BOTH; + else if (style & TBS_TOP) + f |= (style & TBS_VERT) ? wxSL_LEFT : wxSL_TOP; + w = new wxSlider(parent, id, 0, 0, 100, pos, size, f); + kind = ControlKind::Slider; + } else if (ClassIs(cls, "msctls_updown32")) { + long f = (style & UDS_HORZ) ? wxSP_HORIZONTAL : wxSP_VERTICAL; + f |= wxSP_ARROW_KEYS; + if (style & UDS_WRAP) + f |= wxSP_WRAP; + w = new wxSpinButton(parent, id, pos, size, f); + static_cast(w)->SetRange(-1000000, 1000000); + kind = ControlKind::Spin; + } else if (ClassIs(cls, "SysListView32")) { + long f = BorderFlags(style, exStyle, true); + switch (style & LVS_TYPEMASK) { + case LVS_REPORT: f |= wxLC_REPORT; break; + case LVS_LIST: f |= wxLC_LIST; break; + case LVS_SMALLICON: f |= wxLC_SMALL_ICON; break; + default: f |= wxLC_ICON; break; + } + if (style & LVS_SINGLESEL) + f |= wxLC_SINGLE_SEL; + if (style & LVS_NOCOLUMNHEADER) + f |= wxLC_NO_HEADER; + if (style & LVS_EDITLABELS) + f |= wxLC_EDIT_LABELS; + if (style & LVS_SORTASCENDING) + f |= wxLC_SORT_ASCENDING; + else if (style & LVS_SORTDESCENDING) + f |= wxLC_SORT_DESCENDING; + w = new wxListCtrl(parent, id, pos, size, f); + kind = ControlKind::ListView; + } else if (ClassIs(cls, "SysTreeView32")) { + long f = BorderFlags(style, exStyle, true) | wxTR_DEFAULT_STYLE; + if (style & TVS_HASLINES) + f |= wxTR_LINES_AT_ROOT; + w = new wxTreeCtrl(parent, id, pos, size, f); + kind = ControlKind::TreeView; + } else if (ClassIs(cls, "SysTabControl32")) { + w = new GenericWindow(parent, id, pos, size, wxBORDER_NONE); + kind = ControlKind::Tab; + } else if (ClassIs(cls, "Scintilla")) { + w = new wxStyledTextCtrl(parent, id, pos, size, BorderFlags(style, exStyle, false)); + kind = ControlKind::Scintilla; + } else { + long f = BorderFlags(style, exStyle, false); + w = new GenericWindow(parent, id, pos, size, f); + kind = ControlKind::Generic; + } + + HookWindow(w, kind, winId, style, exStyle); + WindowState& st = EnsureState(w); + if (kind == ControlKind::Static || kind == ControlKind::Generic || kind == ControlKind::OwnerDrawButton || + kind == ControlKind::StaticFrame || kind == ControlKind::StaticBitmap) { + st.text = label; + if (kind == ControlKind::Static) + WrapStatic(w, st); + } + if (kind == ControlKind::Static || kind == ControlKind::GroupBox) { + // Statics are never in the tab order. + st.style &= ~WS_TABSTOP; + } + if (kind == ControlKind::Slider) { + Extra(st).maxValue = 100; + } + if (style & WS_DISABLED) + w->Disable(); + if (!(style & WS_VISIBLE)) + w->Hide(); + return w; +} + +wxWindow* CreateWindowForClass(CWnd* pWnd, CREATESTRUCT& cs) { + wxWindow* parent = cs.hwndParent ? ToWx(cs.hwndParent) : nullptr; + const char* cls = cs.lpszClass && !IS_INTRESOURCE(cs.lpszClass) ? cs.lpszClass : ""; + DWORD style = static_cast(cs.style); + bool child = (style & WS_CHILD) != 0; + if (!child || !parent) { + long f = wxDEFAULT_FRAME_STYLE; + if (!(style & WS_THICKFRAME)) + f &= ~wxRESIZE_BORDER; + if (!(style & WS_CAPTION)) + f &= ~wxCAPTION; + wxFrame* frame = new wxFrame(parent ? wxGetTopLevelParent(parent) : nullptr, wxID_ANY, AnsiText(cs.lpszName), + cs.x == CW_USEDEFAULT ? wxDefaultPosition : wxPoint(cs.x, cs.y), + cs.cx == CW_USEDEFAULT ? wxDefaultSize : wxSize(cs.cx, cs.cy), + f | (style & WS_POPUP ? wxFRAME_TOOL_WINDOW | wxFRAME_FLOAT_ON_PARENT : 0)); + HookWindow(frame, ControlKind::Frame, 0, style, cs.dwExStyle); + EnsureState(frame).text = AnsiText(cs.lpszName); + return frame; + } + int winId = static_cast(reinterpret_cast(cs.hMenu)); + wxRect rect(cs.x, cs.y, cs.cx, cs.cy); + static const char* const standard[] = {"Button", "Edit", "Static", "ComboBox", "ListBox", "ScrollBar", + "msctls_progress32", "msctls_trackbar32", "msctls_updown32", + "SysListView32", "SysTreeView32", "SysTabControl32", "Scintilla", + "RICHEDIT", "RichEdit20A", "RichEdit20W"}; + for (const char* s : standard) + if (ClassIs(cls, s)) + return CreateControl(parent, cls, cs.lpszName, winId, rect, style, cs.dwExStyle); + long f = BorderFlags(style, cs.dwExStyle, false); + if (style & WS_VSCROLL) + f |= wxVSCROLL; + if (style & WS_HSCROLL) + f |= wxHSCROLL; + if (pWnd && pWnd->IsKindOf(RUNTIME_CLASS(CView))) + f |= wxWANTS_CHARS; + auto* w = new GenericWindow(parent, ToWxId(winId), rect.GetPosition(), rect.GetSize(), f); + HookWindow(w, ControlKind::Generic, winId, style, cs.dwExStyle); + EnsureState(w).text = AnsiText(cs.lpszName); + if (!(style & WS_VISIBLE)) + w->Hide(); + if (style & WS_DISABLED) + w->Disable(); + return w; +} + +wxWindow* PrepareForSubclass(wxWindow* w, CWnd* pWnd) { + WindowState* st = GetState(w); + if (!st || !pWnd) + return w; + bool paints = HasMessageHandler(pWnd, WM_PAINT) || HasMessageHandler(pWnd, WM_ERASEBKGND); + bool nativeStatic = st->kind == ControlKind::Static || st->kind == ControlKind::StaticBitmap; + bool ownerDrawStatic = st->kind == ControlKind::Static && (st->style & SS_TYPEMASK) == SS_OWNERDRAW; + if (!(paints && nativeStatic) && !ownerDrawStatic) + return w; + wxWindow* parent = w->GetParent(); + wxRect rect = w->GetRect(); + auto* g = new GenericWindow(parent, w->GetId(), rect.GetPosition(), rect.GetSize(), wxBORDER_NONE); + g->MoveAfterInTabOrder(w); + g->SetFont(w->GetFont()); + if (w->GetForegroundColour().IsOk()) + g->SetForegroundColour(w->GetForegroundColour()); + WindowState& gs = EnsureState(g); + HookWindow(g, ControlKind::Generic, st->winId, st->style, st->exStyle); + gs.text = st->text; + gs.font = st->font; + if (!HasMessageHandler(pWnd, WM_PAINT)) + gs.customPaint = [g](wxPaintEvent& e) { PaintGenericStatic(g, e); }; + g->Show(w->IsShown()); + g->Enable(w->IsThisEnabled()); + w->Destroy(); + return g; +} + +// --------------------------------------------------------------------------------------------- +// Control message processing + +namespace { + +LRESULT EditProc(wxTextCtrl* t, WindowState& st, UINT msg, WPARAM wParam, LPARAM lParam, bool& handled) { + handled = true; + EditExtra& ex = Extra(st); + switch (msg) { + case EM_GETSEL: { + long from, to; + t->GetSelection(&from, &to); + from = WxToWinPos(t, st, from); + to = WxToWinPos(t, st, to); + if (wParam) + *reinterpret_cast(wParam) = static_cast(from); + if (lParam) + *reinterpret_cast(lParam) = static_cast(to); + return MAKELRESULT(from, to); + } + case EM_SETSEL: { + long from = static_cast(static_cast(wParam)); + long to = static_cast(static_cast(lParam)); + long len = t->GetLastPosition(); + if (from < 0) { + t->SetInsertionPoint(t->GetInsertionPoint()); + t->SelectNone(); + return 0; + } + from = WinToWxPos(t, st, from); + to = to < 0 ? len : WinToWxPos(t, st, to); + t->SetSelection(std::min(from, len), std::min(to, len)); + return 0; + } + case EM_EXSETSEL: { + auto* cr = reinterpret_cast(lParam); + long len = t->GetLastPosition(); + long to = cr->cpMax < 0 ? len : WinToWxPos(t, st, cr->cpMax); + t->SetSelection(WinToWxPos(t, st, cr->cpMin), std::min(to, len)); + return 0; + } + case EM_EXGETSEL: { + long from, to; + t->GetSelection(&from, &to); + auto* cr = reinterpret_cast(lParam); + cr->cpMin = WxToWinPos(t, st, from); + cr->cpMax = WxToWinPos(t, st, to); + return 0; + } + case EM_REPLACESEL: + t->WriteText(ToEditText(st, AnsiText(reinterpret_cast(lParam)))); + return 0; + case EM_GETMODIFY: + return ex.modified || t->IsModified(); + case EM_SETMODIFY: + ex.modified = wParam != 0; + if (!wParam) + t->DiscardEdits(); + else + t->MarkDirty(); + return 0; + case EM_GETLINECOUNT: + return std::max(1, t->GetNumberOfLines()); + case EM_LINEINDEX: { + int line = static_cast(static_cast(wParam)); + if (line < 0) { + long x, y; + t->PositionToXY(t->GetInsertionPoint(), &x, &y); + line = static_cast(y); + } + if (line >= t->GetNumberOfLines()) + return -1; + return WxToWinPos(t, st, t->XYToPosition(0, line)); + } + case EM_LINELENGTH: { + long x, y; + long pos = static_cast(wParam) < 0 ? t->GetInsertionPoint() : WinToWxPos(t, st, static_cast(wParam)); + if (!t->PositionToXY(pos, &x, &y)) + return 0; + return t->GetLineLength(y); + } + case EM_LINEFROMCHAR: + case EM_EXLINEFROMCHAR: { + long pos = static_cast(wParam) < 0 ? t->GetInsertionPoint() : WinToWxPos(t, st, static_cast(msg == EM_EXLINEFROMCHAR ? lParam : static_cast(wParam))); + long x, y; + if (!t->PositionToXY(pos, &x, &y)) + return std::max(0, t->GetNumberOfLines() - 1); + return y; + } + case EM_GETLINE: { + int line = static_cast(wParam); + char* buf = reinterpret_cast(lParam); + WORD max = *reinterpret_cast(buf); + if (line >= t->GetNumberOfLines()) + return 0; + std::string a = FromWx(t->GetLineText(line)); + size_t n = std::min(a.size(), max); + memcpy(buf, a.data(), n); + return static_cast(n); + } + case EM_LIMITTEXT: + ex.limit = static_cast(wParam); + if (!(st.style & ES_MULTILINE)) + t->SetMaxLength(static_cast(wParam)); + return 0; + case EM_GETLIMITTEXT: + return ex.limit > 0 ? ex.limit : 30000; + case EM_EXLIMITTEXT: + ex.limit = static_cast(lParam); + return 0; + case EM_CANUNDO: + return t->CanUndo(); + case EM_UNDO: + case WM_UNDO: + t->Undo(); + return TRUE; + case EM_REDO: + t->Redo(); + return TRUE; + case EM_CANREDO: + return t->CanRedo(); + case EM_EMPTYUNDOBUFFER: + return 0; + case EM_SETREADONLY: + t->SetEditable(wParam == 0); + if (wParam) + st.style |= ES_READONLY; + else + st.style &= ~ES_READONLY; + return TRUE; + case EM_SETPASSWORDCHAR: + ex.passwordChar = static_cast(wParam); + return 0; + case EM_GETPASSWORDCHAR: + return static_cast(ex.passwordChar); + case EM_GETFIRSTVISIBLELINE: + return 0; + case EM_SCROLLCARET: + t->ShowPosition(t->GetInsertionPoint()); + return TRUE; + case EM_LINESCROLL: + return TRUE; + case EM_SETTABSTOPS: + case EM_SETMARGINS: + case EM_SETRECT: + case EM_SETRECTNP: + case EM_SETEVENTMASK: + case EM_SETOPTIONS: + case EM_SETTARGETDEVICE: + case EM_AUTOURLDETECT: + case EM_SETUNDOLIMIT: + case EM_HIDESELECTION: + return 0; + case EM_GETEVENTMASK: + return 0; + case EM_GETRECT: { + auto* r = reinterpret_cast(lParam); + wxSize sz = t->GetClientSize(); + r->left = r->top = 0; + r->right = sz.x; + r->bottom = sz.y; + return 0; + } + case EM_POSFROMCHAR: { + wxPoint p(0, 0); + return MAKELRESULT(p.x, p.y); + } + case EM_CHARFROMPOS: { + long pos = 0; + wxTextCoord col, row; + if (t->HitTest(wxPoint(LOWORD(lParam), HIWORD(lParam)), &col, &row) != wxTE_HT_UNKNOWN) + pos = t->XYToPosition(col, row); + return MAKELRESULT(WxToWinPos(t, st, pos), 0); + } + case EM_SETBKGNDCOLOR: + if (!wParam) { + t->SetBackgroundColour(ToWxColour(static_cast(lParam))); + t->Refresh(); + } + return 0; + case EM_GETSELTEXT: { + std::string a = FromWx(FromEditText(st, t->GetStringSelection())); + strcpy(reinterpret_cast(lParam), a.c_str()); + return static_cast(a.size()); + } + case EM_GETTEXTRANGE: { + auto* tr = reinterpret_cast(lParam); + wxString v = FromEditText(st, t->GetValue()); + long to = tr->chrg.cpMax < 0 ? static_cast(v.length()) : tr->chrg.cpMax; + std::string a = FromWx(v.Mid(tr->chrg.cpMin, to - tr->chrg.cpMin)); + strcpy(tr->lpstrText, a.c_str()); + return static_cast(a.size()); + } + case EM_SETCHARFORMAT: { + auto* cf = reinterpret_cast(lParam); + wxTextAttr attr; + if (cf->dwMask & CFM_COLOR) + attr.SetTextColour((cf->dwEffects & CFE_AUTOCOLOR) ? t->GetForegroundColour() : ToWxColour(cf->crTextColor)); + if (cf->dwMask & CFM_BOLD) + attr.SetFontWeight((cf->dwEffects & CFE_BOLD) ? wxFONTWEIGHT_BOLD : wxFONTWEIGHT_NORMAL); + if (cf->dwMask & CFM_ITALIC) + attr.SetFontStyle((cf->dwEffects & CFE_ITALIC) ? wxFONTSTYLE_ITALIC : wxFONTSTYLE_NORMAL); + if (cf->dwMask & CFM_UNDERLINE) + attr.SetFontUnderlined((cf->dwEffects & CFE_UNDERLINE) != 0); + if ((cf->dwMask & CFM_SIZE) && cf->yHeight > 0) + attr.SetFontPointSize(cf->yHeight / 20); + if ((cf->dwMask & CFM_FACE) && cf->szFaceName[0]) + attr.SetFontFaceName(AnsiText(cf->szFaceName)); + if (wParam & SCF_SELECTION) { + long from, to; + t->GetSelection(&from, &to); + t->SetStyle(from, to, attr); + } else { + t->SetDefaultStyle(attr); + if (wParam & SCF_ALL) + t->SetStyle(0, t->GetLastPosition(), attr); + } + return TRUE; + } + case EM_GETCHARFORMAT: { + auto* cf = reinterpret_cast(lParam); + cf->dwMask = CFM_COLOR | CFM_BOLD | CFM_ITALIC | CFM_UNDERLINE | CFM_FACE | CFM_SIZE; + wxFont f = t->GetFont(); + cf->dwEffects = (f.GetWeight() >= wxFONTWEIGHT_BOLD ? CFE_BOLD : 0) | + (f.GetStyle() == wxFONTSTYLE_ITALIC ? CFE_ITALIC : 0) | (f.GetUnderlined() ? CFE_UNDERLINE : 0); + cf->yHeight = f.GetPointSize() * 20; + cf->crTextColor = FromWxColour(t->GetForegroundColour()); + std::string face = FromWx(f.GetFaceName()); + strncpy(cf->szFaceName, face.c_str(), sizeof cf->szFaceName - 1); + cf->szFaceName[sizeof cf->szFaceName - 1] = 0; + return cf->dwMask; + } + case EM_SETPARAFORMAT: + case EM_GETPARAFORMAT: + return TRUE; + case EM_STREAMIN: { + auto* es = reinterpret_cast(lParam); + std::string data; + BYTE buf[4096]; + for (;;) { + LONG got = 0; + if (es->pfnCallback(es->dwCookie, buf, sizeof buf, &got) != 0 || got <= 0) + break; + data.append(reinterpret_cast(buf), static_cast(got)); + } + wxString v = ToEditText(st, ToWx(data.data(), static_cast(data.size()))); + if (wParam & SFF_SELECTION) + t->WriteText(v); + else + t->SetValue(v); + return static_cast(data.size()); + } + case EM_STREAMOUT: { + auto* es = reinterpret_cast(lParam); + std::string a = FromWx(FromEditText(st, (wParam & SFF_SELECTION) ? t->GetStringSelection() : t->GetValue())); + size_t off = 0; + while (off < a.size()) { + LONG done = 0; + LONG chunk = static_cast(std::min(a.size() - off, 4096)); + if (es->pfnCallback(es->dwCookie, reinterpret_cast(&a[off]), chunk, &done) != 0 || done <= 0) + break; + off += static_cast(done); + } + return static_cast(off); + } + case EM_FINDTEXTEX: { + auto* ft = reinterpret_cast(lParam); + wxString v = FromEditText(st, t->GetValue()); + wxString needle = AnsiText(ft->lpstrText); + long start = ft->chrg.cpMin; + int pos = v.Mid(static_cast(start)).Find(needle); + if (pos == wxNOT_FOUND) + return -1; + ft->chrgText.cpMin = start + pos; + ft->chrgText.cpMax = start + pos + static_cast(needle.length()); + return ft->chrgText.cpMin; + } + case WM_CUT: + t->Cut(); + return 0; + case WM_COPY: + t->Copy(); + return 0; + case WM_PASTE: + t->Paste(); + return 0; + case WM_CLEAR: { + long from, to; + t->GetSelection(&from, &to); + if (from != to) + t->Remove(from, to); + return 0; + } + case WM_CHAR: { + UINT ch = static_cast(wParam); + if (!t->IsEditable() && ch >= 32) + return 0; + long from, to; + t->GetSelection(&from, &to); + switch (ch) { + case 1: + t->SelectAll(); + return 0; + case 3: + t->Copy(); + return 0; + case 22: + if (t->IsEditable()) + t->Paste(); + return 0; + case 24: + if (t->IsEditable()) + t->Cut(); + return 0; + case 26: + if (t->IsEditable()) + t->Undo(); + return 0; + case 8: + if (!t->IsEditable()) + return 0; + if (from != to) + t->Remove(from, to); + else if (from > 0) + t->Remove(from - 1, from); + return 0; + case 13: + if (st.style & ES_MULTILINE) + t->WriteText("\n"); + return 0; + case 9: + if ((st.style & ES_MULTILINE) && (st.style & ES_WANTRETURN)) + t->WriteText("\t"); + return 0; + default: + if (ch < 32 || ch == 127) + return 0; + break; + } + if (ex.limit > 0) { + long len = static_cast(FromEditText(st, t->GetValue()).length()); + if (len - (to - from) >= ex.limit) { + NotifyParent(t, EN_MAXTEXT); + return 0; + } + } + char a = static_cast(ch); + t->WriteText(ToWx(&a, 1)); + return 0; + } + case WM_KEYDOWN: { + UINT vk = static_cast(wParam); + long from, to; + t->GetSelection(&from, &to); + CurrentMessage* cm = GetCurrentMessageSlot(); + if (cm && cm->event && cm->msg.message == WM_KEYDOWN && cm->msg.wParam == wParam) { + cm->defaultCalled = true; + return 0; + } + if (vk == VK_DELETE && t->IsEditable()) { + if (from != to) + t->Remove(from, to); + else if (from < t->GetLastPosition()) + t->Remove(from, from + 1); + } else if (vk == VK_LEFT && from > 0) { + t->SetInsertionPoint(from - 1); + } else if (vk == VK_RIGHT && to < t->GetLastPosition()) { + t->SetInsertionPoint(to + 1); + } else if (vk == VK_HOME) { + t->SetInsertionPoint(0); + } else if (vk == VK_END) { + t->SetInsertionPointEnd(); + } + return 0; + } + case WM_GETDLGCODE: + return DLGC_WANTCHARS | DLGC_HASSETSEL | DLGC_WANTARROWS | ((st.style & ES_WANTRETURN) ? DLGC_WANTMESSAGE : 0); + default: + handled = false; + return 0; + } +} + +int ItemCount(wxItemContainerImmutable* c) { return static_cast(c->GetCount()); } + +LRESULT ComboProc(wxWindow* w, WindowState& st, UINT msg, WPARAM wParam, LPARAM lParam, bool& handled) { + handled = true; + auto* items = dynamic_cast(w); + auto* combo = wxDynamicCast(w, wxComboBox); + if (!items) { + handled = false; + return 0; + } + switch (msg) { + case CB_ADDSTRING: { + int n = items->Append(AnsiText(reinterpret_cast(lParam))); + return n; + } + case CB_INSERTSTRING: { + int index = static_cast(wParam); + wxString s = AnsiText(reinterpret_cast(lParam)); + if (index < 0 || index >= ItemCount(items)) + return items->Append(s); + return items->Insert(s, static_cast(index)); + } + case CB_DELETESTRING: { + int index = static_cast(wParam); + if (index < 0 || index >= ItemCount(items)) + return CB_ERR; + items->Delete(static_cast(index)); + return ItemCount(items); + } + case CB_RESETCONTENT: + items->Clear(); + if (combo) + combo->ChangeValue(wxString()); + return CB_OKAY; + case CB_GETCOUNT: + return ItemCount(items); + case CB_GETCURSEL: { + int sel = items->GetSelection(); + return sel == wxNOT_FOUND ? CB_ERR : sel; + } + case CB_SETCURSEL: { + int index = static_cast(wParam); + if (index < 0 || index >= ItemCount(items)) { + items->SetSelection(wxNOT_FOUND); + if (combo) + combo->ChangeValue(wxString()); + return CB_ERR; + } + items->SetSelection(index); + return index; + } + case CB_GETLBTEXT: { + int index = static_cast(wParam); + if (index < 0 || index >= ItemCount(items)) + return CB_ERR; + std::string a = FromWx(items->GetString(static_cast(index))); + strcpy(reinterpret_cast(lParam), a.c_str()); + return static_cast(a.size()); + } + case CB_GETLBTEXTLEN: { + int index = static_cast(wParam); + if (index < 0 || index >= ItemCount(items)) + return CB_ERR; + return static_cast(FromWx(items->GetString(static_cast(index))).size()); + } + case CB_FINDSTRING: + case CB_FINDSTRINGEXACT: + case CB_SELECTSTRING: { + wxString s = AnsiText(reinterpret_cast(lParam)); + int n = ItemCount(items); + int start = static_cast(wParam); + for (int k = 0; k < n; ++k) { + int i = (start + 1 + k + n) % n; + if (start < 0) + i = k; + wxString item = items->GetString(static_cast(i)); + bool match = msg == CB_FINDSTRINGEXACT ? item.CmpNoCase(s) == 0 : item.Lower().StartsWith(s.Lower()); + if (match) { + if (msg == CB_SELECTSTRING) + items->SetSelection(i); + return i; + } + } + return CB_ERR; + } + case CB_GETITEMDATA: { + int index = static_cast(wParam); + if (index < 0 || index >= ItemCount(items)) + return CB_ERR; + return reinterpret_cast(items->GetClientData(static_cast(index))); + } + case CB_SETITEMDATA: { + int index = static_cast(wParam); + if (index < 0 || index >= ItemCount(items)) + return CB_ERR; + items->SetClientData(static_cast(index), reinterpret_cast(lParam)); + return CB_OKAY; + } + case CB_GETEDITSEL: + if (combo) { + long from, to; + combo->GetSelection(&from, &to); + return MAKELRESULT(from, to); + } + return 0; + case CB_SETEDITSEL: + if (combo) + combo->SetSelection(static_cast(LOWORD(lParam)), static_cast(HIWORD(lParam))); + return TRUE; + case CB_LIMITTEXT: + if (combo) + combo->SetMaxLength(static_cast(wParam)); + return TRUE; + case CB_SHOWDROPDOWN: + if (combo) { + if (wParam) + combo->Popup(); + else + combo->Dismiss(); + } + return TRUE; + case CB_GETDROPPEDSTATE: + return FALSE; + case CB_SETITEMHEIGHT: + case CB_SETEXTENDEDUI: + case CB_SETDROPPEDWIDTH: + case CB_SETHORIZONTALEXTENT: + case CB_INITSTORAGE: + return CB_OKAY; + case CB_GETITEMHEIGHT: + return w->GetCharHeight() + 2; + case WM_CUT: + if (combo) + combo->Cut(); + return 0; + case WM_COPY: + if (combo) + combo->Copy(); + return 0; + case WM_PASTE: + if (combo) + combo->Paste(); + return 0; + case WM_CLEAR: + if (combo) { + long from, to; + combo->GetSelection(&from, &to); + if (from != to) + combo->Remove(from, to); + } + return 0; + case WM_CHAR: + if (combo) { + UINT ch = static_cast(wParam); + if (ch == 8) { + long from, to; + combo->GetSelection(&from, &to); + if (from != to) + combo->Remove(from, to); + else if (from > 0) + combo->Remove(from - 1, from); + } else if (ch >= 32) { + char a = static_cast(ch); + combo->WriteText(ToWx(&a, 1)); + } + } + return 0; + default: + handled = false; + return 0; + } + (void)st; +} + +LRESULT ListBoxProc(wxListBox* lb, WindowState& st, UINT msg, WPARAM wParam, LPARAM lParam, bool& handled) { + handled = true; + int n = static_cast(lb->GetCount()); + int index = static_cast(wParam); + switch (msg) { + case LB_ADDSTRING: + return lb->Append(AnsiText(reinterpret_cast(lParam))); + case LB_INSERTSTRING: + if (index < 0 || index >= n) + return lb->Append(AnsiText(reinterpret_cast(lParam))); + return lb->Insert(AnsiText(reinterpret_cast(lParam)), static_cast(index)); + case LB_DELETESTRING: + if (index < 0 || index >= n) + return LB_ERR; + lb->Delete(static_cast(index)); + return n - 1; + case LB_RESETCONTENT: + lb->Clear(); + return 0; + case LB_GETCOUNT: + return n; + case LB_GETCURSEL: { + if (lb->HasMultipleSelection()) { + wxArrayInt sel; + lb->GetSelections(sel); + return sel.empty() ? LB_ERR : sel[0]; + } + int s = lb->GetSelection(); + return s == wxNOT_FOUND ? LB_ERR : s; + } + case LB_SETCURSEL: + if (index < 0 || index >= n) { + lb->SetSelection(wxNOT_FOUND); + return index < 0 ? 0 : LB_ERR; + } + lb->SetSelection(index); + return index; + case LB_GETSEL: + return index >= 0 && index < n && lb->IsSelected(index) ? 1 : 0; + case LB_SETSEL: { + int item = static_cast(lParam); + if (item == -1) { + for (int i = 0; i < n; ++i) + wParam ? lb->SetSelection(i) : lb->Deselect(i); + return 0; + } + if (item < 0 || item >= n) + return LB_ERR; + if (wParam) + lb->SetSelection(item); + else + lb->Deselect(item); + return 0; + } + case LB_GETSELCOUNT: { + wxArrayInt sel; + return lb->GetSelections(sel); + } + case LB_GETSELITEMS: { + wxArrayInt sel; + lb->GetSelections(sel); + int* out = reinterpret_cast(lParam); + int count = std::min(static_cast(sel.size()), index); + for (int i = 0; i < count; ++i) + out[i] = sel[static_cast(i)]; + return count; + } + case LB_SELITEMRANGE: + case LB_SELITEMRANGEEX: { + int first = LOWORD(lParam); + int last = HIWORD(lParam); + bool select = msg == LB_SELITEMRANGE ? wParam != 0 : first <= last; + if (msg == LB_SELITEMRANGEEX) { + first = static_cast(wParam); + last = static_cast(lParam); + if (first > last) + std::swap(first, last); + } + for (int i = std::max(0, first); i <= std::min(last, n - 1); ++i) + select ? lb->SetSelection(i) : lb->Deselect(i); + return 0; + } + case LB_GETTEXT: { + if (index < 0 || index >= n) + return LB_ERR; + std::string a = FromWx(lb->GetString(static_cast(index))); + strcpy(reinterpret_cast(lParam), a.c_str()); + return static_cast(a.size()); + } + case LB_GETTEXTLEN: + if (index < 0 || index >= n) + return LB_ERR; + return static_cast(FromWx(lb->GetString(static_cast(index))).size()); + case LB_FINDSTRING: + case LB_FINDSTRINGEXACT: + case LB_SELECTSTRING: { + wxString s = AnsiText(reinterpret_cast(lParam)); + for (int k = 0; k < n; ++k) { + int i = index < 0 ? k : (index + 1 + k) % n; + wxString item = lb->GetString(static_cast(i)); + bool match = msg == LB_FINDSTRINGEXACT ? item.CmpNoCase(s) == 0 : item.Lower().StartsWith(s.Lower()); + if (match) { + if (msg == LB_SELECTSTRING) + lb->SetSelection(i); + return i; + } + } + return LB_ERR; + } + case LB_GETITEMDATA: + if (index < 0 || index >= n) + return LB_ERR; + return reinterpret_cast(lb->GetClientData(static_cast(index))); + case LB_SETITEMDATA: + if (index < 0 || index >= n) + return LB_ERR; + lb->SetClientData(static_cast(index), reinterpret_cast(lParam)); + return 0; + case LB_GETTOPINDEX: + return 0; + case LB_SETTOPINDEX: + if (index >= 0 && index < n) + lb->SetFirstItem(index); + return 0; + case LB_GETCARETINDEX: { + int s = lb->GetSelection(); + return s == wxNOT_FOUND ? 0 : s; + } + case LB_SETCARETINDEX: + return 0; + case LB_GETITEMRECT: { + auto* r = reinterpret_cast(lParam); + int h = lb->GetCharHeight() + 2; + r->left = 0; + r->right = lb->GetClientSize().x; + r->top = index * h; + r->bottom = r->top + h; + return 0; + } + case LB_GETITEMHEIGHT: + return lb->GetCharHeight() + 2; + case LB_SETTABSTOPS: + case LB_SETITEMHEIGHT: + case LB_SETHORIZONTALEXTENT: + case LB_SETCOLUMNWIDTH: + case LB_SETANCHORINDEX: + return 0; + case LB_GETHORIZONTALEXTENT: + return 0; + default: + handled = false; + return 0; + } + (void)st; +} + +LRESULT ButtonProc(wxWindow* w, WindowState& st, UINT msg, WPARAM wParam, LPARAM lParam, bool& handled) { + handled = true; + switch (msg) { + case BM_GETCHECK: + if (auto* cb = wxDynamicCast(w, wxCheckBox)) + return cb->Get3StateValue() == wxCHK_UNDETERMINED ? BST_INDETERMINATE : (cb->GetValue() ? BST_CHECKED : BST_UNCHECKED); + if (auto* rb = wxDynamicCast(w, wxRadioButton)) + return rb->GetValue() ? BST_CHECKED : BST_UNCHECKED; + if (auto* tb = wxDynamicCast(w, wxToggleButton)) + return tb->GetValue() ? BST_CHECKED : BST_UNCHECKED; + return st.checkState; + case BM_SETCHECK: + st.checkState = static_cast(wParam); + if (auto* cb = wxDynamicCast(w, wxCheckBox)) { + if (cb->Is3State()) + cb->Set3StateValue(wParam == BST_INDETERMINATE ? wxCHK_UNDETERMINED + : (wParam == BST_CHECKED ? wxCHK_CHECKED : wxCHK_UNCHECKED)); + else + cb->SetValue(wParam == BST_CHECKED); + } else if (auto* rb = wxDynamicCast(w, wxRadioButton)) { + if (wParam == BST_CHECKED) + rb->SetValue(true); + else if (rb->GetValue()) { + // wx cannot uncheck the only checked radio button of a group directly. + rb->SetValue(false); + } + } else if (auto* tb = wxDynamicCast(w, wxToggleButton)) { + tb->SetValue(wParam == BST_CHECKED); + } else { + w->Refresh(); + } + return 0; + case BM_GETSTATE: { + LRESULT state = SendMessage(ToHwnd(w), BM_GETCHECK, 0, 0); + if (st.kind == ControlKind::OwnerDrawButton && Extra(st).pushed) + state |= BST_PUSHED; + if (w->HasFocus()) + state |= BST_FOCUS; + return state; + } + case BM_SETSTATE: + if (st.kind == ControlKind::OwnerDrawButton) { + Extra(st).pushed = wParam != 0; + w->Refresh(); + } + return 0; + case BM_SETSTYLE: { + DWORD old = st.style; + st.style = (st.style & ~0xFFFFu) | static_cast(wParam & 0xFFFF); + if ((old & BS_TYPEMASK) != (st.style & BS_TYPEMASK) && (st.style & BS_TYPEMASK) == BS_DEFPUSHBUTTON) { + if (auto* b = wxDynamicCast(w, wxButton)) + b->SetDefault(); + } + if (lParam) + w->Refresh(); + return 0; + } + case BM_CLICK: + NotifyParent(w, BN_CLICKED); + return 0; + case BM_SETIMAGE: { + ButtonExtra& bx = Extra(st); + LRESULT old = 0; + if (wParam == IMAGE_BITMAP) { + old = reinterpret_cast(bx.image); + bx.image = reinterpret_cast(lParam); + wxBitmap* bmp = BitmapFromHandle(bx.image); + if (auto* b = wxDynamicCast(w, wxButton)) { + if (bmp && bmp->IsOk()) + b->SetBitmap(*bmp); + } + } else if (wParam == IMAGE_ICON) { + old = reinterpret_cast(bx.icon); + bx.icon = reinterpret_cast(lParam); + if (auto* b = wxDynamicCast(w, wxButton)) { + wxIcon* icon = reinterpret_cast(bx.icon); + if (icon && icon->IsOk()) + b->SetBitmap(*icon); + } + } + w->Refresh(); + return old; + } + case BM_GETIMAGE: { + ButtonExtra& bx = Extra(st); + return wParam == IMAGE_ICON ? reinterpret_cast(bx.icon) : reinterpret_cast(bx.image); + } + case WM_GETDLGCODE: + if (st.kind == ControlKind::RadioButton) + return DLGC_BUTTON | DLGC_RADIOBUTTON; + return DLGC_BUTTON | ((st.style & BS_TYPEMASK) == BS_DEFPUSHBUTTON ? DLGC_DEFPUSHBUTTON : DLGC_UNDEFPUSHBUTTON); + default: + handled = false; + return 0; + } +} + +LRESULT StaticProc(wxWindow* w, WindowState& st, UINT msg, WPARAM wParam, LPARAM lParam, bool& handled) { + handled = true; + switch (msg) { + case STM_SETIMAGE: + case STM_SETICON: { + LRESULT old = reinterpret_cast(st.image); + st.image = reinterpret_cast(lParam); + auto* sb = wxDynamicCast(w, wxStaticBitmap); + if (sb) { + if (msg == STM_SETICON || wParam == IMAGE_ICON) { + wxIcon* icon = reinterpret_cast(lParam); + if (icon && icon->IsOk()) + sb->SetIcon(*icon); + } else { + wxBitmap* bmp = BitmapFromHandle(reinterpret_cast(lParam)); + if (bmp && bmp->IsOk()) { + sb->SetBitmap(*bmp); + if (!(st.style & SS_REALSIZECONTROL) && !(st.style & SS_CENTERIMAGE)) + sb->SetSize(bmp->GetSize()); + } + } + } else { + w->Refresh(); + } + return old; + } + case STM_GETIMAGE: + case STM_GETICON: + return reinterpret_cast(st.image); + case WM_GETDLGCODE: + return DLGC_STATIC; + default: + handled = false; + return 0; + } +} + +LRESULT RangeProc(wxWindow* w, WindowState& st, UINT msg, WPARAM wParam, LPARAM lParam, bool& handled) { + handled = true; + RangeExtra& r = Extra(st); + auto* gauge = wxDynamicCast(w, wxGauge); + auto* slider = wxDynamicCast(w, wxSlider); + auto applyGauge = [&] { + if (!gauge) + return; + int range = std::max(1, r.maxValue - r.minValue); + gauge->SetRange(range); + gauge->SetValue(std::max(0, std::min(range, r.pos - r.minValue))); + }; + switch (msg) { + case PBM_SETRANGE: { + LRESULT old = MAKELRESULT(r.minValue, r.maxValue); + r.minValue = LOWORD(lParam); + r.maxValue = HIWORD(lParam); + applyGauge(); + return old; + } + case PBM_SETRANGE32: { + LRESULT old = MAKELRESULT(r.minValue, r.maxValue); + r.minValue = static_cast(wParam); + r.maxValue = static_cast(lParam); + applyGauge(); + return old; + } + case PBM_GETRANGE: { + auto* range = reinterpret_cast(lParam); + if (range) { + range[0] = r.minValue; + range[1] = r.maxValue; + } + return wParam ? r.minValue : r.maxValue; + } + case PBM_SETPOS: { + int old = r.pos; + r.pos = static_cast(wParam); + applyGauge(); + return old; + } + case PBM_DELTAPOS: { + int old = r.pos; + r.pos += static_cast(wParam); + applyGauge(); + return old; + } + case PBM_SETSTEP: { + int old = r.step; + r.step = static_cast(wParam); + return old; + } + case PBM_STEPIT: { + int old = r.pos; + r.pos += r.step; + if (r.pos > r.maxValue) + r.pos = r.minValue + (r.pos - r.maxValue); + applyGauge(); + return old; + } + case PBM_GETPOS: + return r.pos; + case PBM_SETBARCOLOR: + case PBM_SETBKCOLOR: + return CLR_DEFAULT; + case PBM_SETMARQUEE: + if (gauge && wParam) + gauge->Pulse(); + return TRUE; + case TBM_GETPOS: + return slider ? slider->GetValue() : r.pos; + case TBM_SETPOS: + r.pos = static_cast(lParam); + if (slider) + slider->SetValue(r.pos); + return 0; + case TBM_SETRANGE: + r.minValue = LOWORD(lParam); + r.maxValue = HIWORD(lParam); + if (slider) + slider->SetRange(r.minValue, std::max(r.minValue, r.maxValue)); + return 0; + case TBM_SETRANGEMIN: + r.minValue = static_cast(lParam); + if (slider) + slider->SetRange(r.minValue, std::max(r.minValue, r.maxValue)); + return 0; + case TBM_SETRANGEMAX: + r.maxValue = static_cast(lParam); + if (slider) + slider->SetRange(r.minValue, std::max(r.minValue, r.maxValue)); + return 0; + case TBM_GETRANGEMIN: + return slider ? slider->GetMin() : r.minValue; + case TBM_GETRANGEMAX: + return slider ? slider->GetMax() : r.maxValue; + case TBM_SETTICFREQ: + if (slider) + slider->SetTickFreq(std::max(1, static_cast(wParam))); + return 0; + case TBM_SETPAGESIZE: { + int old = slider ? slider->GetPageSize() : 0; + if (slider) + slider->SetPageSize(static_cast(lParam)); + return old; + } + case TBM_GETPAGESIZE: + return slider ? slider->GetPageSize() : 0; + case TBM_SETLINESIZE: { + int old = slider ? slider->GetLineSize() : 0; + if (slider) + slider->SetLineSize(static_cast(lParam)); + return old; + } + case TBM_GETLINESIZE: + return slider ? slider->GetLineSize() : 1; + case TBM_SETTIC: + if (slider) + slider->SetTick(static_cast(lParam)); + return TRUE; + case TBM_CLEARTICS: + if (slider) + slider->ClearTicks(); + return 0; + case TBM_GETNUMTICS: + return 2; + case TBM_SETSEL: + case TBM_SETSELSTART: + case TBM_SETSELEND: + case TBM_CLEARSEL: + return 0; + case UDM_SETRANGE: + r.maxValue = static_cast(LOWORD(lParam)); + r.minValue = static_cast(HIWORD(lParam)); + return 0; + case UDM_SETRANGE32: + r.minValue = static_cast(wParam); + r.maxValue = static_cast(lParam); + return 0; + case UDM_GETRANGE: + return MAKELRESULT(r.maxValue, r.minValue); + case UDM_GETRANGE32: + if (wParam) + *reinterpret_cast(wParam) = r.minValue; + if (lParam) + *reinterpret_cast(lParam) = r.maxValue; + return 0; + case UDM_SETPOS: + case UDM_SETPOS32: { + int old = r.pos; + r.pos = msg == UDM_SETPOS ? static_cast(LOWORD(lParam)) : static_cast(lParam); + UpdateSpinBuddy(w, st); + return old; + } + case UDM_GETPOS: + return MAKELRESULT(ReadSpinBuddy(st), 0); + case UDM_GETPOS32: + if (lParam) + *reinterpret_cast(lParam) = FALSE; + return ReadSpinBuddy(st); + case UDM_SETBUDDY: { + CWnd* old = st.buddy; + st.buddy = CWnd::FromHandle(reinterpret_cast(wParam)); + return reinterpret_cast(old ? old->m_hWnd : nullptr); + } + case UDM_GETBUDDY: + return reinterpret_cast(st.buddy ? st.buddy->m_hWnd : nullptr); + case UDM_SETBASE: + case UDM_SETACCEL: + return TRUE; + case UDM_GETBASE: + return 10; + case SBM_SETPOS: + if (auto* sb = wxDynamicCast(w, wxScrollBar)) { + int old = sb->GetThumbPosition(); + sb->SetThumbPosition(static_cast(wParam) - r.minValue); + return old + r.minValue; + } + return 0; + case SBM_GETPOS: + if (auto* sb = wxDynamicCast(w, wxScrollBar)) + return sb->GetThumbPosition() + r.minValue; + return 0; + case SBM_SETRANGE: + if (auto* sb = wxDynamicCast(w, wxScrollBar)) { + r.minValue = static_cast(wParam); + r.maxValue = static_cast(lParam); + sb->SetScrollbar(sb->GetThumbPosition(), 1, std::max(1, r.maxValue - r.minValue + 1), std::max(1, (r.maxValue - r.minValue) / 10)); + } + return 0; + case SBM_GETRANGE: + if (wParam) + *reinterpret_cast(wParam) = r.minValue; + if (lParam) + *reinterpret_cast(lParam) = r.maxValue; + return 0; + default: + handled = false; + return 0; + } +} + +LRESULT TabProc(wxWindow* w, WindowState& st, UINT msg, WPARAM wParam, LPARAM lParam, bool& handled) { + handled = true; + TabExtra& tx = Extra(st); + (void)wParam; + (void)lParam; + switch (msg) { + case WM_GETDLGCODE: + return DLGC_WANTARROWS; + default: + handled = false; + break; + } + (void)w; + (void)tx; + return 0; +} + +} // namespace + +LRESULT ControlWindowProc(wxWindow* w, WindowState& st, UINT msg, WPARAM wParam, LPARAM lParam, bool& handled) { + switch (st.kind) { + case ControlKind::Edit: + case ControlKind::RichEdit: + if (auto* t = AsText(w)) + return EditProc(t, st, msg, wParam, lParam, handled); + break; + case ControlKind::ComboBox: + case ControlKind::ComboList: + return ComboProc(w, st, msg, wParam, lParam, handled); + case ControlKind::ListBox: + if (auto* lb = wxDynamicCast(w, wxListBox)) + return ListBoxProc(lb, st, msg, wParam, lParam, handled); + break; + case ControlKind::PushButton: + case ControlKind::CheckBox: + case ControlKind::RadioButton: + case ControlKind::OwnerDrawButton: + case ControlKind::GroupBox: + return ButtonProc(w, st, msg, wParam, lParam, handled); + case ControlKind::Static: + case ControlKind::StaticBitmap: + case ControlKind::StaticFrame: + return StaticProc(w, st, msg, wParam, lParam, handled); + case ControlKind::Progress: + case ControlKind::Slider: + case ControlKind::Spin: + case ControlKind::ScrollBar: + return RangeProc(w, st, msg, wParam, lParam, handled); + case ControlKind::Tab: + return TabProc(w, st, msg, wParam, lParam, handled); + case ControlKind::Scintilla: + if (msg >= 2000 || msg == WM_GETTEXT || msg == WM_SETTEXT || msg == WM_GETTEXTLENGTH) + return ScintillaWindowProc(w, msg, wParam, lParam, handled); + break; + default: + break; + } + handled = false; + return 0; +} + +// --------------------------------------------------------------------------------------------- +// WM_CTLCOLOR emulation + +void ApplyCtlColor(wxWindow* control) { + WindowState* st = GetState(control); + wxWindow* parent = LogicalParent(control); + if (!st || !parent || !IsManagedWindow(parent) || !PermanentWnd(parent)) + return; + UINT msg = WM_CTLCOLORSTATIC; + switch (st->kind) { + case ControlKind::Edit: + case ControlKind::RichEdit: + case ControlKind::ComboBox: + msg = (st->style & ES_READONLY) || !control->IsEnabled() ? WM_CTLCOLORSTATIC : WM_CTLCOLOREDIT; + break; + case ControlKind::ListBox: + case ControlKind::ComboList: + msg = WM_CTLCOLORLISTBOX; + break; + case ControlKind::PushButton: + case ControlKind::OwnerDrawButton: + msg = WM_CTLCOLORBTN; + break; + case ControlKind::Static: + case ControlKind::StaticBitmap: + case ControlKind::CheckBox: + case ControlKind::RadioButton: + case ControlKind::GroupBox: + msg = WM_CTLCOLORSTATIC; + break; + default: + return; + } + CWnd* parentWnd = PermanentWnd(parent); + if (!HasMessageHandler(parentWnd, WM_CTLCOLOR) && !HasMessageHandler(st->permanent, WM_CTLCOLOR + WM_REFLECT_BASE)) + return; + wxBitmap scratch(1, 1); + wxMemoryDC mdc(scratch); + CDC* cdc = WrapDC(&mdc, control); + COLORREF defaultText = 0xFFFFFFFF; + cdc->SetTextColor(defaultText); + LRESULT r = DispatchMessageTo(ToHwnd(parent), msg, reinterpret_cast(cdc->m_hDC), + reinterpret_cast(ToHwnd(control))); + COLORREF text = cdc->GetTextColor(); + COLORREF bk = cdc->GetBkColor(); + int bkMode = cdc->GetBkMode(); + UnwrapDC(cdc); + if (text != defaultText) + control->SetForegroundColour(ToWxColour(text)); + if (r) { + wxBrush brush = BrushFromHandle(reinterpret_cast(r)); + if (brush.IsOk() && brush.GetStyle() != wxBRUSHSTYLE_TRANSPARENT) + control->SetBackgroundColour(brush.GetColour()); + else if (bkMode == OPAQUE) + control->SetBackgroundColour(ToWxColour(bk)); + } + control->Refresh(); +} + +} // namespace mfcwx diff --git a/port/mfcwx/src/cstring.cpp b/port/mfcwx/src/cstring.cpp new file mode 100644 index 000000000..9051fdfc0 --- /dev/null +++ b/port/mfcwx/src/cstring.cpp @@ -0,0 +1,701 @@ +#include "mfcwx/cstring.h" +#include "bridge.h" + +#include +#include +#include +#include + +namespace mfcwx { +bool LoadResourceString(UINT id, std::string& out); +} + +namespace { + +char g_empty[1] = {0}; + +bool IsAsciiSpace(char ch) { return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' || ch == '\v' || ch == '\f'; } + +std::string WideToAnsi(const wchar_t* s) { + std::string out; + if (!s) + return out; + for (; *s; ++s) { + char ch; + mfcwx::UnicodeToAnsi(*s, ch); + out.push_back(ch); + } + return out; +} + +} // namespace + +const char* AfxTranslateFormat(const char* fmt, char* buffer, size_t size) { + if (!fmt || !strchr(fmt, 'I')) + return fmt; + size_t o = 0; + for (const char* p = fmt; *p && o + 4 < size;) { + if (*p != '%') { + buffer[o++] = *p++; + continue; + } + buffer[o++] = *p++; + if (*p == '%') { + buffer[o++] = *p++; + continue; + } + while (*p && strchr("-+ #0123456789.*", *p) && o + 4 < size) + buffer[o++] = *p++; + if (p[0] == 'I' && p[1] == '6' && p[2] == '4') { + buffer[o++] = 'l'; + buffer[o++] = 'l'; + p += 3; + } else if (p[0] == 'I' && p[1] == '3' && p[2] == '2') { + p += 3; + } else if (p[0] == 'I' && strchr("diouxX", p[1])) { + buffer[o++] = 'z'; + p += 1; + } + } + buffer[o] = 0; + return buffer; +} + +CString::CString() noexcept : m_p(g_empty), m_len(0), m_cap(0) {} + +CString::CString(const CString& s) : CString() { Assign(s.m_p, s.m_len); } + +CString::CString(CString&& s) noexcept : m_p(s.m_p), m_len(s.m_len), m_cap(s.m_cap) { + s.m_p = g_empty; + s.m_len = 0; + s.m_cap = 0; +} + +CString::CString(const char* s) : CString() { + if (s && IS_INTRESOURCE(s)) + LoadString(static_cast(reinterpret_cast(s))); + else if (s) + Assign(s, static_cast(strlen(s))); +} + +CString::CString(const unsigned char* s) : CString(reinterpret_cast(s)) {} + +CString::CString(const char* s, int length) : CString() { + if (s && length > 0) + Assign(s, length); +} + +CString::CString(char ch, int repeat) : CString() { + if (repeat > 0) { + Reserve(repeat); + memset(m_p, ch, repeat); + m_len = repeat; + m_p[m_len] = 0; + } +} + +CString::CString(const wchar_t* s) : CString() { + std::string a = WideToAnsi(s); + Assign(a.data(), static_cast(a.size())); +} + +CString::~CString() { + if (m_p != g_empty) + free(m_p); +} + +CString& CString::operator=(const CString& s) { + if (this != &s) + Assign(s.m_p, s.m_len); + return *this; +} + +CString& CString::operator=(CString&& s) noexcept { + if (this != &s) { + CString tmp(std::move(s)); + swap(*this, tmp); + } + return *this; +} + +CString& CString::operator=(const char* s) { + if (!s) { + Empty(); + return *this; + } + if (s >= m_p && s <= m_p + m_len) { + CString copy(s); + swap(*this, copy); + return *this; + } + Assign(s, static_cast(strlen(s))); + return *this; +} + +CString& CString::operator=(const unsigned char* s) { return *this = reinterpret_cast(s); } + +CString& CString::operator=(const wchar_t* s) { + std::string a = WideToAnsi(s); + Assign(a.data(), static_cast(a.size())); + return *this; +} + +CString& CString::operator=(char ch) { + Assign(&ch, 1); + return *this; +} + +void CString::Reserve(int capacity) { + if (capacity <= m_cap) + return; + int newCap = std::max(capacity, m_cap + m_cap / 2); + if (newCap < 15) + newCap = 15; + char* p = static_cast(malloc(static_cast(newCap) + 1)); + memcpy(p, m_p, static_cast(m_len) + 1); + if (m_p != g_empty) + free(m_p); + m_p = p; + m_cap = newCap; +} + +void CString::Assign(const char* s, int length) { + if (length <= 0) { + Empty(); + return; + } + if (s >= m_p && s < m_p + m_cap + 1) { + std::string copy(s, static_cast(length)); + Reserve(length); + memcpy(m_p, copy.data(), static_cast(length)); + } else { + Reserve(length); + memcpy(m_p, s, static_cast(length)); + } + m_len = length; + m_p[m_len] = 0; +} + +void CString::Empty() { + if (m_p != g_empty) + free(m_p); + m_p = g_empty; + m_len = 0; + m_cap = 0; +} + +char* CString::GetBuffer() { + Reserve(m_len); + if (m_p == g_empty) + Reserve(1); + return m_p; +} + +char* CString::GetBuffer(int minLength) { + Reserve(std::max(minLength, 1)); + return m_p; +} + +char* CString::GetBufferSetLength(int length) { + Reserve(std::max(length, 1)); + m_len = std::max(length, 0); + m_p[m_len] = 0; + return m_p; +} + +void CString::ReleaseBuffer(int newLength) { + if (m_p == g_empty) + return; + if (newLength < 0) + newLength = static_cast(strnlen(m_p, static_cast(m_cap) + 1)); + if (newLength > m_cap) + newLength = m_cap; + m_len = newLength; + m_p[m_len] = 0; +} + +void CString::Truncate(int length) { + if (length < m_len && length >= 0) { + m_len = length; + m_p[m_len] = 0; + } +} + +void CString::SetString(const char* s) { *this = s; } + +void CString::SetString(const char* s, int length) { + if (!s) + Empty(); + else + Assign(s, length); +} + +void CString::Append(const char* s, int length) { + if (!s || length <= 0) + return; + if (s >= m_p && s < m_p + m_cap + 1) { + std::string copy(s, static_cast(length)); + Append(copy.data(), length); + return; + } + Reserve(m_len + length); + memcpy(m_p + m_len, s, static_cast(length)); + m_len += length; + m_p[m_len] = 0; +} + +void CString::Append(const char* s) { + if (s) + Append(s, static_cast(strlen(s))); +} + +CString& CString::operator+=(const CString& s) { + Append(s.m_p, s.m_len); + return *this; +} + +CString& CString::operator+=(const char* s) { + Append(s); + return *this; +} + +CString& CString::operator+=(char ch) { + Append(&ch, 1); + return *this; +} + +CString& CString::operator+=(const wchar_t* s) { + std::string a = WideToAnsi(s); + Append(a.data(), static_cast(a.size())); + return *this; +} + +int CString::Compare(const char* s) const { return strcmp(m_p, s ? s : ""); } + +int CString::CompareNoCase(const char* s) const { return strcasecmp(m_p, s ? s : ""); } + +CString CString::Mid(int first) const { return Mid(first, m_len - first); } + +CString CString::Mid(int first, int count) const { + if (first < 0) + first = 0; + if (count < 0) + count = 0; + if (first > m_len) + first = m_len; + if (first + count > m_len) + count = m_len - first; + return CString(m_p + first, count); +} + +CString CString::Left(int count) const { + if (count < 0) + count = 0; + return CString(m_p, std::min(count, m_len)); +} + +CString CString::Right(int count) const { + if (count < 0) + count = 0; + count = std::min(count, m_len); + return CString(m_p + m_len - count, count); +} + +CString CString::SpanIncluding(const char* charSet) const { + return Left(static_cast(strspn(m_p, charSet ? charSet : ""))); +} + +CString CString::SpanExcluding(const char* charSet) const { + return Left(static_cast(strcspn(m_p, charSet ? charSet : ""))); +} + +CString CString::Tokenize(const char* tokens, int& start) const { + if (start < 0 || start >= m_len || !tokens) { + start = -1; + return CString(); + } + const char* p = m_p + start; + p += strspn(p, tokens); + if (*p == 0) { + start = -1; + return CString(); + } + int len = static_cast(strcspn(p, tokens)); + CString result(p, len); + start = static_cast(p - m_p) + len + 1; + return result; +} + +CString& CString::MakeUpper() { + for (int i = 0; i < m_len; ++i) + if (m_p[i] >= 'a' && m_p[i] <= 'z') + m_p[i] = static_cast(m_p[i] - 'a' + 'A'); + return *this; +} + +CString& CString::MakeLower() { + for (int i = 0; i < m_len; ++i) + if (m_p[i] >= 'A' && m_p[i] <= 'Z') + m_p[i] = static_cast(m_p[i] - 'A' + 'a'); + return *this; +} + +CString& CString::MakeReverse() { + std::reverse(m_p, m_p + m_len); + return *this; +} + +int CString::Replace(char oldCh, char newCh) { + int count = 0; + for (int i = 0; i < m_len; ++i) + if (m_p[i] == oldCh) { + m_p[i] = newCh; + ++count; + } + return count; +} + +int CString::Replace(const char* oldStr, const char* newStr) { + if (!oldStr || !*oldStr) + return 0; + if (!newStr) + newStr = ""; + std::string src(m_p, static_cast(m_len)); + std::string result; + size_t oldLen = strlen(oldStr); + size_t pos = 0; + int count = 0; + for (;;) { + size_t hit = src.find(oldStr, pos, oldLen); + if (hit == std::string::npos) + break; + result.append(src, pos, hit - pos); + result.append(newStr); + pos = hit + oldLen; + ++count; + } + if (count) { + result.append(src, pos, std::string::npos); + Assign(result.data(), static_cast(result.size())); + } + return count; +} + +int CString::Remove(char ch) { + int o = 0; + for (int i = 0; i < m_len; ++i) + if (m_p[i] != ch) + m_p[o++] = m_p[i]; + int removed = m_len - o; + m_len = o; + if (m_p != g_empty) + m_p[m_len] = 0; + return removed; +} + +int CString::Insert(int index, char ch) { + char buf[2] = {ch, 0}; + return Insert(index, buf); +} + +int CString::Insert(int index, const char* s) { + if (index < 0) + index = 0; + if (index > m_len) + index = m_len; + int add = s ? static_cast(strlen(s)) : 0; + if (add) { + std::string copy(s, static_cast(add)); + Reserve(m_len + add); + memmove(m_p + index + add, m_p + index, static_cast(m_len - index) + 1); + memcpy(m_p + index, copy.data(), static_cast(add)); + m_len += add; + } + return m_len; +} + +int CString::Delete(int index, int count) { + if (index < 0) + index = 0; + if (count > 0 && index < m_len) { + count = std::min(count, m_len - index); + memmove(m_p + index, m_p + index + count, static_cast(m_len - index - count) + 1); + m_len -= count; + } + return m_len; +} + +CString& CString::TrimLeft() { + int i = 0; + while (i < m_len && IsAsciiSpace(m_p[i])) + ++i; + Delete(0, i); + return *this; +} + +CString& CString::TrimLeft(char target) { + int i = 0; + while (i < m_len && m_p[i] == target) + ++i; + Delete(0, i); + return *this; +} + +CString& CString::TrimLeft(const char* targets) { + if (!targets) + return *this; + int i = 0; + while (i < m_len && strchr(targets, m_p[i])) + ++i; + Delete(0, i); + return *this; +} + +CString& CString::TrimRight() { + int i = m_len; + while (i > 0 && IsAsciiSpace(m_p[i - 1])) + --i; + Truncate(i); + return *this; +} + +CString& CString::TrimRight(char target) { + int i = m_len; + while (i > 0 && m_p[i - 1] == target) + --i; + Truncate(i); + return *this; +} + +CString& CString::TrimRight(const char* targets) { + if (!targets) + return *this; + int i = m_len; + while (i > 0 && strchr(targets, m_p[i - 1])) + --i; + Truncate(i); + return *this; +} + +int CString::Find(char ch, int start) const { + if (start < 0) + start = 0; + for (int i = start; i < m_len; ++i) + if (m_p[i] == ch) + return i; + return -1; +} + +int CString::Find(const char* sub, int start) const { + if (!sub || start < 0 || start > m_len) + return -1; + const char* hit = strstr(m_p + start, sub); + return hit ? static_cast(hit - m_p) : -1; +} + +int CString::ReverseFind(char ch) const { + for (int i = m_len - 1; i >= 0; --i) + if (m_p[i] == ch) + return i; + return -1; +} + +int CString::FindOneOf(const char* charSet) const { + if (!charSet) + return -1; + for (int i = 0; i < m_len; ++i) + if (strchr(charSet, m_p[i])) + return i; + return -1; +} + +void CString::FormatV(const char* fmt, va_list args) { + char translated[4096]; + fmt = AfxTranslateFormat(fmt, translated, sizeof translated); + va_list copy; + va_copy(copy, args); + int n = vsnprintf(nullptr, 0, fmt, copy); + va_end(copy); + if (n <= 0) { + Empty(); + return; + } + std::string buf(static_cast(n) + 1, '\0'); + vsnprintf(&buf[0], buf.size(), fmt, args); + Assign(buf.data(), n); +} + +void CString::AppendFormatV(const char* fmt, va_list args) { + CString tmp; + tmp.FormatV(fmt, args); + *this += tmp; +} + +void CString::Format(const char* fmt, ...) { + va_list args; + va_start(args, fmt); + FormatV(fmt, args); + va_end(args); +} + +void CString::Format(UINT formatId, ...) { + CString fmt; + fmt.LoadString(formatId); + va_list args; + va_start(args, formatId); + FormatV(fmt, args); + va_end(args); +} + +void CString::AppendFormat(const char* fmt, ...) { + va_list args; + va_start(args, fmt); + AppendFormatV(fmt, args); + va_end(args); +} + +void CString::AppendFormat(UINT formatId, ...) { + CString fmt; + fmt.LoadString(formatId); + va_list args; + va_start(args, formatId); + AppendFormatV(fmt, args); + va_end(args); +} + +static void FormatMessageImpl(CString& out, const char* fmt, va_list args) { + // %1..%99 insert arguments, optionally typed as %1!d!; arguments default to strings. + const char* argv[100] = {}; + std::string result; + std::string typeSpec[100]; + int maxArg = 0; + for (const char* p = fmt; *p; ++p) { + if (*p == '%' && p[1] >= '1' && p[1] <= '9') { + int n = p[1] - '0'; + const char* q = p + 2; + if (*q >= '0' && *q <= '9') + n = n * 10 + (*q++ - '0'); + if (*q == '!') { + const char* e = strchr(q + 1, '!'); + if (e) + typeSpec[n] = std::string(q + 1, e); + } + maxArg = std::max(maxArg, n); + } + } + long long values[100] = {}; + for (int i = 1; i <= maxArg; ++i) { + if (typeSpec[i].empty() || typeSpec[i].back() == 's') + argv[i] = va_arg(args, const char*); + else + values[i] = va_arg(args, int); + } + for (const char* p = fmt; *p; ++p) { + if (*p == '%' && p[1] >= '1' && p[1] <= '9') { + int n = p[1] - '0'; + const char* q = p + 2; + if (*q >= '0' && *q <= '9') + n = n * 10 + (*q++ - '0'); + if (*q == '!') { + const char* e = strchr(q + 1, '!'); + if (e) + q = e + 1; + } + if (typeSpec[n].empty() || typeSpec[n].back() == 's') { + result += argv[n] ? argv[n] : ""; + } else { + char buf[64]; + std::string f = "%" + typeSpec[n]; + snprintf(buf, sizeof buf, f.c_str(), static_cast(values[n])); + result += buf; + } + p = q - 1; + } else if (*p == '%' && p[1] == '%') { + result += '%'; + ++p; + } else if (*p == '%' && p[1] == 'n') { + result += '\n'; + ++p; + } else { + result += *p; + } + } + out = result.c_str(); +} + +void CString::FormatMessage(const char* fmt, ...) { + va_list args; + va_start(args, fmt); + FormatMessageImpl(*this, fmt, args); + va_end(args); +} + +void CString::FormatMessage(UINT formatId, ...) { + CString fmt; + fmt.LoadString(formatId); + va_list args; + va_start(args, formatId); + FormatMessageImpl(*this, fmt, args); + va_end(args); +} + +BOOL CString::LoadString(UINT id) { + std::string s; + if (!mfcwx::LoadResourceString(id, s)) { + Empty(); + return FALSE; + } + Assign(s.data(), static_cast(s.size())); + return TRUE; +} + +BSTR CString::AllocSysString() const { + wxString w = mfcwx::ToWx(m_p, m_len); + std::wstring ws = w.ToStdWstring(); + BSTR b = static_cast(malloc((ws.size() + 1) * sizeof(wchar_t))); + wcscpy(b, ws.c_str()); + return b; +} + +BSTR CString::SetSysString(BSTR* bstr) const { + if (*bstr) + free(*bstr); + *bstr = AllocSysString(); + return *bstr; +} + +CString operator+(const CString& a, const CString& b) { + CString r(a); + r += b; + return r; +} + +CString operator+(const CString& a, const char* b) { + CString r(a); + r += b; + return r; +} + +CString operator+(const char* a, const CString& b) { + CString r(a); + r += b; + return r; +} + +CString operator+(const CString& a, char b) { + CString r(a); + r += b; + return r; +} + +CString operator+(char a, const CString& b) { + CString r(a); + r += b; + return r; +} + +CString operator+(const CString& a, const wchar_t* b) { + CString r(a); + r += b; + return r; +} diff --git a/port/mfcwx/src/globalmem.h b/port/mfcwx/src/globalmem.h new file mode 100644 index 000000000..dd9a521f2 --- /dev/null +++ b/port/mfcwx/src/globalmem.h @@ -0,0 +1,40 @@ +#pragma once + +// HGLOBAL/HLOCAL layout: the handle is the data pointer (GlobalLock(h) == h, so GMEM_FIXED +// callers may use the handle directly), preceded by a GlobalHeader. + +#include "mfcwx/win32.h" + +#include + +namespace mfcwx { + +struct GlobalHeader { + unsigned magic; + unsigned flags; + size_t size; + long lockCount; + size_t reserved; +}; + +static_assert(sizeof(GlobalHeader) % 16 == 0, "data must stay 16-byte aligned"); + +constexpr unsigned kGlobalMagic = 0x474C4D58u; + +inline GlobalHeader* GlobalHeaderOf(HGLOBAL h) { + if (!h) + return nullptr; + auto* header = reinterpret_cast(static_cast(h) - sizeof(GlobalHeader)); + return header->magic == kGlobalMagic ? header : nullptr; +} + +// Allocates an HGLOBAL holding a copy of data (size bytes; data may be null for zeroed memory). +HGLOBAL GlobalFromData(const void* data, size_t size, UINT flags = GMEM_MOVEABLE); +bool GlobalIsValid(HGLOBAL h); +inline void* GlobalData(HGLOBAL h) { return GlobalHeaderOf(h) ? h : nullptr; } +inline size_t GlobalDataSize(HGLOBAL h) { + GlobalHeader* header = GlobalHeaderOf(h); + return header ? header->size : 0; +} + +} // namespace mfcwx diff --git a/port/mfcwx/src/internal.h b/port/mfcwx/src/internal.h new file mode 100644 index 000000000..b6a0cdbc7 --- /dev/null +++ b/port/mfcwx/src/internal.h @@ -0,0 +1,198 @@ +#pragma once + +// Internal contracts shared by the mfcwx implementation files. Not visible to application code. + +#include "bridge.h" +#include "mfcwx/rcdata.h" + +#include +#include + +#include +#include +#include +#include +#include + +namespace mfcwx { + +// --------------------------------------------------------------------------------------------- +// Resources (resources.cpp) + +// Registers the generated resource module of the application (g_rcModule_). +void RegisterResourceModule(const rc::Module* module); +// Selects the UI language ("de", "en", ...); also sets the ANSI code page. +void SetResourceLanguage(const char* code); +const char* GetResourceLanguage(); +std::vector AvailableResourceLanguages(); + +// A resource reference as passed to Win32 APIs: MAKEINTRESOURCE(id) or a name. +struct ResRef { + int id = 0; + std::string name; + static ResRef From(const char* lpszName); + static ResRef FromId(UINT id) { ResRef r; r.id = static_cast(id); return r; } + bool Matches(int otherId, const char* otherName) const; +}; + +const rc::Dialog* FindDialog(const ResRef& ref); +const rc::Menu* FindMenu(const ResRef& ref); +const rc::AccelTable* FindAccelTable(const ResRef& ref); +const rc::Toolbar* FindToolbar(const ResRef& ref); +// Path of a file resource (BITMAP, ICON, CURSOR, GIF, ...) on disk, empty if unknown. +std::string FindFileResource(const char* type, const ResRef& ref); +std::vector FindDlgInit(int dialogId); +// Returns the resource string in the ANSI code page. +bool LoadResourceString(UINT id, std::string& out); +const rc::VersionInfo* GetVersionInfo(); +// Directory holding the application's data files (res/, help, pse, examples, ...). +std::string GetDataDirectory(); +void SetDataDirectory(const std::string& dir); + +wxBitmap LoadBitmapResource(const ResRef& ref); +wxIcon LoadIconResource(const ResRef& ref); +wxCursor LoadCursorResource(const ResRef& ref); +// Converts an .rc menu text ("&File\tCtrl+O") to wx syntax ("&File\tCtrl+O" with wx accelerator names). +wxString MenuTextToWx(const char* utf8Text); + +// --------------------------------------------------------------------------------------------- +// Windows (wnd.cpp) + +// Win32 control ids map to wx ids with an offset so they never collide with wx's stock ids. +constexpr int kIdOffset = 20000; +inline int ToWxId(int winId) { return winId == -1 || winId == 0xFFFF ? wxID_ANY : winId + kIdOffset; } +inline int FromWxId(int wxId) { return wxId >= kIdOffset ? wxId - kIdOffset : (wxId == wxID_ANY ? -1 : wxId); } + +enum class ControlKind { + Generic, // custom-drawn wxWindow (CWnd::Create, owner-drawn controls, views) + Static, + StaticBitmap, + StaticFrame, // SS_*RECT / SS_*FRAME / SS_ETCHED* + GroupBox, + PushButton, + CheckBox, + RadioButton, + OwnerDrawButton, + Edit, + RichEdit, + ComboBox, // editable (CBS_DROPDOWN / CBS_SIMPLE) + ComboList, // CBS_DROPDOWNLIST + ListBox, + ScrollBar, + Progress, + Slider, + Spin, + ListView, + TreeView, + Tab, + Scintilla, + Dialog, + Frame, + MDIChild, + ToolBar, + StatusBar, + GLCanvas, +}; + +struct WindowState { + wxWindow* window = nullptr; + CWnd* permanent = nullptr; // attached CWnd (subclassed), may be null + ControlKind kind = ControlKind::Generic; + int winId = 0; // Win32 control id + DWORD style = 0; // Win32 style bits + DWORD exStyle = 0; + LONG_PTR userData = 0; // GWL_USERDATA + std::map> timers; + std::map timerProcs; + HFONT font = nullptr; + bool redraw = true; + bool destroying = false; + bool destroyNotified = false; + bool hooked = false; + bool settingText = false; // suppresses change notifications for programmatic updates + int defaultButtonId = 0; // dialogs: DM_SETDEFID + wxString text; // caption of custom-drawn windows + std::function customPaint; + std::vector> itemData; // unused by most kinds + CWnd* buddy = nullptr; // spin buttons + HWND owner = nullptr; + void* extra = nullptr; // kind-specific data owned by the control implementation + std::function deleteExtra; + ~WindowState() { + if (deleteExtra) + deleteExtra(); + } +}; + +WindowState* GetState(wxWindow* window); // null if the window is not managed +WindowState& EnsureState(wxWindow* window); +inline wxWindow* ToWx(HWND hWnd) { return reinterpret_cast(hWnd); } +inline HWND ToHwnd(wxWindow* w) { return reinterpret_cast(w); } + +// Binds the event bridge to a window created by mfcwx (idempotent). +void HookWindow(wxWindow* window, ControlKind kind, int winId, DWORD style, DWORD exStyle); + +// Message dispatch. All functions must be called on the main thread except SendMessageAnyThread. +LRESULT DispatchMessageTo(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); +LRESULT DefaultWindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); +bool IsMainThread(); +// Runs fn on the main thread and waits for it (Windows SendMessage semantics across threads). +void RunOnMainThread(const std::function& fn); +void PostToMainThread(std::function fn); + +// Converts a window rectangle in dialog units to pixels for the given dialog window. +wxRect DialogUnitsToPixels(wxWindow* dialog, int x, int y, int cx, int cy); + +// Creates the wx controls of a dialog template inside parent; returns false on failure. +bool CreateDialogControls(wxWindow* parent, const rc::Dialog& tmpl); +// Creates a single control from a template entry (also used by CWnd::Create for standard classes). +wxWindow* CreateControl(wxWindow* parent, const char* cls, const char* text, int winId, const wxRect& rect, + DWORD style, DWORD exStyle); + +// Current message being processed (for CWnd::Default / GetCurrentMessage). +struct CurrentMessage { + MSG msg; + wxEvent* event; // originating wx event, null for synthetic messages + bool defaultCalled; // CWnd::Default() requested the toolkit's default processing +}; +CurrentMessage* GetCurrentMessageSlot(); + +// --------------------------------------------------------------------------------------------- +// GDI (gdi.cpp) + +struct GdiObjectImpl; +// HGDIOBJ values are CGdiObject* for application-created objects and stock objects. +wxPen PenFromHandle(HPEN h); +wxBrush BrushFromHandle(HBRUSH h); +wxFont FontFromHandle(HFONT h); +wxBitmap* BitmapFromHandle(HBITMAP h); +HFONT DefaultGuiFont(); +wxColour ToWxColour(COLORREF c); +COLORREF FromWxColour(const wxColour& c); +COLORREF SysColor(int index); + +// Wraps a wx DC that is owned elsewhere (e.g. a wxPaintDC in an event handler) as CDC. +CDC* WrapDC(wxDC* dc, wxWindow* window); +void UnwrapDC(CDC* cdc); + +// --------------------------------------------------------------------------------------------- +// Menus (menu.cpp) + +// HMENU values are CMenu-independent menu handles (MenuHandle*). +struct MenuHandle; +wxMenuBar* BuildMenuBar(const rc::Menu& menu); +wxMenu* BuildPopupMenu(const rc::Menu& menu, int index); +HMENU HandleFromWxMenu(wxMenu* menu); +HMENU HandleFromWxMenuBar(wxMenuBar* bar); +wxMenu* WxMenuFromHandle(HMENU h); +wxMenuBar* WxMenuBarFromHandle(HMENU h); + +// --------------------------------------------------------------------------------------------- +// Application (app.cpp) + +CWinApp*& AppInstance(); +wxWindow* MainWxWindow(); +// Shows a message box; nType is the MB_* combination. Returns IDOK, IDCANCEL, ... +int ShowMessageBox(wxWindow* parent, const char* text, const char* caption, UINT nType); + +} // namespace mfcwx diff --git a/port/mfcwx/src/kernel.cpp b/port/mfcwx/src/kernel.cpp new file mode 100644 index 000000000..8ffda7c08 --- /dev/null +++ b/port/mfcwx/src/kernel.cpp @@ -0,0 +1,1131 @@ +#include "afx.h" +#include "runtime.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#ifdef __linux__ +#include +#endif + +namespace mfcwx { +namespace { + +enum class Kind { Thread, Event, Mutex, Semaphore, Process, ProcessThread, File }; + +struct Object { + explicit Object(Kind k) : kind(k) {} + virtual ~Object() = default; + Kind kind; + int refs = 1; + int handles = 1; + int waiters = 0; + std::string name; +}; + +struct ThreadObj : Object { + ThreadObj() : Object(Kind::Thread) {} + std::function body; + DWORD id = 0; + DWORD exitCode = STILL_ACTIVE; + bool started = false; + bool finished = false; + bool zombie = false; + int suspendCount = 0; + int priority = THREAD_PRIORITY_NORMAL; + size_t stackSize = 0; +}; + +struct EventObj : Object { + EventObj(bool m, bool s) : Object(Kind::Event), manual(m), signaled(s) {} + bool manual; + bool signaled; + unsigned pulseGen = 0; +}; + +struct MutexObj : Object { + MutexObj() : Object(Kind::Mutex) {} + DWORD owner = 0; + int count = 0; +}; + +struct SemaphoreObj : Object { + SemaphoreObj(LONG c, LONG m) : Object(Kind::Semaphore), count(c), max(m) {} + LONG count; + LONG max; +}; + +struct ProcessObj : Object { + ProcessObj() : Object(Kind::Process) {} + pid_t pid = 0; + DWORD exitCode = STILL_ACTIVE; + bool exited = false; +}; + +void Release(Object* o); + +struct ProcessThreadObj : Object { + explicit ProcessThreadObj(ProcessObj* p) : Object(Kind::ProcessThread), process(p) {} + ~ProcessThreadObj() override { Release(process); } + ProcessObj* process; +}; + +struct FileObj : Object { + FileObj(int f, std::string p, bool o) : Object(Kind::File), fd(f), path(std::move(p)), owns(o) {} + ~FileObj() override { + if (owns && fd >= 0) + close(fd); + } + int fd; + std::string path; + bool owns; +}; + +// Kernel state lives for the whole process; detached threads may still use it during exit. +std::mutex& Lock() { + static std::mutex* m = new std::mutex; + return *m; +} +std::condition_variable& Cv() { + static std::condition_variable* cv = new std::condition_variable; + return *cv; +} +std::unordered_set& Objects() { + static auto* s = new std::unordered_set; + return *s; +} +std::map& Names() { + static auto* m = new std::map; + return *m; +} +std::deque& Graveyard() { + static auto* d = new std::deque; + return *d; +} + +constexpr size_t kGraveyardSize = 256; +constexpr size_t kMinStackSize = 16 * 1024 * 1024; +const HANDLE kCurrentThread = reinterpret_cast(static_cast(-2)); +const HANDLE kCurrentProcess = reinterpret_cast(static_cast(-1)); + +thread_local DWORD t_lastError = 0; +thread_local DWORD t_threadId = 0; +thread_local ThreadObj* t_self = nullptr; +std::atomic g_nextThreadId{0x104}; + +DWORD NewThreadId() { return g_nextThreadId.fetch_add(4); } + +Object* LookupLocked(HANDLE h) { + if (!h || h == kCurrentThread || h == kCurrentProcess) + return nullptr; + auto* o = static_cast(h); + return Objects().count(o) ? o : nullptr; +} + +template +T* LookupLocked(HANDLE h, Kind kind) { + Object* o = LookupLocked(h); + return o && o->kind == kind ? static_cast(o) : nullptr; +} + +void Register(Object* o) { + std::lock_guard lk(Lock()); + Objects().insert(o); +} + +void Release(Object* o) { + if (!o) + return; + std::vector doomed; + { + std::lock_guard lk(Lock()); + if (--o->refs > 0) + return; + if (o->kind == Kind::Thread) { + // Freed thread objects stay addressable for a while so that waiting on a stale + // thread handle (MFC auto-delete pattern) still reports a finished thread. + auto* t = static_cast(o); + t->zombie = true; + Graveyard().push_back(t); + while (Graveyard().size() > kGraveyardSize) { + ThreadObj* old = Graveyard().front(); + Graveyard().pop_front(); + Objects().erase(old); + doomed.push_back(old); + } + } else { + Objects().erase(o); + if (!o->name.empty()) { + auto it = Names().find(o->name); + if (it != Names().end() && it->second == o) + Names().erase(it); + } + doomed.push_back(o); + } + } + for (Object* d : doomed) + delete d; +} + +bool IsReady(Object* o, DWORD self, unsigned startGen) { + switch (o->kind) { + case Kind::Thread: + return static_cast(o)->finished; + case Kind::Event: { + auto* e = static_cast(o); + return e->signaled || (e->manual && e->pulseGen != startGen); + } + case Kind::Mutex: { + auto* m = static_cast(o); + return m->owner == 0 || m->owner == self; + } + case Kind::Semaphore: + return static_cast(o)->count > 0; + case Kind::Process: + return static_cast(o)->exited; + case Kind::ProcessThread: + return static_cast(o)->process->exited; + case Kind::File: + return true; + } + return false; +} + +void AcquireLocked(Object* o, DWORD self) { + switch (o->kind) { + case Kind::Event: { + auto* e = static_cast(o); + if (!e->manual) + e->signaled = false; + break; + } + case Kind::Mutex: { + auto* m = static_cast(o); + m->owner = self; + ++m->count; + break; + } + case Kind::Semaphore: + --static_cast(o)->count; + break; + default: + break; + } +} + +void CheckSuspendLocked(std::unique_lock& lk) { + ThreadObj* self = t_self; + if (!self) + return; + while (self->suspendCount > 0) + Cv().wait(lk); +} + +void CheckSuspend() { + if (!t_self) + return; + std::unique_lock lk(Lock()); + CheckSuspendLocked(lk); +} + +void* ThreadMain(void* arg) { + auto* t = static_cast(arg); + t_self = t; + t_threadId = t->id; + DWORD code = 0; + try { + code = t->body(); + } catch (ThreadExit& e) { + code = e.code; + } catch (CException* e) { + char msg[512] = ""; + e->GetErrorMessage(msg, sizeof msg); + fprintf(stderr, "mfcwx: uncaught MFC exception in thread %u: %s\n", static_cast(t->id), msg); + e->Delete(); + code = static_cast(-1); + } + t->body = nullptr; + { + std::lock_guard lk(Lock()); + t->exitCode = code; + t->finished = true; + } + Cv().notify_all(); + t_self = nullptr; + Release(t); + return nullptr; +} + +bool Launch(ThreadObj* t) { + pthread_attr_t attr; + pthread_attr_init(&attr); + size_t stack = std::max(t->stackSize, kMinStackSize); + long page = sysconf(_SC_PAGESIZE); + if (page > 0) + stack = (stack + static_cast(page) - 1) / static_cast(page) * static_cast(page); + pthread_attr_setstacksize(&attr, stack); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + pthread_t thread; + int rc = pthread_create(&thread, &attr, ThreadMain, t); + pthread_attr_destroy(&attr); + return rc == 0; +} + +void FailLaunch(ThreadObj* t) { + { + std::lock_guard lk(Lock()); + t->body = nullptr; + t->exitCode = static_cast(-1); + t->finished = true; + } + Cv().notify_all(); + Release(t); +} + +template +HANDLE CreateNamed(LPCSTR name, Kind kind, Make make) { + std::string key = name ? name : ""; + if (!key.empty()) { + std::lock_guard lk(Lock()); + auto it = Names().find(key); + if (it != Names().end()) { + if (it->second->kind != kind) { + t_lastError = ERROR_INVALID_HANDLE; + return nullptr; + } + ++it->second->handles; + ++it->second->refs; + t_lastError = ERROR_ALREADY_EXISTS; + return it->second; + } + } + T* o = make(); + o->name = key; + { + std::lock_guard lk(Lock()); + Objects().insert(o); + if (!key.empty()) + Names()[key] = o; + } + t_lastError = 0; + return o; +} + +DWORD WaitImpl(DWORD count, const HANDLE* handles, bool waitAll, DWORD ms) { + if (count == 0 || count > 64 || !handles) { + t_lastError = 87; + return WAIT_FAILED; + } + DWORD self = GetCurrentThreadId(); + std::vector objs(count); + std::vector gens(count, 0); + std::unique_lock lk(Lock()); + CheckSuspendLocked(lk); + for (DWORD i = 0; i < count; ++i) { + Object* o = LookupLocked(handles[i]); + if (!o) { + for (DWORD j = 0; j < i; ++j) + --objs[j]->waiters; + lk.unlock(); + for (DWORD j = 0; j < i; ++j) + Release(objs[j]); + t_lastError = ERROR_INVALID_HANDLE; + return WAIT_FAILED; + } + ++o->refs; + ++o->waiters; + objs[i] = o; + if (o->kind == Kind::Event) + gens[i] = static_cast(o)->pulseGen; + } + auto deadline = std::chrono::steady_clock::now() + std::chrono::milliseconds(ms == INFINITE ? 0 : ms); + DWORD result = WAIT_TIMEOUT; + for (bool timedOut = false;;) { + if (waitAll) { + bool all = true; + for (DWORD i = 0; i < count && all; ++i) + all = IsReady(objs[i], self, gens[i]); + if (all) { + for (DWORD i = 0; i < count; ++i) + AcquireLocked(objs[i], self); + result = WAIT_OBJECT_0; + break; + } + } else { + bool done = false; + for (DWORD i = 0; i < count; ++i) + if (IsReady(objs[i], self, gens[i])) { + AcquireLocked(objs[i], self); + result = WAIT_OBJECT_0 + i; + done = true; + break; + } + if (done) + break; + } + if (ms == 0 || timedOut) + break; + if (ms == INFINITE) + Cv().wait(lk); + else if (Cv().wait_until(lk, deadline) == std::cv_status::timeout) + timedOut = true; + } + for (Object* o : objs) + --o->waiters; + lk.unlock(); + Cv().notify_all(); + for (Object* o : objs) + Release(o); + return result; +} + +std::vector SplitCommandLine(const char* cmd) { + std::vector args; + if (!cmd) + return args; + const char* p = cmd; + while (*p == ' ' || *p == '\t') + ++p; + if (!*p) + return args; + std::string prog; + if (*p == '"') { + ++p; + while (*p && *p != '"') + prog += *p++; + if (*p == '"') + ++p; + } else { + while (*p && *p != ' ' && *p != '\t') + prog += *p++; + } + args.push_back(prog); + for (;;) { + while (*p == ' ' || *p == '\t') + ++p; + if (!*p) + break; + std::string arg; + bool quoted = false; + while (*p && (quoted || (*p != ' ' && *p != '\t'))) { + if (*p == '\\') { + size_t n = 0; + while (*p == '\\') { + ++n; + ++p; + } + if (*p == '"') { + arg.append(n / 2, '\\'); + if (n % 2) { + arg += '"'; + ++p; + } + } else { + arg.append(n, '\\'); + } + } else if (*p == '"') { + if (quoted && p[1] == '"') { + arg += '"'; + p += 2; + } else { + quoted = !quoted; + ++p; + } + } else { + arg += *p++; + } + } + args.push_back(arg); + } + return args; +} + +bool IsExecutableFile(const std::string& path) { + struct stat st; + return !path.empty() && stat(path.c_str(), &st) == 0 && S_ISREG(st.st_mode) && access(path.c_str(), X_OK) == 0; +} + +bool EndsWithExe(const std::string& s) { return s.size() > 4 && strcasecmp(s.c_str() + s.size() - 4, ".exe") == 0; } + +std::string ResolveProgram(const std::string& appName) { + std::vector candidates{appName}; + if (EndsWithExe(appName)) + candidates.push_back(appName.substr(0, appName.size() - 4)); + for (const std::string& name : candidates) { + if (name.find('\\') != std::string::npos || name.find('/') != std::string::npos) { + std::string native = FsPath(name.c_str()); + if (IsExecutableFile(native)) + return native; + continue; + } + std::string native = NativePath(name.c_str()); + std::vector dirs; + std::string exe = ExecutablePath(); + size_t slash = exe.rfind('/'); + if (slash != std::string::npos) + dirs.push_back(exe.substr(0, slash)); + const char* path = getenv("PATH"); + std::string all = path ? path : "/usr/local/bin:/usr/bin:/bin"; + size_t start = 0; + for (;;) { + size_t colon = all.find(':', start); + std::string dir = all.substr(start, colon == std::string::npos ? std::string::npos : colon - start); + dirs.push_back(dir.empty() ? "." : dir); + if (colon == std::string::npos) + break; + start = colon + 1; + } + dirs.push_back("."); + for (const std::string& dir : dirs) { + std::string full = dir + "/" + native; + if (IsExecutableFile(full)) + return full; + } + } + return std::string(); +} + +std::string ConvertArgument(const std::string& arg) { + bool drive = arg.size() >= 3 && arg[1] == ':' && (arg[2] == '\\' || arg[2] == '/') && isalpha(static_cast(arg[0])); + if (arg.find('\\') != std::string::npos || drive) + return NativePath(arg.c_str()); + return AnsiToUtf8(arg.c_str()); +} + +void ClearCloexec(int fd) { + int flags = fcntl(fd, F_GETFD); + if (flags >= 0) + fcntl(fd, F_SETFD, flags & ~FD_CLOEXEC); +} + +} // namespace + +HANDLE StartThread(std::function body, DWORD flags, size_t stackSize, DWORD* threadId) { + auto* t = new ThreadObj; + t->body = std::move(body); + t->id = NewThreadId(); + t->stackSize = stackSize; + if (threadId) + *threadId = t->id; + if (flags & CREATE_SUSPENDED) { + t->suspendCount = 1; + Register(t); + return t; + } + t->started = true; + t->refs = 2; + Register(t); + if (!Launch(t)) { + FailLaunch(t); + CloseHandle(t); + t_lastError = ERROR_NOT_ENOUGH_MEMORY; + return nullptr; + } + return t; +} + +bool IsProcessMainThread() { +#if defined(__APPLE__) + return pthread_main_np() != 0; +#elif defined(__linux__) + return static_cast(syscall(SYS_gettid)) == getpid(); +#else + static const pthread_t mainThread = pthread_self(); + return pthread_equal(mainThread, pthread_self()) != 0; +#endif +} + +HANDLE HandleFromFd(int fd, const std::string& nativePath, bool owns) { + if (fd < 0) + return INVALID_HANDLE_VALUE; + auto* f = new FileObj(fd, nativePath, owns); + Register(f); + return f; +} + +int FdFromHandle(HANDLE h) { + std::lock_guard lk(Lock()); + auto* f = LookupLocked(h, Kind::File); + return f ? f->fd : -1; +} + +} // namespace mfcwx + +using namespace mfcwx; + +DWORD GetLastError(void) { return t_lastError; } + +void SetLastError(DWORD err) { t_lastError = err; } + +DWORD GetCurrentThreadId(void) { + if (!t_threadId) + t_threadId = NewThreadId(); + return t_threadId; +} + +DWORD GetCurrentProcessId(void) { return static_cast(getpid()); } + +HANDLE GetCurrentProcess(void) { return kCurrentProcess; } + +HANDLE GetCurrentThread(void) { return kCurrentThread; } + +void Sleep(DWORD ms) { + CheckSuspend(); + if (ms == 0) { + sched_yield(); + } else if (ms == INFINITE) { + for (;;) + pause(); + } else { + struct timespec ts; + ts.tv_sec = ms / 1000; + ts.tv_nsec = static_cast(ms % 1000) * 1000000L; + while (nanosleep(&ts, &ts) != 0 && errno == EINTR) { + } + } + CheckSuspend(); +} + +DWORD WaitForSingleObject(HANDLE h, DWORD ms) { return WaitImpl(1, &h, false, ms); } + +DWORD WaitForMultipleObjects(DWORD count, const HANDLE* handles, BOOL waitAll, DWORD ms) { + return WaitImpl(count, handles, waitAll != FALSE, ms); +} + +BOOL CloseHandle(HANDLE h) { + if (h == kCurrentThread || h == kCurrentProcess) + return TRUE; + Object* o; + { + std::lock_guard lk(Lock()); + o = LookupLocked(h); + if (!o || o->handles <= 0) { + t_lastError = ERROR_INVALID_HANDLE; + return FALSE; + } + if (--o->handles == 0 && !o->name.empty()) { + auto it = Names().find(o->name); + if (it != Names().end() && it->second == o) + Names().erase(it); + o->name.clear(); + } + } + Release(o); + return TRUE; +} + +BOOL GetExitCodeThread(HANDLE thread, LPDWORD code) { + std::lock_guard lk(Lock()); + if (thread == kCurrentThread) { + if (code) + *code = STILL_ACTIVE; + return TRUE; + } + if (auto* t = LookupLocked(thread, Kind::Thread)) { + if (code) + *code = t->finished ? t->exitCode : STILL_ACTIVE; + return TRUE; + } + if (auto* pt = LookupLocked(thread, Kind::ProcessThread)) { + if (code) + *code = pt->process->exited ? pt->process->exitCode : STILL_ACTIVE; + return TRUE; + } + t_lastError = ERROR_INVALID_HANDLE; + return FALSE; +} + +BOOL GetExitCodeProcess(HANDLE process, LPDWORD code) { + std::lock_guard lk(Lock()); + if (process == kCurrentProcess) { + if (code) + *code = STILL_ACTIVE; + return TRUE; + } + auto* p = LookupLocked(process, Kind::Process); + if (!p) { + t_lastError = ERROR_INVALID_HANDLE; + return FALSE; + } + if (code) + *code = p->exited ? p->exitCode : STILL_ACTIVE; + return TRUE; +} + +BOOL TerminateThread(HANDLE thread, DWORD code) { + // Only a thread that has not started yet can be stopped safely. + { + std::lock_guard lk(Lock()); + auto* t = LookupLocked(thread, Kind::Thread); + if (!t) { + t_lastError = ERROR_INVALID_HANDLE; + return FALSE; + } + if (t->started || t->finished) { + t_lastError = 50; + return FALSE; + } + t->started = true; + t->finished = true; + t->exitCode = code; + t->body = nullptr; + } + Cv().notify_all(); + return TRUE; +} + +BOOL TerminateProcess(HANDLE process, UINT code) { + if (process == kCurrentProcess) + _exit(static_cast(code)); + pid_t pid; + { + std::lock_guard lk(Lock()); + auto* p = LookupLocked(process, Kind::Process); + if (!p) { + t_lastError = ERROR_INVALID_HANDLE; + return FALSE; + } + if (p->exited) + return TRUE; + pid = p->pid; + } + if (kill(pid, SIGKILL) != 0) { + SetLastErrorFromErrno(errno); + return FALSE; + } + return TRUE; +} + +HANDLE CreateThread(LPSECURITY_ATTRIBUTES, SIZE_T stack, LPTHREAD_START_ROUTINE fn, LPVOID param, DWORD flags, + LPDWORD threadId) { + if (!fn) { + t_lastError = 87; + return nullptr; + } + DWORD id = 0; + HANDLE h = StartThread([fn, param]() { return fn(param); }, flags, stack, &id); + if (threadId) + *threadId = id; + return h; +} + +DWORD ResumeThread(HANDLE thread) { + ThreadObj* launch = nullptr; + DWORD previous; + { + std::lock_guard lk(Lock()); + auto* t = LookupLocked(thread, Kind::Thread); + if (!t || t->zombie) { + t_lastError = ERROR_INVALID_HANDLE; + return static_cast(-1); + } + previous = static_cast(t->suspendCount); + if (t->suspendCount > 0) + --t->suspendCount; + if (t->suspendCount == 0 && !t->started) { + t->started = true; + ++t->refs; + launch = t; + } + } + Cv().notify_all(); + if (launch && !Launch(launch)) + FailLaunch(launch); + return previous; +} + +DWORD SuspendThread(HANDLE thread) { + // Suspension is cooperative: a running thread stops at its next Sleep or wait. + std::unique_lock lk(Lock()); + ThreadObj* t = thread == kCurrentThread ? t_self : LookupLocked(thread, Kind::Thread); + if (!t || t->zombie || t->finished) { + t_lastError = ERROR_INVALID_HANDLE; + return static_cast(-1); + } + DWORD previous = static_cast(t->suspendCount++); + if (t == t_self) + CheckSuspendLocked(lk); + return previous; +} + +void ExitThread(DWORD code) { + if (t_self) + throw ThreadExit{code, true}; + pthread_exit(nullptr); +} + +BOOL SetThreadPriority(HANDLE thread, int priority) { + std::lock_guard lk(Lock()); + ThreadObj* t = thread == kCurrentThread ? t_self : LookupLocked(thread, Kind::Thread); + if (t) + t->priority = priority; + else if (thread != kCurrentThread) { + t_lastError = ERROR_INVALID_HANDLE; + return FALSE; + } + return TRUE; +} + +int GetThreadPriority(HANDLE thread) { + std::lock_guard lk(Lock()); + ThreadObj* t = thread == kCurrentThread ? t_self : LookupLocked(thread, Kind::Thread); + return t ? t->priority : THREAD_PRIORITY_NORMAL; +} + +BOOL SetPriorityClass(HANDLE, DWORD) { return TRUE; } + +HANDLE CreateEventA(LPSECURITY_ATTRIBUTES, BOOL manualReset, BOOL initialState, LPCSTR name) { + return CreateNamed(name, Kind::Event, + [&]() { return new EventObj(manualReset != FALSE, initialState != FALSE); }); +} + +BOOL SetEvent(HANDLE h) { + { + std::lock_guard lk(Lock()); + auto* e = LookupLocked(h, Kind::Event); + if (!e) { + t_lastError = ERROR_INVALID_HANDLE; + return FALSE; + } + e->signaled = true; + } + Cv().notify_all(); + return TRUE; +} + +BOOL ResetEvent(HANDLE h) { + std::lock_guard lk(Lock()); + auto* e = LookupLocked(h, Kind::Event); + if (!e) { + t_lastError = ERROR_INVALID_HANDLE; + return FALSE; + } + e->signaled = false; + return TRUE; +} + +BOOL PulseEvent(HANDLE h) { + { + std::lock_guard lk(Lock()); + auto* e = LookupLocked(h, Kind::Event); + if (!e) { + t_lastError = ERROR_INVALID_HANDLE; + return FALSE; + } + if (e->manual) { + ++e->pulseGen; + e->signaled = false; + } else if (e->waiters > 0) { + e->signaled = true; + } + } + Cv().notify_all(); + return TRUE; +} + +HANDLE CreateMutexA(LPSECURITY_ATTRIBUTES, BOOL initialOwner, LPCSTR name) { + DWORD self = GetCurrentThreadId(); + return CreateNamed(name, Kind::Mutex, [&]() { + auto* m = new MutexObj; + if (initialOwner) { + m->owner = self; + m->count = 1; + } + return m; + }); +} + +BOOL ReleaseMutex(HANDLE h) { + DWORD self = GetCurrentThreadId(); + { + std::lock_guard lk(Lock()); + auto* m = LookupLocked(h, Kind::Mutex); + if (!m) { + t_lastError = ERROR_INVALID_HANDLE; + return FALSE; + } + if (m->owner != self || m->count <= 0) { + t_lastError = 288; + return FALSE; + } + if (--m->count == 0) + m->owner = 0; + } + Cv().notify_all(); + return TRUE; +} + +HANDLE CreateSemaphoreA(LPSECURITY_ATTRIBUTES, LONG initial, LONG max, LPCSTR name) { + if (max <= 0 || initial < 0 || initial > max) { + t_lastError = 87; + return nullptr; + } + return CreateNamed(name, Kind::Semaphore, [&]() { return new SemaphoreObj(initial, max); }); +} + +BOOL ReleaseSemaphore(HANDLE h, LONG count, LPLONG previous) { + { + std::lock_guard lk(Lock()); + auto* s = LookupLocked(h, Kind::Semaphore); + if (!s) { + t_lastError = ERROR_INVALID_HANDLE; + return FALSE; + } + if (count <= 0 || count > s->max - s->count) { + t_lastError = 298; + return FALSE; + } + if (previous) + *previous = s->count; + s->count += count; + } + Cv().notify_all(); + return TRUE; +} + +static std::recursive_mutex* CriticalSectionImpl(LPCRITICAL_SECTION cs) { + auto* impl = static_cast(__atomic_load_n(&cs->impl, __ATOMIC_ACQUIRE)); + if (impl) + return impl; + auto* fresh = new std::recursive_mutex; + void* expected = nullptr; + if (__atomic_compare_exchange_n(&cs->impl, &expected, static_cast(fresh), false, __ATOMIC_ACQ_REL, + __ATOMIC_ACQUIRE)) + return fresh; + delete fresh; + return static_cast(expected); +} + +void InitializeCriticalSection(LPCRITICAL_SECTION cs) { cs->impl = new std::recursive_mutex; } + +void DeleteCriticalSection(LPCRITICAL_SECTION cs) { + delete static_cast(cs->impl); + cs->impl = nullptr; +} + +void EnterCriticalSection(LPCRITICAL_SECTION cs) { CriticalSectionImpl(cs)->lock(); } + +void LeaveCriticalSection(LPCRITICAL_SECTION cs) { CriticalSectionImpl(cs)->unlock(); } + +BOOL TryEnterCriticalSection(LPCRITICAL_SECTION cs) { return CriticalSectionImpl(cs)->try_lock() ? TRUE : FALSE; } + +LONG InterlockedIncrement(LONG volatile* p) { return __atomic_add_fetch(p, 1, __ATOMIC_SEQ_CST); } + +LONG InterlockedDecrement(LONG volatile* p) { return __atomic_sub_fetch(p, 1, __ATOMIC_SEQ_CST); } + +LONG InterlockedExchange(LONG volatile* p, LONG v) { return __atomic_exchange_n(p, v, __ATOMIC_SEQ_CST); } + +LONG InterlockedExchangeAdd(LONG volatile* p, LONG v) { return __atomic_fetch_add(p, v, __ATOMIC_SEQ_CST); } + +LONG InterlockedCompareExchange(LONG volatile* p, LONG exchange, LONG comparand) { + __atomic_compare_exchange_n(p, &comparand, exchange, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + return comparand; +} + +BOOL CreateProcessA(LPCSTR app, LPSTR cmdLine, LPSECURITY_ATTRIBUTES, LPSECURITY_ATTRIBUTES, BOOL, DWORD, + LPVOID env, LPCSTR curDir, LPSTARTUPINFO si, LPPROCESS_INFORMATION pi) { + std::vector args = SplitCommandLine(cmdLine); + std::string program = app && *app ? std::string(app) : (args.empty() ? std::string() : args[0]); + if (program.empty()) { + t_lastError = 87; + return FALSE; + } + if (args.empty()) + args.push_back(program); + std::string path = ResolveProgram(program); + if (path.empty()) { + t_lastError = ERROR_FILE_NOT_FOUND; + return FALSE; + } + std::vector argStore; + argStore.push_back(path); + for (size_t i = 1; i < args.size(); ++i) + argStore.push_back(ConvertArgument(args[i])); + std::vector argv; + for (std::string& a : argStore) + argv.push_back(&a[0]); + argv.push_back(nullptr); + + std::vector envStore; + std::vector envp; + if (env) { + for (const char* e = static_cast(env); *e; e += strlen(e) + 1) + envStore.push_back(AnsiToUtf8(e)); + for (std::string& e : envStore) + envp.push_back(&e[0]); + envp.push_back(nullptr); + } + std::string cwd = curDir && *curDir ? FsPath(curDir) : std::string(); + + int redirect[3] = {-1, -1, -1}; + int devNull = -1; + if (si && (si->dwFlags & STARTF_USESTDHANDLES)) { + HANDLE stdHandles[3] = {si->hStdInput, si->hStdOutput, si->hStdError}; + for (int i = 0; i < 3; ++i) + redirect[i] = stdHandles[i] ? FdFromHandle(stdHandles[i]) : -1; + if (!si->hStdInput) { + devNull = open("/dev/null", O_RDONLY | O_CLOEXEC); + redirect[0] = devNull; + } + } + + int errPipe[2]; + if (pipe(errPipe) != 0) { + SetLastErrorFromErrno(errno); + if (devNull >= 0) + close(devNull); + return FALSE; + } + fcntl(errPipe[0], F_SETFD, FD_CLOEXEC); + fcntl(errPipe[1], F_SETFD, FD_CLOEXEC); + + pid_t pid = fork(); + if (pid == 0) { + sigset_t none; + sigemptyset(&none); + pthread_sigmask(SIG_SETMASK, &none, nullptr); + signal(SIGPIPE, SIG_DFL); + for (int i = 0; i < 3; ++i) { + if (redirect[i] < 0) + continue; + if (redirect[i] == i) + ClearCloexec(i); + else + dup2(redirect[i], i); + } + int err = 0; + if (!cwd.empty() && chdir(cwd.c_str()) != 0) + err = errno; + if (!err) { + if (env) + execve(path.c_str(), argv.data(), envp.data()); + else + execv(path.c_str(), argv.data()); + err = errno; + } + ssize_t ignored = write(errPipe[1], &err, sizeof err); + (void)ignored; + _exit(127); + } + close(errPipe[1]); + if (devNull >= 0) + close(devNull); + if (pid < 0) { + int err = errno; + close(errPipe[0]); + SetLastErrorFromErrno(err); + return FALSE; + } + int childErr = 0; + ssize_t n; + while ((n = read(errPipe[0], &childErr, sizeof childErr)) < 0 && errno == EINTR) { + } + close(errPipe[0]); + if (n == static_cast(sizeof childErr)) { + int status; + while (waitpid(pid, &status, 0) < 0 && errno == EINTR) { + } + SetLastErrorFromErrno(childErr); + return FALSE; + } + + auto* proc = new ProcessObj; + proc->pid = pid; + proc->refs = 3; + auto* thread = new ProcessThreadObj(proc); + Register(proc); + Register(thread); + std::thread([proc]() { + int status = 0; + pid_t r; + while ((r = waitpid(proc->pid, &status, 0)) < 0 && errno == EINTR) { + } + DWORD code = 0; + if (r == proc->pid) { + if (WIFEXITED(status)) + code = static_cast(WEXITSTATUS(status)); + else if (WIFSIGNALED(status)) + code = static_cast(128 + WTERMSIG(status)); + } + { + std::lock_guard lk(Lock()); + proc->exitCode = code; + proc->exited = true; + } + Cv().notify_all(); + Release(proc); + }).detach(); + + if (pi) { + pi->hProcess = proc; + pi->hThread = thread; + pi->dwProcessId = static_cast(pid); + pi->dwThreadId = NewThreadId(); + } else { + CloseHandle(proc); + CloseHandle(thread); + } + t_lastError = 0; + return TRUE; +} + +UINT WinExec(LPCSTR cmdLine, UINT) { + STARTUPINFO si; + memset(&si, 0, sizeof si); + si.cb = sizeof si; + PROCESS_INFORMATION pi; + std::string cmd = cmdLine ? cmdLine : ""; + if (!CreateProcessA(nullptr, &cmd[0], nullptr, nullptr, FALSE, 0, nullptr, nullptr, &si, &pi)) { + DWORD err = GetLastError(); + return err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND ? err : 11; + } + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + return 33; +} + +void ExitProcess(UINT code) { exit(static_cast(code)); } + +uintptr_t mfcwx_beginthread(void (*fn)(void*), unsigned stack, void* arg) { + if (!fn) + return static_cast(-1); + HANDLE h = StartThread([fn, arg]() -> DWORD { + fn(arg); + return 0; + }, 0, stack, nullptr); + if (!h) + return static_cast(-1); + // _beginthread handles are closed by the runtime when the thread ends. + CloseHandle(h); + return reinterpret_cast(h); +} + +uintptr_t mfcwx_beginthreadex(void*, unsigned stack, unsigned (*fn)(void*), void* arg, unsigned flags, + unsigned* threadId) { + if (!fn) + return 0; + DWORD id = 0; + HANDLE h = StartThread([fn, arg]() -> DWORD { return fn(arg); }, flags, stack, &id); + if (threadId) + *threadId = id; + return reinterpret_cast(h); +} + +void mfcwx_endthread(unsigned code) { ExitThread(code); } + +void mfcwx_sleep_ms(unsigned long ms) { Sleep(static_cast(ms)); } diff --git a/port/mfcwx/src/resources.cpp b/port/mfcwx/src/resources.cpp new file mode 100644 index 000000000..cb979f985 --- /dev/null +++ b/port/mfcwx/src/resources.cpp @@ -0,0 +1,335 @@ +#include "internal.h" + +#include +#include +#include + +#include +#include +#include +#include + +namespace mfcwx { + +namespace { + +struct ResourceContext { + std::vector modules; + std::string language = "en"; + std::string dataDir; + std::mutex bitmapMutex; + std::unordered_map bitmapCache; +}; + +ResourceContext& Ctx() { + static ResourceContext ctx; + return ctx; +} + +bool SameName(const char* a, const char* b) { return a && b && strcasecmp(a, b) == 0; } + +// Languages to search, in order of preference. +std::vector SearchOrder() { + std::vector order; + const std::string& lang = Ctx().language; + auto add = [&order](const rc::Language* l) { + if (l && std::find(order.begin(), order.end(), l) == order.end()) + order.push_back(l); + }; + for (const rc::Module* m : Ctx().modules) { + for (int i = 0; i < m->languageCount; ++i) + if (lang == m->languages[i]->code) + add(m->languages[i]); + } + for (const char* fallback : {"en", "de", "neutral"}) { + for (const rc::Module* m : Ctx().modules) + for (int i = 0; i < m->languageCount; ++i) + if (strcmp(m->languages[i]->code, fallback) == 0) + add(m->languages[i]); + } + for (const rc::Module* m : Ctx().modules) + for (int i = 0; i < m->languageCount; ++i) + add(m->languages[i]); + return order; +} + +int CodePageForLanguage(const char* code) { + for (const rc::Module* m : Ctx().modules) + for (int i = 0; i < m->languageCount; ++i) + if (strcmp(m->languages[i]->code, code) == 0 && m->languages[i]->codepage) + return m->languages[i]->codepage; + return 1252; +} + +} // namespace + +ResRef ResRef::From(const char* lpszName) { + ResRef r; + if (!lpszName) + return r; + if (IS_INTRESOURCE(lpszName)) { + r.id = static_cast(reinterpret_cast(lpszName)); + } else if (lpszName[0] == '#') { + r.id = atoi(lpszName + 1); + } else { + r.name = lpszName; + } + return r; +} + +bool ResRef::Matches(int otherId, const char* otherName) const { + if (!name.empty()) + return SameName(name.c_str(), otherName); + return id != 0 && id == otherId; +} + +void RegisterResourceModule(const rc::Module* module) { + if (module && std::find(Ctx().modules.begin(), Ctx().modules.end(), module) == Ctx().modules.end()) + Ctx().modules.push_back(module); +} + +void SetResourceLanguage(const char* code) { + Ctx().language = code ? code : "en"; + SetAnsiCodePage(CodePageForLanguage(Ctx().language.c_str())); +} + +const char* GetResourceLanguage() { return Ctx().language.c_str(); } + +std::vector AvailableResourceLanguages() { + std::vector codes; + for (const rc::Module* m : Ctx().modules) + for (int i = 0; i < m->languageCount; ++i) { + const rc::Language* l = m->languages[i]; + if (l->dialogCount > 10 && std::find(codes.begin(), codes.end(), l->code) == codes.end()) + codes.push_back(l->code); + } + return codes; +} + +const rc::Dialog* FindDialog(const ResRef& ref) { + for (const rc::Language* l : SearchOrder()) + for (int i = 0; i < l->dialogCount; ++i) + if (ref.Matches(l->dialogs[i].id, l->dialogs[i].name)) + return &l->dialogs[i]; + return nullptr; +} + +const rc::Menu* FindMenu(const ResRef& ref) { + for (const rc::Language* l : SearchOrder()) + for (int i = 0; i < l->menuCount; ++i) + if (ref.Matches(l->menus[i].id, l->menus[i].name)) + return &l->menus[i]; + return nullptr; +} + +const rc::AccelTable* FindAccelTable(const ResRef& ref) { + for (const rc::Language* l : SearchOrder()) + for (int i = 0; i < l->accelCount; ++i) + if (ref.Matches(l->accels[i].id, l->accels[i].name)) + return &l->accels[i]; + return nullptr; +} + +const rc::Toolbar* FindToolbar(const ResRef& ref) { + for (const rc::Language* l : SearchOrder()) + for (int i = 0; i < l->toolbarCount; ++i) + if (ref.Matches(l->toolbars[i].id, l->toolbars[i].name)) + return &l->toolbars[i]; + return nullptr; +} + +std::string FindFileResource(const char* type, const ResRef& ref) { + for (const rc::Language* l : SearchOrder()) + for (int i = 0; i < l->fileCount; ++i) { + const rc::FileResource& f = l->files[i]; + if ((!type || SameName(type, f.type)) && ref.Matches(f.id, f.name)) + return GetDataDirectory() + "/" + f.path; + } + return std::string(); +} + +std::vector FindDlgInit(int dialogId) { + std::vector result; + for (const rc::Language* l : SearchOrder()) { + for (int i = 0; i < l->dlgInitCount; ++i) + if (l->dlgInits[i].dialogId == dialogId) + result.push_back(&l->dlgInits[i]); + if (!result.empty()) + break; + } + return result; +} + +bool LoadResourceString(UINT id, std::string& out) { + for (const rc::Language* l : SearchOrder()) { + const rc::StringEntry* begin = l->strings; + const rc::StringEntry* end = l->strings + l->stringCount; + const rc::StringEntry* it = std::lower_bound( + begin, end, static_cast(id), [](const rc::StringEntry& e, int v) { return e.id < v; }); + if (it != end && it->id == static_cast(id)) { + out = Utf8ToAnsi(it->text); + return true; + } + } + return false; +} + +const rc::VersionInfo* GetVersionInfo() { + for (const rc::Language* l : SearchOrder()) + if (l->version) + return l->version; + return nullptr; +} + +std::string GetDataDirectory() { + if (Ctx().dataDir.empty()) { + wxString dir = wxStandardPaths::Get().GetResourcesDir(); + Ctx().dataDir = std::string(dir.utf8_str()); + } + return Ctx().dataDir; +} + +void SetDataDirectory(const std::string& dir) { Ctx().dataDir = dir; } + +wxBitmap LoadBitmapResource(const ResRef& ref) { + std::string path = FindFileResource("BITMAP", ref); + if (path.empty()) + path = FindFileResource(nullptr, ref); + if (path.empty()) + return wxBitmap(); + std::lock_guard lock(Ctx().bitmapMutex); + auto it = Ctx().bitmapCache.find(path); + if (it != Ctx().bitmapCache.end()) + return it->second; + if (!wxImage::FindHandler(wxBITMAP_TYPE_BMP)) + wxInitAllImageHandlers(); + wxLogNull noLog; + wxImage image; + if (!image.LoadFile(wxString::FromUTF8(path), wxBITMAP_TYPE_ANY)) + return wxBitmap(); + wxBitmap bitmap(image); + Ctx().bitmapCache.emplace(path, bitmap); + return bitmap; +} + +wxIcon LoadIconResource(const ResRef& ref) { + std::string path = FindFileResource("ICON", ref); + wxIcon icon; + if (path.empty()) + return icon; + if (!wxImage::FindHandler(wxBITMAP_TYPE_ICO)) + wxInitAllImageHandlers(); + wxLogNull noLog; + wxImage image; + if (image.LoadFile(wxString::FromUTF8(path), wxBITMAP_TYPE_ICO)) + icon.CopyFromBitmap(wxBitmap(image)); + return icon; +} + +wxCursor LoadCursorResource(const ResRef& ref) { + std::string path = FindFileResource("CURSOR", ref); + if (path.empty()) + return wxCursor(); + if (!wxImage::FindHandler(wxBITMAP_TYPE_CUR)) + wxInitAllImageHandlers(); + wxLogNull noLog; + wxImage image; + if (!image.LoadFile(wxString::FromUTF8(path), wxBITMAP_TYPE_CUR)) + return wxCursor(); + return wxCursor(image); +} + +wxString MenuTextToWx(const char* utf8Text) { + wxString text = wxString::FromUTF8(utf8Text ? utf8Text : ""); + int tab = text.Find('\t'); + if (tab != wxNOT_FOUND) + text = text.Left(tab); + return text; +} + +} // namespace mfcwx + +// --------------------------------------------------------------------------------------------- +// Win32 resource functions + +int LoadString(HINSTANCE, UINT uID, char* lpBuffer, int cchBufferMax) { + if (!lpBuffer || cchBufferMax <= 0) + return 0; + std::string s; + if (!mfcwx::LoadResourceString(uID, s)) { + lpBuffer[0] = 0; + return 0; + } + int n = std::min(static_cast(s.size()), cchBufferMax - 1); + memcpy(lpBuffer, s.data(), static_cast(n)); + lpBuffer[n] = 0; + return n; +} + +int LoadStringA(HINSTANCE hInstance, UINT uID, char* lpBuffer, int cchBufferMax) { + return LoadString(hInstance, uID, lpBuffer, cchBufferMax); +} + +namespace { + +struct ResourceHandle { + std::string type; + mfcwx::ResRef ref; + std::vector data; +}; + +} // namespace + +HRSRC FindResource(HMODULE, const char* lpName, const char* lpType) { + mfcwx::ResRef ref = mfcwx::ResRef::From(lpName); + std::string type; + if (IS_INTRESOURCE(lpType)) { + switch (static_cast(reinterpret_cast(lpType))) { + case 1: type = "CURSOR"; break; + case 2: type = "BITMAP"; break; + case 3: type = "ICON"; break; + case 23: type = "HTML"; break; + default: break; + } + } else if (lpType) { + type = lpType; + } + if (type == "RT_DLGINIT" || (IS_INTRESOURCE(lpType) && reinterpret_cast(lpType) == 240)) + return nullptr; + std::string path = mfcwx::FindFileResource(type.empty() ? nullptr : type.c_str(), ref); + if (path.empty()) + return nullptr; + auto* h = new ResourceHandle{type, ref, {}}; + FILE* f = fopen(path.c_str(), "rb"); + if (!f) { + delete h; + return nullptr; + } + char buf[65536]; + size_t n; + while ((n = fread(buf, 1, sizeof buf, f)) > 0) + h->data.insert(h->data.end(), buf, buf + n); + fclose(f); + return reinterpret_cast(h); +} + +HRSRC FindResourceA(HMODULE hModule, const char* lpName, const char* lpType) { + return FindResource(hModule, lpName, lpType); +} + +HGLOBAL LoadResource(HMODULE, HRSRC hResInfo) { return reinterpret_cast(hResInfo); } + +LPVOID LockResource(HGLOBAL hResData) { + if (!hResData) + return nullptr; + auto* h = reinterpret_cast(hResData); + return h->data.empty() ? nullptr : h->data.data(); +} + +DWORD SizeofResource(HMODULE, HRSRC hResInfo) { + if (!hResInfo) + return 0; + return static_cast(reinterpret_cast(hResInfo)->data.size()); +} + +BOOL FreeResource(HGLOBAL) { return TRUE; } diff --git a/port/mfcwx/src/runtime.h b/port/mfcwx/src/runtime.h new file mode 100644 index 000000000..f3b70bf0a --- /dev/null +++ b/port/mfcwx/src/runtime.h @@ -0,0 +1,67 @@ +#pragma once + +// Internal contracts of the non-GUI runtime (kernel objects, paths, errors). + +#include "mfcwx/win32.h" + +#include +#include + +namespace mfcwx { + +// Thrown by ExitThread/AfxEndThread and caught by the thread trampoline. +struct ThreadExit { + DWORD code; + bool deleteThread; +}; + +// Creates a kernel thread object running body (now, or on ResumeThread with CREATE_SUSPENDED). +// The thread is signaled after body returns; body's result is the exit code. +HANDLE StartThread(std::function body, DWORD flags, size_t stackSize, DWORD* threadId); +bool IsProcessMainThread(); + +// File kernel objects wrap a POSIX descriptor. With owns == false CloseHandle leaves fd open. +HANDLE HandleFromFd(int fd, const std::string& nativePath, bool owns = true); +int FdFromHandle(HANDLE h); + +// Native path for a path given by the application, resolving letter case per component +// when the exact path does not exist (Windows paths are case-insensitive). +std::string FsPath(const char* appPath); +// Absolute, normalized native path ("." and ".." removed); relative paths are based on the cwd. +std::string AbsoluteNativePath(const char* appPath); +// Absolute path in the application's form ('\\' separators, ANSI). +std::string FullAppPath(const char* appPath); + +std::string ExecutablePath(); +std::string TempDirectory(); +std::string UserConfigDirectory(); +void SetCommandLineOverride(const char* commandLine); + +DWORD ErrnoToWin32(int err); +void SetLastErrorFromErrno(int err); + +// FILETIME counts 100 ns intervals since 1601-01-01 UTC. +constexpr LONGLONG kFileTimeUnixEpoch = 116444736000000000LL; +inline FILETIME MakeFileTime(time_t sec, long nsec = 0) { + ULONGLONG v = static_cast(static_cast(sec) * 10000000LL + nsec / 100 + kFileTimeUnixEpoch); + FILETIME ft; + ft.dwLowDateTime = static_cast(v); + ft.dwHighDateTime = static_cast(v >> 32); + return ft; +} +inline LONGLONG FileTimeTicks(const FILETIME& ft) { + return static_cast((static_cast(ft.dwHighDateTime) << 32) | ft.dwLowDateTime); +} +inline time_t FileTimeToUnix(const FILETIME& ft) { + LONGLONG t = FileTimeTicks(ft) - kFileTimeUnixEpoch; + return static_cast(t >= 0 ? t / 10000000LL : -((-t + 9999999LL) / 10000000LL)); +} + +// Case mapping and classification of single ANSI characters in the current code page. +char AnsiToUpper(char ch); +char AnsiToLower(char ch); +bool AnsiIsAlpha(char ch); +bool AnsiIsUpper(char ch); +bool AnsiIsLower(char ch); + +} // namespace mfcwx diff --git a/port/mfcwx/src/windows_impl.h b/port/mfcwx/src/windows_impl.h new file mode 100644 index 000000000..b4bece8f3 --- /dev/null +++ b/port/mfcwx/src/windows_impl.h @@ -0,0 +1,95 @@ +#pragma once + +// Internal helpers shared by wnd.cpp, controls.cpp, dialog.cpp, winapi.cpp, frames and doc/view. + +#include "internal.h" + +#include + +namespace mfcwx { + +template +R OnMainImpl(F&& f, std::false_type) { + R result{}; + RunOnMainThread([&] { result = f(); }); + return result; +} + +template +void OnMainImpl(F&& f, std::true_type) { + RunOnMainThread([&] { f(); }); +} + +// Runs f on the main thread (synchronously) and returns its result. +template +auto OnMain(F&& f) -> decltype(f()) { + using R = decltype(f()); + if (IsMainThread()) + return f(); + return OnMainImpl(std::forward(f), std::is_void()); +} + + +class MessageScope { +public: + MessageScope(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, wxEvent* event); + ~MessageScope(); + bool DefaultCalled() const; + MessageScope(const MessageScope&) = delete; + MessageScope& operator=(const MessageScope&) = delete; +}; + +bool IsManagedWindow(wxWindow* window); +void OnWindowGone(wxWindow* window); +CWnd* PermanentWnd(wxWindow* window); +CWnd* WrapperFor(wxWindow* window); +bool HasMessageHandler(CWnd* pWnd, UINT message); +LRESULT SendFromEvent(wxWindow* window, UINT msg, WPARAM wParam, LPARAM lParam, wxEvent& event, bool* defaultCalled); + +UINT VirtualKeyFromWx(int code); +int WxKeyFromVirtual(UINT vk); +LPARAM KeyLParam(const wxKeyEvent& e, bool up); +WPARAM MouseFlags(const wxMouseEvent& e); +bool TranslateCharToAnsi(const wxKeyEvent& e, UINT& ch); + +UINT_PTR StartTimer(wxWindow* window, UINT_PTR id, UINT elapse, TIMERPROC proc); +bool StopTimer(wxWindow* window, UINT_PTR id); + +// controls.cpp +void BindControlEvents(wxWindow* window, WindowState& state); +// Applies ES_NUMBER / ES_UPPERCASE / ES_LOWERCASE and read-only rules; true if the key was consumed. +bool FilterEditChar(wxWindow* window, WindowState& state, UINT& ch, wxKeyEvent& event); +void NotifyFocusChange(wxWindow* window, WindowState& state, bool gained); +// Replaces a native control by a custom-drawn window if pWnd paints it itself; returns the window to attach. +wxWindow* PrepareForSubclass(wxWindow* window, CWnd* pWnd); +wxWindow* CreateWindowForClass(CWnd* pWnd, CREATESTRUCT& cs); +DWORD CurrentStyle(wxWindow* window); +void ApplyStyle(wxWindow* window, DWORD oldStyle, DWORD newStyle); +LRESULT ControlWindowProc(wxWindow* window, WindowState& state, UINT msg, WPARAM wParam, LPARAM lParam, bool& handled); +// Sends WM_CTLCOLOR* for a control and applies the resulting colours. +void ApplyCtlColor(wxWindow* control); +wxString GetControlText(wxWindow* window); +void SetControlText(wxWindow* window, const wxString& text); + +// wnd.cpp / winapi.cpp +void DestroyWxWindow(wxWindow* window); +// The parent in the Win32 sense (skips internal wx containers). +wxWindow* LogicalParent(wxWindow* window); +void ResetCursorRequest(); +const wxCursor* RequestedCursor(); +// Sends a WM_COMMAND notification for a control to its parent. +void NotifyParent(wxWindow* control, UINT code); +// Sends WM_NOTIFY with the given header (hwndFrom/idFrom are filled in). +LRESULT NotifyParentNM(wxWindow* control, NMHDR* hdr); + +// dialog.cpp +BOOL ApplyDlgInit(CWnd* pWnd, int dialogId); +// Handles Enter/Escape/F1 like the Windows dialog manager; true if consumed. +bool DialogKeyHook(wxWindow* dialog, wxKeyEvent& event); + +// app.cpp +BOOL ShowContextHelp(CWnd* pWnd, HELPINFO* pHelpInfo); +// Runs the application's PreTranslateMessage chain for a key event; true if consumed. +bool PreTranslateKey(wxWindow* topLevel, wxKeyEvent& event, UINT message); + +} // namespace mfcwx diff --git a/port/mfcwx/src/wnd.cpp b/port/mfcwx/src/wnd.cpp new file mode 100644 index 000000000..5113bae1c --- /dev/null +++ b/port/mfcwx/src/wnd.cpp @@ -0,0 +1,1813 @@ +#include "internal.h" +#include "windows_impl.h" + +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace mfcwx { + +// --------------------------------------------------------------------------------------------- +// Threads + +namespace { + +const std::thread::id g_mainThread = std::this_thread::get_id(); + +} // namespace + +bool IsMainThread() { return std::this_thread::get_id() == g_mainThread; } + +void RunOnMainThread(const std::function& fn) { + if (IsMainThread() || !wxTheApp) { + fn(); + return; + } + std::mutex m; + std::condition_variable cv; + bool done = false; + std::exception_ptr error; + wxTheApp->CallAfter([&] { + try { + fn(); + } catch (...) { + error = std::current_exception(); + } + std::lock_guard lock(m); + done = true; + cv.notify_one(); + }); + std::unique_lock lock(m); + cv.wait(lock, [&] { return done; }); + if (error) + std::rethrow_exception(error); +} + +void PostToMainThread(std::function fn) { + if (wxTheApp) + wxTheApp->CallAfter(std::move(fn)); +} + +// --------------------------------------------------------------------------------------------- +// Window registry + +namespace { + +std::unordered_map>& States() { + static auto* states = new std::unordered_map>(); + return *states; +} + +std::unordered_map& TempWrappers() { + static auto* temps = new std::unordered_map(); + return *temps; +} + +// Deleted together with the wx window; performs the final MFC-side cleanup. +class WindowTracker : public wxClientData { +public: + explicit WindowTracker(wxWindow* window) : m_window(window) {} + ~WindowTracker() override { OnWindowGone(m_window); } + +private: + wxWindow* m_window; +}; + +std::vector& MessageStack() { + static std::vector stack; + return stack; +} + +} // namespace + +WindowState* GetState(wxWindow* window) { + if (!window) + return nullptr; + auto it = States().find(window); + return it == States().end() ? nullptr : it->second.get(); +} + +WindowState& EnsureState(wxWindow* window) { + auto& slot = States()[window]; + if (!slot) { + slot.reset(new WindowState); + slot->window = window; + window->SetClientObject(new WindowTracker(window)); + } + return *slot; +} + +bool IsManagedWindow(wxWindow* window) { return window && States().count(window) != 0; } + +void OnWindowGone(wxWindow* window) { + auto it = States().find(window); + if (it == States().end()) + return; + std::unique_ptr state = std::move(it->second); + States().erase(it); + for (auto& t : state->timers) + t.second->Stop(); + auto tmp = TempWrappers().find(window); + if (tmp != TempWrappers().end()) { + CWnd* wrapper = tmp->second; + TempWrappers().erase(tmp); + wrapper->m_hWnd = nullptr; + delete wrapper; + } + CWnd* pWnd = state->permanent; + if (pWnd && pWnd->m_hWnd == ToHwnd(window)) { + if (!state->destroyNotified) { + state->destroyNotified = true; + pWnd->SendMessage(WM_DESTROY); + } + pWnd->m_hWnd = nullptr; + pWnd->PostNcDestroy(); + } +} + +CurrentMessage* GetCurrentMessageSlot() { + auto& stack = MessageStack(); + return stack.empty() ? nullptr : &stack.back(); +} + +MessageScope::MessageScope(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, wxEvent* event) { + CurrentMessage cm; + cm.msg.hwnd = hWnd; + cm.msg.message = message; + cm.msg.wParam = wParam; + cm.msg.lParam = lParam; + cm.msg.time = static_cast(wxGetLocalTimeMillis().GetLo()); + wxPoint p = wxGetMousePosition(); + cm.msg.pt.x = p.x; + cm.msg.pt.y = p.y; + cm.event = event; + cm.defaultCalled = false; + MessageStack().push_back(cm); +} + +MessageScope::~MessageScope() { MessageStack().pop_back(); } + +bool MessageScope::DefaultCalled() const { return MessageStack().back().defaultCalled; } + +CWnd* PermanentWnd(wxWindow* window) { + WindowState* s = GetState(window); + return s ? s->permanent : nullptr; +} + +CWnd* WrapperFor(wxWindow* window) { + if (!window) + return nullptr; + if (CWnd* p = PermanentWnd(window)) + return p; + auto it = TempWrappers().find(window); + if (it != TempWrappers().end()) + return it->second; + EnsureState(window); + CWnd* wrapper = new CWnd; + wrapper->m_hWnd = ToHwnd(window); + TempWrappers()[window] = wrapper; + return wrapper; +} + +bool HasMessageHandler(CWnd* pWnd, UINT message) { + return pWnd && pWnd->FindMessageEntry(message, 0, 0) != nullptr; +} + +// --------------------------------------------------------------------------------------------- +// Message dispatch + +LRESULT DispatchMessageTo(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { + wxWindow* window = ToWx(hWnd); + if (!window || !IsManagedWindow(window)) + return 0; + CWnd* pWnd = PermanentWnd(window); + MessageScope scope(hWnd, msg, wParam, lParam, nullptr); + if (pWnd) + return pWnd->WindowProc(msg, wParam, lParam); + return DefaultWindowProc(hWnd, msg, wParam, lParam); +} + +LRESULT SendFromEvent(wxWindow* window, UINT msg, WPARAM wParam, LPARAM lParam, wxEvent& event, bool* defaultCalled) { + CWnd* pWnd = PermanentWnd(window); + MessageScope scope(ToHwnd(window), msg, wParam, lParam, &event); + LRESULT r = pWnd ? pWnd->WindowProc(msg, wParam, lParam) : DefaultWindowProc(ToHwnd(window), msg, wParam, lParam); + if (defaultCalled) + *defaultCalled = scope.DefaultCalled(); + return r; +} + +// --------------------------------------------------------------------------------------------- +// Keyboard translation + +UINT VirtualKeyFromWx(int code) { + if (code >= 'a' && code <= 'z') + return static_cast(code - 'a' + 'A'); + if ((code >= 'A' && code <= 'Z') || (code >= '0' && code <= '9')) + return static_cast(code); + switch (code) { + case WXK_BACK: return VK_BACK; + case WXK_TAB: return VK_TAB; + case WXK_RETURN: case WXK_NUMPAD_ENTER: return VK_RETURN; + case WXK_ESCAPE: return VK_ESCAPE; + case WXK_SPACE: return VK_SPACE; + case WXK_DELETE: case WXK_NUMPAD_DELETE: return VK_DELETE; + case WXK_SHIFT: return VK_SHIFT; + case WXK_ALT: return VK_MENU; + case WXK_CONTROL: return VK_CONTROL; + case WXK_PAUSE: return VK_PAUSE; + case WXK_CAPITAL: return VK_CAPITAL; + case WXK_END: case WXK_NUMPAD_END: return VK_END; + case WXK_HOME: case WXK_NUMPAD_HOME: return VK_HOME; + case WXK_LEFT: case WXK_NUMPAD_LEFT: return VK_LEFT; + case WXK_UP: case WXK_NUMPAD_UP: return VK_UP; + case WXK_RIGHT: case WXK_NUMPAD_RIGHT: return VK_RIGHT; + case WXK_DOWN: case WXK_NUMPAD_DOWN: return VK_DOWN; + case WXK_PAGEUP: case WXK_NUMPAD_PAGEUP: return VK_PRIOR; + case WXK_PAGEDOWN: case WXK_NUMPAD_PAGEDOWN: return VK_NEXT; + case WXK_INSERT: case WXK_NUMPAD_INSERT: return VK_INSERT; + case WXK_HELP: return VK_HELP; + case WXK_NUMPAD0: case WXK_NUMPAD1: case WXK_NUMPAD2: case WXK_NUMPAD3: case WXK_NUMPAD4: + case WXK_NUMPAD5: case WXK_NUMPAD6: case WXK_NUMPAD7: case WXK_NUMPAD8: case WXK_NUMPAD9: + return static_cast(VK_NUMPAD0 + (code - WXK_NUMPAD0)); + case WXK_MULTIPLY: case WXK_NUMPAD_MULTIPLY: return VK_MULTIPLY; + case WXK_ADD: case WXK_NUMPAD_ADD: return VK_ADD; + case WXK_SUBTRACT: case WXK_NUMPAD_SUBTRACT: return VK_SUBTRACT; + case WXK_DECIMAL: case WXK_NUMPAD_DECIMAL: return VK_DECIMAL; + case WXK_DIVIDE: case WXK_NUMPAD_DIVIDE: return VK_DIVIDE; + case WXK_NUMLOCK: return VK_NUMLOCK; + case WXK_SCROLL: return VK_SCROLL; + case ',': return VK_OEM_COMMA; + case '.': return VK_OEM_PERIOD; + case '-': return VK_OEM_MINUS; + case '+': case '=': return VK_OEM_PLUS; + default: break; + } + if (code >= WXK_F1 && code <= WXK_F24) + return static_cast(VK_F1 + (code - WXK_F1)); + return 0; +} + +int WxKeyFromVirtual(UINT vk) { + if ((vk >= 'A' && vk <= 'Z') || (vk >= '0' && vk <= '9')) + return static_cast(vk); + switch (vk) { + case VK_BACK: return WXK_BACK; + case VK_TAB: return WXK_TAB; + case VK_RETURN: return WXK_RETURN; + case VK_ESCAPE: return WXK_ESCAPE; + case VK_SPACE: return WXK_SPACE; + case VK_DELETE: return WXK_DELETE; + case VK_SHIFT: case VK_LSHIFT: case VK_RSHIFT: return WXK_SHIFT; + case VK_CONTROL: case VK_LCONTROL: case VK_RCONTROL: return WXK_CONTROL; + case VK_MENU: case VK_LMENU: case VK_RMENU: return WXK_ALT; + case VK_CAPITAL: return WXK_CAPITAL; + case VK_END: return WXK_END; + case VK_HOME: return WXK_HOME; + case VK_LEFT: return WXK_LEFT; + case VK_UP: return WXK_UP; + case VK_RIGHT: return WXK_RIGHT; + case VK_DOWN: return WXK_DOWN; + case VK_PRIOR: return WXK_PAGEUP; + case VK_NEXT: return WXK_PAGEDOWN; + case VK_INSERT: return WXK_INSERT; + case VK_NUMLOCK: return WXK_NUMLOCK; + case VK_SCROLL: return WXK_SCROLL; + case VK_LBUTTON: return WXK_LBUTTON; + case VK_RBUTTON: return WXK_RBUTTON; + case VK_MBUTTON: return WXK_MBUTTON; + default: break; + } + if (vk >= VK_F1 && vk <= VK_F1 + 23) + return WXK_F1 + static_cast(vk - VK_F1); + if (vk >= VK_NUMPAD0 && vk <= VK_NUMPAD9) + return WXK_NUMPAD0 + static_cast(vk - VK_NUMPAD0); + return 0; +} + +LPARAM KeyLParam(const wxKeyEvent& e, bool up) { + LPARAM lp = 1; + if (e.AltDown()) + lp |= 1L << 29; + if (up) + lp |= (1L << 30) | (1L << 31); + return lp; +} + +WPARAM MouseFlags(const wxMouseEvent& e) { + WPARAM f = 0; + if (e.LeftIsDown()) + f |= MK_LBUTTON; + if (e.RightIsDown()) + f |= MK_RBUTTON; + if (e.MiddleIsDown()) + f |= MK_MBUTTON; + if (e.ShiftDown()) + f |= MK_SHIFT; + if (e.ControlDown()) + f |= MK_CONTROL; + return f; +} + +bool TranslateCharToAnsi(const wxKeyEvent& e, UINT& ch) { + wxChar uc = e.GetUnicodeKey(); + int code = e.GetKeyCode(); + if (uc == WXK_NONE) { + if (code > 0 && code < 32) { + ch = static_cast(code); + return true; + } + return false; + } + if (uc == 127) + return false; + if (uc < 32) { + ch = static_cast(uc); + return true; + } + char a; + UnicodeToAnsi(static_cast(uc), a); + ch = static_cast(a); + return true; +} + +// --------------------------------------------------------------------------------------------- +// Event bridge: generic window events + +namespace { + +void OnKeyDown(wxKeyEvent& e) { + wxWindow* w = static_cast(e.GetEventObject()); + CWnd* pWnd = PermanentWnd(w); + UINT msg = e.AltDown() && !e.ControlDown() ? WM_SYSKEYDOWN : WM_KEYDOWN; + UINT vk = VirtualKeyFromWx(e.GetKeyCode()); + if (!vk || !HasMessageHandler(pWnd, msg)) { + e.Skip(); + return; + } + bool def = false; + SendFromEvent(w, msg, vk, KeyLParam(e, false), e, &def); + if (def) + e.Skip(); +} + +void OnKeyUp(wxKeyEvent& e) { + wxWindow* w = static_cast(e.GetEventObject()); + CWnd* pWnd = PermanentWnd(w); + UINT msg = e.AltDown() && !e.ControlDown() ? WM_SYSKEYUP : WM_KEYUP; + UINT vk = VirtualKeyFromWx(e.GetKeyCode()); + if (!vk || !HasMessageHandler(pWnd, msg)) { + e.Skip(); + return; + } + bool def = false; + SendFromEvent(w, msg, vk, KeyLParam(e, true), e, &def); + if (def) + e.Skip(); +} + +void OnChar(wxKeyEvent& e) { + wxWindow* w = static_cast(e.GetEventObject()); + WindowState* st = GetState(w); + CWnd* pWnd = st ? st->permanent : nullptr; + UINT ch = 0; + if (!TranslateCharToAnsi(e, ch)) { + e.Skip(); + return; + } + if (st && FilterEditChar(w, *st, ch, e)) + return; + if (!HasMessageHandler(pWnd, WM_CHAR)) { + e.Skip(); + return; + } + bool def = false; + SendFromEvent(w, WM_CHAR, ch, KeyLParam(e, false), e, &def); +} + +void SendMouse(wxMouseEvent& e, UINT msg) { + wxWindow* w = static_cast(e.GetEventObject()); + CWnd* pWnd = PermanentWnd(w); + if (!HasMessageHandler(pWnd, msg)) { + e.Skip(); + return; + } + bool def = false; + SendFromEvent(w, msg, MouseFlags(e), MAKELPARAM(e.GetX(), e.GetY()), e, &def); + // Native controls need the default handling to keep working (focus, selection, ...). + e.Skip(); +} + +void OnMouseWheel(wxMouseEvent& e) { + wxWindow* w = static_cast(e.GetEventObject()); + CWnd* pWnd = PermanentWnd(w); + if (!HasMessageHandler(pWnd, WM_MOUSEWHEEL) || e.GetWheelAxis() != wxMOUSE_WHEEL_VERTICAL) { + e.Skip(); + return; + } + int delta = e.GetWheelRotation() * WHEEL_DELTA / std::max(1, e.GetWheelDelta()); + wxPoint screen = w->ClientToScreen(e.GetPosition()); + bool def = false; + SendFromEvent(w, WM_MOUSEWHEEL, MAKEWPARAM(MouseFlags(e), static_cast(static_cast(delta))), + MAKELPARAM(screen.x, screen.y), e, &def); + if (def) + e.Skip(); +} + +void OnPaint(wxPaintEvent& e) { + wxWindow* w = static_cast(e.GetEventObject()); + CWnd* pWnd = PermanentWnd(w); + WindowState* st = GetState(w); + if (st && st->customPaint) { + st->customPaint(e); + return; + } + if (!HasMessageHandler(pWnd, WM_PAINT)) { + e.Skip(); + return; + } + bool def = false; + SendFromEvent(w, WM_PAINT, 0, 0, e, &def); + if (def) + e.Skip(); +} + +void OnEraseBackground(wxEraseEvent& e) { + wxWindow* w = static_cast(e.GetEventObject()); + CWnd* pWnd = PermanentWnd(w); + if (!HasMessageHandler(pWnd, WM_ERASEBKGND) || !e.GetDC()) { + e.Skip(); + return; + } + CDC* dc = WrapDC(e.GetDC(), w); + bool def = false; + LRESULT r = SendFromEvent(w, WM_ERASEBKGND, reinterpret_cast(dc->m_hDC), 0, e, &def); + UnwrapDC(dc); + if (def || !r) + e.Skip(); +} + +void OnSize(wxSizeEvent& e) { + wxWindow* w = static_cast(e.GetEventObject()); + e.Skip(); + CWnd* pWnd = PermanentWnd(w); + if (!HasMessageHandler(pWnd, WM_SIZE)) + return; + wxSize sz = w->GetClientSize(); + WPARAM type = SIZE_RESTORED; + if (auto* tlw = wxDynamicCast(w, wxTopLevelWindow)) { + if (tlw->IsIconized()) + type = SIZE_MINIMIZED; + else if (tlw->IsMaximized()) + type = SIZE_MAXIMIZED; + } + SendFromEvent(w, WM_SIZE, type, MAKELPARAM(sz.x, sz.y), e, nullptr); +} + +void OnMove(wxMoveEvent& e) { + wxWindow* w = static_cast(e.GetEventObject()); + e.Skip(); + CWnd* pWnd = PermanentWnd(w); + if (!HasMessageHandler(pWnd, WM_MOVE)) + return; + wxPoint p = w->GetPosition(); + SendFromEvent(w, WM_MOVE, 0, MAKELPARAM(p.x, p.y), e, nullptr); +} + +void OnFocus(wxFocusEvent& e, bool set) { + wxWindow* w = static_cast(e.GetEventObject()); + e.Skip(); + WindowState* st = GetState(w); + if (!st) + return; + HWND other = ToHwnd(e.GetWindow()); + CWnd* pWnd = st->permanent; + if (HasMessageHandler(pWnd, set ? WM_SETFOCUS : WM_KILLFOCUS)) + SendFromEvent(w, set ? WM_SETFOCUS : WM_KILLFOCUS, reinterpret_cast(other), 0, e, nullptr); + NotifyFocusChange(w, *st, set); +} + +void OnContextMenu(wxContextMenuEvent& e) { + wxWindow* w = static_cast(e.GetEventObject()); + for (wxWindow* t = w; t; t = t->GetParent()) { + CWnd* pWnd = PermanentWnd(t); + if (HasMessageHandler(pWnd, WM_CONTEXTMENU)) { + wxPoint p = e.GetPosition(); + LPARAM lp = p == wxDefaultPosition ? static_cast(-1) : MAKELPARAM(p.x, p.y); + bool def = false; + SendFromEvent(t, WM_CONTEXTMENU, reinterpret_cast(ToHwnd(w)), lp, e, &def); + if (def) + e.Skip(); + return; + } + if (t->IsTopLevel()) + break; + } + e.Skip(); +} + +void OnSetCursor(wxSetCursorEvent& e) { + wxWindow* w = static_cast(e.GetEventObject()); + CWnd* pWnd = PermanentWnd(w); + if (!HasMessageHandler(pWnd, WM_SETCURSOR)) { + e.Skip(); + return; + } + ResetCursorRequest(); + bool def = false; + LRESULT r = SendFromEvent(w, WM_SETCURSOR, reinterpret_cast(ToHwnd(w)), + MAKELPARAM(HTCLIENT, WM_MOUSEMOVE), e, &def); + const wxCursor* c = RequestedCursor(); + if (r && c) + e.SetCursor(*c); + else + e.Skip(); +} + +void OnShow(wxShowEvent& e) { + wxWindow* w = static_cast(e.GetEventObject()); + e.Skip(); + CWnd* pWnd = PermanentWnd(w); + if (HasMessageHandler(pWnd, WM_SHOWWINDOW)) + SendFromEvent(w, WM_SHOWWINDOW, e.IsShown() ? TRUE : FALSE, 0, e, nullptr); +} + +void OnDestroyEvent(wxWindowDestroyEvent& e) { + e.Skip(); + wxWindow* w = static_cast(e.GetEventObject()); + if (w != e.GetWindow()) + return; + WindowState* st = GetState(w); + if (!st || st->destroyNotified || !st->permanent) + return; + st->destroyNotified = true; + st->permanent->SendMessage(WM_DESTROY); +} + +} // namespace + +void HookWindow(wxWindow* window, ControlKind kind, int winId, DWORD style, DWORD exStyle) { + WindowState& st = EnsureState(window); + st.kind = kind; + st.winId = winId; + st.style = style; + st.exStyle = exStyle; + if (st.hooked) + return; + st.hooked = true; + window->Bind(wxEVT_KEY_DOWN, &OnKeyDown); + window->Bind(wxEVT_KEY_UP, &OnKeyUp); + window->Bind(wxEVT_CHAR, &OnChar); + window->Bind(wxEVT_LEFT_DOWN, [](wxMouseEvent& e) { SendMouse(e, WM_LBUTTONDOWN); }); + window->Bind(wxEVT_LEFT_UP, [](wxMouseEvent& e) { SendMouse(e, WM_LBUTTONUP); }); + window->Bind(wxEVT_LEFT_DCLICK, [](wxMouseEvent& e) { SendMouse(e, WM_LBUTTONDBLCLK); }); + window->Bind(wxEVT_RIGHT_DOWN, [](wxMouseEvent& e) { SendMouse(e, WM_RBUTTONDOWN); }); + window->Bind(wxEVT_RIGHT_UP, [](wxMouseEvent& e) { SendMouse(e, WM_RBUTTONUP); }); + window->Bind(wxEVT_RIGHT_DCLICK, [](wxMouseEvent& e) { SendMouse(e, WM_RBUTTONDBLCLK); }); + window->Bind(wxEVT_MIDDLE_DOWN, [](wxMouseEvent& e) { SendMouse(e, WM_MBUTTONDOWN); }); + window->Bind(wxEVT_MIDDLE_UP, [](wxMouseEvent& e) { SendMouse(e, WM_MBUTTONUP); }); + window->Bind(wxEVT_MOTION, [](wxMouseEvent& e) { SendMouse(e, WM_MOUSEMOVE); }); + window->Bind(wxEVT_MOUSEWHEEL, &OnMouseWheel); + window->Bind(wxEVT_PAINT, &OnPaint); + window->Bind(wxEVT_ERASE_BACKGROUND, &OnEraseBackground); + window->Bind(wxEVT_SIZE, &OnSize); + window->Bind(wxEVT_MOVE, &OnMove); + window->Bind(wxEVT_SET_FOCUS, [](wxFocusEvent& e) { OnFocus(e, true); }); + window->Bind(wxEVT_KILL_FOCUS, [](wxFocusEvent& e) { OnFocus(e, false); }); + window->Bind(wxEVT_CONTEXT_MENU, &OnContextMenu); + window->Bind(wxEVT_SET_CURSOR, &OnSetCursor); + window->Bind(wxEVT_SHOW, &OnShow); + window->Bind(wxEVT_DESTROY, &OnDestroyEvent); + BindControlEvents(window, st); +} + +// --------------------------------------------------------------------------------------------- +// Timers + +namespace { + +class TimerOwner : public wxTimer { +public: + TimerOwner(wxWindow* window, UINT_PTR id) : m_window(window), m_id(id) {} + void Notify() override { + wxWindow* w = m_window; + UINT_PTR id = m_id; + WindowState* st = GetState(w); + if (!st) + return; + auto proc = st->timerProcs.find(id); + if (proc != st->timerProcs.end() && proc->second) { + proc->second(ToHwnd(w), WM_TIMER, id, GetTickCount()); + return; + } + DispatchMessageTo(ToHwnd(w), WM_TIMER, id, 0); + } + +private: + wxWindow* m_window; + UINT_PTR m_id; +}; + +} // namespace + +UINT_PTR StartTimer(wxWindow* window, UINT_PTR id, UINT elapse, TIMERPROC proc) { + WindowState& st = EnsureState(window); + auto it = st.timers.find(id); + if (it != st.timers.end()) + it->second->Stop(); + std::unique_ptr timer(new TimerOwner(window, id)); + timer->Start(static_cast(std::max(elapse, USER_TIMER_MINIMUM))); + st.timers[id] = std::move(timer); + st.timerProcs[id] = proc; + return id ? id : 1; +} + +bool StopTimer(wxWindow* window, UINT_PTR id) { + WindowState* st = GetState(window); + if (!st) + return false; + auto it = st->timers.find(id); + if (it == st->timers.end()) + return false; + it->second->Stop(); + // The timer may be the one currently notifying; delete it later. + wxTimer* timer = it->second.release(); + st->timers.erase(it); + st->timerProcs.erase(id); + if (wxTheApp) + wxTheApp->CallAfter([timer] { delete timer; }); + return true; +} + +} // namespace mfcwx + +using namespace mfcwx; + +// --------------------------------------------------------------------------------------------- +// CWnd + +CRuntimeClass CWnd::classCWnd = {"CWnd", sizeof(CWnd), 0xFFFF, &CWnd::CreateObject, &CCmdTarget::GetThisClass, nullptr}; +static AFX_CLASSINIT _init_CWnd(&CWnd::classCWnd); +CRuntimeClass* CWnd::GetThisClass() { return &CWnd::classCWnd; } +CRuntimeClass* CWnd::GetRuntimeClass() const { return &CWnd::classCWnd; } +CObject* CWnd::CreateObject() { return new CWnd; } + +const AFX_MSGMAP* CWnd::GetMessageMap() const { return GetThisMessageMap(); } +const AFX_MSGMAP* CWnd::GetThisMessageMap() { + static const AFX_MSGMAP_ENTRY entries[] = {{0, 0, 0, 0, AfxSig_end, nullptr}}; + static const AFX_MSGMAP map = {&CCmdTarget::GetThisMessageMap, &entries[0]}; + return ↦ +} + +const CWnd wndTop; +const CWnd wndBottom; +const CWnd wndTopMost; +const CWnd wndNoTopMost; + +CWnd::CWnd() : m_hWnd(nullptr) {} + +CWnd::~CWnd() { + if (m_hWnd && IsMainThread()) { + wxWindow* w = GetWx(); + WindowState* st = GetState(w); + if (st && st->permanent == this) { + st->permanent = nullptr; + if (!st->destroyNotified && !w->IsBeingDeleted()) { + st->destroyNotified = true; + m_hWnd = nullptr; + w->Destroy(); + } + } + m_hWnd = nullptr; + } +} + +CWnd* CWnd::FromWx(wxWindow* window) { return WrapperFor(window); } + +CWnd* CWnd::FromHandle(HWND hWnd) { + if (!hWnd) + return nullptr; + return OnMain([&] { return WrapperFor(ToWx(hWnd)); }); +} + +CWnd* CWnd::FromHandlePermanent(HWND hWnd) { + if (!hWnd) + return nullptr; + return PermanentWnd(ToWx(hWnd)); +} + +void CWnd::DeleteTempMap() {} + +void CWnd::AttachWx(wxWindow* window) { + WindowState& st = EnsureState(window); + auto tmp = TempWrappers().find(window); + if (tmp != TempWrappers().end()) { + tmp->second->m_hWnd = nullptr; + delete tmp->second; + TempWrappers().erase(tmp); + } + st.permanent = this; + m_hWnd = ToHwnd(window); +} + +BOOL CWnd::Attach(HWND hWndNew) { + if (!hWndNew) + return FALSE; + OnMain([&] { AttachWx(ToWx(hWndNew)); }); + return TRUE; +} + +HWND CWnd::Detach() { + HWND h = m_hWnd; + if (h) { + WindowState* st = GetState(GetWx()); + if (st && st->permanent == this) + st->permanent = nullptr; + } + m_hWnd = nullptr; + return h; +} + +BOOL CWnd::SubclassWindow(HWND hWnd) { + if (!hWnd) + return FALSE; + return OnMain([&] { + wxWindow* w = ToWx(hWnd); + w = PrepareForSubclass(w, this); + AttachWx(w); + PreSubclassWindow(); + return TRUE; + }); +} + +BOOL CWnd::SubclassDlgItem(UINT nID, CWnd* pParent) { + if (!pParent) + return FALSE; + HWND h = nullptr; + pParent->GetDlgItem(static_cast(nID), &h); + return h ? SubclassWindow(h) : FALSE; +} + +HWND CWnd::UnsubclassWindow() { return Detach(); } + +BOOL CWnd::PreCreateWindow(CREATESTRUCT&) { return TRUE; } + +BOOL CWnd::Create(const char* lpszClassName, const char* lpszWindowName, DWORD dwStyle, const RECT& rect, + CWnd* pParentWnd, UINT nID, CCreateContext* pContext) { + return CreateEx(0, lpszClassName, lpszWindowName, dwStyle | WS_CHILD, rect.left, rect.top, + rect.right - rect.left, rect.bottom - rect.top, pParentWnd ? pParentWnd->m_hWnd : nullptr, + reinterpret_cast(static_cast(nID)), pContext); +} + +BOOL CWnd::CreateEx(DWORD dwExStyle, const char* lpszClassName, const char* lpszWindowName, DWORD dwStyle, + const RECT& rect, CWnd* pParentWnd, UINT nID, LPVOID lpParam) { + return CreateEx(dwExStyle, lpszClassName, lpszWindowName, dwStyle, rect.left, rect.top, rect.right - rect.left, + rect.bottom - rect.top, pParentWnd ? pParentWnd->m_hWnd : nullptr, + reinterpret_cast(static_cast(nID)), lpParam); +} + +BOOL CWnd::CreateEx(DWORD dwExStyle, const char* lpszClassName, const char* lpszWindowName, DWORD dwStyle, int x, + int y, int nWidth, int nHeight, HWND hWndParent, HMENU nIDorHMenu, LPVOID lpParam) { + return OnMain([&]() -> BOOL { + CREATESTRUCT cs; + memset(&cs, 0, sizeof cs); + cs.dwExStyle = dwExStyle; + cs.lpszClass = lpszClassName; + cs.lpszName = lpszWindowName; + cs.style = static_cast(dwStyle); + cs.x = x; + cs.y = y; + cs.cx = nWidth; + cs.cy = nHeight; + cs.hwndParent = hWndParent; + cs.hMenu = nIDorHMenu; + cs.lpCreateParams = lpParam; + if (!PreCreateWindow(cs)) + return FALSE; + wxWindow* window = CreateWindowForClass(this, cs); + if (!window) + return FALSE; + AttachWx(window); + PreSubclassWindow(); + if (SendMessage(WM_CREATE, 0, reinterpret_cast(&cs)) == -1) { + DestroyWindow(); + return FALSE; + } + if (m_hWnd) { + wxSize sz = window->GetClientSize(); + SendMessage(WM_SIZE, SIZE_RESTORED, MAKELPARAM(sz.x, sz.y)); + if (cs.style & WS_VISIBLE) + window->Show(); + } + return m_hWnd != nullptr; + }); +} + +BOOL CWnd::DestroyWindow() { + if (!m_hWnd) + return FALSE; + return OnMain([&]() -> BOOL { + wxWindow* w = GetWx(); + WindowState* st = GetState(w); + if (st && !st->destroyNotified) { + st->destroyNotified = true; + SendMessage(WM_DESTROY); + } + if (!m_hWnd) + return TRUE; + DestroyWxWindow(w); + return TRUE; + }); +} + +LRESULT CWnd::SendMessage(UINT message, WPARAM wParam, LPARAM lParam) { + return ::SendMessage(m_hWnd, message, wParam, lParam); +} + +BOOL CWnd::PostMessage(UINT message, WPARAM wParam, LPARAM lParam) { + return ::PostMessage(m_hWnd, message, wParam, lParam); +} + +BOOL CWnd::SendNotifyMessage(UINT message, WPARAM wParam, LPARAM lParam) { + return ::SendNotifyMessage(m_hWnd, message, wParam, lParam); +} + +void CWnd::SendMessageToDescendants(UINT message, WPARAM wParam, LPARAM lParam, BOOL bDeep, BOOL bOnlyPerm) { + OnMain([&] { + std::function walk = [&](wxWindow* parent) { + for (wxWindow* child : parent->GetChildren()) { + if (!IsManagedWindow(child)) + continue; + if (!bOnlyPerm || PermanentWnd(child)) + DispatchMessageTo(ToHwnd(child), message, wParam, lParam); + if (bDeep && IsManagedWindow(child)) + walk(child); + } + }; + if (m_hWnd) + walk(GetWx()); + }); +} + +LRESULT CWnd::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) { + LRESULT result = 0; + if (!OnWndMsg(message, wParam, lParam, &result)) + result = DefWindowProc(message, wParam, lParam); + return result; +} + +LRESULT CWnd::DefWindowProc(UINT message, WPARAM wParam, LPARAM lParam) { + return DefaultWindowProc(m_hWnd, message, wParam, lParam); +} + +LRESULT CWnd::Default() { + CurrentMessage* cm = GetCurrentMessageSlot(); + if (!cm) + return 0; + return DefWindowProc(cm->msg.message, cm->msg.wParam, cm->msg.lParam); +} + +const MSG* CWnd::GetCurrentMessage() { + CurrentMessage* cm = GetCurrentMessageSlot(); + static MSG empty; + return cm ? &cm->msg : ∅ +} + +struct AFX_NOTIFY { + LRESULT* pResult; + NMHDR* pNMHDR; +}; + +BOOL CWnd::OnCommand(WPARAM wParam, LPARAM lParam) { + UINT nID = LOWORD(wParam); + int nCode = HIWORD(wParam); + HWND hWndCtrl = reinterpret_cast(lParam); + if (hWndCtrl) { + if (ReflectChildNotify(WM_COMMAND, wParam, lParam, nullptr)) + return TRUE; + } + if (nID == 0) + return FALSE; + return OnCmdMsg(nID, nCode, nullptr, nullptr); +} + +BOOL CWnd::OnNotify(WPARAM, LPARAM lParam, LRESULT* pResult) { + NMHDR* pNMHDR = reinterpret_cast(lParam); + HWND hWndCtrl = pNMHDR->hwndFrom; + UINT nID = static_cast(pNMHDR->idFrom); + int nCode = static_cast(pNMHDR->code); + if (hWndCtrl && ReflectChildNotify(WM_NOTIFY, 0, lParam, pResult)) + return TRUE; + AFX_NOTIFY notify; + notify.pResult = pResult; + notify.pNMHDR = pNMHDR; + return OnCmdMsg(nID, MAKELONG(nCode, WM_NOTIFY), ¬ify, nullptr); +} + +BOOL CWnd::ReflectChildNotify(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult) { + HWND hChild = nullptr; + switch (message) { + case WM_COMMAND: + hChild = reinterpret_cast(lParam); + break; + case WM_NOTIFY: + hChild = reinterpret_cast(lParam)->hwndFrom; + break; + case WM_DRAWITEM: + hChild = reinterpret_cast(lParam)->hwndItem; + break; + case WM_HSCROLL: + case WM_VSCROLL: + case WM_CTLCOLOR: + hChild = reinterpret_cast(lParam); + break; + default: + break; + } + CWnd* child = hChild ? FromHandlePermanent(hChild) : nullptr; + if (!child || child == this) + return FALSE; + return child->OnChildNotify(message, wParam, lParam, pResult); +} + +BOOL CWnd::OnChildNotify(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult) { + UINT reflected = message + WM_REFLECT_BASE; + UINT code = 0; + if (message == WM_COMMAND) + code = HIWORD(wParam); + else if (message == WM_NOTIFY) + code = reinterpret_cast(lParam)->code; + const AFX_MSGMAP_ENTRY* e = nullptr; + for (const AFX_MSGMAP* map = GetMessageMap(); map && !e; map = map->pfnGetBaseMap ? map->pfnGetBaseMap() : nullptr) { + for (const AFX_MSGMAP_ENTRY* it = map->lpEntries; it->nSig != AfxSig_end; ++it) + if (it->nMessage == reflected && (message != WM_COMMAND && message != WM_NOTIFY ? true : it->nCode == code)) { + e = it; + break; + } + if (!map->pfnGetBaseMap) + break; + } + if (!e) + return FALSE; + switch (e->nSig) { + case AfxSig_vv_reflect: + (this->*e->pfn)(); + return TRUE; + case AfxSig_bv_reflect: + return (this->*reinterpret_cast(e->pfn))(); + case AfxSig_vNMHDRpl: + (this->*reinterpret_cast(e->pfn))( + reinterpret_cast(lParam), pResult); + return TRUE; + case AfxSig_bNMHDRpl: + return (this->*reinterpret_cast(e->pfn))( + reinterpret_cast(lParam), pResult); + case AfxSig_hDw: { + HBRUSH h = (static_cast(this)->*reinterpret_cast(e->pfn))( + CDC::FromHandle(reinterpret_cast(wParam)), static_cast(pResult ? *pResult : 0)); + if (pResult) + *pResult = reinterpret_cast(h); + return h != nullptr; + } + case AfxSig_vOWNER: + (static_cast(this)->*reinterpret_cast(e->pfn))( + reinterpret_cast(lParam)); + return TRUE; + case AfxSig_vwwW: + (static_cast(this)->*reinterpret_cast(e->pfn))(LOWORD(wParam), + HIWORD(wParam)); + return TRUE; + default: + return FALSE; + } +} + +BOOL CWnd::OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult) { + LRESULT lResult = 0; + if (message == WM_COMMAND) { + if (OnCommand(wParam, lParam)) { + if (pResult) + *pResult = 1; + return TRUE; + } + return FALSE; + } + if (message == WM_NOTIFY) { + if (OnNotify(wParam, lParam, &lResult)) { + if (pResult) + *pResult = lResult; + return TRUE; + } + return FALSE; + } + UINT lookup = message; + if (message >= WM_CTLCOLORMSGBOX && message <= WM_CTLCOLORSTATIC) + lookup = WM_CTLCOLOR; + if (message == WM_DRAWITEM || message == WM_HSCROLL || message == WM_VSCROLL || + (message >= WM_CTLCOLORMSGBOX && message <= WM_CTLCOLORSTATIC)) { + LRESULT reflectResult = message >= WM_CTLCOLORMSGBOX ? static_cast(message - WM_CTLCOLORMSGBOX) : 0; + if (ReflectChildNotify(lookup, wParam, lParam, &reflectResult)) { + if (pResult) + *pResult = reflectResult; + return TRUE; + } + } + const AFX_MSGMAP_ENTRY* e = FindMessageEntry(lookup, 0, 0); + if (!e) + return FALSE; + union { + AFX_PMSG pfn; + void (CWnd::*vv)(); + BOOL (CWnd::*bv)(); + LRESULT (CWnd::*lwl)(WPARAM, LPARAM); + int (CWnd::*is)(LPCREATESTRUCT); + BOOL (CWnd::*bD)(CDC*); + void (CWnd::*vwii)(UINT, int, int); + void (CWnd::*vwwx)(UINT, UINT, UINT); + void (CWnd::*vwp)(UINT, CPoint); + BOOL (CWnd::*bwsp)(UINT, short, CPoint); + void (CWnd::*vwwW)(UINT, UINT, CScrollBar*); + HBRUSH (CWnd::*hDWw)(CDC*, CWnd*, UINT); + void (CWnd::*vOWNER)(int, LPDRAWITEMSTRUCT); + void (CWnd::*vMEASURE)(int, LPMEASUREITEMSTRUCT); + void (CWnd::*vW)(CWnd*); + void (CWnd::*vWp)(CWnd*, CPoint); + BOOL (CWnd::*bWww)(CWnd*, UINT, UINT); + void (CWnd::*vbw)(BOOL, UINT); + void (CWnd::*vwl)(UINT, LPARAM); + void (CWnd::*vw_ptr)(UINT_PTR); + void (CWnd::*vw_uint)(UINT); + HCURSOR (CWnd::*hv)(); + BOOL (CWnd::*bHELPINFO)(HELPINFO*); + void (CWnd::*vPOS)(MINMAXINFO*); + void (CWnd::*vwWb)(UINT, CWnd*, BOOL); + UINT (CWnd::*wv)(); + void (CWnd::*vHDROP)(HDROP); + void (CWnd::*vMwb)(CMenu*, UINT, BOOL); + void (CWnd::*vii)(int, int); + void (CWnd::*vbWW)(BOOL, CWnd*, CWnd*); + BOOL (CWnd::*bb)(BOOL); + void (CWnd::*vb)(BOOL); + void (CWnd::*vwwh)(UINT, UINT, HMENU); + void (CWnd::*vRECT)(UINT, LPRECT); + } mmf; + mmf.pfn = e->pfn; + UINT_PTR sig = e->nMessage == 0xC000 ? AfxSig_lwl : e->nSig; + switch (sig) { + case AfxSig_vv: + (this->*mmf.vv)(); + break; + case AfxSig_bv: + lResult = (this->*mmf.bv)(); + break; + case AfxSig_lwl: + lResult = (this->*mmf.lwl)(wParam, lParam); + break; + case AfxSig_is: + lResult = (this->*mmf.is)(reinterpret_cast(lParam)); + break; + case AfxSig_bD: + lResult = (this->*mmf.bD)(CDC::FromHandle(reinterpret_cast(wParam))); + break; + case AfxSig_vwii: + (this->*mmf.vwii)(static_cast(wParam), LOWORD(lParam), HIWORD(lParam)); + break; + case AfxSig_vwwx: + (this->*mmf.vwwx)(static_cast(wParam), LOWORD(lParam), HIWORD(lParam)); + break; + case AfxSig_vwp: + (this->*mmf.vwp)(static_cast(wParam), CPoint(static_cast(LOWORD(lParam)), + static_cast(HIWORD(lParam)))); + break; + case AfxSig_bwsp: { + CPoint pt(static_cast(LOWORD(lParam)), static_cast(HIWORD(lParam))); + lResult = (this->*mmf.bwsp)(LOWORD(wParam), static_cast(HIWORD(wParam)), pt); + break; + } + case AfxSig_vwwW: + (this->*mmf.vwwW)(LOWORD(wParam), HIWORD(wParam), + reinterpret_cast(CWnd::FromHandle(reinterpret_cast(lParam)))); + break; + case AfxSig_hDWw: { + UINT ctlType = message == WM_CTLCOLOR ? static_cast(HIWORD(lParam)) : message - WM_CTLCOLORMSGBOX; + lResult = reinterpret_cast((this->*mmf.hDWw)(CDC::FromHandle(reinterpret_cast(wParam)), + CWnd::FromHandle(reinterpret_cast(lParam)), + ctlType)); + break; + } + case AfxSig_vOWNER: + (this->*mmf.vOWNER)(static_cast(wParam), reinterpret_cast(lParam)); + lResult = TRUE; + break; + case AfxSig_vMEASURE: + (this->*mmf.vMEASURE)(static_cast(wParam), reinterpret_cast(lParam)); + lResult = TRUE; + break; + case AfxSig_vW: + (this->*mmf.vW)(CWnd::FromHandle(reinterpret_cast(wParam))); + break; + case AfxSig_vWp: + (this->*mmf.vWp)(CWnd::FromHandle(reinterpret_cast(wParam)), + CPoint(static_cast(LOWORD(lParam)), static_cast(HIWORD(lParam)))); + break; + case AfxSig_bWww: + lResult = (this->*mmf.bWww)(CWnd::FromHandle(reinterpret_cast(wParam)), LOWORD(lParam), HIWORD(lParam)); + break; + case AfxSig_vbw: + (this->*mmf.vbw)(static_cast(wParam), static_cast(lParam)); + break; + case AfxSig_vwl: + (this->*mmf.vwl)(static_cast(wParam), lParam); + break; + case AfxSig_vw_ptr: + (this->*mmf.vw_ptr)(static_cast(wParam)); + break; + case AfxSig_vw_uint: + (this->*mmf.vw_uint)(static_cast(wParam)); + break; + case AfxSig_hv: + lResult = reinterpret_cast((this->*mmf.hv)()); + break; + case AfxSig_bHELPINFO: + lResult = (this->*mmf.bHELPINFO)(reinterpret_cast(lParam)); + break; + case AfxSig_vPOS: + (this->*mmf.vPOS)(reinterpret_cast(lParam)); + break; + case AfxSig_vwWb: + (this->*mmf.vwWb)(LOWORD(wParam), CWnd::FromHandle(reinterpret_cast(lParam)), HIWORD(wParam)); + break; + case AfxSig_wv: + lResult = (this->*mmf.wv)(); + break; + case AfxSig_vHDROP: + (this->*mmf.vHDROP)(reinterpret_cast(wParam)); + break; + case AfxSig_vMwb: + (this->*mmf.vMwb)(CMenu::FromHandle(reinterpret_cast(wParam)), LOWORD(lParam), HIWORD(lParam)); + break; + case AfxSig_vii: + (this->*mmf.vii)(static_cast(LOWORD(lParam)), static_cast(HIWORD(lParam))); + break; + case AfxSig_vbWW: + (this->*mmf.vbWW)(reinterpret_cast(lParam) == m_hWnd, CWnd::FromHandle(reinterpret_cast(lParam)), + CWnd::FromHandle(reinterpret_cast(wParam))); + break; + case AfxSig_bb: + lResult = (this->*mmf.bb)(static_cast(wParam)); + break; + case AfxSig_vb: + (this->*mmf.vb)(static_cast(wParam)); + break; + case AfxSig_vwwh: + (this->*mmf.vwwh)(LOWORD(wParam), HIWORD(wParam), reinterpret_cast(lParam)); + break; + case AfxSig_vRECT: + (this->*mmf.vRECT)(static_cast(wParam), reinterpret_cast(lParam)); + lResult = TRUE; + break; + default: + return FALSE; + } + if (pResult) + *pResult = lResult; + return TRUE; +} + +BOOL CWnd::PreTranslateMessage(MSG*) { return FALSE; } + +// Default handlers: forward to the window's default processing like MFC's CWnd::Default(). +int CWnd::OnCreate(LPCREATESTRUCT) { return static_cast(Default()); } +void CWnd::OnDestroy() { Default(); } +void CWnd::OnNcDestroy() { Default(); } +void CWnd::OnClose() { Default(); } +void CWnd::OnPaint() { Default(); } +BOOL CWnd::OnEraseBkgnd(CDC*) { return static_cast(Default()); } +void CWnd::OnSize(UINT, int, int) { Default(); } +void CWnd::OnMove(int, int) { Default(); } +void CWnd::OnTimer(UINT_PTR) { Default(); } +void CWnd::OnKeyDown(UINT, UINT, UINT) { Default(); } +void CWnd::OnKeyUp(UINT, UINT, UINT) { Default(); } +void CWnd::OnChar(UINT, UINT, UINT) { Default(); } +void CWnd::OnSysKeyDown(UINT, UINT, UINT) { Default(); } +void CWnd::OnSysKeyUp(UINT, UINT, UINT) { Default(); } +void CWnd::OnSysChar(UINT, UINT, UINT) { Default(); } +void CWnd::OnMouseMove(UINT, CPoint) { Default(); } +void CWnd::OnLButtonDown(UINT, CPoint) { Default(); } +void CWnd::OnLButtonUp(UINT, CPoint) { Default(); } +void CWnd::OnLButtonDblClk(UINT, CPoint) { Default(); } +void CWnd::OnRButtonDown(UINT, CPoint) { Default(); } +void CWnd::OnRButtonUp(UINT, CPoint) { Default(); } +void CWnd::OnRButtonDblClk(UINT, CPoint) { Default(); } +void CWnd::OnMButtonDown(UINT, CPoint) { Default(); } +void CWnd::OnMButtonUp(UINT, CPoint) { Default(); } +void CWnd::OnNcLButtonDown(UINT, CPoint) { Default(); } +BOOL CWnd::OnMouseWheel(UINT, short, CPoint) { return static_cast(Default()); } +void CWnd::OnHScroll(UINT, UINT, CScrollBar*) { Default(); } +void CWnd::OnVScroll(UINT, UINT, CScrollBar*) { Default(); } +HBRUSH CWnd::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) { + LRESULT r = 0; + if (pWnd && pWnd != this && pWnd->m_hWnd) { + CWnd* child = FromHandlePermanent(pWnd->m_hWnd); + r = nCtlColor; + if (child && child->OnChildNotify(WM_CTLCOLOR, reinterpret_cast(pDC->m_hDC), + reinterpret_cast(pWnd->m_hWnd), &r)) + return reinterpret_cast(r); + } + return nullptr; +} +void CWnd::OnDrawItem(int, LPDRAWITEMSTRUCT lpDrawItemStruct) { + LRESULT r = 0; + if (lpDrawItemStruct && lpDrawItemStruct->CtlType != ODT_MENU) + ReflectChildNotify(WM_DRAWITEM, 0, reinterpret_cast(lpDrawItemStruct), &r); +} +void CWnd::OnMeasureItem(int, LPMEASUREITEMSTRUCT) {} +void CWnd::OnSetFocus(CWnd*) { Default(); } +void CWnd::OnKillFocus(CWnd*) { Default(); } +void CWnd::OnContextMenu(CWnd*, CPoint) { Default(); } +BOOL CWnd::OnSetCursor(CWnd*, UINT, UINT) { return static_cast(Default()); } +void CWnd::OnShowWindow(BOOL, UINT) { Default(); } +void CWnd::OnSysCommand(UINT, LPARAM) { Default(); } +HCURSOR CWnd::OnQueryDragIcon() { return nullptr; } +BOOL CWnd::OnHelpInfo(HELPINFO* pHelpInfo) { return static_cast(ShowContextHelp(this, pHelpInfo)); } +void CWnd::OnGetMinMaxInfo(MINMAXINFO*) {} +void CWnd::OnActivate(UINT, CWnd*, BOOL) { Default(); } +UINT CWnd::OnGetDlgCode() { return static_cast(Default()); } +void CWnd::OnDropFiles(HDROP) {} +void CWnd::OnInitMenuPopup(CMenu*, UINT, BOOL) {} +void CWnd::OnMDIActivate(BOOL, CWnd*, CWnd*) {} +void CWnd::OnEnable(BOOL) {} +BOOL CWnd::OnNcActivate(BOOL) { return TRUE; } +void CWnd::OnMenuSelect(UINT, UINT, HMENU) {} +void CWnd::OnSizing(UINT, LPRECT) {} + +// --------------------------------------------------------------------------------------------- +// CWnd state and geometry + +DWORD CWnd::GetStyle() const { + return OnMain([&]() -> DWORD { return m_hWnd ? CurrentStyle(GetWx()) : 0; }); +} + +DWORD CWnd::GetExStyle() const { + return OnMain([&]() -> DWORD { + WindowState* st = m_hWnd ? GetState(GetWx()) : nullptr; + return st ? st->exStyle : 0; + }); +} + +BOOL CWnd::ModifyStyle(DWORD dwRemove, DWORD dwAdd, UINT) { + return OnMain([&]() -> BOOL { + if (!m_hWnd) + return FALSE; + DWORD old = CurrentStyle(GetWx()); + DWORD now = (old & ~dwRemove) | dwAdd; + if (now == old) + return FALSE; + ApplyStyle(GetWx(), old, now); + return TRUE; + }); +} + +BOOL CWnd::ModifyStyleEx(DWORD dwRemove, DWORD dwAdd, UINT) { + return OnMain([&]() -> BOOL { + WindowState* st = m_hWnd ? GetState(GetWx()) : nullptr; + if (!st) + return FALSE; + st->exStyle = (st->exStyle & ~dwRemove) | dwAdd; + return TRUE; + }); +} + +int CWnd::GetDlgCtrlID() const { + return OnMain([&]() -> int { + WindowState* st = m_hWnd ? GetState(GetWx()) : nullptr; + return st ? st->winId : 0; + }); +} + +int CWnd::SetDlgCtrlID(int nID) { + return OnMain([&]() -> int { + WindowState* st = m_hWnd ? GetState(GetWx()) : nullptr; + if (!st) + return 0; + int old = st->winId; + st->winId = nID; + GetWx()->SetId(ToWxId(nID)); + return old; + }); +} + +CWnd* CWnd::GetParent() const { + return OnMain([&]() -> CWnd* { + if (!m_hWnd) + return nullptr; + wxWindow* w = GetWx(); + WindowState* st = GetState(w); + if (st && st->owner) + return FromHandle(st->owner); + wxWindow* p = LogicalParent(w); + return p ? WrapperFor(p) : nullptr; + }); +} + +CWnd* CWnd::SetParent(CWnd* pWndNewParent) { + return OnMain([&]() -> CWnd* { + CWnd* old = GetParent(); + if (m_hWnd && pWndNewParent && pWndNewParent->m_hWnd) + GetWx()->Reparent(pWndNewParent->GetWx()); + return old; + }); +} + +CWnd* CWnd::GetOwner() const { return GetParent(); } + +void CWnd::SetOwner(CWnd* pOwnerWnd) { + OnMain([&] { + if (m_hWnd) + EnsureState(GetWx()).owner = pOwnerWnd ? pOwnerWnd->m_hWnd : nullptr; + }); +} + +CWnd* CWnd::GetParentOwner() const { return GetParent(); } + +CFrameWnd* CWnd::GetParentFrame() const { + return OnMain([&]() -> CFrameWnd* { + if (!m_hWnd) + return nullptr; + for (wxWindow* w = LogicalParent(GetWx()); w; w = LogicalParent(w)) { + CWnd* p = PermanentWnd(w); + if (p && p->IsKindOf(RUNTIME_CLASS(CFrameWnd))) + return static_cast(p); + } + return nullptr; + }); +} + +CFrameWnd* CWnd::GetTopLevelFrame() const { + return OnMain([&]() -> CFrameWnd* { + CFrameWnd* frame = IsKindOf(RUNTIME_CLASS(CFrameWnd)) ? static_cast(const_cast(this)) + : GetParentFrame(); + while (frame) { + CFrameWnd* parent = frame->GetParentFrame(); + if (!parent) + break; + frame = parent; + } + return frame; + }); +} + +CWnd* CWnd::GetTopLevelParent() const { + return OnMain([&]() -> CWnd* { + if (!m_hWnd) + return nullptr; + wxWindow* w = GetWx(); + while (w && !w->IsTopLevel() && w->GetParent()) + w = w->GetParent(); + return WrapperFor(w); + }); +} + +CWnd* CWnd::GetTopLevelOwner() const { return GetTopLevelParent(); } + +CWnd* CWnd::GetTopWindow() const { return GetWindow(GW_CHILD); } + +CWnd* CWnd::GetWindow(UINT nCmd) const { return FromHandle(::GetWindow(m_hWnd, nCmd)); } + +CWnd* CWnd::GetNextWindow(UINT nFlag) const { return FromHandle(::GetWindow(m_hWnd, nFlag)); } + +CWnd* CWnd::GetLastActivePopup() const { return const_cast(this); } + +CWnd* CWnd::GetDescendantWindow(int nID, BOOL) const { + return OnMain([&]() -> CWnd* { + if (!m_hWnd) + return nullptr; + std::function find = [&](wxWindow* parent) -> wxWindow* { + for (wxWindow* child : parent->GetChildren()) { + WindowState* st = GetState(child); + if (st && st->winId == nID) + return child; + if (wxWindow* r = find(child)) + return r; + } + return nullptr; + }; + wxWindow* w = find(GetWx()); + return w ? WrapperFor(w) : nullptr; + }); +} + +CWnd* CWnd::ChildWindowFromPoint(POINT point) const { return FromHandle(::ChildWindowFromPoint(m_hWnd, point)); } +CWnd* CWnd::WindowFromPoint(POINT point) { return FromHandle(::WindowFromPoint(point)); } +CWnd* CWnd::GetActiveWindow() { return FromHandle(::GetActiveWindow()); } +CWnd* CWnd::SetActiveWindow() { return FromHandle(::SetActiveWindow(m_hWnd)); } +CWnd* CWnd::GetForegroundWindow() { return FromHandle(::GetForegroundWindow()); } +BOOL CWnd::SetForegroundWindow() { return ::SetForegroundWindow(m_hWnd); } +CWnd* CWnd::GetFocus() { return FromHandle(::GetFocus()); } +CWnd* CWnd::SetFocus() { return FromHandle(::SetFocus(m_hWnd)); } +CWnd* CWnd::GetDesktopWindow() { return nullptr; } +CWnd* CWnd::GetCapture() { return FromHandle(::GetCapture()); } +CWnd* CWnd::SetCapture() { return FromHandle(::SetCapture(m_hWnd)); } +CWnd* CWnd::FindWindow(const char* lpszClassName, const char* lpszWindowName) { + return FromHandle(::FindWindow(lpszClassName, lpszWindowName)); +} +BOOL CWnd::IsChild(const CWnd* pWnd) const { return pWnd && ::IsChild(m_hWnd, pWnd->m_hWnd); } +BOOL CWnd::IsWindowEnabled() const { return ::IsWindowEnabled(m_hWnd); } +BOOL CWnd::EnableWindow(BOOL bEnable) { return ::EnableWindow(m_hWnd, bEnable); } +BOOL CWnd::IsWindowVisible() const { return ::IsWindowVisible(m_hWnd); } +BOOL CWnd::ShowWindow(int nCmdShow) { return ::ShowWindow(m_hWnd, nCmdShow); } +BOOL CWnd::IsIconic() const { return ::IsIconic(m_hWnd); } +BOOL CWnd::IsZoomed() const { return ::IsZoomed(m_hWnd); } +void CWnd::DragAcceptFiles(BOOL bAccept) { ::DragAcceptFiles(m_hWnd, bAccept); } + +void CWnd::SetWindowText(const char* lpszString) { ::SetWindowText(m_hWnd, lpszString); } + +int CWnd::GetWindowText(char* lpszStringBuf, int nMaxCount) const { + return ::GetWindowText(m_hWnd, lpszStringBuf, nMaxCount); +} + +void CWnd::GetWindowText(CString& rString) const { + int n = GetWindowTextLength(); + char* buf = rString.GetBuffer(n + 1); + ::GetWindowText(m_hWnd, buf, n + 1); + rString.ReleaseBuffer(); +} + +int CWnd::GetWindowTextLength() const { return ::GetWindowTextLength(m_hWnd); } + +void CWnd::SetFont(CFont* pFont, BOOL bRedraw) { + SendMessage(WM_SETFONT, reinterpret_cast(pFont ? pFont->m_hObject : nullptr), bRedraw); +} + +CFont* CWnd::GetFont() const { + return CFont::FromHandle(reinterpret_cast(const_cast(this)->SendMessage(WM_GETFONT))); +} + +void CWnd::GetWindowRect(LPRECT lpRect) const { ::GetWindowRect(m_hWnd, lpRect); } +void CWnd::GetClientRect(LPRECT lpRect) const { ::GetClientRect(m_hWnd, lpRect); } +void CWnd::ClientToScreen(LPPOINT lpPoint) const { ::ClientToScreen(m_hWnd, lpPoint); } +void CWnd::ClientToScreen(LPRECT lpRect) const { + ::ClientToScreen(m_hWnd, reinterpret_cast(lpRect)); + ::ClientToScreen(m_hWnd, reinterpret_cast(lpRect) + 1); +} +void CWnd::ScreenToClient(LPPOINT lpPoint) const { ::ScreenToClient(m_hWnd, lpPoint); } +void CWnd::ScreenToClient(LPRECT lpRect) const { + ::ScreenToClient(m_hWnd, reinterpret_cast(lpRect)); + ::ScreenToClient(m_hWnd, reinterpret_cast(lpRect) + 1); +} +void CWnd::MapWindowPoints(CWnd* pwndTo, LPPOINT lpPoint, UINT nCount) const { + ::MapWindowPoints(m_hWnd, pwndTo ? pwndTo->m_hWnd : nullptr, lpPoint, nCount); +} +void CWnd::MapWindowPoints(CWnd* pwndTo, LPRECT lpRect) const { + ::MapWindowPoints(m_hWnd, pwndTo ? pwndTo->m_hWnd : nullptr, reinterpret_cast(lpRect), 2); +} +void CWnd::MoveWindow(int x, int y, int nWidth, int nHeight, BOOL bRepaint) { + ::MoveWindow(m_hWnd, x, y, nWidth, nHeight, bRepaint); +} +void CWnd::MoveWindow(LPCRECT lpRect, BOOL bRepaint) { + ::MoveWindow(m_hWnd, lpRect->left, lpRect->top, lpRect->right - lpRect->left, lpRect->bottom - lpRect->top, + bRepaint); +} +BOOL CWnd::SetWindowPos(const CWnd* pWndInsertAfter, int x, int y, int cx, int cy, UINT nFlags) { + HWND after = nullptr; + if (pWndInsertAfter == &wndTopMost) + after = HWND_TOPMOST; + else if (pWndInsertAfter == &wndNoTopMost) + after = HWND_NOTOPMOST; + else if (pWndInsertAfter == &wndBottom) + after = HWND_BOTTOM; + else if (pWndInsertAfter && pWndInsertAfter != &wndTop) + after = pWndInsertAfter->m_hWnd; + return ::SetWindowPos(m_hWnd, after, x, y, cx, cy, nFlags); +} + +BOOL CWnd::GetWindowPlacement(WINDOWPLACEMENT* lpwndpl) const { + return OnMain([&]() -> BOOL { + if (!m_hWnd || !lpwndpl) + return FALSE; + wxWindow* w = GetWx(); + wxRect r = w->GetRect(); + lpwndpl->flags = 0; + lpwndpl->showCmd = SW_SHOWNORMAL; + if (auto* tlw = wxDynamicCast(w, wxTopLevelWindow)) { + if (tlw->IsMaximized()) + lpwndpl->showCmd = SW_SHOWMAXIMIZED; + else if (tlw->IsIconized()) + lpwndpl->showCmd = SW_SHOWMINIMIZED; + } + lpwndpl->ptMinPosition.x = lpwndpl->ptMinPosition.y = -1; + lpwndpl->ptMaxPosition.x = lpwndpl->ptMaxPosition.y = -1; + lpwndpl->rcNormalPosition.left = r.x; + lpwndpl->rcNormalPosition.top = r.y; + lpwndpl->rcNormalPosition.right = r.x + r.width; + lpwndpl->rcNormalPosition.bottom = r.y + r.height; + return TRUE; + }); +} + +BOOL CWnd::SetWindowPlacement(const WINDOWPLACEMENT* lpwndpl) { + return OnMain([&]() -> BOOL { + if (!m_hWnd || !lpwndpl) + return FALSE; + wxWindow* w = GetWx(); + const RECT& r = lpwndpl->rcNormalPosition; + wxRect rect(r.left, r.top, r.right - r.left, r.bottom - r.top); + if (w->IsTopLevel()) { + int display = wxDisplay::GetFromPoint(rect.GetTopLeft()); + if (display == wxNOT_FOUND) + rect.SetPosition(wxPoint(50, 50)); + } + w->SetSize(rect); + if (auto* tlw = wxDynamicCast(w, wxTopLevelWindow)) { + if (lpwndpl->showCmd == SW_SHOWMAXIMIZED || lpwndpl->showCmd == SW_MAXIMIZE) + tlw->Maximize(); + } + return TRUE; + }); +} + +void CWnd::CenterWindow(CWnd* pAlternateOwner) { + OnMain([&] { + if (!m_hWnd) + return; + wxWindow* w = GetWx(); + if (pAlternateOwner && pAlternateOwner->m_hWnd && w->IsTopLevel()) { + wxRect owner = pAlternateOwner->GetWx()->GetScreenRect(); + wxSize sz = w->GetSize(); + w->Move(owner.x + (owner.width - sz.x) / 2, owner.y + (owner.height - sz.y) / 2); + } else { + w->CentreOnParent(); + } + }); +} + +void CWnd::BringWindowToTop() { ::BringWindowToTop(m_hWnd); } + +void CWnd::CalcWindowRect(LPRECT lpClientRect, UINT) { + OnMain([&] { + if (!m_hWnd || !lpClientRect) + return; + wxWindow* w = GetWx(); + wxSize outer = w->GetSize(); + wxSize inner = w->GetClientSize(); + lpClientRect->right += outer.x - inner.x; + lpClientRect->bottom += outer.y - inner.y; + }); +} + +void CWnd::Invalidate(BOOL bErase) { ::InvalidateRect(m_hWnd, nullptr, bErase); } +void CWnd::InvalidateRect(LPCRECT lpRect, BOOL bErase) { ::InvalidateRect(m_hWnd, lpRect, bErase); } +void CWnd::InvalidateRgn(CRgn*, BOOL bErase) { ::InvalidateRect(m_hWnd, nullptr, bErase); } +void CWnd::UpdateWindow() { ::UpdateWindow(m_hWnd); } +BOOL CWnd::RedrawWindow(LPCRECT lpRectUpdate, CRgn*, UINT flags) { + return ::RedrawWindow(m_hWnd, lpRectUpdate, nullptr, flags); +} +void CWnd::SetRedraw(BOOL bRedraw) { SendMessage(WM_SETREDRAW, bRedraw, 0); } +BOOL CWnd::GetUpdateRect(LPRECT lpRect, BOOL) { + return OnMain([&]() -> BOOL { + if (!m_hWnd) + return FALSE; + wxRect r = GetWx()->GetUpdateRegion().GetBox(); + if (lpRect) { + lpRect->left = r.x; + lpRect->top = r.y; + lpRect->right = r.x + r.width; + lpRect->bottom = r.y + r.height; + } + return !r.IsEmpty(); + }); +} +void CWnd::ScrollWindow(int, int, LPCRECT, LPCRECT) { Invalidate(); } +CPoint CWnd::GetCaretPos() { return CPoint(0, 0); } + +// scroll bars of generic windows +int CWnd::GetScrollPos(int nBar) const { + return OnMain([&]() -> int { + if (!m_hWnd) + return 0; + if (nBar == SB_CTL) + return static_cast(const_cast(this)->SendMessage(SBM_GETPOS)); + return GetWx()->GetScrollPos(nBar == SB_VERT ? wxVERTICAL : wxHORIZONTAL); + }); +} + +int CWnd::SetScrollPos(int nBar, int nPos, BOOL bRedraw) { + return OnMain([&]() -> int { + if (!m_hWnd) + return 0; + if (nBar == SB_CTL) + return static_cast(SendMessage(SBM_SETPOS, static_cast(nPos), bRedraw)); + int orient = nBar == SB_VERT ? wxVERTICAL : wxHORIZONTAL; + int old = GetWx()->GetScrollPos(orient); + GetWx()->SetScrollPos(orient, nPos, bRedraw != FALSE); + return old; + }); +} + +void CWnd::GetScrollRange(int nBar, LPINT lpMinPos, LPINT lpMaxPos) const { + OnMain([&] { + if (!m_hWnd) + return; + int orient = nBar == SB_VERT ? wxVERTICAL : wxHORIZONTAL; + if (lpMinPos) + *lpMinPos = 0; + if (lpMaxPos) + *lpMaxPos = std::max(0, GetWx()->GetScrollRange(orient) - 1); + }); +} + +void CWnd::SetScrollRange(int nBar, int nMinPos, int nMaxPos, BOOL bRedraw) { + OnMain([&] { + if (!m_hWnd || nBar == SB_CTL) + return; + int orient = nBar == SB_VERT ? wxVERTICAL : wxHORIZONTAL; + int range = std::max(0, nMaxPos - nMinPos + 1); + GetWx()->SetScrollbar(orient, std::min(GetWx()->GetScrollPos(orient), range), 1, + nMaxPos > nMinPos ? range : 0, bRedraw != FALSE); + }); +} + +BOOL CWnd::SetScrollInfo(int nBar, LPSCROLLINFO lpScrollInfo, BOOL bRedraw) { + return OnMain([&]() -> BOOL { + if (!m_hWnd || !lpScrollInfo) + return FALSE; + int orient = nBar == SB_VERT ? wxVERTICAL : wxHORIZONTAL; + wxWindow* w = GetWx(); + int pos = w->GetScrollPos(orient); + int thumb = w->GetScrollThumb(orient); + int range = w->GetScrollRange(orient); + if (lpScrollInfo->fMask & SIF_RANGE) + range = lpScrollInfo->nMax - lpScrollInfo->nMin + 1; + if (lpScrollInfo->fMask & SIF_PAGE) + thumb = static_cast(lpScrollInfo->nPage); + if (lpScrollInfo->fMask & SIF_POS) + pos = lpScrollInfo->nPos - ((lpScrollInfo->fMask & SIF_RANGE) ? lpScrollInfo->nMin : 0); + if (thumb >= range && !(lpScrollInfo->fMask & SIF_DISABLENOSCROLL)) + range = 0; + w->SetScrollbar(orient, std::max(0, pos), std::max(1, thumb), std::max(0, range), bRedraw != FALSE); + return TRUE; + }); +} + +BOOL CWnd::GetScrollInfo(int nBar, LPSCROLLINFO lpScrollInfo, UINT nMask) { + return OnMain([&]() -> BOOL { + if (!m_hWnd || !lpScrollInfo) + return FALSE; + int orient = nBar == SB_VERT ? wxVERTICAL : wxHORIZONTAL; + wxWindow* w = GetWx(); + lpScrollInfo->fMask = nMask; + lpScrollInfo->nMin = 0; + lpScrollInfo->nMax = std::max(0, w->GetScrollRange(orient) - 1); + lpScrollInfo->nPage = static_cast(w->GetScrollThumb(orient)); + lpScrollInfo->nPos = w->GetScrollPos(orient); + lpScrollInfo->nTrackPos = lpScrollInfo->nPos; + return TRUE; + }); +} + +int CWnd::GetScrollLimit(int nBar) { + SCROLLINFO si; + GetScrollInfo(nBar, &si, SIF_ALL); + return std::max(0, si.nMax - static_cast(si.nPage) + 1); +} + +void CWnd::ShowScrollBar(UINT nBar, BOOL bShow) { + OnMain([&] { + if (!m_hWnd) + return; + wxWindow* w = GetWx(); + if (nBar == SB_HORZ || nBar == SB_BOTH) + w->ShowScrollbars(bShow ? wxSHOW_SB_ALWAYS : wxSHOW_SB_NEVER, w->HasScrollbar(wxVERTICAL) ? wxSHOW_SB_DEFAULT : wxSHOW_SB_NEVER); + if (nBar == SB_VERT || nBar == SB_BOTH) + w->ShowScrollbars(w->HasScrollbar(wxHORIZONTAL) ? wxSHOW_SB_DEFAULT : wxSHOW_SB_NEVER, bShow ? wxSHOW_SB_ALWAYS : wxSHOW_SB_NEVER); + }); +} + +BOOL CWnd::EnableScrollBar(int, UINT) { return TRUE; } +CScrollBar* CWnd::GetScrollBarCtrl(int) const { return nullptr; } + +UINT_PTR CWnd::SetTimer(UINT_PTR nIDEvent, UINT nElapse, void (*lpfnTimer)(HWND, UINT, UINT_PTR, DWORD)) { + return ::SetTimer(m_hWnd, nIDEvent, nElapse, lpfnTimer); +} + +BOOL CWnd::KillTimer(UINT_PTR nIDEvent) { return ::KillTimer(m_hWnd, nIDEvent); } + +CWnd* CWnd::GetDlgItem(int nID) const { return FromHandle(::GetDlgItem(m_hWnd, nID)); } +void CWnd::GetDlgItem(int nID, HWND* phWnd) const { + if (phWnd) + *phWnd = ::GetDlgItem(m_hWnd, nID); +} +UINT CWnd::GetDlgItemInt(int nID, BOOL* lpTrans, BOOL bSigned) const { + return ::GetDlgItemInt(m_hWnd, nID, lpTrans, bSigned); +} +void CWnd::SetDlgItemInt(int nID, UINT nValue, BOOL bSigned) { ::SetDlgItemInt(m_hWnd, nID, nValue, bSigned); } +int CWnd::GetDlgItemText(int nID, char* lpStr, int nMaxCount) const { + return static_cast(::GetDlgItemText(m_hWnd, nID, lpStr, nMaxCount)); +} +int CWnd::GetDlgItemText(int nID, CString& rString) const { + CWnd* item = GetDlgItem(nID); + if (!item) { + rString.Empty(); + return 0; + } + item->GetWindowText(rString); + return rString.GetLength(); +} +void CWnd::SetDlgItemText(int nID, const char* lpszString) { ::SetDlgItemText(m_hWnd, nID, lpszString); } +UINT CWnd::IsDlgButtonChecked(int nIDButton) const { return ::IsDlgButtonChecked(m_hWnd, nIDButton); } +void CWnd::CheckDlgButton(int nIDButton, UINT nCheck) { ::CheckDlgButton(m_hWnd, nIDButton, nCheck); } +void CWnd::CheckRadioButton(int nIDFirstButton, int nIDLastButton, int nIDCheckButton) { + ::CheckRadioButton(m_hWnd, nIDFirstButton, nIDLastButton, nIDCheckButton); +} +int CWnd::GetCheckedRadioButton(int nIDFirstButton, int nIDLastButton) { + for (int id = nIDFirstButton; id <= nIDLastButton; ++id) + if (IsDlgButtonChecked(id)) + return id; + return 0; +} +LRESULT CWnd::SendDlgItemMessage(int nID, UINT message, WPARAM wParam, LPARAM lParam) { + return ::SendDlgItemMessage(m_hWnd, nID, message, wParam, lParam); +} +CWnd* CWnd::GetNextDlgGroupItem(CWnd* pWndCtl, BOOL bPrevious) const { + return FromHandle(::GetNextDlgGroupItem(m_hWnd, pWndCtl ? pWndCtl->m_hWnd : nullptr, bPrevious)); +} +CWnd* CWnd::GetNextDlgTabItem(CWnd* pWndCtl, BOOL bPrevious) const { + return FromHandle(::GetNextDlgTabItem(m_hWnd, pWndCtl ? pWndCtl->m_hWnd : nullptr, bPrevious)); +} + +BOOL CWnd::UpdateData(BOOL bSaveAndValidate) { + return OnMain([&]() -> BOOL { + CDataExchange dx(this, bSaveAndValidate); + try { + DoDataExchange(&dx); + } catch (CUserException* e) { + e->Delete(); + return FALSE; + } + return TRUE; + }); +} + +void CWnd::DoDataExchange(CDataExchange*) {} + +BOOL CWnd::ExecuteDlgInit(UINT nIDTemplate) { return ApplyDlgInit(this, static_cast(nIDTemplate)); } +BOOL CWnd::ExecuteDlgInit(const char* lpszResourceName) { + ResRef ref = ResRef::From(lpszResourceName); + return ApplyDlgInit(this, ref.id); +} + +CMenu* CWnd::GetMenu() const { return CMenu::FromHandle(::GetMenu(m_hWnd)); } +BOOL CWnd::SetMenu(CMenu* pMenu) { return ::SetMenu(m_hWnd, pMenu ? pMenu->m_hMenu : nullptr); } +void CWnd::DrawMenuBar() { ::DrawMenuBar(m_hWnd); } +CMenu* CWnd::GetSystemMenu(BOOL bRevert) const { return CMenu::FromHandle(::GetSystemMenu(m_hWnd, bRevert)); } + +void CWnd::WinHelp(DWORD_PTR dwData, UINT nCmd) { + CWinApp* app = AfxGetApp(); + if (app) + app->WinHelp(dwData, nCmd); +} + +void CWnd::HtmlHelp(DWORD_PTR dwData, UINT nCmd) { + CWinApp* app = AfxGetApp(); + if (app) + app->HtmlHelp(dwData, nCmd); +} + +HICON CWnd::SetIcon(HICON hIcon, BOOL bBigIcon) { + return reinterpret_cast(SendMessage(WM_SETICON, bBigIcon, reinterpret_cast(hIcon))); +} + +HICON CWnd::GetIcon(BOOL bBigIcon) const { + return reinterpret_cast(const_cast(this)->SendMessage(WM_GETICON, bBigIcon, 0)); +} + +BOOL CWnd::EnableToolTips(BOOL) { return TRUE; } + +int CWnd::MessageBox(const char* lpszText, const char* lpszCaption, UINT nType) { + return ::MessageBox(m_hWnd, lpszText, lpszCaption, nType); +} + +BOOL CWnd::OpenClipboard() { return ::OpenClipboard(m_hWnd); } + +BOOL CWnd::LockWindowUpdate() { + return OnMain([&]() -> BOOL { + if (m_hWnd) + GetWx()->Freeze(); + return TRUE; + }); +} + +void CWnd::UnlockWindowUpdate() { + OnMain([&] { + if (m_hWnd && GetWx()->IsFrozen()) + GetWx()->Thaw(); + }); +} + +LONG_PTR CWnd::GetWindowLongPtr(int nIndex) const { return ::GetWindowLongPtr(m_hWnd, nIndex); } +LONG_PTR CWnd::SetWindowLongPtr(int nIndex, LONG_PTR dwNewLong) { return ::SetWindowLongPtr(m_hWnd, nIndex, dwNewLong); } diff --git a/port/rc/rc2cpp.cmake b/port/rc/rc2cpp.cmake new file mode 100644 index 000000000..3ce2487da --- /dev/null +++ b/port/rc/rc2cpp.cmake @@ -0,0 +1,126 @@ +include_guard(GLOBAL) + +find_package(Python3 COMPONENTS Interpreter REQUIRED) + +set(MFCWX_RC2CPP_SCRIPT "${CMAKE_CURRENT_LIST_DIR}/rc2cpp.py") +get_filename_component(MFCWX_RC_INCLUDE_DIR "${CMAKE_CURRENT_LIST_DIR}/../mfcwx/include" ABSOLUTE) + +# mfcwx_add_rc( RC [MODULE ] [HEADERS ...] [OUTPUT_DIR ] [INCLUDE_DIRS ...] [DEFINES ...] [CODEPAGE ]) +function(mfcwx_add_rc target) + cmake_parse_arguments(PARSE_ARGV 1 ARG "" "RC;MODULE;OUTPUT_DIR;CODEPAGE" "HEADERS;INCLUDE_DIRS;DEFINES") + if(ARG_UNPARSED_ARGUMENTS) + message(FATAL_ERROR "mfcwx_add_rc: unexpected arguments: ${ARG_UNPARSED_ARGUMENTS}") + endif() + if(NOT ARG_RC) + message(FATAL_ERROR "mfcwx_add_rc: RC is required") + endif() + + get_filename_component(rc "${ARG_RC}" ABSOLUTE BASE_DIR "${CMAKE_CURRENT_SOURCE_DIR}") + get_filename_component(rc_dir "${rc}" DIRECTORY) + get_filename_component(rc_name "${rc}" NAME) + if(NOT ARG_MODULE) + get_filename_component(ARG_MODULE "${rc}" NAME_WE) + endif() + string(MAKE_C_IDENTIFIER "${ARG_MODULE}" module) + if(NOT ARG_OUTPUT_DIR) + set(ARG_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/rc2cpp/${module}") + endif() + get_filename_component(out_dir "${ARG_OUTPUT_DIR}" ABSOLUTE BASE_DIR "${CMAKE_CURRENT_BINARY_DIR}") + + set(headers) + foreach(h IN LISTS ARG_HEADERS) + get_filename_component(h "${h}" ABSOLUTE BASE_DIR "${CMAKE_CURRENT_SOURCE_DIR}") + list(APPEND headers "${h}") + endforeach() + + set(args "${rc}" --module "${module}" --out-dir "${out_dir}" -I "${MFCWX_RC_INCLUDE_DIR}") + foreach(d IN LISTS ARG_INCLUDE_DIRS) + get_filename_component(d "${d}" ABSOLUTE BASE_DIR "${CMAKE_CURRENT_SOURCE_DIR}") + list(APPEND args -I "${d}") + endforeach() + foreach(d IN LISTS ARG_DEFINES) + list(APPEND args -D "${d}") + endforeach() + if(ARG_CODEPAGE) + list(APPEND args --codepage "${ARG_CODEPAGE}") + endif() + + # Output names depend on the language set; the build rewrites outputs_file when it changes, which re-runs CMake. + set(outputs_file "${out_dir}/${module}_outputs.txt") + execute_process( + COMMAND "${Python3_EXECUTABLE}" "${MFCWX_RC2CPP_SCRIPT}" ${args} --list-outputs --outputs-file "${outputs_file}" + OUTPUT_VARIABLE listed + ERROR_VARIABLE errors + RESULT_VARIABLE result) + if(NOT result EQUAL 0) + message(FATAL_ERROR "mfcwx_add_rc: rc2cpp.py failed for ${rc}:\n${errors}") + endif() + string(STRIP "${listed}" listed) + string(REPLACE "\n" ";" listed "${listed}") + set(sources ${listed}) + list(FILTER sources INCLUDE REGEX "\\.cpp$") + set(files_list "${out_dir}/${module}_files.txt") + + set(depfile_args) + set(depfile_cli) + if(CMAKE_GENERATOR MATCHES "Ninja") + set(depfile "${out_dir}/${module}.d") + set(depfile_args DEPFILE "${depfile}") + set(depfile_cli --depfile "${depfile}") + endif() + + add_custom_command( + OUTPUT ${sources} "${files_list}" + COMMAND "${Python3_EXECUTABLE}" "${MFCWX_RC2CPP_SCRIPT}" ${args} --quiet --outputs-file "${outputs_file}" ${depfile_cli} + DEPENDS + "${rc}" ${headers} "${MFCWX_RC2CPP_SCRIPT}" + "${MFCWX_RC_INCLUDE_DIR}/mfcwx/winconst.h" "${MFCWX_RC_INCLUDE_DIR}/afxres.h" + ${depfile_args} + COMMENT "rc2cpp: generating resource tables for ${module} from ${rc_name}" + VERBATIM) + + target_sources(${target} PRIVATE ${sources}) + set_source_files_properties(${sources} PROPERTIES GENERATED TRUE) + set_property(SOURCE ${sources} APPEND PROPERTY INCLUDE_DIRECTORIES "${MFCWX_RC_INCLUDE_DIR}") + set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${outputs_file}") + set_property(TARGET ${target} APPEND PROPERTY MFCWX_RC_FILE_LISTS "${files_list}") + set_property(TARGET ${target} APPEND PROPERTY MFCWX_RC_BASE_DIRS "${rc_dir}") + + set(MFCWX_RC_${module}_SOURCES "${sources}" PARENT_SCOPE) + set(MFCWX_RC_${module}_FILES_LIST "${files_list}" PARENT_SCOPE) +endfunction() + +# mfcwx_install_rc_files( DESTINATION [COMPONENT ]): installs the files referenced by the target's .rc files. +function(mfcwx_install_rc_files target) + cmake_parse_arguments(PARSE_ARGV 1 ARG "" "DESTINATION;COMPONENT" "") + if(NOT ARG_DESTINATION) + message(FATAL_ERROR "mfcwx_install_rc_files: DESTINATION is required") + endif() + get_property(lists TARGET ${target} PROPERTY MFCWX_RC_FILE_LISTS) + get_property(dirs TARGET ${target} PROPERTY MFCWX_RC_BASE_DIRS) + if(IS_ABSOLUTE "${ARG_DESTINATION}") + set(dest "${ARG_DESTINATION}") + else() + set(dest "\${CMAKE_INSTALL_PREFIX}/${ARG_DESTINATION}") + endif() + set(component) + if(ARG_COMPONENT) + set(component COMPONENT "${ARG_COMPONENT}") + endif() + list(LENGTH lists count) + if(count EQUAL 0) + return() + endif() + math(EXPR last "${count} - 1") + foreach(i RANGE ${last}) + list(GET lists ${i} list_file) + list(GET dirs ${i} base_dir) + install(CODE " + file(STRINGS \"${list_file}\" _mfcwx_rc_files) + foreach(_mfcwx_rc_file IN LISTS _mfcwx_rc_files) + get_filename_component(_mfcwx_rc_dir \"\${_mfcwx_rc_file}\" DIRECTORY) + file(INSTALL DESTINATION \"${dest}/\${_mfcwx_rc_dir}\" TYPE FILE FILES \"${base_dir}/\${_mfcwx_rc_file}\") + endforeach()" + ${component}) + endforeach() +endfunction() diff --git a/port/rc/rc2cpp.py b/port/rc/rc2cpp.py new file mode 100755 index 000000000..eb3adf3d6 --- /dev/null +++ b/port/rc/rc2cpp.py @@ -0,0 +1,2008 @@ +#!/usr/bin/env python3 +"""Convert a Windows resource script (.rc) into C++ tables for mfcwx::rc (see mfcwx/rcdata.h).""" + +import argparse +import codecs +import os +import re +import sys + +WS_POPUP = 0x80000000 +WS_CHILD = 0x40000000 +WS_VISIBLE = 0x10000000 +WS_CAPTION = 0x00C00000 +WS_BORDER = 0x00800000 +WS_SYSMENU = 0x00080000 +WS_GROUP = 0x00020000 +WS_TABSTOP = 0x00010000 +DS_SETFONT = 0x40 +SS_LEFT, SS_CENTER, SS_RIGHT, SS_ICON = 0, 1, 2, 3 +(BS_PUSHBUTTON, BS_DEFPUSHBUTTON, BS_CHECKBOX, BS_AUTOCHECKBOX, BS_RADIOBUTTON, BS_3STATE, + BS_AUTO3STATE, BS_GROUPBOX, BS_USERBUTTON, BS_AUTORADIOBUTTON, BS_PUSHBOX) = range(11) +ES_LEFT = 0 +CBS_SIMPLE = 0x1 +CBS_TYPEMASK = 0x3 +LBS_NOTIFY = 0x1 +SBS_HORZ = 0 + +MF_GRAYED = 0x1 +MF_DISABLED = 0x2 +MF_CHECKED = 0x8 +MF_POPUP = 0x10 +MF_MENUBARBREAK = 0x20 +MF_MENUBREAK = 0x40 +MF_SEPARATOR = 0x800 +MF_HELP = 0x4000 + +FVIRTKEY = 0x01 +FNOINVERT = 0x02 +FSHIFT = 0x04 +FCONTROL = 0x08 +FALT = 0x10 + +CHILD = WS_CHILD | WS_VISIBLE + +TEXT_CONTROLS = { + 'LTEXT': ('Static', SS_LEFT | WS_GROUP), + 'RTEXT': ('Static', SS_RIGHT | WS_GROUP), + 'CTEXT': ('Static', SS_CENTER | WS_GROUP), + 'PUSHBUTTON': ('Button', BS_PUSHBUTTON | WS_TABSTOP), + 'DEFPUSHBUTTON': ('Button', BS_DEFPUSHBUTTON | WS_TABSTOP), + 'PUSHBOX': ('Button', BS_PUSHBOX | WS_TABSTOP), + 'USERBUTTON': ('Button', BS_USERBUTTON | WS_TABSTOP), + 'CHECKBOX': ('Button', BS_CHECKBOX | WS_TABSTOP), + 'AUTOCHECKBOX': ('Button', BS_AUTOCHECKBOX | WS_TABSTOP), + 'STATE3': ('Button', BS_3STATE | WS_TABSTOP), + 'AUTO3STATE': ('Button', BS_AUTO3STATE | WS_TABSTOP), + 'RADIOBUTTON': ('Button', BS_RADIOBUTTON), + 'AUTORADIOBUTTON': ('Button', BS_AUTORADIOBUTTON), + 'GROUPBOX': ('Button', BS_GROUPBOX), +} + +PLAIN_CONTROLS = { + 'EDITTEXT': ('Edit', ES_LEFT | WS_BORDER | WS_TABSTOP), + 'COMBOBOX': ('ComboBox', WS_TABSTOP), + 'LISTBOX': ('ListBox', LBS_NOTIFY | WS_BORDER), + 'SCROLLBAR': ('ScrollBar', SBS_HORZ), +} + +CLASS_NAMES = {'BUTTON': 'Button', 'EDIT': 'Edit', 'STATIC': 'Static', 'LISTBOX': 'ListBox', + 'SCROLLBAR': 'ScrollBar', 'COMBOBOX': 'ComboBox'} +CLASS_ORDINALS = {0x80: 'Button', 0x81: 'Edit', 0x82: 'Static', 0x83: 'ListBox', 0x84: 'ScrollBar', + 0x85: 'ComboBox'} + +MENU_OPTIONS = {'GRAYED': MF_GRAYED, 'INACTIVE': MF_DISABLED, 'CHECKED': MF_CHECKED, + 'MENUBARBREAK': MF_MENUBARBREAK, 'MENUBREAK': MF_MENUBREAK, 'HELP': MF_HELP} +ACCEL_OPTIONS = {'VIRTKEY': FVIRTKEY, 'ASCII': 0, 'NOINVERT': FNOINVERT, 'SHIFT': FSHIFT, + 'CONTROL': FCONTROL, 'ALT': FALT} + +MEMORY_OPTIONS = {'DISCARDABLE', 'MOVEABLE', 'FIXED', 'PURE', 'IMPURE', 'PRELOAD', 'LOADONCALL', + 'SHARED', 'NONSHARED'} +FILE_TYPES = {'BITMAP', 'ICON', 'CURSOR', 'FONT', 'FONTDIR', 'HTML', 'MESSAGETABLE', 'ANICURSOR', + 'ANIICON', 'MANIFEST', 'PLUGPLAY', 'VXD', 'RCDATA', 'DLGINCLUDE'} +SKIP_TYPES = {'TEXTINCLUDE', 'DESIGNINFO', 'AFX_DIALOG_LAYOUT'} +TOP_TYPES = {'DIALOG', 'DIALOGEX', 'MENU', 'MENUEX', 'ACCELERATORS', 'TOOLBAR', 'DLGINIT', + 'VERSIONINFO'} | FILE_TYPES | SKIP_TYPES +RT_NAMES = {1: 'CURSOR', 2: 'BITMAP', 3: 'ICON', 4: 'MENU', 5: 'DIALOG', 6: 'STRING', 7: 'FONTDIR', + 8: 'FONT', 9: 'ACCELERATOR', 10: 'RCDATA', 11: 'MESSAGETABLE', 12: 'GROUP_CURSOR', + 14: 'GROUP_ICON', 16: 'VERSION', 17: 'DLGINCLUDE', 19: 'PLUGPLAY', 20: 'VXD', + 21: 'ANICURSOR', 22: 'ANIICON', 23: 'HTML', 24: 'MANIFEST', 240: 'DLGINIT', 241: 'TOOLBAR'} + +DLGINIT_TEXT_MESSAGES = {0x0401, 0x0403, 0x1234} + +BUILTIN_SYMBOLS = { + 'LANG_NEUTRAL': 0x00, 'LANG_CHINESE': 0x04, 'LANG_CZECH': 0x05, 'LANG_DANISH': 0x06, + 'LANG_GERMAN': 0x07, 'LANG_GREEK': 0x08, 'LANG_ENGLISH': 0x09, 'LANG_SPANISH': 0x0a, + 'LANG_FINNISH': 0x0b, 'LANG_FRENCH': 0x0c, 'LANG_HUNGARIAN': 0x0e, 'LANG_ITALIAN': 0x10, + 'LANG_JAPANESE': 0x11, 'LANG_KOREAN': 0x12, 'LANG_DUTCH': 0x13, 'LANG_NORWEGIAN': 0x14, + 'LANG_POLISH': 0x15, 'LANG_PORTUGUESE': 0x16, 'LANG_RUSSIAN': 0x19, 'LANG_CROATIAN': 0x1a, + 'LANG_SERBIAN': 0x1a, 'LANG_SWEDISH': 0x1d, 'LANG_TURKISH': 0x1f, + 'SUBLANG_NEUTRAL': 0x00, 'SUBLANG_DEFAULT': 0x01, 'SUBLANG_SYS_DEFAULT': 0x02, + 'SUBLANG_GERMAN': 0x01, 'SUBLANG_GERMAN_SWISS': 0x02, 'SUBLANG_GERMAN_AUSTRIAN': 0x03, + 'SUBLANG_ENGLISH_US': 0x01, 'SUBLANG_ENGLISH_UK': 0x02, 'SUBLANG_FRENCH': 0x01, + 'SUBLANG_FRENCH_SWISS': 0x04, 'SUBLANG_SPANISH': 0x01, 'SUBLANG_SPANISH_MODERN': 0x03, + 'SUBLANG_SERBIAN_LATIN': 0x02, 'SUBLANG_SERBIAN_CYRILLIC': 0x03, 'SUBLANG_POLISH_POLAND': 0x01, + 'SUBLANG_GREEK_GREECE': 0x01, 'SUBLANG_RUSSIAN_RUSSIA': 0x01, + 'VS_VERSION_INFO': 1, +} + +LANG_CODE_BY_ID = {(0x07, 0x02): 'de-CH', (0x1a, 0x01): 'hr'} +LANG_CODE_BY_PRIMARY = { + 0x04: 'zh', 0x05: 'cs', 0x06: 'da', 0x07: 'de', 0x08: 'el', 0x09: 'en', 0x0a: 'es', 0x0b: 'fi', + 0x0c: 'fr', 0x0e: 'hu', 0x10: 'it', 0x11: 'ja', 0x12: 'ko', 0x13: 'nl', 0x14: 'no', 0x15: 'pl', + 0x16: 'pt', 0x19: 'ru', 0x1a: 'rs', 0x1d: 'sv', 0x1f: 'tr', +} + +HEADER_ALIASES = {name: 'mfcwx/winconst.h' for name in ( + 'windows.h', 'winres.h', 'winresrc.h', 'winuser.h', 'winver.h', 'commctrl.h', 'winnt.h', + 'windef.h', 'richedit.h', 'dlgs.h', 'winuser.rh', 'commctrl.rh', 'winnt.rh')} +RC_EXTENSIONS = {'.rc', '.rc2', '.rc3', '.dlg'} + +CAST_TYPES = { + 'BYTE': (8, False), 'UCHAR': (8, False), 'unsigned char': (8, False), 'char': (8, True), + 'WORD': (16, False), 'USHORT': (16, False), 'WCHAR': (16, False), 'ATOM': (16, False), + 'unsigned short': (16, False), 'SHORT': (16, True), 'short': (16, True), + 'DWORD': (32, False), 'UINT': (32, False), 'ULONG': (32, False), 'unsigned': (32, False), + 'unsigned int': (32, False), 'unsigned long': (32, False), 'COLORREF': (32, False), + 'LCID': (32, False), 'int': (32, True), 'INT': (32, True), 'LONG': (32, True), + 'long': (32, True), 'BOOL': (32, True), +} + +PREC = {'||': 1, '&&': 2, '|': 3, '^': 4, '&': 5, '==': 6, '!=': 6, '<': 7, '>': 7, '<=': 7, + '>=': 7, '<<': 8, '>>': 8, '+': 9, '-': 9, '*': 10, '/': 10, '%': 10} + + +class Tok: + __slots__ = ('k', 'v', 's', 'x', 'cp', 'ln', 'fi') + + def __init__(self, k, v, s, x, cp, ln, fi): + self.k = k + self.v = v + self.s = s + self.x = x + self.cp = cp + self.ln = ln + self.fi = fi + + +EOF_TOK = Tok('eof', None, '', None, 0, 0, 0) + +LEX_RE = re.compile(r''' + (?P[ \t\f\v\x00\x1a]+) + | (?P//.*) + | (?P/\*) + | (?PL?"(?:[^"\\]|\\.|"")*") + | (?PL?"(?:[^"\\]|\\.|"")*) + | (?P0[xX][0-9A-Fa-f]*[uUlL]*|[0-9]+[uUlL]*) + | (?P[A-Za-z_][A-Za-z0-9_]*) + | (?P<<|>>|<=|>=|==|!=|&&|\|\||.) +''', re.X) + + +def lex(text, out, cp, ln, fi, unterminated=None): + pos = 0 + n = len(text) + match = LEX_RE.match + append = out.append + while pos < n: + m = match(text, pos) + g = m.lastgroup + pos = m.end() + if g == 'ws': + continue + if g == 'lc': + break + if g == 'bc': + e = text.find('*/', pos) + if e < 0: + return True + pos = e + 2 + continue + s = m.group() + if g == 'i': + append(Tok('id', s, s, None, cp, ln, fi)) + elif g == 'n': + body = s.rstrip('uUlL') + suffix = s[len(body):].lower() + if body[:2] in ('0x', '0X'): + v = int(body[2:], 16) if len(body) > 2 else 0 + else: + v = int(body) + append(Tok('num', v, s, 'l' in suffix, cp, ln, fi)) + elif g == 's': + wide = s[0] == 'L' + append(Tok('str', s[2 if wide else 1:-1], s, wide, cp, ln, fi)) + elif g == 'su': + wide = s[0] == 'L' + if unterminated is not None: + unterminated(ln, fi) + append(Tok('str', s[2 if wide else 1:], s, wide, cp, ln, fi)) + else: + append(Tok('op', s, s, None, cp, ln, fi)) + return False + + +def describe(t): + if t.k == 'eof': + return 'end of input' + if t.k == 'str': + return 'string' + return "'%s'" % t.s + + +class Diag: + def __init__(self, quiet): + self.quiet = quiet + self.errors = 0 + self.warnings = 0 + + def error(self, loc, msg): + self.errors += 1 + print('%s: error: %s' % (loc, msg), file=sys.stderr) + + def warn(self, loc, msg): + self.warnings += 1 + if not self.quiet: + print('%s: warning: %s' % (loc, msg), file=sys.stderr) + + def note(self, msg): + if not self.quiet: + print('rc2cpp: %s' % msg, file=sys.stderr) + + +class EvalError(Exception): + pass + + +class ParseError(Exception): + def __init__(self, tok, msg): + Exception.__init__(self, msg) + self.tok = tok + self.msg = msg + + +class Cursor: + __slots__ = ('t', 'i', 'n', 'eof') + + def __init__(self, toks, eof=None): + self.t = toks + self.i = 0 + self.n = len(toks) + self.eof = eof or EOF_TOK + + def peek(self, k=0): + j = self.i + k + return self.t[j] if j < self.n else self.eof + + def next(self): + i = self.i + if i < self.n: + self.i = i + 1 + return self.t[i] + return self.eof + + def at_eof(self): + return self.i >= self.n + + +def apply_cast(name, v): + spec = CAST_TYPES.get(name) + if spec is None: + return v + bits, signed = spec + v &= (1 << bits) - 1 + if signed and v >> (bits - 1): + v -= 1 << bits + return v + + +def binop(o, a, b): + if o == '|': + return a | b + if o == '&': + return a & b + if o == '+': + return a + b + if o == '-': + return a - b + if o == '^': + return a ^ b + if o == '<<': + return a << (b & 63) + if o == '>>': + return a >> (b & 63) + if o == '*': + return a * b + if o in ('/', '%'): + if b == 0: + raise EvalError('division by zero') + q = abs(a) // abs(b) + if (a < 0) != (b < 0): + q = -q + return q if o == '/' else a - q * b + if o == '==': + return int(a == b) + if o == '!=': + return int(a != b) + if o == '<': + return int(a < b) + if o == '>': + return int(a > b) + if o == '<=': + return int(a <= b) + if o == '>=': + return int(a >= b) + if o == '&&': + return int(bool(a) and bool(b)) + if o == '||': + return int(bool(a) or bool(b)) + raise EvalError("unsupported operator '%s'" % o) + + +class Evaluator: + def __init__(self, macros): + self.macros = macros + self.expanding = set() + self.cache = None + self.saw_long = False + self.undefined = {} + self.undefined_hits = 0 + self.builtins_used = set() + + def freeze(self): + self.cache = {} + + def eval_pp(self, toks): + cur = Cursor(toks) + v = self.expr(cur, 1, True) + if not cur.at_eof(): + raise EvalError('unexpected %s in #if expression' % describe(cur.peek())) + return v + + def expr(self, cur, min_prec=1, pp=False): + v = self.unary(cur, pp) + while True: + t = cur.peek() + if t.k != 'op': + return v + p = PREC.get(t.v) + if p is None or p < min_prec: + return v + cur.next() + v = binop(t.v, v, self.expr(cur, p + 1, pp)) + + def expect_close(self, cur): + t = cur.next() + if t.k != 'op' or t.v != ')': + raise EvalError("expected ')' but found %s" % describe(t)) + + def unary(self, cur, pp=False): + t = cur.next() + k = t.k + if k == 'num': + if t.x: + self.saw_long = True + return t.v + if k == 'id': + if pp and t.v == 'defined': + nt = cur.peek() + paren = nt.k == 'op' and nt.v == '(' + if paren: + cur.next() + n = cur.next() + if n.k != 'id': + raise EvalError("expected identifier after 'defined'") + if paren: + self.expect_close(cur) + return 1 if n.v in self.macros else 0 + if not pp and t.v.upper() == 'NOT' and t.v not in self.macros: + return ~self.unary(cur, pp) + return self.ident(t, cur, pp) + if k == 'op': + o = t.v + if o == '(': + cast = self.try_cast(cur) + if cast is not None: + return apply_cast(cast, self.unary(cur, pp)) + v = self.expr(cur, 1, pp) + self.expect_close(cur) + return v + if o == '-': + return -self.unary(cur, pp) + if o == '+': + return self.unary(cur, pp) + if o == '~': + return ~self.unary(cur, pp) + if o == '!': + return 0 if self.unary(cur, pp) else 1 + if k == 'eof': + raise EvalError('unexpected end of expression') + raise EvalError('unexpected %s in expression' % describe(t)) + + def is_object_macro(self, name): + m = self.macros.get(name) + return m is not None and m[0] is None + + def is_symbol(self, name): + return self.is_object_macro(name) or name in BUILTIN_SYMBOLS + + def try_cast(self, cur): + j = 0 + words = [] + while True: + t = cur.peek(j) + if t.k == 'id' and not self.is_symbol(t.v): + words.append(t.v) + elif t.k == 'op' and t.v == '*' and words: + words.append('*') + else: + break + j += 1 + if not words: + return None + t = cur.peek(j) + if t.k != 'op' or t.v != ')': + return None + a = cur.peek(j + 1) + if not (a.k in ('num', 'id') or (a.k == 'op' and a.v in ('(', '-', '~', '+', '!'))): + return None + for _ in range(j + 1): + cur.next() + return ' '.join(words) + + def collect_args(self, cur): + cur.next() + args = [[]] + depth = 1 + while True: + t = cur.next() + if t.k == 'eof': + raise EvalError('unterminated macro argument list') + if t.k == 'op': + if t.v == '(': + depth += 1 + elif t.v == ')': + depth -= 1 + if depth == 0: + break + elif t.v == ',' and depth == 1: + args.append([]) + continue + args[-1].append(t) + return args + + def ident(self, t, cur, pp): + name = t.v + m = self.macros.get(name) + if m is None: + if pp: + return 0 + b = BUILTIN_SYMBOLS.get(name) + if b is not None: + self.builtins_used.add(name) + return b + self.undefined_hits += 1 + rec = self.undefined.get(name) + if rec is None: + self.undefined[name] = [1, t] + else: + rec[0] += 1 + return 0 + params, body = m + if params is not None: + nt = cur.peek() + if nt.k != 'op' or nt.v != '(': + if not pp: + self.undefined_hits += 1 + self.undefined.setdefault(name, [0, t])[0] += 1 + return 0 + args = self.collect_args(cur) + index = {p: i for i, p in enumerate(params)} + expanded = [] + for bt in body: + if bt.k == 'id' and bt.v in index and index[bt.v] < len(args): + expanded.extend(args[index[bt.v]]) + else: + expanded.append(bt) + body = expanded + elif self.cache is not None and not pp: + c = self.cache.get(name) + if c is not None: + if c[1]: + self.saw_long = True + return c[0] + if name in self.expanding: + raise EvalError("recursive macro '%s'" % name) + self.expanding.add(name) + saved = self.saw_long + self.saw_long = False + try: + if body: + sub = Cursor(body) + v = self.expr(sub, 1, pp) + if not sub.at_eof(): + raise EvalError("macro '%s' is not a constant expression" % name) + else: + v = 0 + finally: + self.expanding.discard(name) + long_seen = self.saw_long + self.saw_long = saved or long_seen + if params is None and self.cache is not None and not pp: + self.cache[name] = (v, long_seen) + return v + + +def codec_for(cp): + if cp == 65001: + return 'utf-8' + name = 'cp%d' % cp + try: + codecs.lookup(name) + return name + except LookupError: + return None + + +STRING_ESC_RE = re.compile(r'""|\\(?:[0-7]{1,3}|[xX][0-9A-Fa-f]{1,2}|.)') +STRING_ESC_WIDE_RE = re.compile(r'""|\\(?:[0-7]{1,3}|[xX][0-9A-Fa-f]{1,4}|.)') +SIMPLE_ESCAPES = {'n': '\n', 't': '\t', 'r': '\r', '\\': '\\', '"': '"', + 'a': '\x08'} # rc.exe maps \a to 0x08, not BEL + + +def _esc_value(e): + if e == '""': + return 34, False + c = e[1] + if c in SIMPLE_ESCAPES: + return ord(SIMPLE_ESCAPES[c]), False + if c in '01234567': + return int(e[1:], 8), True + if c in 'xX' and len(e) > 2: + return int(e[2:], 16), True + return None, False + + +def _narrow_sub(m): + v, _ = _esc_value(m.group()) + if v is None: + return m.group() + return chr(v & 0xFF) + + +def string_bytes(raw, wide, cp): + if not wide and cp != 1200: + if '\\' in raw or '"' in raw: + raw = STRING_ESC_RE.sub(_narrow_sub, raw) + return raw.encode('latin-1') + return decode_rc_string(raw, cp, wide).encode('utf-16-le') + + +def decode_rc_string(raw, cp, wide): + if not wide and cp != 1200: + b = string_bytes(raw, False, cp) + return b.decode(codec_for(cp) or 'cp1252', 'replace') + out = [] + pos = 0 + pending = [] + + def flush(): + if pending: + s = ''.join(pending) + if cp == 1200: + out.append(s) + else: + out.append(s.encode('latin-1').decode(codec_for(cp) or 'cp1252', 'replace')) + del pending[:] + + for m in STRING_ESC_WIDE_RE.finditer(raw): + pending.append(raw[pos:m.start()]) + pos = m.end() + v, numeric = _esc_value(m.group()) + if v is None: + pending.append(m.group()) + elif numeric: + flush() + out.append(chr(v)) + else: + pending.append(chr(v)) + pending.append(raw[pos:]) + flush() + return ''.join(out) + + +def read_source(path): + data = open(path, 'rb').read() + if data.startswith(b'\xff\xfe'): + return data[2:].decode('utf-16-le', 'replace'), 1200 + if data.startswith(b'\xfe\xff'): + return data[2:].decode('utf-16-be', 'replace'), 1200 + if data.startswith(b'\xef\xbb\xbf'): + return data[3:].decode('latin-1'), 65001 + return data.decode('latin-1'), None + + +class DirCache: + def __init__(self): + self.cache = {} + + def names(self, d): + r = self.cache.get(d) + if r is None: + try: + r = os.listdir(d) + except OSError: + r = [] + self.cache[d] = r + return r + + def resolve(self, base, rel): + cur = base + out = [] + for part in rel.split('/'): + if part in ('', '.'): + continue + if part == '..': + cur = os.path.dirname(cur) + out.append('..') + continue + names = self.names(cur) + if part in names: + actual = part + else: + low = part.lower() + matches = sorted(n for n in names if n.lower() == low) + if not matches: + return None + actual = matches[0] + out.append(actual) + cur = os.path.join(cur, actual) + return out + + +class Preprocessor: + def __init__(self, include_dirs, diag, default_cp, dirs): + self.include_dirs = include_dirs + self.diag = diag + self.default_cp = default_cp + self.cp = default_cp + self.dirs = dirs + self.macros = {} + self.tokens = [] + self.files = [] + self.deps = [] + self.once = set() + self.stack = [] + self.skipped_includes = [] + self.ev = Evaluator(self.macros) + + def define_cmdline(self, spec): + name, _, value = spec.partition('=') + toks = [] + lex(value if _ else '1', toks, 0, 0, 0) + self.macros[name.strip()] = (None, toks) + + def find_include(self, name, cur_dir, angle): + rel = name.replace('\\', '/') + if os.path.isabs(rel): + return rel if os.path.isfile(rel) else None + dirs = [] + if not angle: + dirs.append(cur_dir) + dirs.extend(reversed(self.stack)) + dirs.extend(self.include_dirs) + for d in dirs: + p = os.path.join(d, rel) + if os.path.isfile(p): + return p + parts = self.dirs.resolve(d, rel) + if parts: + p = os.path.join(d, *parts) + if os.path.isfile(p): + return p + alias = HEADER_ALIASES.get(os.path.basename(rel).lower()) + if alias and alias != rel: + return self.find_include(alias, cur_dir, True) + return None + + def process_file(self, path, rc_mode): + real = os.path.realpath(path) + if real in self.once: + return + if len(self.stack) > 40: + self.diag.error(path, 'includes nested too deeply') + return + text, forced_cp = read_source(path) + fi = len(self.files) + self.files.append(path) + self.deps.append(os.path.abspath(path)) + saved_cp = self.cp + if forced_cp is not None: + self.cp = forced_cp + cur_dir = os.path.dirname(os.path.abspath(path)) + self.stack.append(cur_dir) + diag = self.diag + toks = self.tokens + sink = [] + + def unterminated(ln, fi_): + diag.warn('%s:%d' % (self.files[fi_], ln), 'unterminated string literal') + + lines = text.split('\n') + n = len(lines) + cond = [] + active = True + in_block = False + i = 0 + while i < n: + line = lines[i] + ln = i + 1 + i += 1 + if line.endswith('\r'): + line = line[:-1] + if in_block: + e = line.find('*/') + if e < 0: + continue + line = ' ' * (e + 2) + line[e + 2:] + in_block = False + stripped = line.lstrip() + if stripped.startswith('#'): + while line.endswith('\\') and i < n: + line = line[:-1] + lines[i].rstrip('\r') + i += 1 + loc = '%s:%d' % (path, ln) + m = re.match(r'\s*#\s*([A-Za-z_]\w*)?(.*)$', line) + name = m.group(1) or '' + rest = m.group(2) + if name in ('if', 'ifdef', 'ifndef'): + if not active: + cond.append([False, True, False]) + else: + if name == 'if': + val = self.eval_if(rest, ln, fi, loc) + else: + mm = re.match(r'\s*([A-Za-z_]\w*)', rest) + if not mm: + diag.warn(loc, '#%s without identifier' % name) + val = False + else: + val = (mm.group(1) in self.macros) != (name == 'ifndef') + cond.append([val, val, True]) + active = cond[-1][0] + elif name == 'elif': + if not cond: + diag.warn(loc, '#elif without #if') + continue + top = cond[-1] + if top[2] and not top[1]: + top[0] = self.eval_if(rest, ln, fi, loc) + top[1] = top[0] + else: + top[0] = False + active = top[0] + elif name == 'else': + if not cond: + diag.warn(loc, '#else without #if') + continue + top = cond[-1] + top[0] = top[2] and not top[1] + top[1] = True + active = top[0] + elif name == 'endif': + if not cond: + diag.warn(loc, '#endif without #if') + continue + cond.pop() + active = cond[-1][0] if cond else True + elif not active: + pass + elif name == 'define': + mm = re.match(r'\s*([A-Za-z_]\w*)(\([^)]*\))?(.*)$', rest) + if not mm: + diag.warn(loc, 'malformed #define') + continue + params = None + if mm.group(2) is not None: + params = [p.strip() for p in mm.group(2)[1:-1].split(',') if p.strip()] + body = [] + in_block = lex(mm.group(3), body, 0, ln, fi) + old = self.macros.get(mm.group(1)) + if old is not None and (old[0] != params or [t.s for t in old[1]] != [t.s for t in body]): + diag.warn(loc, "macro '%s' redefined" % mm.group(1)) + self.macros[mm.group(1)] = (params, body) + continue + elif name == 'undef': + mm = re.match(r'\s*([A-Za-z_]\w*)', rest) + if mm: + self.macros.pop(mm.group(1), None) + elif name == 'include': + mm = re.match(r'\s*(?:"([^"]*)"|<([^>]*)>)', rest) + if not mm: + diag.warn(loc, 'malformed #include') + continue + inc = mm.group(1) if mm.group(1) is not None else mm.group(2) + found = self.find_include(inc, cur_dir, mm.group(1) is None) + if found is None: + self.skipped_includes.append((loc, inc)) + else: + ext = os.path.splitext(found)[1].lower() + self.process_file(found, rc_mode and ext in RC_EXTENSIONS) + rest = rest[mm.end():] + elif name == 'pragma': + mm = re.match(r'\s*code_page\s*\(\s*(\w+)\s*\)', rest) + if mm: + v = mm.group(1) + if v.isdigit() and (int(v) == 1200 or codec_for(int(v))): + self.cp = int(v) + elif v.upper() == 'DEFAULT': + self.cp = self.default_cp + else: + diag.warn(loc, 'unsupported code page %s' % v) + elif re.match(r'\s*once\b', rest): + self.once.add(real) + elif name == 'error': + diag.warn(loc, '#error%s' % rest) + elif name in ('warning', 'line', ''): + pass + else: + diag.warn(loc, 'ignoring unknown directive #%s' % name) + if '/*' in rest: + del sink[:] + in_block = lex(rest, sink, 0, ln, fi) + continue + if active and rc_mode: + in_block = lex(line, toks, self.cp, ln, fi, unterminated) + elif '/*' in line: + del sink[:] + in_block = lex(line, sink, 0, ln, fi) + if cond: + diag.warn(path, 'unterminated #if at end of file') + self.stack.pop() + if forced_cp is not None: + self.cp = saved_cp + + def eval_if(self, rest, ln, fi, loc): + toks = [] + lex(rest, toks, 0, ln, fi) + try: + return bool(self.ev.eval_pp(toks)) + except EvalError as e: + self.diag.warn(loc, 'cannot evaluate #if expression (%s); treating as false' % e) + return False + + +def lang_code(primary, sub): + if primary == 0: + return 'neutral' + c = LANG_CODE_BY_ID.get((primary, sub)) + if c: + return c + c = LANG_CODE_BY_PRIMARY.get(primary) + if c: + return c + return 'lang%04x' % ((sub << 10) | primary) + + +def to_s16(v): + v &= 0xFFFF + return v - 0x10000 if v & 0x8000 else v + + +def to_s32(v): + v &= 0xFFFFFFFF + return v - 0x100000000 if v & 0x80000000 else v + + +def ctrl_id(v): + v = to_s32(v) + return -1 if v in (-1, 0xFFFF) else v + + +class Rec: + def __init__(self, **kw): + self.__dict__.update(kw) + + +class LangData: + def __init__(self, code, langid): + self.code = code + self.langid = langid + self.cp = None + self.dialogs = [] + self.menus = [] + self.strings = {} + self.accels = [] + self.toolbars = [] + self.files = [] + self.dlginits = [] + self.version = None + self.keys = set() + + +def is_begin(t): + return (t.k == 'id' and t.v.upper() == 'BEGIN') or (t.k == 'op' and t.v == '{') + + +def is_end(t): + return (t.k == 'id' and t.v.upper() == 'END') or (t.k == 'op' and t.v == '}') + + +def kw(t): + return t.v.upper() if t.k == 'id' else None + + +class RcParser: + def __init__(self, pp, rc_path, diag, dirs): + self.pp = pp + self.ev = pp.ev + self.diag = diag + self.dirs = dirs + last = pp.tokens[-1] if pp.tokens else EOF_TOK + self.cur = Cursor(pp.tokens, Tok('eof', None, '', None, 0, last.ln, last.fi)) + self.rc_dir = os.path.dirname(os.path.abspath(rc_path)) + self.langs = {} + self.lang = None + self.string_named = {} + self.missing_files = {} + self.skipped = [] + self.decode_errors = 0 + + def loc(self, t): + return '%s:%d' % (self.pp.files[t.fi] if self.pp.files else '?', t.ln) + + def parse(self): + c = self.cur + while c.peek().k != 'eof': + start = c.i + try: + self.statement() + except ParseError as e: + self.diag.error(self.loc(e.tok), e.msg) + self.recover(start) + + def recover(self, start): + c = self.cur + if c.i <= start: + c.i = start + 1 + while c.peek().k != 'eof': + t = c.peek() + prev = c.t[c.i - 1] + if prev.ln != t.ln or prev.fi != t.fi: + u = kw(t) + if u in ('STRINGTABLE', 'LANGUAGE'): + return + t2 = c.peek(1) + if t2.k == 'id' and t2.ln == t.ln and t2.v.upper() in TOP_TYPES and t.k in ('id', 'num', 'str'): + return + c.next() + + def expr(self, min_prec=1): + t = self.cur.peek() + try: + return self.ev.expr(self.cur, min_prec) + except EvalError as e: + raise ParseError(t, str(e)) + + def expr_sym(self): + start = self.cur.i + t = self.cur.peek() + v = self.expr() + return v, (t.v if self.cur.i == start + 1 and t.k == 'id' else None) + + def unary(self): + t = self.cur.peek() + try: + return self.ev.unary(self.cur) + except EvalError as e: + raise ParseError(t, str(e)) + + def style_expr(self, value): + c = self.cur + while True: + t = c.peek() + if t.k == 'id' and t.v.upper() == 'NOT' and t.v not in self.ev.macros: + c.next() + value &= ~self.unary() + else: + value |= self.expr(PREC['^']) + nt = c.peek() + if nt.k == 'op' and nt.v == '|': + c.next() + continue + return value & 0xFFFFFFFF + + def accept_op(self, v): + t = self.cur.peek() + if t.k == 'op' and t.v == v: + self.cur.next() + return True + return False + + def expect_begin(self): + t = self.cur.next() + if not is_begin(t): + raise ParseError(t, 'expected BEGIN but found %s' % describe(t)) + + def text(self, t): + s = decode_rc_string(t.v, t.cp, t.x) + if '�' in s: + self.decode_errors += 1 + self.diag.warn(self.loc(t), 'string contains bytes undefined in code page %d' % t.cp) + return s + + def macro_string(self, t): + m = self.ev.macros.get(t.v) if t.k == 'id' else None + if m is not None and m[0] is None and len(m[1]) == 1 and m[1][0].k == 'str': + return self.text(m[1][0]) + return None + + def string(self): + t = self.cur.next() + if t.k == 'str': + return self.text(t) + s = self.macro_string(t) + if s is None: + raise ParseError(t, 'expected string but found %s' % describe(t)) + return s + + def coords(self, n): + out = [] + for i in range(n): + if i: + self.accept_op(',') + out.append(to_s16(self.expr())) + return out + + def skip_block(self): + self.expect_begin() + depth = 1 + while depth: + t = self.cur.next() + if t.k == 'eof': + raise ParseError(t, 'unterminated BEGIN block') + if is_begin(t): + depth += 1 + elif is_end(t): + depth -= 1 + + def get_lang(self, primary, sub): + primary &= 0x3FF + sub &= 0x3F + code = lang_code(primary, sub) + L = self.langs.get(code) + if L is None: + L = LangData(code, (sub << 10) | primary if code != 'neutral' else 0) + self.langs[code] = L + return L + + def language_args(self): + p = self.expr() + self.accept_op(',') + s = self.expr() + return self.get_lang(p, s) + + def target(self, override, tok): + L = override or self.lang + if L is None: + L = self.get_lang(0, 0) + if L.cp is None: + L.cp = tok.cp + return L + + def add_unique(self, L, kind, rid, rname, tok): + key = (kind, rid, rname.upper() if rname else None) + if key in L.keys: + self.diag.warn(self.loc(tok), 'duplicate %s resource %s in language %s ignored' + % (kind, rname or rid, L.code)) + return False + L.keys.add(key) + return True + + def resource_options(self): + override = None + c = self.cur + while True: + u = kw(c.peek()) + if u in MEMORY_OPTIONS: + c.next() + elif u == 'LANGUAGE': + c.next() + override = self.language_args() + elif u in ('CHARACTERISTICS', 'VERSION'): + c.next() + self.expr() + else: + return override + + def name_ref(self): + c = self.cur + t = c.peek() + if t.k == 'str' or self.macro_string(t) is not None: + return 0, self.string(), None + if t.k == 'id': + if self.ev.is_symbol(t.v): + return self.expr() & 0xFFFF, None, t.v + c.next() + self.string_named[t.v] = self.string_named.get(t.v, 0) + 1 + return 0, t.v, t.v + if t.k == 'num' or (t.k == 'op' and t.v in ('(', '-', '~', '+')): + return self.expr() & 0xFFFF, None, None + raise ParseError(t, 'expected resource name but found %s' % describe(t)) + + def statement(self): + c = self.cur + t = c.peek() + u = kw(t) + if u == 'LANGUAGE': + c.next() + self.lang = self.language_args() + return + if u == 'STRINGTABLE': + c.next() + self.parse_stringtable(t) + return + if u in ('VERSION', 'CHARACTERISTICS'): + c.next() + self.expr() + return + rid, rname, sym = self.name_ref() + tt = c.next() + if tt.k == 'id': + ut = tt.v.upper() + if ut in ('DIALOG', 'DIALOGEX'): + return self.parse_dialog(rid, rname, sym, t, ut == 'DIALOGEX') + if ut in ('MENU', 'MENUEX'): + return self.parse_menu(rid, rname, sym, t, ut == 'MENUEX') + if ut == 'ACCELERATORS': + return self.parse_accelerators(rid, rname, sym, t) + if ut == 'TOOLBAR': + return self.parse_toolbar(rid, rname, sym, t) + if ut == 'DLGINIT': + return self.parse_dlginit(rid, rname, sym, t) + if ut == 'VERSIONINFO': + return self.parse_versioninfo(t) + if ut in SKIP_TYPES: + self.resource_options() + if is_begin(c.peek()): + self.skip_block() + return + if ut in FILE_TYPES: + typename = ut + elif self.ev.is_symbol(tt.v): + c.i -= 1 + v = self.expr() & 0xFFFF + typename = RT_NAMES.get(v, '#%d' % v) + else: + typename = tt.v + elif tt.k == 'str': + typename = self.text(tt) + elif tt.k == 'num': + typename = RT_NAMES.get(tt.v, '#%d' % tt.v) + else: + raise ParseError(tt, 'expected resource type but found %s' % describe(tt)) + self.parse_file_resource(typename, rid, rname, sym, t) + + def parse_file_resource(self, typename, rid, rname, sym, tok): + c = self.cur + override = self.resource_options() + t = c.peek() + if t.k == 'str': + path = self.string() + elif is_begin(t): + self.skip_block() + self.skipped.append((self.loc(tok), '%s resource %s with inline data' + % (typename, rname or sym or rid))) + return + else: + parts = [] + while c.peek().k != 'eof' and c.peek().ln == t.ln and c.peek().fi == t.fi: + parts.append(c.next().s) + path = ''.join(parts) + self.diag.warn(self.loc(t), 'unquoted file name %r' % path) + L = self.target(override, tok) + if not self.add_unique(L, typename, rid, rname, tok): + return + rel = path.replace('\\', '/') + if os.path.isabs(rel) or re.match(r'^[A-Za-z]:/', rel): + self.diag.warn(self.loc(tok), 'absolute resource path %r' % path) + resolved = rel if os.path.exists(rel) else None + else: + parts = self.dirs.resolve(self.rc_dir, rel) + resolved = '/'.join(parts) if parts else None + if resolved is not None and not os.path.isfile(os.path.join(self.rc_dir, resolved)): + resolved = None + if resolved is None: + resolved = '/'.join(p for p in rel.split('/') if p not in ('', '.')) + if resolved not in self.missing_files: + self.missing_files[resolved] = self.loc(tok) + self.diag.warn(self.loc(tok), 'resource file not found: %s' % path) + L.files.append(Rec(type=typename, id=rid, name=rname, path=resolved, sym=sym, + exists=resolved not in self.missing_files)) + + def parse_stringtable(self, tok): + c = self.cur + override = self.resource_options() + self.expect_begin() + L = self.target(override, tok) + while not is_end(c.peek()): + it = c.peek() + if it.k == 'eof': + raise ParseError(it, 'unterminated STRINGTABLE') + hits = self.ev.undefined_hits + sid, sym = self.expr_sym() + sid &= 0xFFFF + self.accept_op(',') + text = self.string() + if self.ev.undefined_hits != hits: + self.skipped.append((self.loc(it), 'string %s with undefined id' % (sym or ''))) + elif sid in L.strings: + self.diag.warn(self.loc(it), 'duplicate string id %d (%s) in language %s ignored' + % (sid, sym or '', L.code)) + else: + L.strings[sid] = (text, sym) + c.next() + + def parse_dialog(self, rid, rname, sym, tok, ex): + c = self.cur + override = self.resource_options() + x, y, cx, cy = self.coords(4) + if self.accept_op(','): + self.expr() + style = None + exstyle = 0 + caption = None + font = None + menu = (0, None) + while True: + t = c.peek() + if is_begin(t): + break + u = kw(t) + c.next() + if u == 'STYLE': + style = self.style_expr(0) + elif u == 'EXSTYLE': + exstyle = self.style_expr(0) + elif u == 'CAPTION': + caption = self.string() + elif u == 'FONT': + size = self.expr() + self.accept_op(',') + face = self.string() + weight = italic = 0 + if self.accept_op(','): + weight = self.expr() + if self.accept_op(','): + italic = self.expr() + if self.accept_op(','): + self.expr() + font = (to_s16(size), face, to_s16(weight), 1 if italic & 0xFF else 0) + elif u == 'MENU': + mid, mname, _ = self.name_ref() + menu = (mid, mname) + elif u == 'CLASS': + _, cname, _ = self.name_ref() + self.skipped.append((self.loc(t), 'CLASS %s of dialog %s' % (cname, rname or sym or rid))) + elif u == 'LANGUAGE': + override = self.language_args() + elif u in ('CHARACTERISTICS', 'VERSION'): + self.expr() + elif u in MEMORY_OPTIONS: + pass + else: + raise ParseError(t, 'unexpected %s in dialog header' % describe(t)) + if style is None: + style = WS_POPUP | WS_BORDER | WS_SYSMENU + if caption is not None: + style |= WS_CAPTION + if font is not None: + style |= DS_SETFONT + self.expect_begin() + controls = [] + while not is_end(c.peek()): + controls.append(self.parse_control()) + c.next() + L = self.target(override, tok) + if self.add_unique(L, 'DIALOG', rid, rname, tok): + L.dialogs.append(Rec(id=rid, name=rname, sym=sym, x=x, y=y, cx=cx, cy=cy, style=style, + exstyle=exstyle, caption=caption, font=font, menu=menu, + controls=controls)) + + def ctrl_text(self): + c = self.cur + t = c.peek() + if t.k == 'str' or self.macro_string(t) is not None: + return self.string() + if t.k == 'id' and not self.ev.is_symbol(t.v): + c.next() + return t.v + return '#%d' % (self.expr() & 0xFFFF) + + def ctrl_class(self): + c = self.cur + t = c.next() + if t.k == 'str': + name = self.text(t) + elif t.k == 'id': + name = self.macro_string(t) + if name is None: + name = t.v + elif t.k == 'num': + return CLASS_ORDINALS.get(t.v, '#%d' % t.v) + else: + raise ParseError(t, 'expected control class but found %s' % describe(t)) + return CLASS_NAMES.get(name.upper(), name) + + def ctrl_tail(self, style, exstyle): + if self.accept_op(','): + style = self.style_expr(style) + if self.accept_op(','): + exstyle = self.style_expr(exstyle) + if self.accept_op(','): + self.expr() + return style, exstyle + + def parse_control(self): + c = self.cur + t = c.next() + u = kw(t) + exstyle = 0 + if u == 'CONTROL': + text = self.ctrl_text() + self.accept_op(',') + cid, sym = self.expr_sym() + self.accept_op(',') + cls = self.ctrl_class() + self.accept_op(',') + style = self.style_expr(CHILD) + self.accept_op(',') + x, y, cx, cy = self.coords(4) + if self.accept_op(','): + exstyle = self.style_expr(0) + if self.accept_op(','): + self.expr() + elif u in TEXT_CONTROLS: + cls, default = TEXT_CONTROLS[u] + text = self.ctrl_text() + self.accept_op(',') + cid, sym = self.expr_sym() + self.accept_op(',') + x, y, cx, cy = self.coords(4) + style, exstyle = self.ctrl_tail(CHILD | default, 0) + elif u in PLAIN_CONTROLS: + cls, default = PLAIN_CONTROLS[u] + text = None + cid, sym = self.expr_sym() + self.accept_op(',') + x, y, cx, cy = self.coords(4) + style, exstyle = self.ctrl_tail(CHILD | default, 0) + if u == 'COMBOBOX' and not style & CBS_TYPEMASK: + style |= CBS_SIMPLE + elif u == 'ICON': + cls = 'Static' + text = self.ctrl_text() + self.accept_op(',') + cid, sym = self.expr_sym() + self.accept_op(',') + x, y = self.coords(2) + cx = cy = 0 + style = CHILD | SS_ICON + if self.accept_op(','): + cx, cy = self.coords(2) + style, exstyle = self.ctrl_tail(style, 0) + else: + raise ParseError(t, 'unknown control statement %s' % describe(t)) + if is_begin(c.peek()): + self.skip_block() + self.skipped.append((self.loc(t), 'control data block of %s' % (sym or cid))) + return Rec(cls=cls, text=text, id=ctrl_id(cid), x=x, y=y, cx=cx, cy=cy, + style=style & 0xFFFFFFFF, exstyle=exstyle & 0xFFFFFFFF, sym=sym) + + def menu_options(self): + c = self.cur + flags = 0 + while True: + t = c.peek() + if t.k == 'op' and t.v in (',', '|') and kw(c.peek(1)) in MENU_OPTIONS: + c.next() + continue + u = kw(t) + if u in MENU_OPTIONS: + c.next() + flags |= MENU_OPTIONS[u] + continue + return flags + + def ex_fields(self, count): + vals = [0] * count + syms = [None] * count + for i in range(count): + if not self.accept_op(','): + break + t = self.cur.peek() + if (t.k == 'op' and t.v == ',') or is_begin(t) or is_end(t): + continue + vals[i], syms[i] = self.expr_sym() + return vals, syms + + def menu_items(self, items, depth, ex): + c = self.cur + self.expect_begin() + while not is_end(c.peek()): + t = c.next() + u = kw(t) + if u == 'MENUITEM': + if kw(c.peek()) == 'SEPARATOR': + c.next() + items.append((depth, 0, MF_SEPARATOR, None, None)) + continue + text = self.string() + if ex: + (mid, mtype, state), syms = self.ex_fields(3) + items.append((depth, to_s32(mid), (mtype | state) & 0xFFFFFFFF, text, syms[0])) + else: + self.accept_op(',') + mid, sym = self.expr_sym() + items.append((depth, mid & 0xFFFF, self.menu_options(), text, sym)) + elif u == 'POPUP': + text = self.string() + if ex: + (mid, mtype, state, _), syms = self.ex_fields(4) + items.append((depth, to_s32(mid), MF_POPUP | ((mtype | state) & 0xFFFFFFFF), text, syms[0])) + else: + items.append((depth, 0, MF_POPUP | self.menu_options(), text, None)) + self.menu_items(items, depth + 1, ex) + elif t.k == 'eof': + raise ParseError(t, 'unterminated menu') + else: + raise ParseError(t, 'unexpected %s in menu' % describe(t)) + c.next() + + def parse_menu(self, rid, rname, sym, tok, ex): + override = self.resource_options() + items = [] + self.menu_items(items, 0, ex) + L = self.target(override, tok) + if self.add_unique(L, 'MENU', rid, rname, tok): + L.menus.append(Rec(id=rid, name=rname, sym=sym, items=items)) + + def parse_accelerators(self, rid, rname, sym, tok): + c = self.cur + override = self.resource_options() + self.expect_begin() + items = [] + while not is_end(c.peek()): + t = c.peek() + if t.k == 'eof': + raise ParseError(t, 'unterminated ACCELERATORS') + event = None + if t.k == 'str': + c.next() + event = self.text(t) + else: + keyval = self.expr() + self.accept_op(',') + cmd, csym = self.expr_sym() + flags = 0 + while True: + nt = c.peek() + if nt.k == 'op' and nt.v in (',', '|') and kw(c.peek(1)) in ACCEL_OPTIONS: + c.next() + continue + u = kw(nt) + if u in ACCEL_OPTIONS: + c.next() + flags |= ACCEL_OPTIONS[u] + continue + break + if event is not None: + if len(event) == 2 and event[0] == '^': + ch = event[1].upper() + key = ord(ch) - 0x40 + if not 0 < key < 0x20: + self.diag.warn(self.loc(t), 'invalid control character %r' % event) + key &= 0x1F + elif len(event) == 1: + key = ord(event.upper() if flags & FVIRTKEY else event) + else: + self.diag.warn(self.loc(t), 'invalid accelerator key %r' % event) + key = ord(event[0]) if event else 0 + else: + key = keyval & 0xFFFF + items.append((key, cmd & 0xFFFF, flags, csym)) + c.next() + L = self.target(override, tok) + if self.add_unique(L, 'ACCELERATORS', rid, rname, tok): + L.accels.append(Rec(id=rid, name=rname, sym=sym, items=items)) + + def parse_toolbar(self, rid, rname, sym, tok): + c = self.cur + self.resource_options() + w, h = self.coords(2) + override = self.resource_options() + self.expect_begin() + buttons = [] + while not is_end(c.peek()): + t = c.next() + u = kw(t) + if u == 'BUTTON': + bid, bsym = self.expr_sym() + buttons.append((bid & 0xFFFF, bsym)) + elif u == 'SEPARATOR': + buttons.append((0, None)) + else: + raise ParseError(t, 'unexpected %s in TOOLBAR' % describe(t)) + c.next() + L = self.target(override, tok) + if self.add_unique(L, 'TOOLBAR', rid, rname, tok): + L.toolbars.append(Rec(id=rid, name=rname, sym=sym, w=w, h=h, buttons=buttons)) + + def parse_dlginit(self, rid, rname, sym, tok): + c = self.cur + override = self.resource_options() + self.expect_begin() + data = bytearray() + while not is_end(c.peek()): + t = c.peek() + if t.k == 'eof': + raise ParseError(t, 'unterminated DLGINIT') + if t.k == 'str': + c.next() + data += string_bytes(t.v, t.x, t.cp) + else: + self.ev.saw_long = False + v = self.expr() + if self.ev.saw_long: + data += (v & 0xFFFFFFFF).to_bytes(4, 'little') + else: + data += (v & 0xFFFF).to_bytes(2, 'little') + self.accept_op(',') + c.next() + L = self.target(override, tok) + codec = codec_for(tok.cp) or 'cp1252' + off = 0 + n = len(data) + while off + 2 <= n: + ctl = int.from_bytes(data[off:off + 2], 'little') + off += 2 + if ctl == 0: + break + if off + 6 > n: + self.diag.warn(self.loc(tok), 'truncated DLGINIT data') + break + msg = int.from_bytes(data[off:off + 2], 'little') + length = int.from_bytes(data[off + 2:off + 6], 'little') + off += 6 + payload = bytes(data[off:off + length]) + off += length + if msg in DLGINIT_TEXT_MESSAGES: + text = payload.split(b'\0', 1)[0].decode(codec, 'replace') + L.dlginits.append(Rec(dialog=rid, control=ctrl_id(ctl), text=text, sym=sym)) + else: + self.skipped.append((self.loc(tok), 'DLGINIT entry with message 0x%04X for control %d of %s (%d bytes)' + % (msg, ctl, rname or sym or rid, length))) + if rname: + self.diag.warn(self.loc(tok), 'DLGINIT for string-named dialog %s stored with dialogId 0' % rname) + + def version_block(self): + c = self.cur + self.expect_begin() + items = [] + while not is_end(c.peek()): + t = c.next() + u = kw(t) + if u == 'BLOCK': + name = self.string() + while self.accept_op(','): + self.expr() + items.append(('BLOCK', name, self.version_block())) + elif u == 'VALUE': + key = self.string() + vals = [] + while self.accept_op(','): + if c.peek().k == 'str': + text = self.string() + while c.peek().k == 'str': + text += self.string() + vals.append(text) + else: + vals.append(self.expr()) + items.append(('VALUE', key, vals)) + elif t.k == 'eof': + raise ParseError(t, 'unterminated VERSIONINFO block') + else: + raise ParseError(t, 'unexpected %s in VERSIONINFO' % describe(t)) + c.next() + return items + + def parse_versioninfo(self, tok): + c = self.cur + override = self.resource_options() + fixed = {'FILEVERSION': [0, 0, 0, 0], 'PRODUCTVERSION': [0, 0, 0, 0]} + while not is_begin(c.peek()): + t = c.next() + u = kw(t) + if u in fixed: + vals = [self.expr()] + while self.accept_op(','): + vals.append(self.expr()) + fixed[u] = ([v & 0xFFFF for v in vals] + [0, 0, 0, 0])[:4] + elif u in ('FILEFLAGSMASK', 'FILEFLAGS', 'FILEOS', 'FILETYPE', 'FILESUBTYPE'): + self.expr() + elif u == 'LANGUAGE': + override = self.language_args() + else: + raise ParseError(t, 'unexpected %s in VERSIONINFO header' % describe(t)) + items = self.version_block() + pairs = [] + for kind, name, children in items: + if kind == 'BLOCK' and name.lower() == 'stringfileinfo': + for k2, n2, c2 in children: + if k2 == 'BLOCK': + for k3, key, vals in c2: + if k3 == 'VALUE' and all(isinstance(v, str) for v in vals): + pairs.append((key.split('\0', 1)[0], ''.join(vals).split('\0', 1)[0])) + break + break + L = self.target(override, tok) + if L.version is None: + L.version = Rec(file=fixed['FILEVERSION'], product=fixed['PRODUCTVERSION'], pairs=pairs) + + +ESCAPE_TABLE = [] +for _b in range(256): + if _b == 0x22: + ESCAPE_TABLE.append('\\"') + elif _b == 0x5C: + ESCAPE_TABLE.append('\\\\') + elif _b == 0x0A: + ESCAPE_TABLE.append('\\n') + elif _b == 0x09: + ESCAPE_TABLE.append('\\t') + elif _b == 0x0D: + ESCAPE_TABLE.append('\\r') + elif 0x20 <= _b < 0x7F: + ESCAPE_TABLE.append(chr(_b)) + else: + ESCAPE_TABLE.append('\\%03o' % _b) + + +def c_str(s): + if s is None: + return 'nullptr' + pieces = list(map(ESCAPE_TABLE.__getitem__, s.encode('utf-8'))) + chunks = [''.join(pieces[i:i + 400]) for i in range(0, len(pieces), 400)] or [''] + out = [] + for ch in chunks: + while '??' in ch: + ch = ch.replace('??', '?\\?') + out.append('"%s"' % ch) + return ' '.join(out) + + +def c_ident(s): + return re.sub(r'[^A-Za-z0-9_]', '_', s) + + +def sym_comment(sym): + return ' // %s' % sym if sym else '' + + +def u32(v): + return '0x%08Xu' % (v & 0xFFFFFFFF) + + +def emit_language(L, module, rc_name, default_cp): + o = [] + w = o.append + var = 'g_rcLang_%s_%s' % (module, c_ident(L.code)) + w('// Generated by rc2cpp.py from %s, language %s. Do not edit.' % (rc_name, L.code)) + w('#include "mfcwx/rcdata.h"') + w('') + w('namespace {') + w('') + w('using namespace mfcwx::rc;') + w('') + for i, d in enumerate(L.dialogs): + if not d.controls: + continue + w('const Control kDialog%dControls[] = {' % i) + for ct in d.controls: + w(' {%s, %s, %d, %d, %d, %d, %d, %s, %s},%s' % ( + c_str(ct.cls), c_str(ct.text), ct.id, ct.x, ct.y, ct.cx, ct.cy, u32(ct.style), + u32(ct.exstyle), sym_comment(ct.sym))) + w('};') + w('') + if L.dialogs: + w('const Dialog kDialogs[] = {') + for i, d in enumerate(L.dialogs): + size, face, weight, italic = d.font if d.font else (0, None, 0, 0) + w(' {%d, %s, %d, %d, %d, %d, %s, %s, %s, %s, %d, %d, %d, %s, %d, %s, %d},%s' % ( + d.id, c_str(d.name), d.x, d.y, d.cx, d.cy, u32(d.style), u32(d.exstyle), + c_str(d.caption), c_str(face), size, weight, italic, c_str(d.menu[1]), d.menu[0], + 'kDialog%dControls' % i if d.controls else 'nullptr', len(d.controls), + sym_comment(d.sym))) + w('};') + w('') + for i, m in enumerate(L.menus): + w('const MenuItem kMenu%dItems[] = {%s' % (i, sym_comment(m.sym))) + for depth, mid, flags, text, sym in m.items: + w(' {%d, %d, 0x%X, %s},%s' % (depth, mid, flags, c_str(text), sym_comment(sym))) + w('};') + w('') + if L.menus: + w('const Menu kMenus[] = {') + for i, m in enumerate(L.menus): + w(' {%d, %s, kMenu%dItems, %d},%s' % (m.id, c_str(m.name), i, len(m.items), sym_comment(m.sym))) + w('};') + w('') + strings = sorted(L.strings.items()) + if strings: + w('const StringEntry kStrings[] = {') + for sid, (text, sym) in strings: + w(' {%d, %s},%s' % (sid, c_str(text), sym_comment(sym))) + w('};') + w('') + for i, a in enumerate(L.accels): + w('const Accel kAccel%dItems[] = {%s' % (i, sym_comment(a.sym))) + for key, cmd, flags, sym in a.items: + w(' {0x%02X, %d, 0x%02X},%s' % (key, cmd, flags, sym_comment(sym))) + w('};') + w('') + if L.accels: + w('const AccelTable kAccels[] = {') + for i, a in enumerate(L.accels): + w(' {%d, %s, kAccel%dItems, %d},%s' % (a.id, c_str(a.name), i, len(a.items), sym_comment(a.sym))) + w('};') + w('') + for i, tb in enumerate(L.toolbars): + w('const int kToolbar%dButtons[] = {%s' % (i, sym_comment(tb.sym))) + for bid, sym in tb.buttons: + w(' %d,%s' % (bid, sym_comment(sym))) + w('};') + w('') + if L.toolbars: + w('const Toolbar kToolbars[] = {') + for i, tb in enumerate(L.toolbars): + w(' {%d, %s, %d, %d, %s, %d},%s' % (tb.id, c_str(tb.name), tb.w, tb.h, + 'kToolbar%dButtons' % i if tb.buttons else 'nullptr', + len(tb.buttons), sym_comment(tb.sym))) + w('};') + w('') + if L.files: + w('const FileResource kFiles[] = {') + for f in L.files: + w(' {%s, %d, %s, %s},%s' % (c_str(f.type), f.id, c_str(f.name), c_str(f.path), sym_comment(f.sym))) + w('};') + w('') + if L.dlginits: + w('const DlgInitEntry kDlgInits[] = {') + for e in L.dlginits: + w(' {%d, %d, %s},%s' % (e.dialog, e.control, c_str(e.text), sym_comment(e.sym))) + w('};') + w('') + if L.version: + v = L.version + if v.pairs: + w('const char* const kVersionKeys[] = {') + for key, val in v.pairs: + w(' %s, %s,' % (c_str(key), c_str(val))) + w('};') + w('') + w('const VersionInfo kVersion = {{%s}, {%s}, %s, %d};' % ( + ', '.join(str(x) for x in v.file), ', '.join(str(x) for x in v.product), + 'kVersionKeys' if v.pairs else 'nullptr', len(v.pairs))) + w('') + w('} // namespace') + w('') + w('extern const mfcwx::rc::Language %s;' % var) + w('const mfcwx::rc::Language %s = {' % var) + w(' %s, 0x%04X, %d,' % (c_str(L.code), L.langid, L.cp or default_cp)) + w(' %s, %d,' % ('kDialogs' if L.dialogs else 'nullptr', len(L.dialogs))) + w(' %s, %d,' % ('kMenus' if L.menus else 'nullptr', len(L.menus))) + w(' %s, %d,' % ('kStrings' if strings else 'nullptr', len(strings))) + w(' %s, %d,' % ('kAccels' if L.accels else 'nullptr', len(L.accels))) + w(' %s, %d,' % ('kToolbars' if L.toolbars else 'nullptr', len(L.toolbars))) + w(' %s, %d,' % ('kFiles' if L.files else 'nullptr', len(L.files))) + w(' %s, %d,' % ('kDlgInits' if L.dlginits else 'nullptr', len(L.dlginits))) + w(' %s,' % ('&kVersion' if L.version else 'nullptr')) + w('};') + w('') + return '\n'.join(o) + + +def emit_module(langs, module, rc_name): + o = [] + w = o.append + w('// Generated by rc2cpp.py from %s. Do not edit.' % rc_name) + w('#include "mfcwx/rcdata.h"') + w('') + names = ['g_rcLang_%s_%s' % (module, c_ident(L.code)) for L in langs] + for n in names: + w('extern const mfcwx::rc::Language %s;' % n) + if names: + w('') + w('namespace {') + w('const mfcwx::rc::Language* const kLanguages[] = {') + for n in names: + w(' &%s,' % n) + w('};') + w('} // namespace') + w('') + w('extern const mfcwx::rc::Module g_rcModule_%s;' % module) + w('const mfcwx::rc::Module g_rcModule_%s = {%s, %s, %d};' % ( + module, c_str(module), 'kLanguages' if names else 'nullptr', len(names))) + w('') + return '\n'.join(o) + + +def output_names(module, langs): + names = ['%s_res.cpp' % module] + names += ['%s_res_%s.cpp' % (module, L.code) for L in langs] + names.append('%s_files.txt' % module) + return names + + +def write_if_changed(path, content): + try: + with open(path, 'r', encoding='utf-8', newline='') as f: + if f.read() == content: + return False + except OSError: + pass + with open(path, 'w', encoding='utf-8', newline='\n') as f: + f.write(content) + return True + + +def write_file(path, content): + with open(path, 'w', encoding='utf-8', newline='\n') as f: + f.write(content) + + +def print_stats(rc_name, module, langs, out=sys.stdout): + cols = ('dialogs', 'controls', 'menus', 'items', 'strings', 'accels', 'keys', 'toolbars', 'files', + 'dlginit', 'version') + print('%s (module %s): %d language(s)' % (rc_name, module, len(langs)), file=out) + print('%-8s %-6s %-5s ' % ('lang', 'langid', 'cp') + ' '.join('%8s' % c for c in cols), file=out) + total = [0] * len(cols) + for L in langs: + row = [len(L.dialogs), sum(len(d.controls) for d in L.dialogs), len(L.menus), + sum(len(m.items) for m in L.menus), len(L.strings), len(L.accels), + sum(len(a.items) for a in L.accels), len(L.toolbars), len(L.files), len(L.dlginits), + 1 if L.version else 0] + total = [a + b for a, b in zip(total, row)] + print('%-8s 0x%04X %-5s ' % (L.code, L.langid, L.cp) + ' '.join('%8d' % v for v in row), file=out) + print('%-8s %-6s %-5s ' % ('total', '', '') + ' '.join('%8d' % v for v in total), file=out) + + +def depfile_escape(p): + return p.replace('\\', '/').replace(' ', '\\ ').replace('#', '\\#').replace('$', '$$') + + +def main(argv=None): + ap = argparse.ArgumentParser(description='Convert a Windows .rc resource script into mfcwx::rc C++ tables.') + ap.add_argument('rc', help='resource script to convert') + ap.add_argument('-m', '--module', help='module name used in file and symbol names (default: .rc base name)') + ap.add_argument('-o', '--out-dir', help='directory for the generated files') + ap.add_argument('-I', dest='include_dirs', action='append', default=[], metavar='DIR', + help='additional include directory') + ap.add_argument('-D', dest='defines', action='append', default=[], metavar='NAME[=VALUE]') + ap.add_argument('-U', dest='undefines', action='append', default=[], metavar='NAME') + ap.add_argument('--header', action='append', default=[], help='header read for #defines before the .rc') + ap.add_argument('--no-default-headers', action='store_true', help='do not pre-include mfcwx/winconst.h') + ap.add_argument('--codepage', type=int, default=1252, help='code page before the first #pragma code_page') + ap.add_argument('--stats', action='store_true', help='print per-language resource counts') + ap.add_argument('--list-outputs', action='store_true', help='print the files that would be generated and exit') + ap.add_argument('--outputs-file', help='file holding the output list; fail if the list changed') + ap.add_argument('--depfile', help='write a Makefile-style dependency file') + ap.add_argument('-q', '--quiet', action='store_true', help='only print errors and summaries') + ap.add_argument('-v', '--verbose', action='store_true', help='list every undefined/string-named symbol') + args = ap.parse_args(argv) + + rc_path = args.rc + if not os.path.isfile(rc_path): + print('rc2cpp: error: cannot open %s' % rc_path, file=sys.stderr) + return 1 + rc_name = os.path.basename(rc_path) + module = c_ident(args.module or os.path.splitext(rc_name)[0]) + if not module or module[0].isdigit(): + module = '_' + module + script_dir = os.path.dirname(os.path.abspath(__file__)) + include_dirs = [os.path.abspath(d) for d in args.include_dirs] + default_inc = os.path.normpath(os.path.join(script_dir, '..', 'mfcwx', 'include')) + if os.path.isdir(default_inc) and default_inc not in include_dirs: + include_dirs.append(default_inc) + + diag = Diag(args.quiet or args.list_outputs) + dirs = DirCache() + if codec_for(args.codepage) is None: + print('rc2cpp: error: unsupported code page %d' % args.codepage, file=sys.stderr) + return 1 + pp = Preprocessor(include_dirs, diag, args.codepage, dirs) + pp.define_cmdline('_WIN32') + pp.define_cmdline('RC_INVOKED') + for d in args.defines: + pp.define_cmdline(d) + for u in args.undefines: + pp.macros.pop(u, None) + if not args.no_default_headers: + hdr = pp.find_include('mfcwx/winconst.h', os.path.dirname(os.path.abspath(rc_path)), True) + if hdr: + pp.process_file(hdr, False) + else: + diag.warn(rc_name, 'mfcwx/winconst.h not found in include path') + for h in args.header: + pp.process_file(h, False) + pp.process_file(rc_path, True) + pp.ev.freeze() + + parser = RcParser(pp, rc_path, diag, dirs) + parser.parse() + langs = list(parser.langs.values()) + outs = output_names(module, langs) + out_dir = args.out_dir + out_paths = [os.path.join(out_dir, n) if out_dir else n for n in outs] + listing = ''.join(p.replace('\\', '/') + '\n' for p in out_paths) + + if args.list_outputs: + if diag.errors: + return 1 + if args.outputs_file: + os.makedirs(os.path.dirname(os.path.abspath(args.outputs_file)), exist_ok=True) + write_if_changed(args.outputs_file, listing) + sys.stdout.write(listing) + return 0 + + if diag.errors: + print('rc2cpp: %d error(s) in %s; nothing generated' % (diag.errors, rc_name), file=sys.stderr) + return 1 + + if out_dir: + os.makedirs(out_dir, exist_ok=True) + write_file(os.path.join(out_dir, outs[0]), emit_module(langs, module, rc_name)) + for L, name in zip(langs, outs[1:-1]): + write_file(os.path.join(out_dir, name), emit_language(L, module, rc_name, args.codepage)) + files = sorted({f.path for L in langs for f in L.files if f.exists}) + write_file(os.path.join(out_dir, outs[-1]), ''.join(f + '\n' for f in files)) + if args.depfile: + deps = sorted(set(pp.deps) | {os.path.abspath(__file__)}) + write_file(args.depfile, '%s: %s\n' % ( + depfile_escape(os.path.abspath(out_paths[0])), + ' \\\n '.join(depfile_escape(d) for d in deps))) + + if args.stats: + print_stats(rc_name, module, langs) + + report(parser, pp, diag, args.verbose, rc_name) + + if args.outputs_file: + try: + with open(args.outputs_file, encoding='utf-8') as f: + previous = f.read() + except OSError: + previous = None + if previous is not None and previous != listing: + write_file(args.outputs_file, listing) + print('rc2cpp: error: the set of languages in %s changed; re-run CMake (the next build does it ' + 'automatically)' % rc_name, file=sys.stderr) + return 1 + if previous is None: + write_file(args.outputs_file, listing) + return 0 + + +def report(parser, pp, diag, verbose, rc_name): + ev = pp.ev + if ev.undefined: + items = sorted(ev.undefined.items()) + if diag.quiet and not verbose: + print('rc2cpp: warning: %s: %d undefined symbol(s) evaluated as 0 (%s%s)' % ( + rc_name, len(items), + ', '.join(n for n, _ in items[:5]), ', ...' if len(items) > 5 else ''), file=sys.stderr) + else: + print('rc2cpp: warning: %d undefined symbol(s) evaluated as 0:' % len(items), file=sys.stderr) + for name, (count, tok) in items: + print(' %-40s %5d use(s), first at %s' % (name, count, parser.loc(tok)), file=sys.stderr) + if parser.missing_files: + print('rc2cpp: warning: %d referenced file(s) not found' % len(parser.missing_files), file=sys.stderr) + if ev.builtins_used: + diag.note('symbols taken from the built-in fallback table (missing from the headers): %s' + % ', '.join(sorted(ev.builtins_used))) + if parser.string_named: + names = sorted(parser.string_named) + shown = names if verbose else names[:12] + diag.note('%d identifier(s) without #define used as string resource names: %s%s' + % (len(names), ', '.join(shown), '' if len(shown) == len(names) else ', ...')) + for loc, inc in pp.skipped_includes: + diag.note('%s: include file "%s" not found, skipped' % (loc, inc)) + if parser.skipped: + if verbose: + for loc, what in parser.skipped: + diag.note('%s: skipped %s' % (loc, what)) + else: + diag.note('%d construct(s) skipped (use -v to list them)' % len(parser.skipped)) + +if __name__ == '__main__': + sys.exit(main()) diff --git a/port/rc/test/CMakeLists.txt b/port/rc/test/CMakeLists.txt new file mode 100644 index 000000000..f03077fdd --- /dev/null +++ b/port/rc/test/CMakeLists.txt @@ -0,0 +1,25 @@ +cmake_minimum_required(VERSION 3.16) +project(rc2cpp_test LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +include("${CMAKE_CURRENT_LIST_DIR}/../rc2cpp.cmake") +get_filename_component(ROOT "${CMAKE_CURRENT_LIST_DIR}/../../.." ABSOLUTE) + +add_executable(rc_test rc_test.cpp cryptool_checks.cpp numbershark_checks.cpp aestool_volren_checks.cpp) +target_include_directories(rc_test PRIVATE "${MFCWX_RC_INCLUDE_DIR}") +if(CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU") + target_compile_options(rc_test PRIVATE -Wall -Wextra -pedantic) +endif() + +mfcwx_add_rc(rc_test RC "${ROOT}/CrypTool/CrypTool.rc" MODULE CrypTool HEADERS "${ROOT}/CrypTool/resource.h") +mfcwx_add_rc(rc_test RC "${ROOT}/aestool/aestool.rc" MODULE aestool HEADERS "${ROOT}/aestool/resource.h") +mfcwx_add_rc(rc_test RC "${ROOT}/NumberShark/MFC-ZahlenHai.rc" MODULE NumberShark HEADERS "${ROOT}/NumberShark/resource.h") +mfcwx_add_rc(rc_test RC "${ROOT}/libVolRen/VolRen.rc" MODULE VolRen HEADERS "${ROOT}/libVolRen/resource.h") + +install(TARGETS rc_test RUNTIME DESTINATION bin) +mfcwx_install_rc_files(rc_test DESTINATION share/rc_test) + +enable_testing() +add_test(NAME rc_test COMMAND rc_test) diff --git a/port/rc/test/aestool_volren_checks.cpp b/port/rc/test/aestool_volren_checks.cpp new file mode 100644 index 000000000..68eb2a189 --- /dev/null +++ b/port/rc/test/aestool_volren_checks.cpp @@ -0,0 +1,36 @@ +#include "rc_test.h" + +#include "afxres.h" +#include "../../../aestool/Resource.h" +#include "../../../libVolRen/resource.h" + +namespace rctest { + +void runAesToolVolRenChecks() { + const Module& aes = g_rcModule_aestool; + RC_CHECK(aes.languageCount == 7); + const Language* el = language(aes, "el"); + RC_CHECK(el && startsWith(string(*el, IDS_STRING_DECOK), "Το αποκÏ�υπτογÏ�αφημένο αÏ�χείο %s")); + const Language* de = language(aes, "de"); + RC_CHECK(de && dialog(*de, IDD_AESTOOL_DIALOG) != nullptr); + RC_CHECK(de && file(*de, "ICON", IDR_MAINFRAME, nullptr) != nullptr); + const Language* pl = language(aes, "pl"); + RC_CHECK(pl && file(*pl, "CURSOR", 0, "AFX_IDC_CONTEXTHELP") != nullptr); + + const Module& volren = g_rcModule_VolRen; + RC_CHECK(volren.languageCount == 1); + const Language* en = language(volren, "en"); + RC_CHECK(en && en->langId == 0x0409 && en->dlgInitCount == 0); + if (!en) + return; + const Dialog* editor = dialog(*en, IDD_EDITOR); + RC_CHECK(editor && equals(editor->caption, "Transfer Function Dialog") && editor->controlCount == 1); + RC_CHECK(editor && editor->style == (DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU | DS_SETFONT)); + const Control* ctrl = editor ? control(*editor, IDC_GTTRAPEZEDITORCTRL1) : nullptr; + RC_CHECK(ctrl && equals(ctrl->cls, "{90393A34-2F31-48A4-9323-8CEC42C73954}")); + RC_CHECK(ctrl && ctrl->style == (WS_CHILD | WS_VISIBLE | WS_TABSTOP) && ctrl->cx == 405 && ctrl->cy == 97); + RC_CHECK(en->version && en->version->keyCount == 9); + RC_CHECK(en->version && equals(en->version->keys[2], "FileDescription") && equals(en->version->keys[3], "VolRen DLL")); +} + +} // namespace rctest diff --git a/port/rc/test/cryptool_checks.cpp b/port/rc/test/cryptool_checks.cpp new file mode 100644 index 000000000..5b6541018 --- /dev/null +++ b/port/rc/test/cryptool_checks.cpp @@ -0,0 +1,169 @@ +#include "rc_test.h" + +#include "afxres.h" +#include "../../../CrypTool/resource.h" + +namespace rctest { + +static void checkLanguages(const Module& m) { + static const char* const codes[] = {"pl", "rs", "de", "fr", "de-CH", "en", "es", "el"}; + RC_CHECK(m.languageCount == 8); + for (int i = 0; i < m.languageCount && i < 8; ++i) + RC_CHECK(equals(m.languages[i]->code, codes[i])); + int dialogs = 0, menus = 0, toolbars = 0, accels = 0; + for (int i = 0; i < m.languageCount; ++i) { + const Language& l = *m.languages[i]; + dialogs += l.dialogCount; + menus += l.menuCount; + toolbars += l.toolbarCount; + accels += l.accelCount; + if (!equals(l.code, "de-CH")) { + RC_CHECK(l.dialogCount >= 150); + RC_CHECK(l.stringCount >= 1600); + RC_CHECK(l.version != nullptr); + } + } + RC_CHECK(dialogs >= 1100); + RC_CHECK(menus == 56); + RC_CHECK(toolbars == 7); + RC_CHECK(accels == 7); +} + +static void checkPolish(const Language& pl) { + RC_CHECK(pl.langId == 0x0415); + RC_CHECK(pl.codepage == 1250); + + const Dialog* props = dialog(pl, IDD_FILE_PROPERTIES); + RC_CHECK(props != nullptr); + if (props) { + RC_CHECK(equals(props->caption, "WÅ‚aÅ›ciwoÅ›ci dokumentu")); + RC_CHECK(equals(props->fontName, "MS Sans Serif") && props->fontSize == 8); + RC_CHECK(props->style == (DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU)); + RC_CHECK(props->cx == 362 && props->cy == 159); + + const Control* edit = control(*props, IDC_EDIT1); + RC_CHECK(edit && equals(edit->cls, "Edit") && edit->text == nullptr); + RC_CHECK(edit && edit->style == (WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_LEFT | ES_AUTOHSCROLL | ES_READONLY)); + RC_CHECK(edit && !(edit->style & WS_BORDER)); + RC_CHECK(edit && edit->x == 103 && edit->y == 66 && edit->cx == 245 && edit->cy == 12); + + const Control* ok = control(*props, IDOK); + RC_CHECK(ok && equals(ok->cls, "Button") && equals(ok->text, "Zamknij")); + RC_CHECK(ok && ok->style == (WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_DEFPUSHBUTTON)); + + const Control* group = controlByText(*props, "Informacje o aktualnie wybranym dokumencie"); + RC_CHECK(group && group->id == -1 && group->style == (WS_CHILD | WS_VISIBLE | BS_GROUPBOX)); + + const Control* label = controlByText(*props, "Nazwa pliku:"); + RC_CHECK(label && equals(label->cls, "Static") && label->style == (WS_CHILD | WS_VISIBLE | WS_GROUP | SS_RIGHT)); + + const Control* temp = controlByText(*props, "Nazwa pliku tymczasowego:"); + RC_CHECK(temp && temp->style == (WS_CHILD | WS_VISIBLE | WS_GROUP | SS_LEFT) && temp->exStyle == WS_EX_RIGHT); + } + + const Dialog* dh = dialog(pl, IDD_DIFFIEHELLMANVISUALIZATION); + RC_CHECK(dh != nullptr); + if (dh) { + RC_CHECK(equals(dh->caption, "Demonstracja protokoÅ‚u Diffie'ego - Hellmana: wizualizacja wymiany kluczy.")); + const Control* bmpButton = controlByText(*dh, "IDB_DH_BUTTON1_R_"); + RC_CHECK(bmpButton && bmpButton->style == (WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_OWNERDRAW | BS_BITMAP)); + const Control* bmpStatic = controlByText(*dh, "#550"); + RC_CHECK(bmpStatic && equals(bmpStatic->cls, "Static") && (bmpStatic->style & SS_TYPEMASK) == SS_BITMAP); + } + + const char* texts[8] = {}; + RC_CHECK(dlgInitTexts(pl, IDD_KEYASYM_GENERATION, IDC_COMBO1, texts, 8) == 4); + RC_CHECK(equals(texts[0], "512") && equals(texts[1], "768") && equals(texts[2], "1024") && equals(texts[3], "2048")); + + const Dialog* keyasym = dialog(pl, IDD_KEYASYM_GENERATION); + const Control* combo = keyasym ? control(*keyasym, IDC_COMBO1) : nullptr; + RC_CHECK(combo && equals(combo->cls, "ComboBox")); + RC_CHECK(combo && (combo->style & 3) == CBS_DROPDOWN); +} + +static void checkEnglish(const Language& en) { + RC_CHECK(en.langId == 0x0809); + RC_CHECK(equals(string(en, IDS_CRYPTOOL_RUNTIME_LANGUAGE_STRING), "English")); + + const AccelTable* acc = accelTable(en, IDR_MAINFRAME); + RC_CHECK(acc != nullptr); + if (acc) { + const Accel* copy = accel(*acc, ID_EDIT_COPY, 'C'); + RC_CHECK(copy && copy->flags == (kFVirtKey | kFControl | kFNoInvert)); + const Accel* help = accel(*acc, ID_CONTEXT_HELP, VK_F1); + RC_CHECK(help && help->flags == (kFVirtKey | kFShift | kFNoInvert)); + const Accel* undo = accel(*acc, ID_EDIT_UNDO, VK_BACK); + RC_CHECK(undo && undo->flags == (kFVirtKey | kFAlt | kFNoInvert)); + } + + const Menu* mainMenu = menu(en, IDR_MAINFRAME); + RC_CHECK(mainMenu != nullptr); + if (mainMenu && mainMenu->itemCount > 3) { + RC_CHECK(mainMenu->items[0].depth == 0 && mainMenu->items[0].flags == kMfPopup); + RC_CHECK(equals(mainMenu->items[0].text, "&File")); + RC_CHECK(mainMenu->items[1].depth == 1 && mainMenu->items[1].id == ID_FILE_NEW); + RC_CHECK(equals(mainMenu->items[1].text, "&New\tCtrl+N")); + RC_CHECK(mainMenu->items[3].id == ID_FILE_CLOSE && mainMenu->items[3].flags == kMfGrayed); + } + + const Toolbar* tb = toolbar(en, IDR_MAINFRAME); + RC_CHECK(tb && tb->buttonWidth == 16 && tb->buttonHeight == 15 && tb->buttonCount == 15); + RC_CHECK(tb && tb->buttons[0] == ID_FILE_NEW && tb->buttons[5] == 0); + + const FileResource* bmp = file(en, "BITMAP", IDR_MAINFRAME, nullptr); + RC_CHECK(bmp && equals(bmp->path, "res/mainfram.bmp")); + const FileResource* named = file(en, "BITMAP", 0, "PASTEU"); + RC_CHECK(named && named->id == 0 && equals(named->path, "res/EditPaste.bmp")); + RC_CHECK(file(en, "ICON", IDR_MAINFRAME, nullptr) != nullptr); + + const Dialog* playfair = dialog(en, IDD_PLAYFAIR_ANALYSIS); + const Control* scroll = playfair ? control(*playfair, IDC_SCROLLBAR1) : nullptr; + RC_CHECK(scroll && equals(scroll->cls, "ScrollBar") && scroll->style == (WS_CHILD | WS_VISIBLE | SBS_HORZ)); + + RC_CHECK(en.version && en.version->fileVersion[0] == 1 && en.version->fileVersion[1] == 4 && + en.version->fileVersion[2] == 31 && en.version->fileVersion[3] == 0); + RC_CHECK(en.version && en.version->keyCount >= 1 && equals(en.version->keys[0], "CompanyName") && + equals(en.version->keys[1], "CrypTool Team")); +} + +static void checkGreek(const Language& el) { + RC_CHECK(el.langId == 0x0408); + RC_CHECK(el.codepage == 1253); + RC_CHECK(contains(string(el, IDS_RSA_LOG_FOUND_NO_SOLUTION), "ί")); +} + +static void checkSwiss(const Language& ch) { + RC_CHECK(ch.langId == 0x0807 && ch.dialogCount == 1); + const Dialog* d = dialog(ch, AFX_IDD_NEWTYPEDLG); + RC_CHECK(d != nullptr); + if (d) { + const Control* list = control(*d, AFX_IDC_LISTBOX); + RC_CHECK(list && equals(list->cls, "ListBox") && + list->style == (WS_CHILD | WS_VISIBLE | WS_BORDER | LBS_NOTIFY | WS_VSCROLL | WS_TABSTOP)); + const Control* label = control(*d, IDC_STATIC); + RC_CHECK(label && label->style == (WS_CHILD | WS_VISIBLE | SS_LEFT)); + } +} + +void runCrypToolChecks() { + const Module& m = g_rcModule_CrypTool; + checkLanguages(m); + if (const Language* pl = language(m, "pl")) + checkPolish(*pl); + else + RC_CHECK(!"Polish language missing"); + if (const Language* en = language(m, "en")) + checkEnglish(*en); + else + RC_CHECK(!"English language missing"); + if (const Language* el = language(m, "el")) + checkGreek(*el); + else + RC_CHECK(!"Greek language missing"); + if (const Language* ch = language(m, "de-CH")) + checkSwiss(*ch); + else + RC_CHECK(!"Swiss German language missing"); +} + +} // namespace rctest diff --git a/port/rc/test/numbershark_checks.cpp b/port/rc/test/numbershark_checks.cpp new file mode 100644 index 000000000..83083872c --- /dev/null +++ b/port/rc/test/numbershark_checks.cpp @@ -0,0 +1,48 @@ +#include "rc_test.h" + +#include "afxres.h" +#include "../../../NumberShark/resource.h" + +namespace rctest { + +void runNumberSharkChecks() { + const Module& m = g_rcModule_NumberShark; + RC_CHECK(m.languageCount == 7); + + const Language* el = language(m, "el"); + RC_CHECK(el && el->codepage == 1253); + if (el) + RC_CHECK(startsWith(string(*el, IDS_RULES), "ΠεÏ�ιγÏ�αφή:\r\n")); + + const Language* pl = language(m, "pl"); + RC_CHECK(pl != nullptr); + if (!pl) + return; + RC_CHECK(startsWith(string(*pl, IDS_RULES), "Opis gry:\n - Na poczÄ…tku gry Number Shark możesz wybrać rozmiar \"shell square\"")); + + const Dialog* about = dialog(*pl, IDD_ABOUTBOX); + RC_CHECK(about && about->fontWeight == 0 && equals(about->fontName, "MS Shell Dlg")); + const Control* icon = about ? controlByText(*about, "#128") : nullptr; + RC_CHECK(icon && equals(icon->cls, "Static") && icon->id == -1); + RC_CHECK(icon && icon->style == (WS_CHILD | WS_VISIBLE | SS_ICON) && icon->cx == 20 && icon->cy == 20); + + const Dialog* options = dialog(*pl, IDD_DIALOG1); + const Control* hidden = options ? control(*options, IDC_BUTTON_MAX) : nullptr; + RC_CHECK(hidden && hidden->style == (WS_CHILD | WS_TABSTOP | BS_PUSHBUTTON | BS_MULTILINE)); + + const Dialog* main = dialog(*pl, IDD_MFCZAHLENHAI_DIALOG); + RC_CHECK(main && main->exStyle == WS_EX_APPWINDOW && (main->style & WS_VISIBLE)); + const Control* shark = main ? control(*main, IDC_STATIC_HAI_LISTE) : nullptr; + RC_CHECK(shark && equals(shark->text, "#169") && (shark->style & SS_TYPEMASK) == SS_BITMAP); + const Control* group = main ? controlByText(*main, "Kurs gry - [\"prime\" oznacza liczbÄ™ pierwszÄ…]") : nullptr; + RC_CHECK(group != nullptr); + + const FileResource* bmp = file(*pl, "BITMAP", IDB_BITMAP_NUMBERSHARK_01, nullptr); + RC_CHECK(bmp && equals(bmp->path, "Bilder/Zahlenhai01.bmp")); + + const AccelTable* acc = accelTable(*pl, IDR_ACCELERATOR1); + const Accel* rest = acc ? accel(*acc, ID_GIVE_REST, 'N') : nullptr; + RC_CHECK(rest && rest->flags == (kFVirtKey | kFShift | kFControl | kFNoInvert)); +} + +} // namespace rctest diff --git a/port/rc/test/rc_test.cpp b/port/rc/test/rc_test.cpp new file mode 100644 index 000000000..2a2ae7fa6 --- /dev/null +++ b/port/rc/test/rc_test.cpp @@ -0,0 +1,186 @@ +#include "rc_test.h" + +#include +#include + +namespace rctest { + +static int g_checks = 0; +static int g_failures = 0; + +void check(bool ok, const char* what, const char* file, int line) { + ++g_checks; + if (!ok) { + ++g_failures; + std::printf("FAIL %s:%d: %s\n", file, line, what); + } +} + +bool equals(const char* a, const char* b) { + return a && b && std::strcmp(a, b) == 0; +} + +bool startsWith(const char* s, const char* prefix) { + return s && prefix && std::strncmp(s, prefix, std::strlen(prefix)) == 0; +} + +bool contains(const char* s, const char* part) { + return s && part && std::strstr(s, part) != nullptr; +} + +const Language* language(const Module& m, const char* code) { + for (int i = 0; i < m.languageCount; ++i) + if (equals(m.languages[i]->code, code)) + return m.languages[i]; + return nullptr; +} + +const Dialog* dialog(const Language& l, int id) { + for (int i = 0; i < l.dialogCount; ++i) + if (l.dialogs[i].id == id && !l.dialogs[i].name) + return &l.dialogs[i]; + return nullptr; +} + +const Control* control(const Dialog& d, int id) { + for (int i = 0; i < d.controlCount; ++i) + if (d.controls[i].id == id) + return &d.controls[i]; + return nullptr; +} + +const Control* controlByText(const Dialog& d, const char* text) { + for (int i = 0; i < d.controlCount; ++i) + if (equals(d.controls[i].text, text)) + return &d.controls[i]; + return nullptr; +} + +const char* string(const Language& l, int id) { + int lo = 0, hi = l.stringCount - 1; + while (lo <= hi) { + int mid = (lo + hi) / 2; + if (l.strings[mid].id == id) + return l.strings[mid].text; + if (l.strings[mid].id < id) + lo = mid + 1; + else + hi = mid - 1; + } + return nullptr; +} + +const Menu* menu(const Language& l, int id) { + for (int i = 0; i < l.menuCount; ++i) + if (l.menus[i].id == id && !l.menus[i].name) + return &l.menus[i]; + return nullptr; +} + +const AccelTable* accelTable(const Language& l, int id) { + for (int i = 0; i < l.accelCount; ++i) + if (l.accels[i].id == id && !l.accels[i].name) + return &l.accels[i]; + return nullptr; +} + +const Accel* accel(const AccelTable& t, int command, int key) { + for (int i = 0; i < t.itemCount; ++i) + if (t.items[i].id == command && t.items[i].key == key) + return &t.items[i]; + return nullptr; +} + +const Toolbar* toolbar(const Language& l, int id) { + for (int i = 0; i < l.toolbarCount; ++i) + if (l.toolbars[i].id == id && !l.toolbars[i].name) + return &l.toolbars[i]; + return nullptr; +} + +const FileResource* file(const Language& l, const char* type, int id, const char* name) { + for (int i = 0; i < l.fileCount; ++i) { + const FileResource& f = l.files[i]; + if (!equals(f.type, type)) + continue; + if (name ? equals(f.name, name) : (f.id == id && !f.name)) + return &f; + } + return nullptr; +} + +int dlgInitTexts(const Language& l, int dialogId, int controlId, const char** out, int max) { + int n = 0; + for (int i = 0; i < l.dlgInitCount; ++i) + if (l.dlgInits[i].dialogId == dialogId && l.dlgInits[i].controlId == controlId && n < max) + out[n++] = l.dlgInits[i].text; + return n; +} + +} // namespace rctest + +using namespace rctest; + +static void printStats(const Module& m) { + std::printf("%s: %d language(s)\n", m.name, m.languageCount); + std::printf(" %-7s %-6s %5s %7s %8s %5s %6s %7s %6s %8s %5s %7s %7s\n", "lang", "langid", "cp", "dialogs", + "controls", "menus", "items", "strings", "accels", "toolbars", "files", "dlginit", "version"); + for (int i = 0; i < m.languageCount; ++i) { + const Language& l = *m.languages[i]; + int controls = 0, items = 0; + for (int d = 0; d < l.dialogCount; ++d) + controls += l.dialogs[d].controlCount; + for (int k = 0; k < l.menuCount; ++k) + items += l.menus[k].itemCount; + std::printf(" %-7s 0x%04X %5d %7d %8d %5d %6d %7d %6d %8d %5d %7d %7s\n", l.code, l.langId, l.codepage, + l.dialogCount, controls, l.menuCount, items, l.stringCount, l.accelCount, l.toolbarCount, + l.fileCount, l.dlgInitCount, l.version ? "yes" : "no"); + } +} + +static void structuralChecks(const Module& m) { + RC_CHECK(m.name != nullptr); + RC_CHECK(m.languageCount > 0); + for (int i = 0; i < m.languageCount; ++i) { + const Language& l = *m.languages[i]; + RC_CHECK(l.code != nullptr); + for (int k = 1; k < l.stringCount; ++k) + RC_CHECK(l.strings[k - 1].id < l.strings[k].id); + for (int k = 0; k < l.stringCount; ++k) + RC_CHECK(l.strings[k].text != nullptr); + for (int d = 0; d < l.dialogCount; ++d) { + const Dialog& dlg = l.dialogs[d]; + RC_CHECK((dlg.controls != nullptr) == (dlg.controlCount > 0)); + RC_CHECK((dlg.id != 0) != (dlg.name != nullptr)); + for (int c = 0; c < dlg.controlCount; ++c) + RC_CHECK(dlg.controls[c].cls != nullptr); + } + for (int k = 0; k < l.menuCount; ++k) { + const Menu& mn = l.menus[k]; + RC_CHECK(mn.itemCount > 0 && mn.items[0].depth == 0); + for (int j = 1; j < mn.itemCount; ++j) + RC_CHECK(mn.items[j].depth <= mn.items[j - 1].depth + 1); + for (int j = 0; j < mn.itemCount; ++j) + RC_CHECK((mn.items[j].text == nullptr) == ((mn.items[j].flags & kMfSeparator) != 0)); + } + for (int k = 0; k < l.fileCount; ++k) { + const FileResource& f = l.files[k]; + RC_CHECK(f.type != nullptr && f.path != nullptr); + RC_CHECK((f.id != 0) != (f.name != nullptr)); + RC_CHECK(std::strchr(f.path, '\\') == nullptr); + } + } +} + +int main() { + const Module* modules[] = {&g_rcModule_CrypTool, &g_rcModule_aestool, &g_rcModule_NumberShark, &g_rcModule_VolRen}; + for (const Module* m : modules) { + printStats(*m); + structuralChecks(*m); + } + runCrypToolChecks(); + runNumberSharkChecks(); + runAesToolVolRenChecks(); + std::printf("%d checks, %d failure(s)\n", g_checks, g_failures); + return g_failures ? 1 : 0; +} diff --git a/port/rc/test/rc_test.h b/port/rc/test/rc_test.h new file mode 100644 index 000000000..6f33dcf39 --- /dev/null +++ b/port/rc/test/rc_test.h @@ -0,0 +1,46 @@ +#pragma once + +#include "mfcwx/rcdata.h" + +extern const mfcwx::rc::Module g_rcModule_CrypTool; +extern const mfcwx::rc::Module g_rcModule_aestool; +extern const mfcwx::rc::Module g_rcModule_NumberShark; +extern const mfcwx::rc::Module g_rcModule_VolRen; + +namespace rctest { + +using namespace mfcwx::rc; + +constexpr uint32_t kFVirtKey = 0x01; +constexpr uint32_t kFNoInvert = 0x02; +constexpr uint32_t kFShift = 0x04; +constexpr uint32_t kFControl = 0x08; +constexpr uint32_t kFAlt = 0x10; +constexpr uint32_t kMfGrayed = 0x01; +constexpr uint32_t kMfPopup = 0x10; +constexpr uint32_t kMfSeparator = 0x800; + +void check(bool ok, const char* what, const char* file, int line); +bool equals(const char* a, const char* b); +bool startsWith(const char* s, const char* prefix); +bool contains(const char* s, const char* part); + +const Language* language(const Module& m, const char* code); +const Dialog* dialog(const Language& l, int id); +const Control* control(const Dialog& d, int id); +const Control* controlByText(const Dialog& d, const char* text); +const char* string(const Language& l, int id); +const Menu* menu(const Language& l, int id); +const AccelTable* accelTable(const Language& l, int id); +const Accel* accel(const AccelTable& t, int command, int key); +const Toolbar* toolbar(const Language& l, int id); +const FileResource* file(const Language& l, const char* type, int id, const char* name); +int dlgInitTexts(const Language& l, int dialogId, int controlId, const char** out, int max); + +void runCrypToolChecks(); +void runNumberSharkChecks(); +void runAesToolVolRenChecks(); + +} // namespace rctest + +#define RC_CHECK(cond) ::rctest::check((cond), #cond, __FILE__, __LINE__) diff --git a/port/secude/CMakeLists.txt b/port/secude/CMakeLists.txt new file mode 100644 index 000000000..d2441f777 --- /dev/null +++ b/port/secude/CMakeLists.txt @@ -0,0 +1,106 @@ +cmake_minimum_required(VERSION 3.16) + +project(secude_compat LANGUAGES C CXX) + +get_filename_component(SECUDE_COMPAT_REPO_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/../.." ABSOLUTE) + +if(NOT TARGET OpenSSL::Crypto) + find_package(OpenSSL 3 REQUIRED) +endif() + +add_library(secude_compat STATIC + src/alloc.cpp + src/error.cpp + src/der.cpp + src/oids.cpp + src/data_flags.cpp + src/structs.cpp + src/encode.cpp + src/dname.cpp + src/lnumber.cpp + src/legacy_hash.cpp + src/legacy_cipher.cpp + src/provider.cpp + src/hash.cpp + src/symcipher.cpp + src/asym.cpp + src/crypt_api.cpp + src/pse.cpp + src/cert.cpp + src/cadb.cpp + src/pkcs12.cpp + src/dump.cpp + src/misc.cpp + src/keystore.cpp + src/symbols.cpp +) + +target_compile_features(secude_compat PRIVATE cxx_std_17 PUBLIC cxx_std_11) +set_target_properties(secude_compat PROPERTIES + CXX_EXTENSIONS OFF + POSITION_INDEPENDENT_CODE ON) + +target_include_directories(secude_compat + PUBLIC + "${SECUDE_COMPAT_REPO_ROOT}/secude" + "${CMAKE_CURRENT_SOURCE_DIR}/include" + PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}/src") + +target_link_libraries(secude_compat PUBLIC OpenSSL::Crypto) + +if(CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU") + target_compile_options(secude_compat PRIVATE -Wall -Wextra -Wno-unused-parameter -Wno-missing-field-initializers) +endif() + +add_executable(secude_compat_mkpse tools/mkpse.cpp) +target_link_libraries(secude_compat_mkpse PRIVATE secude_compat) + +if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) + set(_secude_compat_standalone ON) +else() + set(_secude_compat_standalone OFF) +endif() +option(SECUDE_COMPAT_TESTS "Build the secude_compat tests" ${_secude_compat_standalone}) + +if(SECUDE_COMPAT_TESTS) + enable_testing() + # CrypTool's SecudeLib.h exactly as the app sees it (quote includes resolve next to the header first) + add_library(secude_compat_test_cryptool_binding OBJECT tests/test_binding.cpp) + target_include_directories(secude_compat_test_cryptool_binding PRIVATE + "${SECUDE_COMPAT_REPO_ROOT}/CrypTool" + "${CMAKE_CURRENT_SOURCE_DIR}/tests") + target_link_libraries(secude_compat_test_cryptool_binding PRIVATE secude_compat) + # the application is compiled as C++14 + set_target_properties(secude_compat_test_cryptool_binding PROPERTIES CXX_STANDARD 14 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) + if(CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU") + target_compile_options(secude_compat_test_cryptool_binding PRIVATE -Wno-deprecated-register -Wno-register) + endif() + + add_library(secude_compat_test_libec_binding OBJECT + "${SECUDE_COMPAT_REPO_ROOT}/libec/sources/ECsecude.c" + tests/test_libec_binding.c) + target_include_directories(secude_compat_test_libec_binding PRIVATE + "${SECUDE_COMPAT_REPO_ROOT}/libec/include") + target_link_libraries(secude_compat_test_libec_binding PRIVATE secude_compat) + set_target_properties(secude_compat_test_libec_binding PROPERTIES C_STANDARD 90 C_EXTENSIONS ON) + + add_executable(secude_compat_tests + tests/main.cpp + tests/test_hash.cpp + tests/test_cipher.cpp + tests/test_lnumber.cpp + tests/test_encode.cpp + tests/test_asym.cpp + tests/test_pse.cpp + tests/test_pkcs12.cpp + $ + $) + target_include_directories(secude_compat_tests PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/tests" "${CMAKE_CURRENT_SOURCE_DIR}/src") + target_compile_features(secude_compat_tests PRIVATE cxx_std_17) + target_link_libraries(secude_compat_tests PRIVATE secude_compat) + + add_test(NAME secude_compat_tests COMMAND secude_compat_tests) + add_test(NAME secude_compat_tests_builtin_legacy COMMAND secude_compat_tests) + set_tests_properties(secude_compat_tests_builtin_legacy PROPERTIES ENVIRONMENT "SECUDE_COMPAT_NO_LEGACY_PROVIDER=1") +endif() diff --git a/port/secude/README.md b/port/secude/README.md new file mode 100644 index 000000000..bd1a007fd --- /dev/null +++ b/port/secude/README.md @@ -0,0 +1,156 @@ +# secude_compat — SECUDE replacement for the CrypTool port + +CrypTool 1 on Windows loads the proprietary `SECUDE.DLL`. This static library +implements the subset of the SECUDE SDK 7.4 API that CrypTool and libec use +(every function and data object of the `DoAll` list in +`CrypTool/SecudeLib.h`, plus `global_add_error` and `rabinstest`, which libec +calls directly) on top of OpenSSL 3. The SECUDE headers in `secude/` are used +unchanged apart from small portability guards. + +## Building + +``` +cmake -S port/secude -B build/secude -DOPENSSL_ROOT_DIR=/opt/homebrew/opt/openssl@3 +cmake --build build/secude && ctest --test-dir build/secude +``` + +Targets: `secude_compat` (static library; public include directories `secude/` +and `port/secude/include`, links `OpenSSL::Crypto`), `secude_compat_mkpse` +(key store generator) and, when `SECUDE_COMPAT_TESTS` is on (default for +standalone builds), `secude_compat_tests`. ctest runs the suite twice: with +the OpenSSL legacy provider and with `SECUDE_COMPAT_NO_LEGACY_PROVIDER=1`. + +## Binding + +* `CrypTool/SecudeLib.cpp`: on non-Windows platforms `OpenSecudeLib()` fills + the `CSecudeLib` function/data pointers through `secude_compat_symbol(name)` + (a `GetProcAddress` replacement) instead of loading `SECUDE.DLL`. +* `libec/sources/ECsecude.c`: on non-Windows platforms `ECSecudeLib` is + statically initialised with the library functions, so libec works even + before `CrypToolApp` copies the pointers. + +## Algorithms + +| AlgId | Implementation | +| --- | --- | +| `md2_aid`, `sha_aid` (SHA-0) | built in (not available in OpenSSL 3) | +| `md4_aid`, `ripemd160_aid` | OpenSSL (legacy/default provider), built-in fallback | +| `md5_aid`, `sha1_aid` | OpenSSL | +| `desECB_aid`, `desCBC_pad_aid`, `desEDE_aid`, `desCBC3_aid`, `idea_aid`, `rc2CBC_aid`, `rc4_aid` | OpenSSL legacy provider, built-in fallback | +| `rsa_aid` (2.5.8.1.1, INTEGER keysize), `rsaEncryption_aid` | OpenSSL BIGNUM | +| `dsa_aid` (1.3.14.3.2.12, Dss-Parms) | OpenSSL BIGNUM | + +Notes on behaviour that CrypTool relies on: + +* Hash results are always written to freshly allocated `octets`; input and + output may be the same `OctetString`. +* Symmetric output and RSA en/decryption output are written into the caller's + buffer (`bits`/`octets`) when it is non-NULL (appending at `nbits`/`noctets`), + otherwise allocated. +* SECUDE "std" padding (`desECB`, `desCBC`, `desEDE`, `desCBC3`, `idea`): the + last block is filled with zero octets, its last octet holds the number of + data octets in that block. `desCBC_pad`, `desCBC3_pad`, `desEDE3CBC`, + `ideaCBC`, `rc2CBC` and AES use PKCS#5 padding. IVs default to zero; RC2 + uses the key length as effective key bits unless the parameter says + otherwise; `idea_aid` is ECB. +* Bad padding fails with `EDECRYPTION` (1792), which the brute-force analysis + treats as "wrong key". +* RSA encryption is textbook RSA on blocks of (bits-1)/8 octets (the last + block is zero padded on the right), each encrypted into (bits+7)/8 octets; + decryption yields (bits-1)/8 octets per block. This is what the hybrid + encryption and side-channel demos expect. +* RSA signatures are PKCS#1 v1.5 with the DigestInfo encodings listed in + `CrypTool/PSEDemo.cpp`; DSA signatures are DER `SEQUENCE { r, s }`. +* RSA private keys are stored like SECUDE does: `KeyBits { p, q }`. The public + exponent is taken from the PSE certificate, 65537 otherwise. +* `L_NUMBER` is the 32-bit `sec_uint4` of the headers on every platform: + `a[0]` holds the number of words (two's complement for negative values), + `a[1..n]` the magnitude, least significant word first. + +## File formats + +### PSE (`*.pse`, CA: `capse.cse`) + +DER encoded, encrypted with AES-256-GCM under a PBKDF2-HMAC-SHA256 key +(100000 iterations, 16 octet salt) derived from the PIN: + +``` +CompatPSE ::= SEQUENCE { + format UTF8String "SECUDE-compat PSE", + version INTEGER (1), + kdf SEQUENCE { pbkdf2 OID, salt OCTET STRING, iterations INTEGER, hmacWithSHA256 OID }, + cipher SEQUENCE { aes256-GCM OID, nonce OCTET STRING }, + ciphertext OCTET STRING } -- ciphertext || tag, AAD = DER of the fields above +PSEContents ::= SEQUENCE { flags INTEGER (1 = one key pair only), created Time, + objects SEQUENCE OF PSEObject } +PSEObject ::= SEQUENCE { name UTF8String, type OID, created Time, updated Time, + value OCTET STRING } +``` + +A wrong PIN fails with `EPIN`, a file in another format with `EPSEFILE`. +Object types use the private arc `2.206.5.1` (`SKnew` .1, `SKold` .2, +`SignSK` .3, `DecSKnew` .4, `DecSKold` .5, `AuthSK` .6, `Cert` .10, +`SignCert` .11, `EncCert` .12, `AuthCert` .13, `PKRoot` .20, `FCPath` .21, +`SerialNumber` .30, `Name` .31, `Uid` .40). Values: certificates as X.509 +DER, keys as `KeyInfo` (SubjectPublicKeyInfo layout), `PKRoot` and `FCPath` +as below, anything else (e.g. `EcPrivKey` of type `Uid`) as raw octets. + +``` +PKRoot ::= SEQUENCE { ca Name, newkey SerialKey, oldkey [0] SerialKey OPTIONAL, + extensions [3] Extensions OPTIONAL } +SerialKey ::= SEQUENCE { serial INTEGER, version INTEGER, key SubjectPublicKeyInfo, + validity Validity OPTIONAL, + signature [1] SEQUENCE { AlgorithmIdentifier, BIT STRING } OPTIONAL } +FCPath ::= SEQUENCE OF SET OF Certificate +``` + +### CA database (`/cadb.der`) + +``` +CADatabase ::= SEQUENCE { version INTEGER (1), entries SEQUENCE OF + SEQUENCE { keyType INTEGER, issued Time, certificate Certificate } } +``` + +Users are looked up by subject DN (case-insensitive, whitespace-normalised +comparison), certificates by serial number. Name strings are written least +significant RDN first (`CN=..., DC=cryptool, DC=org`) as in SECUDE. + +## Key store + +`secude_compat_mkpse ` (or `secude_compat_create_sample_keystore()`) +creates the files the installer ships, in the layout CrypTool expects: + +``` +/PSE/PSECA/capse.cse CA PSE "CN=CrypTool CA 2, DC=cryptool, DC=org", + RSA-2048, PIN 3.14159265358979323844 +/PSE/PSECA/cadb.der CA database +/PSE/[SideChannelAttack][Bob][RSA-512][1152179494][PIN=1234].pse +/PSE/[HybridEncryption][Bob][EC-prime239v1][1178702474][PIN=1234] public EC data (text) +/PSE/[HybridEncryption][Bob][EC-prime239v1][1178702474][PIN=1234].pse EcPrivKey +``` + +The keys are newly generated (the SECUDE-encrypted originals cannot be read); +names, DNs and PINs match the originals. The EC text file has the format +written by `CKeyFile::CreateEcKeyFiles` (a, b, p, Gx, Gy, cofactor, order, +Qx, Qy as `0X...` hex lines). `secude_compat_create_ca()` creates only the +CA (for a first-run setup). + +## Not supported + +Smart cards (`SC_DATA` is ignored), revocation lists +(`af_pse_get_CrlSet` fails with `ENOCRL`), subject alternative names in +`af_create_Certificate` and PKCS#8 attributes in `aux_create_PrivateKeyInfo` +(both fail with `ENOTSUPPORTED`), and the SQMODN hash (`HashInput` is ignored). + +## Integration notes + +* Do not include OpenSSL headers and SECUDE headers in the same translation + unit: SECUDE's `RSA`, `DSA`, `SHA1`, ... enumerators and its BIO/SSL + declarations clash with OpenSSL (see `src/internal.hpp` for the renaming + this library uses internally). +* `CrypTool/SecudeLib.h` uses `register` in its prototypes: fine in C++14 + (deprecation warning), an error in C++17 unless `-Wno-register` is used. +* `CrypToolApp.cpp` builds the key store paths with backslashes + (`PSE\\PSECA\\capse.cse`); use `/` on other platforms. Its + `ECSecudeLib.##c = SecudeLib.##c` loop is not valid outside MSVC and can be + dropped there (`ECsecude.c` already initialises the table). diff --git a/port/secude/include/secude_compat.h b/port/secude/include/secude_compat.h new file mode 100644 index 000000000..eb3bdcf62 --- /dev/null +++ b/port/secude/include/secude_compat.h @@ -0,0 +1,52 @@ +/* + * Open-source replacement for the subset of the SECUDE SDK used by CrypTool, + * implemented on top of OpenSSL 3. All SECUDE functions and data objects are + * exported under their original names (C linkage); this header only declares + * the additional entry points of the compatibility layer. + */ +#ifndef SECUDE_COMPAT_H +#define SECUDE_COMPAT_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* GetProcAddress()-like lookup of a SECUDE function or data object by its + undecorated name (e.g. "af_open", "rsa_aid"). Returns NULL if unknown. */ +void *secude_compat_symbol(const char *name); + +/* Number of entries in the symbol table and access by index (for tests). */ +int secude_compat_symbol_count(void); +const char *secude_compat_symbol_name(int index); + +/* Human readable identification of this library. */ +const char *secude_compat_version(void); + +/* 1 if the OpenSSL legacy provider is used for MD4/DES/IDEA/RC2/RC4, + 0 if the built-in implementations are used instead. */ +int secude_compat_legacy_provider_active(void); + +/* Default PIN of the CrypTool CA PSE (PSEUDO_MASTER_CA_PINNR). */ +#define SECUDE_COMPAT_CA_PIN "3.14159265358979323844" +#define SECUDE_COMPAT_CA_NAME "CN=CrypTool CA 2, DC=cryptool, DC=org" + +/* Creates a CA PSE (RSA key, self-signed certificate, PKRoot, SerialNo) at + ca_pse_path and an empty CA database in ca_dir. Returns 0 on success. */ +int secude_compat_create_ca(const char *ca_pse_path, const char *ca_dir, + const char *pin, const char *ca_name, + int key_bits, int validity_days); + +/* Creates the complete key store below key_store_root: + /PSE/PSECA/capse.cse CA PSE (PIN SECUDE_COMPAT_CA_PIN) + /PSE/PSECA/cadb.der CA database + /PSE/[SideChannelAttack][Bob][RSA-512][1152179494][PIN=1234].pse + /PSE/[HybridEncryption][Bob][EC-prime239v1][1178702474][PIN=1234](.pse) + Existing files are left untouched unless overwrite is non-zero. + Returns 0 on success. */ +int secude_compat_create_sample_keystore(const char *key_store_root, int overwrite); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/port/secude/src/alloc.cpp b/port/secude/src/alloc.cpp new file mode 100644 index 000000000..01b1a0c97 --- /dev/null +++ b/port/secude/src/alloc.cpp @@ -0,0 +1,268 @@ +#include "internal.hpp" + +#include +#include + +namespace compat { + +namespace { + +struct Registry { + std::mutex lock; + std::unordered_set live; +}; + +Registry ®istry() +{ + static Registry *r = new Registry; + return *r; +} + +} // namespace + +void *mem_alloc(size_t n) +{ + void *p = std::calloc(1, n ? n : 1); + if (!p) + throw std::bad_alloc(); + Registry &r = registry(); + std::lock_guard g(r.lock); + r.live.insert(p); + return p; +} + +void *mem_realloc(void *p, size_t n) +{ + if (!p) + return mem_alloc(n); + Registry &r = registry(); + std::lock_guard g(r.lock); + if (!r.live.count(p)) + throw Error(EINVALID, "realloc of foreign memory"); + void *q = std::realloc(p, n ? n : 1); + if (!q) + throw std::bad_alloc(); + r.live.erase(p); + r.live.insert(q); + return q; +} + +void mem_free(void *p) +{ + if (!p) + return; + Registry &r = registry(); + { + std::lock_guard g(r.lock); + if (!r.live.erase(p)) + return; + } + std::free(p); +} + +bool mem_owned(const void *p) +{ + if (!p) + return false; + Registry &r = registry(); + std::lock_guard g(r.lock); + return r.live.count(const_cast(p)) != 0; +} + +char *mem_strdup(const char *s) +{ + if (!s) + return nullptr; + size_t n = std::strlen(s); + char *d = static_cast(mem_alloc(n + 1)); + std::memcpy(d, s, n); + return d; +} + +char *mem_strdup(const std::string &s) +{ + char *d = static_cast(mem_alloc(s.size() + 1)); + std::memcpy(d, s.data(), s.size()); + return d; +} + +void fill_ostr(OctetString *o, const uint8_t *data, size_t len) +{ + char *buf = static_cast(mem_alloc(len + 1)); + if (len) + std::memcpy(buf, data, len); + o->octets = buf; + o->noctets = static_cast(len); +} + +OctetString *new_ostr(const uint8_t *data, size_t len) +{ + OctetString *o = mem_new(); + fill_ostr(o, data, len); + return o; +} + +void fill_bstr(BitString *o, const Bytes &b, size_t nbits) +{ + char *buf = static_cast(mem_alloc(b.size() + 1)); + if (!b.empty()) + std::memcpy(buf, b.data(), b.size()); + o->bits = buf; + o->nbits = static_cast(nbits); +} + +BitString *new_bstr(const Bytes &b, size_t nbits) +{ + BitString *o = mem_new(); + fill_bstr(o, b, nbits); + return o; +} + +Bytes bytes_of(const OctetString *o) +{ + if (!o || !o->octets || !o->noctets) + return {}; + const uint8_t *p = reinterpret_cast(o->octets); + return Bytes(p, p + o->noctets); +} + +Bytes bytes_of(const BitString *b) +{ + if (!b || !b->bits || !b->nbits) + return {}; + const uint8_t *p = reinterpret_cast(b->bits); + return Bytes(p, p + (b->nbits + 7) / 8); +} + +Bytes bytes_of(const char *s) +{ + if (!s) + return {}; + return Bytes(s, s + std::strlen(s)); +} + +} // namespace compat + +using namespace compat; + +extern "C" { + +void *aux_malloc(size_t size) +{ + return guarded("aux_malloc", nullptr, [&] { return mem_alloc(size); }); +} + +void *aux_calloc(size_t nr, size_t size) +{ + return guarded("aux_calloc", nullptr, [&]() -> void * { + if (size && nr > SIZE_MAX / size) + throw std::bad_alloc(); + return mem_alloc(nr * size); + }); +} + +void aux_free(void *ptr) +{ + mem_free(ptr); +} + +char *aux_cpy_String(char *str) +{ + return guarded("aux_cpy_String", nullptr, [&] { return mem_strdup(str); }); +} + +void aux_free_String(char **str) +{ + if (!str) + return; + mem_free(*str); + *str = nullptr; +} + +OctetString *aux_alloc_OctetString(void) +{ + return guarded("aux_alloc_OctetString", nullptr, [] { return mem_new(); }); +} + +OctetString *aux_cpy_OctetString(OctetString *ostr) +{ + return guarded("aux_cpy_OctetString", nullptr, [&]() -> OctetString * { + if (!ostr) + return nullptr; + return new_ostr(bytes_of(ostr)); + }); +} + +void aux_free2_OctetString(OctetString *ostr) +{ + if (!ostr) + return; + mem_free(ostr->octets); + ostr->octets = nullptr; + ostr->noctets = 0; +} + +void aux_free_OctetString(OctetString **ostr) +{ + if (!ostr || !*ostr) + return; + if (!mem_owned(*ostr)) { + *ostr = nullptr; + return; + } + aux_free2_OctetString(*ostr); + mem_free(*ostr); + *ostr = nullptr; +} + +int aux_cpy2_BitString(BitString *dup_bstr, BitString *bstr) +{ + return guarded("aux_cpy2_BitString", -1, [&] { + if (!dup_bstr || !bstr) + fail(EINVALID, "missing parameter"); + fill_bstr(dup_bstr, bytes_of(bstr), bstr->nbits); + return 0; + }); +} + +void aux_free2_BitString(BitString *bstr) +{ + if (!bstr) + return; + mem_free(bstr->bits); + bstr->bits = nullptr; + bstr->nbits = 0; +} + +void aux_free_BitString(BitString **bstr) +{ + if (!bstr || !*bstr) + return; + if (!mem_owned(*bstr)) { + *bstr = nullptr; + return; + } + aux_free2_BitString(*bstr); + mem_free(*bstr); + *bstr = nullptr; +} + +OctetString *aux_BString2OString(BitString *bstr) +{ + return guarded("aux_BString2OString", nullptr, [&]() -> OctetString * { + if (!bstr) + fail(EINVALID, "missing BitString"); + return new_ostr(bytes_of(bstr)); + }); +} + +BitString *aux_OString2BString(OctetString *ostr) +{ + return guarded("aux_OString2BString", nullptr, [&]() -> BitString * { + if (!ostr) + fail(EINVALID, "missing OctetString"); + return new_bstr(bytes_of(ostr), size_t(ostr->noctets) * 8); + }); +} + +} // extern "C" diff --git a/port/secude/src/asym.cpp b/port/secude/src/asym.cpp new file mode 100644 index 000000000..91e34d76b --- /dev/null +++ b/port/secude/src/asym.cpp @@ -0,0 +1,409 @@ +// RSA and DSA with SECUDE key representations: +// RSA public KeyInfo: rsa (2.5.8.1.1, INTEGER keysize), KeyBits {n, e} +// RSA private KeyInfo: rsa, KeyBits {p, q} (or {-, -, n, d}); e from the +// PSE certificate, 65537 otherwise +// DSA public/private: dsa (1.3.14.3.2.12, KeyBits {p, q, g}), INTEGER y / x +#include "bn.hpp" +#include "der.hpp" + +namespace compat { + +namespace { + +const unsigned long kF4 = 65537; + +KeyBits *keybits_of(const KeyInfo *k) +{ + if (!k) + fail(EINVALID, "missing key"); + return dec_keybits(bytes_of(&k->subjectkey)); +} + +struct KeyBitsHolder { + KeyBits *k; + explicit KeyBitsHolder(KeyBits *kb) : k(kb) {} + ~KeyBitsHolder() + { + free_keybits_content(k); + mem_free(k); + } + KeyBitsHolder(const KeyBitsHolder &) = delete; + KeyBitsHolder &operator=(const KeyBitsHolder &) = delete; +}; + +AlgEnc enc_of(const AlgId *a) +{ + const AlgInfo *i = a ? alg_by_oid(a->objid) : nullptr; + return i ? i->enc : NoAlgEnc; +} + +AlgId *rsa_algid(int bits) +{ + AlgId *a = mem_new(); + a->objid = copy_objid(rsa_aid.objid); + unsigned int *p = mem_new(); + *p = unsigned(bits); + a->param = p; + return a; +} + +KeyInfo *keyinfo_from_parts(AlgId *alg, std::initializer_list parts) +{ + KeyBits kb{}; + OctetString *dst[] = {&kb.part1, &kb.part2, &kb.part3, &kb.part4, &kb.part5}; + int i = 0; + for (const Bytes *p : parts) + fill_ostr(dst[i++], *p); + kb.choice = i; + Bytes der_bytes; + try { + der_bytes = enc_keybits(&kb); + } catch (...) { + free_keybits_content(&kb); + throw; + } + free_keybits_content(&kb); + KeyInfo *k = mem_new(); + k->subjectAI = alg; + fill_bstr(&k->subjectkey, der_bytes, der_bytes.size() * 8); + return k; +} + +Bn generate_prime(int bits, const BIGNUM *add = nullptr, const BIGNUM *rem = nullptr) +{ + Bn p = bn_new(); + bn_check(BN_generate_prime_ex2(p.get(), bits, 0, add, rem, nullptr, bn_ctx())); + return p; +} + +Bytes digest_info(Hash h, const Bytes &hash) +{ + static const struct { + Hash h; + const Oid *oid; + } map[] = {{Hash::MD2, &oids::md2}, {Hash::MD4, &oids::md4}, {Hash::MD5, &oids::md5}, + {Hash::SHA0, &oids::sha}, {Hash::SHA1, &oids::sha1}, {Hash::RIPEMD160, &oids::ripemd160}, + {Hash::SHA256, &oids::sha256}, {Hash::SHA384, &oids::sha384}, {Hash::SHA512, &oids::sha512}}; + for (const auto &m : map) + if (m.h == h) + return der::seq({der::seq({der::oid(*m.oid), der::null()}), der::octet_string(hash)}); + fail(EUNKNOWNALGID, "no DigestInfo for hash"); +} + +Bytes pkcs1_type1(const Bytes &t, size_t k) +{ + if (t.size() + 11 > k) + fail(EKEYSIZE, "RSA key too short for this signature algorithm"); + Bytes em(k, 0xff); + em[0] = 0x00; + em[1] = 0x01; + em[k - t.size() - 1] = 0x00; + std::memcpy(em.data() + k - t.size(), t.data(), t.size()); + return em; +} + +struct DsaKey { + Bn p, q, g, key; +}; + +DsaKey dsa_key(const KeyInfo *k) +{ + if (!k || !k->subjectAI || !k->subjectAI->param) + fail(EALGNOTFITKEY, "DSA key without domain parameters"); + auto *params = static_cast(k->subjectAI->param); + Bytes key_der = bytes_of(&k->subjectkey); + der::Node n = der::parse_one(key_der); + DsaKey d{bn_from(bytes_of(¶ms->part1)), bn_from(bytes_of(¶ms->part2)), bn_from(bytes_of(¶ms->part3)), + bn_from(der::get_unsigned(n))}; + if (BN_is_zero(d.q.get()) || BN_is_zero(d.p.get())) + fail(EALGNOTFITKEY, "invalid DSA domain parameters"); + return d; +} + +Bn dsa_message(const DsaKey &k, Hash h, const uint8_t *msg, size_t n) +{ + Bytes hv = digest(h, msg, n); + Bn z = bn_from(hv); + int qbits = BN_num_bits(k.q.get()); + int hbits = int(hv.size() * 8); + if (hbits > qbits) + bn_check(BN_rshift(z.get(), z.get(), hbits - qbits)); + return z; +} + +Bn mod_exp(const BIGNUM *b, const BIGNUM *e, const BIGNUM *m) +{ + Bn r = bn_new(); + bn_check(BN_mod_exp(r.get(), b, e, m, bn_ctx())); + return r; +} + +Bn rsa_private_op(const RsaPrivate &k, const BIGNUM *c) +{ + Bn n = bn_from(k.n); + if (BN_cmp(c, n.get()) >= 0) + fail(EDECRYPTION, "ciphertext block out of range"); + if (k.p.empty() || k.q.empty()) + return mod_exp(c, bn_from(k.d).get(), n.get()); + Bn p = bn_from(k.p), q = bn_from(k.q), d = bn_from(k.d); + Bn one = bn_word(1), pm1 = bn_new(), qm1 = bn_new(), dp = bn_new(), dq = bn_new(), qinv = bn_new(); + bn_check(BN_sub(pm1.get(), p.get(), one.get())); + bn_check(BN_sub(qm1.get(), q.get(), one.get())); + bn_check(BN_mod(dp.get(), d.get(), pm1.get(), bn_ctx())); + bn_check(BN_mod(dq.get(), d.get(), qm1.get(), bn_ctx())); + if (!BN_mod_inverse(qinv.get(), q.get(), p.get(), bn_ctx())) + fail(EALGNOTFITKEY, "invalid RSA primes"); + Bn m1 = bn_new(), m2 = bn_new(), h = bn_new(), r = bn_new(); + bn_check(BN_mod_exp_mont_consttime(m1.get(), c, dp.get(), p.get(), bn_ctx(), nullptr)); + bn_check(BN_mod_exp_mont_consttime(m2.get(), c, dq.get(), q.get(), bn_ctx(), nullptr)); + bn_check(BN_mod_sub(h.get(), m1.get(), m2.get(), p.get(), bn_ctx())); + bn_check(BN_mod_mul(h.get(), h.get(), qinv.get(), p.get(), bn_ctx())); + bn_check(BN_mul(r.get(), h.get(), q.get(), bn_ctx())); + bn_check(BN_add(r.get(), r.get(), m2.get())); + return r; +} + +} // namespace + +bool keyinfo_is_rsa(const KeyInfo *k) { return k && enc_of(k->subjectAI) == SECUDE_ALG_RSA; } + +bool keyinfo_is_dsa(const KeyInfo *k) { return k && enc_of(k->subjectAI) == SECUDE_ALG_DSA; } + +int rsa_bits(const Bytes &n) { return BN_num_bits(bn_from(n).get()); } + +RsaPublic rsa_public_from_keyinfo(const KeyInfo *k) +{ + if (!keyinfo_is_rsa(k)) + fail(EALGNOTFITKEY, "not an RSA key"); + KeyBitsHolder kb(keybits_of(k)); + if (!kb.k->part1.noctets || !kb.k->part2.noctets) + fail(EALGNOTFITKEY, "incomplete RSA public key"); + return {bytes_of(&kb.k->part1), bytes_of(&kb.k->part2)}; +} + +RsaPrivate rsa_private_from_keyinfo(const KeyInfo *k, const Bytes &e_hint) +{ + if (!keyinfo_is_rsa(k)) + fail(EALGNOTFITKEY, "not an RSA key"); + KeyBitsHolder kb(keybits_of(k)); + RsaPrivate r; + Bn e = e_hint.empty() ? bn_word(kF4) : bn_from(e_hint); + r.e = bn_bytes(e.get()); + if (kb.k->part1.noctets && kb.k->part2.noctets) { + r.p = bytes_of(&kb.k->part1); + r.q = bytes_of(&kb.k->part2); + Bn p = bn_from(r.p), q = bn_from(r.q), n = bn_new(), one = bn_word(1); + Bn pm1 = bn_new(), qm1 = bn_new(), phi = bn_new(), d = bn_new(); + bn_check(BN_mul(n.get(), p.get(), q.get(), bn_ctx())); + bn_check(BN_sub(pm1.get(), p.get(), one.get())); + bn_check(BN_sub(qm1.get(), q.get(), one.get())); + bn_check(BN_mul(phi.get(), pm1.get(), qm1.get(), bn_ctx())); + if (!BN_mod_inverse(d.get(), e.get(), phi.get(), bn_ctx())) + fail(EALGNOTFITKEY, "public exponent does not fit the RSA primes"); + r.n = bn_bytes(n.get()); + r.d = bn_bytes(d.get()); + } else if (kb.k->part3.noctets && kb.k->part4.noctets) { + r.n = bytes_of(&kb.k->part3); + r.d = bytes_of(&kb.k->part4); + } else { + fail(EALGNOTFITKEY, "incomplete RSA private key"); + } + return r; +} + +void rsa_generate(int bits, KeyInfo **pub, KeyInfo **priv) +{ + if (bits < MIN_ASYM_KEYSIZE || bits > MAXKEYLENGTH) + fail(EKEYSIZE, str_printf("unsupported RSA key size %d", bits)); + Bn e = bn_word(kF4), one = bn_word(1); + int pbits = (bits + 1) / 2, qbits = bits - pbits; + for (;;) { + Bn p = generate_prime(pbits), q = generate_prime(qbits); + Bn pm1 = bn_new(), qm1 = bn_new(), g1 = bn_new(), g2 = bn_new(), n = bn_new(); + bn_check(BN_sub(pm1.get(), p.get(), one.get())); + bn_check(BN_sub(qm1.get(), q.get(), one.get())); + bn_check(BN_gcd(g1.get(), pm1.get(), e.get(), bn_ctx())); + bn_check(BN_gcd(g2.get(), qm1.get(), e.get(), bn_ctx())); + bn_check(BN_mul(n.get(), p.get(), q.get(), bn_ctx())); + if (!BN_is_one(g1.get()) || !BN_is_one(g2.get()) || !BN_cmp(p.get(), q.get()) || BN_num_bits(n.get()) != bits) + continue; + Bytes nb = bn_bytes(n.get()), eb = bn_bytes(e.get()), pb = bn_bytes(p.get()), qb = bn_bytes(q.get()); + *pub = keyinfo_from_parts(rsa_algid(bits), {&nb, &eb}); + try { + *priv = keyinfo_from_parts(rsa_algid(bits), {&pb, &qb}); + } catch (...) { + free_keyinfo(*pub); + *pub = nullptr; + throw; + } + return; + } +} + +void dsa_generate(int bits, KeyInfo **pub, KeyInfo **priv) +{ + if (bits < 320 || bits > MAXKEYLENGTH) + fail(EKEYSIZE, str_printf("unsupported DSA key size %d", bits)); + Bn q = generate_prime(160); + Bn add = bn_new(), rem = bn_word(1); + bn_check(BN_lshift1(add.get(), q.get())); + Bn p = generate_prime(bits, add.get(), rem.get()); + Bn pm1 = bn_new(), e = bn_new(), g = bn_new(), one = bn_word(1); + bn_check(BN_sub(pm1.get(), p.get(), one.get())); + bn_check(BN_div(e.get(), nullptr, pm1.get(), q.get(), bn_ctx())); + for (unsigned long h = 2;; ++h) { + g = mod_exp(bn_word(h).get(), e.get(), p.get()); + if (!BN_is_one(g.get())) + break; + } + Bn x = bn_new(), y; + do { + bn_check(BN_priv_rand_range(x.get(), q.get())); + } while (BN_is_zero(x.get())); + y = mod_exp(g.get(), x.get(), p.get()); + + auto make = [&](const BIGNUM *value) { + KeyBits *params = mem_new(); + fill_ostr(¶ms->part1, bn_bytes(p.get())); + fill_ostr(¶ms->part2, bn_bytes(q.get())); + fill_ostr(¶ms->part3, bn_bytes(g.get())); + params->choice = 3; + KeyInfo *k = mem_new(); + k->subjectAI = mem_new(); + k->subjectAI->objid = copy_objid(dsa_aid.objid); + k->subjectAI->param = params; + Bytes v = der::integer_unsigned(bn_bytes(value)); + fill_bstr(&k->subjectkey, v, v.size() * 8); + return k; + }; + *pub = make(y.get()); + *priv = make(x.get()); +} + +Bytes rsa_encrypt_blocks(const RsaPublic &k, const uint8_t *p, size_t n) +{ + int bits = rsa_bits(k.n); + size_t in_block = size_t(bits - 1) / 8, out_block = size_t(bits + 7) / 8; + if (in_block == 0) + fail(EKEYSIZE, "RSA modulus too small"); + Bn modulus = bn_from(k.n), e = bn_from(k.e); + Bytes out; + for (size_t off = 0; off < n; off += in_block) { + Bytes block(in_block, 0); + std::memcpy(block.data(), p + off, std::min(in_block, n - off)); + Bn c = mod_exp(bn_from(block).get(), e.get(), modulus.get()); + der::append(out, bn_bytes(c.get(), out_block)); + } + return out; +} + +Bytes rsa_decrypt_blocks(const RsaPrivate &k, const uint8_t *p, size_t n) +{ + int bits = rsa_bits(k.n); + size_t out_block = size_t(bits - 1) / 8, in_block = size_t(bits + 7) / 8; + if (n % in_block) + fail(EDECRYPTION, "ciphertext length does not match the RSA modulus"); + Bytes out; + for (size_t off = 0; off < n; off += in_block) { + Bn m = rsa_private_op(k, bn_from(Bytes(p + off, p + off + in_block)).get()); + der::append(out, bn_bytes(m.get(), out_block)); + } + return out; +} + +Bytes rsa_raw_private(const RsaPrivate &k, const Bytes &m) +{ + size_t size = size_t(rsa_bits(k.n) + 7) / 8; + return bn_bytes(rsa_private_op(k, bn_from(m).get()).get(), size); +} + +Bytes rsa_private_key_der(const RsaPrivate &k) +{ + if (k.p.empty() || k.q.empty()) + fail(EALGNOTFITKEY, "RSA private key without primes"); + Bn p = bn_from(k.p), q = bn_from(k.q), d = bn_from(k.d), one = bn_word(1); + Bn pm1 = bn_new(), qm1 = bn_new(), dp = bn_new(), dq = bn_new(), qinv = bn_new(); + bn_check(BN_sub(pm1.get(), p.get(), one.get())); + bn_check(BN_sub(qm1.get(), q.get(), one.get())); + bn_check(BN_mod(dp.get(), d.get(), pm1.get(), bn_ctx())); + bn_check(BN_mod(dq.get(), d.get(), qm1.get(), bn_ctx())); + if (!BN_mod_inverse(qinv.get(), q.get(), p.get(), bn_ctx())) + fail(EALGNOTFITKEY, "invalid RSA primes"); + return der::seq({der::integer(0), der::integer_unsigned(k.n), der::integer_unsigned(k.e), der::integer_unsigned(k.d), + der::integer_unsigned(k.p), der::integer_unsigned(k.q), der::integer_unsigned(bn_bytes(dp.get())), + der::integer_unsigned(bn_bytes(dq.get())), der::integer_unsigned(bn_bytes(qinv.get()))}); +} + +Bytes sign_message(const KeyInfo *priv, const Bytes &e_hint, const AlgId *sig_alg, const uint8_t *msg, size_t n) +{ + const AlgInfo *a = sig_alg ? alg_by_oid(sig_alg->objid) : nullptr; + if (!a || a->type != SIG || a->hash == Hash::None) + fail(EUNKNOWNALGID, "not a signature algorithm"); + if (a->enc == SECUDE_ALG_RSA) { + if (!keyinfo_is_rsa(priv)) + fail(EALGNOTFITKEY, "RSA signature algorithm needs an RSA key"); + RsaPrivate k = rsa_private_from_keyinfo(priv, e_hint); + size_t size = size_t(rsa_bits(k.n) + 7) / 8; + return rsa_raw_private(k, pkcs1_type1(digest_info(a->hash, digest(a->hash, msg, n)), size)); + } + if (!keyinfo_is_dsa(priv)) + fail(EALGNOTFITKEY, "DSA signature algorithm needs a DSA key"); + DsaKey k = dsa_key(priv); + Bn z = dsa_message(k, a->hash, msg, n); + for (;;) { + Bn nonce = bn_new(), kinv = bn_new(), r = bn_new(), s = bn_new(); + bn_check(BN_priv_rand_range(nonce.get(), k.q.get())); + if (BN_is_zero(nonce.get())) + continue; + Bn gk = mod_exp(k.g.get(), nonce.get(), k.p.get()); + bn_check(BN_nnmod(r.get(), gk.get(), k.q.get(), bn_ctx())); + if (BN_is_zero(r.get()) || !BN_mod_inverse(kinv.get(), nonce.get(), k.q.get(), bn_ctx())) + continue; + bn_check(BN_mod_mul(s.get(), k.key.get(), r.get(), k.q.get(), bn_ctx())); + bn_check(BN_mod_add(s.get(), s.get(), z.get(), k.q.get(), bn_ctx())); + bn_check(BN_mod_mul(s.get(), s.get(), kinv.get(), k.q.get(), bn_ctx())); + if (BN_is_zero(s.get())) + continue; + return der::seq({der::integer_unsigned(bn_bytes(r.get())), der::integer_unsigned(bn_bytes(s.get()))}); + } +} + +bool verify_message(const KeyInfo *pub, const AlgId *sig_alg, const uint8_t *msg, size_t n, const Bytes &sig) +{ + const AlgInfo *a = sig_alg ? alg_by_oid(sig_alg->objid) : nullptr; + if (!a || a->type != SIG || a->hash == Hash::None) + fail(EUNKNOWNALGID, "not a signature algorithm"); + if (a->enc == SECUDE_ALG_RSA) { + if (!keyinfo_is_rsa(pub)) + fail(EALGNOTFITKEY, "RSA signature algorithm needs an RSA key"); + RsaPublic k = rsa_public_from_keyinfo(pub); + Bn modulus = bn_from(k.n), s = bn_from(sig); + if (BN_cmp(s.get(), modulus.get()) >= 0) + return false; + size_t size = size_t(rsa_bits(k.n) + 7) / 8; + Bytes em = bn_bytes(mod_exp(s.get(), bn_from(k.e).get(), modulus.get()).get(), size); + return em == pkcs1_type1(digest_info(a->hash, digest(a->hash, msg, n)), size); + } + if (!keyinfo_is_dsa(pub)) + fail(EALGNOTFITKEY, "DSA signature algorithm needs a DSA key"); + DsaKey k = dsa_key(pub); + der::Node node = der::parse_one(sig); + if (node.tag != der::SEQUENCE) + return false; + der::Reader rd(node); + Bn r = bn_from(der::get_unsigned(rd.next(der::INTEGER))), s = bn_from(der::get_unsigned(rd.next(der::INTEGER))); + rd.expect_end(); + if (BN_is_zero(r.get()) || BN_is_zero(s.get()) || BN_cmp(r.get(), k.q.get()) >= 0 || BN_cmp(s.get(), k.q.get()) >= 0) + return false; + Bn z = dsa_message(k, a->hash, msg, n); + Bn w = bn_new(), u1 = bn_new(), u2 = bn_new(), v = bn_new(); + if (!BN_mod_inverse(w.get(), s.get(), k.q.get(), bn_ctx())) + return false; + bn_check(BN_mod_mul(u1.get(), z.get(), w.get(), k.q.get(), bn_ctx())); + bn_check(BN_mod_mul(u2.get(), r.get(), w.get(), k.q.get(), bn_ctx())); + bn_check(BN_mod_exp2_mont(v.get(), k.g.get(), u1.get(), k.key.get(), u2.get(), k.p.get(), bn_ctx(), nullptr)); + bn_check(BN_nnmod(v.get(), v.get(), k.q.get(), bn_ctx())); + return BN_cmp(v.get(), r.get()) == 0; +} + +} // namespace compat diff --git a/port/secude/src/bn.hpp b/port/secude/src/bn.hpp new file mode 100644 index 000000000..708f34a20 --- /dev/null +++ b/port/secude/src/bn.hpp @@ -0,0 +1,65 @@ +#pragma once + +#include "internal.hpp" + +namespace compat { + +struct BnDeleter { + void operator()(BIGNUM *b) const { BN_clear_free(b); } +}; +using Bn = std::unique_ptr; + +inline Bn bn_new() +{ + BIGNUM *b = BN_new(); + if (!b) + throw std::bad_alloc(); + return Bn(b); +} + +inline Bn bn_from(const Bytes &be) +{ + Bn b = bn_new(); + if (!be.empty() && !BN_bin2bn(be.data(), int(be.size()), b.get())) + throw std::bad_alloc(); + return b; +} + +inline Bn bn_word(unsigned long w) +{ + Bn b = bn_new(); + if (!BN_set_word(b.get(), w)) + throw std::bad_alloc(); + return b; +} + +// Big-endian magnitude, left-padded with zeros to at least `size` octets. +inline Bytes bn_bytes(const BIGNUM *b, size_t size = 0) +{ + size_t n = size_t(BN_num_bytes(b)); + if (n < size) + n = size; + Bytes out(n); + if (n) + BN_bn2binpad(b, out.data(), int(n)); + return out; +} + +inline BN_CTX *bn_ctx() +{ + struct CtxDeleter { + void operator()(BN_CTX *c) const { BN_CTX_free(c); } + }; + thread_local std::unique_ptr c(BN_CTX_secure_new()); + if (!c) + throw std::bad_alloc(); + return c.get(); +} + +inline void bn_check(int ok) +{ + if (!ok) + fail(EINTERNAL, "big number operation failed"); +} + +} // namespace compat diff --git a/port/secude/src/cadb.cpp b/port/secude/src/cadb.cpp new file mode 100644 index 000000000..f13a04281 --- /dev/null +++ b/port/secude/src/cadb.cpp @@ -0,0 +1,171 @@ +// CA database: /cadb.der +// CADatabase ::= SEQUENCE { version INTEGER (1), entries SEQUENCE OF Entry } +// Entry ::= SEQUENCE { keyType INTEGER, issued Time, certificate Certificate } +#include "der.hpp" + +#include + +namespace compat { + +namespace { + +const char kFileName[] = "cadb.der"; + +struct Entry { + int key_type; + std::string issued; + Bytes cert; +}; + +std::string db_path(PSE pse) +{ + const char *dir = pse_cadir(pse); + if (!dir || !*dir) + fail(ENODIR, "PSE was opened without CA directory"); + std::string p = dir; + if (p.back() != '/' && p.back() != '\\') + p += '/'; + return p + kFileName; +} + +std::vector load(const std::string &path) +{ + bool exists = false; + Bytes file = read_file(path, exists); + std::vector out; + if (!exists) + return out; + der::Reader top(der::parse_one(file)); + if (der::get_integer(top.next(der::INTEGER)) != 1) + fail(EFILECORRUPTED, "unsupported CA database version"); + der::Reader entries(top.next(der::SEQUENCE)); + while (!entries.empty()) { + der::Reader e(entries.next(der::SEQUENCE)); + Entry x; + x.key_type = int(der::get_integer(e.next(der::INTEGER))); + x.issued = der::get_time(e.next()); + x.cert = e.next(der::SEQUENCE).raw(); + out.push_back(std::move(x)); + } + return out; +} + +void save(const std::string &path, const std::vector &entries) +{ + Bytes body; + for (const auto &e : entries) + der::append(body, der::seq({der::integer(e.key_type), der::time(e.issued), e.cert})); + write_file_atomic(path, der::seq({der::integer(1), der::tlv(der::SEQUENCE, body)}), 0644); +} + +Bytes serial_of(const Certificate *c) +{ + Bytes s = bytes_of(c->tbs->serialnumber); + return strip_leading_zeros(s.data(), s.size()); +} + +struct CertHolder { + Certificate *c; + explicit CertHolder(Certificate *cert) : c(cert) {} + ~CertHolder() { free_certificate(c); } + CertHolder(const CertHolder &) = delete; + CertHolder &operator=(const CertHolder &) = delete; +}; + +} // namespace + +} // namespace compat + +using namespace compat; + +extern "C" { + +int af_cadb_add_Certificate(PSE pse_handle, KeyType keytype, Certificate *newcert) +{ + return guarded("af_cadb_add_Certificate", -1, [&] { + if (!newcert || !newcert->tbs) + fail(EINVALID, "missing certificate"); + std::string path = db_path(pse_handle); + std::vector entries = load(path); + Bytes serial = serial_of(newcert); + for (const auto &e : entries) { + CertHolder c(dec_certificate(e.cert)); + if (serial_of(c.c) == serial) + fail(ECADBCERT, "a certificate with this serial number is already registered"); + } + entries.push_back(Entry{int(keytype), utc_now(), enc_certificate(newcert)}); + save(path, entries); + return 0; + }); +} + +Certificate *af_cadb_get_Certificate(PSE pse_handle, OctetString *serial) +{ + return guarded("af_cadb_get_Certificate", nullptr, [&]() -> Certificate * { + if (!serial) + fail(EINVALID, "missing serial number"); + Bytes wanted = bytes_of(serial); + wanted = strip_leading_zeros(wanted.data(), wanted.size()); + for (const auto &e : load(db_path(pse_handle))) { + Certificate *c = dec_certificate(e.cert); + if (serial_of(c) == wanted) + return c; + free_certificate(c); + } + fail(ECADBCERT, "certificate not found in CA database"); + }); +} + +SET_OF_IssuedCertificate *af_cadb_get_user(PSE pse_handle, Name *name) +{ + return guarded("af_cadb_get_user", nullptr, [&] { + DName *wanted = string_to_dname(name); + SET_OF_IssuedCertificate *head = nullptr, **tail = &head; + try { + for (const auto &e : load(db_path(pse_handle))) { + CertHolder c(dec_certificate(e.cert)); + if (!dname_equal(c.c->tbs->subject, wanted)) + continue; + auto *s = mem_new(); + *tail = s; + tail = &s->next; + s->element = mem_new(); + s->element->serial = new_ostr(bytes_of(c.c->tbs->serialnumber)); + s->element->date_of_issue = mem_strdup(e.issued); + } + } catch (...) { + free_dname(wanted); + aux_free_SET_OF_IssuedCertificate(&head); + throw; + } + free_dname(wanted); + if (!head) + fail(ECADBUSER, std::string("no certificate registered for ") + name); + return head; + }); +} + +SET_OF_Name *af_cadb_list_user(PSE pse_handle) +{ + return guarded("af_cadb_list_user", nullptr, [&] { + std::vector names; + for (const auto &e : load(db_path(pse_handle))) { + CertHolder c(dec_certificate(e.cert)); + std::string n = dname_to_string(c.c->tbs->subject); + if (std::find(names.begin(), names.end(), n) == names.end()) + names.push_back(n); + } + SET_OF_Name *head = nullptr, **tail = &head; + for (const auto &n : names) { + auto *s = mem_new(); + s->element = mem_strdup(n); + *tail = s; + tail = &s->next; + } + if (!head) + fail(ECADBUSER, "CA database is empty"); + return head; + }); +} + +} // extern "C" diff --git a/port/secude/src/cert.cpp b/port/secude/src/cert.cpp new file mode 100644 index 000000000..4c2a8df72 --- /dev/null +++ b/port/secude/src/cert.cpp @@ -0,0 +1,581 @@ +// X.509 certificates: prototype creation, certification by the CA PSE, +// PKRoot handling and printing. +#include "bn.hpp" +#include "der.hpp" + +#include +#include + +namespace compat { + +namespace { + +constexpr long kDefaultValiditySeconds = 365L * 24 * 3600; + +const AlgId *default_sig_alg(const KeyInfo *k) +{ + return keyinfo_is_dsa(k) ? &dsaWithSHA1_aid : &sha1WithRSASignature_aid; +} + +void add_extension(CertExtensions *&ext, const Oid &id, bool critical, const Bytes &value) +{ + if (!ext) + ext = mem_new(); + SEQUENCE_OF_Extension **tail = &ext->nonSupported; + int seq = 0; + while (*tail) { + tail = &(*tail)->next; + ++seq; + } + auto *n = mem_new(); + n->element = mem_new(); + n->element->extnId = new_objid(id); + n->element->critical = critical; + n->element->extnDERcode = new_ostr(value); + n->element->seqnum = seq; + *tail = n; +} + +Validity *validity_from_now(long seconds) +{ + Validity *v = mem_new(); + v->notbefore = mem_strdup(utc_now()); + v->notafter = mem_strdup(utc_now(seconds)); + return v; +} + +// Signs t (re-encoded) with priv and returns the finished certificate (takes ownership of t). +Certificate *sign_tbs(ToBeSigned *t, const KeyInfo *priv, const Bytes &e_hint) +{ + Certificate *c = mem_new(); + c->tbs = t; + try { + Bytes tbs = enc_tbs(t); + c->tbs_DERcode = new_ostr(tbs); + Bytes sig = sign_message(priv, e_hint, t->signatureAI, tbs.data(), tbs.size()); + c->sig = mem_new(); + c->sig->signAI = copy_algid(t->signatureAI); + fill_bstr(&c->sig->signature, sig, sig.size() * 8); + return c; + } catch (...) { + free_certificate(c); + throw; + } +} + +Bytes increment(Bytes serial) +{ + for (size_t i = serial.size(); i-- > 0;) + if (++serial[i] != 0) + return serial; + serial.insert(serial.begin(), 1); + return serial; +} + +struct KeyInfoHolder { + KeyInfo *k; + explicit KeyInfoHolder(KeyInfo *ki) : k(ki) {} + ~KeyInfoHolder() { free_keyinfo(k); } + KeyInfoHolder(const KeyInfoHolder &) = delete; + KeyInfoHolder &operator=(const KeyInfoHolder &) = delete; +}; + +struct CertHolder { + Certificate *c; + explicit CertHolder(Certificate *cert) : c(cert) {} + ~CertHolder() { free_certificate(c); } + CertHolder(const CertHolder &) = delete; + CertHolder &operator=(const CertHolder &) = delete; +}; + +bool same_serial(const OctetString *a, const OctetString *b) +{ + Bytes x = bytes_of(a), y = bytes_of(b); + return strip_leading_zeros(x.data(), x.size()) == strip_leading_zeros(y.data(), y.size()); +} + +} // namespace + +std::string format_time(const char *t) +{ + if (!t) + return "(none)"; + std::string s(t); + int year, mon, day, hour, min, sec = 0; + size_t p = 0; + if (s.size() >= 15 && s[14] == 'Z') { + year = std::stoi(s.substr(0, 4)); + p = 4; + } else if (s.size() >= 11) { + year = std::stoi(s.substr(0, 2)); + year += year < 50 ? 2000 : 1900; + p = 2; + } else { + return s; + } + mon = std::stoi(s.substr(p, 2)); + day = std::stoi(s.substr(p + 2, 2)); + hour = std::stoi(s.substr(p + 4, 2)); + min = std::stoi(s.substr(p + 6, 2)); + if (s.size() > p + 9 && std::isdigit(static_cast(s[p + 8]))) + sec = std::stoi(s.substr(p + 8, 2)); + std::tm tm{}; + tm.tm_year = year - 1900; + tm.tm_mon = mon - 1; + tm.tm_mday = day; + tm.tm_hour = hour; + tm.tm_min = min; + tm.tm_sec = sec; + std::time_t tt = timegm(&tm); + std::tm norm{}; + gmtime_r(&tt, &norm); + char buf[64]; + std::strftime(buf, sizeof buf, "%a %b %d %H:%M:%S %Y UTC", &norm); + return std::string(buf) + " (" + s + ")"; +} + +std::string sprint_algid(const AlgId *aid) +{ + if (!aid || !aid->objid) + return "Algorithm (none)"; + std::string s = "Algorithm " + alg_display_name(aid->objid) + " (OID " + oid_to_string(oid_of(aid->objid)) + ")"; + const AlgInfo *a = alg_by_oid(aid->objid); + ParmType pt = a ? a->parm : NoParmType; + if (!aid->param) { + ParmType effective = aid->parmoverride ? *aid->parmoverride : pt; + if (effective == PARM_NULL) + s += ", NULL"; + return s; + } + switch (pt) { + case PARM_INTEGER: + s += ", Keysize = " + std::to_string(*static_cast(aid->param)); + break; + case PARM_KeyBits: { + auto *k = static_cast(aid->param); + Bytes p = bytes_of(&k->part1); + s += ", Keysize = " + std::to_string(BN_num_bits(bn_from(p).get())); + break; + } + case PARM_OctetString: { + Bytes iv = bytes_of(static_cast(aid->param)); + s += ", IV = " + hex(iv.data(), iv.size()); + break; + } + case PARM_RC2CBC: { + auto *p = static_cast(aid->param); + Bytes iv = bytes_of(&p->IV); + s += ", version = " + std::to_string(p->version) + ", IV = " + hex(iv.data(), iv.size()); + break; + } + case PARM_PKCS5: { + auto *p = static_cast(aid->param); + Bytes salt = bytes_of(&p->salt); + s += ", salt = " + hex(salt.data(), salt.size()) + ", iterations = " + std::to_string(p->iterationCount); + break; + } + default: { + Bytes raw = bytes_of(static_cast(aid->param)); + s += ", parameter = " + hex(raw.data(), raw.size()); + break; + } + } + return s; +} + +namespace { + +std::string sprint_number(const char *label, const Bytes &v, const std::string &indent) +{ + int bits = BN_num_bits(bn_from(v).get()); + Bytes minimal = strip_leading_zeros(v.data(), v.size()); + return indent + label + " (" + std::to_string(bits) + " bits):\n" + hexdump(minimal.data(), minimal.size(), indent + " ", false); +} + +std::string sprint_keyinfo(const KeyInfo *k, uint32_t flags, const std::string &indent) +{ + std::string s; + if (!flags || (flags & ALGID)) + s += indent + sprint_algid(k->subjectAI) + "\n"; + if (flags & BITSTRING) { + Bytes b = bytes_of(&k->subjectkey); + s += indent + "Key BIT STRING (" + std::to_string(k->subjectkey.nbits) + " bits):\n" + + hexdump(b.data(), b.size(), indent + " ", false); + } + if (!flags || (flags & KEYBITS)) { + try { + if (keyinfo_is_rsa(k)) { + KeyBits *kb = dec_keybits(bytes_of(&k->subjectkey)); + Bytes p1 = bytes_of(&kb->part1), p2 = bytes_of(&kb->part2); + free_keybits_content(kb); + mem_free(kb); + s += sprint_number("Modulus", p1, indent); + s += sprint_number("Public exponent", p2, indent); + } else if (keyinfo_is_dsa(k)) { + if (k->subjectAI->param) { + auto *params = static_cast(k->subjectAI->param); + s += sprint_number("Prime p", bytes_of(¶ms->part1), indent); + s += sprint_number("Subprime q", bytes_of(¶ms->part2), indent); + s += sprint_number("Base g", bytes_of(¶ms->part3), indent); + } + Bytes y = bytes_of(&k->subjectkey); + s += sprint_number("Public key y", der::get_unsigned(der::parse_one(y)), indent); + } + } catch (const Error &) { + s += indent + "(undecodable key)\n"; + } + } + return s; +} + +std::string sprint_extension(const v3Extension *x, const std::string &indent) +{ + Oid id = oid_of(x->extnId); + Bytes v = bytes_of(x->extnDERcode); + std::string name, value; + try { + if (id == oids::cryptoolPseName) { + name = "CrypToolPSEName"; + value = der::get_string(der::parse_one(v)); + } else if (id == oids::basicConstraints) { + name = "BasicConstraints"; + der::Reader r(der::parse_one(v)); + der::Node n; + bool ca = r.next_optional(der::BOOLEAN, n) && der::get_boolean(n); + value = ca ? "CA:TRUE" : "CA:FALSE"; + if (r.next_optional(der::INTEGER, n)) + value += ", pathlen:" + std::to_string(der::get_integer(n)); + } else if (id == oids::keyUsage) { + name = "KeyUsage"; + size_t nbits = 0; + Bytes bits = der::get_bit_string(der::parse_one(v), nbits); + static const char *names[] = {"digitalSignature", "nonRepudiation", "keyEncipherment", + "dataEncipherment", "keyAgreement", "keyCertSign", + "cRLSign", "encipherOnly", "decipherOnly"}; + for (size_t i = 0; i < nbits && i < 9; ++i) + if (bits[i / 8] & (0x80 >> (i % 8))) + value += (value.empty() ? "" : ", ") + std::string(names[i]); + } else if (id == oids::subjectKeyIdentifier) { + name = "SubjectKeyIdentifier"; + Bytes kid = der::parse_one(v).value(); + value = hex(kid.data(), kid.size()); + } else { + name = oid_to_string(id); + value = hex(v.data(), v.size()); + } + } catch (const Error &) { + name = oid_to_string(id); + value = hex(v.data(), v.size()); + } + return indent + name + (x->critical ? " (critical)" : "") + ": " + value + "\n"; +} + +std::string sprint_certificate(const Certificate *c) +{ + if (!c || !c->tbs) + fail(EINVALID, "missing certificate"); + uint32_t f = print_cert_flags(); + uint32_t kf = print_keyinfo_flags() & 0xff; + const ToBeSigned *t = c->tbs; + std::string s = "Certificate:\n"; + const std::string in = " "; + if (!f || (f & VER)) + s += in + "Version: " + std::to_string(t->version + 1) + "\n"; + if (!f || (f & TBS)) { + Bytes serial = bytes_of(t->serialnumber); + s += in + "SerialNumber: " + hex(serial.data(), serial.size()) + "\n"; + } + if (!f || (f & ALG)) + s += in + "Signature algorithm: " + sprint_algid(t->signatureAI) + "\n"; + if (!f || (f & ISSUER)) + s += in + "Issuer: " + dname_to_string(t->issuer) + "\n"; + if ((!f || (f & VAL)) && t->valid) { + s += in + "Validity:\n"; + s += in + " NotBefore: " + format_time(t->valid->notbefore) + "\n"; + s += in + " NotAfter: " + format_time(t->valid->notafter) + "\n"; + } + if (!f || (f & SUBJECT)) + s += in + "Subject: " + dname_to_string(t->subject) + "\n"; + if ((!f || (f & KEYINFO)) && t->subjectPK) + s += in + "SubjectPublicKeyInfo:\n" + sprint_keyinfo(t->subjectPK, kf, in + " "); + if ((!f || (f & EXTENSIONS)) && t->extensions && t->extensions->nonSupported) { + s += in + "Extensions:\n"; + for (const SEQUENCE_OF_Extension *x = t->extensions->nonSupported; x; x = x->next) + if (x->element) + s += sprint_extension(x->element, in + " "); + } + if ((!f || (f & SIGNAT)) && c->sig) { + Bytes sig = bytes_of(&c->sig->signature); + s += in + "Signature (" + alg_display_name(c->sig->signAI ? c->sig->signAI->objid : nullptr) + "):\n" + + hexdump(sig.data(), sig.size(), in + " ", false); + } + if (f & (HSH | DER)) { + Bytes der_bytes = enc_certificate(c); + if (f & HSH) { + Bytes md5 = digest(Hash::MD5, der_bytes.data(), der_bytes.size()); + Bytes sha1 = digest(Hash::SHA1, der_bytes.data(), der_bytes.size()); + s += in + "Fingerprint (MD5): " + hex(md5.data(), md5.size()) + "\n"; + s += in + "Fingerprint (SHA-1): " + hex(sha1.data(), sha1.size()) + "\n"; + } + if (f & DER) + s += in + "DER code:\n" + hexdump(der_bytes.data(), der_bytes.size(), in + " ", true); + } + return s; +} + +const Certificate *find_in_fcpath(const FCPath *f, const DName *issuer, const OctetString *serial) +{ + for (; f; f = f->next_forwardpath) + for (const SET_OF_Certificate *s = f->liste; s; s = s->next) + if (s->element && s->element->tbs && dname_equal(s->element->tbs->issuer, issuer) && + same_serial(s->element->tbs->serialnumber, serial)) + return s->element; + return nullptr; +} + +} // namespace + +Certificate *make_ca_certificate(PSE pse, const KeyInfo *pub, const AlgId *sig_alg, const DName *subject, + long validity_seconds) +{ + auto store = store_of(pse); + KeyInfoHolder priv(read_private_key(pse, SIGNATURE)); + Bytes e_hint; + if (keyinfo_is_rsa(pub)) + e_hint = rsa_public_from_keyinfo(pub).e; + ToBeSigned *t = mem_new(); + try { + t->version = 2; + t->serialnumber = new_ostr(Bytes{0}); + t->signatureAI = copy_algid(sig_alg); + t->issuer = copy_dname(subject); + t->subject = copy_dname(subject); + t->valid = validity_from_now(validity_seconds); + t->subjectPK = copy_keyinfo(pub); + add_extension(t->extensions, oids::basicConstraints, true, der::seq({der::boolean(true)})); + const uint8_t usage[1] = {0x06}; + add_extension(t->extensions, oids::keyUsage, true, der::bit_string(usage, 7)); + } catch (...) { + free_tbs(t); + throw; + } + return sign_tbs(t, priv.k, e_hint); +} + +} // namespace compat + +using namespace compat; + +extern "C" { + +Certificate *af_create_Certificate(PSE pse_handle, KeyInfo *keyinfo, AlgId *sig_alg, char *obj_name, DName *subject, + OctetString *serial, GeneralNames *subject_names, KeyIdentifier *subjKeyId, + Boolean CA, KeyUsage *key_usage) +{ + return guarded("af_create_Certificate", nullptr, [&] { + auto store = store_of(pse_handle); + if (!keyinfo) + fail(EINVALID, "missing public key"); + if (!subject) + fail(ENONAME, "missing subject name"); + if (subject_names) + fail(ENOTSUPPORTED, "subject alternative names are not supported"); + std::string object = obj_name && *obj_name ? obj_name : private_key_object(pse_handle, SIGNATURE); + const PseObject *o = store_find(store, object); + if (!o) + fail(EPSEOBJECTNOTEXISTING, "PSE object " + object + " does not exist"); + KeyInfoHolder priv(dec_keyinfo(o->value)); + Bytes e_hint; + if (keyinfo_is_rsa(keyinfo)) + e_hint = rsa_public_from_keyinfo(keyinfo).e; + + ToBeSigned *t = mem_new(); + try { + Bytes sn = serial && serial->noctets ? bytes_of(serial) : Bytes{1}; + t->serialnumber = new_ostr(sn); + t->signatureAI = copy_algid(sig_alg ? sig_alg : default_sig_alg(priv.k)); + t->issuer = copy_dname(subject); + t->subject = copy_dname(subject); + t->valid = validity_from_now(kDefaultValiditySeconds); + t->subjectPK = copy_keyinfo(keyinfo); + if (subjKeyId) + add_extension(t->extensions, oids::subjectKeyIdentifier, false, der::octet_string(bytes_of(subjKeyId))); + if (key_usage) { + CertExtensions tmp{}; + Ext_KeyUsage ku{}; + ku.critical = TRUE; + ku.extnValue = key_usage; + tmp.keyUsage = &ku; + CertExtensions *norm = dec_extensions(enc_extensions(&tmp)); + add_extension(t->extensions, oids::keyUsage, true, bytes_of(norm->nonSupported->element->extnDERcode)); + free_extensions(norm); + } + t->version = t->extensions ? 2 : 0; + } catch (...) { + free_tbs(t); + throw; + } + return sign_tbs(t, priv.k, e_hint); + }); +} + +Certificate *af_certify(PSE pse_handle, Certificate *tbc_cert, Validity *validity, AlgId *sig_alg, DName *name) +{ + return guarded("af_certify", nullptr, [&] { + auto store = store_of(pse_handle); + if (!tbc_cert || !tbc_cert->tbs || !tbc_cert->tbs->subjectPK) + fail(EINVALID, "incomplete prototype certificate"); + KeyInfoHolder priv(read_private_key(pse_handle, SIGNATURE)); + const PseObject *own = store_find(store, certificate_object(pse_handle, SIGNATURE)); + if (!own) + fail(EROOTCERT, "the CA PSE holds no certificate"); + CertHolder ca(dec_certificate(own->value)); + + const PseObject *sn = store_find(store, SerialNumber_name); + Bytes serial = sn && !sn->value.empty() ? sn->value : Bytes{1}; + + ToBeSigned *t = mem_new(); + try { + t->serialnumber = new_ostr(serial); + t->signatureAI = copy_algid(sig_alg ? sig_alg : default_sig_alg(priv.k)); + t->issuer = copy_dname(ca.c->tbs->subject); + t->subject = copy_dname(name ? name : tbc_cert->tbs->subject); + t->valid = validity ? copy_validity(validity) : validity_from_now(kDefaultValiditySeconds); + t->subjectPK = copy_keyinfo(tbc_cert->tbs->subjectPK); + Bytes ext = enc_extensions(tbc_cert->tbs->extensions); + if (!ext.empty()) + t->extensions = dec_extensions(ext); + t->version = t->extensions ? 2 : 0; + } catch (...) { + free_tbs(t); + throw; + } + Certificate *c = sign_tbs(t, priv.k, rsa_exponent_hint(pse_handle, priv.k)); + try { + store_put(store, SerialNumber_name, oid_of(&SerialNumber_oid), increment(serial)); + } catch (...) { + free_certificate(c); + throw; + } + return c; + }); +} + +PKRoot *aux_create_PKRoot(Certificate *cert1, Certificate *cert2) +{ + return guarded("aux_create_PKRoot", nullptr, [&] { + if (!cert1 || !cert1->tbs) + fail(EINVALID, "missing root certificate"); + auto make = [](const Certificate *c) { + Serial *s = mem_new(); + s->serial = new_ostr(bytes_of(c->tbs->serialnumber)); + s->version = c->tbs->version; + s->key = copy_keyinfo(c->tbs->subjectPK); + s->valid = copy_validity(c->tbs->valid); + s->sig = copy_signature(c->sig); + return s; + }; + PKRoot *p = mem_new(); + p->ca = copy_dname(cert1->tbs->subject); + p->newkey = make(cert1); + if (cert2 && cert2->tbs) + p->oldkey = make(cert2); + p->extensions = copy_extensions(cert1->tbs->extensions); + return p; + }); +} + +Certificate *af_PKRoot2Protocert(PKRoot *pkroot) +{ + return guarded("af_PKRoot2Protocert", nullptr, [&] { + if (!pkroot || !pkroot->newkey || !pkroot->newkey->key) + fail(EINVALID, "incomplete PKRoot"); + const Serial *k = pkroot->newkey; + Certificate *c = mem_new(); + try { + ToBeSigned *t = mem_new(); + c->tbs = t; + t->version = k->version; + t->serialnumber = new_ostr(bytes_of(k->serial)); + t->signatureAI = copy_algid(k->sig ? k->sig->signAI : &sha1WithRSASignature_aid); + t->issuer = copy_dname(pkroot->ca); + t->subject = copy_dname(pkroot->ca); + t->valid = k->valid ? copy_validity(k->valid) : validity_from_now(kDefaultValiditySeconds); + t->subjectPK = copy_keyinfo(k->key); + t->extensions = copy_extensions(pkroot->extensions); + c->tbs_DERcode = new_ostr(enc_tbs(t)); + c->sig = copy_signature(k->sig); + if (!c->sig) { + c->sig = mem_new(); + c->sig->signAI = copy_algid(t->signatureAI); + } + return c; + } catch (...) { + free_certificate(c); + throw; + } + }); +} + +Certificate *af_pse_get_Certificate(PSE pse_handle, KeyType type, DName *issuer, OctetString *serial) +{ + return guarded("af_pse_get_Certificate", nullptr, [&]() -> Certificate * { + auto store = store_of(pse_handle); + if (!issuer && !serial) { + const PseObject *o = store_find(store, certificate_object(pse_handle, type)); + if (!o) + fail(EPSEOBJECTNOTEXISTING, "PSE holds no certificate"); + return dec_certificate(o->value); + } + for (const char *name : {Cert_name, SignCert_name, EncCert_name}) { + const PseObject *o = store_find(store, name); + if (!o) + continue; + Certificate *c = dec_certificate(o->value); + if ((!issuer || dname_equal(c->tbs->issuer, issuer)) && (!serial || same_serial(c->tbs->serialnumber, serial))) + return c; + free_certificate(c); + } + if (const PseObject *o = store_find(store, FCPath_name)) { + FCPath *f = dec_fcpath(o->value); + const Certificate *found = find_in_fcpath(f, issuer, serial); + Certificate *c = found ? copy_certificate(found) : nullptr; + free_fcpath(f); + if (c) + return c; + } + fail(EPSEOBJECTNOTEXISTING, "certificate not found in PSE"); + }); +} + +Certificates *af_pse_get_Certificates(PSE pse_handle, KeyType type, DName *name) +{ + return guarded("af_pse_get_Certificates", nullptr, [&] { + auto store = store_of(pse_handle); + const PseObject *o = store_find(store, certificate_object(pse_handle, type)); + if (!o) + fail(EPSEOBJECTNOTEXISTING, "PSE holds no certificate"); + Certificates *c = mem_new(); + try { + c->usercertificate = dec_certificate(o->value); + if (const PseObject *f = store_find(store, FCPath_name)) + c->forwardpath = dec_fcpath(f->value); + return c; + } catch (...) { + aux_free_Certificates(&c); + throw; + } + }); +} + +char *aux_sprint_Certificate(PSE pse_handle, char *string, Certificate *cert) +{ + return guarded("aux_sprint_Certificate", nullptr, [&] { return append_string(string, sprint_certificate(cert)); }); +} + +char *aux_sprint_AlgId(char *string, AlgId *aid) +{ + return guarded("aux_sprint_AlgId", nullptr, [&] { return append_string(string, sprint_algid(aid)); }); +} + +} // extern "C" diff --git a/port/secude/src/crypt_api.cpp b/port/secude/src/crypt_api.cpp new file mode 100644 index 000000000..5502035ed --- /dev/null +++ b/port/secude/src/crypt_api.cpp @@ -0,0 +1,307 @@ +// sec_* and af_* encryption, signature and key generation entry points. +// +// RSA encryption (rsa / rsaEncryption keys) is textbook RSA as used by CrypTool: +// the input is split into blocks of (bits-1)/8 octets (the last block is +// padded with zero octets), each block is encrypted into (bits+7)/8 octets. +// Decryption returns (bits-1)/8 octets per block. +#include "internal.hpp" + +namespace compat { + +namespace { + +struct KeyInfoHolder { + KeyInfo *k = nullptr; + ~KeyInfoHolder() { free_keyinfo(k); } +}; + +const KeyInfo *resolve_key(const Key *key, KeyInfoHolder &holder) +{ + if (!key) + fail(EINVALID, "missing key"); + if (key->key) + return key->key; + if (key->pse_sel) { + auto s = store_of(key->pse_sel); + std::string name = key->pse_sel->object ? key->pse_sel->object : SKnew_name; + const PseObject *o = store_find(s, name); + if (!o) + fail(EPSEOBJECTNOTEXISTING, "PSE object " + name + " does not exist"); + holder.k = dec_keyinfo(o->value); + return holder.k; + } + fail(EINVALID, "key has neither a value nor a PSE reference"); +} + +const AlgInfo *algorithm_of(const Key *key, const KeyInfo *k) +{ + const AlgId *a = key->alg && key->alg->objid ? key->alg : k->subjectAI; + const AlgInfo *info = a ? alg_by_oid(a->objid) : nullptr; + if (!info) + fail(EUNKNOWNALGID, "unknown key algorithm"); + return info; +} + +const AlgId *cipher_algid(const Key *key, const KeyInfo *k) +{ + return key->alg && key->alg->objid ? key->alg : k->subjectAI; +} + +void put_bits(BitString *out, const Bytes &data) +{ + if (!out) + fail(EINVALID, "missing output"); + if (!out->bits) { + fill_bstr(out, data, data.size() * 8); + return; + } + size_t off = out->nbits / 8; + if (!data.empty()) + std::memcpy(out->bits + off, data.data(), data.size()); + out->nbits = sec_uint4((off + data.size()) * 8); +} + +void put_octets(OctetString *out, const Bytes &data) +{ + if (!out) + fail(EINVALID, "missing output"); + if (!out->octets) { + fill_ostr(out, data); + return; + } + size_t off = out->noctets; + if (!data.empty()) + std::memcpy(out->octets + off, data.data(), data.size()); + out->noctets = sec_uint4(off + data.size()); +} + +Bytes encrypt(const Key *key, const uint8_t *in, size_t n) +{ + KeyInfoHolder holder; + const KeyInfo *k = resolve_key(key, holder); + const AlgInfo *a = algorithm_of(key, k); + if (a->enc == SECUDE_ALG_RSA) + return rsa_encrypt_blocks(rsa_public_from_keyinfo(k), in, n); + if (a->type != SYM_ENC) + fail(EALGNOTFITKEY, std::string(a->name) + " cannot be used for encryption"); + return sym_encrypt(cipher_algid(key, k), bytes_of(&k->subjectkey), k->subjectkey.nbits, in, n); +} + +Bytes decrypt(const Key *key, const Bytes &e_hint, const uint8_t *in, size_t n) +{ + KeyInfoHolder holder; + const KeyInfo *k = resolve_key(key, holder); + const AlgInfo *a = algorithm_of(key, k); + if (a->enc == SECUDE_ALG_RSA) + return rsa_decrypt_blocks(rsa_private_from_keyinfo(k, e_hint), in, n); + if (a->type != SYM_ENC) + fail(EALGNOTFITKEY, std::string(a->name) + " cannot be used for decryption"); + return sym_decrypt(cipher_algid(key, k), bytes_of(&k->subjectkey), k->subjectkey.nbits, in, n); +} + +const uint8_t *data_of(const OctetString *o) { return o ? reinterpret_cast(o->octets) : nullptr; } + +int key_bits(const KeyInfo *k) +{ + if (keyinfo_is_rsa(k)) { + KeyBits *kb = dec_keybits(bytes_of(&k->subjectkey)); + Bytes a = bytes_of(&kb->part1), b = bytes_of(&kb->part2); + bool pub = kb->choice == 2 && a.size() > b.size() + 4; + free_keybits_content(kb); + mem_free(kb); + if (pub) + return rsa_bits(a); + return rsa_bits(a) + rsa_bits(b); + } + if (keyinfo_is_dsa(k) && k->subjectAI->param) + return rsa_bits(bytes_of(&static_cast(k->subjectAI->param)->part1)); + return int(k->subjectkey.nbits); +} + +} // namespace + +} // namespace compat + +using namespace compat; + +extern "C" { + +int sec_encrypt_all(OctetString *in_octets, BitString *out_bits, Key *key) +{ + return guarded("sec_encrypt_all", -1, [&] { + if (!in_octets) + fail(EINVALID, "missing input"); + put_bits(out_bits, encrypt(key, data_of(in_octets), in_octets->noctets)); + return 0; + }); +} + +int sec_decrypt_all(BitString *in_bits, OctetString *out_octets, Key *key) +{ + return guarded("sec_decrypt_all", -1, [&] { + if (!in_bits) + fail(EINVALID, "missing input"); + Bytes in = bytes_of(in_bits); + put_octets(out_octets, decrypt(key, Bytes(), in.data(), in.size())); + return 0; + }); +} + +RC sec_verify_all(OctetString *in_octets, Signature *signature, Key *key, HashInput *hash_input) +{ + return guarded("sec_verify_all", -1, [&] { + if (!in_octets || !signature) + fail(EINVALID, "missing parameter"); + KeyInfoHolder holder; + const KeyInfo *k = resolve_key(key, holder); + if (!verify_message(k, signature->signAI, data_of(in_octets), in_octets->noctets, bytes_of(&signature->signature))) + fail(EVERIFY, "signature verification failed"); + return 0; + }); +} + +RC rsa_sign_all(OctetString *hash, BitString *sign, KeyBits *key) +{ + return guarded("rsa_sign_all", -1, [&] { + if (!hash || !sign || !key) + fail(EINVALID, "missing parameter"); + KeyInfoHolder holder; + holder.k = mem_new(); + holder.k->subjectAI = copy_algid(&rsa_aid); + Bytes kb = enc_keybits(key); + fill_bstr(&holder.k->subjectkey, kb, kb.size() * 8); + Bytes sig = rsa_raw_private(rsa_private_from_keyinfo(holder.k, Bytes()), bytes_of(hash)); + fill_bstr(sign, sig, sig.size() * 8); + return 0; + }); +} + +int sec_get_key(KeyInfo *keyinfo, Key *key) +{ + return guarded("sec_get_key", -1, [&] { + if (!keyinfo) + fail(EINVALID, "missing output"); + KeyInfoHolder holder; + KeyInfo *copy = copy_keyinfo(resolve_key(key, holder)); + *keyinfo = *copy; + mem_free(copy); + return 0; + }); +} + +OctetString *sec_random_ostr(sec_uint4 noctects, int security) +{ + return guarded("sec_random_ostr", nullptr, [&] { return new_ostr(random_bytes(noctects)); }); +} + +int af_encrypt_all(PSE pse_handle, OctetString *inoctets, BitString *outbits, Key *key, DName *dname) +{ + return guarded("af_encrypt_all", -1, [&] { + if (!inoctets) + fail(EINVALID, "missing input"); + store_of(pse_handle); + if (key && (key->key || key->pse_sel)) { + put_bits(outbits, encrypt(key, data_of(inoctets), inoctets->noctets)); + return 0; + } + Certificate *own = af_pse_get_Certificate(pse_handle, ENCRYPTION, nullptr, nullptr); + if (!own) + fail(EPSEOBJECTNOTEXISTING, "no encryption key available"); + Key k{}; + k.key = own->tbs->subjectPK; + try { + put_bits(outbits, encrypt(&k, data_of(inoctets), inoctets->noctets)); + } catch (...) { + free_certificate(own); + throw; + } + free_certificate(own); + return 0; + }); +} + +int af_decrypt_all(PSE pse_handle, BitString *inbits, OctetString *outoctets, Key *key) +{ + return guarded("af_decrypt_all", -1, [&] { + if (!inbits) + fail(EINVALID, "missing input"); + Bytes in = bytes_of(inbits); + if (key && (key->key || key->pse_sel)) { + KeyInfoHolder holder; + Bytes hint = rsa_exponent_hint(pse_handle, resolve_key(key, holder)); + put_octets(outoctets, decrypt(key, hint, in.data(), in.size())); + return 0; + } + KeyInfoHolder priv; + priv.k = read_private_key(pse_handle, ENCRYPTION); + Key k{}; + k.key = priv.k; + put_octets(outoctets, decrypt(&k, rsa_exponent_hint(pse_handle, priv.k), in.data(), in.size())); + return 0; + }); +} + +RC af_sign_all(PSE pse_handle, OctetString *in_octets, Signature *signature) +{ + return guarded("af_sign_all", -1, [&] { + if (!in_octets || !signature) + fail(EINVALID, "missing parameter"); + KeyInfoHolder priv; + priv.k = read_private_key(pse_handle, SIGNATURE); + if (!signature->signAI) + signature->signAI = keyinfo_is_dsa(priv.k) ? &dsaWithSHA1_aid : &sha1WithRSASignature_aid; + Bytes sig = sign_message(priv.k, rsa_exponent_hint(pse_handle, priv.k), signature->signAI, data_of(in_octets), + in_octets->noctets); + fill_bstr(&signature->signature, sig, sig.size() * 8); + return 0; + }); +} + +RC af_gen_key(PSE pse_handle, Key *key, KeyType ktype, Boolean replace) +{ + return guarded("af_gen_key", -1, [&] { + auto store = store_of(pse_handle); + if (!key || !key->alg || !key->alg->objid) + fail(EINVALID, "missing key algorithm"); + const AlgInfo *a = alg_by_oid(key->alg->objid); + if (!a || (a->enc != SECUDE_ALG_RSA && a->enc != SECUDE_ALG_DSA)) + fail(EUNKNOWNALGID, "key generation supports RSA and DSA only"); + int bits = key->key_size > 0 ? key->key_size : DEF_ASYM_KEYSIZE; + std::string object = store_onekeypaironly(store) ? SKnew_name : (ktype == SIGNATURE ? SignSK_name : DecSKnew_name); + if (!replace && store_find(store, object)) + fail(EPSEALREADYEXISTING, "PSE object " + object + " already exists"); + KeyInfo *pub = nullptr, *priv = nullptr; + if (a->enc == SECUDE_ALG_RSA) + rsa_generate(bits, &pub, &priv); + else + dsa_generate(bits, &pub, &priv); + KeyInfoHolder priv_holder; + priv_holder.k = priv; + try { + store_put(store, object, oid_of(pse_object_oid(object.c_str())), enc_keyinfo(priv)); + } catch (...) { + free_keyinfo(pub); + throw; + } + key->key = pub; + return 0; + }); +} + +RC af_pse_get_keysize(PSE pse_handle, int *sig, int *enc, AlgEnc *sig_type) +{ + return guarded("af_pse_get_keysize", -1, [&] { + KeyInfoHolder s, e; + s.k = read_private_key(pse_handle, SIGNATURE); + e.k = read_private_key(pse_handle, ENCRYPTION); + if (sig) + *sig = key_bits(s.k); + if (enc) + *enc = key_bits(e.k); + if (sig_type) + *sig_type = keyinfo_is_dsa(s.k) ? SECUDE_ALG_DSA : SECUDE_ALG_RSA; + return 0; + }); +} + +} // extern "C" diff --git a/port/secude/src/data_flags.cpp b/port/secude/src/data_flags.cpp new file mode 100644 index 000000000..89fb02e65 --- /dev/null +++ b/port/secude/src/data_flags.cpp @@ -0,0 +1,18 @@ +// Kept free of SECUDE headers: crypt_p.h declares print_keyinfo_flag as +// unsigned char, but CrypTool accesses it through a sec_uint4 pointer, so the +// object must provide four bytes. +#include + +extern "C" { +uint32_t print_cert_flag = 2 | 4 | 8 | 16 | 32 | 64 | 256 | 512 | 1024; +alignas(4) uint32_t print_keyinfo_flag = 1 | 4 | 8; +} + +namespace compat { + +uint32_t print_cert_flags() { return print_cert_flag; } +uint32_t print_keyinfo_flags() { return print_keyinfo_flag; } +void *print_cert_flag_address() { return &print_cert_flag; } +void *print_keyinfo_flag_address() { return &print_keyinfo_flag; } + +} // namespace compat diff --git a/port/secude/src/der.cpp b/port/secude/src/der.cpp new file mode 100644 index 000000000..723e6879e --- /dev/null +++ b/port/secude/src/der.cpp @@ -0,0 +1,353 @@ +#include "der.hpp" + +#include + +namespace compat { +namespace der { + +namespace { + +void put_length(Bytes &out, size_t n) +{ + if (n < 0x80) { + out.push_back(uint8_t(n)); + return; + } + uint8_t buf[sizeof(size_t)]; + int k = 0; + while (n) { + buf[k++] = uint8_t(n & 0xff); + n >>= 8; + } + out.push_back(uint8_t(0x80 | k)); + while (k) + out.push_back(buf[--k]); +} + +[[noreturn]] void bad(const char *what) +{ + fail(EDECODE, std::string("ASN.1 decoding failed: ") + what); +} + +} // namespace + +Bytes tlv(uint8_t tag, const uint8_t *p, size_t n) +{ + Bytes out; + out.reserve(n + 6); + out.push_back(tag); + put_length(out, n); + if (n) + out.insert(out.end(), p, p + n); + return out; +} + +void append(Bytes &out, const Bytes &part) +{ + out.insert(out.end(), part.begin(), part.end()); +} + +Bytes concat(std::initializer_list parts) +{ + Bytes out; + for (const auto &p : parts) + append(out, p); + return out; +} + +Bytes seq(std::initializer_list parts) +{ + return tlv(SEQUENCE, concat(parts)); +} + +Bytes set_of(std::vector parts) +{ + std::sort(parts.begin(), parts.end()); + Bytes body; + for (const auto &p : parts) + append(body, p); + return tlv(SET, body); +} + +Bytes integer(long long v) +{ + Bytes b; + unsigned long long u = static_cast(v); + for (int i = 0; i < 8; ++i) + b.insert(b.begin(), uint8_t(u >> (8 * i))); + size_t i = 0; + while (i + 1 < b.size() && ((b[i] == 0x00 && !(b[i + 1] & 0x80)) || (b[i] == 0xff && (b[i + 1] & 0x80)))) + ++i; + return tlv(INTEGER, Bytes(b.begin() + long(i), b.end())); +} + +Bytes integer_unsigned(const uint8_t *p, size_t n) +{ + while (n > 1 && *p == 0) { + ++p; + --n; + } + Bytes c; + if (n == 0 || (p[0] & 0x80)) + c.push_back(0); + c.insert(c.end(), p, p + n); + return tlv(INTEGER, c); +} + +Bytes oid(const Oid &o) +{ + if (o.size() < 2 || o[0] > 2) + fail(EENCODE, "invalid object identifier"); + Bytes c; + auto put = [&c](unsigned long long v) { + uint8_t tmp[10]; + int k = 0; + do { + tmp[k++] = uint8_t(v & 0x7f); + v >>= 7; + } while (v); + while (k > 1) + c.push_back(uint8_t(tmp[--k] | 0x80)); + c.push_back(tmp[0]); + }; + put(static_cast(o[0]) * 40 + o[1]); + for (size_t i = 2; i < o.size(); ++i) + put(o[i]); + return tlv(OID, c); +} + +Bytes octet_string(const Bytes &b) { return tlv(OCTET_STRING, b); } + +Bytes bit_string(const uint8_t *p, size_t nbits) +{ + size_t n = (nbits + 7) / 8; + Bytes c; + c.push_back(uint8_t((8 - nbits % 8) % 8)); + if (n) + c.insert(c.end(), p, p + n); + if (nbits % 8) + c.back() &= uint8_t(0xff << (8 - nbits % 8)); + return tlv(BIT_STRING, c); +} + +Bytes null() { return Bytes{NULL_, 0}; } + +Bytes boolean(bool v) { return Bytes{BOOLEAN, 1, uint8_t(v ? 0xff : 0)}; } + +Bytes string(uint8_t tag, const std::string &s) +{ + if (tag == UTF8String) { + std::string u; + for (unsigned char ch : s) { + if (ch < 0x80) { + u += char(ch); + } else { + u += char(0xC0 | (ch >> 6)); + u += char(0x80 | (ch & 0x3f)); + } + } + return tlv(tag, reinterpret_cast(u.data()), u.size()); + } + if (tag == BMPString) { + Bytes c; + for (unsigned char ch : s) { + c.push_back(0); + c.push_back(ch); + } + return tlv(tag, c); + } + return tlv(tag, reinterpret_cast(s.data()), s.size()); +} + +Bytes time(const std::string &t) +{ + uint8_t tag = t.size() >= 15 ? GeneralizedTime : UTCTime; + return tlv(tag, reinterpret_cast(t.data()), t.size()); +} + +bool try_parse_tlv(const uint8_t *p, size_t n, Node &out) +{ + if (n < 2) + return false; + const uint8_t *q = p; + uint8_t tag = *q++; + if ((tag & 0x1f) == 0x1f) + return false; + size_t len; + uint8_t l0 = *q++; + if (l0 < 0x80) { + len = l0; + } else { + int k = l0 & 0x7f; + if (k == 0 || k > 4 || size_t(k) > n - 2) + return false; + len = 0; + for (int i = 0; i < k; ++i) + len = (len << 8) | *q++; + } + size_t hdr = size_t(q - p); + if (len > n - hdr) + return false; + out.tag = tag; + out.hdr = p; + out.val = q; + out.len = len; + out.total = hdr + len; + return true; +} + +Node Reader::next() +{ + Node n; + if (!try_parse_tlv(p_, size_t(end_ - p_), n)) + bad("malformed element"); + p_ += n.total; + return n; +} + +Node Reader::next(uint8_t tag) +{ + Node n = next(); + if (n.tag != tag) + bad("unexpected tag"); + return n; +} + +bool Reader::next_optional(uint8_t tag, Node &out) +{ + if (!peek(tag)) + return false; + out = next(); + return true; +} + +void Reader::expect_end() const +{ + if (p_ != end_) + bad("junk at end of element"); +} + +Node parse_one(const Bytes &b) +{ + Reader r(b); + Node n = r.next(); + r.expect_end(); + return n; +} + +long long get_integer(const Node &n) +{ + if (n.tag != INTEGER || n.len == 0 || n.len > 8) + bad("INTEGER expected"); + long long v = (n.val[0] & 0x80) ? -1 : 0; + for (size_t i = 0; i < n.len; ++i) + v = static_cast((static_cast(v) << 8) | n.val[i]); + return v; +} + +Bytes get_unsigned(const Node &n) +{ + if (n.tag != INTEGER || n.len == 0) + bad("INTEGER expected"); + return strip_leading_zeros(n.val, n.len); +} + +Oid get_oid(const Node &n) +{ + if (n.tag != OID || n.len == 0) + bad("OBJECT IDENTIFIER expected"); + Oid o; + unsigned long long v = 0; + bool first = true; + for (size_t i = 0; i < n.len; ++i) { + v = (v << 7) | (n.val[i] & 0x7f); + if (v > 0xffffffffULL) + bad("OBJECT IDENTIFIER component too large"); + if (!(n.val[i] & 0x80)) { + if (first) { + unsigned a = v < 40 ? 0 : v < 80 ? 1 : 2; + o.push_back(a); + o.push_back(unsigned(v - 40ULL * a)); + first = false; + } else { + o.push_back(unsigned(v)); + } + v = 0; + } + } + if (v != 0 || first) + bad("truncated OBJECT IDENTIFIER"); + return o; +} + +Bytes get_bit_string(const Node &n, size_t &nbits) +{ + if (n.tag != BIT_STRING || n.len == 0 || n.val[0] > 7) + bad("BIT STRING expected"); + nbits = (n.len - 1) * 8 - n.val[0]; + return Bytes(n.val + 1, n.val + n.len); +} + +std::string get_string(const Node &n) +{ + std::string s; + if (n.tag == BMPString) { + for (size_t i = 0; i + 1 < n.len; i += 2) { + unsigned c = (unsigned(n.val[i]) << 8) | n.val[i + 1]; + s += c < 0x100 ? char(c) : '?'; + } + return s; + } + if (n.tag == UTF8String) { + for (size_t i = 0; i < n.len; ++i) { + uint8_t c = n.val[i]; + if (c < 0x80) { + s += char(c); + } else if ((c & 0xE0) == 0xC0 && i + 1 < n.len) { + unsigned cp = (unsigned(c & 0x1f) << 6) | (n.val[i + 1] & 0x3f); + s += cp < 0x100 ? char(cp) : '?'; + ++i; + } else { + while (i + 1 < n.len && (n.val[i + 1] & 0xC0) == 0x80) + ++i; + s += '?'; + } + } + return s; + } + if (n.constructed()) + bad("primitive string expected"); + return std::string(reinterpret_cast(n.val), n.len); +} + +std::string get_time(const Node &n) +{ + if (n.tag != UTCTime && n.tag != GeneralizedTime) + bad("time expected"); + return std::string(reinterpret_cast(n.val), n.len); +} + +bool get_boolean(const Node &n) +{ + if (n.tag != BOOLEAN || n.len != 1) + bad("BOOLEAN expected"); + return n.val[0] != 0; +} + +} // namespace der + +Bytes strip_leading_zeros(const uint8_t *p, size_t n) +{ + while (n > 1 && *p == 0) { + ++p; + --n; + } + return Bytes(p, p + n); +} + +Bytes enc_unsigned_integer(const uint8_t *p, size_t n) +{ + return der::integer_unsigned(p, n); +} + +} // namespace compat diff --git a/port/secude/src/der.hpp b/port/secude/src/der.hpp new file mode 100644 index 000000000..3cc7852db --- /dev/null +++ b/port/secude/src/der.hpp @@ -0,0 +1,94 @@ +#pragma once + +#include "internal.hpp" + +#include + +// Minimal DER writer/reader (single-octet tags, definite lengths). +namespace compat { +namespace der { + +enum Tag : uint8_t { + BOOLEAN = 0x01, + INTEGER = 0x02, + BIT_STRING = 0x03, + OCTET_STRING = 0x04, + NULL_ = 0x05, + OID = 0x06, + UTF8String = 0x0C, + PrintableString = 0x13, + T61String = 0x14, + IA5String = 0x16, + UTCTime = 0x17, + GeneralizedTime = 0x18, + BMPString = 0x1E, + SEQUENCE = 0x30, + SET = 0x31, +}; + +inline constexpr uint8_t ctx(unsigned n, bool constructed = true) +{ + return uint8_t(0x80 | (constructed ? 0x20 : 0) | n); +} + +Bytes tlv(uint8_t tag, const uint8_t *p, size_t n); +inline Bytes tlv(uint8_t tag, const Bytes &content) { return tlv(tag, content.data(), content.size()); } +Bytes seq(std::initializer_list parts); +Bytes set_of(std::vector parts); // sorted, as DER requires +Bytes concat(std::initializer_list parts); +void append(Bytes &out, const Bytes &part); + +Bytes integer(long long v); +Bytes integer_unsigned(const uint8_t *p, size_t n); +inline Bytes integer_unsigned(const Bytes &b) { return integer_unsigned(b.data(), b.size()); } +Bytes oid(const Oid &o); +Bytes octet_string(const Bytes &b); +Bytes bit_string(const uint8_t *p, size_t nbits); +Bytes null(); +Bytes boolean(bool v); +Bytes string(uint8_t tag, const std::string &s); +Bytes time(const std::string &t); // UTCTime (13 chars) or GeneralizedTime (15 chars) + +struct Node { + uint8_t tag = 0; + const uint8_t *hdr = nullptr; // start of TLV + const uint8_t *val = nullptr; // start of contents + size_t len = 0; // contents length + size_t total = 0; // header + contents + bool constructed() const { return tag & 0x20; } + Bytes raw() const { return Bytes(hdr, hdr + total); } + Bytes value() const { return Bytes(val, val + len); } +}; + +class Reader { +public: + Reader(const uint8_t *p, size_t n) : p_(p), end_(p + n) {} + explicit Reader(const Bytes &b) : Reader(b.data(), b.size()) {} + explicit Reader(Bytes &&) = delete; // nodes would point into a destroyed buffer + explicit Reader(const Node &n) : Reader(n.val, n.len) {} + bool empty() const { return p_ == end_; } + bool peek(uint8_t tag) const { return p_ != end_ && *p_ == tag; } + Node next(); + Node next(uint8_t tag); + bool next_optional(uint8_t tag, Node &out); + void expect_end() const; + +private: + const uint8_t *p_; + const uint8_t *end_; +}; + +Node parse_one(const Bytes &b); // exactly one element, nothing trailing +Node parse_one(Bytes &&) = delete; +bool try_parse_tlv(const uint8_t *p, size_t n, Node &out); + +long long get_integer(const Node &n); +Bytes get_unsigned(const Node &n); // INTEGER contents without sign octets +Oid get_oid(const Node &n); +Bytes get_bit_string(const Node &n, size_t &nbits); +std::string get_string(const Node &n); // any string type, converted to Latin-1 where possible +std::string get_time(const Node &n); +bool get_boolean(const Node &n); + +} // namespace der +} // namespace compat diff --git a/port/secude/src/dname.cpp b/port/secude/src/dname.cpp new file mode 100644 index 000000000..f7b866638 --- /dev/null +++ b/port/secude/src/dname.cpp @@ -0,0 +1,333 @@ +// Distinguished names: string form "CN=..., DC=..., DC=..." (least significant +// RDN first, as in RFC 4514) <-> DName list in DER order. +#include "der.hpp" + +#include +#include +#include + +namespace compat { + +namespace { + +struct AttrKeyword { + const char *keyword; + Oid oid; + uint8_t tag; +}; + +const std::vector &keywords() +{ + static const std::vector k = { + {"CN", {2, 5, 4, 3}, der::PrintableString}, + {"C", {2, 5, 4, 6}, der::PrintableString}, + {"L", {2, 5, 4, 7}, der::PrintableString}, + {"ST", {2, 5, 4, 8}, der::PrintableString}, + {"SP", {2, 5, 4, 8}, der::PrintableString}, + {"STREET", {2, 5, 4, 9}, der::PrintableString}, + {"O", {2, 5, 4, 10}, der::PrintableString}, + {"OU", {2, 5, 4, 11}, der::PrintableString}, + {"T", {2, 5, 4, 12}, der::PrintableString}, + {"TITLE", {2, 5, 4, 12}, der::PrintableString}, + {"SN", {2, 5, 4, 4}, der::PrintableString}, + {"SERIALNUMBER", {2, 5, 4, 5}, der::PrintableString}, + {"G", {2, 5, 4, 42}, der::PrintableString}, + {"GN", {2, 5, 4, 42}, der::PrintableString}, + {"DC", {0, 9, 2342, 19200300, 100, 1, 25}, der::IA5String}, + {"UID", {0, 9, 2342, 19200300, 100, 1, 1}, der::UTF8String}, + {"EMAIL", {1, 2, 840, 113549, 1, 9, 1}, der::IA5String}, + {"E", {1, 2, 840, 113549, 1, 9, 1}, der::IA5String}, + }; + return k; +} + +const AttrKeyword *keyword_by_name(const std::string &name) +{ + for (const auto &k : keywords()) + if (!strcasecmp(k.keyword, name.c_str())) + return &k; + return nullptr; +} + +const AttrKeyword *keyword_by_oid(const Oid &o) +{ + for (const auto &k : keywords()) + if (k.oid == o) + return &k; + return nullptr; +} + +bool printable(const std::string &s) +{ + for (unsigned char c : s) + if (!(std::isalnum(c) || std::strchr(" '()+,-./:=?", c)) || c >= 0x80) + return false; + return true; +} + +std::string trim(const std::string &s) +{ + size_t b = s.find_first_not_of(" \t"); + if (b == std::string::npos) + return {}; + size_t e = s.find_last_not_of(" \t"); + return s.substr(b, e - b + 1); +} + +Oid parse_dotted(const std::string &s) +{ + Oid o; + size_t i = 0; + while (i < s.size()) { + if (!std::isdigit(static_cast(s[i]))) + return {}; + unsigned long v = std::strtoul(s.c_str() + i, nullptr, 10); + o.push_back(unsigned(v)); + size_t dot = s.find('.', i); + if (dot == std::string::npos) + break; + i = dot + 1; + } + return o.size() >= 2 ? o : Oid{}; +} + +std::string value_string(const AttrValueAssertion *ava) +{ + Bytes v = bytes_of(ava->element_IF_1); + uint8_t tag = uint8_t(ava->attr_encoding); + Bytes tlv = der::tlv(tag, v); + der::Node n; + der::try_parse_tlv(tlv.data(), tlv.size(), n); + if (tag == der::UTF8String || tag == der::BMPString) + return der::get_string(n); + return std::string(v.begin(), v.end()); +} + +std::string normalize(const std::string &s) +{ + std::string out; + bool space = false; + for (unsigned char c : trim(s)) { + if (c == ' ' || c == '\t') { + space = true; + continue; + } + if (space) + out += ' '; + space = false; + out += char(std::tolower(c)); + } + return out; +} + +struct ParsedAva { + Oid oid; + uint8_t tag; + Bytes value; +}; + +// Splits at unquoted/unescaped separators. +std::vector split(const std::string &s, const char *seps) +{ + std::vector out; + std::string cur; + bool quoted = false; + for (size_t i = 0; i < s.size(); ++i) { + char c = s[i]; + if (c == '\\' && i + 1 < s.size()) { + cur += c; + cur += s[++i]; + continue; + } + if (c == '"') + quoted = !quoted; + if (!quoted && std::strchr(seps, c)) { + out.push_back(cur); + cur.clear(); + continue; + } + cur += c; + } + out.push_back(cur); + return out; +} + +std::string unescape(const std::string &raw) +{ + std::string v = trim(raw); + if (v.size() >= 2 && v.front() == '"' && v.back() == '"') + v = v.substr(1, v.size() - 2); + std::string out; + for (size_t i = 0; i < v.size(); ++i) { + if (v[i] == '\\' && i + 1 < v.size()) + ++i; + out += v[i]; + } + return out; +} + +ParsedAva parse_ava(const std::string &text) +{ + size_t eq = text.find('='); + if (eq == std::string::npos) + fail(EWRONGNAME, "invalid name component '" + trim(text) + "'"); + std::string key = trim(text.substr(0, eq)); + std::string value = unescape(text.substr(eq + 1)); + ParsedAva a; + if (const AttrKeyword *k = keyword_by_name(key)) { + a.oid = k->oid; + a.tag = k->tag; + } else { + a.oid = parse_dotted(key); + if (a.oid.empty()) + fail(EWRONGNAME, "unknown attribute type '" + key + "'"); + a.tag = der::PrintableString; + } + if (a.tag == der::PrintableString && !printable(value)) + a.tag = der::UTF8String; + Bytes tlv = der::string(a.tag, value); + a.value = der::parse_one(tlv).value(); + return a; +} + +} // namespace + +DName *string_to_dname(const char *s) +{ + if (!s) + fail(EWRONGNAME, "missing name"); + std::vector> rdns; + for (const std::string &rdn : split(s, ",;")) { + if (trim(rdn).empty()) + continue; + std::vector avas; + for (const std::string &ava : split(rdn, "+")) + avas.push_back(parse_ava(ava)); + rdns.push_back(std::move(avas)); + } + if (rdns.empty()) + fail(EWRONGNAME, "empty name"); + std::reverse(rdns.begin(), rdns.end()); + DName *head = nullptr, **tail = &head; + for (const auto &avas : rdns) { + DName *d = mem_new(); + *tail = d; + tail = &d->next; + RDName **rtail = &d->element_IF_2; + for (const auto &a : avas) { + RDName *r = mem_new(); + *rtail = r; + rtail = &r->next; + r->member_IF_0 = mem_new(); + r->member_IF_0->element_IF_0 = new_objid(a.oid); + r->member_IF_0->element_IF_1 = new_ostr(a.value); + r->member_IF_0->attr_encoding = static_cast(a.tag); + } + } + return head; +} + +static std::string ava_to_string(const AttrValueAssertion *ava) +{ + Oid o = oid_of(ava->element_IF_0); + const AttrKeyword *k = keyword_by_oid(o); + std::string key = k ? k->keyword : oid_to_string(o); + std::string v = value_string(ava); + bool quote = v.find_first_of(",;+\"") != std::string::npos || (!v.empty() && (v.front() == ' ' || v.back() == ' ')); + if (quote) { + std::string q = "\""; + for (char c : v) { + if (c == '"' || c == '\\') + q += '\\'; + q += c; + } + v = q + "\""; + } + return key + "=" + v; +} + +std::string dname_to_string(const DName *d) +{ + std::vector rdns; + for (; d; d = d->next) { + std::string r; + for (const RDName *rd = d->element_IF_2; rd; rd = rd->next) { + if (!rd->member_IF_0) + continue; + if (!r.empty()) + r += " + "; + r += ava_to_string(rd->member_IF_0); + } + rdns.push_back(r); + } + std::string out; + for (auto it = rdns.rbegin(); it != rdns.rend(); ++it) { + if (!out.empty()) + out += ", "; + out += *it; + } + return out; +} + +bool dname_equal(const DName *a, const DName *b) +{ + for (; a && b; a = a->next, b = b->next) { + const RDName *x = a->element_IF_2, *y = b->element_IF_2; + for (; x && y; x = x->next, y = y->next) { + if (!x->member_IF_0 || !y->member_IF_0) + return false; + if (!oid_equal(x->member_IF_0->element_IF_0, y->member_IF_0->element_IF_0)) + return false; + if (normalize(value_string(x->member_IF_0)) != normalize(value_string(y->member_IF_0))) + return false; + } + if (x || y) + return false; + } + return !a && !b; +} + +} // namespace compat + +using namespace compat; + +extern "C" { + +DName *aux_Name2DName(char *name) +{ + return guarded("aux_Name2DName", nullptr, [&] { return string_to_dname(name); }); +} + +char *aux_DName2Name(DName *name_ae) +{ + return guarded("aux_DName2Name", nullptr, [&]() -> char * { + if (!name_ae) + fail(ENONAME, "missing name"); + return mem_strdup(dname_to_string(name_ae)); + }); +} + +char *aux_DName2Attr(DName *dname, char *attr_key) +{ + return guarded("aux_DName2Attr", nullptr, [&]() -> char * { + if (!attr_key) + fail(EINVALID, "missing attribute keyword"); + Oid wanted; + if (const AttrKeyword *k = keyword_by_name(attr_key)) + wanted = k->oid; + else + wanted = parse_dotted(attr_key); + for (const DName *d = dname; d; d = d->next) + for (const RDName *r = d->element_IF_2; r; r = r->next) + if (r->member_IF_0 && oid_equal(r->member_IF_0->element_IF_0, wanted)) + return mem_strdup(value_string(r->member_IF_0)); + fail(ENONAME, std::string("attribute ") + attr_key + " not present"); + }); +} + +int aux_cmp_DName(DName *a, DName *b) +{ + return dname_equal(a, b) ? 0 : 1; +} + +} // extern "C" diff --git a/port/secude/src/dump.cpp b/port/secude/src/dump.cpp new file mode 100644 index 000000000..fb6465b02 --- /dev/null +++ b/port/secude/src/dump.cpp @@ -0,0 +1,298 @@ +// Text dumps: ASN.1 structure dump (sdumpasn) and hex dumps (aux_sxdump). +#include "der.hpp" + +#include +#include + +namespace compat { + +namespace { + +const char *tag_name(uint8_t tag) +{ + switch (tag & 0x1f) { + case 0x01: return "BOOLEAN"; + case 0x02: return "INTEGER"; + case 0x03: return "BIT STRING"; + case 0x04: return "OCTET STRING"; + case 0x05: return "NULL"; + case 0x06: return "OBJECT IDENTIFIER"; + case 0x0A: return "ENUMERATED"; + case 0x0C: return "UTF8String"; + case 0x10: return "SEQUENCE"; + case 0x11: return "SET"; + case 0x12: return "NumericString"; + case 0x13: return "PrintableString"; + case 0x14: return "T61String"; + case 0x16: return "IA5String"; + case 0x17: return "UTCTime"; + case 0x18: return "GeneralizedTime"; + case 0x1A: return "VisibleString"; + case 0x1E: return "BMPString"; + default: return nullptr; + } +} + +struct Ber { + uint8_t tag; + size_t hdr; + size_t len; + bool indefinite; +}; + +// Parses one BER header at p; returns false if malformed. +bool ber_header(const uint8_t *p, size_t n, Ber &b) +{ + if (n < 2 || (p[0] & 0x1f) == 0x1f) + return false; + b.tag = p[0]; + b.indefinite = false; + if (p[1] < 0x80) { + b.len = p[1]; + b.hdr = 2; + } else if (p[1] == 0x80) { + if (!(b.tag & 0x20)) + return false; + b.indefinite = true; + b.len = 0; + b.hdr = 2; + } else { + size_t k = p[1] & 0x7f; + if (k > 4 || n < 2 + k) + return false; + b.len = 0; + for (size_t i = 0; i < k; ++i) + b.len = (b.len << 8) | p[2 + i]; + b.hdr = 2 + k; + } + return b.indefinite || b.len <= n - b.hdr; +} + +class Dumper { +public: + Dumper(int options, int base) : options_(options), base_(base) {} + + // Dumps all elements in [p, p+n); returns consumed length or SIZE_MAX on error. + size_t elements(const uint8_t *p, size_t n, size_t offset, int depth, bool until_eoc) + { + size_t pos = 0; + while (pos < n) { + if (until_eoc && n - pos >= 2 && p[pos] == 0 && p[pos + 1] == 0) + return pos + 2; + size_t used = element(p + pos, n - pos, offset + pos, depth); + if (used == SIZE_MAX) + return SIZE_MAX; + pos += used; + } + return until_eoc ? SIZE_MAX : pos; + } + + size_t element(const uint8_t *p, size_t n, size_t offset, int depth) + { + Ber b; + if (!ber_header(p, n, b)) + return SIZE_MAX; + std::string indent(size_t(depth) * 2, ' '); + std::string head = str_printf("%6lu %4s: ", static_cast(offset + size_t(base_)), + b.indefinite ? "NDEF" : std::to_string(b.len).c_str()); + std::string name = describe_tag(b.tag); + const uint8_t *v = p + b.hdr; + if (b.tag & 0x20) { + out_ += head + indent + name + " {\n"; + size_t used = b.indefinite ? elements(v, n - b.hdr, offset + b.hdr, depth + 1, true) + : elements(v, b.len, offset + b.hdr, depth + 1, false); + if (used == SIZE_MAX) + return SIZE_MAX; + out_ += std::string(6 + 1 + 4 + 2, ' ') + indent + "}\n"; + return b.hdr + (b.indefinite ? used : b.len); + } + out_ += head + indent + name + primitive(b.tag, v, b.len, offset + b.hdr, depth); + return b.hdr + b.len; + } + + std::string text() const { return out_; } + std::string &text() { return out_; } + +private: + std::string describe_tag(uint8_t tag) const + { + uint8_t cls = tag & 0xc0; + if (cls == 0) { + const char *n = tag_name(tag); + return n ? n : str_printf("[UNIVERSAL %u]", tag & 0x1f); + } + if (cls == 0x80) + return str_printf("[%u]", tag & 0x1f); + return str_printf("[%s %u]", cls == 0x40 ? "APPLICATION" : "PRIVATE", tag & 0x1f); + } + + bool encapsulated(const uint8_t *v, size_t n) const + { + if (n < 2 || (v[0] != 0x30 && v[0] != 0x31)) + return false; + size_t pos = 0; + while (pos < n) { + Ber b; + if (!ber_header(v + pos, n - pos, b) || b.indefinite) + return false; + pos += b.hdr + b.len; + } + return pos == n; + } + + std::string hexlines(const uint8_t *v, size_t n, int depth) const + { + size_t limit = (options_ & ASN1dump_no_length_limit) ? n : std::min(n, 128); + std::string indent(size_t(6 + 1 + 4 + 2 + 2 * depth + 2), ' '); + std::string s = hexdump(v, limit, indent, false); + if (limit < n) + s += indent + str_printf("[ Another %lu bytes skipped ]\n", static_cast(n - limit)); + return s; + } + + std::string primitive(uint8_t tag, const uint8_t *v, size_t n, size_t offset, int depth) + { + if (tag & 0xc0) + return "\n" + hexlines(v, n, depth); + try { + Bytes tlv = der::tlv(tag, v, n); + der::Node node = der::parse_one(tlv); + switch (tag) { + case 0x01: + return std::string(" ") + (der::get_boolean(node) ? "TRUE" : "FALSE") + "\n"; + case 0x02: + case 0x0A: + if (n <= 4) + return " " + std::to_string(der::get_integer(node)) + "\n"; + return "\n" + hexlines(v, n, depth); + case 0x05: + return "\n"; + case 0x06: { + Oid o = der::get_oid(node); + const AlgInfo *a = alg_by_oid(o); + return " " + oid_to_string(o) + (a ? std::string(" (") + a->name + ")" : "") + "\n"; + } + case 0x03: + case 0x04: { + const uint8_t *data = tag == 0x03 ? v + 1 : v; + size_t len = tag == 0x03 ? (n ? n - 1 : 0) : n; + if (tag == 0x03 && (n == 0 || v[0] != 0)) + return "\n" + hexlines(v, n, depth); + if (encapsulated(data, len)) { + Dumper inner(options_, base_); + inner.elements(data, len, offset + (tag == 0x03 ? 1 : 0), depth + 1, false); + return ", encapsulates {\n" + inner.text() + std::string(6 + 1 + 4 + 2, ' ') + + std::string(size_t(depth) * 2, ' ') + "}\n"; + } + return "\n" + hexlines(v, n, depth); + } + case 0x17: + case 0x18: + return " " + format_time(der::get_time(node).c_str()) + "\n"; + default: + if (tag_name(tag)) + return " '" + der::get_string(node) + "'\n"; + return "\n" + hexlines(v, n, depth); + } + } catch (const Error &) { + return "\n" + hexlines(v, n, depth); + } + } + + int options_; + int base_; + std::string out_; +}; + +std::string dump_asn1(const uint8_t *p, size_t n, int options, int base) +{ + Dumper d(options, base); + if (n == 0) + return "FATAL: decoding failed! (empty input)\n"; + if (options & ASN1dump_multiple_PDU) { + if (d.elements(p, n, 0, 0, false) == SIZE_MAX) + d.text() += "FATAL: decoding failed!\n"; + return d.text(); + } + size_t used = d.element(p, n, 0, 0); + if (used == SIZE_MAX) { + d.text() += "FATAL: decoding failed!\n"; + return d.text(); + } + if (used < n) + d.text() += str_printf("junk at end of PDU (%lu octets)\n", static_cast(n - used)); + return d.text(); +} + +} // namespace + +std::string hexdump(const uint8_t *p, size_t n, const std::string &indent, bool ascii) +{ + std::string s; + for (size_t off = 0; off < n; off += 16) { + std::string line = indent + str_printf("%6lX ", static_cast(off)); + std::string text; + for (size_t i = 0; i < 16; ++i) { + if (i && i % 4 == 0) + line += ' '; + if (i == 8) + line += ' '; + if (off + i < n) { + uint8_t c = p[off + i]; + line += str_printf("%02X", c); + text += (c >= 0x20 && c < 0x7f) ? char(c) : '.'; + } else if (ascii) { + line += " "; + } + } + if (ascii) + line += " " + text; + while (!line.empty() && line.back() == ' ') + line.pop_back(); + s += line + "\n"; + } + return s; +} + +} // namespace compat + +using namespace compat; + +extern "C" { + +char *sdumpasn(OctetString *ostr, int options, int displayed_offset) +{ + return guarded("sdumpasn", nullptr, [&] { + Bytes b = bytes_of(ostr); + return mem_strdup(dump_asn1(b.data(), b.size(), options, displayed_offset)); + }); +} + +void aux_adump(char *from, int num, int options, int displayed_offset) +{ + guarded_void("aux_adump", [&] { + if (!from || num < 0) + fail(EINVALID, "missing input"); + std::fputs(dump_asn1(reinterpret_cast(from), size_t(num), options, displayed_offset).c_str(), stdout); + }); +} + +char *aux_sxdump(char *string, char *buffer, sec_int4 len, sec_int4 addr_type) +{ + return guarded("aux_sxdump", nullptr, [&] { + if (!buffer && len > 0) + fail(EINVALID, "missing buffer"); + return append_string(string, hexdump(reinterpret_cast(buffer), len > 0 ? size_t(len) : 0, "", true)); + }); +} + +char *aux_sxdump2(char *string, char *buffer, sec_int4 len, sec_int4 addr_type) +{ + return guarded("aux_sxdump2", nullptr, [&] { + if (!buffer && len > 0) + fail(EINVALID, "missing buffer"); + return append_string(string, hexdump(reinterpret_cast(buffer), len > 0 ? size_t(len) : 0, "", false)); + }); +} + +} // extern "C" diff --git a/port/secude/src/encode.cpp b/port/secude/src/encode.cpp new file mode 100644 index 000000000..4bc9a43d7 --- /dev/null +++ b/port/secude/src/encode.cpp @@ -0,0 +1,861 @@ +// DER encoding/decoding of SECUDE structures (X.509, PKCS#1, PKCS#8, PKRoot, FCPath). +#include "der.hpp" + +#include + +namespace compat { + +namespace { + +ParmType parm_type(const ObjId *o) +{ + const AlgInfo *a = alg_by_oid(o); + return a ? a->parm : NoParmType; +} + +Bytes enc_ostr_value(const OctetString &o) +{ + return der::octet_string(bytes_of(&o)); +} + +Bytes enc_int_part(const OctetString &o) +{ + Bytes b = bytes_of(&o); + return der::integer_unsigned(b); +} + +void fill_part(OctetString &dst, const der::Node &n) +{ + fill_ostr(&dst, der::get_unsigned(n)); +} + +AlgId *dec_algid_node(const der::Node &node); +DName *dec_dname_node(const der::Node &node); +KeyInfo *dec_keyinfo_node(const der::Node &node); +CertExtensions *dec_extensions_node(const der::Node &node); + +} // namespace + +std::string utc_now(long offset_seconds) +{ + std::time_t t = std::time(nullptr) + offset_seconds; + std::tm tm{}; + gmtime_r(&t, &tm); + char buf[32]; + if (tm.tm_year + 1900 >= 2050) + std::strftime(buf, sizeof buf, "%Y%m%d%H%M%SZ", &tm); + else + std::strftime(buf, sizeof buf, "%y%m%d%H%M%SZ", &tm); + return buf; +} + +// ---- AlgorithmIdentifier ---------------------------------------------------- + +Bytes enc_algid(const AlgId *a) +{ + if (!a || !a->objid) + fail(EALGID, "missing algorithm identifier"); + Bytes body = der::oid(oid_of(a->objid)); + ParmType pt = parm_type(a->objid); + if (a->param) { + switch (pt) { + case PARM_INTEGER: + der::append(body, der::integer(*static_cast(a->param))); + break; + case PARM_OctetString: + der::append(body, der::octet_string(bytes_of(static_cast(a->param)))); + break; + case PARM_KeyBits: { + auto *k = static_cast(a->param); + der::append(body, der::seq({enc_int_part(k->part1), enc_int_part(k->part2), enc_int_part(k->part3)})); + break; + } + case PARM_RC2CBC: { + auto *p = static_cast(a->param); + der::append(body, der::seq({der::integer(p->version), enc_ostr_value(p->IV)})); + break; + } + case PARM_PKCS5: { + auto *p = static_cast(a->param); + der::append(body, der::seq({enc_ostr_value(p->salt), der::integer(p->iterationCount)})); + break; + } + case NoParmType: + der::append(body, bytes_of(static_cast(a->param))); + break; + case PARM_NULL: + der::append(body, der::null()); + break; + default: + break; + } + } else { + ParmType effective = a->parmoverride ? *a->parmoverride : pt; + if (effective == PARM_NULL) + der::append(body, der::null()); + } + return der::tlv(der::SEQUENCE, body); +} + +namespace { + +AlgId *dec_algid_node(const der::Node &node) +{ + if (node.tag != der::SEQUENCE) + fail(EDECODE, "AlgorithmIdentifier expected"); + der::Reader r(node); + Oid oid = der::get_oid(r.next()); + AlgId *a = mem_new(); + try { + a->objid = new_objid(oid); + ParmType pt = parm_type(a->objid); + if (r.empty()) { + if (pt == PARM_NULL) { + a->parmoverride = mem_new(); + *a->parmoverride = PARM_ABSENT; + } + return a; + } + der::Node p = r.next(); + r.expect_end(); + if (p.tag == der::NULL_ && pt != NoParmType) { + if (pt != PARM_NULL) { + a->parmoverride = mem_new(); + *a->parmoverride = PARM_NULL; + } + return a; + } + switch (pt) { + case PARM_INTEGER: { + unsigned int *v = mem_new(); + *v = static_cast(der::get_integer(p)); + a->param = v; + break; + } + case PARM_OctetString: + if (p.tag != der::OCTET_STRING) + fail(EDECODE, "OCTET STRING parameter expected"); + a->param = new_ostr(p.value()); + break; + case PARM_KeyBits: { + if (p.tag != der::SEQUENCE) + fail(EDECODE, "domain parameters expected"); + der::Reader pr(p); + KeyBits *k = mem_new(); + a->param = k; + fill_part(k->part1, pr.next(der::INTEGER)); + fill_part(k->part2, pr.next(der::INTEGER)); + fill_part(k->part3, pr.next(der::INTEGER)); + k->choice = 3; + break; + } + case PARM_RC2CBC: { + auto *rp = mem_new(); + a->param = rp; + if (p.tag == der::OCTET_STRING) { + rp->version = 58; + fill_ostr(&rp->IV, p.value()); + } else { + der::Reader pr(p); + der::Node n; + if (pr.next_optional(der::INTEGER, n)) + rp->version = int(der::get_integer(n)); + else + rp->version = 58; + fill_ostr(&rp->IV, pr.next(der::OCTET_STRING).value()); + } + break; + } + case PARM_PKCS5: { + auto *pp = mem_new(); + a->param = pp; + der::Reader pr(p); + fill_ostr(&pp->salt, pr.next(der::OCTET_STRING).value()); + pp->iterationCount = int(der::get_integer(pr.next(der::INTEGER))); + break; + } + case NoParmType: + a->param = new_ostr(p.raw()); + break; + default: + break; + } + return a; + } catch (...) { + free_algid(a); + throw; + } +} + +} // namespace + +AlgId *dec_algid(const Bytes &der_bytes) +{ + return dec_algid_node(der::parse_one(der_bytes)); +} + +// ---- KeyBits / KeyInfo ---------------------------------------------------------- + +Bytes enc_keybits(const KeyBits *k) +{ + if (!k) + fail(EINVALID, "missing KeyBits"); + const OctetString *parts[] = {&k->part1, &k->part2, &k->part3, &k->part4, &k->part5}; + int n = k->choice; + if (n < 1 || n > 5) { + n = 0; + while (n < 5 && parts[n]->noctets) + ++n; + } + if (n == 0) + fail(EINVALID, "empty KeyBits"); + Bytes body; + for (int i = 0; i < n; ++i) + der::append(body, enc_int_part(*parts[i])); + return der::tlv(der::SEQUENCE, body); +} + +KeyBits *dec_keybits(const Bytes &der_bytes) +{ + der::Node node = der::parse_one(der_bytes); + KeyBits *k = mem_new(); + try { + OctetString *parts[] = {&k->part1, &k->part2, &k->part3, &k->part4, &k->part5}; + if (node.tag == der::INTEGER) { + fill_part(k->part1, node); + k->choice = 1; + return k; + } + if (node.tag != der::SEQUENCE) + fail(EDECODE, "KeyBits expected"); + der::Reader r(node); + int n = 0; + while (!r.empty()) { + if (n == 5) + fail(EDECODE, "too many key components"); + fill_part(*parts[n++], r.next(der::INTEGER)); + } + k->choice = n; + return k; + } catch (...) { + free_keybits_content(k); + mem_free(k); + throw; + } +} + +Bytes enc_keyinfo(const KeyInfo *k) +{ + if (!k) + fail(EINVALID, "missing KeyInfo"); + return der::seq({enc_algid(k->subjectAI), + der::bit_string(reinterpret_cast(k->subjectkey.bits), k->subjectkey.nbits)}); +} + +namespace { + +KeyInfo *dec_keyinfo_node(const der::Node &node) +{ + if (node.tag != der::SEQUENCE) + fail(EDECODE, "SubjectPublicKeyInfo expected"); + der::Reader r(node); + KeyInfo *k = mem_new(); + try { + k->subjectAI = dec_algid_node(r.next(der::SEQUENCE)); + size_t nbits = 0; + Bytes bits = der::get_bit_string(r.next(), nbits); + fill_bstr(&k->subjectkey, bits, nbits); + r.expect_end(); + return k; + } catch (...) { + free_keyinfo(k); + throw; + } +} + +} // namespace + +KeyInfo *dec_keyinfo(const Bytes &der_bytes) +{ + return dec_keyinfo_node(der::parse_one(der_bytes)); +} + +// ---- Name ------------------------------------------------------------------- + +Bytes enc_dname(const DName *d) +{ + Bytes body; + for (; d; d = d->next) { + std::vector atvs; + for (const RDName *r = d->element_IF_2; r; r = r->next) { + const AttrValueAssertion *ava = r->member_IF_0; + if (!ava || !ava->element_IF_0) + continue; + int tag = ava->attr_encoding; + if (tag <= 0 || tag > 30) + tag = der::UTF8String; + Bytes value = bytes_of(ava->element_IF_1); + atvs.push_back(der::seq({der::oid(oid_of(ava->element_IF_0)), der::tlv(uint8_t(tag), value)})); + } + der::append(body, der::set_of(atvs)); + } + return der::tlv(der::SEQUENCE, body); +} + +namespace { + +DName *dec_dname_node(const der::Node &node) +{ + if (node.tag != der::SEQUENCE) + fail(EDECODE, "Name expected"); + DName *head = nullptr, **tail = &head; + try { + der::Reader r(node); + while (!r.empty()) { + der::Node set = r.next(der::SET); + DName *dn = mem_new(); + *tail = dn; + tail = &dn->next; + RDName **rtail = &dn->element_IF_2; + der::Reader sr(set); + while (!sr.empty()) { + der::Reader ar(sr.next(der::SEQUENCE)); + RDName *rd = mem_new(); + *rtail = rd; + rtail = &rd->next; + AttrValueAssertion *ava = mem_new(); + rd->member_IF_0 = ava; + ava->element_IF_0 = new_objid(der::get_oid(ar.next())); + der::Node v = ar.next(); + ava->element_IF_1 = new_ostr(v.value()); + ava->attr_encoding = static_cast(v.tag & 0x1f); + ar.expect_end(); + } + } + return head; + } catch (...) { + free_dname(head); + throw; + } +} + +} // namespace + +DName *dec_dname(const Bytes &der_bytes) +{ + return dec_dname_node(der::parse_one(der_bytes)); +} + +// ---- Validity, extensions, certificates -------------------------------------- + +Bytes enc_validity(const Validity *v) +{ + if (!v || !v->notbefore || !v->notafter) + fail(EINVALID, "missing validity"); + return der::seq({der::time(v->notbefore), der::time(v->notafter)}); +} + +static Bytes extension(const Oid &id, bool critical, const Bytes &value) +{ + Bytes body = der::oid(id); + if (critical) + der::append(body, der::boolean(true)); + der::append(body, der::octet_string(value)); + return der::tlv(der::SEQUENCE, body); +} + +Bytes enc_extensions(const CertExtensions *e) +{ + if (!e) + return {}; + Bytes body; + if (e->subjectKeyId && e->subjectKeyId->extnValue) + der::append(body, extension(oids::subjectKeyIdentifier, e->subjectKeyId->critical, + der::octet_string(bytes_of(e->subjectKeyId->extnValue)))); + if (e->authorityKeyId && e->authorityKeyId->extnValue && e->authorityKeyId->extnValue->authorityKeyIdentifier) + der::append(body, extension(oids::authorityKeyIdentifier, e->authorityKeyId->critical, + der::seq({der::tlv(der::ctx(0, false), bytes_of(e->authorityKeyId->extnValue->authorityKeyIdentifier))}))); + if (e->keyUsage && e->keyUsage->extnValue) { + const KeyUsage *ku = e->keyUsage->extnValue; + const Boolean flags[] = {ku->digitalSignature, ku->nonRepudiation, ku->keyEncipherment, ku->dataEncipherment, + ku->keyAgreement, ku->keyCertSign, ku->cRLSign, ku->encipherOnly, ku->decipherOnly}; + uint8_t bits[2] = {0, 0}; + size_t nbits = 0; + for (size_t i = 0; i < 9; ++i) + if (flags[i]) { + bits[i / 8] |= uint8_t(0x80 >> (i % 8)); + nbits = i + 1; + } + der::append(body, extension(oids::keyUsage, e->keyUsage->critical, der::bit_string(bits, nbits))); + } + if (e->basicConstraints && e->basicConstraints->extnValue) { + const BasicConstraints *bc = e->basicConstraints->extnValue; + Bytes v; + if (bc->cA) + der::append(v, der::boolean(true)); + if (bc->pathLenConstraint >= 0) + der::append(v, der::integer(bc->pathLenConstraint)); + der::append(body, extension(oids::basicConstraints, e->basicConstraints->critical, der::tlv(der::SEQUENCE, v))); + } + for (const SEQUENCE_OF_Extension *s = e->nonSupported; s; s = s->next) { + if (!s->element || !s->element->extnId) + continue; + der::append(body, extension(oid_of(s->element->extnId), s->element->critical, bytes_of(s->element->extnDERcode))); + } + if (body.empty()) + return {}; + return der::tlv(der::SEQUENCE, body); +} + +namespace { + +CertExtensions *dec_extensions_node(const der::Node &node) +{ + CertExtensions *e = mem_new(); + try { + der::Reader r(node); + SEQUENCE_OF_Extension **tail = &e->nonSupported; + int seq = 0; + while (!r.empty()) { + der::Reader xr(r.next(der::SEQUENCE)); + auto *n = mem_new(); + *tail = n; + tail = &n->next; + n->element = mem_new(); + n->element->extnId = new_objid(der::get_oid(xr.next())); + der::Node c; + if (xr.next_optional(der::BOOLEAN, c)) + n->element->critical = der::get_boolean(c); + n->element->extnDERcode = new_ostr(xr.next(der::OCTET_STRING).value()); + n->element->seqnum = seq++; + xr.expect_end(); + } + return e; + } catch (...) { + free_extensions(e); + throw; + } +} + +} // namespace + +CertExtensions *dec_extensions(const Bytes &der_bytes) +{ + der::Node n = der::parse_one(der_bytes); + if (n.tag != der::SEQUENCE) + fail(EDECODE, "Extensions expected"); + return dec_extensions_node(n); +} + +Bytes enc_tbs(const ToBeSigned *t) +{ + if (!t) + fail(EINVALID, "missing ToBeSigned"); + Bytes ext = enc_extensions(t->extensions); + int version = t->version; + if (!ext.empty() && version < 2) + version = 2; + Bytes body; + if (version > 0) + der::append(body, der::tlv(der::ctx(0), der::integer(version))); + Bytes serial = bytes_of(t->serialnumber); + if (serial.empty()) + serial.push_back(0); + der::append(body, der::integer_unsigned(serial)); + der::append(body, enc_algid(t->signatureAI)); + der::append(body, enc_dname(t->issuer)); + der::append(body, enc_validity(t->valid)); + der::append(body, enc_dname(t->subject)); + der::append(body, enc_keyinfo(t->subjectPK)); + if (t->issuerUId) { + Bytes bs = der::bit_string(reinterpret_cast(t->issuerUId->bits), t->issuerUId->nbits); + bs[0] = der::ctx(1, false); + der::append(body, bs); + } + if (t->subjectUId) { + Bytes bs = der::bit_string(reinterpret_cast(t->subjectUId->bits), t->subjectUId->nbits); + bs[0] = der::ctx(2, false); + der::append(body, bs); + } + if (!ext.empty()) + der::append(body, der::tlv(der::ctx(3), ext)); + return der::tlv(der::SEQUENCE, body); +} + +Bytes enc_signature_value(const Signature *s) +{ + if (!s) + fail(EINVALID, "missing signature"); + return der::bit_string(reinterpret_cast(s->signature.bits), s->signature.nbits); +} + +Bytes enc_certificate(const Certificate *c) +{ + if (!c || !c->sig) + fail(EINVALID, "incomplete certificate"); + Bytes tbs = c->tbs_DERcode && c->tbs_DERcode->noctets ? bytes_of(c->tbs_DERcode) : enc_tbs(c->tbs); + return der::seq({tbs, enc_algid(c->sig->signAI), enc_signature_value(c->sig)}); +} + +namespace { + +ToBeSigned *dec_tbs_node(const der::Node &node) +{ + if (node.tag != der::SEQUENCE) + fail(EDECODE, "TBSCertificate expected"); + ToBeSigned *t = mem_new(); + try { + der::Reader r(node); + der::Node n; + if (r.next_optional(der::ctx(0), n)) { + der::Reader vr(n); + t->version = int(der::get_integer(vr.next())); + } + t->serialnumber = new_ostr(der::get_unsigned(r.next(der::INTEGER))); + t->signatureAI = dec_algid_node(r.next(der::SEQUENCE)); + t->issuer = dec_dname_node(r.next(der::SEQUENCE)); + der::Reader vr(r.next(der::SEQUENCE)); + t->valid = mem_new(); + t->valid->notbefore = mem_strdup(der::get_time(vr.next())); + t->valid->notafter = mem_strdup(der::get_time(vr.next())); + t->subject = dec_dname_node(r.next(der::SEQUENCE)); + t->subjectPK = dec_keyinfo_node(r.next(der::SEQUENCE)); + while (!r.empty()) { + der::Node x = r.next(); + if (x.tag == der::ctx(1, false) || x.tag == der::ctx(2, false)) { + if (x.len == 0 || x.val[0] > 7) + fail(EDECODE, "invalid unique identifier"); + BitString *b = new_bstr(Bytes(x.val + 1, x.val + x.len), (x.len - 1) * 8 - x.val[0]); + (x.tag == der::ctx(1, false) ? t->issuerUId : t->subjectUId) = b; + } else if (x.tag == der::ctx(3)) { + der::Reader er(x); + t->extensions = dec_extensions_node(er.next(der::SEQUENCE)); + } else { + fail(EDECODE, "unexpected element in TBSCertificate"); + } + } + return t; + } catch (...) { + free_tbs(t); + throw; + } +} + +Certificate *dec_certificate_node(const der::Node &node) +{ + if (node.tag != der::SEQUENCE) + fail(EDECODE, "Certificate expected"); + Certificate *c = mem_new(); + try { + der::Reader r(node); + der::Node tbs = r.next(der::SEQUENCE); + c->tbs_DERcode = new_ostr(tbs.raw()); + c->tbs = dec_tbs_node(tbs); + c->sig = mem_new(); + c->sig->signAI = dec_algid_node(r.next(der::SEQUENCE)); + size_t nbits = 0; + Bytes bits = der::get_bit_string(r.next(), nbits); + fill_bstr(&c->sig->signature, bits, nbits); + r.expect_end(); + return c; + } catch (...) { + free_certificate(c); + throw; + } +} + +} // namespace + +Certificate *dec_certificate(const Bytes &der_bytes) +{ + return dec_certificate_node(der::parse_one(der_bytes)); +} + +// ---- PKRoot / FCPath (formats of this compatibility layer) -------------------- + +static Bytes enc_serial_key(const Serial *s) +{ + if (!s || !s->key) + fail(EINVALID, "incomplete PKRoot key"); + Bytes serial = bytes_of(s->serial); + if (serial.empty()) + serial.push_back(0); + Bytes body = der::concat({der::integer_unsigned(serial), der::integer(s->version), enc_keyinfo(s->key)}); + if (s->valid) + der::append(body, enc_validity(s->valid)); + if (s->sig) + der::append(body, der::tlv(der::ctx(1), der::seq({enc_algid(s->sig->signAI), enc_signature_value(s->sig)}))); + return der::tlv(der::SEQUENCE, body); +} + +static Serial *dec_serial_key(const der::Node &node) +{ + Serial *s = mem_new(); + try { + der::Reader r(node); + s->serial = new_ostr(der::get_unsigned(r.next(der::INTEGER))); + s->version = int(der::get_integer(r.next(der::INTEGER))); + s->key = dec_keyinfo_node(r.next(der::SEQUENCE)); + der::Node n; + if (r.next_optional(der::SEQUENCE, n)) { + der::Reader vr(n); + s->valid = mem_new(); + s->valid->notbefore = mem_strdup(der::get_time(vr.next())); + s->valid->notafter = mem_strdup(der::get_time(vr.next())); + } + if (r.next_optional(der::ctx(1), n)) { + der::Reader sr(der::Reader(n).next(der::SEQUENCE)); + s->sig = mem_new(); + s->sig->signAI = dec_algid_node(sr.next(der::SEQUENCE)); + size_t nbits = 0; + Bytes bits = der::get_bit_string(sr.next(), nbits); + fill_bstr(&s->sig->signature, bits, nbits); + } + r.expect_end(); + return s; + } catch (...) { + free_serial(s); + throw; + } +} + +Bytes enc_pkroot(const PKRoot *p) +{ + if (!p || !p->newkey) + fail(EINVALID, "incomplete PKRoot"); + Bytes body = der::concat({enc_dname(p->ca), enc_serial_key(p->newkey)}); + if (p->oldkey) + der::append(body, der::tlv(der::ctx(0), enc_serial_key(p->oldkey))); + Bytes ext = enc_extensions(p->extensions); + if (!ext.empty()) + der::append(body, der::tlv(der::ctx(3), ext)); + return der::tlv(der::SEQUENCE, body); +} + +PKRoot *dec_pkroot(const Bytes &der_bytes) +{ + der::Node node = der::parse_one(der_bytes); + if (node.tag != der::SEQUENCE) + fail(EDECODE, "PKRoot expected"); + PKRoot *p = mem_new(); + try { + der::Reader r(node); + p->ca = dec_dname_node(r.next(der::SEQUENCE)); + p->newkey = dec_serial_key(r.next(der::SEQUENCE)); + der::Node n; + if (r.next_optional(der::ctx(0), n)) + p->oldkey = dec_serial_key(der::Reader(n).next(der::SEQUENCE)); + if (r.next_optional(der::ctx(3), n)) + p->extensions = dec_extensions_node(der::Reader(n).next(der::SEQUENCE)); + r.expect_end(); + return p; + } catch (...) { + free_pkroot(p); + throw; + } +} + +Bytes enc_fcpath(const FCPath *f) +{ + Bytes body; + for (; f; f = f->next_forwardpath) { + std::vector certs; + for (const SET_OF_Certificate *s = f->liste; s; s = s->next) + if (s->element) + certs.push_back(enc_certificate(s->element)); + der::append(body, der::set_of(certs)); + } + return der::tlv(der::SEQUENCE, body); +} + +FCPath *dec_fcpath(const Bytes &der_bytes) +{ + der::Node node = der::parse_one(der_bytes); + if (node.tag != der::SEQUENCE) + fail(EDECODE, "FCPath expected"); + FCPath *head = nullptr, **tail = &head; + try { + der::Reader r(node); + while (!r.empty()) { + FCPath *f = mem_new(); + *tail = f; + tail = &f->next_forwardpath; + SET_OF_Certificate **ctail = &f->liste; + der::Reader sr(r.next(der::SET)); + while (!sr.empty()) { + auto *e = mem_new(); + *ctail = e; + ctail = &e->next; + e->element = dec_certificate_node(sr.next()); + } + } + return head; + } catch (...) { + free_fcpath(head); + throw; + } +} + +// ---- PKCS#8 ----------------------------------------------------------------- + +Bytes enc_privatekeyinfo(const PrivateKeyInfo *p) +{ + if (!p || !p->privateKeyAlgorithm || !p->privateKey) + fail(EINVALID, "incomplete PrivateKeyInfo"); + return der::seq({der::integer(p->version), enc_algid(p->privateKeyAlgorithm), der::octet_string(bytes_of(p->privateKey))}); +} + +PrivateKeyInfo *dec_privatekeyinfo(const Bytes &der_bytes) +{ + der::Node node = der::parse_one(der_bytes); + if (node.tag != der::SEQUENCE) + fail(EDECODE, "PrivateKeyInfo expected"); + PrivateKeyInfo *p = mem_new(); + try { + der::Reader r(node); + p->version = int(der::get_integer(r.next(der::INTEGER))); + p->privateKeyAlgorithm = dec_algid_node(r.next(der::SEQUENCE)); + p->privateKey = new_ostr(r.next(der::OCTET_STRING).value()); + return p; + } catch (...) { + free_privatekeyinfo(p); + throw; + } +} + +} // namespace compat + +using namespace compat; + +extern "C" { + +OctetString *e_OctetString(OctetString *ostr) +{ + return guarded("e_OctetString", nullptr, [&]() -> OctetString * { + if (!ostr) + fail(EINVALID, "missing OctetString"); + return new_ostr(der::octet_string(bytes_of(ostr))); + }); +} + +OctetString *d_OctetString(OctetString *asn1_string) +{ + return guarded("d_OctetString", nullptr, [&]() -> OctetString * { + Bytes in = bytes_of(asn1_string); + der::Node n = der::parse_one(in); + if (n.tag != der::OCTET_STRING) + fail(EDECODE, "OCTET STRING expected"); + return new_ostr(n.value()); + }); +} + +OctetString *e_PrintableString(char *infostruct) +{ + return guarded("e_PrintableString", nullptr, [&]() -> OctetString * { + if (!infostruct) + fail(EINVALID, "missing string"); + return new_ostr(der::string(der::PrintableString, infostruct)); + }); +} + +char *d_PrintableString(OctetString *asn1string) +{ + return guarded("d_PrintableString", nullptr, [&] { + Bytes in = bytes_of(asn1string); + return mem_strdup(der::get_string(der::parse_one(in))); + }); +} + +OctetString *e_AlgId(AlgId *algid) +{ + return guarded("e_AlgId", nullptr, [&] { return new_ostr(enc_algid(algid)); }); +} + +BitString *e_KeyBits(KeyBits *kb) +{ + return guarded("e_KeyBits", nullptr, [&] { + Bytes b = enc_keybits(kb); + return new_bstr(b, b.size() * 8); + }); +} + +KeyBits *d_KeyBits(BitString *s) +{ + return guarded("d_KeyBits", nullptr, [&] { return dec_keybits(bytes_of(s)); }); +} + +OctetString *e_KeyInfo(KeyInfo *ki) +{ + return guarded("e_KeyInfo", nullptr, [&] { return new_ostr(enc_keyinfo(ki)); }); +} + +KeyInfo *d_KeyInfo(OctetString *asn1_string) +{ + return guarded("d_KeyInfo", nullptr, [&] { return dec_keyinfo(bytes_of(asn1_string)); }); +} + +OctetString *e_Certificate(Certificate *certificate) +{ + return guarded("e_Certificate", nullptr, [&] { return new_ostr(enc_certificate(certificate)); }); +} + +Certificate *d_Certificate(OctetString *asn1_string) +{ + return guarded("d_Certificate", nullptr, [&] { return dec_certificate(bytes_of(asn1_string)); }); +} + +OctetString *e_PKRoot(PKRoot *pkroot) +{ + return guarded("e_PKRoot", nullptr, [&] { return new_ostr(enc_pkroot(pkroot)); }); +} + +OctetString *e_FCPath(FCPath *fcpath) +{ + return guarded("e_FCPath", nullptr, [&] { return new_ostr(enc_fcpath(fcpath)); }); +} + +RSAPrivateKey *d_RSAPrivateKey(OctetString *asn1_string) +{ + return guarded("d_RSAPrivateKey", nullptr, [&] { + Bytes in = bytes_of(asn1_string); + der::Node node = der::parse_one(in); + if (node.tag != der::SEQUENCE) + fail(EDECODE, "RSAPrivateKey expected"); + RSAPrivateKey *k = mem_new(); + try { + der::Reader r(node); + k->version = int(der::get_integer(r.next(der::INTEGER))); + for (OctetString *o : {&k->modulus, &k->pubex, &k->privex, &k->prime1, &k->prime2, &k->exp1, &k->exp2, &k->coeff}) + fill_ostr(o, der::get_unsigned(r.next(der::INTEGER))); + return k; + } catch (...) { + aux_free_RSAPrivateKey(&k); + throw; + } + }); +} + +ContentInfo *d_ContentInfo(OctetString *asn1_string) +{ + return guarded("d_ContentInfo", nullptr, [&] { + Bytes in = bytes_of(asn1_string); + der::Node node = der::parse_one(in); + if (node.tag != der::SEQUENCE) + fail(EDECODE, "ContentInfo expected"); + der::Reader r(node); + Oid type = der::get_oid(r.next()); + OctetString *content = nullptr; + der::Node n; + if (r.next_optional(der::ctx(0), n)) { + der::Node inner = der::Reader(n).next(); + content = new_ostr(type == oids::data && inner.tag == der::OCTET_STRING ? inner.value() : inner.raw()); + } + r.expect_end(); + ContentInfo *ci = mem_new(); + ci->contentType = new_objid(type); + ci->content = content; + return ci; + }); +} + +} // extern "C" diff --git a/port/secude/src/error.cpp b/port/secude/src/error.cpp new file mode 100644 index 000000000..893f2c3cc --- /dev/null +++ b/port/secude/src/error.cpp @@ -0,0 +1,192 @@ +#include "internal.hpp" + +#include +#include + +namespace compat { + +namespace { + +struct Entry { + int number; + std::string text; + std::string proc; +}; + +constexpr size_t kMaxErrors = 32; + +std::deque &stack() +{ + thread_local std::deque s; + return s; +} + +struct NamedError { + int code; + const char *name; + const char *text; +}; + +const NamedError kErrors[] = { + {EALGID, "EALGID", "invalid algorithm identifier"}, + {EOBJ, "EOBJ", "PSE object not found"}, + {EKEYSIZE, "EKEYSIZE", "invalid key size"}, + {EVERIFY, "EVERIFY", "signature verification failed"}, + {ESYSTEM, "ESYSTEM", "system error"}, + {EMALLOC, "EMALLOC", "memory allocation failed"}, + {EENCODE, "EENCODE", "ASN.1 encoding failed"}, + {EDECODE, "EDECODE", "ASN.1 decoding failed"}, + {EREADPSE, "EREADPSE", "cannot read PSE"}, + {EWRITEPSE, "EWRITEPSE", "cannot write PSE"}, + {EROOTCERT, "EROOTCERT", "root certificate missing"}, + {ENODIR, "ENODIR", "no CA directory"}, + {EWRITEFILE, "EWRITEFILE", "cannot write file"}, + {EREADFILE, "EREADFILE", "cannot read file"}, + {EINVALID, "EINVALID", "invalid parameter"}, + {EFILENOTEXISTING, "EFILENOTEXISTING", "file does not exist"}, + {EWRONGPARM, "EWRONGPARM", "wrong parameter"}, + {EINTERNAL, "EINTERNAL", "internal error"}, + {ENOTSUPPORTED, "ENOTSUPPORTED", "function not supported"}, + {EUNKNOWNALGID, "EUNKNOWNALGID", "unknown algorithm"}, + {EALGNOTFITKEY, "EALGNOTFITKEY", "algorithm does not fit the key"}, + {EDECRYPTION, "EDECRYPTION", "decryption failed"}, + {EPIN, "EPIN", "wrong PIN"}, + {EPSEALREADYEXISTING, "EPSEALREADYEXISTING", "PSE already exists"}, + {EPSENOTEXISTING, "EPSENOTEXISTING", "PSE does not exist"}, + {EPSEOBJECTNOTEXISTING, "EPSEOBJECTNOTEXISTING", "PSE object does not exist"}, + {EPSEFILE, "EPSEFILE", "not a valid PSE file"}, + {EPSEFILESTRUCTURE, "EPSEFILESTRUCTURE", "invalid PSE file structure"}, + {ECADBCERT, "ECADBCERT", "certificate not found in CA database"}, + {ECADBUSER, "ECADBUSER", "user not found in CA database"}, + {ENOCRL, "ENOCRL", "no revocation list available"}, + {ENONAME, "ENONAME", "no name available"}, + {EVERIFICATION, "EVERIFICATION", "verification failed"}, + {ERANDOM, "ERANDOM", "random number generation failed"}, +}; + +} // namespace + +const char *error_name(int code) +{ + for (const auto &e : kErrors) + if (e.code == code) + return e.name; + return "ERROR"; +} + +static const char *error_text(int code) +{ + for (const auto &e : kErrors) + if (e.code == code) + return e.text; + return "error"; +} + +void fail(int code, const std::string &text) +{ + throw Error(code, text.empty() ? error_text(code) : text); +} + +void push_error(int code, const char *proc, const std::string &text) +{ + auto &s = stack(); + if (s.size() >= kMaxErrors) + s.pop_front(); + s.push_back(Entry{code, text.empty() ? error_text(code) : text, proc ? proc : ""}); +} + +std::string str_printf(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + va_list ap2; + va_copy(ap2, ap); + int n = std::vsnprintf(nullptr, 0, fmt, ap); + va_end(ap); + std::string out; + if (n > 0) { + out.resize(size_t(n) + 1); + std::vsnprintf(&out[0], out.size(), fmt, ap2); + out.resize(size_t(n)); + } + va_end(ap2); + return out; +} + +char *append_string(char *string, const std::string &text) +{ + if (!string) + return mem_strdup(text); + size_t old = std::strlen(string); + char *r = static_cast(mem_realloc(string, old + text.size() + 1)); + std::memcpy(r + old, text.data(), text.size()); + r[old + text.size()] = 0; + return r; +} + +} // namespace compat + +using namespace compat; + +extern "C" { + +int th_last_error(void) +{ + auto &s = stack(); + return s.empty() ? 0 : s.back().number; +} + +char *th_get_last_error_text(void) +{ + auto &s = stack(); + static char empty[1] = {0}; + return s.empty() ? empty : const_cast(s.back().text.c_str()); +} + +struct ErrStack *th_remove_last_error(void) +{ + return guarded("th_remove_last_error", nullptr, [] { + auto &s = stack(); + ErrStack *e = mem_new(); + e->e_is_error = TRUE; + e->e_addrtype = int_n; + if (!s.empty()) { + e->e_number = s.back().number; + e->e_text = mem_strdup(s.back().text); + e->e_proc = mem_strdup(s.back().proc); + s.pop_back(); + } + return e; + }); +} + +void global_add_error(int number, char *text, char *addr, Struct_No addrtype, char *proc) +{ + push_error(number, proc, text ? text : ""); +} + +void aux_free_error(void) +{ + stack().clear(); +} + +char *aux_sprint_error(PSE pse_handle, char *string, int verbose) +{ + return guarded("aux_sprint_error", nullptr, [&] { + auto &s = stack(); + std::string out; + if (s.empty()) + out = "no error\n"; + for (auto it = s.rbegin(); it != s.rend(); ++it) { + out += str_printf("%s (0x%04X): %s", error_name(it->number), unsigned(it->number), it->text.c_str()); + if (verbose && !it->proc.empty()) + out += " [" + it->proc + "]"; + out += "\n"; + if (!verbose) + break; + } + return append_string(string, out); + }); +} + +} // extern "C" diff --git a/port/secude/src/hash.cpp b/port/secude/src/hash.cpp new file mode 100644 index 000000000..626ecfc69 --- /dev/null +++ b/port/secude/src/hash.cpp @@ -0,0 +1,157 @@ +#include "legacy.hpp" + +namespace compat { + +namespace { + +const char *evp_name(Hash h) +{ + switch (h) { + case Hash::MD4: return "MD4"; + case Hash::MD5: return "MD5"; + case Hash::SHA1: return "SHA1"; + case Hash::RIPEMD160: return "RIPEMD160"; + case Hash::SHA256: return "SHA256"; + case Hash::SHA384: return "SHA384"; + case Hash::SHA512: return "SHA512"; + default: return nullptr; + } +} + +class Hasher { +public: + explicit Hasher(Hash h) + { + if (const char *name = evp_name(h)) { + if (EVP_MD *md = fetch_md(name)) { + evp_ = EVP_MD_CTX_new(); + bool ok = evp_ && EVP_DigestInit_ex2(evp_, md, nullptr); + EVP_MD_free(md); + if (!ok) + fail(EINTERNAL, "digest initialisation failed"); + return; + } + } + own_ = legacy::make_digest(h); + if (!own_) + fail(EUNKNOWNALGID, "hash algorithm not available"); + } + ~Hasher() { EVP_MD_CTX_free(evp_); } + Hasher(const Hasher &) = delete; + Hasher &operator=(const Hasher &) = delete; + + void update(const uint8_t *p, size_t n) + { + if (evp_) { + if (n && !EVP_DigestUpdate(evp_, p, n)) + fail(EINTERNAL, "digest update failed"); + } else if (n) { + own_->update(p, n); + } + } + + Bytes final() + { + if (!evp_) + return own_->final(); + Bytes out(EVP_MAX_MD_SIZE); + unsigned int len = 0; + if (!EVP_DigestFinal_ex(evp_, out.data(), &len)) + fail(EINTERNAL, "digest finalisation failed"); + out.resize(len); + return out; + } + +private: + EVP_MD_CTX *evp_ = nullptr; + std::unique_ptr own_; +}; + +Hash hash_of(const AlgId *aid) +{ + if (!aid || !aid->objid) + fail(EALGID, "missing hash algorithm"); + const AlgInfo *a = alg_by_oid(aid->objid); + if (!a || a->hash == Hash::None) + fail(EUNKNOWNALGID, "not a hash algorithm: " + oid_to_string(oid_of(aid->objid))); + return a->hash; +} + +} // namespace + +Bytes digest(Hash h, const uint8_t *p, size_t n) +{ + Hasher hs(h); + hs.update(p, n); + return hs.final(); +} + +size_t digest_size(Hash h) +{ + switch (h) { + case Hash::MD2: + case Hash::MD4: + case Hash::MD5: return 16; + case Hash::SHA0: + case Hash::SHA1: + case Hash::RIPEMD160: return 20; + case Hash::SHA256: return 32; + case Hash::SHA384: return 48; + case Hash::SHA512: return 64; + default: return 0; + } +} + +} // namespace compat + +using namespace compat; + +extern "C" { + +RC sec_hash_init(void **context, AlgId *alg_id, HashInput *hash_input) +{ + return guarded("sec_hash_init", -1, [&] { + if (!context) + fail(EINVALID, "missing context"); + *context = new Hasher(hash_of(alg_id)); + return 0; + }); +} + +RC sec_hash_more(void **context, OctetString *in_octets) +{ + return guarded("sec_hash_more", -1, [&] { + if (!context || !*context) + fail(ECONTEXTINVALID, "hash context not initialised"); + if (in_octets && in_octets->noctets) + static_cast(*context)->update(reinterpret_cast(in_octets->octets), in_octets->noctets); + return 0; + }); +} + +RC sec_hash_end(void **context, OctetString *hash_result) +{ + return guarded("sec_hash_end", -1, [&] { + if (!context || !*context) + fail(ECONTEXTINVALID, "hash context not initialised"); + std::unique_ptr h(static_cast(*context)); + *context = nullptr; + Bytes d = h->final(); + if (hash_result) + fill_ostr(hash_result, d); + return 0; + }); +} + +RC sec_hash_all(OctetString *in_octets, OctetString *hash_result, AlgId *alg_id, HashInput *hash_input) +{ + return guarded("sec_hash_all", -1, [&] { + if (!in_octets || !hash_result) + fail(EINVALID, "missing parameter"); + Bytes d = digest(hash_of(alg_id), reinterpret_cast(in_octets->octets), in_octets->noctets); + fill_ostr(hash_result, d); + return 0; + }); +} + +} // extern "C" diff --git a/port/secude/src/internal.hpp b/port/secude/src/internal.hpp new file mode 100644 index 000000000..687977ff4 --- /dev/null +++ b/port/secude/src/internal.hpp @@ -0,0 +1,313 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +// SECUDE's AlgEnc/AlgHash enumerators (RSA, DSA, SHA1, ...) clash with OpenSSL names, +// and secude/bio.h, secude/ssl.h re-declare OpenSSL's BIO/SSL API. The SSL +// part of SECUDE is not needed, so those headers are suppressed. +#ifndef HEADER_BIO_H +#define HEADER_BIO_H +#endif +#define HEADER_SSL_H +#define HEADER_SSL3_H +#define HEADER_SSL23_H +typedef SSL *H_SSL; +#define RSA SECUDE_ALG_RSA +#define DSA SECUDE_ALG_DSA +#define MD2 SECUDE_HASH_MD2 +#define MD4 SECUDE_HASH_MD4 +#define MD5 SECUDE_HASH_MD5 +#define SHA SECUDE_HASH_SHA +#define SHA1 SECUDE_HASH_SHA1 +#define RIPEMD160 SECUDE_HASH_RIPEMD160 +#include +#include +#include +#include +#undef RSA +#undef DSA +#undef MD2 +#undef MD4 +#undef MD5 +#undef SHA +#undef SHA1 +#undef RIPEMD160 + +// arithmet.h maps short names (div, add, comp, ...) to arithmetic_*; keep them out of C++ code. +#undef div +#undef madd +#undef msub +#undef mmult +#undef mdiv +#undef mexp +#undef mexp2 +#undef cadd +#undef add +#undef sub +#undef mult +#undef dmult +#undef shift +#undef wshift +#undef comp +#undef normalize +#undef trans + +#include "secude_compat.h" + +extern "C" { +// Used by CrypTool but not declared by the headers it includes. +OctetString *aux_alloc_OctetString(void); +extern AlgId md2WithRSASignature_aid; +} + +namespace compat { + +using Bytes = std::vector; + +// ---- memory ------------------------------------------------------------- +// Every block handed out to callers is registered; freeing an unknown +// pointer is ignored (CrypTool occasionally passes stack or foreign memory). +void *mem_alloc(size_t n); +void *mem_realloc(void *p, size_t n); +void mem_free(void *p); +bool mem_owned(const void *p); +char *mem_strdup(const char *s); +char *mem_strdup(const std::string &s); + +template T *mem_new() { return static_cast(mem_alloc(sizeof(T))); } + +OctetString *new_ostr(const uint8_t *data, size_t len); +inline OctetString *new_ostr(const Bytes &b) { return new_ostr(b.data(), b.size()); } +void fill_ostr(OctetString *o, const uint8_t *data, size_t len); +inline void fill_ostr(OctetString *o, const Bytes &b) { fill_ostr(o, b.data(), b.size()); } +BitString *new_bstr(const Bytes &b, size_t nbits); +void fill_bstr(BitString *o, const Bytes &b, size_t nbits); +Bytes bytes_of(const OctetString *o); +Bytes bytes_of(const BitString *b); +Bytes bytes_of(const char *s); + +// ---- errors --------------------------------------------------------------- +struct Error : std::runtime_error { + int code; + Error(int c, const std::string &msg) : std::runtime_error(msg), code(c) {} +}; +[[noreturn]] void fail(int code, const std::string &text = std::string()); +void push_error(int code, const char *proc, const std::string &text); +const char *error_name(int code); + +// Runs f() and converts exceptions into SECUDE errors (returned value: onfail). +template R guarded(const char *proc, R onfail, F &&f) +{ + try { + return f(); + } catch (const Error &e) { + push_error(e.code, proc, e.what()); + } catch (const std::bad_alloc &) { + push_error(EMALLOC, proc, "out of memory"); + } catch (const std::exception &e) { + push_error(EINVALID, proc, e.what()); + } + return onfail; +} + +template void guarded_void(const char *proc, F &&f) +{ + guarded(proc, -1, [&] { f(); return 0; }); +} + +// ---- OIDs and algorithms -------------------------------------------------- +using Oid = std::vector; +Oid oid_of(const ObjId *o); +ObjId *new_objid(const Oid &o); +void fill_objid(ObjId *dst, const Oid &o); +std::string oid_to_string(const Oid &o); +bool oid_equal(const ObjId *a, const ObjId *b); +bool oid_equal(const ObjId *a, const Oid &b); + +enum class Cipher { None, DES, DES3, IDEA, RC2, RC4, AES }; +enum class Pad { None, Std, Pkcs }; +enum class Hash { None, MD2, MD4, MD5, SHA0, SHA1, RIPEMD160, SHA256, SHA384, SHA512 }; + +struct AlgInfo { + const char *name; + Oid oid; + ParmType parm; + AlgType type; + AlgEnc enc; + Hash hash; + Cipher cipher; + AlgMode mode; + Pad pad; + AlgId *global; +}; + +const AlgInfo *alg_by_oid(const Oid &o); +const AlgInfo *alg_by_oid(const ObjId *o); +const AlgInfo *alg_by_name(const char *name); +std::string alg_display_name(const ObjId *o); +ObjId *pse_object_oid(const char *name); +const char *pse_object_name(const ObjId *oid); + +// Well-known OIDs (values are documented in README.md). +namespace oids { +extern const Oid rsa, rsaEncryption, dsa, id_dsa; +extern const Oid md2, md4, md5, sha, sha1, ripemd160, sha256, sha384, sha512; +extern const Oid data, encryptedData; +extern const Oid keyBag, pkcs8ShroudedKeyBag, certBag, x509Certificate; +extern const Oid friendlyName, localKeyId; +extern const Oid pbeSHA1RC4_128, pbeSHA1RC4_40, pbeSHA1DES3, pbeSHA1DES2, pbeSHA1RC2_128, pbeSHA1RC2_40; +extern const Oid pbes2, pbkdf2, hmacSHA1, hmacSHA256, hmacSHA384, hmacSHA512; +extern const Oid aes128CBC, aes192CBC, aes256CBC, desEDE3CBC; +extern const Oid subjectKeyIdentifier, keyUsage, basicConstraints, authorityKeyIdentifier; +extern const Oid cryptoolPseName; +} // namespace oids + +// ---- deep copy / free of SECUDE structures -------------------------------- +ObjId *copy_objid(const ObjId *o); +void free_objid_content(ObjId *o); +AlgId *copy_algid(const AlgId *a); +void free_algid_content(AlgId *a); +void free_algid(AlgId *a); +KeyBits *copy_keybits(const KeyBits *k); +void free_keybits_content(KeyBits *k); +KeyInfo *copy_keyinfo(const KeyInfo *k); +void free_keyinfo_content(KeyInfo *k); +void free_keyinfo(KeyInfo *k); +DName *copy_dname(const DName *d); +void free_dname(DName *d); +Validity *copy_validity(const Validity *v); +void free_validity(Validity *v); +Signature *copy_signature(const Signature *s); +void free_signature(Signature *s); +CertExtensions *copy_extensions(const CertExtensions *e); +void free_extensions(CertExtensions *e); +ToBeSigned *copy_tbs(const ToBeSigned *t); +void free_tbs(ToBeSigned *t); +Certificate *copy_certificate(const Certificate *c); +void free_certificate(Certificate *c); +FCPath *copy_fcpath(const FCPath *f); +void free_fcpath(FCPath *f); +PKRoot *copy_pkroot(const PKRoot *p); +void free_pkroot(PKRoot *p); +void free_serial(Serial *s); +void free_privatekeyinfo(PrivateKeyInfo *p); + +// ---- DER encoding of SECUDE structures (encode.cpp) ----------------------- +Bytes enc_algid(const AlgId *a); +AlgId *dec_algid(const Bytes &der); +Bytes enc_keybits(const KeyBits *k); +KeyBits *dec_keybits(const Bytes &der); +Bytes enc_keyinfo(const KeyInfo *k); +KeyInfo *dec_keyinfo(const Bytes &der); +Bytes enc_dname(const DName *d); +DName *dec_dname(const Bytes &der); +Bytes enc_validity(const Validity *v); +Bytes enc_extensions(const CertExtensions *e); +CertExtensions *dec_extensions(const Bytes &der); +Bytes enc_tbs(const ToBeSigned *t); +Bytes enc_signature_value(const Signature *s); +Bytes enc_certificate(const Certificate *c); +Certificate *dec_certificate(const Bytes &der); +Bytes enc_pkroot(const PKRoot *p); +PKRoot *dec_pkroot(const Bytes &der); +Bytes enc_fcpath(const FCPath *f); +FCPath *dec_fcpath(const Bytes &der); +Bytes enc_privatekeyinfo(const PrivateKeyInfo *p); +PrivateKeyInfo *dec_privatekeyinfo(const Bytes &der); +Bytes enc_unsigned_integer(const uint8_t *p, size_t n); +Bytes strip_leading_zeros(const uint8_t *p, size_t n); +std::string utc_now(long offset_seconds = 0); + +// ---- names ------------------------------------------------------------------ +std::string dname_to_string(const DName *d); +DName *string_to_dname(const char *s); +bool dname_equal(const DName *a, const DName *b); + +// ---- digests (hash.cpp) ---------------------------------------------------- +Bytes digest(Hash h, const uint8_t *p, size_t n); +size_t digest_size(Hash h); + +// ---- asymmetric (asym.cpp) ------------------------------------------------ +struct RsaPublic { Bytes n, e; }; +struct RsaPrivate { Bytes n, e, d, p, q; }; +struct DsaParams { Bytes p, q, g; }; + +bool keyinfo_is_rsa(const KeyInfo *k); +bool keyinfo_is_dsa(const KeyInfo *k); +RsaPublic rsa_public_from_keyinfo(const KeyInfo *k); +RsaPrivate rsa_private_from_keyinfo(const KeyInfo *k, const Bytes &e_hint); +int rsa_bits(const Bytes &n); + +void rsa_generate(int bits, KeyInfo **pub, KeyInfo **priv); +void dsa_generate(int bits, KeyInfo **pub, KeyInfo **priv); +Bytes rsa_encrypt_blocks(const RsaPublic &k, const uint8_t *p, size_t n); +Bytes rsa_decrypt_blocks(const RsaPrivate &k, const uint8_t *p, size_t n); +Bytes rsa_raw_private(const RsaPrivate &k, const Bytes &m); +Bytes rsa_private_key_der(const RsaPrivate &k); + +// Sign/verify the message (hashing included) with the signature algorithm sig_alg. +Bytes sign_message(const KeyInfo *priv, const Bytes &e_hint, const AlgId *sig_alg, const uint8_t *msg, size_t n); +bool verify_message(const KeyInfo *pub, const AlgId *sig_alg, const uint8_t *msg, size_t n, const Bytes &sig); + +// ---- symmetric (symcipher.cpp) ---------------------------------------------- +Bytes sym_encrypt(const AlgId *alg, const Bytes &key, size_t keybits, const uint8_t *in, size_t n); +Bytes sym_decrypt(const AlgId *alg, const Bytes &key, size_t keybits, const uint8_t *in, size_t n); + +// ---- providers (provider.cpp) --------------------------------------------- +OSSL_LIB_CTX *libctx(); +bool legacy_enabled(); +EVP_MD *fetch_md(const char *name); // nullptr if unavailable +EVP_CIPHER *fetch_cipher(const char *name); // nullptr if unavailable (or legacy disabled) + +// ---- PSE internals (pse.cpp) ---------------------------------------------- +struct PseStore; +struct PseObject { + std::string name; + Oid type; + std::string created, updated; + Bytes value; +}; + +std::shared_ptr store_of(PSE pse); +std::shared_ptr store_of(PSESel *sel); +const PseObject *store_find(const std::shared_ptr &s, const std::string &name); +void store_put(const std::shared_ptr &s, const std::string &name, const Oid &type, const Bytes &value); +bool store_onekeypaironly(const std::shared_ptr &s); +const char *pse_cadir(PSE pse); +std::string private_key_object(PSE pse, KeyType type); +std::string certificate_object(PSE pse, KeyType type); +KeyInfo *read_private_key(PSE pse, KeyType type); +Bytes rsa_exponent_hint(PSE pse, const KeyInfo *priv); +Certificate *make_ca_certificate(PSE pse, const KeyInfo *pub, const AlgId *sig_alg, const DName *subject, + long validity_seconds); + +// ---- print flags (data_flags.cpp) ----------------------------------------- +uint32_t print_cert_flags(); +uint32_t print_keyinfo_flags(); +void *print_cert_flag_address(); +void *print_keyinfo_flag_address(); + +// ---- misc ------------------------------------------------------------------- +Bytes random_bytes(size_t n); +Bytes read_file(const std::string &path, bool &exists); +void write_file_atomic(const std::string &path, const Bytes &data, int mode = 0600); +std::string hex(const uint8_t *p, size_t n, bool upper = true); +std::string hexdump(const uint8_t *p, size_t n, const std::string &indent, bool ascii); +std::string format_time(const char *t); +std::string str_printf(const char *fmt, ...); +char *append_string(char *string, const std::string &text); + +} // namespace compat diff --git a/port/secude/src/keystore.cpp b/port/secude/src/keystore.cpp new file mode 100644 index 000000000..7c42d2268 --- /dev/null +++ b/port/secude/src/keystore.cpp @@ -0,0 +1,266 @@ +// Creation of the CrypTool key store (CA PSE, CA database, sample keys). +#include +#include + +#include "bn.hpp" +#include "der.hpp" + +#include +#include + +namespace compat { + +namespace { + +constexpr long kDay = 24L * 3600; + +std::string join(const std::string &dir, const std::string &name) +{ + if (dir.empty() || dir.back() == '/') + return dir + name; + return dir + "/" + name; +} + +void make_dirs(const std::string &path) +{ + std::string cur; + for (size_t i = 0; i <= path.size(); ++i) { + if (i == path.size() || path[i] == '/') { + if (!cur.empty() && ::mkdir(cur.c_str(), 0755) != 0 && errno != EEXIST) + fail(EWRITEFILE, "cannot create directory " + cur + ": " + std::strerror(errno)); + } + if (i < path.size()) + cur += path[i]; + } +} + +bool exists(const std::string &path) +{ + struct stat st; + return ::stat(path.c_str(), &st) == 0; +} + +// Raises the most recent SECUDE error as exception (for chaining API calls). +[[noreturn]] void rethrow(const char *what) +{ + int code = th_last_error(); + std::string text = th_get_last_error_text(); + fail(code ? code : EINTERNAL, std::string(what) + ": " + text); +} + +struct PseGuard { + PSE pse; + explicit PseGuard(PSE p) : pse(p) {} + ~PseGuard() + { + if (pse) + af_close(pse); + } + PseGuard(const PseGuard &) = delete; + PseGuard &operator=(const PseGuard &) = delete; +}; + +void create_ca(const std::string &pse_path, const std::string &ca_dir, const char *pin, const char *name, int bits, + int days) +{ + make_dirs(ca_dir); + std::remove(join(ca_dir, "cadb.der").c_str()); + std::remove(pse_path.c_str()); + PseGuard ca(af_create(const_cast(pse_path.c_str()), const_cast(ca_dir.c_str()), + const_cast(pin), nullptr, TRUE)); + if (!ca.pse) + rethrow("af_create"); + Key key{}; + key.alg = &rsa_aid; + key.key_size = bits; + if (af_gen_key(ca.pse, &key, SIGNATURE, TRUE) != 0) + rethrow("af_gen_key"); + DName *dn = string_to_dname(name); + Certificate *cert = nullptr; + try { + cert = make_ca_certificate(ca.pse, key.key, &sha1WithRsaEncryption_aid, dn, long(days) * kDay); + } catch (...) { + free_dname(dn); + free_keyinfo(key.key); + throw; + } + free_dname(dn); + free_keyinfo(key.key); + PKRoot *root = aux_create_PKRoot(cert, nullptr); + Bytes one{1}; + OctetString serial{1, reinterpret_cast(one.data())}; + bool ok = root && af_pse_update(ca.pse, const_cast(Cert_name), cert, Cert_OID) == 0 && + af_pse_update_PKRoot(ca.pse, root) == 0 && af_pse_update_SerialNumber(ca.pse, &serial) == 0 && + af_cadb_add_Certificate(ca.pse, SIGNATURE, cert) == 0; + free_pkroot(root); + free_certificate(cert); + if (!ok) + rethrow("CA initialisation"); +} + +// Mirrors CDlgKeyAsymGeneration::CreateAsymKeys for an RSA key. +void create_rsa_user(const std::string &pse_dir, const std::string &ca_pse, const std::string &ca_dir, + const std::string &name, const std::string &first_name, int bits, const std::string &time, + const std::string &info, const char *pin) +{ + std::string key_id = "[" + name + "][" + first_name + "][RSA-" + std::to_string(bits) + "][" + time + "][" + info + "]"; + std::string path = join(pse_dir, key_id + ".pse"); + std::remove(path.c_str()); + PseGuard user(af_create(const_cast(path.c_str()), nullptr, const_cast(pin), nullptr, TRUE)); + if (!user.pse) + rethrow("af_create"); + Key key{}; + key.alg = &rsa_aid; + key.key_size = bits; + if (af_gen_key(user.pse, &key, SIGNATURE, TRUE) != 0) + rethrow("af_gen_key"); + std::string dn_text = "CN=" + first_name + " " + name + " [" + time + "], DC=cryptool, DC=org"; + DName *dn = string_to_dname(dn_text.c_str()); + char serial_text[] = "000001"; + OctetString proto_serial{6, serial_text}; + Certificate *proto = af_create_Certificate(user.pse, key.key, &sha1WithRSASignature_aid, const_cast(SKnew_name), + dn, &proto_serial, nullptr, nullptr, TRUE, nullptr); + free_dname(dn); + free_keyinfo(key.key); + if (!proto) + rethrow("af_create_Certificate"); + + OctetString *ext_value = e_PrintableString(const_cast(key_id.c_str())); + v3Extension ext{}; + ObjId ext_oid{}; + fill_objid(&ext_oid, oids::cryptoolPseName); + ext.extnId = &ext_oid; + ext.extnDERcode = ext_value; + SEQUENCE_OF_Extension list{&ext, nullptr}; + CertExtensions cert_ext{}; + cert_ext.nonSupported = &list; + CertExtensions *own_ext = proto->tbs->extensions; + proto->tbs->extensions = &cert_ext; + + PseGuard ca(af_open(const_cast(ca_pse.c_str()), const_cast(ca_dir.c_str()), + const_cast(SECUDE_COMPAT_CA_PIN), nullptr)); + Certificate *cert = nullptr; + if (ca.pse) { + OctetString *serial = sec_random_ostr(8, 1); + if (serial && af_pse_update_SerialNumber(ca.pse, serial) == 0) { + Validity v{}; + std::string from = utc_now(), to = utc_now(7305 * kDay); + v.notbefore = const_cast(from.c_str()); + v.notafter = const_cast(to.c_str()); + cert = af_certify(ca.pse, proto, &v, &sha1WithRSASignature_aid, nullptr); + } + aux_free_OctetString(&serial); + } + proto->tbs->extensions = own_ext; + free_certificate(proto); + aux_free_OctetString(&ext_value); + free_objid_content(&ext_oid); + if (!cert) + rethrow("af_certify"); + PKRoot *root = af_pse_get_PKRoot(ca.pse); + bool ok = root && af_cadb_add_Certificate(ca.pse, SIGNATURE, cert) == 0 && af_pse_update_PKRoot(user.pse, root) == 0 && + af_pse_update(user.pse, const_cast(Cert_name), cert, Cert_OID) == 0; + free_pkroot(root); + free_certificate(cert); + if (!ok) + rethrow("registering the certificate"); +} + +std::string hex_number(const BIGNUM *b) +{ + Bytes v = bn_bytes(b); + std::string h = hex(v.data(), v.size()); + size_t nz = h.find_first_not_of('0'); + return "0X" + (nz == std::string::npos ? std::string("0") : h.substr(nz)); +} + +// Mirrors CKeyFile::CreateEcKeyFiles: public data as text file, private key in a PSE. +void create_ec_user(const std::string &pse_dir, const std::string &key_id, const char *curve, int nid, const char *pin) +{ + EC_GROUP *group = EC_GROUP_new_by_curve_name_ex(libctx(), nullptr, nid); + if (!group) + fail(EINTERNAL, std::string("curve ") + curve + " not available"); + Bn p = bn_new(), a = bn_new(), b = bn_new(), gx = bn_new(), gy = bn_new(), order = bn_new(), cofactor = bn_new(); + Bn d = bn_new(), qx = bn_new(), qy = bn_new(); + EC_POINT *q = EC_POINT_new(group); + bool ok = q && EC_GROUP_get_curve(group, p.get(), a.get(), b.get(), bn_ctx()) && + EC_POINT_get_affine_coordinates(group, EC_GROUP_get0_generator(group), gx.get(), gy.get(), bn_ctx()) && + EC_GROUP_get_order(group, order.get(), bn_ctx()) && EC_GROUP_get_cofactor(group, cofactor.get(), bn_ctx()); + while (ok) { + ok = BN_priv_rand_range(d.get(), order.get()); + if (ok && !BN_is_zero(d.get())) + break; + } + ok = ok && EC_POINT_mul(group, q, d.get(), nullptr, nullptr, bn_ctx()) && + EC_POINT_get_affine_coordinates(group, q, qx.get(), qy.get(), bn_ctx()); + EC_POINT_free(q); + EC_GROUP_free(group); + if (!ok) + fail(EINTERNAL, "EC key generation failed"); + + std::string text; + for (const BIGNUM *v : {a.get(), b.get(), p.get(), gx.get(), gy.get(), cofactor.get(), order.get(), qx.get(), qy.get()}) + text += hex_number(v) + "\n"; + std::string pub_path = join(pse_dir, key_id); + write_file_atomic(pub_path, Bytes(text.begin(), text.end()), 0644); + + std::string pse_path = pub_path + ".pse"; + std::remove(pse_path.c_str()); + PseGuard pse(af_create(const_cast(pse_path.c_str()), nullptr, const_cast(pin), nullptr, FALSE)); + if (!pse.pse) + rethrow("af_create"); + Bytes priv = bn_bytes(d.get()); + OctetString raw{sec_uint4(priv.size()), reinterpret_cast(priv.data())}; + OctetString *encoded = e_OctetString(&raw); + int rc = encoded ? af_pse_update(pse.pse, const_cast("EcPrivKey"), encoded, Uid_OID) : -1; + aux_free_OctetString(&encoded); + if (rc != 0) + rethrow("af_pse_update"); +} + +} // namespace + +} // namespace compat + +using namespace compat; + +extern "C" { + +int secude_compat_create_ca(const char *ca_pse_path, const char *ca_dir, const char *pin, const char *ca_name, + int key_bits, int validity_days) +{ + return guarded("secude_compat_create_ca", -1, [&] { + if (!ca_pse_path || !ca_dir) + fail(EINVALID, "missing path"); + create_ca(ca_pse_path, ca_dir, pin ? pin : SECUDE_COMPAT_CA_PIN, ca_name ? ca_name : SECUDE_COMPAT_CA_NAME, + key_bits > 0 ? key_bits : 2048, validity_days > 0 ? validity_days : 7305); + return 0; + }); +} + +int secude_compat_create_sample_keystore(const char *key_store_root, int overwrite) +{ + return guarded("secude_compat_create_sample_keystore", -1, [&] { + if (!key_store_root) + fail(EINVALID, "missing key store directory"); + std::string pse_dir = join(key_store_root, "PSE"); + std::string ca_dir = join(pse_dir, "PSECA"); + std::string ca_pse = join(ca_dir, "capse.cse"); + make_dirs(ca_dir); + + bool new_ca = overwrite || !exists(ca_pse); + if (new_ca) + create_ca(ca_pse, ca_dir, SECUDE_COMPAT_CA_PIN, SECUDE_COMPAT_CA_NAME, 2048, 7305); + + const char *rsa_id = "[SideChannelAttack][Bob][RSA-512][1152179494][PIN=1234]"; + if (new_ca || !exists(join(pse_dir, std::string(rsa_id) + ".pse"))) + create_rsa_user(pse_dir, ca_pse, ca_dir, "SideChannelAttack", "Bob", 512, "1152179494", "PIN=1234", "1234"); + + const char *ec_id = "[HybridEncryption][Bob][EC-prime239v1][1178702474][PIN=1234]"; + if (overwrite || !exists(join(pse_dir, std::string(ec_id) + ".pse")) || !exists(join(pse_dir, ec_id))) + create_ec_user(pse_dir, ec_id, "prime239v1", NID_X9_62_prime239v1, "1234"); + return 0; + }); +} + +} // extern "C" diff --git a/port/secude/src/legacy.hpp b/port/secude/src/legacy.hpp new file mode 100644 index 000000000..db47c0b8e --- /dev/null +++ b/port/secude/src/legacy.hpp @@ -0,0 +1,36 @@ +#pragma once + +#include "internal.hpp" + +// Built-in implementations of algorithms that OpenSSL 3 lacks (MD2, SHA-0) or +// only offers through the optional legacy provider. +namespace compat { +namespace legacy { + +class Digest { +public: + virtual ~Digest() = default; + virtual void update(const uint8_t *p, size_t n) = 0; + virtual Bytes final() = 0; +}; + +// MD2, MD4, MD5, SHA0, SHA1, RIPEMD160; nullptr for other algorithms. +std::unique_ptr make_digest(Hash h); + +class BlockCipher { +public: + virtual ~BlockCipher() = default; + virtual size_t block_size() const = 0; + virtual void encrypt(const uint8_t *in, uint8_t *out) const = 0; + virtual void decrypt(const uint8_t *in, uint8_t *out) const = 0; +}; + +std::unique_ptr make_des(const uint8_t key[8]); +std::unique_ptr make_des3(const uint8_t *key, size_t keylen); // 16 or 24 bytes +std::unique_ptr make_idea(const uint8_t key[16]); +std::unique_ptr make_rc2(const uint8_t *key, size_t keylen, unsigned effective_bits); + +void rc4(const uint8_t *key, size_t keylen, const uint8_t *in, uint8_t *out, size_t n); + +} // namespace legacy +} // namespace compat diff --git a/port/secude/src/legacy_cipher.cpp b/port/secude/src/legacy_cipher.cpp new file mode 100644 index 000000000..19c933f5d --- /dev/null +++ b/port/secude/src/legacy_cipher.cpp @@ -0,0 +1,395 @@ +// DES/3DES (FIPS 46-3), IDEA, RC2 (RFC 2268) and RC4. +#include "legacy.hpp" + +#include + +namespace compat { +namespace legacy { + +namespace { + +// ---- DES ---------------------------------------------------------------------------- + +const uint8_t kIP[64] = {58, 50, 42, 34, 26, 18, 10, 2, 60, 52, 44, 36, 28, 20, 12, 4, + 62, 54, 46, 38, 30, 22, 14, 6, 64, 56, 48, 40, 32, 24, 16, 8, + 57, 49, 41, 33, 25, 17, 9, 1, 59, 51, 43, 35, 27, 19, 11, 3, + 61, 53, 45, 37, 29, 21, 13, 5, 63, 55, 47, 39, 31, 23, 15, 7}; +const uint8_t kFP[64] = {40, 8, 48, 16, 56, 24, 64, 32, 39, 7, 47, 15, 55, 23, 63, 31, + 38, 6, 46, 14, 54, 22, 62, 30, 37, 5, 45, 13, 53, 21, 61, 29, + 36, 4, 44, 12, 52, 20, 60, 28, 35, 3, 43, 11, 51, 19, 59, 27, + 34, 2, 42, 10, 50, 18, 58, 26, 33, 1, 41, 9, 49, 17, 57, 25}; +const uint8_t kE[48] = {32, 1, 2, 3, 4, 5, 4, 5, 6, 7, 8, 9, 8, 9, 10, 11, + 12, 13, 12, 13, 14, 15, 16, 17, 16, 17, 18, 19, 20, 21, 20, 21, + 22, 23, 24, 25, 24, 25, 26, 27, 28, 29, 28, 29, 30, 31, 32, 1}; +const uint8_t kP[32] = {16, 7, 20, 21, 29, 12, 28, 17, 1, 15, 23, 26, 5, 18, 31, 10, + 2, 8, 24, 14, 32, 27, 3, 9, 19, 13, 30, 6, 22, 11, 4, 25}; +const uint8_t kPC1[56] = {57, 49, 41, 33, 25, 17, 9, 1, 58, 50, 42, 34, 26, 18, 10, 2, 59, 51, 43, + 35, 27, 19, 11, 3, 60, 52, 44, 36, 63, 55, 47, 39, 31, 23, 15, 7, 62, 54, + 46, 38, 30, 22, 14, 6, 61, 53, 45, 37, 29, 21, 13, 5, 28, 20, 12, 4}; +const uint8_t kPC2[48] = {14, 17, 11, 24, 1, 5, 3, 28, 15, 6, 21, 10, 23, 19, 12, 4, + 26, 8, 16, 7, 27, 20, 13, 2, 41, 52, 31, 37, 47, 55, 30, 40, + 51, 45, 33, 48, 44, 49, 39, 56, 34, 53, 46, 42, 50, 36, 29, 32}; +const uint8_t kShifts[16] = {1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1}; +const uint8_t kS[8][64] = { + {14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7, 0, 15, 7, 4, 14, 2, + 13, 1, 10, 6, 12, 11, 9, 5, 3, 8, 4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, + 3, 10, 5, 0, 15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13}, + {15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10, 3, 13, 4, 7, 15, 2, + 8, 14, 12, 0, 1, 10, 6, 9, 11, 5, 0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, + 9, 3, 2, 15, 13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9}, + {10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8, 13, 7, 0, 9, 3, 4, + 6, 10, 2, 8, 5, 14, 12, 11, 15, 1, 13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, + 5, 10, 14, 7, 1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12}, + {7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15, 13, 8, 11, 5, 6, 15, + 0, 3, 4, 7, 2, 12, 1, 10, 14, 9, 10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, + 5, 2, 8, 4, 3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14}, + {2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9, 14, 11, 2, 12, 4, 7, + 13, 1, 5, 0, 15, 10, 3, 9, 8, 6, 4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, + 6, 3, 0, 14, 11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3}, + {12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11, 10, 15, 4, 2, 7, 12, + 9, 5, 6, 1, 13, 14, 0, 11, 3, 8, 9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, + 1, 13, 11, 6, 4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13}, + {4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1, 13, 0, 11, 7, 4, 9, + 1, 10, 14, 3, 5, 12, 2, 15, 8, 6, 1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, + 0, 5, 9, 2, 6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12}, + {13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7, 1, 15, 13, 8, 10, 3, + 7, 4, 12, 5, 6, 11, 0, 14, 9, 2, 7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, + 15, 3, 5, 8, 2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11}, +}; + +uint64_t permute(uint64_t in, const uint8_t *table, int n, int inbits) +{ + uint64_t out = 0; + for (int i = 0; i < n; ++i) + out = (out << 1) | ((in >> (inbits - table[i])) & 1); + return out; +} + +struct SpTables { + uint32_t sp[8][64]; + SpTables() + { + for (int box = 0; box < 8; ++box) + for (int six = 0; six < 64; ++six) { + int row = ((six >> 4) & 2) | (six & 1); + int col = (six >> 1) & 0xf; + uint64_t s = uint64_t(kS[box][row * 16 + col]) << (28 - 4 * box); + sp[box][six] = uint32_t(permute(s, kP, 32, 32)); + } + } +}; + +const SpTables &sp_tables() +{ + static const SpTables t; + return t; +} + +uint64_t load64(const uint8_t *p) +{ + uint64_t v = 0; + for (int i = 0; i < 8; ++i) + v = (v << 8) | p[i]; + return v; +} + +void store64(uint8_t *p, uint64_t v) +{ + for (int i = 7; i >= 0; --i) { + p[i] = uint8_t(v); + v >>= 8; + } +} + +class DesKey { +public: + explicit DesKey(const uint8_t key[8]) + { + uint64_t cd = permute(load64(key), kPC1, 56, 64); + uint32_t c = uint32_t(cd >> 28) & 0x0fffffff, d = uint32_t(cd) & 0x0fffffff; + for (int i = 0; i < 16; ++i) { + for (int s = 0; s < kShifts[i]; ++s) { + c = ((c << 1) | (c >> 27)) & 0x0fffffff; + d = ((d << 1) | (d >> 27)) & 0x0fffffff; + } + sub_[i] = permute((uint64_t(c) << 28) | d, kPC2, 48, 56); + } + } + + uint64_t crypt(uint64_t block, bool decrypt) const + { + const SpTables &t = sp_tables(); + uint64_t ip = permute(block, kIP, 64, 64); + uint32_t l = uint32_t(ip >> 32), r = uint32_t(ip); + for (int i = 0; i < 16; ++i) { + uint64_t e = permute(r, kE, 48, 32) ^ sub_[decrypt ? 15 - i : i]; + uint32_t f = 0; + for (int box = 0; box < 8; ++box) + f |= t.sp[box][(e >> (42 - 6 * box)) & 0x3f]; + uint32_t nl = r; + r = l ^ f; + l = nl; + } + return permute((uint64_t(r) << 32) | l, kFP, 64, 64); + } + +private: + uint64_t sub_[16]; +}; + +class Des : public BlockCipher { +public: + explicit Des(const uint8_t key[8]) : k_(key) {} + size_t block_size() const override { return 8; } + void encrypt(const uint8_t *in, uint8_t *out) const override { store64(out, k_.crypt(load64(in), false)); } + void decrypt(const uint8_t *in, uint8_t *out) const override { store64(out, k_.crypt(load64(in), true)); } + +private: + DesKey k_; +}; + +class Des3 : public BlockCipher { +public: + Des3(const uint8_t *key, size_t keylen) : k1_(key), k2_(key + 8), k3_(keylen >= 24 ? key + 16 : key) {} + size_t block_size() const override { return 8; } + void encrypt(const uint8_t *in, uint8_t *out) const override + { + store64(out, k3_.crypt(k2_.crypt(k1_.crypt(load64(in), false), true), false)); + } + void decrypt(const uint8_t *in, uint8_t *out) const override + { + store64(out, k1_.crypt(k2_.crypt(k3_.crypt(load64(in), true), false), true)); + } + +private: + DesKey k1_, k2_, k3_; +}; + +// ---- IDEA --------------------------------------------------------------------------- + +uint16_t idea_mul(uint32_t a, uint32_t b) +{ + if (!a) + a = 0x10000; + if (!b) + b = 0x10000; + uint32_t p = uint32_t((uint64_t(a) * b) % 0x10001); + return uint16_t(p & 0xffff); +} + +uint16_t idea_inv(uint16_t x) +{ + if (x <= 1) + return x; + // Fermat: x^(p-2) mod p for the prime p = 65537 + uint64_t result = 1, base = x, e = 0x10001 - 2; + while (e) { + if (e & 1) + result = (result * base) % 0x10001; + base = (base * base) % 0x10001; + e >>= 1; + } + return uint16_t(result); +} + +class Idea : public BlockCipher { +public: + explicit Idea(const uint8_t key[16]) + { + uint16_t k[8]; + for (int i = 0; i < 8; ++i) + k[i] = uint16_t((key[2 * i] << 8) | key[2 * i + 1]); + for (int i = 0; i < 52; ++i) { + if (i && i % 8 == 0) { + uint16_t r[8]; + for (int j = 0; j < 8; ++j) + r[j] = uint16_t((k[(j + 1) % 8] << 9) | (k[(j + 2) % 8] >> 7)); + std::memcpy(k, r, sizeof k); + } + ek_[i] = k[i % 8]; + } + dk_[0] = idea_inv(ek_[48]); + dk_[1] = uint16_t(-ek_[49]); + dk_[2] = uint16_t(-ek_[50]); + dk_[3] = idea_inv(ek_[51]); + for (int r = 1; r < 8; ++r) { + int e = 48 - 6 * r; + dk_[6 * r - 2] = ek_[e + 4]; + dk_[6 * r - 1] = ek_[e + 5]; + dk_[6 * r] = idea_inv(ek_[e]); + dk_[6 * r + 1] = uint16_t(-ek_[e + 2]); + dk_[6 * r + 2] = uint16_t(-ek_[e + 1]); + dk_[6 * r + 3] = idea_inv(ek_[e + 3]); + } + dk_[46] = ek_[4]; + dk_[47] = ek_[5]; + dk_[48] = idea_inv(ek_[0]); + dk_[49] = uint16_t(-ek_[1]); + dk_[50] = uint16_t(-ek_[2]); + dk_[51] = idea_inv(ek_[3]); + } + size_t block_size() const override { return 8; } + void encrypt(const uint8_t *in, uint8_t *out) const override { crypt(ek_, in, out); } + void decrypt(const uint8_t *in, uint8_t *out) const override { crypt(dk_, in, out); } + +private: + static void crypt(const uint16_t *k, const uint8_t *in, uint8_t *out) + { + uint16_t x1 = uint16_t((in[0] << 8) | in[1]), x2 = uint16_t((in[2] << 8) | in[3]); + uint16_t x3 = uint16_t((in[4] << 8) | in[5]), x4 = uint16_t((in[6] << 8) | in[7]); + for (int r = 0; r < 8; ++r, k += 6) { + x1 = idea_mul(x1, k[0]); + x2 = uint16_t(x2 + k[1]); + x3 = uint16_t(x3 + k[2]); + x4 = idea_mul(x4, k[3]); + uint16_t t0 = idea_mul(uint16_t(x1 ^ x3), k[4]); + uint16_t t1 = idea_mul(uint16_t(t0 + (x2 ^ x4)), k[5]); + t0 = uint16_t(t0 + t1); + x1 ^= t1; + x4 ^= t0; + uint16_t t = uint16_t(x2 ^ t0); + x2 = uint16_t(x3 ^ t1); + x3 = t; + } + uint16_t y1 = idea_mul(x1, k[0]), y2 = uint16_t(x3 + k[1]), y3 = uint16_t(x2 + k[2]), y4 = idea_mul(x4, k[3]); + const uint16_t y[4] = {y1, y2, y3, y4}; + for (int i = 0; i < 4; ++i) { + out[2 * i] = uint8_t(y[i] >> 8); + out[2 * i + 1] = uint8_t(y[i]); + } + } + + uint16_t ek_[52]; + uint16_t dk_[52]; +}; + +// ---- RC2 ---------------------------------------------------------------------------- + +const uint8_t kPiTable[256] = { + 0xd9, 0x78, 0xf9, 0xc4, 0x19, 0xdd, 0xb5, 0xed, 0x28, 0xe9, 0xfd, 0x79, 0x4a, 0xa0, 0xd8, 0x9d, 0xc6, 0x7e, 0x37, + 0x83, 0x2b, 0x76, 0x53, 0x8e, 0x62, 0x4c, 0x64, 0x88, 0x44, 0x8b, 0xfb, 0xa2, 0x17, 0x9a, 0x59, 0xf5, 0x87, 0xb3, + 0x4f, 0x13, 0x61, 0x45, 0x6d, 0x8d, 0x09, 0x81, 0x7d, 0x32, 0xbd, 0x8f, 0x40, 0xeb, 0x86, 0xb7, 0x7b, 0x0b, 0xf0, + 0x95, 0x21, 0x22, 0x5c, 0x6b, 0x4e, 0x82, 0x54, 0xd6, 0x65, 0x93, 0xce, 0x60, 0xb2, 0x1c, 0x73, 0x56, 0xc0, 0x14, + 0xa7, 0x8c, 0xf1, 0xdc, 0x12, 0x75, 0xca, 0x1f, 0x3b, 0xbe, 0xe4, 0xd1, 0x42, 0x3d, 0xd4, 0x30, 0xa3, 0x3c, 0xb6, + 0x26, 0x6f, 0xbf, 0x0e, 0xda, 0x46, 0x69, 0x07, 0x57, 0x27, 0xf2, 0x1d, 0x9b, 0xbc, 0x94, 0x43, 0x03, 0xf8, 0x11, + 0xc7, 0xf6, 0x90, 0xef, 0x3e, 0xe7, 0x06, 0xc3, 0xd5, 0x2f, 0xc8, 0x66, 0x1e, 0xd7, 0x08, 0xe8, 0xea, 0xde, 0x80, + 0x52, 0xee, 0xf7, 0x84, 0xaa, 0x72, 0xac, 0x35, 0x4d, 0x6a, 0x2a, 0x96, 0x1a, 0xd2, 0x71, 0x5a, 0x15, 0x49, 0x74, + 0x4b, 0x9f, 0xd0, 0x5e, 0x04, 0x18, 0xa4, 0xec, 0xc2, 0xe0, 0x41, 0x6e, 0x0f, 0x51, 0xcb, 0xcc, 0x24, 0x91, 0xaf, + 0x50, 0xa1, 0xf4, 0x70, 0x39, 0x99, 0x7c, 0x3a, 0x85, 0x23, 0xb8, 0xb4, 0x7a, 0xfc, 0x02, 0x36, 0x5b, 0x25, 0x55, + 0x97, 0x31, 0x2d, 0x5d, 0xfa, 0x98, 0xe3, 0x8a, 0x92, 0xae, 0x05, 0xdf, 0x29, 0x10, 0x67, 0x6c, 0xba, 0xc9, 0xd3, + 0x00, 0xe6, 0xcf, 0xe1, 0x9e, 0xa8, 0x2c, 0x63, 0x16, 0x01, 0x3f, 0x58, 0xe2, 0x89, 0xa9, 0x0d, 0x38, 0x34, 0x1b, + 0xab, 0x33, 0xff, 0xb0, 0xbb, 0x48, 0x0c, 0x5f, 0xb9, 0xb1, 0xcd, 0x2e, 0xc5, 0xf3, 0xdb, 0x47, 0xe5, 0xa5, 0x9c, + 0x77, 0x0a, 0xa6, 0x20, 0x68, 0xfe, 0x7f, 0xc1, 0xad, +}; + +class Rc2 : public BlockCipher { +public: + Rc2(const uint8_t *key, size_t keylen, unsigned bits) + { + uint8_t l[128] = {0}; + size_t t = std::min(keylen, 128); + std::memcpy(l, key, t); + for (size_t i = t; i < 128; ++i) + l[i] = kPiTable[uint8_t(l[i - 1] + l[i - t])]; + if (bits == 0 || bits > 1024) + bits = 1024; + size_t t8 = (bits + 7) / 8; + uint8_t tm = uint8_t(0xff >> (8 * t8 - bits)); + l[128 - t8] = kPiTable[l[128 - t8] & tm]; + for (size_t i = 128 - t8; i-- > 0;) + l[i] = kPiTable[l[i + 1] ^ l[i + t8]]; + for (int i = 0; i < 64; ++i) + k_[i] = uint16_t(l[2 * i] | (l[2 * i + 1] << 8)); + } + size_t block_size() const override { return 8; } + + void encrypt(const uint8_t *in, uint8_t *out) const override + { + uint16_t r[4]; + load(in, r); + int j = 0; + for (int round = 0; round < 16; ++round) { + for (int i = 0; i < 4; ++i) { + r[i] = uint16_t(r[i] + k_[j++] + (r[(i + 3) % 4] & r[(i + 2) % 4]) + (~r[(i + 3) % 4] & r[(i + 1) % 4])); + r[i] = rol16(r[i], kRot[i]); + } + if (round == 4 || round == 10) + for (int i = 0; i < 4; ++i) + r[i] = uint16_t(r[i] + k_[r[(i + 3) % 4] & 63]); + } + store(out, r); + } + + void decrypt(const uint8_t *in, uint8_t *out) const override + { + uint16_t r[4]; + load(in, r); + int j = 63; + for (int round = 15; round >= 0; --round) { + for (int i = 3; i >= 0; --i) { + r[i] = ror16(r[i], kRot[i]); + r[i] = uint16_t(r[i] - k_[j--] - (r[(i + 3) % 4] & r[(i + 2) % 4]) - (~r[(i + 3) % 4] & r[(i + 1) % 4])); + } + if (round == 5 || round == 11) + for (int i = 3; i >= 0; --i) + r[i] = uint16_t(r[i] - k_[r[(i + 3) % 4] & 63]); + } + store(out, r); + } + +private: + static constexpr int kRot[4] = {1, 2, 3, 5}; + static uint16_t rol16(uint16_t x, int n) { return uint16_t((x << n) | (x >> (16 - n))); } + static uint16_t ror16(uint16_t x, int n) { return uint16_t((x >> n) | (x << (16 - n))); } + static void load(const uint8_t *in, uint16_t r[4]) + { + for (int i = 0; i < 4; ++i) + r[i] = uint16_t(in[2 * i] | (in[2 * i + 1] << 8)); + } + static void store(uint8_t *out, const uint16_t r[4]) + { + for (int i = 0; i < 4; ++i) { + out[2 * i] = uint8_t(r[i]); + out[2 * i + 1] = uint8_t(r[i] >> 8); + } + } + uint16_t k_[64]; +}; + +} // namespace + +std::unique_ptr make_des(const uint8_t key[8]) { return std::make_unique(key); } + +std::unique_ptr make_des3(const uint8_t *key, size_t keylen) +{ + return std::make_unique(key, keylen); +} + +std::unique_ptr make_idea(const uint8_t key[16]) { return std::make_unique(key); } + +std::unique_ptr make_rc2(const uint8_t *key, size_t keylen, unsigned effective_bits) +{ + return std::make_unique(key, keylen, effective_bits); +} + +void rc4(const uint8_t *key, size_t keylen, const uint8_t *in, uint8_t *out, size_t n) +{ + uint8_t s[256]; + for (int i = 0; i < 256; ++i) + s[i] = uint8_t(i); + uint8_t j = 0; + for (int i = 0; i < 256; ++i) { + j = uint8_t(j + s[i] + key[size_t(i) % keylen]); + std::swap(s[i], s[j]); + } + uint8_t x = 0, y = 0; + for (size_t k = 0; k < n; ++k) { + x = uint8_t(x + 1); + y = uint8_t(y + s[x]); + std::swap(s[x], s[y]); + out[k] = in[k] ^ s[uint8_t(s[x] + s[y])]; + } +} + +} // namespace legacy +} // namespace compat diff --git a/port/secude/src/legacy_hash.cpp b/port/secude/src/legacy_hash.cpp new file mode 100644 index 000000000..0dbc62ba0 --- /dev/null +++ b/port/secude/src/legacy_hash.cpp @@ -0,0 +1,536 @@ +// MD2 (RFC 1319), MD4 (RFC 1320), MD5 (RFC 1321), SHA-0/SHA-1 (FIPS 180/180-1) +// and RIPEMD-160, operating on the SECUDE context structures. +#include "legacy.hpp" + +#include + +namespace compat { +namespace legacy { + +namespace { + +inline uint32_t rol(uint32_t x, int n) { return (x << n) | (x >> (32 - n)); } + +inline uint32_t load_le(const uint8_t *p) +{ + return uint32_t(p[0]) | (uint32_t(p[1]) << 8) | (uint32_t(p[2]) << 16) | (uint32_t(p[3]) << 24); +} + +inline uint32_t load_be(const uint8_t *p) +{ + return (uint32_t(p[0]) << 24) | (uint32_t(p[1]) << 16) | (uint32_t(p[2]) << 8) | uint32_t(p[3]); +} + +inline void store_le(uint8_t *p, uint32_t v) +{ + for (int i = 0; i < 4; ++i) + p[i] = uint8_t(v >> (8 * i)); +} + +inline void store_be(uint8_t *p, uint32_t v) +{ + for (int i = 0; i < 4; ++i) + p[i] = uint8_t(v >> (24 - 8 * i)); +} + +// ---- MD2 ---------------------------------------------------------------------- + +const uint8_t kPiSubst[256] = { + 41, 46, 67, 201, 162, 216, 124, 1, 61, 54, 84, 161, 236, 240, 6, 19, 98, 167, 5, 243, 192, 199, + 115, 140, 152, 147, 43, 217, 188, 76, 130, 202, 30, 155, 87, 60, 253, 212, 224, 22, 103, 66, 111, 24, + 138, 23, 229, 18, 190, 78, 196, 214, 218, 158, 222, 73, 160, 251, 245, 142, 187, 47, 238, 122, 169, 104, + 121, 145, 21, 178, 7, 63, 148, 194, 16, 137, 11, 34, 95, 33, 128, 127, 93, 154, 90, 144, 50, 39, + 53, 62, 204, 231, 191, 247, 151, 3, 255, 25, 48, 179, 72, 165, 181, 209, 215, 94, 146, 42, 172, 86, + 170, 198, 79, 184, 56, 210, 150, 164, 125, 182, 118, 252, 107, 226, 156, 116, 4, 241, 69, 157, 112, 89, + 100, 113, 135, 32, 134, 91, 207, 101, 230, 45, 168, 2, 27, 96, 37, 173, 174, 176, 185, 246, 28, 70, + 97, 105, 52, 64, 126, 15, 85, 71, 163, 35, 221, 81, 175, 58, 195, 92, 249, 206, 186, 197, 234, 38, + 44, 83, 13, 110, 133, 40, 132, 9, 211, 223, 205, 244, 65, 129, 77, 82, 106, 220, 55, 200, 108, 193, + 171, 250, 36, 225, 123, 8, 12, 189, 177, 74, 120, 136, 149, 139, 227, 99, 232, 109, 233, 203, 213, 254, + 59, 0, 29, 57, 242, 239, 183, 14, 102, 88, 208, 228, 166, 119, 114, 248, 235, 117, 75, 10, 49, 68, + 80, 180, 143, 237, 31, 26, 219, 153, 141, 51, 159, 17, 131, 20, +}; + +void md2_transform(SEC_MD2_CTX *c, const uint8_t *block) +{ + uint8_t x[48]; + for (int i = 0; i < 16; ++i) { + x[i] = c->state[i]; + x[i + 16] = block[i]; + x[i + 32] = uint8_t(c->state[i] ^ block[i]); + } + unsigned t = 0; + for (unsigned round = 0; round < 18; ++round) { + for (int k = 0; k < 48; ++k) + t = x[k] ^= kPiSubst[t]; + t = (t + round) & 0xff; + } + for (int i = 0; i < 16; ++i) + c->state[i] = x[i]; + t = c->checksum[15]; + for (int i = 0; i < 16; ++i) + t = c->checksum[i] ^= kPiSubst[block[i] ^ t]; +} + +// ---- MD4 / MD5 ------------------------------------------------------------------ + +#define MD_F(x, y, z) (((x) & (y)) | (~(x) & (z))) +#define MD4_G(x, y, z) (((x) & (y)) | ((x) & (z)) | ((y) & (z))) +#define MD5_G(x, y, z) (((x) & (z)) | ((y) & ~(z))) +#define MD_H(x, y, z) ((x) ^ (y) ^ (z)) +#define MD5_I(x, y, z) ((y) ^ ((x) | ~(z))) + +void md4_transform(uint32_t state[4], const uint8_t *block) +{ + uint32_t x[16]; + for (int i = 0; i < 16; ++i) + x[i] = load_le(block + 4 * i); + uint32_t a = state[0], b = state[1], c = state[2], d = state[3]; + static const int r1[4] = {3, 7, 11, 19}, r2[4] = {3, 5, 9, 13}, r3[4] = {3, 9, 11, 15}; + for (int i = 0; i < 16; ++i) { + uint32_t t = rol(a + MD_F(b, c, d) + x[i], r1[i % 4]); + a = d; d = c; c = b; b = t; + } + static const int o2[16] = {0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15}; + for (int i = 0; i < 16; ++i) { + uint32_t t = rol(a + MD4_G(b, c, d) + x[o2[i]] + 0x5a827999u, r2[i % 4]); + a = d; d = c; c = b; b = t; + } + static const int o3[16] = {0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15}; + for (int i = 0; i < 16; ++i) { + uint32_t t = rol(a + MD_H(b, c, d) + x[o3[i]] + 0x6ed9eba1u, r3[i % 4]); + a = d; d = c; c = b; b = t; + } + state[0] += a; state[1] += b; state[2] += c; state[3] += d; +} + +void md5_transform(uint32_t state[4], const uint8_t *block) +{ + static const uint32_t K[64] = { + 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, + 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, + 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8, + 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, + 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, + 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, + 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, + 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391, + }; + static const int S[64] = {7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, + 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, + 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, + 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21}; + uint32_t x[16]; + for (int i = 0; i < 16; ++i) + x[i] = load_le(block + 4 * i); + uint32_t a = state[0], b = state[1], c = state[2], d = state[3]; + for (int i = 0; i < 64; ++i) { + uint32_t f; + int g; + if (i < 16) { + f = MD_F(b, c, d); + g = i; + } else if (i < 32) { + f = MD5_G(b, c, d); + g = (5 * i + 1) % 16; + } else if (i < 48) { + f = MD_H(b, c, d); + g = (3 * i + 5) % 16; + } else { + f = MD5_I(b, c, d); + g = (7 * i) % 16; + } + uint32_t t = d; + d = c; + c = b; + b = b + rol(a + f + K[i] + x[g], S[i]); + a = t; + } + state[0] += a; state[1] += b; state[2] += c; state[3] += d; +} + +// Shared Merkle-Damgard bookkeeping of the RFC 1320/1321 contexts. +template +void md_update(Ctx *c, const uint8_t *p, size_t n) +{ + size_t index = (c->count[0] >> 3) & 0x3f; + uint64_t bits = (uint64_t(c->count[1]) << 32 | c->count[0]) + (uint64_t(n) << 3); + c->count[0] = uint32_t(bits); + c->count[1] = uint32_t(bits >> 32); + size_t part = 64 - index; + size_t i = 0; + if (n >= part) { + std::memcpy(c->buffer + index, p, part); + Transform(c->state, c->buffer); + for (i = part; i + 63 < n; i += 64) + Transform(c->state, p + i); + index = 0; + } + std::memcpy(c->buffer + index, p + i, n - i); +} + +template +void md_final(uint8_t digest[16], Ctx *c) +{ + uint8_t bits[8]; + store_le(bits, c->count[0]); + store_le(bits + 4, c->count[1]); + size_t index = (c->count[0] >> 3) & 0x3f; + size_t padlen = index < 56 ? 56 - index : 120 - index; + static const uint8_t pad[64] = {0x80}; + md_update(c, pad, padlen); + md_update(c, bits, 8); + for (int i = 0; i < 4; ++i) + store_le(digest + 4 * i, c->state[i]); + std::memset(c, 0, sizeof *c); +} + +template void md_init(Ctx *c) +{ + std::memset(c, 0, sizeof *c); + c->state[0] = 0x67452301; + c->state[1] = 0xefcdab89; + c->state[2] = 0x98badcfe; + c->state[3] = 0x10325476; +} + +// ---- SHA-0 / SHA-1 on SHS_INFO (countLo/countHi count bits) --------------------- + +void shs_transform(SHS_INFO *s, const uint8_t *block, bool sha1) +{ + uint32_t w[80]; + for (int i = 0; i < 16; ++i) + w[i] = load_be(block + 4 * i); + for (int i = 16; i < 80; ++i) { + uint32_t t = w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16]; + w[i] = sha1 ? rol(t, 1) : t; + } + uint32_t a = s->digest[0], b = s->digest[1], c = s->digest[2], d = s->digest[3], e = s->digest[4]; + for (int i = 0; i < 80; ++i) { + uint32_t f, k; + if (i < 20) { + f = (b & c) | (~b & d); + k = 0x5a827999; + } else if (i < 40) { + f = b ^ c ^ d; + k = 0x6ed9eba1; + } else if (i < 60) { + f = (b & c) | (b & d) | (c & d); + k = 0x8f1bbcdc; + } else { + f = b ^ c ^ d; + k = 0xca62c1d6; + } + uint32_t t = rol(a, 5) + f + e + k + w[i]; + e = d; + d = c; + c = rol(b, 30); + b = a; + a = t; + } + s->digest[0] += a; s->digest[1] += b; s->digest[2] += c; s->digest[3] += d; s->digest[4] += e; +} + +void shs_init(SHS_INFO *s) +{ + std::memset(s, 0, sizeof *s); + s->digest[0] = 0x67452301; + s->digest[1] = 0xefcdab89; + s->digest[2] = 0x98badcfe; + s->digest[3] = 0x10325476; + s->digest[4] = 0xc3d2e1f0; +} + +void shs_update(SHS_INFO *s, const uint8_t *p, size_t n, bool sha1) +{ + uint8_t *buf = reinterpret_cast(s->data); + size_t index = (s->countLo >> 3) & 0x3f; + uint64_t bits = (uint64_t(s->countHi) << 32 | s->countLo) + (uint64_t(n) << 3); + s->countLo = uint32_t(bits); + s->countHi = uint32_t(bits >> 32); + while (n) { + size_t take = std::min(n, size_t(64) - index); + std::memcpy(buf + index, p, take); + index += take; + p += take; + n -= take; + if (index == 64) { + uint8_t block[64]; + std::memcpy(block, buf, 64); + shs_transform(s, block, sha1); + index = 0; + } + } +} + +void shs_final(SHS_INFO *s, bool sha1) +{ + uint8_t len[8]; + store_be(len, s->countHi); + store_be(len + 4, s->countLo); + size_t index = (s->countLo >> 3) & 0x3f; + size_t padlen = index < 56 ? 56 - index : 120 - index; + static const uint8_t pad[64] = {0x80}; + shs_update(s, pad, padlen, sha1); + shs_update(s, len, 8, sha1); +} + +// ---- RIPEMD-160 ------------------------------------------------------------------- + +struct Rmd160 { + uint32_t h[5] = {0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0}; + uint8_t buf[64]; + size_t used = 0; + uint64_t total = 0; + + static uint32_t f(int j, uint32_t x, uint32_t y, uint32_t z) + { + switch (j / 16) { + case 0: return x ^ y ^ z; + case 1: return (x & y) | (~x & z); + case 2: return (x | ~y) ^ z; + case 3: return (x & z) | (y & ~z); + default: return x ^ (y | ~z); + } + } + + void transform(const uint8_t *block) + { + static const int r[80] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 7, 4, 13, 1, + 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8, 3, 10, 14, 4, 9, 15, 8, 1, + 2, 7, 0, 6, 13, 11, 5, 12, 1, 9, 11, 10, 0, 8, 12, 4, 13, 3, 7, 15, + 14, 5, 6, 2, 4, 0, 5, 9, 7, 12, 2, 10, 14, 1, 3, 8, 11, 6, 15, 13}; + static const int rp[80] = {5, 14, 7, 0, 9, 2, 11, 4, 13, 6, 15, 8, 1, 10, 3, 12, 6, 11, 3, 7, + 0, 13, 5, 10, 14, 15, 8, 12, 4, 9, 1, 2, 15, 5, 1, 3, 7, 14, 6, 9, + 11, 8, 12, 2, 10, 0, 4, 13, 8, 6, 4, 1, 3, 11, 15, 0, 5, 12, 2, 13, + 9, 7, 10, 14, 12, 15, 10, 4, 1, 5, 8, 7, 6, 2, 13, 14, 0, 3, 9, 11}; + static const int s[80] = {11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8, 7, 6, 8, 13, + 11, 9, 7, 15, 7, 12, 15, 9, 11, 7, 13, 12, 11, 13, 6, 7, 14, 9, 13, 15, + 14, 8, 13, 6, 5, 12, 7, 5, 11, 12, 14, 15, 14, 15, 9, 8, 9, 14, 5, 6, + 8, 6, 5, 12, 9, 15, 5, 11, 6, 8, 13, 12, 5, 12, 13, 14, 11, 8, 5, 6}; + static const int sp[80] = {8, 9, 9, 11, 13, 15, 15, 5, 7, 7, 8, 11, 14, 14, 12, 6, 9, 13, 15, 7, + 12, 8, 9, 11, 7, 7, 12, 7, 6, 15, 13, 11, 9, 7, 15, 11, 8, 6, 6, 14, + 12, 13, 5, 14, 13, 13, 7, 5, 15, 5, 8, 11, 14, 14, 6, 14, 6, 9, 12, 9, + 12, 5, 15, 8, 8, 5, 12, 9, 12, 5, 14, 6, 8, 13, 6, 5, 15, 13, 11, 11}; + static const uint32_t K[5] = {0x00000000, 0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xa953fd4e}; + static const uint32_t KP[5] = {0x50a28be6, 0x5c4dd124, 0x6d703ef3, 0x7a6d76e9, 0x00000000}; + uint32_t x[16]; + for (int i = 0; i < 16; ++i) + x[i] = load_le(block + 4 * i); + uint32_t al = h[0], bl = h[1], cl = h[2], dl = h[3], el = h[4]; + uint32_t ar = al, br = bl, cr = cl, dr = dl, er = el; + for (int j = 0; j < 80; ++j) { + uint32_t t = rol(al + f(j, bl, cl, dl) + x[r[j]] + K[j / 16], s[j]) + el; + al = el; el = dl; dl = rol(cl, 10); cl = bl; bl = t; + t = rol(ar + f(79 - j, br, cr, dr) + x[rp[j]] + KP[j / 16], sp[j]) + er; + ar = er; er = dr; dr = rol(cr, 10); cr = br; br = t; + } + uint32_t t = h[1] + cl + dr; + h[1] = h[2] + dl + er; + h[2] = h[3] + el + ar; + h[3] = h[4] + al + br; + h[4] = h[0] + bl + cr; + h[0] = t; + } + + void update(const uint8_t *p, size_t n) + { + total += n; + while (n) { + size_t take = std::min(n, sizeof buf - used); + std::memcpy(buf + used, p, take); + used += take; + p += take; + n -= take; + if (used == 64) { + transform(buf); + used = 0; + } + } + } + + Bytes final() + { + uint64_t bits = total * 8; + uint8_t pad = 0x80; + update(&pad, 1); + uint8_t zero = 0; + while (used != 56) + update(&zero, 1); + uint8_t len[8]; + store_le(len, uint32_t(bits)); + store_le(len + 4, uint32_t(bits >> 32)); + update(len, 8); + Bytes out(20); + for (int i = 0; i < 5; ++i) + store_le(out.data() + 4 * i, h[i]); + return out; + } +}; + +// ---- Digest adapters ------------------------------------------------------------- + +class Md2Digest : public Digest { + SEC_MD2_CTX c; +public: + Md2Digest() { sec_MD2Init(&c); } + void update(const uint8_t *p, size_t n) override { sec_MD2Update(&c, const_cast(p), sec_uint4(n)); } + Bytes final() override + { + Bytes d(16); + sec_MD2Final(d.data(), &c); + return d; + } +}; + +class Md4Digest : public Digest { + SEC_MD4_CTX c; +public: + Md4Digest() { md_init(&c); } + void update(const uint8_t *p, size_t n) override { md_update(&c, p, n); } + Bytes final() override + { + Bytes d(16); + md_final(d.data(), &c); + return d; + } +}; + +class Md5Digest : public Digest { + SEC_MD5_CTX c; +public: + Md5Digest() { md_init(&c); } + void update(const uint8_t *p, size_t n) override { md_update(&c, p, n); } + Bytes final() override + { + Bytes d(16); + md_final(d.data(), &c); + return d; + } +}; + +class ShsDigest : public Digest { + SHS_INFO s; + bool sha1; +public: + explicit ShsDigest(bool is_sha1) : sha1(is_sha1) { shs_init(&s); } + void update(const uint8_t *p, size_t n) override { shs_update(&s, p, n, sha1); } + Bytes final() override + { + shs_final(&s, sha1); + Bytes d(20); + for (int i = 0; i < 5; ++i) + store_be(d.data() + 4 * i, s.digest[i]); + return d; + } +}; + +class RmdDigest : public Digest { + Rmd160 r; +public: + void update(const uint8_t *p, size_t n) override { r.update(p, n); } + Bytes final() override { return r.final(); } +}; + +} // namespace + +std::unique_ptr make_digest(Hash h) +{ + switch (h) { + case Hash::MD2: return std::make_unique(); + case Hash::MD4: return std::make_unique(); + case Hash::MD5: return std::make_unique(); + case Hash::SHA0: return std::make_unique(false); + case Hash::SHA1: return std::make_unique(true); + case Hash::RIPEMD160: return std::make_unique(); + default: return nullptr; + } +} + +} // namespace legacy +} // namespace compat + +using namespace compat::legacy; + +extern "C" { + +void sec_MD2Init(SEC_MD2_CTX *context) +{ + std::memset(context, 0, sizeof *context); +} + +void sec_MD2Update(SEC_MD2_CTX *context, unsigned char *input, sec_uint4 inputLen) +{ + unsigned index = context->count; + context->count = (index + inputLen) & 0xf; + unsigned part = 16 - index; + unsigned i = 0; + if (inputLen >= part) { + std::memcpy(context->buffer + index, input, part); + md2_transform(context, context->buffer); + for (i = part; i + 15 < inputLen; i += 16) + md2_transform(context, input + i); + index = 0; + } + std::memcpy(context->buffer + index, input + i, inputLen - i); +} + +void sec_MD2Final(unsigned char *digest, SEC_MD2_CTX *context) +{ + unsigned char padding[16]; + unsigned padlen = 16 - context->count; + std::memset(padding, int(padlen), padlen); + sec_MD2Update(context, padding, padlen); + unsigned char checksum[16]; + std::memcpy(checksum, context->checksum, 16); + sec_MD2Update(context, checksum, 16); + std::memcpy(digest, context->state, 16); + std::memset(context, 0, sizeof *context); +} + +void sec_MD4Init(SEC_MD4_CTX *context) { md_init(context); } + +void sec_MD4Update(SEC_MD4_CTX *context, unsigned char *input, sec_uint4 inputLen) +{ + md_update(context, input, inputLen); +} + +void sec_MD4Final(unsigned char *digest, SEC_MD4_CTX *context) +{ + md_final(digest, context); +} + +void sec_MD5Init(SEC_MD5_CTX *context) { md_init(context); } + +void sec_MD5Update(SEC_MD5_CTX *context, unsigned char *input, sec_uint4 inputLen) +{ + md_update(context, input, inputLen); +} + +void sec_MD5Final(unsigned char *digest, SEC_MD5_CTX *context) +{ + md_final(digest, context); +} + +// After shsFinal()/shs1Final() the message digest is in shsInfo->digest[0..4] +// as host-order words (big-endian when serialised). +void shsInit(SHS_INFO *shsInfo) { shs_init(shsInfo); } + +void shsUpdate(SHS_INFO *shsInfo, unsigned char *buffer, sec_int4 count) +{ + if (count > 0) + shs_update(shsInfo, buffer, size_t(count), false); +} + +void shsFinal(SHS_INFO *shsInfo) { shs_final(shsInfo, false); } + +void shs1Init(SHS_INFO *shsInfo) { shs_init(shsInfo); } + +void shs1Update(SHS_INFO *shsInfo, unsigned char *buffer, sec_int4 count) +{ + if (count > 0) + shs_update(shsInfo, buffer, size_t(count), true); +} + +void shs1Final(SHS_INFO *shsInfo) { shs_final(shsInfo, true); } + +} // extern "C" diff --git a/port/secude/src/lnumber.cpp b/port/secude/src/lnumber.cpp new file mode 100644 index 000000000..e8abfd364 --- /dev/null +++ b/port/secude/src/lnumber.cpp @@ -0,0 +1,266 @@ +// SECUDE long-number arithmetic on L_NUMBER arrays: +// a[0] number of 32-bit words (two's complement of it for negative values) +// a[1..n] magnitude, least significant word first +#include "bn.hpp" + +namespace compat { + +namespace { + +BN_CTX *ctx() { return bn_ctx(); } + +constexpr L_NUMBER kSign = 0x80000000u; +constexpr size_t kMaxWords = MAXGENL * 4; + +Bn from_ln(const L_NUMBER *a) +{ + if (!a) + fail(EINVALID, "missing L_NUMBER"); + L_NUMBER head = a[0]; + bool negative = head & kSign; + size_t n = negative ? size_t(L_NUMBER(~head + 1)) : size_t(head); + if (n > kMaxWords) + fail(EINVALID, "L_NUMBER too long"); + std::vector be(n * 4); + for (size_t i = 0; i < n; ++i) { + L_NUMBER w = a[n - i]; + be[4 * i] = uint8_t(w >> 24); + be[4 * i + 1] = uint8_t(w >> 16); + be[4 * i + 2] = uint8_t(w >> 8); + be[4 * i + 3] = uint8_t(w); + } + Bn b = bn_new(); + if (!BN_bin2bn(be.data(), int(be.size()), b.get())) + throw std::bad_alloc(); + BN_set_negative(b.get(), negative && !BN_is_zero(b.get())); + return b; +} + +void to_ln(const BIGNUM *b, L_NUMBER *out) +{ + int nbytes = BN_num_bytes(b); + size_t n = size_t(nbytes + 3) / 4; + std::vector be(n * 4); + if (n && BN_bn2binpad(b, be.data(), int(be.size())) < 0) + fail(EINTERNAL, "BN_bn2binpad failed"); + for (size_t i = 0; i < n; ++i) { + size_t off = (n - 1 - i) * 4; + out[i + 1] = (L_NUMBER(be[off]) << 24) | (L_NUMBER(be[off + 1]) << 16) | (L_NUMBER(be[off + 2]) << 8) | be[off + 3]; + } + out[0] = BN_is_negative(b) ? L_NUMBER(~L_NUMBER(n) + 1) : L_NUMBER(n); + // libec reads x[1] of a zero result (e.g. the remainder in ln_to_string) + if (n == 0) + out[1] = 0; +} + +void check(int ok) { bn_check(ok); } + +} // namespace + +} // namespace compat + +using namespace compat; + +extern "C" { + +int lngtouse(L_NUMBER_ARRAY modul) +{ + return guarded("lngtouse", -1, [&] { return BN_num_bits(from_ln(modul).get()) - 1; }); +} + +int arithmetic_comp(L_NUMBER *Ap, L_NUMBER *Bp) +{ + return guarded("arithmetic_comp", 0, [&] { + int c = BN_cmp(from_ln(Ap).get(), from_ln(Bp).get()); + return c < 0 ? -1 : c > 0 ? 1 : 0; + }); +} + +void arithmetic_add(L_NUMBER_ARRAY Ap, L_NUMBER_ARRAY Bp, L_NUMBER_ARRAY Sum) +{ + guarded_void("arithmetic_add", [&] { + Bn a = from_ln(Ap), b = from_ln(Bp), r = bn_new(); + check(BN_add(r.get(), a.get(), b.get())); + to_ln(r.get(), Sum); + }); +} + +void arithmetic_sub(L_NUMBER_ARRAY Ap, L_NUMBER_ARRAY Bp, L_NUMBER_ARRAY Diff) +{ + guarded_void("arithmetic_sub", [&] { + Bn a = from_ln(Ap), b = from_ln(Bp), r = bn_new(); + check(BN_sub(r.get(), a.get(), b.get())); + to_ln(r.get(), Diff); + }); +} + +void arithmetic_mult(L_NUMBER_ARRAY A, L_NUMBER_ARRAY B, L_NUMBER_ARRAY erg) +{ + guarded_void("arithmetic_mult", [&] { + Bn a = from_ln(A), b = from_ln(B), r = bn_new(); + check(BN_mul(r.get(), a.get(), b.get(), ctx())); + to_ln(r.get(), erg); + }); +} + +void arithmetic_div(L_NUMBER_ARRAY A, L_NUMBER_ARRAY B, L_NUMBER_ARRAY Q, L_NUMBER_ARRAY R) +{ + guarded_void("arithmetic_div", [&] { + Bn a = from_ln(A), b = from_ln(B), q = bn_new(), r = bn_new(); + if (BN_is_zero(b.get())) + fail(EINVALID, "division by zero"); + check(BN_div(q.get(), r.get(), a.get(), b.get(), ctx())); + if (Q) + to_ln(q.get(), Q); + if (R) + to_ln(r.get(), R); + }); +} + +void arithmetic_shift(L_NUMBER_ARRAY A, int exp2b, L_NUMBER_ARRAY S) +{ + guarded_void("arithmetic_shift", [&] { + Bn a = from_ln(A), r = bn_new(); + bool negative = BN_is_negative(a.get()); + BN_set_negative(a.get(), 0); + if (exp2b >= 0) + check(BN_lshift(r.get(), a.get(), exp2b)); + else + check(BN_rshift(r.get(), a.get(), -exp2b)); + BN_set_negative(r.get(), negative && !BN_is_zero(r.get())); + to_ln(r.get(), S); + }); +} + +static int modular(const char *proc, L_NUMBER *op1, L_NUMBER *op2, L_NUMBER *erg, L_NUMBER *modul, + int (*fn)(BIGNUM *, const BIGNUM *, const BIGNUM *, const BIGNUM *, BN_CTX *)) +{ + return guarded(proc, -1, [&] { + Bn a = from_ln(op1), b = from_ln(op2), m = from_ln(modul), r = bn_new(); + if (BN_is_zero(m.get())) + fail(EINVALID, "modulus is zero"); + BN_set_negative(m.get(), 0); + check(fn(r.get(), a.get(), b.get(), m.get(), ctx())); + to_ln(r.get(), erg); + return 0; + }); +} + +int arithmetic_madd(L_NUMBER_ARRAY op1, L_NUMBER_ARRAY op2, L_NUMBER_ARRAY erg, L_NUMBER_ARRAY modul) +{ + return modular("arithmetic_madd", op1, op2, erg, modul, BN_mod_add); +} + +int arithmetic_msub(L_NUMBER_ARRAY op1, L_NUMBER_ARRAY op2, L_NUMBER_ARRAY erg, L_NUMBER_ARRAY modul) +{ + return modular("arithmetic_msub", op1, op2, erg, modul, BN_mod_sub); +} + +int arithmetic_mmult(L_NUMBER_ARRAY op1, L_NUMBER_ARRAY op2, L_NUMBER_ARRAY erg, L_NUMBER_ARRAY modul) +{ + return modular("arithmetic_mmult", op1, op2, erg, modul, BN_mod_mul); +} + +int arithmetic_mdiv(L_NUMBER_ARRAY op1, L_NUMBER_ARRAY op2, L_NUMBER_ARRAY erg, L_NUMBER_ARRAY modul) +{ + return guarded("arithmetic_mdiv", -1, [&] { + Bn a = from_ln(op1), b = from_ln(op2), m = from_ln(modul), inv = bn_new(), r = bn_new(); + if (BN_is_zero(m.get())) + fail(EINVALID, "modulus is zero"); + BN_set_negative(m.get(), 0); + if (!BN_mod_inverse(inv.get(), b.get(), m.get(), ctx())) + fail(EINVALID, "divisor not invertible"); + check(BN_mod_mul(r.get(), a.get(), inv.get(), m.get(), ctx())); + to_ln(r.get(), erg); + return 0; + }); +} + +int arithmetic_mexp(L_NUMBER_ARRAY bas, L_NUMBER_ARRAY exp, L_NUMBER_ARRAY erg, L_NUMBER_ARRAY modul) +{ + return guarded("arithmetic_mexp", -1, [&] { + Bn b = from_ln(bas), e = from_ln(exp), m = from_ln(modul), r = bn_new(); + if (BN_is_zero(m.get())) + fail(EINVALID, "modulus is zero"); + if (BN_is_negative(e.get())) + fail(EINVALID, "negative exponent"); + BN_set_negative(m.get(), 0); + check(BN_nnmod(b.get(), b.get(), m.get(), ctx())); + check(BN_mod_exp(r.get(), b.get(), e.get(), m.get(), ctx())); + to_ln(r.get(), erg); + return 0; + }); +} + +// 0 for a probable prime, -1 otherwise (libec's curve validation) +int rabinstest(L_NUMBER_ARRAY zahl) +{ + return guarded("rabinstest", -1, [&] { + Bn n = from_ln(zahl); + if (BN_is_negative(n.get())) + return -1; + int r = BN_check_prime(n.get(), ctx(), nullptr); + if (r < 0) + fail(EINTERNAL, "primality test failed"); + return r == 1 ? 0 : -1; + }); +} + +int rndm(int lgth, L_NUMBER_ARRAY zahl, int version) +{ + return guarded("rndm", -1, [&] { + Bn r = bn_new(); + if (lgth <= 0) + BN_zero(r.get()); + else + check(BN_rand(r.get(), lgth, BN_RAND_TOP_ANY, BN_RAND_BOTTOM_ANY)); + to_ln(r.get(), zahl); + return 0; + }); +} + +RC aux_OctetString2LN2(L_NUMBER *lnum, OctetString *ostr) +{ + return guarded("aux_OctetString2LN2", -1, [&] { + if (!lnum || !ostr) + fail(EINVALID, "missing parameter"); + Bytes b = bytes_of(ostr); + Bn r = bn_new(); + if (!b.empty() && !BN_bin2bn(b.data(), int(b.size()), r.get())) + throw std::bad_alloc(); + to_ln(r.get(), lnum); + return 0; + }); +} + +RC aux_BitString2LN2(L_NUMBER *lnum, BitString *bstr) +{ + return guarded("aux_BitString2LN2", -1, [&] { + if (!lnum || !bstr) + fail(EINVALID, "missing parameter"); + Bytes b = bytes_of(bstr); + Bn r = bn_new(); + if (!b.empty()) { + if (!BN_bin2bn(b.data(), int(b.size()), r.get())) + throw std::bad_alloc(); + check(BN_rshift(r.get(), r.get(), int(b.size() * 8 - bstr->nbits))); + } + to_ln(r.get(), lnum); + return 0; + }); +} + +OctetString *aux_LN2OctetString(L_NUMBER_ARRAY lnum, int size) +{ + return guarded("aux_LN2OctetString", nullptr, [&] { + Bn a = from_ln(lnum); + size_t minimal = size_t(BN_num_bytes(a.get())); + size_t n = size > 0 && size_t(size) > minimal ? size_t(size) : minimal; + Bytes out(n); + if (n) + BN_bn2binpad(a.get(), out.data(), int(n)); + return new_ostr(out); + }); +} + +} // extern "C" diff --git a/port/secude/src/misc.cpp b/port/secude/src/misc.cpp new file mode 100644 index 000000000..505e0c2ce --- /dev/null +++ b/port/secude/src/misc.cpp @@ -0,0 +1,169 @@ +// Files, randomness, licensing and version information. +#include "internal.hpp" + +#include +#include +#include +#include +#include +#include +#include + +namespace compat { + +Bytes random_bytes(size_t n) +{ + Bytes b(n); + if (n && RAND_bytes_ex(libctx(), b.data(), n, 0) != 1) + fail(ERANDOM, "random number generator failed"); + return b; +} + +Bytes read_file(const std::string &path, bool &exists) +{ + exists = false; + FILE *f = std::fopen(path.c_str(), "rb"); + if (!f) { + if (errno == ENOENT || errno == ENOTDIR) + return {}; + fail(EREADFILE, "cannot open " + path + ": " + std::strerror(errno)); + } + exists = true; + Bytes data; + uint8_t buf[65536]; + size_t n; + while ((n = std::fread(buf, 1, sizeof buf, f)) > 0) + data.insert(data.end(), buf, buf + n); + bool error = std::ferror(f); + std::fclose(f); + if (error) + fail(EREADFILE, "cannot read " + path); + return data; +} + +void write_file_atomic(const std::string &path, const Bytes &data, int mode) +{ + std::string tmp = path + ".tmp" + std::to_string(::getpid()); + int fd = ::open(tmp.c_str(), O_WRONLY | O_CREAT | O_TRUNC, mode); + if (fd < 0) + fail(EWRITEFILE, "cannot create " + tmp + ": " + std::strerror(errno)); + size_t done = 0; + while (done < data.size()) { + ssize_t w = ::write(fd, data.data() + done, data.size() - done); + if (w < 0) { + if (errno == EINTR) + continue; + ::close(fd); + ::unlink(tmp.c_str()); + fail(EWRITEFILE, "cannot write " + tmp); + } + done += size_t(w); + } + if (::fsync(fd) != 0 || ::close(fd) != 0) { + ::unlink(tmp.c_str()); + fail(EWRITEFILE, "cannot write " + tmp); + } + if (std::rename(tmp.c_str(), path.c_str()) != 0) { + ::unlink(tmp.c_str()); + fail(EWRITEFILE, "cannot replace " + path + ": " + std::strerror(errno)); + } +} + +std::string hex(const uint8_t *p, size_t n, bool upper) +{ + static const char *digits[2] = {"0123456789abcdef", "0123456789ABCDEF"}; + std::string s; + s.reserve(n * 2); + for (size_t i = 0; i < n; ++i) { + s += digits[upper][p[i] >> 4]; + s += digits[upper][p[i] & 0xf]; + } + return s; +} + +} // namespace compat + +using namespace compat; + +namespace { + +const char *version_string() +{ + static const std::string v = std::string("SECUDE compatibility layer 1.0 for CrypTool (") + OPENSSL_VERSION_TEXT + ")"; + return v.c_str(); +} + +} // namespace + +extern "C" { + +const char *secude_compat_version(void) { return version_string(); } + +RC SECUDE_HasValidTicket(char **rcIdOrError) +{ + static char text[] = "no license ticket required"; + if (rcIdOrError) + *rcIdOrError = text; + return 0; +} + +int light_version(void) +{ + return 0; +} + +char *aux_sprint_version(char *string) +{ + return guarded("aux_sprint_version", nullptr, [&] { return append_string(string, version_string()); }); +} + +OctetString *aux_file2OctetString(char *fn) +{ + return guarded("aux_file2OctetString", nullptr, [&] { + if (!fn) + fail(EINVALID, "missing file name"); + bool exists = false; + Bytes data = read_file(fn, exists); + if (!exists) + fail(EFILENOTEXISTING, std::string("file ") + fn + " does not exist"); + return new_ostr(data); + }); +} + +// flag 3 appends to the file, every other value creates or truncates it. +int aux_OctetString2file(OctetString *ostr, char *fn, int flag) +{ + return guarded("aux_OctetString2file", -1, [&] { + if (!ostr || !fn) + fail(EINVALID, "missing parameter"); + FILE *f = std::fopen(fn, flag == 3 ? "ab" : "wb"); + if (!f) + fail(EWRITEFILE, std::string("cannot open ") + fn + ": " + std::strerror(errno)); + size_t n = ostr->noctets ? std::fwrite(ostr->octets, 1, ostr->noctets, f) : 0; + bool ok = n == ostr->noctets; + ok = std::fclose(f) == 0 && ok; + if (!ok) + fail(EWRITEFILE, std::string("cannot write ") + fn); + return 0; + }); +} + +OctetString *aux_latin1_to_unicode(char *latin_string, Boolean termination) +{ + return guarded("aux_latin1_to_unicode", nullptr, [&] { + if (!latin_string) + fail(EINVALID, "missing string"); + Bytes b; + for (const unsigned char *p = reinterpret_cast(latin_string); *p; ++p) { + b.push_back(0); + b.push_back(*p); + } + if (termination) { + b.push_back(0); + b.push_back(0); + } + return new_ostr(b); + }); +} + +} // extern "C" diff --git a/port/secude/src/oids.cpp b/port/secude/src/oids.cpp new file mode 100644 index 000000000..0014ff2bf --- /dev/null +++ b/port/secude/src/oids.cpp @@ -0,0 +1,491 @@ +#include "internal.hpp" + +#include + +namespace compat { + +namespace oids { +const Oid rsa{2, 5, 8, 1, 1}; +const Oid rsaEncryption{1, 2, 840, 113549, 1, 1, 1}; +const Oid dsa{1, 3, 14, 3, 2, 12}; +const Oid id_dsa{1, 2, 840, 10040, 4, 1}; +const Oid md2{1, 2, 840, 113549, 2, 2}; +const Oid md4{1, 2, 840, 113549, 2, 4}; +const Oid md5{1, 2, 840, 113549, 2, 5}; +const Oid sha{1, 3, 14, 3, 2, 18}; +const Oid sha1{1, 3, 14, 3, 2, 26}; +const Oid ripemd160{1, 3, 36, 3, 2, 1}; +const Oid sha256{2, 16, 840, 1, 101, 3, 4, 2, 1}; +const Oid sha384{2, 16, 840, 1, 101, 3, 4, 2, 2}; +const Oid sha512{2, 16, 840, 1, 101, 3, 4, 2, 3}; +const Oid data{1, 2, 840, 113549, 1, 7, 1}; +const Oid encryptedData{1, 2, 840, 113549, 1, 7, 6}; +const Oid keyBag{1, 2, 840, 113549, 1, 12, 10, 1, 1}; +const Oid pkcs8ShroudedKeyBag{1, 2, 840, 113549, 1, 12, 10, 1, 2}; +const Oid certBag{1, 2, 840, 113549, 1, 12, 10, 1, 3}; +const Oid x509Certificate{1, 2, 840, 113549, 1, 9, 22, 1}; +const Oid friendlyName{1, 2, 840, 113549, 1, 9, 20}; +const Oid localKeyId{1, 2, 840, 113549, 1, 9, 21}; +const Oid pbeSHA1RC4_128{1, 2, 840, 113549, 1, 12, 1, 1}; +const Oid pbeSHA1RC4_40{1, 2, 840, 113549, 1, 12, 1, 2}; +const Oid pbeSHA1DES3{1, 2, 840, 113549, 1, 12, 1, 3}; +const Oid pbeSHA1DES2{1, 2, 840, 113549, 1, 12, 1, 4}; +const Oid pbeSHA1RC2_128{1, 2, 840, 113549, 1, 12, 1, 5}; +const Oid pbeSHA1RC2_40{1, 2, 840, 113549, 1, 12, 1, 6}; +const Oid pbes2{1, 2, 840, 113549, 1, 5, 13}; +const Oid pbkdf2{1, 2, 840, 113549, 1, 5, 12}; +const Oid hmacSHA1{1, 2, 840, 113549, 2, 7}; +const Oid hmacSHA256{1, 2, 840, 113549, 2, 9}; +const Oid hmacSHA384{1, 2, 840, 113549, 2, 10}; +const Oid hmacSHA512{1, 2, 840, 113549, 2, 11}; +const Oid aes128CBC{2, 16, 840, 1, 101, 3, 4, 1, 2}; +const Oid aes192CBC{2, 16, 840, 1, 101, 3, 4, 1, 22}; +const Oid aes256CBC{2, 16, 840, 1, 101, 3, 4, 1, 42}; +const Oid desEDE3CBC{1, 2, 840, 113549, 3, 7}; +const Oid subjectKeyIdentifier{2, 5, 29, 14}; +const Oid keyUsage{2, 5, 29, 15}; +const Oid basicConstraints{2, 5, 29, 19}; +const Oid authorityKeyIdentifier{2, 5, 29, 35}; +const Oid cryptoolPseName{2, 206, 5, 4, 3, 2}; +} // namespace oids + +} // namespace compat + +using namespace compat; + +#define OID_ELEMS(name, ...) static unsigned int name##_elems[] = {__VA_ARGS__} +#define OID_OBJ(name) {int(sizeof(name##_elems) / sizeof(unsigned int)), name##_elems} + +// Object identifiers of algorithms +OID_ELEMS(rsa, 2, 5, 8, 1, 1); +OID_ELEMS(rsaEncryption, 1, 2, 840, 113549, 1, 1, 1); +OID_ELEMS(dsa, 1, 3, 14, 3, 2, 12); +OID_ELEMS(md2, 1, 2, 840, 113549, 2, 2); +OID_ELEMS(md4, 1, 2, 840, 113549, 2, 4); +OID_ELEMS(md5, 1, 2, 840, 113549, 2, 5); +OID_ELEMS(sha, 1, 3, 14, 3, 2, 18); +OID_ELEMS(sha1, 1, 3, 14, 3, 2, 26); +OID_ELEMS(ripemd160, 1, 3, 36, 3, 2, 1); +OID_ELEMS(md2WithRsaEncryption, 1, 2, 840, 113549, 1, 1, 2); +OID_ELEMS(md4WithRsaEncryption, 1, 2, 840, 113549, 1, 1, 3); +OID_ELEMS(md5WithRsaEncryption, 1, 2, 840, 113549, 1, 1, 4); +OID_ELEMS(sha1WithRsaEncryption, 1, 2, 840, 113549, 1, 1, 5); +OID_ELEMS(md2WithRSASignature, 1, 3, 14, 3, 2, 24); +OID_ELEMS(shaWithRSASignature, 1, 3, 14, 3, 2, 15); +OID_ELEMS(sha1WithRSASignature, 1, 3, 14, 3, 2, 29); +OID_ELEMS(ripemd160WithRSASignature, 1, 3, 36, 3, 3, 1, 2); +OID_ELEMS(dsaWithSHA, 1, 3, 14, 3, 2, 13); +OID_ELEMS(dsaWithSHA1, 1, 3, 14, 3, 2, 27); +OID_ELEMS(desECB, 1, 3, 14, 3, 2, 6); +OID_ELEMS(desCBC, 1, 3, 14, 3, 2, 7); +OID_ELEMS(desCBC_pad, 1, 3, 36, 3, 1, 1, 2, 1); +OID_ELEMS(desEDE, 1, 3, 14, 3, 2, 17); +OID_ELEMS(desCBC3, 1, 3, 36, 3, 1, 3, 2); +OID_ELEMS(desCBC3_pad, 1, 3, 36, 3, 1, 3, 2, 1); +OID_ELEMS(desEDE3CBC, 1, 2, 840, 113549, 3, 7); +OID_ELEMS(idea, 1, 3, 36, 3, 1, 2); +OID_ELEMS(ideaCBC, 1, 3, 6, 1, 4, 1, 188, 7, 1, 1, 2); +OID_ELEMS(rc2CBC, 1, 2, 840, 113549, 3, 2); +OID_ELEMS(rc4, 1, 2, 840, 113549, 3, 4); +OID_ELEMS(aes128ECB, 2, 16, 840, 1, 101, 3, 4, 1, 1); +OID_ELEMS(aes128CBC, 2, 16, 840, 1, 101, 3, 4, 1, 2); +OID_ELEMS(aes192ECB, 2, 16, 840, 1, 101, 3, 4, 1, 21); +OID_ELEMS(aes192CBC, 2, 16, 840, 1, 101, 3, 4, 1, 22); +OID_ELEMS(aes256ECB, 2, 16, 840, 1, 101, 3, 4, 1, 41); +OID_ELEMS(aes256CBC, 2, 16, 840, 1, 101, 3, 4, 1, 42); +OID_ELEMS(pbeWithSHA1And128BitRC4, 1, 2, 840, 113549, 1, 12, 1, 1); +OID_ELEMS(pbeWithSHA1And40BitRC4, 1, 2, 840, 113549, 1, 12, 1, 2); +OID_ELEMS(pbeWithSHA1AndDES3xCBC, 1, 2, 840, 113549, 1, 12, 1, 3); +OID_ELEMS(pbeWithSHA1AndDES3CBC, 1, 2, 840, 113549, 1, 12, 1, 4); +OID_ELEMS(pbeWithSHA1And128BitRC2CBC, 1, 2, 840, 113549, 1, 12, 1, 5); +OID_ELEMS(pbeWithSHA1And40BitRC2CBC, 1, 2, 840, 113549, 1, 12, 1, 6); + +// Object identifiers of PSE object types (private arc 2.206.5.1, see README.md) +OID_ELEMS(SKnew, 2, 206, 5, 1, 1); +OID_ELEMS(SKold, 2, 206, 5, 1, 2); +OID_ELEMS(SignSK, 2, 206, 5, 1, 3); +OID_ELEMS(DecSKnew, 2, 206, 5, 1, 4); +OID_ELEMS(DecSKold, 2, 206, 5, 1, 5); +OID_ELEMS(AuthSK, 2, 206, 5, 1, 6); +OID_ELEMS(Cert, 2, 206, 5, 1, 10); +OID_ELEMS(SignCert, 2, 206, 5, 1, 11); +OID_ELEMS(EncCert, 2, 206, 5, 1, 12); +OID_ELEMS(AuthCert, 2, 206, 5, 1, 13); +OID_ELEMS(PKRoot, 2, 206, 5, 1, 20); +OID_ELEMS(FCPath, 2, 206, 5, 1, 21); +OID_ELEMS(SerialNumber, 2, 206, 5, 1, 30); +OID_ELEMS(Name, 2, 206, 5, 1, 31); +OID_ELEMS(Uid, 2, 206, 5, 1, 40); + +static unsigned int rsa_default_keysize = DEF_ASYM_KEYSIZE; +static ObjId md2WithRSASignature_oid_obj = OID_OBJ(md2WithRSASignature); + +extern "C" { + +ObjId rsa_oid = OID_OBJ(rsa); +ObjId rsaEncryption_oid = OID_OBJ(rsaEncryption); +ObjId dsa_oid = OID_OBJ(dsa); +ObjId md2_oid = OID_OBJ(md2); +ObjId md4_oid = OID_OBJ(md4); +ObjId md5_oid = OID_OBJ(md5); +ObjId sha_oid = OID_OBJ(sha); +ObjId sha1_oid = OID_OBJ(sha1); +ObjId ripemd160_oid = OID_OBJ(ripemd160); +ObjId md2WithRsaEncryption_oid = OID_OBJ(md2WithRsaEncryption); +ObjId md4WithRsaEncryption_oid = OID_OBJ(md4WithRsaEncryption); +ObjId md5WithRsaEncryption_oid = OID_OBJ(md5WithRsaEncryption); +ObjId sha1WithRsaEncryption_oid = OID_OBJ(sha1WithRsaEncryption); +ObjId shaWithRSASignature_oid = OID_OBJ(shaWithRSASignature); +ObjId sha1WithRSASignature_oid = OID_OBJ(sha1WithRSASignature); +ObjId ripemd160WithRSASignature_oid = OID_OBJ(ripemd160WithRSASignature); +ObjId dsaWithSHA_oid = OID_OBJ(dsaWithSHA); +ObjId dsaWithSHA1_oid = OID_OBJ(dsaWithSHA1); +ObjId desECB_oid = OID_OBJ(desECB); +ObjId desCBC_oid = OID_OBJ(desCBC); +ObjId desCBC_pad_oid = OID_OBJ(desCBC_pad); +ObjId desEDE_oid = OID_OBJ(desEDE); +ObjId desCBC3_oid = OID_OBJ(desCBC3); +ObjId desCBC3_pad_oid = OID_OBJ(desCBC3_pad); +ObjId desEDE3CBC_oid = OID_OBJ(desEDE3CBC); +ObjId idea_oid = OID_OBJ(idea); +ObjId ideaCBC_oid = OID_OBJ(ideaCBC); +ObjId rc2CBC_oid = OID_OBJ(rc2CBC); +ObjId rc4_oid = OID_OBJ(rc4); +ObjId aes128ECB_oid = OID_OBJ(aes128ECB); +ObjId aes128CBC_oid = OID_OBJ(aes128CBC); +ObjId aes192ECB_oid = OID_OBJ(aes192ECB); +ObjId aes192CBC_oid = OID_OBJ(aes192CBC); +ObjId aes256ECB_oid = OID_OBJ(aes256ECB); +ObjId aes256CBC_oid = OID_OBJ(aes256CBC); +ObjId pbeWithSHA1And128BitRC4_oid = OID_OBJ(pbeWithSHA1And128BitRC4); +ObjId pbeWithSHA1And40BitRC4_oid = OID_OBJ(pbeWithSHA1And40BitRC4); +ObjId pbeWithSHA1AndDES3xCBC_oid = OID_OBJ(pbeWithSHA1AndDES3xCBC); +ObjId pbeWithSHA1AndDES3CBC_oid = OID_OBJ(pbeWithSHA1AndDES3CBC); +ObjId pbeWithSHA1And128BitRC2CBC_oid = OID_OBJ(pbeWithSHA1And128BitRC2CBC); +ObjId pbeWithSHA1And40BitRC2CBC_oid = OID_OBJ(pbeWithSHA1And40BitRC2CBC); + +ObjId SKnew_oid = OID_OBJ(SKnew); +ObjId SKold_oid = OID_OBJ(SKold); +ObjId SignSK_oid = OID_OBJ(SignSK); +ObjId DecSKnew_oid = OID_OBJ(DecSKnew); +ObjId DecSKold_oid = OID_OBJ(DecSKold); +ObjId AuthSK_oid = OID_OBJ(AuthSK); +ObjId Cert_oid = OID_OBJ(Cert); +ObjId SignCert_oid = OID_OBJ(SignCert); +ObjId EncCert_oid = OID_OBJ(EncCert); +ObjId AuthCert_oid = OID_OBJ(AuthCert); +ObjId PKRoot_oid = OID_OBJ(PKRoot); +ObjId FCPath_oid = OID_OBJ(FCPath); +ObjId SerialNumber_oid = OID_OBJ(SerialNumber); +ObjId Name_oid = OID_OBJ(Name); +ObjId Uid_oid = OID_OBJ(Uid); + +ObjId *Uid_OID = &Uid_oid; +ObjId *Cert_OID = &Cert_oid; + +AlgId rsa_aid = {&rsa_oid, &rsa_default_keysize, nullptr}; +AlgId rsaEncryption_aid = {&rsaEncryption_oid, nullptr, nullptr}; +AlgId dsa_aid = {&dsa_oid, nullptr, nullptr}; +AlgId md2_aid = {&md2_oid, nullptr, nullptr}; +AlgId md4_aid = {&md4_oid, nullptr, nullptr}; +AlgId md5_aid = {&md5_oid, nullptr, nullptr}; +AlgId sha_aid = {&sha_oid, nullptr, nullptr}; +AlgId sha1_aid = {&sha1_oid, nullptr, nullptr}; +AlgId ripemd160_aid = {&ripemd160_oid, nullptr, nullptr}; +AlgId md2WithRsaEncryption_aid = {&md2WithRsaEncryption_oid, nullptr, nullptr}; +AlgId md4WithRsaEncryption_aid = {&md4WithRsaEncryption_oid, nullptr, nullptr}; +AlgId md5WithRsaEncryption_aid = {&md5WithRsaEncryption_oid, nullptr, nullptr}; +AlgId sha1WithRsaEncryption_aid = {&sha1WithRsaEncryption_oid, nullptr, nullptr}; +AlgId md2WithRSASignature_aid = {&md2WithRSASignature_oid_obj, nullptr, nullptr}; +AlgId shaWithRSASignature_aid = {&shaWithRSASignature_oid, nullptr, nullptr}; +AlgId sha1WithRSASignature_aid = {&sha1WithRSASignature_oid, nullptr, nullptr}; +AlgId ripemd160WithRSASignature_aid = {&ripemd160WithRSASignature_oid, nullptr, nullptr}; +AlgId dsaWithSHA_aid = {&dsaWithSHA_oid, nullptr, nullptr}; +AlgId dsaWithSHA1_aid = {&dsaWithSHA1_oid, nullptr, nullptr}; +AlgId desECB_aid = {&desECB_oid, nullptr, nullptr}; +AlgId desCBC_aid = {&desCBC_oid, nullptr, nullptr}; +AlgId desCBC_pad_aid = {&desCBC_pad_oid, nullptr, nullptr}; +AlgId desEDE_aid = {&desEDE_oid, nullptr, nullptr}; +AlgId desCBC3_aid = {&desCBC3_oid, nullptr, nullptr}; +AlgId desCBC3_pad_aid = {&desCBC3_pad_oid, nullptr, nullptr}; +AlgId desEDE3CBC_aid = {&desEDE3CBC_oid, nullptr, nullptr}; +AlgId idea_aid = {&idea_oid, nullptr, nullptr}; +AlgId ideaCBC_aid = {&ideaCBC_oid, nullptr, nullptr}; +AlgId rc2CBC_aid = {&rc2CBC_oid, nullptr, nullptr}; +AlgId rc4_aid = {&rc4_oid, nullptr, nullptr}; +AlgId aes128ECB_aid = {&aes128ECB_oid, nullptr, nullptr}; +AlgId aes128CBC_aid = {&aes128CBC_oid, nullptr, nullptr}; +AlgId aes192ECB_aid = {&aes192ECB_oid, nullptr, nullptr}; +AlgId aes192CBC_aid = {&aes192CBC_oid, nullptr, nullptr}; +AlgId aes256ECB_aid = {&aes256ECB_oid, nullptr, nullptr}; +AlgId aes256CBC_aid = {&aes256CBC_oid, nullptr, nullptr}; + +} // extern "C" + +namespace compat { + +namespace { + +using H = Hash; +using C = Cipher; + +const std::vector &alg_table() +{ + static const std::vector table = { + {"md2", oids::md2, PARM_NULL, HASH, NoAlgEnc, H::MD2, C::None, NoAlgMode, Pad::None, &md2_aid}, + {"md4", oids::md4, PARM_NULL, HASH, NoAlgEnc, H::MD4, C::None, NoAlgMode, Pad::None, &md4_aid}, + {"md5", oids::md5, PARM_NULL, HASH, NoAlgEnc, H::MD5, C::None, NoAlgMode, Pad::None, &md5_aid}, + {"sha", oids::sha, PARM_NULL, HASH, NoAlgEnc, H::SHA0, C::None, NoAlgMode, Pad::None, &sha_aid}, + {"sha1", oids::sha1, PARM_NULL, HASH, NoAlgEnc, H::SHA1, C::None, NoAlgMode, Pad::None, &sha1_aid}, + {"ripemd160", oids::ripemd160, PARM_NULL, HASH, NoAlgEnc, H::RIPEMD160, C::None, NoAlgMode, Pad::None, &ripemd160_aid}, + {"sha256", oids::sha256, PARM_NULL, HASH, NoAlgEnc, H::SHA256, C::None, NoAlgMode, Pad::None, nullptr}, + {"sha384", oids::sha384, PARM_NULL, HASH, NoAlgEnc, H::SHA384, C::None, NoAlgMode, Pad::None, nullptr}, + {"sha512", oids::sha512, PARM_NULL, HASH, NoAlgEnc, H::SHA512, C::None, NoAlgMode, Pad::None, nullptr}, + + {"RSA", oids::rsa, PARM_INTEGER, ASYM_ENC, SECUDE_ALG_RSA, H::None, C::None, NoAlgMode, Pad::None, &rsa_aid}, + {"rsaEncryption", oids::rsaEncryption, PARM_NULL, ASYM_ENC, SECUDE_ALG_RSA, H::None, C::None, NoAlgMode, Pad::None, &rsaEncryption_aid}, + {"DSA", oids::dsa, PARM_KeyBits, SIG, SECUDE_ALG_DSA, H::None, C::None, NoAlgMode, Pad::None, &dsa_aid}, + {"id-dsa", oids::id_dsa, PARM_KeyBits, SIG, SECUDE_ALG_DSA, H::None, C::None, NoAlgMode, Pad::None, nullptr}, + + {"md2WithRsaEncryption", {1, 2, 840, 113549, 1, 1, 2}, PARM_NULL, SIG, SECUDE_ALG_RSA, H::MD2, C::None, NoAlgMode, Pad::None, &md2WithRsaEncryption_aid}, + {"md4WithRsaEncryption", {1, 2, 840, 113549, 1, 1, 3}, PARM_NULL, SIG, SECUDE_ALG_RSA, H::MD4, C::None, NoAlgMode, Pad::None, &md4WithRsaEncryption_aid}, + {"md5WithRsaEncryption", {1, 2, 840, 113549, 1, 1, 4}, PARM_NULL, SIG, SECUDE_ALG_RSA, H::MD5, C::None, NoAlgMode, Pad::None, &md5WithRsaEncryption_aid}, + {"sha1WithRsaEncryption", {1, 2, 840, 113549, 1, 1, 5}, PARM_NULL, SIG, SECUDE_ALG_RSA, H::SHA1, C::None, NoAlgMode, Pad::None, &sha1WithRsaEncryption_aid}, + {"sha256WithRsaEncryption", {1, 2, 840, 113549, 1, 1, 11}, PARM_NULL, SIG, SECUDE_ALG_RSA, H::SHA256, C::None, NoAlgMode, Pad::None, nullptr}, + {"md2WithRSASignature", {1, 3, 14, 3, 2, 24}, PARM_NULL, SIG, SECUDE_ALG_RSA, H::MD2, C::None, NoAlgMode, Pad::None, &md2WithRSASignature_aid}, + {"md5WithRSASignature", {1, 3, 14, 3, 2, 25}, PARM_NULL, SIG, SECUDE_ALG_RSA, H::MD5, C::None, NoAlgMode, Pad::None, nullptr}, + {"shaWithRSASignature", {1, 3, 14, 3, 2, 15}, PARM_NULL, SIG, SECUDE_ALG_RSA, H::SHA0, C::None, NoAlgMode, Pad::None, &shaWithRSASignature_aid}, + {"sha1WithRSASignature", {1, 3, 14, 3, 2, 29}, PARM_NULL, SIG, SECUDE_ALG_RSA, H::SHA1, C::None, NoAlgMode, Pad::None, &sha1WithRSASignature_aid}, + {"ripemd160WithRSASignature", {1, 3, 36, 3, 3, 1, 2}, PARM_NULL, SIG, SECUDE_ALG_RSA, H::RIPEMD160, C::None, NoAlgMode, Pad::None, &ripemd160WithRSASignature_aid}, + {"dsaWithSHA", {1, 3, 14, 3, 2, 13}, PARM_ABSENT, SIG, SECUDE_ALG_DSA, H::SHA0, C::None, NoAlgMode, Pad::None, &dsaWithSHA_aid}, + {"dsaWithSHA1", {1, 3, 14, 3, 2, 27}, PARM_ABSENT, SIG, SECUDE_ALG_DSA, H::SHA1, C::None, NoAlgMode, Pad::None, &dsaWithSHA1_aid}, + {"id-dsa-with-sha1", {1, 2, 840, 10040, 4, 3}, PARM_ABSENT, SIG, SECUDE_ALG_DSA, H::SHA1, C::None, NoAlgMode, Pad::None, nullptr}, + + {"desECB", {1, 3, 14, 3, 2, 6}, PARM_ABSENT, SYM_ENC, DES, H::None, C::DES, ECB, Pad::Std, &desECB_aid}, + {"desCBC", {1, 3, 14, 3, 2, 7}, PARM_OctetString, SYM_ENC, DES, H::None, C::DES, CBC, Pad::Std, &desCBC_aid}, + {"desCBC_pad", {1, 3, 36, 3, 1, 1, 2, 1}, PARM_OctetString, SYM_ENC, DES, H::None, C::DES, CBC, Pad::Pkcs, &desCBC_pad_aid}, + {"desEDE", {1, 3, 14, 3, 2, 17}, PARM_ABSENT, SYM_ENC, DES3, H::None, C::DES3, ECB, Pad::Std, &desEDE_aid}, + {"desCBC3", {1, 3, 36, 3, 1, 3, 2}, PARM_OctetString, SYM_ENC, DES3, H::None, C::DES3, CBC, Pad::Std, &desCBC3_aid}, + {"desCBC3_pad", {1, 3, 36, 3, 1, 3, 2, 1}, PARM_OctetString, SYM_ENC, DES3, H::None, C::DES3, CBC, Pad::Pkcs, &desCBC3_pad_aid}, + {"desEDE3CBC", oids::desEDE3CBC, PARM_OctetString, SYM_ENC, DES3, H::None, C::DES3, CBC, Pad::Pkcs, &desEDE3CBC_aid}, + {"idea", {1, 3, 36, 3, 1, 2}, PARM_ABSENT, SYM_ENC, IDEA, H::None, C::IDEA, ECB, Pad::Std, &idea_aid}, + {"ideaCBC", {1, 3, 6, 1, 4, 1, 188, 7, 1, 1, 2}, PARM_OctetString, SYM_ENC, IDEA, H::None, C::IDEA, CBC, Pad::Pkcs, &ideaCBC_aid}, + {"rc2CBC", {1, 2, 840, 113549, 3, 2}, PARM_RC2CBC, SYM_ENC, RC2, H::None, C::RC2, CBC, Pad::Pkcs, &rc2CBC_aid}, + {"rc4", {1, 2, 840, 113549, 3, 4}, PARM_ABSENT, SYM_ENC, RC4, H::None, C::RC4, NoAlgMode, Pad::None, &rc4_aid}, + {"aes128ECB", {2, 16, 840, 1, 101, 3, 4, 1, 1}, PARM_ABSENT, SYM_ENC, AES128, H::None, C::AES, ECB, Pad::Pkcs, &aes128ECB_aid}, + {"aes128CBC", oids::aes128CBC, PARM_OctetString, SYM_ENC, AES128, H::None, C::AES, CBC, Pad::Pkcs, &aes128CBC_aid}, + {"aes192ECB", {2, 16, 840, 1, 101, 3, 4, 1, 21}, PARM_ABSENT, SYM_ENC, AES192, H::None, C::AES, ECB, Pad::Pkcs, &aes192ECB_aid}, + {"aes192CBC", oids::aes192CBC, PARM_OctetString, SYM_ENC, AES192, H::None, C::AES, CBC, Pad::Pkcs, &aes192CBC_aid}, + {"aes256ECB", {2, 16, 840, 1, 101, 3, 4, 1, 41}, PARM_ABSENT, SYM_ENC, AES256, H::None, C::AES, ECB, Pad::Pkcs, &aes256ECB_aid}, + {"aes256CBC", oids::aes256CBC, PARM_OctetString, SYM_ENC, AES256, H::None, C::AES, CBC, Pad::Pkcs, &aes256CBC_aid}, + + {"pbeWithSHA1And128BitRC4", oids::pbeSHA1RC4_128, PARM_PKCS5, PBE, RC4, H::SHA1, C::RC4, NoAlgMode, Pad::None, nullptr}, + {"pbeWithSHA1And40BitRC4", oids::pbeSHA1RC4_40, PARM_PKCS5, PBE, RC4, H::SHA1, C::RC4, NoAlgMode, Pad::None, nullptr}, + {"pbeWithSHA1AndDES3xCBC", oids::pbeSHA1DES3, PARM_PKCS5, PBE, DES3, H::SHA1, C::DES3, CBC, Pad::Pkcs, nullptr}, + {"pbeWithSHA1AndDES3CBC", oids::pbeSHA1DES2, PARM_PKCS5, PBE, DES3, H::SHA1, C::DES3, CBC, Pad::Pkcs, nullptr}, + {"pbeWithSHA1And128BitRC2CBC", oids::pbeSHA1RC2_128, PARM_PKCS5, PBE, RC2, H::SHA1, C::RC2, CBC, Pad::Pkcs, nullptr}, + {"pbeWithSHA1And40BitRC2CBC", oids::pbeSHA1RC2_40, PARM_PKCS5, PBE, RC2, H::SHA1, C::RC2, CBC, Pad::Pkcs, nullptr}, + }; + return table; +} + +struct PseObjectName { + const char *name; + ObjId *oid; +}; + +const PseObjectName kPseObjects[] = { + {SKnew_name, &SKnew_oid}, + {SKold_name, &SKold_oid}, + {SignSK_name, &SignSK_oid}, + {DecSKnew_name, &DecSKnew_oid}, + {DecSKold_name, &DecSKold_oid}, + {AuthSK_name, &AuthSK_oid}, + {Cert_name, &Cert_oid}, + {SignCert_name, &SignCert_oid}, + {EncCert_name, &EncCert_oid}, + {AuthCert_name, &AuthCert_oid}, + {PKRoot_name, &PKRoot_oid}, + {FCPath_name, &FCPath_oid}, + {SerialNumber_name, &SerialNumber_oid}, + {"Name", &Name_oid}, +}; + +} // namespace + +Oid oid_of(const ObjId *o) +{ + if (!o || o->oid_nelem <= 0 || !o->oid_elements) + return {}; + return Oid(o->oid_elements, o->oid_elements + o->oid_nelem); +} + +void fill_objid(ObjId *dst, const Oid &o) +{ + unsigned int *e = static_cast(mem_alloc(sizeof(unsigned int) * (o.size() ? o.size() : 1))); + for (size_t i = 0; i < o.size(); ++i) + e[i] = o[i]; + dst->oid_nelem = int(o.size()); + dst->oid_elements = e; +} + +ObjId *new_objid(const Oid &o) +{ + ObjId *r = mem_new(); + fill_objid(r, o); + return r; +} + +std::string oid_to_string(const Oid &o) +{ + std::string s; + for (size_t i = 0; i < o.size(); ++i) { + if (i) + s += '.'; + s += std::to_string(o[i]); + } + return s; +} + +bool oid_equal(const ObjId *a, const Oid &b) +{ + if (!a) + return false; + if (size_t(a->oid_nelem) != b.size()) + return false; + for (size_t i = 0; i < b.size(); ++i) + if (a->oid_elements[i] != b[i]) + return false; + return true; +} + +bool oid_equal(const ObjId *a, const ObjId *b) +{ + return a && b && oid_equal(a, oid_of(b)); +} + +const AlgInfo *alg_by_oid(const Oid &o) +{ + for (const auto &a : alg_table()) + if (a.oid == o) + return &a; + return nullptr; +} + +const AlgInfo *alg_by_oid(const ObjId *o) +{ + return o ? alg_by_oid(oid_of(o)) : nullptr; +} + +const AlgInfo *alg_by_name(const char *name) +{ + if (!name) + return nullptr; + for (const auto &a : alg_table()) + if (!strcasecmp(a.name, name)) + return &a; + return nullptr; +} + +std::string alg_display_name(const ObjId *o) +{ + const AlgInfo *a = alg_by_oid(o); + return a ? a->name : "unknown"; +} + +ObjId *pse_object_oid(const char *name) +{ + if (!name) + return nullptr; + for (const auto &p : kPseObjects) + if (!std::strcmp(p.name, name)) + return p.oid; + return nullptr; +} + +const char *pse_object_name(const ObjId *oid) +{ + for (const auto &p : kPseObjects) + if (oid_equal(oid, p.oid)) + return p.name; + return nullptr; +} + +ObjId *copy_objid(const ObjId *o) +{ + return o ? new_objid(oid_of(o)) : nullptr; +} + +void free_objid_content(ObjId *o) +{ + if (!o) + return; + mem_free(o->oid_elements); + o->oid_elements = nullptr; + o->oid_nelem = 0; +} + +} // namespace compat + +extern "C" { + +int aux_cmp_ObjId(ObjId *oid1, ObjId *oid2) +{ + if (!oid1 || !oid2) + return oid1 == oid2 ? 0 : 1; + return oid_equal(oid1, oid2) ? 0 : 1; +} + +ObjId *aux_cpy_ObjId(ObjId *oid) +{ + return guarded("aux_cpy_ObjId", nullptr, [&] { return copy_objid(oid); }); +} + +int aux_cpy2_ObjId(ObjId *dup_oid, ObjId *oid) +{ + return guarded("aux_cpy2_ObjId", -1, [&] { + if (!dup_oid || !oid) + fail(EINVALID, "missing object identifier"); + fill_objid(dup_oid, oid_of(oid)); + return 0; + }); +} + +void aux_free_ObjId(ObjId **oid) +{ + if (!oid || !*oid) + return; + if (!mem_owned(*oid)) { + *oid = nullptr; + return; + } + free_objid_content(*oid); + mem_free(*oid); + *oid = nullptr; +} + +AlgEnc aux_ObjId2AlgEnc(ObjId *given_objid) +{ + const AlgInfo *a = alg_by_oid(given_objid); + return a ? a->enc : NoAlgEnc; +} + +AlgId *aux_Name2AlgId(char *name) +{ + const AlgInfo *a = alg_by_name(name); + if (!a || !a->global) { + push_error(EUNKNOWNALGID, "aux_Name2AlgId", std::string("unknown algorithm name ") + (name ? name : "(null)")); + return nullptr; + } + return a->global; +} + +ObjId *af_get_objoid(char *objname) +{ + ObjId *o = pse_object_oid(objname); + if (!o) + push_error(EOBJNAME, "af_get_objoid", std::string("unknown PSE object ") + (objname ? objname : "(null)")); + return o; +} + +} // extern "C" diff --git a/port/secude/src/pkcs12.cpp b/port/secude/src/pkcs12.cpp new file mode 100644 index 000000000..6f29b5eb5 --- /dev/null +++ b/port/secude/src/pkcs12.cpp @@ -0,0 +1,498 @@ +// PKCS#12 (RFC 7292) encoding and decoding of P12_Safe structures and +// PKCS#8 PrivateKeyInfo creation. Supports the password-integrity mode with +// pkcs-12PbeIds (3DES, RC2, RC4) and PBES2 (PBKDF2 with AES/3DES). +#include "bn.hpp" +#include "der.hpp" +#include "symcipher.hpp" + +#include + +namespace compat { + +namespace { + +constexpr int kDefaultIterations = 2048; + +struct PbeInfo { + const Oid *oid; + Cipher cipher; + size_t keylen; + unsigned rc2_bits; +}; + +const PbeInfo kPbe[] = { + {&oids::pbeSHA1RC4_128, Cipher::RC4, 16, 0}, {&oids::pbeSHA1RC4_40, Cipher::RC4, 5, 0}, + {&oids::pbeSHA1DES3, Cipher::DES3, 24, 0}, {&oids::pbeSHA1DES2, Cipher::DES3, 16, 0}, + {&oids::pbeSHA1RC2_128, Cipher::RC2, 16, 128}, {&oids::pbeSHA1RC2_40, Cipher::RC2, 5, 40}, +}; + +const PbeInfo *pbe_by_oid(const Oid &o) +{ + for (const auto &p : kPbe) + if (*p.oid == o) + return &p; + return nullptr; +} + +struct HashChoice { + Hash hash; + const Oid *digest_oid; + const Oid *hmac_oid; + const char *name; + size_t block; +}; + +const HashChoice kHashes[] = { + {Hash::SHA1, &oids::sha1, &oids::hmacSHA1, "SHA1", 64}, + {Hash::SHA256, &oids::sha256, &oids::hmacSHA256, "SHA256", 64}, + {Hash::SHA384, &oids::sha384, &oids::hmacSHA384, "SHA384", 128}, + {Hash::SHA512, &oids::sha512, &oids::hmacSHA512, "SHA512", 128}, +}; + +const HashChoice &hash_by_digest_oid(const Oid &o) +{ + for (const auto &h : kHashes) + if (*h.digest_oid == o) + return h; + fail(EP12INTMODE, "unsupported PKCS#12 MAC algorithm " + oid_to_string(o)); +} + +const HashChoice &hash_by_hmac_oid(const Oid &o) +{ + for (const auto &h : kHashes) + if (*h.hmac_oid == o) + return h; + fail(EP12ENCALG, "unsupported PBKDF2 PRF " + oid_to_string(o)); +} + +// RFC 7292 appendix B.2 +Bytes p12_kdf(const HashChoice &h, const Bytes &password, const Bytes &salt, int iterations, uint8_t id, size_t n) +{ + size_t u = digest_size(h.hash), v = h.block; + auto stretch = [v](const Bytes &in) { + Bytes out; + if (in.empty()) + return out; + size_t len = v * ((in.size() + v - 1) / v); + for (size_t i = 0; i < len; ++i) + out.push_back(in[i % in.size()]); + return out; + }; + Bytes d(v, id), i = stretch(salt), p = stretch(password); + der::append(i, p); + Bytes out; + while (out.size() < n) { + Bytes a = d; + der::append(a, i); + a = digest(h.hash, a.data(), a.size()); + for (int r = 1; r < iterations; ++r) + a = digest(h.hash, a.data(), a.size()); + out.insert(out.end(), a.begin(), a.begin() + long(std::min(u, n - out.size()))); + Bytes b; + while (b.size() < v) + der::append(b, a); + b.resize(v); + for (size_t off = 0; off < i.size(); off += v) { + unsigned carry = 1; + for (size_t k = v; k-- > 0;) { + carry += unsigned(i[off + k]) + b[k]; + i[off + k] = uint8_t(carry); + carry >>= 8; + } + } + } + return out; +} + +Bytes hmac(const HashChoice &h, const Bytes &key, const Bytes &data) +{ + Bytes out(EVP_MAX_MD_SIZE); + size_t len = 0; + if (!EVP_Q_mac(libctx(), "HMAC", nullptr, h.name, nullptr, key.data(), key.size(), data.data(), data.size(), + out.data(), out.size(), &len)) + fail(EINTERNAL, "HMAC failed"); + out.resize(len); + return out; +} + +// BMPString password (as produced by aux_latin1_to_unicode) as Latin-1 text for PBKDF2. +std::string password_text(const Bytes &bmp) +{ + std::string s; + for (size_t i = 0; i + 1 < bmp.size(); i += 2) { + unsigned c = (unsigned(bmp[i]) << 8) | bmp[i + 1]; + if (!c) + break; + s += char(c < 0x100 ? c : '?'); + } + return s; +} + +Bytes utf8(const std::string &latin1) +{ + Bytes t = der::string(der::UTF8String, latin1); + return der::parse_one(t).value(); +} + +Bytes pbe_crypt(const Bytes &algid_der, const Bytes &password, const uint8_t *in, size_t n, bool encrypt) +{ + der::Reader r(der::parse_one(algid_der)); + Oid oid = der::get_oid(r.next()); + der::Node params = r.next(der::SEQUENCE); + CipherSpec s; + if (const PbeInfo *p = pbe_by_oid(oid)) { + der::Reader pr(params); + Bytes salt = pr.next(der::OCTET_STRING).value(); + int iter = int(der::get_integer(pr.next(der::INTEGER))); + const HashChoice &h = kHashes[0]; + s.cipher = p->cipher; + s.key = p12_kdf(h, password, salt, iter, 1, p->keylen); + if (p->cipher != Cipher::RC4) { + s.mode = CBC; + s.pad = Pad::Pkcs; + s.iv = p12_kdf(h, password, salt, iter, 2, 8); + } + s.rc2_bits = p->rc2_bits; + } else if (oid == oids::pbes2) { + der::Reader pr(params); + der::Reader kdf(pr.next(der::SEQUENCE)); + der::Reader enc(pr.next(der::SEQUENCE)); + if (der::get_oid(kdf.next()) != oids::pbkdf2) + fail(EP12ENCALG, "unsupported PBES2 key derivation"); + der::Reader kp(kdf.next(der::SEQUENCE)); + Bytes salt = kp.next(der::OCTET_STRING).value(); + int iter = int(der::get_integer(kp.next(der::INTEGER))); + der::Node node; + long long keylen = 0; + if (kp.next_optional(der::INTEGER, node)) + keylen = der::get_integer(node); + const HashChoice *prf = &kHashes[0]; + if (kp.next_optional(der::SEQUENCE, node)) + prf = &hash_by_hmac_oid(der::get_oid(der::Reader(node).next())); + Oid eoid = der::get_oid(enc.next()); + s.iv = enc.next(der::OCTET_STRING).value(); + s.mode = CBC; + s.pad = Pad::Pkcs; + size_t klen; + if (eoid == oids::aes128CBC || eoid == oids::aes192CBC || eoid == oids::aes256CBC) { + s.cipher = Cipher::AES; + klen = eoid == oids::aes128CBC ? 16 : eoid == oids::aes192CBC ? 24 : 32; + } else if (eoid == oids::desEDE3CBC) { + s.cipher = Cipher::DES3; + klen = 24; + } else { + fail(EP12ENCALG, "unsupported PBES2 cipher " + oid_to_string(eoid)); + } + if (keylen && size_t(keylen) != klen) + fail(EP12ENCALG, "inconsistent PBES2 key length"); + std::string pw = password_text(password); + Bytes pw8 = utf8(pw); + EVP_MD *md = fetch_md(prf->name); + s.key.assign(klen, 0); + bool ok = md && PKCS5_PBKDF2_HMAC(reinterpret_cast(pw8.data()), int(pw8.size()), salt.data(), + int(salt.size()), iter, md, int(klen), s.key.data()); + EVP_MD_free(md); + if (!ok) + fail(EINTERNAL, "PBKDF2 failed"); + } else { + fail(EP12ENCALG, "unsupported PKCS#12 encryption algorithm " + oid_to_string(oid)); + } + return encrypt ? cipher_encrypt(s, in, n) : cipher_decrypt(s, in, n); +} + +Bytes pbe_algid(const Oid &oid, int iterations) +{ + if (!pbe_by_oid(oid)) + fail(EP12ENCALG, "unsupported PKCS#12 encryption algorithm " + oid_to_string(oid)); + return der::seq({der::oid(oid), der::seq({der::octet_string(random_bytes(8)), der::integer(iterations)})}); +} + +Bytes attributes(const P12_Bag *b) +{ + std::vector attrs; + if (b->friendlyName && b->friendlyName->noctets) + attrs.push_back(der::seq({der::oid(oids::friendlyName), + der::set_of({der::tlv(der::BMPString, bytes_of(b->friendlyName))})})); + if (b->localKeyID && b->localKeyID->noctets) + attrs.push_back(der::seq({der::oid(oids::localKeyId), der::set_of({der::octet_string(bytes_of(b->localKeyID))})})); + if (attrs.empty()) + return {}; + return der::set_of(attrs); +} + +Bytes safe_bag(const Oid &type, const Bytes &value, const P12_Bag *b) +{ + Bytes body = der::concat({der::oid(type), der::tlv(der::ctx(0), value)}); + der::append(body, attributes(b)); + return der::tlv(der::SEQUENCE, body); +} + +Bytes content_info_data(const Bytes &content) +{ + return der::seq({der::oid(oids::data), der::tlv(der::ctx(0), der::octet_string(content))}); +} + +Bytes content_info_encrypted(const Bytes &content, const Oid &alg, int iterations, const Bytes &password) +{ + Bytes algid = pbe_algid(alg, iterations); + Bytes enc = pbe_crypt(algid, password, content.data(), content.size(), true); + Bytes eci = der::seq({der::oid(oids::data), algid, der::tlv(der::ctx(0, false), enc)}); + return der::seq({der::oid(oids::encryptedData), der::tlv(der::ctx(0), der::seq({der::integer(0), eci}))}); +} + +Bytes octet_content(const der::Node &n) +{ + if (n.tag == der::OCTET_STRING) + return n.value(); + if (n.tag == (der::OCTET_STRING | 0x20)) { + Bytes out; + der::Reader r(n); + while (!r.empty()) + der::append(out, octet_content(r.next())); + return out; + } + fail(EDECODE, "OCTET STRING expected"); +} + +void append_bag(P12_Safe *safe, P12_Bag *bag) +{ + P12_Bag **tail = &safe->bags; + while (*tail) + tail = &(*tail)->next; + *tail = bag; +} + +void parse_attributes(const der::Node &set, P12_Bag *bag) +{ + der::Reader r(set); + while (!r.empty()) { + der::Reader a(r.next(der::SEQUENCE)); + Oid id = der::get_oid(a.next()); + der::Reader values(a.next(der::SET)); + if (values.empty()) + continue; + der::Node v = values.next(); + if (id == oids::friendlyName && v.tag == der::BMPString && !bag->friendlyName) + bag->friendlyName = new_ostr(v.value()); + else if (id == oids::localKeyId && v.tag == der::OCTET_STRING && !bag->localKeyID) + bag->localKeyID = new_ostr(v.value()); + } +} + +void keep_lowest(P12_Safe *safe, int iterations) +{ + if (!safe->iterationCount || iterations < safe->iterationCount) + safe->iterationCount = iterations; +} + +int iterations_of(const Bytes &algid_der) +{ + der::Reader r(der::parse_one(algid_der)); + Oid oid = der::get_oid(r.next()); + der::Reader p(r.next(der::SEQUENCE)); + if (oid == oids::pbes2) { + der::Reader kdf(p.next(der::SEQUENCE)); + kdf.next(); + der::Reader kp(kdf.next(der::SEQUENCE)); + kp.next(); + return int(der::get_integer(kp.next(der::INTEGER))); + } + p.next(); + return int(der::get_integer(p.next(der::INTEGER))); +} + +void parse_safe_contents(const Bytes &der_bytes, const Bytes &password, P12_Safe *safe, bool encrypted) +{ + der::Reader bags(der::parse_one(der_bytes)); + while (!bags.empty()) { + der::Reader b(bags.next(der::SEQUENCE)); + Oid type = der::get_oid(b.next()); + der::Node value = der::Reader(b.next(der::ctx(0))).next(); + der::Node attrs; + bool have_attrs = b.next_optional(der::SET, attrs); + P12_Bag *bag = nullptr; + if (type == oids::keyBag || type == oids::pkcs8ShroudedKeyBag) { + Bytes pkcs8 = value.raw(); + if (type == oids::pkcs8ShroudedKeyBag) { + der::Reader e(value); + Bytes algid = e.next(der::SEQUENCE).raw(); + Bytes enc = e.next(der::OCTET_STRING).value(); + pkcs8 = pbe_crypt(algid, password, enc.data(), enc.size(), false); + if (!safe->alg_oid.espvk) + safe->alg_oid.espvk = new_objid(der::get_oid(der::Reader(der::parse_one(algid)).next())); + keep_lowest(safe, iterations_of(algid)); + } + bag = mem_new(); + bag->type = P12_bc_key; + bag->int_encryption = type == oids::pkcs8ShroudedKeyBag; + bag->content.key = dec_privatekeyinfo(pkcs8); + } else if (type == oids::certBag) { + der::Reader c(value); + if (der::get_oid(c.next()) != oids::x509Certificate) + continue; + Bytes cert = octet_content(der::Reader(c.next(der::ctx(0))).next()); + bag = mem_new(); + bag->type = P12_bc_cert; + bag->int_encryption = encrypted; + bag->content.cert = dec_certificate(cert); + } else { + continue; + } + append_bag(safe, bag); + if (have_attrs) + parse_attributes(attrs, bag); + } +} + +} // namespace + +} // namespace compat + +using namespace compat; + +extern "C" { + +int pkcs12_encode(P12_Safe *safe, OctetString *password, OctetString *asn1_out) +{ + return guarded("pkcs12_encode", -1, [&] { + if (!safe || !password || !asn1_out) + fail(EINVALID, "missing parameter"); + Bytes pw = bytes_of(password); + int iterations = safe->iterationCount > 0 ? safe->iterationCount : kDefaultIterations; + Oid enc_alg = safe->alg_oid.enc ? oid_of(safe->alg_oid.enc) : oids::pbeSHA1RC2_40; + Oid key_alg = safe->alg_oid.espvk ? oid_of(safe->alg_oid.espvk) : oids::pbeSHA1DES3; + Bytes certs_encrypted, certs_plain, keys; + for (const P12_Bag *b = safe->bags; b; b = b->next) { + if (b->type == P12_bc_cert && b->content.cert) { + Bytes cert = enc_certificate(b->content.cert); + Bytes bag = safe_bag(oids::certBag, + der::seq({der::oid(oids::x509Certificate), der::tlv(der::ctx(0), der::octet_string(cert))}), b); + der::append(b->int_encryption ? certs_encrypted : certs_plain, bag); + } else if (b->type == P12_bc_key && b->content.key) { + Bytes pkcs8 = enc_privatekeyinfo(b->content.key); + if (b->int_encryption) { + Bytes algid = pbe_algid(key_alg, iterations); + Bytes enc = pbe_crypt(algid, pw, pkcs8.data(), pkcs8.size(), true); + der::append(keys, safe_bag(oids::pkcs8ShroudedKeyBag, der::seq({algid, der::octet_string(enc)}), b)); + } else { + der::append(keys, safe_bag(oids::keyBag, pkcs8, b)); + } + } + } + Bytes auth; + if (!certs_encrypted.empty()) + der::append(auth, content_info_encrypted(der::tlv(der::SEQUENCE, certs_encrypted), enc_alg, iterations, pw)); + if (!certs_plain.empty()) + der::append(auth, content_info_data(der::tlv(der::SEQUENCE, certs_plain))); + if (!keys.empty()) + der::append(auth, content_info_data(der::tlv(der::SEQUENCE, keys))); + Bytes auth_safe = der::tlv(der::SEQUENCE, auth); + + const HashChoice &h = hash_by_digest_oid(safe->alg_oid.mac ? oid_of(safe->alg_oid.mac) : oids::sha1); + Bytes salt = random_bytes(8); + Bytes mac = hmac(h, p12_kdf(h, pw, salt, iterations, 3, digest_size(h.hash)), auth_safe); + Bytes mac_data = der::seq({der::seq({der::seq({der::oid(*h.digest_oid), der::null()}), der::octet_string(mac)}), + der::octet_string(salt), der::integer(iterations)}); + fill_ostr(asn1_out, der::seq({der::integer(3), content_info_data(auth_safe), mac_data})); + return 0; + }); +} + +int pkcs12_decode(OctetString *asn1string, OctetString *password, P12_Safe *safe, Boolean *verified) +{ + return guarded("pkcs12_decode", -1, [&] { + if (!asn1string || !password || !safe) + fail(EINVALID, "missing parameter"); + Bytes pw = bytes_of(password); + Bytes in = bytes_of(asn1string); + der::Reader pfx(der::parse_one(in)); + if (der::get_integer(pfx.next(der::INTEGER)) != 3) + fail(EDECODE, "unsupported PFX version"); + der::Reader ci(pfx.next(der::SEQUENCE)); + if (der::get_oid(ci.next()) != oids::data) + fail(EP12INTMODE, "only password integrity mode is supported"); + Bytes auth_safe = octet_content(der::Reader(ci.next(der::ctx(0))).next()); + + if (verified) + *verified = FALSE; + der::Node mac_node; + if (pfx.next_optional(der::SEQUENCE, mac_node)) { + der::Reader md(mac_node); + der::Reader di(md.next(der::SEQUENCE)); + Oid digest_oid = der::get_oid(der::Reader(di.next(der::SEQUENCE)).next()); + Bytes expected = di.next(der::OCTET_STRING).value(); + Bytes salt = md.next(der::OCTET_STRING).value(); + der::Node it; + int iterations = md.next_optional(der::INTEGER, it) ? int(der::get_integer(it)) : 1; + const HashChoice &h = hash_by_digest_oid(digest_oid); + Bytes mac = hmac(h, p12_kdf(h, pw, salt, iterations, 3, digest_size(h.hash)), auth_safe); + if (mac != expected) + fail(EVERIFY, "PKCS#12 integrity check failed (wrong password?)"); + if (verified) + *verified = TRUE; + safe->alg_oid.mac = new_objid(digest_oid); + keep_lowest(safe, iterations); + } + + safe->version = 3; + der::Reader cis(der::parse_one(auth_safe)); + while (!cis.empty()) { + der::Reader c(cis.next(der::SEQUENCE)); + Oid type = der::get_oid(c.next()); + der::Node content = der::Reader(c.next(der::ctx(0))).next(); + if (type == oids::data) { + parse_safe_contents(octet_content(content), pw, safe, false); + } else if (type == oids::encryptedData) { + der::Reader ed(content); + ed.next(der::INTEGER); + der::Reader eci(ed.next(der::SEQUENCE)); + eci.next(); + Bytes algid = eci.next(der::SEQUENCE).raw(); + der::Node enc = eci.next(); + Bytes ct = enc.tag == der::ctx(0, false) ? enc.value() : octet_content(der::Reader(enc).next()); + Bytes plain = pbe_crypt(algid, pw, ct.data(), ct.size(), false); + if (!safe->alg_oid.enc) + safe->alg_oid.enc = new_objid(der::get_oid(der::Reader(der::parse_one(algid)).next())); + keep_lowest(safe, iterations_of(algid)); + parse_safe_contents(plain, pw, safe, true); + } + } + return 0; + }); +} + +PrivateKeyInfo *aux_create_PrivateKeyInfo(KeyInfo *key, Certificate *cert, SET_OF_Attr *attributes) +{ + return guarded("aux_create_PrivateKeyInfo", nullptr, [&] { + if (!key) + fail(EINVALID, "missing private key"); + if (attributes) + fail(ENOTSUPPORTED, "PKCS#8 attributes are not supported"); + PrivateKeyInfo *p = mem_new(); + try { + if (keyinfo_is_rsa(key)) { + Bytes e; + if (cert && cert->tbs && keyinfo_is_rsa(cert->tbs->subjectPK)) + e = rsa_public_from_keyinfo(cert->tbs->subjectPK).e; + RsaPrivate k = rsa_private_from_keyinfo(key, e); + p->privateKeyAlgorithm = copy_algid(&rsaEncryption_aid); + p->privateKey = new_ostr(rsa_private_key_der(k)); + } else if (keyinfo_is_dsa(key)) { + if (!key->subjectAI->param) + fail(EALGNOTFITKEY, "DSA key without domain parameters"); + p->privateKeyAlgorithm = mem_new(); + p->privateKeyAlgorithm->objid = new_objid(oids::id_dsa); + p->privateKeyAlgorithm->param = copy_keybits(static_cast(key->subjectAI->param)); + p->privateKey = new_ostr(bytes_of(&key->subjectkey)); + } else { + fail(EALGNOTFITKEY, "only RSA and DSA keys can be exported"); + } + return p; + } catch (...) { + free_privatekeyinfo(p); + throw; + } + }); +} + +} // extern "C" diff --git a/port/secude/src/provider.cpp b/port/secude/src/provider.cpp new file mode 100644 index 000000000..089ec3404 --- /dev/null +++ b/port/secude/src/provider.cpp @@ -0,0 +1,65 @@ +// Private OpenSSL library context with the default provider and, if present, +// the legacy provider (MD4, DES, IDEA, RC2, RC4). The application's default +// context is left untouched. Setting SECUDE_COMPAT_NO_LEGACY_PROVIDER forces +// the built-in implementations. +#include "internal.hpp" + +#include +#include + +namespace compat { + +namespace { + +struct Providers { + OSSL_LIB_CTX *ctx = nullptr; + bool legacy = false; + + Providers() + { + ctx = OSSL_LIB_CTX_new(); + if (!ctx) + return; + ERR_set_mark(); + OSSL_PROVIDER_load(ctx, "default"); + const char *off = std::getenv("SECUDE_COMPAT_NO_LEGACY_PROVIDER"); + if (!(off && *off && std::strcmp(off, "0"))) + legacy = OSSL_PROVIDER_load(ctx, "legacy") != nullptr; + ERR_pop_to_mark(); + } +}; + +Providers &providers() +{ + static Providers *p = new Providers; + return *p; +} + +} // namespace + +OSSL_LIB_CTX *libctx() { return providers().ctx; } + +bool legacy_enabled() { return providers().legacy; } + +EVP_MD *fetch_md(const char *name) +{ + ERR_set_mark(); + EVP_MD *md = EVP_MD_fetch(libctx(), name, nullptr); + ERR_pop_to_mark(); + return md; +} + +EVP_CIPHER *fetch_cipher(const char *name) +{ + ERR_set_mark(); + EVP_CIPHER *c = EVP_CIPHER_fetch(libctx(), name, nullptr); + ERR_pop_to_mark(); + return c; +} + +} // namespace compat + +extern "C" int secude_compat_legacy_provider_active(void) +{ + return compat::legacy_enabled() ? 1 : 0; +} diff --git a/port/secude/src/pse.cpp b/port/secude/src/pse.cpp new file mode 100644 index 000000000..ee5d7b738 --- /dev/null +++ b/port/secude/src/pse.cpp @@ -0,0 +1,743 @@ +// Personal Security Environment: a PIN protected key store file. +// +// CompatPSE ::= SEQUENCE { +// format UTF8String "SECUDE-compat PSE", +// version INTEGER (1), +// kdf SEQUENCE { pbkdf2 OID, salt OCTET STRING, iterations INTEGER, hmacWithSHA256 OID }, +// cipher SEQUENCE { aes256-GCM OID, nonce OCTET STRING }, +// ciphertext OCTET STRING } -- AES-256-GCM(PSEContents) || tag, AAD = DER of the fields above +// PSEContents ::= SEQUENCE { flags INTEGER, created Time, objects SEQUENCE OF PSEObject } +// PSEObject ::= SEQUENCE { name UTF8String, type OID, created Time, updated Time, value OCTET STRING } +#include "der.hpp" + +#include +#include + +namespace compat { + +namespace { + +const char kFormat[] = "SECUDE-compat PSE"; +const Oid kAesGcm{2, 16, 840, 1, 101, 3, 4, 1, 46}; +constexpr int kIterations = 100000; + +} // namespace + +struct PseStore { + std::string path; + bool onekeypaironly = true; + std::string created; + std::vector objects; + Bytes salt; + int iterations = kIterations; + Bytes key; + + void derive(const char *pin) + { + key.assign(32, 0); + const char *p = pin ? pin : ""; + if (!PKCS5_PBKDF2_HMAC(p, int(std::strlen(p)), salt.data(), int(salt.size()), iterations, EVP_sha256(), + int(key.size()), key.data())) + fail(EINTERNAL, "key derivation failed"); + } + + Bytes header() const + { + return der::concat({der::string(der::UTF8String, kFormat), der::integer(1), + der::seq({der::oid(oids::pbkdf2), der::octet_string(salt), der::integer(iterations), + der::oid(oids::hmacSHA256)})}); + } + + Bytes contents() const + { + Bytes objs; + for (const auto &o : objects) + der::append(objs, der::seq({der::string(der::UTF8String, o.name), der::oid(o.type), der::time(o.created), + der::time(o.updated), der::octet_string(o.value)})); + return der::seq({der::integer(onekeypaironly ? 1 : 0), der::time(created), der::tlv(der::SEQUENCE, objs)}); + } + + void save() const + { + Bytes plain = contents(); + Bytes nonce = random_bytes(12); + Bytes head = header(); + Bytes cipher_params = der::seq({der::oid(kAesGcm), der::octet_string(nonce)}); + Bytes aad = der::concat({head, cipher_params}); + Bytes ct = aes_gcm(true, nonce, aad, plain); + write_file_atomic(path, der::tlv(der::SEQUENCE, der::concat({aad, der::octet_string(ct)}))); + } + + void load(const char *pin) + { + bool exists = false; + Bytes file = read_file(path, exists); + if (!exists) + fail(EPSENOTEXISTING, "PSE " + path + " does not exist"); + Bytes nonce, ct, aad; + try { + der::Node top = der::parse_one(file); + if (top.tag != der::SEQUENCE) + fail(EPSEFILE); + der::Reader r(top); + der::Node fmt = r.next(der::UTF8String); + if (der::get_string(fmt) != kFormat) + fail(EPSEFILE); + der::Node ver = r.next(der::INTEGER); + if (der::get_integer(ver) != 1) + fail(EPSEFILESTRUCTURE, "unsupported PSE version"); + der::Node kdf = r.next(der::SEQUENCE); + der::Reader kr(kdf); + if (der::get_oid(kr.next()) != oids::pbkdf2) + fail(EPSEFILESTRUCTURE, "unsupported key derivation"); + salt = kr.next(der::OCTET_STRING).value(); + iterations = int(der::get_integer(kr.next(der::INTEGER))); + if (der::get_oid(kr.next()) != oids::hmacSHA256 || iterations < 1) + fail(EPSEFILESTRUCTURE, "unsupported key derivation"); + der::Node cp = r.next(der::SEQUENCE); + der::Reader cr(cp); + if (der::get_oid(cr.next()) != kAesGcm) + fail(EPSEFILESTRUCTURE, "unsupported PSE encryption"); + nonce = cr.next(der::OCTET_STRING).value(); + ct = r.next(der::OCTET_STRING).value(); + r.expect_end(); + aad.assign(fmt.hdr, cp.hdr + cp.total); + } catch (const Error &e) { + if (e.code == EDECODE) + fail(EPSEFILE, path + " is not a PSE of this implementation"); + throw; + } + derive(pin); + Bytes plain = aes_gcm(false, nonce, aad, ct); + der::Node c = der::parse_one(plain); + der::Reader r(c); + onekeypaironly = der::get_integer(r.next(der::INTEGER)) & 1; + created = der::get_time(r.next()); + der::Reader objs(r.next(der::SEQUENCE)); + objects.clear(); + while (!objs.empty()) { + der::Reader o(objs.next(der::SEQUENCE)); + PseObject po; + po.name = der::get_string(o.next(der::UTF8String)); + po.type = der::get_oid(o.next()); + po.created = der::get_time(o.next()); + po.updated = der::get_time(o.next()); + po.value = o.next(der::OCTET_STRING).value(); + objects.push_back(std::move(po)); + } + } + +private: + Bytes aes_gcm(bool encrypt, const Bytes &nonce, const Bytes &aad, const Bytes &in) const + { + EVP_CIPHER *cipher = fetch_cipher("AES-256-GCM"); + if (!cipher) + fail(EINTERNAL, "AES-256-GCM not available"); + EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); + Bytes out(in.size() + 16); + int len = 0, fin = 0; + bool ok = ctx && EVP_CipherInit_ex2(ctx, cipher, nullptr, nullptr, encrypt, nullptr); + size_t data_len = in.size(); + if (!encrypt) { + if (in.size() < 16) + ok = false; + else + data_len -= 16; + } + if (ok) + ok = EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, int(nonce.size()), nullptr) && + EVP_CipherInit_ex2(ctx, nullptr, key.data(), nonce.data(), encrypt, nullptr) && + EVP_CipherUpdate(ctx, nullptr, &len, aad.data(), int(aad.size())) && + EVP_CipherUpdate(ctx, out.data(), &len, in.data(), int(data_len)); + if (ok && !encrypt) + ok = EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, 16, const_cast(in.data() + data_len)); + if (ok) + ok = EVP_CipherFinal_ex(ctx, out.data() + len, &fin); + if (ok && encrypt) + ok = EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, 16, out.data() + len + fin); + EVP_CIPHER_CTX_free(ctx); + EVP_CIPHER_free(cipher); + if (!ok) { + if (!encrypt) + fail(EPIN, "wrong PIN or damaged PSE"); + fail(EINTERNAL, "PSE encryption failed"); + } + out.resize(size_t(len + fin) + (encrypt ? 16 : 0)); + return out; + } +}; + +namespace { + +struct HandleData { + std::shared_ptr store; +}; + +struct SelData { + std::shared_ptr store; + PSE owner = nullptr; // nullptr: opened by sec_open/sec_create +}; + +struct Registry { + std::mutex lock; + std::map handles; + std::map sels; +}; + +Registry ®() +{ + static Registry *r = new Registry; + return *r; +} + +std::shared_ptr open_store(const char *path, const char *pin) +{ + if (!path || !*path) + fail(EINVALID, "missing PSE name"); + auto s = std::make_shared(); + s->path = path; + s->load(pin); + return s; +} + +std::shared_ptr create_store(const char *path, const char *pin, bool onekeypaironly) +{ + if (!path || !*path) + fail(EINVALID, "missing PSE name"); + bool exists = false; + read_file(path, exists); + if (exists) + fail(EPSEALREADYEXISTING, std::string("PSE ") + path + " already exists"); + auto s = std::make_shared(); + s->path = path; + s->onekeypaironly = onekeypaironly; + s->created = utc_now(); + s->salt = random_bytes(16); + s->derive(pin); + s->save(); + return s; +} + +PSE new_handle(const std::shared_ptr &store, const char *psename, const char *cadir) +{ + AF_ctx *ctx = mem_new(); + AF_options defaults = AF_DEF_OPTIONS; + ctx->options = defaults; + ctx->onekeypaironly = store->onekeypaironly; + ctx->pse_name = mem_strdup(psename); + ctx->ca_dir = mem_strdup(cadir); + Registry &r = reg(); + std::lock_guard g(r.lock); + r.handles[ctx].store = store; + return ctx; +} + +PSESel *new_sel(const std::shared_ptr &store, PSE owner) +{ + PSESel *sel = mem_new(); + Registry &r = reg(); + std::lock_guard g(r.lock); + r.sels[sel] = SelData{store, owner}; + return sel; +} + +void destroy_sel(PSESel *sel) +{ + mem_free(sel->object); + aux_free_ObjId(&sel->object_type); + mem_free(sel); +} + +bool is_static_objid(const ObjId *o) +{ + return o && !mem_owned(o); +} + +std::string object_name_for(const char *name, const ObjId *type) +{ + if (name && *name) + return name; + if (const char *n = pse_object_name(type)) + return n; + fail(EOBJNAME, "missing PSE object name"); +} + +Oid object_type_for(const std::string &name, const ObjId *type) +{ + if (type && type->oid_nelem > 0) + return oid_of(type); + if (const ObjId *o = pse_object_oid(name.c_str())) + return oid_of(o); + return oid_of(&Uid_oid); +} + +bool type_in(const Oid &t, std::initializer_list list) +{ + for (const ObjId *o : list) + if (t == oid_of(o)) + return true; + return false; +} + +bool is_cert_type(const Oid &t) { return type_in(t, {&Cert_oid, &SignCert_oid, &EncCert_oid, &AuthCert_oid}); } + +bool is_key_type(const Oid &t) +{ + return type_in(t, {&SKnew_oid, &SKold_oid, &SignSK_oid, &DecSKnew_oid, &DecSKold_oid, &AuthSK_oid}); +} + +void *decode_object(const PseObject &o) +{ + if (is_cert_type(o.type)) + return dec_certificate(o.value); + if (is_key_type(o.type)) + return dec_keyinfo(o.value); + if (o.type == oid_of(&PKRoot_oid)) + return dec_pkroot(o.value); + if (o.type == oid_of(&FCPath_oid)) + return dec_fcpath(o.value); + if (o.type == oid_of(&Name_oid)) + return dec_dname(o.value); + return new_ostr(o.value); +} + +Bytes encode_object(const Oid &type, void *opaque) +{ + if (!opaque) + fail(EINVALID, "missing PSE object value"); + if (is_cert_type(type)) + return enc_certificate(static_cast(opaque)); + if (is_key_type(type)) + return enc_keyinfo(static_cast(opaque)); + if (type == oid_of(&PKRoot_oid)) + return enc_pkroot(static_cast(opaque)); + if (type == oid_of(&FCPath_oid)) + return enc_fcpath(static_cast(opaque)); + if (type == oid_of(&Name_oid)) + return enc_dname(static_cast(opaque)); + return bytes_of(static_cast(opaque)); +} + +const PseObject &require(const std::shared_ptr &s, const std::string &name) +{ + const PseObject *o = store_find(s, name); + if (!o) + fail(EPSEOBJECTNOTEXISTING, "PSE object " + name + " does not exist"); + return *o; +} + +Bytes toc_der(const PseStore &s) +{ + Bytes objs; + for (const auto &o : s.objects) + der::append(objs, der::seq({der::string(der::UTF8String, o.name), der::time(o.created), der::time(o.updated), + der::integer(long(o.value.size())), der::integer(0), der::oid(o.type)})); + std::string update = s.created; + for (const auto &o : s.objects) + if (o.updated > update) + update = o.updated; + return der::seq({der::string(der::UTF8String, s.path), der::time(s.created), der::time(update), der::integer(0), + der::tlv(der::SEQUENCE, objs)}); +} + +} // namespace + +std::shared_ptr store_of(PSE pse) +{ + Registry &r = reg(); + std::lock_guard g(r.lock); + auto it = r.handles.find(pse); + if (it == r.handles.end()) + fail(EINVALID, "invalid PSE handle"); + return it->second.store; +} + +std::shared_ptr store_of(PSESel *sel) +{ + Registry &r = reg(); + std::lock_guard g(r.lock); + auto it = r.sels.find(sel); + if (it == r.sels.end()) + fail(EINVALID, "invalid PSE selector"); + return it->second.store; +} + +const PseObject *store_find(const std::shared_ptr &s, const std::string &name) +{ + for (const auto &o : s->objects) + if (o.name == name) + return &o; + return nullptr; +} + +void store_put(const std::shared_ptr &s, const std::string &name, const Oid &type, const Bytes &value) +{ + std::string now = utc_now(); + for (auto &o : s->objects) + if (o.name == name) { + o.type = type; + o.value = value; + o.updated = now; + s->save(); + return; + } + s->objects.push_back(PseObject{name, type, now, now, value}); + s->save(); +} + +bool store_onekeypaironly(const std::shared_ptr &s) { return s->onekeypaironly; } + +const char *pse_cadir(PSE pse) +{ + store_of(pse); + return pse->ca_dir; +} + +static std::string pick(const std::shared_ptr &s, const char *preferred, const char *fallback) +{ + if (!s->onekeypaironly && store_find(s, preferred)) + return preferred; + return fallback; +} + +std::string private_key_object(PSE pse, KeyType type) +{ + auto s = store_of(pse); + return pick(s, type == SIGNATURE ? SignSK_name : DecSKnew_name, SKnew_name); +} + +std::string certificate_object(PSE pse, KeyType type) +{ + auto s = store_of(pse); + return pick(s, type == SIGNATURE ? SignCert_name : EncCert_name, Cert_name); +} + +KeyInfo *read_private_key(PSE pse, KeyType type) +{ + auto s = store_of(pse); + return dec_keyinfo(require(s, private_key_object(pse, type)).value); +} + +Bytes rsa_exponent_hint(PSE pse, const KeyInfo *priv) +{ + if (!keyinfo_is_rsa(priv)) + return {}; + auto s = store_of(pse); + for (const char *name : {Cert_name, SignCert_name, EncCert_name}) { + const PseObject *o = store_find(s, name); + if (!o) + continue; + try { + Certificate *c = dec_certificate(o->value); + Bytes e; + try { + RsaPublic pub = rsa_public_from_keyinfo(c->tbs->subjectPK); + RsaPrivate k = rsa_private_from_keyinfo(priv, pub.e); + if (k.n == pub.n) + e = pub.e; + } catch (const Error &) { + } + free_certificate(c); + if (!e.empty()) + return e; + } catch (const Error &) { + } + } + return {}; +} + +} // namespace compat + +using namespace compat; + +extern "C" { + +PSE af_create(char *psename, char *cadir, char *pin, SC_DATA *sc_data, Boolean onekeypaironly) +{ + return guarded("af_create", nullptr, [&] { + return new_handle(create_store(psename, pin, onekeypaironly != 0), psename, cadir); + }); +} + +PSE af_open(char *psename, char *cadir, char *pin, SC_DATA *sc_data) +{ + return guarded("af_open", nullptr, [&] { return new_handle(open_store(psename, pin), psename, cadir); }); +} + +RC af_close(PSE pse_handle) +{ + return guarded("af_close", -1, [&] { + Registry &r = reg(); + std::vector sels; + { + std::lock_guard g(r.lock); + if (!r.handles.erase(pse_handle)) + fail(EINVALID, "invalid PSE handle"); + for (auto it = r.sels.begin(); it != r.sels.end();) { + if (it->second.owner == pse_handle) { + sels.push_back(it->first); + it = r.sels.erase(it); + } else { + ++it; + } + } + } + for (PSESel *s : sels) + destroy_sel(s); + mem_free(pse_handle->pse_name); + mem_free(pse_handle->ca_dir); + mem_free(pse_handle); + return 0; + }); +} + +PSESel *af_get_PSESel(PSE pse_hdl, ObjId *af_object) +{ + return guarded("af_get_PSESel", nullptr, [&] { + PSESel *sel = new_sel(store_of(pse_hdl), pse_hdl); + if (const char *name = pse_object_name(af_object)) { + sel->object = mem_strdup(name); + sel->object_type = copy_objid(af_object); + } + return sel; + }); +} + +RC af_get_options(PSE pse, AF_options *options) +{ + return guarded("af_get_options", -1, [&] { + store_of(pse); + if (!options) + fail(EINVALID, "missing options"); + *options = pse->options; + return 0; + }); +} + +PSESel *sec_create(char *psename, char *pin, SC_DATA *sc_data, Boolean onekeypaironly) +{ + return guarded("sec_create", nullptr, [&] { return new_sel(create_store(psename, pin, onekeypaironly != 0), nullptr); }); +} + +PSESel *sec_open(char *pse_name, char *pin, SC_DATA *sc_data) +{ + return guarded("sec_open", nullptr, [&] { return new_sel(open_store(pse_name, pin), nullptr); }); +} + +static RC release_sel(PSESel *pse_sel) +{ + Registry &r = reg(); + { + std::lock_guard g(r.lock); + if (!r.sels.erase(pse_sel)) + return -1; + } + destroy_sel(pse_sel); + return 0; +} + +RC sec_close(PSESel *pse_sel) +{ + if (release_sel(pse_sel) < 0) { + push_error(EINVALID, "sec_close", "invalid PSE selector"); + return -1; + } + return 0; +} + +void aux_free_PSESel(PSESel **pse_sel) +{ + if (!pse_sel || !*pse_sel) + return; + if (release_sel(*pse_sel) < 0 && mem_owned(*pse_sel)) + destroy_sel(*pse_sel); + *pse_sel = nullptr; +} + +int sec_onekeypaironly(PSESel *pse_sel) +{ + return guarded("sec_onekeypaironly", 0, [&] { return store_onekeypaironly(store_of(pse_sel)) ? TRUE : FALSE; }); +} + +int sec_write_PSE(PSESel *pse_sel, OctetString *value) +{ + return guarded("sec_write_PSE", -1, [&] { + auto s = store_of(pse_sel); + std::string name = object_name_for(pse_sel->object, pse_sel->object_type); + if (!value) + fail(EINVALID, "missing value"); + store_put(s, name, object_type_for(name, pse_sel->object_type), bytes_of(value)); + return 0; + }); +} + +RC sec_read_PSE(PSESel *pse_sel, OctetString *value) +{ + return guarded("sec_read_PSE", -1, [&] { + auto s = store_of(pse_sel); + std::string name = object_name_for(pse_sel->object, pse_sel->object_type); + const PseObject &o = require(s, name); + if (pse_sel->object_type && pse_sel->object_type->oid_nelem > 0 && oid_of(pse_sel->object_type) != o.type && + !oid_equal(pse_sel->object_type, &Uid_oid)) + fail(EOBJ, "PSE object " + name + " has a different type"); + if (!value) + fail(EINVALID, "missing value"); + fill_ostr(value, o.value); + return 0; + }); +} + +RC sec_write(PSESel *pse_sel, OctetString *content) +{ + return guarded("sec_write", -1, [&] { + auto s = store_of(pse_sel); + std::string name = object_name_for(pse_sel->object, pse_sel->object_type); + if (!content) + fail(EINVALID, "missing content"); + const PseObject *existing = store_find(s, name); + Oid type = existing && !(pse_sel->object_type && pse_sel->object_type->oid_nelem > 0) + ? existing->type + : object_type_for(name, pse_sel->object_type); + store_put(s, name, type, bytes_of(content)); + return 0; + }); +} + +RC sec_read(PSESel *pse_sel, OctetString *content) +{ + return guarded("sec_read", -1, [&] { + auto s = store_of(pse_sel); + if (!content) + fail(EINVALID, "missing content"); + if (pse_sel->object && !std::strcmp(pse_sel->object, Toc_name)) { + fill_ostr(content, toc_der(*s)); + return 0; + } + fill_ostr(content, require(s, object_name_for(pse_sel->object, pse_sel->object_type)).value); + return 0; + }); +} + +PSEToc *d_PSEToc(OctetString *asn1_string) +{ + return guarded("d_PSEToc", nullptr, [&] { + Bytes in = bytes_of(asn1_string); + der::Node top = der::parse_one(in); + if (top.tag != der::SEQUENCE) + fail(EDECODE, "PSE table of contents expected"); + PSEToc *t = mem_new(); + try { + der::Reader r(top); + t->owner = mem_strdup(der::get_string(r.next(der::UTF8String))); + t->create = mem_strdup(der::get_time(r.next())); + t->update = mem_strdup(der::get_time(r.next())); + t->status = unsigned(der::get_integer(r.next(der::INTEGER))); + der::Reader objs(r.next(der::SEQUENCE)); + PSEObjects **tail = &t->obj; + while (!objs.empty()) { + der::Reader o(objs.next(der::SEQUENCE)); + PSEObjects *po = mem_new(); + *tail = po; + tail = &po->next; + po->name = mem_strdup(der::get_string(o.next(der::UTF8String))); + po->create = mem_strdup(der::get_time(o.next())); + po->update = mem_strdup(der::get_time(o.next())); + po->noOctets = int(der::get_integer(o.next(der::INTEGER))); + po->status = unsigned(der::get_integer(o.next(der::INTEGER))); + po->objType = new_objid(der::get_oid(o.next())); + } + return t; + } catch (...) { + aux_free_PSEToc(&t); + throw; + } + }); +} + +void *af_pse_get(PSE pse_handle, char *objname, ObjId *objtype) +{ + return guarded("af_pse_get", nullptr, [&] { + auto s = store_of(pse_handle); + if (!objname) + fail(EOBJNAME, "missing PSE object name"); + const PseObject &o = require(s, objname); + void *result = decode_object(o); + if (objtype && !is_static_objid(objtype)) + fill_objid(objtype, o.type); + return result; + }); +} + +RC af_pse_update(PSE pse_handle, char *objname, void *opaque, ObjId *opaque_type) +{ + return guarded("af_pse_update", -1, [&] { + auto s = store_of(pse_handle); + std::string name = object_name_for(objname, opaque_type); + Oid type = object_type_for(name, opaque_type); + store_put(s, name, type, encode_object(type, opaque)); + return 0; + }); +} + +DName *af_pse_get_Name(PSE pse_handle) +{ + return guarded("af_pse_get_Name", nullptr, [&]() -> DName * { + auto s = store_of(pse_handle); + if (const PseObject *o = store_find(s, "Name")) + return dec_dname(o->value); + const PseObject &c = require(s, certificate_object(pse_handle, SIGNATURE)); + Certificate *cert = dec_certificate(c.value); + DName *d = copy_dname(cert->tbs->subject); + free_certificate(cert); + return d; + }); +} + +SerialNumber *af_pse_get_SerialNumber(PSE pse_handle) +{ + return guarded("af_pse_get_SerialNumber", nullptr, [&] { + return new_ostr(require(store_of(pse_handle), SerialNumber_name).value); + }); +} + +RC af_pse_update_SerialNumber(PSE pse_handle, SerialNumber *serial) +{ + return guarded("af_pse_update_SerialNumber", -1, [&] { + if (!serial || !serial->noctets) + fail(EINVALID, "missing serial number"); + store_put(store_of(pse_handle), SerialNumber_name, oid_of(&SerialNumber_oid), bytes_of(serial)); + return 0; + }); +} + +PKRoot *af_pse_get_PKRoot(PSE pse_handle) +{ + return guarded("af_pse_get_PKRoot", nullptr, [&] { + return dec_pkroot(require(store_of(pse_handle), PKRoot_name).value); + }); +} + +int af_pse_update_PKRoot(PSE pse_handle, PKRoot *pkroot) +{ + return guarded("af_pse_update_PKRoot", -1, [&] { + store_put(store_of(pse_handle), PKRoot_name, oid_of(&PKRoot_oid), enc_pkroot(pkroot)); + return 0; + }); +} + +FCPath *af_pse_get_FCPath(PSE pse_handle, DName *name) +{ + return guarded("af_pse_get_FCPath", nullptr, [&] { + return dec_fcpath(require(store_of(pse_handle), FCPath_name).value); + }); +} + +CrlSet *af_pse_get_CrlSet(PSE pse_handle) +{ + return guarded("af_pse_get_CrlSet", nullptr, [&]() -> CrlSet * { + store_of(pse_handle); + fail(ENOCRL, "revocation lists are not supported by this implementation"); + }); +} + +} // extern "C" diff --git a/port/secude/src/structs.cpp b/port/secude/src/structs.cpp new file mode 100644 index 000000000..df838ad08 --- /dev/null +++ b/port/secude/src/structs.cpp @@ -0,0 +1,652 @@ +// Deep copy and release of SECUDE structures. +#include "internal.hpp" + +namespace compat { + +namespace { + +ParmType parm_type_of(const ObjId *o) +{ + const AlgInfo *a = alg_by_oid(o); + return a ? a->parm : NoParmType; +} + +void copy_ostr_into(OctetString *dst, const OctetString *src) +{ + fill_ostr(dst, bytes_of(src)); +} + +char *copy_str(const char *s) { return mem_strdup(s); } + +} // namespace + +AlgId *copy_algid(const AlgId *a) +{ + if (!a) + return nullptr; + AlgId *r = mem_new(); + r->objid = copy_objid(a->objid); + if (a->parmoverride) { + r->parmoverride = mem_new(); + *r->parmoverride = *a->parmoverride; + } + if (!a->param) + return r; + switch (parm_type_of(a->objid)) { + case PARM_INTEGER: { + unsigned int *v = mem_new(); + *v = *static_cast(a->param); + r->param = v; + break; + } + case PARM_OctetString: + case NoParmType: + r->param = new_ostr(bytes_of(static_cast(a->param))); + break; + case PARM_KeyBits: + r->param = copy_keybits(static_cast(a->param)); + break; + case PARM_RC2CBC: { + auto *src = static_cast(a->param); + auto *p = mem_new(); + p->version = src->version; + copy_ostr_into(&p->IV, &src->IV); + r->param = p; + break; + } + case PARM_PKCS5: { + auto *src = static_cast(a->param); + auto *p = mem_new(); + p->iterationCount = src->iterationCount; + copy_ostr_into(&p->salt, &src->salt); + r->param = p; + break; + } + default: + break; + } + return r; +} + +void free_algid_content(AlgId *a) +{ + if (!a) + return; + if (a->param && mem_owned(a->param)) { + switch (parm_type_of(a->objid)) { + case PARM_OctetString: + case NoParmType: { + OctetString *o = static_cast(a->param); + aux_free_OctetString(&o); + break; + } + case PARM_KeyBits: { + KeyBits *k = static_cast(a->param); + free_keybits_content(k); + mem_free(k); + break; + } + case PARM_RC2CBC: + aux_free2_OctetString(&static_cast(a->param)->IV); + mem_free(a->param); + break; + case PARM_PKCS5: + aux_free2_OctetString(&static_cast(a->param)->salt); + mem_free(a->param); + break; + default: + mem_free(a->param); + break; + } + a->param = nullptr; + } + ObjId *o = a->objid; + aux_free_ObjId(&o); + a->objid = nullptr; + mem_free(a->parmoverride); + a->parmoverride = nullptr; +} + +void free_algid(AlgId *a) +{ + if (!mem_owned(a)) + return; + if (!a) + return; + free_algid_content(a); + mem_free(a); +} + +KeyBits *copy_keybits(const KeyBits *k) +{ + if (!k) + return nullptr; + KeyBits *r = mem_new(); + copy_ostr_into(&r->part1, &k->part1); + copy_ostr_into(&r->part2, &k->part2); + copy_ostr_into(&r->part3, &k->part3); + copy_ostr_into(&r->part4, &k->part4); + copy_ostr_into(&r->part5, &k->part5); + r->choice = k->choice; + return r; +} + +void free_keybits_content(KeyBits *k) +{ + if (!k) + return; + aux_free2_OctetString(&k->part1); + aux_free2_OctetString(&k->part2); + aux_free2_OctetString(&k->part3); + aux_free2_OctetString(&k->part4); + aux_free2_OctetString(&k->part5); +} + +KeyInfo *copy_keyinfo(const KeyInfo *k) +{ + if (!k) + return nullptr; + KeyInfo *r = mem_new(); + r->subjectAI = copy_algid(k->subjectAI); + fill_bstr(&r->subjectkey, bytes_of(&k->subjectkey), k->subjectkey.nbits); + return r; +} + +void free_keyinfo_content(KeyInfo *k) +{ + if (!k) + return; + free_algid(k->subjectAI); + k->subjectAI = nullptr; + aux_free2_BitString(&k->subjectkey); +} + +void free_keyinfo(KeyInfo *k) +{ + if (!mem_owned(k)) + return; + if (!k) + return; + free_keyinfo_content(k); + mem_free(k); +} + +DName *copy_dname(const DName *d) +{ + DName *head = nullptr, **tail = &head; + for (; d; d = d->next) { + DName *n = mem_new(); + RDName **rtail = &n->element_IF_2; + for (const RDName *r = d->element_IF_2; r; r = r->next) { + RDName *nr = mem_new(); + if (r->member_IF_0) { + AttrValueAssertion *ava = mem_new(); + ava->element_IF_0 = copy_objid(r->member_IF_0->element_IF_0); + if (r->member_IF_0->element_IF_1) + ava->element_IF_1 = new_ostr(bytes_of(r->member_IF_0->element_IF_1)); + ava->attr_encoding = r->member_IF_0->attr_encoding; + nr->member_IF_0 = ava; + } + *rtail = nr; + rtail = &nr->next; + } + *tail = n; + tail = &n->next; + } + return head; +} + +void free_dname(DName *d) +{ + while (d && mem_owned(d)) { + DName *next = d->next; + RDName *r = d->element_IF_2; + while (r) { + RDName *rn = r->next; + if (r->member_IF_0) { + aux_free_ObjId(&r->member_IF_0->element_IF_0); + aux_free_OctetString(&r->member_IF_0->element_IF_1); + mem_free(r->member_IF_0); + } + mem_free(r); + r = rn; + } + mem_free(d); + d = next; + } +} + +Validity *copy_validity(const Validity *v) +{ + if (!v) + return nullptr; + Validity *r = mem_new(); + r->notbefore = copy_str(v->notbefore); + r->notafter = copy_str(v->notafter); + return r; +} + +void free_validity(Validity *v) +{ + if (!mem_owned(v)) + return; + if (!v) + return; + mem_free(v->notbefore); + mem_free(v->notafter); + mem_free(v); +} + +Signature *copy_signature(const Signature *s) +{ + if (!s) + return nullptr; + Signature *r = mem_new(); + r->signAI = copy_algid(s->signAI); + fill_bstr(&r->signature, bytes_of(&s->signature), s->signature.nbits); + return r; +} + +void free_signature(Signature *s) +{ + if (!mem_owned(s)) + return; + if (!s) + return; + free_algid(s->signAI); + aux_free2_BitString(&s->signature); + mem_free(s); +} + +CertExtensions *copy_extensions(const CertExtensions *e) +{ + if (!e) + return nullptr; + CertExtensions *r = mem_new(); + SEQUENCE_OF_Extension **tail = &r->nonSupported; + for (const SEQUENCE_OF_Extension *s = e->nonSupported; s; s = s->next) { + if (!s->element) + continue; + auto *n = mem_new(); + n->element = mem_new(); + n->element->extnId = copy_objid(s->element->extnId); + n->element->critical = s->element->critical; + if (s->element->extnDERcode) + n->element->extnDERcode = new_ostr(bytes_of(s->element->extnDERcode)); + n->element->seqnum = s->element->seqnum; + *tail = n; + tail = &n->next; + } + return r; +} + +void free_extensions(CertExtensions *e) +{ + if (!mem_owned(e)) + return; + if (!e) + return; + SEQUENCE_OF_Extension *s = e->nonSupported; + while (s) { + SEQUENCE_OF_Extension *next = s->next; + if (s->element) { + aux_free_ObjId(&s->element->extnId); + aux_free_OctetString(&s->element->extnDERcode); + mem_free(s->element); + } + mem_free(s); + s = next; + } + mem_free(e); +} + +ToBeSigned *copy_tbs(const ToBeSigned *t) +{ + if (!t) + return nullptr; + ToBeSigned *r = mem_new(); + r->version = t->version; + if (t->serialnumber) + r->serialnumber = new_ostr(bytes_of(t->serialnumber)); + r->signatureAI = copy_algid(t->signatureAI); + r->issuer = copy_dname(t->issuer); + r->valid = copy_validity(t->valid); + r->subject = copy_dname(t->subject); + r->subjectPK = copy_keyinfo(t->subjectPK); + if (t->issuerUId) + r->issuerUId = new_bstr(bytes_of(t->issuerUId), t->issuerUId->nbits); + if (t->subjectUId) + r->subjectUId = new_bstr(bytes_of(t->subjectUId), t->subjectUId->nbits); + r->extensions = copy_extensions(t->extensions); + return r; +} + +void free_tbs(ToBeSigned *t) +{ + if (!mem_owned(t)) + return; + if (!t) + return; + aux_free_OctetString(&t->serialnumber); + free_algid(t->signatureAI); + free_dname(t->issuer); + free_validity(t->valid); + free_dname(t->subject); + free_keyinfo(t->subjectPK); + aux_free_BitString(&t->issuerUId); + aux_free_BitString(&t->subjectUId); + free_extensions(t->extensions); + mem_free(t); +} + +Certificate *copy_certificate(const Certificate *c) +{ + if (!c) + return nullptr; + Certificate *r = mem_new(); + if (c->tbs_DERcode) + r->tbs_DERcode = new_ostr(bytes_of(c->tbs_DERcode)); + r->tbs = copy_tbs(c->tbs); + r->sig = copy_signature(c->sig); + return r; +} + +void free_certificate(Certificate *c) +{ + if (!mem_owned(c)) + return; + if (!c) + return; + aux_free_OctetString(&c->tbs_DERcode); + free_tbs(c->tbs); + free_signature(c->sig); + mem_free(c); +} + +FCPath *copy_fcpath(const FCPath *f) +{ + FCPath *head = nullptr, **tail = &head; + for (; f; f = f->next_forwardpath) { + FCPath *n = mem_new(); + SET_OF_Certificate **ctail = &n->liste; + for (const SET_OF_Certificate *s = f->liste; s; s = s->next) { + if (!s->element) + continue; + auto *e = mem_new(); + e->element = copy_certificate(s->element); + *ctail = e; + ctail = &e->next; + } + *tail = n; + tail = &n->next_forwardpath; + } + return head; +} + +void free_fcpath(FCPath *f) +{ + while (f && mem_owned(f)) { + FCPath *next = f->next_forwardpath; + SET_OF_Certificate *s = f->liste; + while (s) { + SET_OF_Certificate *sn = s->next; + free_certificate(s->element); + mem_free(s); + s = sn; + } + mem_free(f); + f = next; + } +} + +static Serial *copy_serial(const Serial *s) +{ + if (!s) + return nullptr; + Serial *r = mem_new(); + if (s->serial) + r->serial = new_ostr(bytes_of(s->serial)); + r->version = s->version; + r->key = copy_keyinfo(s->key); + r->valid = copy_validity(s->valid); + r->sig = copy_signature(s->sig); + return r; +} + +void free_serial(Serial *s) +{ + if (!mem_owned(s)) + return; + if (!s) + return; + aux_free_OctetString(&s->serial); + free_keyinfo(s->key); + free_validity(s->valid); + free_signature(s->sig); + mem_free(s); +} + +PKRoot *copy_pkroot(const PKRoot *p) +{ + if (!p) + return nullptr; + PKRoot *r = mem_new(); + r->ca = copy_dname(p->ca); + r->newkey = copy_serial(p->newkey); + r->oldkey = copy_serial(p->oldkey); + r->extensions = copy_extensions(p->extensions); + return r; +} + +void free_pkroot(PKRoot *p) +{ + if (!mem_owned(p)) + return; + if (!p) + return; + free_dname(p->ca); + free_serial(p->newkey); + free_serial(p->oldkey); + free_extensions(p->extensions); + mem_free(p); +} + +void free_privatekeyinfo(PrivateKeyInfo *p) +{ + if (!mem_owned(p)) + return; + if (!p) + return; + free_algid(p->privateKeyAlgorithm); + aux_free_OctetString(&p->privateKey); + mem_free(p); +} + +} // namespace compat + +using namespace compat; + +extern "C" { + +AlgId *aux_cpy_AlgId(AlgId *aid) +{ + return guarded("aux_cpy_AlgId", nullptr, [&] { return copy_algid(aid); }); +} + +int aux_cpy2_AlgId(AlgId *dup_aid, AlgId *aid) +{ + return guarded("aux_cpy2_AlgId", -1, [&] { + if (!dup_aid || !aid) + fail(EINVALID, "missing algorithm identifier"); + AlgId *c = copy_algid(aid); + *dup_aid = *c; + mem_free(c); + return 0; + }); +} + +void aux_free2_AlgId(AlgId *algid) { free_algid_content(algid); } + +void aux_free_AlgId(AlgId **aid) +{ + if (!aid || !*aid) + return; + if (!mem_owned(*aid)) { + *aid = nullptr; + return; + } + free_algid(*aid); + *aid = nullptr; +} + +void aux_free2_KeyBits(KeyBits *p) { free_keybits_content(p); } + +void aux_free_KeyBits(KeyBits **kb) +{ + if (!kb || !*kb) + return; + if (!mem_owned(*kb)) { + *kb = nullptr; + return; + } + free_keybits_content(*kb); + mem_free(*kb); + *kb = nullptr; +} + +void aux_free2_KeyInfo(KeyInfo *p) { free_keyinfo_content(p); } + +void aux_free_KeyInfo(KeyInfo **ki) +{ + if (!ki || !*ki) + return; + if (!mem_owned(*ki)) { + *ki = nullptr; + return; + } + free_keyinfo(*ki); + *ki = nullptr; +} + +void aux_free_DName(DName **dn) +{ + if (!dn || !*dn) + return; + if (!mem_owned(*dn)) { + *dn = nullptr; + return; + } + free_dname(*dn); + *dn = nullptr; +} + +void aux_free_Certificate(Certificate **cert) +{ + if (!cert || !*cert) + return; + if (!mem_owned(*cert)) { + *cert = nullptr; + return; + } + free_certificate(*cert); + *cert = nullptr; +} + +void aux_free_Certificates(Certificates **to_be_freed) +{ + if (!to_be_freed || !*to_be_freed) + return; + if (!mem_owned(*to_be_freed)) { + *to_be_freed = nullptr; + return; + } + free_certificate((*to_be_freed)->usercertificate); + free_fcpath((*to_be_freed)->forwardpath); + mem_free(*to_be_freed); + *to_be_freed = nullptr; +} + +void aux_free_FCPath(FCPath **path) +{ + if (!path || !*path) + return; + if (!mem_owned(*path)) { + *path = nullptr; + return; + } + free_fcpath(*path); + *path = nullptr; +} + +void aux_free_PKRoot(PKRoot **pkroot) +{ + if (!pkroot || !*pkroot) + return; + if (!mem_owned(*pkroot)) { + *pkroot = nullptr; + return; + } + free_pkroot(*pkroot); + *pkroot = nullptr; +} + +void aux_free_RSAPrivateKey(RSAPrivateKey **privkey) +{ + if (!privkey || !*privkey) + return; + if (!mem_owned(*privkey)) { + *privkey = nullptr; + return; + } + RSAPrivateKey *k = *privkey; + for (OctetString *o : {&k->modulus, &k->pubex, &k->privex, &k->prime1, &k->prime2, &k->exp1, &k->exp2, &k->coeff}) + aux_free2_OctetString(o); + mem_free(k); + *privkey = nullptr; +} + +void aux_free_SET_OF_IssuedCertificate(SET_OF_IssuedCertificate **isscertset) +{ + if (!isscertset) + return; + SET_OF_IssuedCertificate *s = *isscertset; + while (s && mem_owned(s)) { + SET_OF_IssuedCertificate *next = s->next; + if (s->element) { + aux_free_OctetString(&s->element->serial); + mem_free(s->element->date_of_issue); + mem_free(s->element); + } + mem_free(s); + s = next; + } + *isscertset = nullptr; +} + +void aux_free_PSEToc(PSEToc **toc) +{ + if (!toc || !*toc) + return; + if (!mem_owned(*toc)) { + *toc = nullptr; + return; + } + PSEToc *t = *toc; + mem_free(t->owner); + mem_free(t->create); + mem_free(t->update); + PSEObjects *o = t->obj; + while (o) { + PSEObjects *next = o->next; + mem_free(o->name); + mem_free(o->create); + mem_free(o->update); + aux_free_ObjId(&o->objType); + mem_free(o); + o = next; + } + mem_free(t); + *toc = nullptr; +} + +} // extern "C" diff --git a/port/secude/src/symbols.cpp b/port/secude/src/symbols.cpp new file mode 100644 index 000000000..2659dedd3 --- /dev/null +++ b/port/secude/src/symbols.cpp @@ -0,0 +1,241 @@ +// Name -> address table of all SECUDE functions and data objects used by +// CrypTool (the DoAll list in CrypTool/SecudeLib.h) and libec. +#include "internal.hpp" + +namespace { + +struct Symbol { + const char *name; + void *address; +}; + +const std::vector &symbols() +{ + static const std::vector table = { + {"SECUDE_HasValidTicket", reinterpret_cast(&SECUDE_HasValidTicket)}, + {"light_version", reinterpret_cast(&light_version)}, + {"aux_sprint_version", reinterpret_cast(&aux_sprint_version)}, + {"aux_sxdump2", reinterpret_cast(&aux_sxdump2)}, + {"aux_sxdump", reinterpret_cast(&aux_sxdump)}, + {"d_KeyBits", reinterpret_cast(&d_KeyBits)}, + {"af_cadb_add_Certificate", reinterpret_cast(&af_cadb_add_Certificate)}, + {"aux_free_OctetString", reinterpret_cast(&aux_free_OctetString)}, + {"aux_alloc_OctetString", reinterpret_cast(&aux_alloc_OctetString)}, + {"sec_verify_all", reinterpret_cast(&sec_verify_all)}, + {"arithmetic_comp", reinterpret_cast(&arithmetic_comp)}, + {"af_cadb_list_user", reinterpret_cast(&af_cadb_list_user)}, + {"af_pse_get", reinterpret_cast(&af_pse_get)}, + {"af_pse_update", reinterpret_cast(&af_pse_update)}, + {"af_pse_update_SerialNumber", reinterpret_cast(&af_pse_update_SerialNumber)}, + {"e_OctetString", reinterpret_cast(&e_OctetString)}, + {"lngtouse", reinterpret_cast(&lngtouse)}, + {"aux_OctetString2LN2", reinterpret_cast(&aux_OctetString2LN2)}, + {"d_OctetString", reinterpret_cast(&d_OctetString)}, + {"aux_free_DName", reinterpret_cast(&aux_free_DName)}, + {"aux_OctetString2file", reinterpret_cast(&aux_OctetString2file)}, + {"af_certify", reinterpret_cast(&af_certify)}, + {"af_create_Certificate", reinterpret_cast(&af_create_Certificate)}, + {"aux_Name2DName", reinterpret_cast(&aux_Name2DName)}, + {"af_gen_key", reinterpret_cast(&af_gen_key)}, + {"sec_encrypt_all", reinterpret_cast(&sec_encrypt_all)}, + {"sec_decrypt_all", reinterpret_cast(&sec_decrypt_all)}, + {"arithmetic_mexp", reinterpret_cast(&arithmetic_mexp)}, + {"arithmetic_shift", reinterpret_cast(&arithmetic_shift)}, + {"aux_OString2BString", reinterpret_cast(&aux_OString2BString)}, + {"aux_BitString2LN2", reinterpret_cast(&aux_BitString2LN2)}, + {"arithmetic_sub", reinterpret_cast(&arithmetic_sub)}, + {"rndm", reinterpret_cast(&rndm)}, + {"arithmetic_msub", reinterpret_cast(&arithmetic_msub)}, + {"arithmetic_mdiv", reinterpret_cast(&arithmetic_mdiv)}, + {"arithmetic_mmult", reinterpret_cast(&arithmetic_mmult)}, + {"arithmetic_madd", reinterpret_cast(&arithmetic_madd)}, + {"arithmetic_div", reinterpret_cast(&arithmetic_div)}, + {"arithmetic_mult", reinterpret_cast(&arithmetic_mult)}, + {"arithmetic_add", reinterpret_cast(&arithmetic_add)}, + {"sec_hash_all", reinterpret_cast(&sec_hash_all)}, + {"sec_hash_init", reinterpret_cast(&sec_hash_init)}, + {"sec_hash_more", reinterpret_cast(&sec_hash_more)}, + {"sec_hash_end", reinterpret_cast(&sec_hash_end)}, + {"aux_sprint_Certificate", reinterpret_cast(&aux_sprint_Certificate)}, + {"aux_sprint_error", reinterpret_cast(&aux_sprint_error)}, + {"aux_LN2OctetString", reinterpret_cast(&aux_LN2OctetString)}, + {"af_create", reinterpret_cast(&af_create)}, + {"sec_create", reinterpret_cast(&sec_create)}, + {"af_cadb_get_Certificate", reinterpret_cast(&af_cadb_get_Certificate)}, + {"aux_BString2OString", reinterpret_cast(&aux_BString2OString)}, + {"aux_free_Certificate", reinterpret_cast(&aux_free_Certificate)}, + {"aux_free_Certificates", reinterpret_cast(&aux_free_Certificates)}, + {"d_ContentInfo", reinterpret_cast(&d_ContentInfo)}, + {"aux_free_SET_OF_IssuedCertificate", reinterpret_cast(&aux_free_SET_OF_IssuedCertificate)}, + {"af_close", reinterpret_cast(&af_close)}, + {"sec_close", reinterpret_cast(&sec_close)}, + {"af_encrypt_all", reinterpret_cast(&af_encrypt_all)}, + {"af_cadb_get_user", reinterpret_cast(&af_cadb_get_user)}, + {"th_get_last_error_text", reinterpret_cast(&th_get_last_error_text)}, + {"th_last_error", reinterpret_cast(&th_last_error)}, + {"af_open", reinterpret_cast(&af_open)}, + {"aux_file2OctetString", reinterpret_cast(&aux_file2OctetString)}, + {"sdumpasn", reinterpret_cast(&sdumpasn)}, + {"aux_adump", reinterpret_cast(&aux_adump)}, + {"af_pse_get_Name", reinterpret_cast(&af_pse_get_Name)}, + {"aux_DName2Attr", reinterpret_cast(&aux_DName2Attr)}, + {"af_pse_get_SerialNumber", reinterpret_cast(&af_pse_get_SerialNumber)}, + {"af_pse_get_Certificates", reinterpret_cast(&af_pse_get_Certificates)}, + {"af_pse_get_CrlSet", reinterpret_cast(&af_pse_get_CrlSet)}, + {"d_PSEToc", reinterpret_cast(&d_PSEToc)}, + {"af_decrypt_all", reinterpret_cast(&af_decrypt_all)}, + {"af_sign_all", reinterpret_cast(&af_sign_all)}, + {"aux_free", reinterpret_cast(&aux_free)}, + {"th_remove_last_error", reinterpret_cast(&th_remove_last_error)}, + {"aux_free_BitString", reinterpret_cast(&aux_free_BitString)}, + {"aux_free_AlgId", reinterpret_cast(&aux_free_AlgId)}, + {"aux_free_KeyInfo", reinterpret_cast(&aux_free_KeyInfo)}, + {"aux_free_ObjId", reinterpret_cast(&aux_free_ObjId)}, + {"aux_free_KeyBits", reinterpret_cast(&aux_free_KeyBits)}, + {"aux_free_PSEToc", reinterpret_cast(&aux_free_PSEToc)}, + {"aux_cpy_ObjId", reinterpret_cast(&aux_cpy_ObjId)}, + {"aux_cpy_String", reinterpret_cast(&aux_cpy_String)}, + {"aux_free_String", reinterpret_cast(&aux_free_String)}, + {"sec_onekeypaironly", reinterpret_cast(&sec_onekeypaironly)}, + {"af_get_PSESel", reinterpret_cast(&af_get_PSESel)}, + {"aux_latin1_to_unicode", reinterpret_cast(&aux_latin1_to_unicode)}, + {"aux_calloc", reinterpret_cast(&aux_calloc)}, + {"aux_malloc", reinterpret_cast(&aux_malloc)}, + {"aux_cpy_OctetString", reinterpret_cast(&aux_cpy_OctetString)}, + {"aux_create_PrivateKeyInfo", reinterpret_cast(&aux_create_PrivateKeyInfo)}, + {"af_pse_get_FCPath", reinterpret_cast(&af_pse_get_FCPath)}, + {"aux_free_FCPath", reinterpret_cast(&aux_free_FCPath)}, + {"af_pse_get_PKRoot", reinterpret_cast(&af_pse_get_PKRoot)}, + {"af_PKRoot2Protocert", reinterpret_cast(&af_PKRoot2Protocert)}, + {"aux_free_PKRoot", reinterpret_cast(&aux_free_PKRoot)}, + {"aux_free_RSAPrivateKey", reinterpret_cast(&aux_free_RSAPrivateKey)}, + {"pkcs12_encode", reinterpret_cast(&pkcs12_encode)}, + {"aux_free_error", reinterpret_cast(&aux_free_error)}, + {"af_pse_update_PKRoot", reinterpret_cast(&af_pse_update_PKRoot)}, + {"aux_cmp_DName", reinterpret_cast(&aux_cmp_DName)}, + {"aux_create_PKRoot", reinterpret_cast(&aux_create_PKRoot)}, + {"sec_write_PSE", reinterpret_cast(&sec_write_PSE)}, + {"e_FCPath", reinterpret_cast(&e_FCPath)}, + {"e_Certificate", reinterpret_cast(&e_Certificate)}, + {"aux_ObjId2AlgEnc", reinterpret_cast(&aux_ObjId2AlgEnc)}, + {"d_RSAPrivateKey", reinterpret_cast(&d_RSAPrivateKey)}, + {"e_KeyBits", reinterpret_cast(&e_KeyBits)}, + {"aux_cpy_AlgId", reinterpret_cast(&aux_cpy_AlgId)}, + {"e_KeyInfo", reinterpret_cast(&e_KeyInfo)}, + {"e_PKRoot", reinterpret_cast(&e_PKRoot)}, + {"pkcs12_decode", reinterpret_cast(&pkcs12_decode)}, + {"af_pse_get_Certificate", reinterpret_cast(&af_pse_get_Certificate)}, + {"aux_DName2Name", reinterpret_cast(&aux_DName2Name)}, + {"aux_sprint_AlgId", reinterpret_cast(&aux_sprint_AlgId)}, + {"e_PrintableString", reinterpret_cast(&e_PrintableString)}, + {"d_PrintableString", reinterpret_cast(&d_PrintableString)}, + {"aux_cmp_ObjId", reinterpret_cast(&aux_cmp_ObjId)}, + {"af_get_objoid", reinterpret_cast(&af_get_objoid)}, + {"af_pse_get_keysize", reinterpret_cast(&af_pse_get_keysize)}, + {"sec_write", reinterpret_cast(&sec_write)}, + {"sec_read", reinterpret_cast(&sec_read)}, + {"sec_read_PSE", reinterpret_cast(&sec_read_PSE)}, + {"d_KeyInfo", reinterpret_cast(&d_KeyInfo)}, + {"aux_Name2AlgId", reinterpret_cast(&aux_Name2AlgId)}, + {"af_get_options", reinterpret_cast(&af_get_options)}, + {"aux_cpy2_AlgId", reinterpret_cast(&aux_cpy2_AlgId)}, + {"aux_free2_AlgId", reinterpret_cast(&aux_free2_AlgId)}, + {"aux_cpy2_ObjId", reinterpret_cast(&aux_cpy2_ObjId)}, + {"aux_free2_BitString", reinterpret_cast(&aux_free2_BitString)}, + {"aux_free_PSESel", reinterpret_cast(&aux_free_PSESel)}, + {"e_AlgId", reinterpret_cast(&e_AlgId)}, + {"rsa_sign_all", reinterpret_cast(&rsa_sign_all)}, + {"sec_get_key", reinterpret_cast(&sec_get_key)}, + {"sec_open", reinterpret_cast(&sec_open)}, + {"d_Certificate", reinterpret_cast(&d_Certificate)}, + {"aux_free2_KeyBits", reinterpret_cast(&aux_free2_KeyBits)}, + {"aux_free2_KeyInfo", reinterpret_cast(&aux_free2_KeyInfo)}, + {"aux_free2_OctetString", reinterpret_cast(&aux_free2_OctetString)}, + {"aux_cpy2_BitString", reinterpret_cast(&aux_cpy2_BitString)}, + {"sec_MD2Init", reinterpret_cast(&sec_MD2Init)}, + {"sec_MD2Update", reinterpret_cast(&sec_MD2Update)}, + {"sec_MD2Final", reinterpret_cast(&sec_MD2Final)}, + {"sec_MD4Init", reinterpret_cast(&sec_MD4Init)}, + {"sec_MD4Update", reinterpret_cast(&sec_MD4Update)}, + {"sec_MD4Final", reinterpret_cast(&sec_MD4Final)}, + {"sec_MD5Init", reinterpret_cast(&sec_MD5Init)}, + {"sec_MD5Update", reinterpret_cast(&sec_MD5Update)}, + {"sec_MD5Final", reinterpret_cast(&sec_MD5Final)}, + {"shsInit", reinterpret_cast(&shsInit)}, + {"shsUpdate", reinterpret_cast(&shsUpdate)}, + {"shsFinal", reinterpret_cast(&shsFinal)}, + {"shs1Init", reinterpret_cast(&shs1Init)}, + {"shs1Update", reinterpret_cast(&shs1Update)}, + {"shs1Final", reinterpret_cast(&shs1Final)}, + {"sec_random_ostr", reinterpret_cast(&sec_random_ostr)}, + // called directly by libec (s_ecvali.c) + {"global_add_error", reinterpret_cast(&global_add_error)}, + {"rabinstest", reinterpret_cast(&rabinstest)}, + {"ripemd160WithRSASignature_aid", static_cast(&ripemd160WithRSASignature_aid)}, + {"shaWithRSASignature_aid", static_cast(&shaWithRSASignature_aid)}, + {"md2WithRsaEncryption_aid", static_cast(&md2WithRsaEncryption_aid)}, + {"md2WithRSASignature_aid", static_cast(&md2WithRSASignature_aid)}, + {"dsa_aid", static_cast(&dsa_aid)}, + {"rc2CBC_aid", static_cast(&rc2CBC_aid)}, + {"rc4_aid", static_cast(&rc4_aid)}, + {"md5WithRsaEncryption_aid", static_cast(&md5WithRsaEncryption_aid)}, + {"desEDE_aid", static_cast(&desEDE_aid)}, + {"desCBC3_aid", static_cast(&desCBC3_aid)}, + {"desCBC_pad_aid", static_cast(&desCBC_pad_aid)}, + {"desECB_aid", static_cast(&desECB_aid)}, + {"idea_aid", static_cast(&idea_aid)}, + {"md2_aid", static_cast(&md2_aid)}, + {"md4_aid", static_cast(&md4_aid)}, + {"md5_aid", static_cast(&md5_aid)}, + {"sha_aid", static_cast(&sha_aid)}, + {"sha1_aid", static_cast(&sha1_aid)}, + {"ripemd160_aid", static_cast(&ripemd160_aid)}, + {"sha1WithRSASignature_aid", static_cast(&sha1WithRSASignature_aid)}, + {"rsa_aid", static_cast(&rsa_aid)}, + {"rsaEncryption_aid", static_cast(&rsaEncryption_aid)}, + {"dsaWithSHA_aid", static_cast(&dsaWithSHA_aid)}, + {"dsaWithSHA1_aid", static_cast(&dsaWithSHA1_aid)}, + {"Uid_OID", static_cast(&Uid_OID)}, + {"Cert_OID", static_cast(&Cert_OID)}, + {"sha1_oid", static_cast(&sha1_oid)}, + {"pbeWithSHA1And40BitRC2CBC_oid", static_cast(&pbeWithSHA1And40BitRC2CBC_oid)}, + {"pbeWithSHA1AndDES3xCBC_oid", static_cast(&pbeWithSHA1AndDES3xCBC_oid)}, + {"PKRoot_oid", static_cast(&PKRoot_oid)}, + {"SKnew_oid", static_cast(&SKnew_oid)}, + {"Cert_oid", static_cast(&Cert_oid)}, + {"FCPath_oid", static_cast(&FCPath_oid)}, + {"print_cert_flag", compat::print_cert_flag_address()}, + {"print_keyinfo_flag", compat::print_keyinfo_flag_address()}, + }; + return table; +} + +} // namespace + +using namespace compat; + +extern "C" { + +void *secude_compat_symbol(const char *name) +{ + if (!name) + return nullptr; + for (const auto &s : symbols()) + if (!std::strcmp(s.name, name)) + return s.address; + return nullptr; +} + +int secude_compat_symbol_count(void) +{ + return int(symbols().size()); +} + +const char *secude_compat_symbol_name(int index) +{ + if (index < 0 || size_t(index) >= symbols().size()) + return nullptr; + return symbols()[size_t(index)].name; +} + +} // extern "C" diff --git a/port/secude/src/symcipher.cpp b/port/secude/src/symcipher.cpp new file mode 100644 index 000000000..17ce7df1f --- /dev/null +++ b/port/secude/src/symcipher.cpp @@ -0,0 +1,244 @@ +// Symmetric ciphers. SECUDE "std" padding: the last block is filled with zero +// octets and its final octet holds the number of data octets in that block +// (0..blocksize-1); PKCS padding as in PKCS#5/RFC 1423. +#include "legacy.hpp" +#include "symcipher.hpp" + +#include + +namespace compat { + +namespace { + +size_t block_size(Cipher c) +{ + switch (c) { + case Cipher::RC4: + return 1; + case Cipher::AES: + return 16; + default: + return 8; + } +} + +void check_key(Cipher c, size_t keylen) +{ + bool ok = false; + switch (c) { + case Cipher::DES: ok = keylen == 8; break; + case Cipher::DES3: ok = keylen == 16 || keylen == 24; break; + case Cipher::IDEA: ok = keylen == 16; break; + case Cipher::RC2: ok = keylen >= 1 && keylen <= 128; break; + case Cipher::RC4: ok = keylen >= 1 && keylen <= 256; break; + case Cipher::AES: ok = keylen == 16 || keylen == 24 || keylen == 32; break; + default: break; + } + if (!ok) + fail(EKEYSIZE, str_printf("invalid key length of %u bits", unsigned(keylen * 8))); +} + +std::string evp_cipher_name(Cipher c, AlgMode mode, size_t keylen) +{ + bool cbc = mode == CBC; + switch (c) { + case Cipher::DES: return cbc ? "DES-CBC" : "DES-ECB"; + case Cipher::DES3: + if (keylen == 16) + return cbc ? "DES-EDE-CBC" : "DES-EDE"; + return cbc ? "DES-EDE3-CBC" : "DES-EDE3"; + case Cipher::IDEA: return cbc ? "IDEA-CBC" : "IDEA-ECB"; + case Cipher::RC2: return cbc ? "RC2-CBC" : "RC2-ECB"; + case Cipher::RC4: return "RC4"; + case Cipher::AES: return str_printf("AES-%u-%s", unsigned(keylen * 8), cbc ? "CBC" : "ECB"); + default: return {}; + } +} + +bool run_evp(const CipherSpec &s, const uint8_t *in, size_t n, Bytes &out, bool encrypt) +{ + std::string name = evp_cipher_name(s.cipher, s.mode, s.key.size()); + EVP_CIPHER *cipher = name.empty() ? nullptr : fetch_cipher(name.c_str()); + if (!cipher) + return false; + EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); + bool ok = ctx && EVP_CipherInit_ex2(ctx, cipher, nullptr, nullptr, encrypt, nullptr); + if (ok && (s.cipher == Cipher::RC2 || s.cipher == Cipher::RC4)) { + size_t keylen = s.key.size(); + size_t keybits = s.rc2_bits; + OSSL_PARAM params[3]; + int i = 0; + params[i++] = OSSL_PARAM_construct_size_t(OSSL_CIPHER_PARAM_KEYLEN, &keylen); + if (s.cipher == Cipher::RC2) + params[i++] = OSSL_PARAM_construct_size_t(OSSL_CIPHER_PARAM_RC2_KEYBITS, &keybits); + params[i] = OSSL_PARAM_construct_end(); + ok = EVP_CIPHER_CTX_set_params(ctx, params); + } + if (ok) + ok = EVP_CIPHER_CTX_set_padding(ctx, 0) && + EVP_CipherInit_ex2(ctx, nullptr, s.key.data(), s.iv.empty() ? nullptr : s.iv.data(), encrypt, nullptr); + out.assign(n + 32, 0); + int len1 = 0, len2 = 0; + if (ok) + ok = EVP_CipherUpdate(ctx, out.data(), &len1, in, int(n)) && EVP_CipherFinal_ex(ctx, out.data() + len1, &len2); + EVP_CIPHER_CTX_free(ctx); + EVP_CIPHER_free(cipher); + if (!ok) + fail(EINTERNAL, "cipher operation failed"); + out.resize(size_t(len1 + len2)); + return true; +} + +Bytes run_builtin(const CipherSpec &s, const uint8_t *in, size_t n, bool encrypt) +{ + Bytes out(n); + if (s.cipher == Cipher::RC4) { + legacy::rc4(s.key.data(), s.key.size(), in, out.data(), n); + return out; + } + std::unique_ptr bc; + switch (s.cipher) { + case Cipher::DES: bc = legacy::make_des(s.key.data()); break; + case Cipher::DES3: bc = legacy::make_des3(s.key.data(), s.key.size()); break; + case Cipher::IDEA: bc = legacy::make_idea(s.key.data()); break; + case Cipher::RC2: bc = legacy::make_rc2(s.key.data(), s.key.size(), s.rc2_bits); break; + default: fail(EUNKNOWNALGID, "cipher not available"); + } + size_t bs = bc->block_size(); + uint8_t chain[16] = {0}; + if (s.mode == CBC) + std::memcpy(chain, s.iv.data(), bs); + for (size_t off = 0; off < n; off += bs) { + const uint8_t *src = in + off; + uint8_t *dst = out.data() + off; + if (s.mode != CBC) { + encrypt ? bc->encrypt(src, dst) : bc->decrypt(src, dst); + } else if (encrypt) { + uint8_t x[16]; + for (size_t i = 0; i < bs; ++i) + x[i] = src[i] ^ chain[i]; + bc->encrypt(x, dst); + std::memcpy(chain, dst, bs); + } else { + uint8_t saved[16]; + std::memcpy(saved, src, bs); + bc->decrypt(src, dst); + for (size_t i = 0; i < bs; ++i) + dst[i] ^= chain[i]; + std::memcpy(chain, saved, bs); + } + } + return out; +} + +Bytes raw(const CipherSpec &s, const uint8_t *in, size_t n, bool encrypt) +{ + Bytes out; + if (run_evp(s, in, n, out, encrypt)) + return out; + return run_builtin(s, in, n, encrypt); +} + +unsigned rc2_bits_from_version(int version) +{ + switch (version) { + case 160: return 40; + case 120: return 64; + case 58: return 128; + default: return version >= 256 ? unsigned(version) : 0; + } +} + +CipherSpec spec_from_algid(const AlgId *alg, const Bytes &key) +{ + if (!alg || !alg->objid) + fail(EALGID, "missing algorithm identifier"); + const AlgInfo *a = alg_by_oid(alg->objid); + if (!a || a->type != SYM_ENC || a->cipher == Cipher::None) + fail(EUNKNOWNALGID, "not a symmetric algorithm: " + oid_to_string(oid_of(alg->objid))); + CipherSpec s; + s.cipher = a->cipher; + s.mode = a->mode; + s.pad = a->pad; + s.key = key; + check_key(s.cipher, key.size()); + size_t bs = block_size(s.cipher); + if (s.mode == CBC) { + s.iv.assign(bs, 0); + const OctetString *iv = nullptr; + if (alg->param && a->parm == PARM_OctetString) + iv = static_cast(alg->param); + else if (alg->param && a->parm == PARM_RC2CBC) + iv = &static_cast(alg->param)->IV; + if (iv && iv->noctets) { + if (iv->noctets != bs) + fail(EWRONGPARM, "wrong IV length"); + s.iv = bytes_of(iv); + } + } + if (s.cipher == Cipher::RC2) { + unsigned bits = 0; + if (alg->param) + bits = rc2_bits_from_version(static_cast(alg->param)->version); + s.rc2_bits = bits ? bits : unsigned(key.size() * 8); + } + return s; +} + +} // namespace + +Bytes cipher_encrypt(const CipherSpec &s, const uint8_t *in, size_t n) +{ + size_t bs = block_size(s.cipher); + Bytes data(in, in + n); + if (bs > 1) { + if (s.pad == Pad::Std) { + size_t r = n % bs; + data.resize(n - r + bs, 0); + data.back() = uint8_t(r); + } else if (s.pad == Pad::Pkcs) { + size_t k = bs - n % bs; + data.insert(data.end(), k, uint8_t(k)); + } else if (n % bs) { + fail(EINVALID, "input is not a multiple of the block size"); + } + } + return raw(s, data.data(), data.size(), true); +} + +Bytes cipher_decrypt(const CipherSpec &s, const uint8_t *in, size_t n) +{ + size_t bs = block_size(s.cipher); + if (bs > 1 && (n % bs || (s.pad != Pad::None && n == 0))) + fail(EDECRYPTION, "ciphertext length is not a multiple of the block size"); + Bytes out = raw(s, in, n, false); + if (bs > 1 && s.pad == Pad::Std) { + size_t r = out.back(); + if (r >= bs) + fail(EDECRYPTION, "invalid padding"); + out.resize(out.size() - bs + r); + } else if (bs > 1 && s.pad == Pad::Pkcs) { + size_t k = out.back(); + if (k == 0 || k > bs) + fail(EDECRYPTION, "invalid padding"); + for (size_t i = out.size() - k; i < out.size(); ++i) + if (out[i] != k) + fail(EDECRYPTION, "invalid padding"); + out.resize(out.size() - k); + } + return out; +} + +Bytes sym_encrypt(const AlgId *alg, const Bytes &key, size_t keybits, const uint8_t *in, size_t n) +{ + Bytes k(key.begin(), key.begin() + long(std::min(key.size(), (keybits + 7) / 8))); + return cipher_encrypt(spec_from_algid(alg, k), in, n); +} + +Bytes sym_decrypt(const AlgId *alg, const Bytes &key, size_t keybits, const uint8_t *in, size_t n) +{ + Bytes k(key.begin(), key.begin() + long(std::min(key.size(), (keybits + 7) / 8))); + return cipher_decrypt(spec_from_algid(alg, k), in, n); +} + +} // namespace compat diff --git a/port/secude/src/symcipher.hpp b/port/secude/src/symcipher.hpp new file mode 100644 index 000000000..eda3d7959 --- /dev/null +++ b/port/secude/src/symcipher.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include "internal.hpp" + +namespace compat { + +struct CipherSpec { + Cipher cipher = Cipher::None; + AlgMode mode = ECB; + Pad pad = Pad::None; + Bytes key; + Bytes iv; + unsigned rc2_bits = 0; +}; + +Bytes cipher_encrypt(const CipherSpec &s, const uint8_t *in, size_t n); +Bytes cipher_decrypt(const CipherSpec &s, const uint8_t *in, size_t n); + +} // namespace compat diff --git a/port/secude/tests/main.cpp b/port/secude/tests/main.cpp new file mode 100644 index 000000000..7c7d5a87f --- /dev/null +++ b/port/secude/tests/main.cpp @@ -0,0 +1,75 @@ +#include "testing.hpp" + +#include +#include +#include + +#include "secude_compat.h" + +extern "C" int secude_compat_test_libec(void); + +namespace testing { + +namespace { +int failures = 0; +std::string g_scratch; +} // namespace + +std::vector ®istry() +{ + static std::vector r; + return r; +} + +void report_failure(const char *file, int line, const std::string &what) +{ + ++failures; + std::fprintf(stderr, " FAILED %s:%d: %s\n", file, line, what.c_str()); +} + +std::string scratch_dir() { return g_scratch; } + +std::string hex(const void *p, size_t n) +{ + static const char d[] = "0123456789abcdef"; + std::string s; + const unsigned char *b = static_cast(p); + for (size_t i = 0; i < n; ++i) { + s += d[b[i] >> 4]; + s += d[b[i] & 15]; + } + return s; +} + +} // namespace testing + +TEST(libec_binding) +{ + CHECK_EQ(secude_compat_test_libec(), 0); +} + +int main(int argc, char **argv) +{ + char tmpl[] = "/tmp/secude_compat_tests.XXXXXX"; + const char *dir = mkdtemp(tmpl); + if (!dir) { + std::perror("mkdtemp"); + return 2; + } + testing::g_scratch = dir; + std::printf("SECUDE compat tests (%s, legacy provider %s)\n", secude_compat_version(), + secude_compat_legacy_provider_active() ? "active" : "not used"); + const char *only = argc > 1 ? argv[1] : nullptr; + for (const auto &c : testing::registry()) { + if (only && !std::strstr(c.name, only)) + continue; + int before = testing::failures; + c.fn(); + std::printf("%s %s\n", testing::failures == before ? "ok " : "FAIL", c.name); + } + std::string cleanup = "rm -rf '" + testing::g_scratch + "'"; + if (std::system(cleanup.c_str()) != 0) + std::fprintf(stderr, "could not remove %s\n", testing::g_scratch.c_str()); + std::printf("%d failure(s)\n", testing::failures); + return testing::failures ? 1 : 0; +} diff --git a/port/secude/tests/test_asym.cpp b/port/secude/tests/test_asym.cpp new file mode 100644 index 000000000..817a7571f --- /dev/null +++ b/port/secude/tests/test_asym.cpp @@ -0,0 +1,183 @@ +#include "bn.hpp" +#include "der.hpp" +#include "testing.hpp" + +#include +#include + +using namespace compat; + +namespace { + +struct KeyPair { + KeyInfo *pub = nullptr, *priv = nullptr; + ~KeyPair() + { + aux_free_KeyInfo(&pub); + aux_free_KeyInfo(&priv); + } +}; + +Signature sign(KeyInfo *priv, AlgId *alg, const Bytes &msg) +{ + Bytes s = sign_message(priv, Bytes(), alg, msg.data(), msg.size()); + Signature sig{}; + sig.signAI = alg; + fill_bstr(&sig.signature, s, s.size() * 8); + return sig; +} + +int verify(KeyInfo *pub, Signature *sig, const Bytes &msg) +{ + Key key{}; + key.key = pub; + OctetString in{sec_uint4(msg.size()), reinterpret_cast(const_cast(msg.data()))}; + return sec_verify_all(&in, sig, &key, nullptr); +} + +EVP_PKEY *openssl_rsa(const RsaPublic &k) +{ + Bn n = bn_from(k.n), e = bn_from(k.e); + OSSL_PARAM_BLD *b = OSSL_PARAM_BLD_new(); + OSSL_PARAM_BLD_push_BN(b, OSSL_PKEY_PARAM_RSA_N, n.get()); + OSSL_PARAM_BLD_push_BN(b, OSSL_PKEY_PARAM_RSA_E, e.get()); + OSSL_PARAM *params = OSSL_PARAM_BLD_to_param(b); + EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_from_name(nullptr, "RSA", nullptr); + EVP_PKEY *pkey = nullptr; + EVP_PKEY_fromdata_init(ctx); + EVP_PKEY_fromdata(ctx, &pkey, EVP_PKEY_PUBLIC_KEY, params); + EVP_PKEY_CTX_free(ctx); + OSSL_PARAM_free(params); + OSSL_PARAM_BLD_free(b); + return pkey; +} + +} // namespace + +TEST(asym_rsa_sign_verify_all_algorithms) +{ + for (int bits : {512, 768}) { + KeyPair kp; + rsa_generate(bits, &kp.pub, &kp.priv); + RsaPublic pub = rsa_public_from_keyinfo(kp.pub); + CHECK_EQ(rsa_bits(pub.n), bits); + CHECK_HEX(pub.e.data(), pub.e.size(), "010001"); + Bytes msg = random_bytes(100); + for (AlgId *alg : {&md2WithRsaEncryption_aid, &md5WithRsaEncryption_aid, &shaWithRSASignature_aid, + &sha1WithRSASignature_aid, &ripemd160WithRSASignature_aid, &sha1WithRsaEncryption_aid}) { + Signature sig = sign(kp.priv, alg, msg); + CHECK_EQ(sig.signature.nbits, sec_uint4(bits)); + CHECK_EQ(verify(kp.pub, &sig, msg), 0); + sig.signature.bits[3] ^= 1; + CHECK_EQ(verify(kp.pub, &sig, msg), -1); + aux_free2_BitString(&sig.signature); + } + // PKCS#1 v1.5 interoperability + Signature sig = sign(kp.priv, &md5WithRsaEncryption_aid, msg); + EVP_PKEY *pkey = openssl_rsa(pub); + EVP_MD_CTX *mctx = EVP_MD_CTX_new(); + CHECK_EQ(EVP_DigestVerifyInit_ex(mctx, nullptr, "MD5", nullptr, nullptr, pkey, nullptr), 1); + CHECK_EQ(EVP_DigestVerify(mctx, reinterpret_cast(sig.signature.bits), sig.signature.nbits / 8, + msg.data(), msg.size()), + 1); + EVP_MD_CTX_free(mctx); + EVP_PKEY_free(pkey); + aux_free2_BitString(&sig.signature); + } + aux_free_error(); +} + +TEST(asym_rsa_small_key_and_blocks) +{ + KeyPair kp; + rsa_generate(301, &kp.pub, &kp.priv); + RsaPublic pub = rsa_public_from_keyinfo(kp.pub); + CHECK_EQ(rsa_bits(pub.n), 301); + RsaPrivate priv = rsa_private_from_keyinfo(kp.priv, Bytes()); + // block sizes: 37 octets of plain text per 38 octet cipher block + Bytes msg = random_bytes(37 * 3); + Bytes c = rsa_encrypt_blocks(pub, msg.data(), msg.size()); + CHECK_EQ(c.size(), size_t(38 * 3)); + CHECK(rsa_decrypt_blocks(priv, c.data(), c.size()) == msg); + Bytes partial = random_bytes(40); + c = rsa_encrypt_blocks(pub, partial.data(), partial.size()); + Bytes d = rsa_decrypt_blocks(priv, c.data(), c.size()); + CHECK_EQ(d.size(), size_t(74)); + CHECK(Bytes(d.begin(), d.begin() + 40) == partial); + CHECK(Bytes(d.begin() + 40, d.end()) == Bytes(34, 0)); +} + +TEST(asym_rsa_textbook_property_for_side_channel_demo) +{ + KeyPair kp; + rsa_generate(512, &kp.pub, &kp.priv); + RsaPublic pub = rsa_public_from_keyinfo(kp.pub); + RsaPrivate priv = rsa_private_from_keyinfo(kp.priv, Bytes()); + Bytes session = random_bytes(16); + Bytes block(63 - 16, 0); + block.insert(block.end(), session.begin(), session.end()); + Bytes c = rsa_encrypt_blocks(pub, block.data(), block.size()); + CHECK_EQ(c.size(), size_t(64)); + // C' = C * 3^e mod n decrypts to 3*m mod n + Bn n = bn_from(pub.n), e = bn_from(pub.e), three = bn_word(3), f = bn_new(), c2 = bn_new(); + BN_CTX *ctx = BN_CTX_new(); + BN_mod_exp(f.get(), three.get(), e.get(), n.get(), ctx); + BN_mod_mul(c2.get(), bn_from(c).get(), f.get(), n.get(), ctx); + Bytes m2 = rsa_decrypt_blocks(priv, bn_bytes(c2.get(), 64).data(), 64); + Bn expect = bn_new(); + BN_mod_mul(expect.get(), bn_from(block).get(), three.get(), n.get(), ctx); + CHECK(BN_cmp(bn_from(m2).get(), expect.get()) == 0); + BN_CTX_free(ctx); +} + +TEST(asym_rsa_user_chosen_exponent) +{ + Bytes e17{0x11}; + KeyPair kp; + RsaPrivate k17; + for (;;) { + aux_free_KeyInfo(&kp.pub); + aux_free_KeyInfo(&kp.priv); + rsa_generate(512, &kp.pub, &kp.priv); + try { + k17 = rsa_private_from_keyinfo(kp.priv, e17); + break; + } catch (const Error &e) { + CHECK_EQ(e.code, EALGNOTFITKEY); // 17 divides (p-1)(q-1) + } + } + RsaPrivate base = rsa_private_from_keyinfo(kp.priv, Bytes()); + CHECK(k17.d != base.d); + Bytes m = random_bytes(20); + Bytes s = rsa_raw_private(k17, m); + Bn r = bn_new(); + BN_CTX *ctx = BN_CTX_new(); + BN_mod_exp(r.get(), bn_from(s).get(), bn_from(e17).get(), bn_from(k17.n).get(), ctx); + CHECK(BN_cmp(r.get(), bn_from(m).get()) == 0); + BN_CTX_free(ctx); +} + +TEST(asym_dsa_sign_verify) +{ + KeyPair kp; + dsa_generate(512, &kp.pub, &kp.priv); + CHECK(keyinfo_is_dsa(kp.pub)); + auto *params = static_cast(kp.pub->subjectAI->param); + CHECK_EQ(rsa_bits(bytes_of(¶ms->part1)), 512); + CHECK_EQ(rsa_bits(bytes_of(¶ms->part2)), 160); + Bytes msg = random_bytes(77); + for (AlgId *alg : {&dsaWithSHA_aid, &dsaWithSHA1_aid}) { + Signature sig = sign(kp.priv, alg, msg); + CHECK_EQ(verify(kp.pub, &sig, msg), 0); + Bytes other = msg; + other[0] ^= 1; + CHECK_EQ(verify(kp.pub, &sig, other), -1); + aux_free2_BitString(&sig.signature); + } + Signature wrong = sign(kp.priv, &dsaWithSHA1_aid, msg); + wrong.signAI = &md5WithRsaEncryption_aid; + CHECK_EQ(verify(kp.pub, &wrong, msg), -1); + CHECK_EQ(th_last_error(), EALGNOTFITKEY); + aux_free2_BitString(&wrong.signature); + aux_free_error(); +} diff --git a/port/secude/tests/test_binding.cpp b/port/secude/tests/test_binding.cpp new file mode 100644 index 000000000..164be8926 --- /dev/null +++ b/port/secude/tests/test_binding.cpp @@ -0,0 +1,69 @@ +// Compiles CrypTool/SecudeLib.h exactly as the application does and binds +// every member through secude_compat_symbol(), mirroring the non-Windows +// branch of CSecudeLib::OpenSecudeLib(). +typedef void *HINSTANCE; +typedef unsigned int UINT; + +#include "SecudeLib.h" +#include "secude_compat.h" +#include "testing.hpp" + +#include + +int CSecudeLib::OpenSecudeLib() +{ + int errcnt = 0; +#define DoOneFn(a, b, c, d) \ + c = (c##_t)secude_compat_symbol(#c); \ + if (c == NULL) \ + errcnt++; +#define DoOneData(a, b) \ + b = (a *)secude_compat_symbol(#b); \ + if (b == NULL) \ + errcnt++; + DoAll +#undef DoOneFn +#undef DoOneData + char *line; + Status = errcnt ? 0 : (SECUDE_HasValidTicket(&line) < 0 ? 1 : 2); + if ((MaxBits = light_version()) == 0) + MaxBits = 8192; + return errcnt; +} + +CSecudeLib::CSecudeLib() : MaxBits(0), hDLL(nullptr), Status(0) {} +CSecudeLib::~CSecudeLib() {} +int CSecudeLib::CloseSecudeLib() { return 0; } + +TEST(binding_cryptool_secudelib) +{ + CSecudeLib lib; + CHECK_EQ(lib.OpenSecudeLib(), 0); + CHECK_EQ(lib.GetStatus(), 2); + CHECK_EQ(lib.MaxBits, 8192); + CHECK(secude_compat_symbol("no_such_symbol") == nullptr); + + char data[] = "abc"; + OctetString in = {3, data}; + OctetString out = {0, nullptr}; + CHECK_EQ(lib.sec_hash_all(&in, &out, lib.md5_aid, NULL), 0); + CHECK_HEX(out.octets, out.noctets, "900150983cd24fb0d6963f7d28e17f72"); + lib.aux_free(out.octets); + + // flags are written through sec_uint4 pointers + *lib.print_keyinfo_flag = 0x0d; + *lib.print_cert_flag = 0x77e; + CHECK_EQ(*lib.print_keyinfo_flag, 0x0du); + + CHECK(lib.af_open(const_cast("/nonexistent/x.pse"), NULL, const_cast("1"), NULL) == NULL); + CHECK(lib.LASTERROR != 0); + CHECK(std::strlen(lib.LASTTEXT) > 0); + char *err = lib.aux_sprint_error(NULL, NULL, 1); + CHECK(err && std::strstr(err, "EPSENOTEXISTING")); + lib.aux_free_String(&err); + lib.aux_free_error(); + CHECK_EQ(lib.LASTERROR, 0); + CHECK_EQ((*lib.Cert_OID)->oid_nelem, lib.Cert_oid->oid_nelem); + CHECK(lib.rsa_aid->param != nullptr); + CHECK(std::strstr(lib.aux_sprint_version(NULL), "compatibility") != nullptr); +} diff --git a/port/secude/tests/test_cipher.cpp b/port/secude/tests/test_cipher.cpp new file mode 100644 index 000000000..ef197beac --- /dev/null +++ b/port/secude/tests/test_cipher.cpp @@ -0,0 +1,229 @@ +#include "legacy.hpp" +#include "testing.hpp" + +#include +#include + +using namespace compat; + +namespace { + +Bytes unhex(const std::string &h) +{ + Bytes b; + for (size_t i = 0; i + 1 < h.size(); i += 2) + b.push_back(uint8_t(std::stoi(h.substr(i, 2), nullptr, 16))); + return b; +} + +struct SymKey { + Key key{}; + KeyInfo info{}; + Bytes material; + SymKey(AlgId *alg, const Bytes &k) : material(k) + { + info.subjectAI = alg; + info.subjectkey.bits = reinterpret_cast(material.data()); + info.subjectkey.nbits = sec_uint4(material.size() * 8); + key.key = &info; + } +}; + +Bytes encrypt(AlgId *alg, const Bytes &key, const Bytes &plain) +{ + SymKey k(alg, key); + OctetString in{sec_uint4(plain.size()), reinterpret_cast(const_cast(plain.data()))}; + std::vector buf(plain.size() + 64); + BitString out{0, buf.data()}; + CHECK_EQ(sec_encrypt_all(&in, &out, &k.key), 0); + return Bytes(buf.begin(), buf.begin() + out.nbits / 8); +} + +int decrypt(AlgId *alg, const Bytes &key, const Bytes &cipher, Bytes &plain) +{ + SymKey k(alg, key); + BitString in{sec_uint4(cipher.size() * 8), reinterpret_cast(const_cast(cipher.data()))}; + std::vector buf(cipher.size() + 64); + OctetString out{0, buf.data()}; + int rc = sec_decrypt_all(&in, &out, &k.key); + plain.assign(buf.begin(), buf.begin() + out.noctets); + return rc; +} + +} // namespace + +TEST(cipher_des_known_answer) +{ + Bytes key = unhex("133457799bbcdff1"), pt = unhex("0123456789abcdef"); + uint8_t ct[8]; + legacy::make_des(key.data())->encrypt(pt.data(), ct); + CHECK_HEX(ct, 8, "85e813540f0ab405"); + Bytes c = encrypt(&desECB_aid, key, pt); + CHECK_EQ(c.size(), size_t(16)); + CHECK_HEX(c.data(), 8, "85e813540f0ab405"); + Bytes back; + CHECK_EQ(decrypt(&desECB_aid, key, c, back), 0); + CHECK(back == pt); +} + +TEST(cipher_idea_known_answer) +{ + Bytes key = unhex("00010002000300040005000600070008"), pt = unhex("0000000100020003"); + auto idea = legacy::make_idea(key.data()); + uint8_t ct[8], back[8]; + idea->encrypt(pt.data(), ct); + CHECK_HEX(ct, 8, "11fbed2b01986de5"); + idea->decrypt(ct, back); + CHECK_HEX(back, 8, "0000000100020003"); + Bytes c = encrypt(&idea_aid, key, pt); + CHECK_HEX(c.data(), 8, "11fbed2b01986de5"); +} + +TEST(cipher_rc2_rfc2268) +{ + struct { + const char *key; + unsigned bits; + const char *pt; + const char *ct; + } v[] = { + {"0000000000000000", 63, "0000000000000000", "ebb773f993278eff"}, + {"ffffffffffffffff", 64, "ffffffffffffffff", "278b27e42e2f0d49"}, + {"3000000000000000", 64, "1000000000000001", "30649edf9be7d2c2"}, + {"88", 64, "0000000000000000", "61a8a244adacccf0"}, + {"88bca90e90875a", 64, "0000000000000000", "6ccf4308974c267f"}, + {"88bca90e90875a7f0f79c384627bafb2", 64, "0000000000000000", "1a807d272bbe5db1"}, + {"88bca90e90875a7f0f79c384627bafb2", 128, "0000000000000000", "2269552ab0f85ca6"}, + }; + for (const auto &t : v) { + Bytes key = unhex(t.key), pt = unhex(t.pt); + auto rc2 = legacy::make_rc2(key.data(), key.size(), t.bits); + uint8_t ct[8], back[8]; + rc2->encrypt(pt.data(), ct); + CHECK_HEX(ct, 8, t.ct); + rc2->decrypt(ct, back); + CHECK_HEX(back, 8, t.pt); + } +} + +TEST(cipher_rc4_known_answer) +{ + const char *pt = "Plaintext"; + uint8_t out[9]; + legacy::rc4(reinterpret_cast("Key"), 3, reinterpret_cast(pt), out, 9); + CHECK_HEX(out, 9, "bbf316e8d940af0ad3"); + Bytes c = encrypt(&rc4_aid, Bytes{'K', 'e', 'y'}, Bytes(pt, pt + 9)); + CHECK_HEX(c.data(), c.size(), "bbf316e8d940af0ad3"); +} + +TEST(cipher_round_trips_and_padding) +{ + struct { + AlgId *alg; + size_t keylen; + size_t block; + bool std_pad; + } algs[] = { + {&desECB_aid, 8, 8, true}, {&desCBC_pad_aid, 8, 8, false}, {&desCBC3_aid, 16, 8, true}, + {&desEDE_aid, 16, 8, true}, {&idea_aid, 16, 8, true}, {&rc2CBC_aid, 1, 8, false}, + {&rc2CBC_aid, 16, 8, false}, {&rc4_aid, 5, 1, false}, {&rc4_aid, 16, 1, false}, + {&aes128CBC_aid, 16, 16, false}, {&desCBC3_aid, 24, 8, true}, + }; + for (const auto &a : algs) { + Bytes key = random_bytes(a.keylen); + for (size_t len = 0; len < 35; ++len) { + Bytes pt = random_bytes(len); + Bytes c = encrypt(a.alg, key, pt); + size_t expected = a.block == 1 ? len : (len / a.block + 1) * a.block; + CHECK_EQ(c.size(), expected); + Bytes back; + CHECK_EQ(decrypt(a.alg, key, c, back), 0); + CHECK(back == pt); + } + } +} + +TEST(cipher_std_padding_layout) +{ + Bytes key = unhex("0123456789abcdef"); + Bytes pt = {'a', 'b', 'c'}; + Bytes c = encrypt(&desECB_aid, key, pt); + uint8_t block[8]; + legacy::make_des(key.data())->decrypt(c.data(), block); + CHECK_HEX(block, 8, "6162630000000003"); +} + +TEST(cipher_bad_padding_is_decryption_error) +{ + Bytes key = unhex("0123456789abcdef"); + uint8_t garbage[8] = {0x61, 0x62, 0x63, 0, 0, 0, 0, 0x09}; + uint8_t ct[8]; + legacy::make_des(key.data())->encrypt(garbage, ct); + Bytes back; + CHECK_EQ(decrypt(&desECB_aid, key, Bytes(ct, ct + 8), back), -1); + ErrStack *err = th_remove_last_error(); + CHECK_EQ(err->e_number, 1792); + aux_free(err->e_text); + aux_free(err->e_proc); + aux_free(err); + CHECK_EQ(decrypt(&desECB_aid, key, Bytes(ct, ct + 7), back), -1); + aux_free_error(); +} + +TEST(cipher_builtin_matches_openssl_legacy) +{ + OSSL_LIB_CTX *ctx = OSSL_LIB_CTX_new(); + OSSL_PROVIDER_load(ctx, "default"); + if (!OSSL_PROVIDER_load(ctx, "legacy")) { + std::printf(" (legacy provider not available, cross-check skipped)\n"); + OSSL_LIB_CTX_free(ctx); + return; + } + auto ref = [&](const char *name, const Bytes &key, unsigned rc2bits, const Bytes &in) { + EVP_CIPHER *c = EVP_CIPHER_fetch(ctx, name, nullptr); + EVP_CIPHER_CTX *cc = EVP_CIPHER_CTX_new(); + EVP_CipherInit_ex2(cc, c, nullptr, nullptr, 1, nullptr); + size_t keylen = key.size(), bits = rc2bits; + if (EVP_CIPHER_get_key_length(c) != int(keylen)) { + OSSL_PARAM p[3] = {OSSL_PARAM_construct_size_t(OSSL_CIPHER_PARAM_KEYLEN, &keylen), + OSSL_PARAM_construct_end(), OSSL_PARAM_construct_end()}; + if (rc2bits) + p[1] = OSSL_PARAM_construct_size_t(OSSL_CIPHER_PARAM_RC2_KEYBITS, &bits); + EVP_CIPHER_CTX_set_params(cc, p); + } else if (rc2bits) { + OSSL_PARAM p[2] = {OSSL_PARAM_construct_size_t(OSSL_CIPHER_PARAM_RC2_KEYBITS, &bits), OSSL_PARAM_construct_end()}; + EVP_CIPHER_CTX_set_params(cc, p); + } + EVP_CIPHER_CTX_set_padding(cc, 0); + EVP_CipherInit_ex2(cc, nullptr, key.data(), nullptr, 1, nullptr); + Bytes out(in.size() + 16); + int n = 0, f = 0; + EVP_CipherUpdate(cc, out.data(), &n, in.data(), int(in.size())); + EVP_CipherFinal_ex(cc, out.data() + n, &f); + out.resize(size_t(n + f)); + EVP_CIPHER_CTX_free(cc); + EVP_CIPHER_free(c); + return out; + }; + auto ecb = [](legacy::BlockCipher &bc, const Bytes &in) { + Bytes out(in.size()); + for (size_t i = 0; i < in.size(); i += 8) + bc.encrypt(in.data() + i, out.data() + i); + return out; + }; + for (int round = 0; round < 20; ++round) { + Bytes data = random_bytes(64); + Bytes k8 = random_bytes(8), k16 = random_bytes(16), k24 = random_bytes(24); + CHECK(ecb(*legacy::make_des(k8.data()), data) == ref("DES-ECB", k8, 0, data)); + CHECK(ecb(*legacy::make_des3(k16.data(), 16), data) == ref("DES-EDE", k16, 0, data)); + CHECK(ecb(*legacy::make_des3(k24.data(), 24), data) == ref("DES-EDE3", k24, 0, data)); + CHECK(ecb(*legacy::make_idea(k16.data()), data) == ref("IDEA-ECB", k16, 0, data)); + size_t rl = size_t(1 + round % 16); + Bytes kr = random_bytes(rl); + CHECK(ecb(*legacy::make_rc2(kr.data(), kr.size(), unsigned(rl * 8)), data) == ref("RC2-ECB", kr, unsigned(rl * 8), data)); + Bytes rc4out(data.size()); + legacy::rc4(kr.data(), kr.size(), data.data(), rc4out.data(), data.size()); + CHECK(rc4out == ref("RC4", kr, 0, data)); + } + OSSL_LIB_CTX_free(ctx); +} diff --git a/port/secude/tests/test_encode.cpp b/port/secude/tests/test_encode.cpp new file mode 100644 index 000000000..ea1fa2aa5 --- /dev/null +++ b/port/secude/tests/test_encode.cpp @@ -0,0 +1,145 @@ +#include "der.hpp" +#include "testing.hpp" + +using namespace compat; + +namespace { + +std::string str(char *s) +{ + std::string r = s ? s : "(null)"; + aux_free(s); + return r; +} + +} // namespace + +TEST(encode_octetstring_and_printable) +{ + char data[] = "hello"; + OctetString in{5, data}; + OctetString *e = e_OctetString(&in); + CHECK_HEX(e->octets, e->noctets, "040568656c6c6f"); + OctetString *d = d_OctetString(e); + CHECK_HEX(d->octets, d->noctets, "68656c6c6f"); + aux_free_OctetString(&e); + aux_free_OctetString(&d); + CHECK(e == nullptr); + + char name[] = "[SideChannelAttack][Bob][RSA-512][1152179494][PIN=1234]"; + OctetString *p = e_PrintableString(name); + CHECK_EQ(uint8_t(p->octets[0]), uint8_t(0x13)); + CHECK_EQ(str(d_PrintableString(p)), std::string(name)); + aux_free_OctetString(&p); +} + +TEST(encode_keybits_keyinfo_algid) +{ + uint8_t n[] = {0xc0, 0x01, 0x02}, ev[] = {0x01, 0x00, 0x01}; + KeyBits kb{}; + kb.part1 = {3, reinterpret_cast(n)}; + kb.part2 = {3, reinterpret_cast(ev)}; + BitString *bs = e_KeyBits(&kb); + CHECK_HEX(bs->bits, bs->nbits / 8, "300b020400c001020203010001"); + KeyBits *back = d_KeyBits(bs); + CHECK_EQ(back->choice, 2); + CHECK_HEX(back->part1.octets, back->part1.noctets, "c00102"); + CHECK_HEX(back->part2.octets, back->part2.noctets, "010001"); + aux_free_KeyBits(&back); + + KeyInfo ki{}; + ki.subjectAI = aux_cpy_AlgId(&rsa_aid); + *static_cast(ki.subjectAI->param) = 1024; + CHECK(aux_cpy2_BitString(&ki.subjectkey, bs) == 0); + OctetString *enc = e_KeyInfo(&ki); + KeyInfo *dec = d_KeyInfo(enc); + CHECK_EQ(aux_cmp_ObjId(dec->subjectAI->objid, rsa_aid.objid), 0); + CHECK_EQ(*static_cast(dec->subjectAI->param), 1024u); + CHECK_EQ(str(aux_sprint_AlgId(nullptr, dec->subjectAI)), std::string("Algorithm RSA (OID 2.5.8.1.1), Keysize = 1024")); + CHECK_EQ(str(aux_sprint_AlgId(nullptr, &sha1WithRsaEncryption_aid)), + std::string("Algorithm sha1WithRsaEncryption (OID 1.2.840.113549.1.1.5), NULL")); + CHECK_EQ(aux_ObjId2AlgEnc(dec->subjectAI->objid), SECUDE_ALG_RSA); + CHECK_EQ(aux_ObjId2AlgEnc(dsa_aid.objid), SECUDE_ALG_DSA); + CHECK_EQ(aux_ObjId2AlgEnc(idea_aid.objid), IDEA); + CHECK_EQ(rsa_aid.objid->oid_nelem, 5); + CHECK_EQ(dsa_aid.objid->oid_nelem, 6); + aux_free_KeyInfo(&dec); + aux_free_OctetString(&enc); + aux_free2_KeyInfo(&ki); + aux_free_BitString(&bs); + + OctetString *a = e_AlgId(&md5WithRsaEncryption_aid); + CHECK_HEX(a->octets, a->noctets, "300d06092a864886f70d0101040500"); + aux_free_OctetString(&a); + a = e_AlgId(&dsaWithSHA1_aid); + CHECK_HEX(a->octets, a->noctets, "300706052b0e03021b"); + aux_free_OctetString(&a); + CHECK(aux_Name2AlgId(const_cast("desCBC_pad")) == &desCBC_pad_aid); +} + +TEST(encode_distinguished_names) +{ + const char *text = "CN=Bob SideChannelAttack [1152179494], DC=cryptool, DC=org"; + DName *dn = aux_Name2DName(const_cast(text)); + CHECK(dn != nullptr); + Bytes der_bytes = enc_dname(dn); + // most significant RDN first in DER: DC=org + CHECK_EQ(testing::hex(der_bytes.data(), der_bytes.size()).find("060a0992268993f22c6401191603" "6f7267"), size_t(12)); + CHECK_EQ(str(aux_DName2Name(dn)), std::string(text)); + CHECK_EQ(str(aux_DName2Attr(dn, const_cast("CN"))), std::string("Bob SideChannelAttack [1152179494]")); + DName *other = aux_Name2DName(const_cast("cn=bob sidechannelattack [1152179494],DC=cryptool,DC=org")); + CHECK_EQ(aux_cmp_DName(dn, other), 0); + DName *diff = aux_Name2DName(const_cast("CN=Alice, DC=cryptool, DC=org")); + CHECK(aux_cmp_DName(dn, diff) != 0); + aux_free_DName(&dn); + aux_free_DName(&other); + aux_free_DName(&diff); + CHECK(dn == nullptr); +} + +TEST(encode_latin1_unicode) +{ + OctetString *u = aux_latin1_to_unicode(const_cast("A\xe4"), TRUE); + CHECK_HEX(u->octets, u->noctets, "004100e40000"); + aux_free_OctetString(&u); +} + +TEST(encode_asn1_dump) +{ + Bytes good = der::seq({der::integer(5), der::oid(oids::sha1), der::octet_string(der::seq({der::null()}))}); + OctetString o{sec_uint4(good.size()), reinterpret_cast(good.data())}; + std::string dump = str(sdumpasn(&o, 0, 0)); + CHECK(dump.find("SEQUENCE {") != std::string::npos); + CHECK(dump.find("1.3.14.3.2.26 (sha1)") != std::string::npos); + CHECK(dump.find("encapsulates") != std::string::npos); + CHECK(dump.find("FATAL") == std::string::npos); + + Bytes junk = good; + junk.push_back(0); + o = {sec_uint4(junk.size()), reinterpret_cast(junk.data())}; + CHECK(str(sdumpasn(&o, 0, 0)).find("junk at end of PDU") != std::string::npos); + char text[] = "plain text, not ASN.1"; + o = {sec_uint4(std::strlen(text)), text}; + CHECK(str(sdumpasn(&o, 0, 0)).find("FATAL: decoding failed!") != std::string::npos); + + char buf[] = "0123456789abcdefXYZ"; + std::string hexd = str(aux_sxdump(nullptr, buf, 19, 0)); + CHECK(hexd.find("30313233 34353637 38396162 63646566") != std::string::npos); + CHECK(hexd.find("0123456789abcdef") != std::string::npos); +} + +TEST(memory_safety_of_free_functions) +{ + // CrypTool passes addresses of stack variables and globals to free functions + char *s = aux_cpy_String(const_cast("x")); + aux_free(&s); + aux_free_String(&s); + CHECK(s == nullptr); + ObjId *global = &SKnew_oid; + aux_free_ObjId(&global); + CHECK(global == nullptr); + CHECK_EQ(SKnew_oid.oid_nelem, 5); + AlgId *g = &rsa_aid; + aux_free_AlgId(&g); + CHECK(rsa_aid.param != nullptr); +} diff --git a/port/secude/tests/test_hash.cpp b/port/secude/tests/test_hash.cpp new file mode 100644 index 000000000..7a58aef26 --- /dev/null +++ b/port/secude/tests/test_hash.cpp @@ -0,0 +1,177 @@ +#include "legacy.hpp" +#include "testing.hpp" + +#include + +using namespace compat; + +namespace { + +struct Vector { + AlgId *aid; + const char *msg; + const char *digest; +}; + +const char kLong[] = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"; + +std::vector vectors() +{ + return { + {&md2_aid, "", "8350e5a3e24c153df2275c9f80692773"}, + {&md2_aid, "abc", "da853b0d3f88d99b30283a69e6ded6bb"}, + {&md2_aid, "message digest", "ab4f496bfb2a530b219ff33031fe06b0"}, + {&md2_aid, "abcdefghijklmnopqrstuvwxyz", "4e8ddff3650292ab5a4108c3aa47940b"}, + {&md4_aid, "", "31d6cfe0d16ae931b73c59d7e0c089c0"}, + {&md4_aid, "abc", "a448017aaf21d8525fc10ae87aa6729d"}, + {&md4_aid, "message digest", "d9130a8164549fe818874806e1c7014b"}, + {&md5_aid, "", "d41d8cd98f00b204e9800998ecf8427e"}, + {&md5_aid, "abc", "900150983cd24fb0d6963f7d28e17f72"}, + {&md5_aid, "message digest", "f96b697d7cb7938d525a2f31aaf161d0"}, + {&sha_aid, "abc", "0164b8a914cd2a5e74c4f7ff082c4d97f1edf880"}, + {&sha_aid, kLong, "d2516ee1acfa5baf33dfc1c471e438449ef134c8"}, + {&sha1_aid, "abc", "a9993e364706816aba3e25717850c26c9cd0d89d"}, + {&sha1_aid, kLong, "84983e441c3bd26ebaae4aa1f95129e5e54670f1"}, + {&ripemd160_aid, "", "9c1185a5c5e9fc54612808977ee8f548b2258d31"}, + {&ripemd160_aid, "abc", "8eb208f7e05d987a9b044a8e98c6b087f15a0bfc"}, + {&ripemd160_aid, kLong, "12a053384a9c0c88e405a06c27dcf49ada62eb2b"}, + }; +} + +OctetString ostr(const char *s) +{ + return OctetString{sec_uint4(std::strlen(s)), const_cast(s)}; +} + +} // namespace + +TEST(hash_known_answers_all) +{ + for (const auto &v : vectors()) { + OctetString in = ostr(v.msg); + OctetString out{0, nullptr}; + CHECK_EQ(sec_hash_all(&in, &out, v.aid, nullptr), 0); + CHECK_HEX(out.octets, out.noctets, v.digest); + aux_free2_OctetString(&out); + } +} + +TEST(hash_known_answers_incremental) +{ + for (const auto &v : vectors()) { + void *ctx = nullptr; + CHECK_EQ(sec_hash_init(&ctx, v.aid, nullptr), 0); + size_t n = std::strlen(v.msg); + for (size_t i = 0; i < n; i += 3) { + OctetString part{sec_uint4(std::min(3, n - i)), const_cast(v.msg + i)}; + CHECK_EQ(sec_hash_more(&ctx, &part), 0); + } + OctetString out{0, nullptr}; + CHECK_EQ(sec_hash_end(&ctx, &out), 0); + CHECK(ctx == nullptr); + CHECK_HEX(out.octets, out.noctets, v.digest); + aux_free2_OctetString(&out); + } +} + +TEST(hash_in_place_like_pkcs5_dialog) +{ + OctetString h{0, nullptr}; + OctetString in = ostr("abc"); + CHECK_EQ(sec_hash_all(&in, &h, &sha1_aid, nullptr), 0); + CHECK_EQ(sec_hash_all(&h, &h, &sha1_aid, nullptr), 0); + Bytes once = digest(Hash::SHA1, reinterpret_cast("abc"), 3); + Bytes twice = digest(Hash::SHA1, once.data(), once.size()); + CHECK_HEX(h.octets, h.noctets, testing::hex(twice.data(), twice.size())); +} + +TEST(hash_million_a) +{ + std::string a(1000000, 'a'); + OctetString in{sec_uint4(a.size()), &a[0]}; + OctetString out{0, nullptr}; + CHECK_EQ(sec_hash_all(&in, &out, &sha1_aid, nullptr), 0); + CHECK_HEX(out.octets, out.noctets, "34aa973cd4c4daa4f61eeb2bdbad27316534016f"); + CHECK_EQ(sec_hash_all(&in, &out, &md5_aid, nullptr), 0); + CHECK_HEX(out.octets, out.noctets, "7707d6ae4e027c70eea2a935c2296f21"); + CHECK_EQ(sec_hash_all(&in, &out, &ripemd160_aid, nullptr), 0); + CHECK_HEX(out.octets, out.noctets, "52783243c1697bdbe16d37f97f68f08325dc1528"); +} + +TEST(hash_context_apis) +{ + unsigned char d[16]; + SEC_MD2_CTX m2; + sec_MD2Init(&m2); + sec_MD2Update(&m2, (unsigned char *)"message ", 8); + sec_MD2Update(&m2, (unsigned char *)"digest", 6); + sec_MD2Final(d, &m2); + CHECK_HEX(d, 16, "ab4f496bfb2a530b219ff33031fe06b0"); + SEC_MD4_CTX m4; + sec_MD4Init(&m4); + sec_MD4Update(&m4, (unsigned char *)"abc", 3); + sec_MD4Final(d, &m4); + CHECK_HEX(d, 16, "a448017aaf21d8525fc10ae87aa6729d"); + SEC_MD5_CTX m5; + sec_MD5Init(&m5); + sec_MD5Update(&m5, (unsigned char *)"message digest", 14); + sec_MD5Final(d, &m5); + CHECK_HEX(d, 16, "f96b697d7cb7938d525a2f31aaf161d0"); + + SHS_INFO s; + unsigned char be[20]; + shsInit(&s); + shsUpdate(&s, (unsigned char *)"ab", 2); + shsUpdate(&s, (unsigned char *)"c", 1); + shsFinal(&s); + for (int i = 0; i < 5; ++i) + for (int j = 0; j < 4; ++j) + be[4 * i + j] = (unsigned char)(s.digest[i] >> (24 - 8 * j)); + CHECK_HEX(be, 20, "0164b8a914cd2a5e74c4f7ff082c4d97f1edf880"); + shs1Init(&s); + shs1Update(&s, (unsigned char *)kLong, sec_int4(std::strlen(kLong))); + shs1Final(&s); + for (int i = 0; i < 5; ++i) + for (int j = 0; j < 4; ++j) + be[4 * i + j] = (unsigned char)(s.digest[i] >> (24 - 8 * j)); + CHECK_HEX(be, 20, "84983e441c3bd26ebaae4aa1f95129e5e54670f1"); +} + +TEST(hash_builtin_matches_openssl) +{ + OSSL_LIB_CTX *ctx = OSSL_LIB_CTX_new(); + OSSL_PROVIDER_load(ctx, "default"); + bool legacy = OSSL_PROVIDER_load(ctx, "legacy") != nullptr; + struct { + Hash h; + const char *name; + } algs[] = {{Hash::MD4, "MD4"}, {Hash::MD5, "MD5"}, {Hash::SHA1, "SHA1"}, {Hash::RIPEMD160, "RIPEMD160"}}; + for (size_t len : {0u, 1u, 55u, 56u, 63u, 64u, 65u, 1000u}) { + Bytes msg = random_bytes(len); + for (const auto &a : algs) { + EVP_MD *md = EVP_MD_fetch(ctx, a.name, nullptr); + if (!md) { + CHECK(a.h == Hash::MD4 && !legacy); + continue; + } + unsigned char ref[EVP_MAX_MD_SIZE]; + unsigned int n = 0; + EVP_Digest(msg.data(), msg.size(), ref, &n, md, nullptr); + EVP_MD_free(md); + auto own = legacy::make_digest(a.h); + own->update(msg.data(), msg.size()); + Bytes d = own->final(); + CHECK_HEX(d.data(), d.size(), testing::hex(ref, n)); + } + } + OSSL_LIB_CTX_free(ctx); +} + +TEST(hash_unknown_algorithm_reports_error) +{ + OctetString in = ostr("abc"); + OctetString out{0, nullptr}; + CHECK_EQ(sec_hash_all(&in, &out, &desECB_aid, nullptr), -1); + CHECK_EQ(th_last_error(), EUNKNOWNALGID); + aux_free_error(); +} diff --git a/port/secude/tests/test_libec_binding.c b/port/secude/tests/test_libec_binding.c new file mode 100644 index 000000000..c5d5da150 --- /dev/null +++ b/port/secude/tests/test_libec_binding.c @@ -0,0 +1,29 @@ +/* Checks libec's statically initialised ECSecudeLib table (libec/sources/ECsecude.c). */ +#include + +#include "ECsecude.h" + +int secude_compat_test_libec(void) +{ + L_NUMBER a[MAXLGTH] = {1, 7}, b[MAXLGTH] = {1, 5}, m[MAXLGTH] = {1, 11}, r[MAXLGTH]; + OctetString msg, hash; + char abc[] = "abc"; + + if (!ECSecudeLib.arithmetic_mmult || !ECSecudeLib.sec_hash_all || !ECSecudeLib.sha1_aid || !ECSecudeLib.ripemd160_aid) + return 1; + if (ECSecudeLib.arithmetic_mmult(a, b, r, m) != 0 || r[0] != 1 || r[1] != 2) + return 2; + if (ECSecudeLib.arithmetic_comp(a, b) != 1 || ECSecudeLib.lngtouse(a) != 2) + return 3; + ECSecudeLib.arithmetic_div(a, b, r, b); + if (r[1] != 1 || b[1] != 2) + return 4; + msg.noctets = 3; + msg.octets = abc; + hash.noctets = 0; + hash.octets = NULL; + if (ECSecudeLib.sec_hash_all(&msg, &hash, ECSecudeLib.sha1_aid, NULL) != 0 || hash.noctets != 20 || + (unsigned char)hash.octets[0] != 0xa9) + return 5; + return 0; +} diff --git a/port/secude/tests/test_lnumber.cpp b/port/secude/tests/test_lnumber.cpp new file mode 100644 index 000000000..13f4bc5d2 --- /dev/null +++ b/port/secude/tests/test_lnumber.cpp @@ -0,0 +1,182 @@ +#include "bn.hpp" +#include "testing.hpp" + +#include + +using namespace compat; + +namespace { + +struct Ln { + L_NUMBER v[MAXGENL]; + Ln() { std::memset(v, 0, sizeof v); } +}; + +Ln ln_from(const BIGNUM *b) +{ + Ln l; + Bytes be = bn_bytes(b); + OctetString o{sec_uint4(be.size()), reinterpret_cast(be.data())}; + CHECK_EQ(aux_OctetString2LN2(l.v, &o), 0); + if (BN_is_negative(b)) + l.v[0] = L_NUMBER(~l.v[0] + 1); + return l; +} + +Bn bn_of(const Ln &l) +{ + Ln copy = l; + bool neg = copy.v[0] & 0x80000000u; + if (neg) + copy.v[0] = L_NUMBER(~copy.v[0] + 1); + OctetString *o = aux_LN2OctetString(copy.v, 0); + Bn b = bn_from(bytes_of(o)); + aux_free_OctetString(&o); + BN_set_negative(b.get(), neg); + return b; +} + +Bn rnd(int bits) +{ + Bn b = bn_new(); + if (bits > 0) + BN_rand(b.get(), bits, BN_RAND_TOP_ANY, BN_RAND_BOTTOM_ANY); + return b; +} + +bool same(const BIGNUM *a, const Ln &l) +{ + return BN_cmp(a, bn_of(l).get()) == 0; +} + +} // namespace + +TEST(lnumber_layout) +{ + uint8_t be[] = {0x01, 0x02, 0x03, 0x04, 0x05}; + OctetString o{5, reinterpret_cast(be)}; + L_NUMBER l[MAXLGTH]; + CHECK_EQ(aux_OctetString2LN2(l, &o), 0); + CHECK_EQ(l[0], L_NUMBER(2)); + CHECK_EQ(l[1], L_NUMBER(0x02030405)); + CHECK_EQ(l[2], L_NUMBER(0x01)); + CHECK_EQ(lngtouse(l), 32); + L_NUMBER zero[1] = {0}; + CHECK_EQ(lngtouse(zero), -1); + OctetString *back = aux_LN2OctetString(l, 8); + CHECK_HEX(back->octets, back->noctets, "0000000102030405"); + aux_free_OctetString(&back); + L_NUMBER big[3] = {2, 0, 0x01000000}; + back = aux_LN2OctetString(big, 2); + CHECK_HEX(back->octets, back->noctets, "0100000000000000"); + aux_free_OctetString(&back); +} + +TEST(lnumber_bitstring_and_random) +{ + uint8_t bits[] = {0xab, 0xcd}; + BitString b{12, reinterpret_cast(bits)}; + L_NUMBER l[MAXLGTH]; + CHECK_EQ(aux_BitString2LN2(l, &b), 0); + CHECK_EQ(l[0], L_NUMBER(1)); + CHECK_EQ(l[1], L_NUMBER(0xabc)); + for (int i = 0; i < 50; ++i) { + CHECK_EQ(rndm(1, l, 0), 0); + CHECK(l[0] == 0 || (l[0] == 1 && l[1] == 1)); + CHECK_EQ(rndm(100, l, 0), 0); + CHECK(lngtouse(l) < 100); + } +} + +TEST(lnumber_matches_bignum) +{ + BN_CTX *ctx = BN_CTX_new(); + for (int round = 0; round < 300; ++round) { + int sizes[] = {0, 1, 31, 32, 33, 64, 160, 239, 512, 1024, 2048}; + Bn a = rnd(sizes[round % 11]), b = rnd(sizes[(round / 11) % 11]); + Bn m = rnd(239 + round % 300); + BN_set_bit(m.get(), 0); + if (BN_is_zero(m.get()) || BN_is_one(m.get())) + BN_set_word(m.get(), 65537); + Ln la = ln_from(a.get()), lb = ln_from(b.get()), lm = ln_from(m.get()), r, q; + Bn e = bn_new(); + + CHECK_EQ(arithmetic_comp(la.v, lb.v), BN_cmp(a.get(), b.get())); + arithmetic_add(la.v, lb.v, r.v); + BN_add(e.get(), a.get(), b.get()); + CHECK(same(e.get(), r)); + arithmetic_sub(la.v, lb.v, r.v); + BN_sub(e.get(), a.get(), b.get()); + CHECK(same(e.get(), r)); + arithmetic_mult(la.v, lb.v, r.v); + BN_mul(e.get(), a.get(), b.get(), ctx); + CHECK(same(e.get(), r)); + if (!BN_is_zero(b.get())) { + Bn eq = bn_new(); + arithmetic_div(la.v, lb.v, q.v, r.v); + BN_div(eq.get(), e.get(), a.get(), b.get(), ctx); + CHECK(same(eq.get(), q)); + CHECK(same(e.get(), r)); + } + arithmetic_shift(la.v, 37, r.v); + BN_lshift(e.get(), a.get(), 37); + CHECK(same(e.get(), r)); + arithmetic_shift(la.v, -5, r.v); + BN_rshift(e.get(), a.get(), 5); + CHECK(same(e.get(), r)); + + CHECK_EQ(arithmetic_madd(la.v, lb.v, r.v, lm.v), 0); + BN_mod_add(e.get(), a.get(), b.get(), m.get(), ctx); + CHECK(same(e.get(), r)); + CHECK_EQ(arithmetic_msub(la.v, lb.v, r.v, lm.v), 0); + BN_mod_sub(e.get(), a.get(), b.get(), m.get(), ctx); + CHECK(same(e.get(), r)); + CHECK_EQ(arithmetic_mmult(la.v, lb.v, r.v, lm.v), 0); + BN_mod_mul(e.get(), a.get(), b.get(), m.get(), ctx); + CHECK(same(e.get(), r)); + CHECK_EQ(arithmetic_mexp(la.v, lb.v, r.v, lm.v), 0); + BN_mod_exp(e.get(), a.get(), b.get(), m.get(), ctx); + CHECK(same(e.get(), r)); + Bn inv = bn_new(); + ERR_set_mark(); + bool invertible = BN_mod_inverse(inv.get(), b.get(), m.get(), ctx) != nullptr; + ERR_pop_to_mark(); + int rc = arithmetic_mdiv(la.v, lb.v, r.v, lm.v); + if (invertible) { + CHECK_EQ(rc, 0); + BN_mod_mul(e.get(), a.get(), inv.get(), m.get(), ctx); + CHECK(same(e.get(), r)); + } else { + CHECK_EQ(rc, -1); + } + } + aux_free_error(); + BN_CTX_free(ctx); +} + +TEST(lnumber_aliasing_like_libec) +{ + Bn a = rnd(300), m = rnd(301); + BN_set_bit(m.get(), 300); + Ln la = ln_from(a.get()), lm = ln_from(m.get()); + arithmetic_mmult(la.v, la.v, la.v, lm.v); + Bn e = bn_new(); + BN_CTX *ctx = BN_CTX_new(); + BN_mod_mul(e.get(), a.get(), a.get(), m.get(), ctx); + CHECK(same(e.get(), la)); + Ln n = ln_from(e.get()), tmp; + arithmetic_div(n.v, lm.v, tmp.v, n.v); + BN_nnmod(e.get(), e.get(), m.get(), ctx); + CHECK(same(e.get(), n)); + BN_CTX_free(ctx); +} + +TEST(lnumber_zero_result_clears_first_word) +{ + // libec's ln_to_string reads rem[1] without looking at rem[0] + L_NUMBER a[MAXLGTH] = {1, 32}, b[MAXLGTH] = {1, 16}, q[MAXLGTH], r[MAXLGTH] = {1, 0xdeadbeef}; + arithmetic_div(a, b, q, r); + CHECK_EQ(q[1], L_NUMBER(2)); + CHECK_EQ(r[0], L_NUMBER(0)); + CHECK_EQ(r[1], L_NUMBER(0)); +} diff --git a/port/secude/tests/test_pkcs12.cpp b/port/secude/tests/test_pkcs12.cpp new file mode 100644 index 000000000..394264d7a --- /dev/null +++ b/port/secude/tests/test_pkcs12.cpp @@ -0,0 +1,177 @@ +#include "bn.hpp" +#include "testing.hpp" + +#include +#include + +using namespace compat; + +namespace { + +std::string root() { return testing::scratch_dir() + "/p12"; } + +// CrypTool's PKCS12_encode (SecudeTools.cpp) +OctetString *export_pse(PSE pse, OctetString *PIN, int encrypt_priv_key) +{ + P12_Safe P12safe; + std::memset(&P12safe, 0, sizeof(P12safe)); + P12safe.version = 3; + P12safe.iterationCount = 2048; + P12safe.alg_oid.mac = aux_cpy_ObjId(&sha1_oid); + P12safe.alg_oid.enc = aux_cpy_ObjId(&pbeWithSHA1And40BitRC2CBC_oid); + P12safe.alg_oid.espvk = aux_cpy_ObjId(&pbeWithSHA1AndDES3xCBC_oid); + CHECK(sec_onekeypaironly(af_get_PSESel(pse, nullptr))); + ObjId oid; + Certificate *cert = (Certificate *)af_pse_get(pse, const_cast("Cert"), &oid); + KeyInfo *key = (KeyInfo *)af_pse_get(pse, const_cast("SKnew"), &oid); + CHECK(cert && key); + char oarr[4] = {0x01, 0x00, 0x00, 0x00}; + OctetString ostr{4, oarr}; + OctetString *lkid = aux_cpy_OctetString(&ostr); + + P12_Bag *keybag = (P12_Bag *)aux_malloc(sizeof(P12_Bag)); + std::memset(keybag, 0, sizeof(P12_Bag)); + keybag->int_encryption = encrypt_priv_key; + keybag->type = P12_bc_key; + keybag->friendlyName = aux_latin1_to_unicode(const_cast("name"), FALSE); + keybag->localKeyID = aux_cpy_OctetString(lkid); + keybag->content.key = aux_create_PrivateKeyInfo(key, cert, nullptr); + CHECK(keybag->content.key != nullptr); + P12_Bag *certbag = (P12_Bag *)aux_malloc(sizeof(P12_Bag)); + std::memset(certbag, 0, sizeof(P12_Bag)); + certbag->int_encryption = TRUE; + certbag->type = P12_bc_cert; + certbag->localKeyID = lkid; + certbag->content.cert = cert; + keybag->next = certbag; + P12safe.bags = keybag; + aux_free_KeyInfo(&key); + + PKRoot *pkroot = af_pse_get_PKRoot(pse); + if (pkroot) { + P12_Bag *rootbag = (P12_Bag *)aux_malloc(sizeof(P12_Bag)); + std::memset(rootbag, 0, sizeof(P12_Bag)); + rootbag->int_encryption = TRUE; + rootbag->type = P12_bc_cert; + rootbag->content.cert = af_PKRoot2Protocert(pkroot); + certbag->next = rootbag; + aux_free_PKRoot(&pkroot); + } + OctetString *P12container = aux_alloc_OctetString(); + CHECK_EQ(pkcs12_encode(&P12safe, PIN, P12container), 0); + return P12container; +} + +} // namespace + +TEST(pkcs12_export_import_round_trip) +{ + std::string dir = root(); + CHECK_EQ(secude_compat_create_sample_keystore(dir.c_str(), 0), 0); + std::string bob = dir + "/PSE/[SideChannelAttack][Bob][RSA-512][1152179494][PIN=1234].pse"; + PSE pse = af_open(const_cast(bob.c_str()), nullptr, const_cast("1234"), nullptr); + CHECK(pse != nullptr); + OctetString *pw = aux_latin1_to_unicode(const_cast("secret"), TRUE); + OctetString *p12 = export_pse(pse, pw, TRUE); + CHECK(p12 && p12->noctets > 500); + char *dump = sdumpasn(p12, 0, 0); + CHECK(std::string(dump).find("FATAL") == std::string::npos); + aux_free_String(&dump); + + // CrypTool's PKCS12_import into a new PSE + P12_Safe safe; + std::memset(&safe, 0, sizeof safe); + Boolean verified = 0; + CHECK_EQ(pkcs12_decode(p12, pw, &safe, &verified), 0); + CHECK(verified); + CHECK_EQ(safe.iterationCount, 2048); + CHECK_EQ(aux_cmp_ObjId(safe.alg_oid.enc, &pbeWithSHA1And40BitRC2CBC_oid), 0); + int keys = 0, certs = 0; + PrivateKeyInfo *pki = nullptr; + for (P12_Bag *b = safe.bags; b; b = b->next) { + if (b->type == P12_bc_key) { + ++keys; + pki = b->content.key; + CHECK(b->localKeyID && b->friendlyName); + } else if (b->type == P12_bc_cert) { + ++certs; + } + } + CHECK_EQ(keys, 1); + CHECK_EQ(certs, 2); + CHECK_EQ(aux_ObjId2AlgEnc(pki->privateKeyAlgorithm->objid), SECUDE_ALG_RSA); + RSAPrivateKey *rsa = d_RSAPrivateKey(pki->privateKey); + CHECK(rsa && rsa->pubex.noctets == 3); + KeyInfo *orig = (KeyInfo *)af_pse_get(pse, const_cast("SKnew"), nullptr); + KeyBits *kb = d_KeyBits(&orig->subjectkey); + CHECK(bytes_of(&kb->part1) == bytes_of(&rsa->prime1)); + CHECK(bytes_of(&kb->part2) == bytes_of(&rsa->prime2)); + aux_free_KeyBits(&kb); + aux_free_KeyInfo(&orig); + aux_free_RSAPrivateKey(&rsa); + + OctetString *wrong = aux_latin1_to_unicode(const_cast("wrong"), TRUE); + P12_Safe other; + std::memset(&other, 0, sizeof other); + CHECK_EQ(pkcs12_decode(p12, wrong, &other, &verified), -1); + CHECK(!verified); + aux_free_error(); + + // the same file is readable by OpenSSL (MAC and shrouded key do not need the legacy provider) + const unsigned char *p = reinterpret_cast(p12->octets); + PKCS12 *ossl = d2i_PKCS12(nullptr, &p, long(p12->noctets)); + CHECK(ossl != nullptr); + CHECK_EQ(PKCS12_verify_mac(ossl, "secret", -1), 1); + PKCS12_free(ossl); + + aux_free_OctetString(&wrong); + aux_free_OctetString(&p12); + aux_free_OctetString(&pw); + af_close(pse); +} + +TEST(pkcs12_import_openssl_default_file) +{ + // RSA key and certificate created by OpenSSL 3 with its defaults (PBES2, AES-256, SHA-256 MAC) + EVP_PKEY *pkey = EVP_RSA_gen(1024); + X509 *x = X509_new(); + X509_set_version(x, 2); + ASN1_INTEGER_set(X509_get_serialNumber(x), 7); + X509_gmtime_adj(X509_getm_notBefore(x), 0); + X509_gmtime_adj(X509_getm_notAfter(x), 3600); + X509_set_pubkey(x, pkey); + X509_NAME *name = X509_get_subject_name(x); + X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, reinterpret_cast("OpenSSL user"), -1, -1, 0); + X509_set_issuer_name(x, name); + X509_sign(x, pkey, EVP_sha256()); + PKCS12 *p12 = PKCS12_create("pass", "friendly", pkey, x, nullptr, 0, 0, 0, 0, 0); + CHECK(p12 != nullptr); + unsigned char *der = nullptr; + int len = i2d_PKCS12(p12, &der); + OctetString in{sec_uint4(len), reinterpret_cast(der)}; + OctetString *pw = aux_latin1_to_unicode(const_cast("pass"), TRUE); + P12_Safe safe; + std::memset(&safe, 0, sizeof safe); + Boolean verified = 0; + CHECK_EQ(pkcs12_decode(&in, pw, &safe, &verified), 0); + CHECK(verified); + int keys = 0, certs = 0; + for (P12_Bag *b = safe.bags; b; b = b->next) { + if (b->type == P12_bc_key) { + ++keys; + RSAPrivateKey *rsa = d_RSAPrivateKey(b->content.key->privateKey); + CHECK(rsa != nullptr); + aux_free_RSAPrivateKey(&rsa); + } else if (b->type == P12_bc_cert) { + ++certs; + CHECK_EQ(std::string(aux_DName2Name(b->content.cert->tbs->subject)), std::string("CN=OpenSSL user")); + } + } + CHECK_EQ(keys, 1); + CHECK_EQ(certs, 1); + aux_free_OctetString(&pw); + OPENSSL_free(der); + PKCS12_free(p12); + X509_free(x); + EVP_PKEY_free(pkey); +} diff --git a/port/secude/tests/test_pse.cpp b/port/secude/tests/test_pse.cpp new file mode 100644 index 000000000..27a40f80f --- /dev/null +++ b/port/secude/tests/test_pse.cpp @@ -0,0 +1,511 @@ +// Integration tests replaying the call sequences of the CrypTool dialogs. +#include "bn.hpp" +#include "testing.hpp" + +#include +#include +#include +#include + +using namespace compat; + +namespace { + +const char kCaPin[] = SECUDE_COMPAT_CA_PIN; + +struct Store { + std::string root, pse_dir, ca_dir, ca_pse; +}; + +const Store &store() +{ + static Store s = [] { + Store st; + st.root = testing::scratch_dir() + "/keystore"; + st.pse_dir = st.root + "/PSE"; + st.ca_dir = st.pse_dir + "/PSECA"; + st.ca_pse = st.ca_dir + "/capse.cse"; + if (secude_compat_create_sample_keystore(st.root.c_str(), 0) != 0) + testing::report_failure(__FILE__, __LINE__, std::string("keystore creation: ") + th_get_last_error_text()); + return st; + }(); + return s; +} + +char *cstr(const std::string &s) { return const_cast(s.c_str()); } + +PSE open_ca() +{ + return af_open(cstr(store().ca_pse), cstr(store().ca_dir), const_cast(kCaPin), nullptr); +} + +std::string read_text(const std::string &path) +{ + std::ifstream f(path, std::ios::binary); + std::stringstream ss; + ss << f.rdbuf(); + return ss.str(); +} + +std::string text_of(char *s) +{ + std::string r = s ? s : ""; + aux_free_String(&s); + return r; +} + +// CDlgKeyAsymGeneration::CreateAsymKeys for RSA/DSA +std::string create_user(const std::string &name, const std::string &first, bool dsa, int bits, const std::string &time, + const char *pin) +{ + std::string key_id = "[" + name + "][" + first + "][" + (dsa ? "DSA-" : "RSA-") + std::to_string(bits) + "][" + time + "]"; + std::string path = store().pse_dir + "/" + key_id + ".pse"; + PSE pse = af_create(cstr(path), nullptr, const_cast(pin), nullptr, TRUE); + CHECK(pse != nullptr); + Key keyinfo{}; + keyinfo.alg = dsa ? &dsa_aid : &rsa_aid; + keyinfo.key_size = bits; + CHECK_EQ(af_gen_key(pse, &keyinfo, SIGNATURE, 1), 0); + char serial_text[] = "000001"; + OctetString SNummer{6, serial_text}; + std::string dn_text = "CN=" + first + " " + name + " [" + time + "], DC=cryptool, DC=org"; + DName *DisName = aux_Name2DName(cstr(dn_text)); + AlgId *alg = dsa ? &dsaWithSHA1_aid : &sha1WithRSASignature_aid; + Certificate *Zert = af_create_Certificate(pse, keyinfo.key, alg, const_cast("SKnew"), DisName, &SNummer, + nullptr, nullptr, TRUE, nullptr); + CHECK(Zert != nullptr); + + SEQUENCE_OF_Extension extensions; + extensions.element = (v3Extension *)aux_malloc(sizeof(v3Extension)); + static ObjId oid; + static unsigned int oid_n[6] = {2, 206, 5, 4, 3, 2}; + oid.oid_nelem = 6; + oid.oid_elements = oid_n; + extensions.element->extnId = &oid; + extensions.element->critical = false; + extensions.element->extnDERcode = e_PrintableString(cstr(key_id)); + extensions.next = nullptr; + CertExtensions cert_ext; + std::memset(&cert_ext, 0, sizeof(CertExtensions)); + cert_ext.nonSupported = &extensions; + Zert->tbs->extensions = &cert_ext; + + PSE PseHandle2 = open_ca(); + CHECK(PseHandle2 != nullptr); + SerialNumber *serial = sec_random_ostr(8, 1); + CHECK(af_cadb_get_Certificate(PseHandle2, serial) == nullptr); + aux_free_error(); + CHECK_EQ(af_pse_update_SerialNumber(PseHandle2, serial), 0); + Certificate *Zert2 = af_certify(PseHandle2, Zert, nullptr, &sha1WithRSASignature_aid, nullptr); + CHECK(Zert2 != nullptr); + CHECK(Zert2->tbs->serialnumber->noctets >= 1); + CHECK_EQ(af_cadb_add_Certificate(PseHandle2, SIGNATURE, Zert2), 0); + CHECK_EQ(af_cadb_add_Certificate(PseHandle2, SIGNATURE, Zert2), -1); + aux_free_error(); + PKRoot *pkroot = af_pse_get_PKRoot(PseHandle2); + CHECK(pkroot != nullptr); + CHECK_EQ(af_pse_update_PKRoot(pse, pkroot), 0); + aux_free_PKRoot(&pkroot); + CHECK_EQ(af_pse_update(pse, const_cast("Cert"), Zert2, *&Cert_OID), 0); + af_close(PseHandle2); + aux_free_DName(&DisName); + af_close(pse); + aux_free_OctetString(&serial); + aux_free_Certificate(&Zert2); + aux_free_OctetString(&extensions.element->extnDERcode); + aux_free(extensions.element); + Zert->tbs->extensions = nullptr; + aux_free_Certificate(&Zert); + aux_free_KeyInfo(&keyinfo.key); + return key_id; +} + +Certificate *ca_lookup(const std::string &dn) +{ + PSE ca = open_ca(); + SET_OF_IssuedCertificate *Liste = af_cadb_get_user(ca, cstr(dn)); + CHECK(Liste != nullptr); + Certificate *Zert = Liste ? af_cadb_get_Certificate(ca, Liste->element->serial) : nullptr; + CHECK(Zert != nullptr); + aux_free_SET_OF_IssuedCertificate(&Liste); + af_close(ca); + return Zert; +} + +} // namespace + +TEST(pse_create_open_and_pin) +{ + std::string path = testing::scratch_dir() + "/test.pse"; + PSE pse = af_create(cstr(path), nullptr, const_cast("1234"), nullptr, TRUE); + CHECK(pse != nullptr); + CHECK_EQ(std::string(pse->pse_name), path); + CHECK(af_create(cstr(path), nullptr, const_cast("1234"), nullptr, TRUE) == nullptr); + CHECK_EQ(th_last_error(), EPSEALREADYEXISTING); + CHECK_EQ(af_close(pse), 0); + + CHECK(af_open(cstr(path), nullptr, const_cast("4321"), nullptr) == nullptr); + CHECK_EQ(th_last_error(), EPIN); + CHECK(af_open(cstr(path), cstr(store().ca_dir), const_cast("NULL"), nullptr) == nullptr); + CHECK_EQ(th_last_error(), EPIN); + pse = af_open(cstr(path), nullptr, const_cast("1234"), nullptr); + CHECK(pse != nullptr); + af_close(pse); + + std::string junk = testing::scratch_dir() + "/junk.pse"; + std::ofstream(junk) << "not a PSE"; + CHECK(af_open(cstr(junk), nullptr, const_cast("1234"), nullptr) == nullptr); + CHECK(th_last_error() != EPIN); + CHECK(af_open(cstr(testing::scratch_dir() + "/missing.pse"), nullptr, const_cast("1"), nullptr) == nullptr); + CHECK_EQ(th_last_error(), EPSENOTEXISTING); + aux_free_error(); + + OctetString *raw = aux_file2OctetString(cstr(path)); + std::string dump = text_of(sdumpasn(raw, 0, 0)); + CHECK(dump.find("FATAL") == std::string::npos && dump.find("junk") == std::string::npos); + aux_free_OctetString(&raw); + CHECK(aux_file2OctetString(cstr(testing::scratch_dir() + "/nothing")) == nullptr); + CHECK_EQ(th_last_error(), EFILENOTEXISTING); + aux_free_error(); +} + +TEST(pse_objects_and_selectors) +{ + std::string path = testing::scratch_dir() + "/objects.pse"; + PSE pse = af_create(cstr(path), nullptr, const_cast("pin"), nullptr, FALSE); + char raw[] = "\x01\x02\x03"; + OctetString value{3, raw}; + OctetString *ostr = e_OctetString(&value); + ObjId *uid_before = Uid_OID; + CHECK_EQ(af_pse_update(pse, const_cast("EcPrivKey"), ostr, *&Uid_OID), 0); + aux_free_OctetString(&ostr); + af_close(pse); + + pse = af_open(cstr(path), nullptr, const_cast("pin"), nullptr); + OctetString *got = (OctetString *)af_pse_get(pse, const_cast("EcPrivKey"), Uid_OID); + CHECK(got != nullptr); + OctetString *inner = d_OctetString(got); + CHECK_HEX(inner->octets, inner->noctets, "010203"); + L_NUMBER l[MAXLGTH]; + CHECK_EQ(aux_OctetString2LN2(l, inner), 0); + CHECK_EQ(l[1], L_NUMBER(0x010203)); + CHECK(Uid_OID == uid_before); + CHECK_EQ(Uid_OID->oid_nelem, 5); + + PSESel *sel = af_get_PSESel(pse, nullptr); + CHECK_EQ(sec_onekeypaironly(sel), FALSE); + sel->object = aux_cpy_String(const_cast(Toc_name)); + OctetString toc_der{0, nullptr}; + CHECK_EQ(sec_read(sel, &toc_der), 0); + PSEToc *toc = d_PSEToc(&toc_der); + CHECK(toc && toc->obj && std::string(toc->obj->name) == "EcPrivKey"); + aux_free_PSEToc(&toc); + aux_free2_OctetString(&toc_der); + ObjId oid; + CHECK(af_pse_get(pse, const_cast("missing"), &oid) == nullptr); + CHECK_EQ(th_last_error(), EPSEOBJECTNOTEXISTING); + aux_free_error(); + aux_free_OctetString(&got); + aux_free_OctetString(&inner); + af_close(pse); +} + +TEST(pse_key_generation_certification_and_use) +{ + std::string id = create_user("Doe", "Alice", false, 768, "1700000000", "4711"); + Certificate *Zert = ca_lookup("CN=Alice Doe [1700000000], DC=cryptool, DC=org"); + CHECK_EQ(Zert->tbs->subjectPK->subjectAI->objid->oid_nelem, 5); + char *alg_name = aux_sprint_AlgId(nullptr, Zert->tbs->subjectPK->subjectAI); + CHECK_EQ(std::string(alg_name), std::string("Algorithm RSA (OID 2.5.8.1.1), Keysize = 768")); + aux_free(&alg_name); + aux_free_String(&alg_name); + + // private PSE name extension (used by the PKCS#12 import) + char *pse_name = nullptr; + for (SEQUENCE_OF_Extension *x = Zert->tbs->extensions ? Zert->tbs->extensions->nonSupported : nullptr; x; x = x->next) + if (x->element->extnId->oid_nelem == 6 && x->element->extnId->oid_elements[1] == 206) + pse_name = d_PrintableString(x->element->extnDERcode); + CHECK(pse_name && std::string(pse_name) == id); + aux_free(pse_name); + + // RsaEnc / RsaDec + std::string doc = "CrypTool RSA encryption round trip"; + OctetString in{sec_uint4(doc.size()), cstr(doc)}; + std::vector buf(doc.size() + doc.size() / 37 + 1025); + BitString out{0, buf.data()}; + Key Schluessel{}; + Schluessel.key = Zert->tbs->subjectPK; + Schluessel.alg = &rsa_aid; + PSE ca = open_ca(); + CHECK_EQ(af_encrypt_all(ca, &in, &out, &Schluessel, nullptr), 0); + af_close(ca); + CHECK_EQ(out.nbits, sec_uint4(96 * 8)); + OctetString *outOctet = aux_BString2OString(&out); + PSE user = af_open(cstr(store().pse_dir + "/" + id + ".pse"), nullptr, const_cast("4711"), nullptr); + CHECK(user != nullptr); + BitString cin{outOctet->noctets * 8, outOctet->octets}; + std::vector plain(cin.nbits / 8 + 256); + OctetString dec{0, plain.data()}; + Key priv{}; + priv.alg = &rsa_aid; + CHECK_EQ(af_decrypt_all(user, &cin, &dec, &priv), 0); + CHECK_EQ(dec.noctets, sec_uint4(95)); + CHECK_EQ(std::string(dec.octets, doc.size()), doc); + aux_free_OctetString(&outOctet); + + // Sign / Verify + Signature Signatur; + Signatur.signAI = &ripemd160WithRSASignature_aid; + user->options.af_sign_check_Validity = FALSE; + CHECK_EQ(af_sign_all(user, &in, &Signatur), 0); + Key keyinfo{}; + keyinfo.key = Zert->tbs->subjectPK; + CHECK_EQ(sec_verify_all(&in, &Signatur, &keyinfo, nullptr), 0); + int sig_bits = 0, enc_bits = 0; + AlgEnc sig_type = NoAlgEnc; + CHECK_EQ(af_pse_get_keysize(user, &sig_bits, &enc_bits, &sig_type), 0); + CHECK_EQ(sig_bits, 768); + CHECK_EQ(sig_type, SECUDE_ALG_RSA); + + // certificate display (CDlgKeyAsym / sprint_Certificate_with_key) + *(sec_uint4 *)secude_compat_symbol("print_cert_flag") = TBS | KEYINFO | VAL | ISSUER | ALG | SIGNAT | HSH | VER | SUBJECT | EXTENSIONS; + *(sec_uint4 *)secude_compat_symbol("print_keyinfo_flag") = ALGID | KEYBITS | PK; + std::string text = text_of(aux_sprint_Certificate(user, nullptr, Zert)); + CHECK(text.find("Modulus (768 bits)") != std::string::npos); + CHECK(text.find("Public exponent") != std::string::npos); + CHECK(text.find("CrypToolPSEName: " + id) != std::string::npos); + CHECK(text.find("Issuer: CN=CrypTool CA 2, DC=cryptool, DC=org") != std::string::npos); + + // PKRoot copied from the CA reconstructs the CA certificate + PKRoot *root = af_pse_get_PKRoot(user); + Certificate *proto = af_PKRoot2Protocert(root); + ca = open_ca(); + Certificate *ca_cert = af_pse_get_Certificate(ca, SIGNATURE, nullptr, nullptr); + OctetString *a = e_Certificate(proto), *b = e_Certificate(ca_cert); + CHECK(a->noctets == b->noctets && !std::memcmp(a->octets, b->octets, a->noctets)); + Key ca_key{}; + ca_key.key = ca_cert->tbs->subjectPK; + CHECK_EQ(sec_verify_all(Zert->tbs_DERcode, Zert->sig, &ca_key, nullptr), 0); + Certificates *certs = af_pse_get_Certificates(user, ENCRYPTION, nullptr); + CHECK(certs && certs->usercertificate && !certs->forwardpath); + aux_free_Certificates(&certs); + aux_free_OctetString(&a); + aux_free_OctetString(&b); + aux_free_Certificate(&proto); + aux_free_Certificate(&ca_cert); + aux_free_PKRoot(&root); + af_close(ca); + af_close(user); + aux_free2_BitString(&Signatur.signature); + aux_free_Certificate(&Zert); +} + +TEST(pse_dsa_user) +{ + std::string id = create_user("Dsa", "Dave", true, 512, "1700000001", "dsa"); + Certificate *Zert = ca_lookup("CN=Dave Dsa [1700000001], DC=cryptool, DC=org"); + CHECK_EQ(Zert->tbs->subjectPK->subjectAI->objid->oid_nelem, 6); + PSE user = af_open(cstr(store().pse_dir + "/" + id + ".pse"), nullptr, const_cast("dsa"), nullptr); + char msg[] = "signed by DSA"; + OctetString in{sec_uint4(std::strlen(msg)), msg}; + for (AlgId *alg : {&dsaWithSHA_aid, &dsaWithSHA1_aid}) { + Signature s; + s.signAI = alg; + CHECK_EQ(af_sign_all(user, &in, &s), 0); + Key k{}; + k.key = Zert->tbs->subjectPK; + CHECK_EQ(sec_verify_all(&in, &s, &k, nullptr), 0); + aux_free2_BitString(&s.signature); + } + af_close(user); + aux_free_Certificate(&Zert); +} + +TEST(pse_tutorial_flow_with_user_chosen_exponent) +{ + // CPSEDemo::CreatePSE / AccessPSE with p, q and e chosen by the user + Bn p = bn_new(), q = bn_new(), e = bn_word(17), n = bn_new(), one = bn_word(1), g = bn_new(), t = bn_new(); + BN_CTX *ctx = BN_CTX_new(); + do { + BN_generate_prime_ex(p.get(), 256, 0, nullptr, nullptr, nullptr); + BN_sub(t.get(), p.get(), one.get()); + BN_gcd(g.get(), t.get(), e.get(), ctx); + } while (!BN_is_one(g.get())); + do { + BN_generate_prime_ex(q.get(), 256, 0, nullptr, nullptr, nullptr); + BN_sub(t.get(), q.get(), one.get()); + BN_gcd(g.get(), t.get(), e.get(), ctx); + } while (!BN_is_one(g.get())); + BN_mul(n.get(), p.get(), q.get(), ctx); + BN_CTX_free(ctx); + + KeyBits keybits; + std::memset(&keybits, 0, sizeof(KeyBits)); + Bytes pb = bn_bytes(p.get()), qb = bn_bytes(q.get()), nb = bn_bytes(n.get()), eb = bn_bytes(e.get()); + keybits.part1 = {sec_uint4(pb.size()), reinterpret_cast(pb.data())}; + keybits.part2 = {sec_uint4(qb.size()), reinterpret_cast(qb.data())}; + BitString *bitstring = e_KeyBits(&keybits); + KeyInfo keyinfo; + CHECK_EQ(aux_cpy2_BitString(&keyinfo.subjectkey, bitstring), 0); + aux_free_BitString(&bitstring); + keyinfo.subjectAI = aux_cpy_AlgId(&rsa_aid); + *reinterpret_cast(keyinfo.subjectAI->param) = 512; + + std::string name = store().pse_dir + "/[Tutorial][Tom][RSA-512][1].pse"; + PSE m_hPSE = af_create(cstr(name), nullptr, const_cast("tut"), nullptr, TRUE); + PSE_Sel *psesel = af_get_PSESel(m_hPSE, static_cast(0)); + psesel->object = aux_cpy_String(const_cast(SKnew_name)); + psesel->object_type = &SKnew_oid; + OctetString *octetstring = e_KeyInfo(&keyinfo); + aux_free2_KeyInfo(&keyinfo); + CHECK_EQ(sec_write_PSE(psesel, octetstring), 0); + aux_free_OctetString(&octetstring); + + char serial_text[] = "000001"; + OctetString osSerial{6, serial_text}; + DName *dn = aux_Name2DName(const_cast("CN=Tom Tutorial [1], DC=cryptool, DC=org")); + std::memset(&keybits, 0, sizeof(KeyBits)); + keybits.part1 = {sec_uint4(nb.size()), reinterpret_cast(nb.data())}; + keybits.part2 = {sec_uint4(eb.size()), reinterpret_cast(eb.data())}; + bitstring = e_KeyBits(&keybits); + CHECK_EQ(aux_cpy2_BitString(&keyinfo.subjectkey, bitstring), 0); + aux_free_BitString(&bitstring); + keyinfo.subjectAI = aux_cpy_AlgId(&rsa_aid); + Certificate *Cert = af_create_Certificate(m_hPSE, &keyinfo, &sha1WithRSASignature_aid, const_cast("SKnew"), dn, + &osSerial, nullptr, nullptr, TRUE, nullptr); + CHECK(Cert != nullptr); + Key self{}; + self.key = &keyinfo; + CHECK_EQ(sec_verify_all(Cert->tbs_DERcode, Cert->sig, &self, nullptr), 0); + PSE hPSE_CA = open_ca(); + Certificate *Cert_CA = af_certify(hPSE_CA, Cert, nullptr, &sha1WithRSASignature_aid, nullptr); + CHECK_EQ(af_cadb_add_Certificate(hPSE_CA, SIGNATURE, Cert_CA), 0); + CHECK_EQ(af_pse_update(m_hPSE, const_cast("Cert"), Cert_CA, *&Cert_OID), 0); + af_close(hPSE_CA); + af_close(m_hPSE); + + // AccessPSE + PSESel *sel = sec_open(cstr(name), const_cast("tut"), nullptr); + CHECK(sel != nullptr); + sel->object = aux_cpy_String(const_cast(SKnew_name)); + sel->object_type = aux_cpy_ObjId(&SKnew_oid); + OctetString value; + CHECK_EQ(sec_read_PSE(sel, &value), 0); + KeyInfo *ki = d_KeyInfo(&value); + KeyBits *kb = d_KeyBits(&ki->subjectkey); + CHECK(bytes_of(&kb->part1) == pb && bytes_of(&kb->part2) == qb); + aux_free_KeyInfo(&ki); + aux_free2_OctetString(&value); + aux_free_KeyBits(&kb); + aux_free_String(&sel->object); + aux_free_ObjId(&sel->object_type); + sel->object = aux_cpy_String(const_cast(Cert_name)); + sel->object_type = aux_cpy_ObjId(&Cert_oid); + CHECK_EQ(sec_read_PSE(sel, &value), 0); + Certificate *cert = d_Certificate(&value); + CHECK_EQ(text_of(aux_DName2Name(cert->tbs->subject)), std::string("CN=Tom Tutorial [1], DC=cryptool, DC=org")); + kb = d_KeyBits(&cert->tbs->subjectPK->subjectkey); + CHECK(bytes_of(&kb->part2) == eb); + aux_free_KeyBits(&kb); + aux_free_Certificate(&cert); + aux_free2_OctetString(&value); + CHECK_EQ(sec_close(sel), 0); + + // private key operations use the exponent of the PSE certificate + PSE pse = af_open(cstr(name), nullptr, const_cast("tut"), nullptr); + char msg[] = "tutorial"; + OctetString in{8, msg}; + Signature s; + s.signAI = &md5WithRsaEncryption_aid; + CHECK_EQ(af_sign_all(pse, &in, &s), 0); + Key k{}; + k.key = Cert_CA->tbs->subjectPK; + CHECK_EQ(sec_verify_all(&in, &s, &k, nullptr), 0); + aux_free2_BitString(&s.signature); + af_close(pse); + aux_free_DName(&dn); + aux_free2_KeyInfo(&keyinfo); + aux_free_Certificate(&Cert); + aux_free_Certificate(&Cert_CA); +} + +TEST(pse_sample_keystore) +{ + const Store &st = store(); + std::string rsa = st.pse_dir + "/[SideChannelAttack][Bob][RSA-512][1152179494][PIN=1234].pse"; + PSE bob = af_open(cstr(rsa), nullptr, const_cast("1234"), nullptr); + CHECK(bob != nullptr); + Certificate *Zert = ca_lookup("CN=Bob SideChannelAttack [1152179494], DC=cryptool, DC=org"); + + // CDlgHybridEncryptionDemo: session key left padded to (512-1)/8 octets + Bytes session = random_bytes(16); + Bytes padded(63 - 16, 0); + padded.insert(padded.end(), session.begin(), session.end()); + OctetString in{63, reinterpret_cast(padded.data())}; + std::vector buf(2048); + BitString out{0, buf.data()}; + Key Schluessel{}; + Schluessel.key = Zert->tbs->subjectPK; + Schluessel.alg = &rsa_aid; + PSE ca = open_ca(); + CHECK_EQ(af_encrypt_all(ca, &in, &out, &Schluessel, nullptr), 0); + af_close(ca); + CHECK_EQ(out.nbits, sec_uint4(512)); + std::vector dbuf(64 + 256); + OctetString dec{0, dbuf.data()}; + Key priv{}; + priv.alg = &rsa_aid; + CHECK_EQ(af_decrypt_all(bob, &out, &dec, &priv), 0); + CHECK_EQ(dec.noctets, sec_uint4(63)); + CHECK(Bytes(dbuf.begin() + 47, dbuf.begin() + 63) == session); + af_close(bob); + aux_free_Certificate(&Zert); + + // EC key of the hybrid encryption demo: text file with domain parameters and public key + std::string ec = st.pse_dir + "/[HybridEncryption][Bob][EC-prime239v1][1178702474][PIN=1234]"; + std::istringstream lines(read_text(ec)); + std::vector v; + for (std::string line; std::getline(lines, line);) + v.push_back(line); + CHECK_EQ(v.size(), size_t(9)); + CHECK_EQ(v[0], std::string("0X7FFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFF8000000000007FFFFFFFFFFC")); + CHECK_EQ(v[3], std::string("0XFFA963CDCA8816CCC33B8642BEDF905C3D358573D3F27FBBD3B3CB9AAAF")); + CHECK_EQ(v[5], std::string("0X1")); + PSE ecpse = af_open(cstr(ec + ".pse"), nullptr, const_cast("1234"), nullptr); + CHECK(ecpse != nullptr); + OctetString *ostr = (OctetString *)af_pse_get(ecpse, const_cast("EcPrivKey"), *&Uid_OID); + af_close(ecpse); + OctetString *d = d_OctetString(ostr); + Bn priv_key = bn_from(bytes_of(d)); + EC_GROUP *group = EC_GROUP_new_by_curve_name(NID_X9_62_prime239v1); + EC_POINT *q = EC_POINT_new(group); + Bn x = bn_new(), y = bn_new(); + EC_POINT_mul(group, q, priv_key.get(), nullptr, nullptr, nullptr); + EC_POINT_get_affine_coordinates(group, q, x.get(), y.get(), nullptr); + char *xs = BN_bn2hex(x.get()), *ys = BN_bn2hex(y.get()); + std::string xh = xs, yh = ys; + while (xh.size() > 1 && xh[0] == '0') + xh.erase(0, 1); + while (yh.size() > 1 && yh[0] == '0') + yh.erase(0, 1); + CHECK_EQ(v[7], "0X" + xh); + CHECK_EQ(v[8], "0X" + yh); + OPENSSL_free(xs); + OPENSSL_free(ys); + EC_POINT_free(q); + EC_GROUP_free(group); + aux_free_OctetString(&ostr); + aux_free_OctetString(&d); + + // serial numbers handed out by the CA increase + ca = open_ca(); + SerialNumber *s1 = af_pse_get_SerialNumber(ca); + CHECK(s1 != nullptr); + SET_OF_Name *users = af_cadb_list_user(ca); + int count = 0; + for (SET_OF_Name *u = users; u; u = u->next) + ++count; + CHECK(count >= 2); + af_close(ca); + aux_free_OctetString(&s1); +} diff --git a/port/secude/tests/testing.hpp b/port/secude/tests/testing.hpp new file mode 100644 index 000000000..3ea6eef6e --- /dev/null +++ b/port/secude/tests/testing.hpp @@ -0,0 +1,54 @@ +#pragma once + +#include +#include +#include +#include + +namespace testing { + +struct Case { + const char *name; + std::function fn; +}; + +std::vector ®istry(); +void report_failure(const char *file, int line, const std::string &what); +std::string scratch_dir(); + +struct Register { + Register(const char *name, std::function fn) { registry().push_back({name, std::move(fn)}); } +}; + +std::string hex(const void *p, size_t n); + +} // namespace testing + +#define TEST_CONCAT2(a, b) a##b +#define TEST_CONCAT(a, b) TEST_CONCAT2(a, b) +#define TEST(name) \ + static void name(); \ + static testing::Register TEST_CONCAT(register_, name)(#name, name); \ + static void name() + +#define CHECK(cond) \ + do { \ + if (!(cond)) \ + testing::report_failure(__FILE__, __LINE__, "CHECK(" #cond ")"); \ + } while (0) + +#define CHECK_EQ(a, b) \ + do { \ + auto va_ = (a); \ + auto vb_ = (b); \ + if (!(va_ == vb_)) \ + testing::report_failure(__FILE__, __LINE__, "CHECK_EQ(" #a ", " #b ")"); \ + } while (0) + +#define CHECK_HEX(ptr, len, expected) \ + do { \ + std::string got_ = testing::hex((ptr), (len)); \ + if (got_ != (expected)) \ + testing::report_failure(__FILE__, __LINE__, \ + std::string(#ptr " = ") + got_ + ", expected " + (expected)); \ + } while (0) diff --git a/port/secude/tools/mkpse.cpp b/port/secude/tools/mkpse.cpp new file mode 100644 index 000000000..d9552f2b3 --- /dev/null +++ b/port/secude/tools/mkpse.cpp @@ -0,0 +1,35 @@ +// Creates the CrypTool key store (CA PSE, CA database and the sample keys +// shipped with the installer) in the format of the SECUDE compatibility layer. +#include +#include + +#include "secude_compat.h" + +extern "C" char *th_get_last_error_text(void); + +int main(int argc, char **argv) +{ + bool overwrite = false; + const char *root = nullptr; + for (int i = 1; i < argc; ++i) { + if (!std::strcmp(argv[i], "--overwrite")) + overwrite = true; + else if (!root && argv[i][0] != '-') + root = argv[i]; + else + root = nullptr, i = argc; + } + if (!root) { + std::fprintf(stderr, + "usage: %s [--overwrite] \n" + "creates /PSE (sample keys) and /PSE/PSECA (CA)\n", + argv[0]); + return 2; + } + if (secude_compat_create_sample_keystore(root, overwrite ? 1 : 0) != 0) { + std::fprintf(stderr, "%s: %s\n", argv[0], th_get_last_error_text()); + return 1; + } + std::printf("key store created in %s (%s)\n", root, secude_compat_version()); + return 0; +} diff --git a/port/thirdparty/CMakeLists.txt b/port/thirdparty/CMakeLists.txt new file mode 100644 index 000000000..16a10cf27 --- /dev/null +++ b/port/thirdparty/CMakeLists.txt @@ -0,0 +1,55 @@ +cmake_minimum_required(VERSION 3.20) + +if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) + project(CrypToolThirdParty LANGUAGES C CXX) + set(CRYPTOOL_THIRDPARTY_STANDALONE ON) +else() + set(CRYPTOOL_THIRDPARTY_STANDALONE OFF) +endif() + +if(NOT DEFINED REPO_ROOT) + get_filename_component(REPO_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/../.." ABSOLUTE) +endif() +set(CT_REPO_ROOT "${REPO_ROOT}") +set(CT_THIRDPARTY_DIR "${CMAKE_CURRENT_SOURCE_DIR}") + +option(CRYPTOOL_THIRDPARTY_TESTS "Build the third-party smoke tests" ${CRYPTOOL_THIRDPARTY_STANDALONE}) +option(CRYPTOOL_THIRDPARTY_PIC "Build the third-party static libraries as position independent code" OFF) + +if(CRYPTOOL_THIRDPARTY_TESTS) + enable_testing() +endif() + +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") +include(helpers) + +# Pre-standard C++ headers (iostream.h, fstream.h, iomanip.h, strstrea.h, new.h). +add_library(cryptool_compat_headers INTERFACE) +target_include_directories(cryptool_compat_headers INTERFACE + "${CMAKE_CURRENT_SOURCE_DIR}/compat_include") +add_library(cryptool::compat_headers ALIAS cryptool_compat_headers) + +# Sources of the CrypTool application, scanned for #include spellings so the +# case-alias directories also cover the spellings used by the application. +file(GLOB CT_APP_SCAN_FILES + "${CT_REPO_ROOT}/CrypTool/*.cpp" "${CT_REPO_ROOT}/CrypTool/*.CPP" + "${CT_REPO_ROOT}/CrypTool/*.h" "${CT_REPO_ROOT}/CrypTool/*.H") + +set(CT_ALL_PARTS gmp miracl analyse ntl ec cracklib apfloat cvact aes openssl) +set(CRYPTOOL_THIRDPARTY_PARTS "${CT_ALL_PARTS}" CACHE STRING "Third-party components to configure") + +foreach(_part IN LISTS CT_ALL_PARTS) + if(_part IN_LIST CRYPTOOL_THIRDPARTY_PARTS AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/cmake/${_part}.cmake") + include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/${_part}.cmake") + endif() +endforeach() + +# Everything the CrypTool executable needs from this directory. +add_library(cryptool_thirdparty INTERFACE) +add_library(cryptool::thirdparty ALIAS cryptool_thirdparty) +foreach(_t IN ITEMS cryptool_analyse cryptool_miracl cryptool_ntl cryptool_ec cryptool_cracklib + apfloat_compat cryptool_gmp cvact_compat cryptool_aes_candidates cryptool_openssl_compat) + if(TARGET ${_t}) + target_link_libraries(cryptool_thirdparty INTERFACE ${_t}) + endif() +endforeach() diff --git a/port/thirdparty/aes_candidates/ct_aes_prelude.h b/port/thirdparty/aes_candidates/ct_aes_prelude.h new file mode 100644 index 000000000..6c8edd1a8 --- /dev/null +++ b/port/thirdparty/aes_candidates/ct_aes_prelude.h @@ -0,0 +1,30 @@ +#ifndef CT_AES_PRELUDE_H +#define CT_AES_PRELUDE_H + +/* Every system header the AES candidate sources use, included before + "long" is narrowed so that system declarations keep their real types. */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(__linux__) +#include +#endif + +#ifndef LittleEndian +#if defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +#define LittleEndian 0 +#else +#define LittleEndian 1 +#endif +#endif +#ifndef ALIGN32 +#define ALIGN32 0 +#endif + +#endif diff --git a/port/thirdparty/aes_candidates/mars.c b/port/thirdparty/aes_candidates/mars.c new file mode 100644 index 000000000..d026054fb --- /dev/null +++ b/port/thirdparty/aes_candidates/mars.c @@ -0,0 +1,8 @@ +#include "ct_aes_prelude.h" + +/* The IBM MARS code assumes a 32-bit long (WORD32). */ +#define long int +#include "Mars/mars-opt.c" +#undef long + +const size_t ct_aes_mars_sizes[2] = {sizeof(keyInstanceMars), sizeof(cipherInstanceMars)}; diff --git a/port/thirdparty/aes_candidates/rc6.c b/port/thirdparty/aes_candidates/rc6.c new file mode 100644 index 000000000..b5c39727c --- /dev/null +++ b/port/thirdparty/aes_candidates/rc6.c @@ -0,0 +1,8 @@ +#include "ct_aes_prelude.h" + +/* The RC6 reference code assumes a 32-bit long. */ +#define long int +#include "RC6/rc6.c" +#undef long + +const size_t ct_aes_rc6_sizes[2] = {sizeof(keyInstanceRC6), sizeof(cipherInstanceRC6)}; diff --git a/port/thirdparty/aes_candidates/rijndael_sizes.c b/port/thirdparty/aes_candidates/rijndael_sizes.c new file mode 100644 index 000000000..d17a8d7c3 --- /dev/null +++ b/port/thirdparty/aes_candidates/rijndael_sizes.c @@ -0,0 +1,5 @@ +#include "ct_aes_prelude.h" + +#include "Rijndael/rijndael-api-fst.h" + +const size_t ct_aes_rijndael_sizes[2] = {sizeof(keyInstanceRijndael), sizeof(cipherInstanceRijndael)}; diff --git a/port/thirdparty/aes_candidates/serpent.c b/port/thirdparty/aes_candidates/serpent.c new file mode 100644 index 000000000..a40056ec3 --- /dev/null +++ b/port/thirdparty/aes_candidates/serpent.c @@ -0,0 +1,27 @@ +#include "ct_aes_prelude.h" + +/* SERPENT.C converts its (narrowed) words with "%08lx"; keep the conversions + on a real unsigned long. */ +static int ct_serpent_sscanf(const char *str, const char *fmt, unsigned int *out) +{ + unsigned long value; + int n = sscanf(str, fmt, &value); + if (n == 1) + *out = (unsigned int)value; + return n; +} + +static int ct_serpent_sprintf(char *buf, const char *fmt, unsigned int value) +{ + return sprintf(buf, fmt, (unsigned long)value); +} + +#define sscanf ct_serpent_sscanf +#define sprintf ct_serpent_sprintf + +/* The Serpent submission code assumes a 32-bit long. */ +#define long int +#include "Serpent/SERPENT.C" +#undef long + +const size_t ct_aes_serpent_sizes[2] = {sizeof(keyInstanceSerpent), sizeof(cipherInstanceSerpent)}; diff --git a/port/thirdparty/aes_candidates/twofish.c b/port/thirdparty/aes_candidates/twofish.c new file mode 100644 index 000000000..017ddcd3a --- /dev/null +++ b/port/thirdparty/aes_candidates/twofish.c @@ -0,0 +1,5 @@ +#include "ct_aes_prelude.h" + +#include "Twofish/TWOFISH2.C" + +const size_t ct_aes_twofish_sizes[2] = {sizeof(keyInstanceTwofish), sizeof(cipherInstanceTwofish)}; diff --git a/port/thirdparty/aes_candidates/wrapper.h.in b/port/thirdparty/aes_candidates/wrapper.h.in new file mode 100644 index 000000000..531b2e0cb --- /dev/null +++ b/port/thirdparty/aes_candidates/wrapper.h.in @@ -0,0 +1,13 @@ +/* Generated: @CT_WRAPPER_NAME@ for the CrypTool port (original: @CT_WRAPPER_ORIGINAL@). */ +#ifndef @CT_WRAPPER_GUARD@ +#define @CT_WRAPPER_GUARD@ + +#include "ct_aes_prelude.h" + +#pragma push_macro("long") +#undef long +@CT_WRAPPER_NARROW_LONG@ +#include "@CT_WRAPPER_ORIGINAL@" +#pragma pop_macro("long") + +#endif diff --git a/port/thirdparty/apfloat_compat/include/ap.h b/port/thirdparty/apfloat_compat/include/ap.h new file mode 100644 index 000000000..42a8dbd1c --- /dev/null +++ b/port/thirdparty/apfloat_compat/include/ap.h @@ -0,0 +1,18 @@ +// Source-compatible subset of apfloat 2.41's ap.h, backed by GMP (see apint.h). +#include +#include +#include + +#if !defined(__AP_H) +#define __AP_H + +#include + +#define APFLOAT_COMPAT 1 +#define APFLOAT_COMPAT_VERSION "2.41-compat" + +bool apinit (void); +void apdeinit (void); +void apbase (int digit); + +#endif // __AP_H diff --git a/port/thirdparty/apfloat_compat/include/apint.h b/port/thirdparty/apfloat_compat/include/apint.h new file mode 100644 index 000000000..5df918c0c --- /dev/null +++ b/port/thirdparty/apfloat_compat/include/apint.h @@ -0,0 +1,147 @@ +// Source-compatible subset of apfloat 2.41's apint, backed by GMP's mpz_class. +// Division and remainder truncate toward zero; conversions from double and +// strings with a fractional part round toward negative infinity (floor). +#ifndef __cplusplus +#error Must use C++ for the type apint. +#endif + +#if !defined(__APINT_H) +#define __APINT_H + +#include +#include +#include +#include + +#include + +#include "ap.h" + +class apint +{ + template + using if_integral = typename std::enable_if::value, int>::type; + +public: + apint () {} + apint (int newval) : val (static_cast (newval)) {} + apint (unsigned newval) : val (static_cast (newval)) {} + apint (long newval) : val (newval) {} + apint (unsigned long newval) : val (newval) {} + apint (long long newval) { setll (newval); } + apint (unsigned long long newval) { setull (newval); } + apint (double newval); + apint (const char *newval) { setstr (newval); } + apint (const std::string &newval) { setstr (newval.c_str ()); } + explicit apint (const mpz_class &newval) : val (newval) {} + + apint &operator= (int d) { val = static_cast (d); return *this; } + apint &operator= (unsigned d) { val = static_cast (d); return *this; } + apint &operator= (long d) { val = d; return *this; } + apint &operator= (unsigned long d) { val = d; return *this; } + apint &operator= (long long d) { setll (d); return *this; } + apint &operator= (unsigned long long d) { setull (d); return *this; } + apint &operator= (double d) { return *this = apint (d); } + apint &operator= (const char *d) { setstr (d); return *this; } + apint &operator= (const std::string &d) { setstr (d.c_str ()); return *this; } + + friend apint operator+ (const apint &d1, const apint &d2) { return apint (mpz_class (d1.val + d2.val)); } + friend apint operator- (const apint &d1, const apint &d2) { return apint (mpz_class (d1.val - d2.val)); } + friend apint operator* (const apint &d1, const apint &d2) { return apint (mpz_class (d1.val * d2.val)); } + friend apint operator/ (const apint &d1, const apint &d2); + friend apint operator% (const apint &d1, const apint &d2); + friend apint operator<< (const apint &d1, size_t d2); + friend apint operator>> (const apint &d1, size_t d2); + + friend bool operator== (const apint &d1, const apint &d2) { return d1.val == d2.val; } + friend bool operator!= (const apint &d1, const apint &d2) { return d1.val != d2.val; } + friend bool operator>= (const apint &d1, const apint &d2) { return d1.val >= d2.val; } + friend bool operator<= (const apint &d1, const apint &d2) { return d1.val <= d2.val; } + friend bool operator> (const apint &d1, const apint &d2) { return d1.val > d2.val; } + friend bool operator< (const apint &d1, const apint &d2) { return d1.val < d2.val; } + + template = 0> friend apint operator+ (const apint &d1, T d2) { return d1 + apint (d2); } + template = 0> friend apint operator+ (T d1, const apint &d2) { return apint (d1) + d2; } + template = 0> friend apint operator- (const apint &d1, T d2) { return d1 - apint (d2); } + template = 0> friend apint operator- (T d1, const apint &d2) { return apint (d1) - d2; } + template = 0> friend apint operator* (const apint &d1, T d2) { return d1 * apint (d2); } + template = 0> friend apint operator* (T d1, const apint &d2) { return apint (d1) * d2; } + template = 0> friend apint operator/ (const apint &d1, T d2) { return d1 / apint (d2); } + template = 0> friend apint operator/ (T d1, const apint &d2) { return apint (d1) / d2; } + template = 0> friend apint operator% (const apint &d1, T d2) { return d1 % apint (d2); } + template = 0> friend apint operator% (T d1, const apint &d2) { return apint (d1) % d2; } + + template = 0> friend bool operator== (const apint &d1, T d2) { return d1 == apint (d2); } + template = 0> friend bool operator== (T d1, const apint &d2) { return apint (d1) == d2; } + template = 0> friend bool operator!= (const apint &d1, T d2) { return d1 != apint (d2); } + template = 0> friend bool operator!= (T d1, const apint &d2) { return apint (d1) != d2; } + template = 0> friend bool operator>= (const apint &d1, T d2) { return d1 >= apint (d2); } + template = 0> friend bool operator>= (T d1, const apint &d2) { return apint (d1) >= d2; } + template = 0> friend bool operator<= (const apint &d1, T d2) { return d1 <= apint (d2); } + template = 0> friend bool operator<= (T d1, const apint &d2) { return apint (d1) <= d2; } + template = 0> friend bool operator> (const apint &d1, T d2) { return d1 > apint (d2); } + template = 0> friend bool operator> (T d1, const apint &d2) { return apint (d1) > d2; } + template = 0> friend bool operator< (const apint &d1, T d2) { return d1 < apint (d2); } + template = 0> friend bool operator< (T d1, const apint &d2) { return apint (d1) < d2; } + + friend bool operator== (const apint &d1, double d2) { return cmpd (d1.val, d2) == 0; } + friend bool operator== (double d1, const apint &d2) { return cmpd (d2.val, d1) == 0; } + friend bool operator!= (const apint &d1, double d2) { return cmpd (d1.val, d2) != 0; } + friend bool operator!= (double d1, const apint &d2) { return cmpd (d2.val, d1) != 0; } + friend bool operator>= (const apint &d1, double d2) { return cmpd (d1.val, d2) >= 0; } + friend bool operator>= (double d1, const apint &d2) { return cmpd (d2.val, d1) <= 0; } + friend bool operator<= (const apint &d1, double d2) { return cmpd (d1.val, d2) <= 0; } + friend bool operator<= (double d1, const apint &d2) { return cmpd (d2.val, d1) >= 0; } + friend bool operator> (const apint &d1, double d2) { return cmpd (d1.val, d2) > 0; } + friend bool operator> (double d1, const apint &d2) { return cmpd (d2.val, d1) < 0; } + friend bool operator< (const apint &d1, double d2) { return cmpd (d1.val, d2) < 0; } + friend bool operator< (double d1, const apint &d2) { return cmpd (d2.val, d1) > 0; } + + friend std::ostream &operator<< (std::ostream &, const apint &); + friend std::istream &operator>> (std::istream &, apint &); + + apint &operator++ () { ++val; return *this; } + apint &operator-- () { --val; return *this; } + apint operator++ (int) { apint t (*this); ++val; return t; } + apint operator-- (int) { apint t (*this); --val; return t; } + apint &operator+= (const apint &d) { val += d.val; return *this; } + apint &operator-= (const apint &d) { val -= d.val; return *this; } + apint &operator*= (const apint &d) { val *= d.val; return *this; } + apint &operator/= (const apint &d) { return *this = *this / d; } + apint &operator%= (const apint &d) { return *this = *this % d; } + apint &operator<<= (size_t d) { return *this = *this << d; } + apint &operator>>= (size_t d) { return *this = *this >> d; } + apint operator+ () const { return *this; } + apint operator- () const { return apint (mpz_class (-val)); } + + int sign (void) const { return sgn (val); } + void sign (int newsign); + + std::string toString (int base = 0) const; + + mpz_class val; + +private: + void setll (long long v); + void setull (unsigned long long v); + void setstr (const char *s); + static int cmpd (const mpz_class &a, double b) { return mpz_cmp_d (a.get_mpz_t (), b); } +}; + +typedef struct apdiv_struct +{ + apint quot; + apint rem; +} apdiv_t; + +apint pow (apint base, unsigned long exp); +apint pow (apint base, unsigned exp); +apint pow (apint base, long exp); +apint pow (apint base, int exp); +apint abs (apint x); +apdiv_t div (apint numer, apint denom); +apint gcd (apint a, apint b); +apint lcm (apint a, apint b); +apint powmod (apint base, apint exp, apint modulus); + +#endif // __APINT_H diff --git a/port/thirdparty/apfloat_compat/src/apint.cpp b/port/thirdparty/apfloat_compat/src/apint.cpp new file mode 100644 index 000000000..8827b5646 --- /dev/null +++ b/port/thirdparty/apfloat_compat/src/apint.cpp @@ -0,0 +1,277 @@ +#include "apint.h" + +#include +#include +#include +#include +#include +#include +#include + +namespace { + +std::atomic g_base{10}; + +constexpr long kMaxStringExponent = 100000000L; + +int digit_value(unsigned char c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + if (c >= 'a' && c <= 'z') + return c - 'a' + 10; + if (c >= 'A' && c <= 'Z') + return c - 'A' + 10; + return INT_MAX; +} + +void set_magnitude(mpz_class &out, unsigned long long v) +{ + mpz_import(out.get_mpz_t(), 1, 1, sizeof v, 0, 0, &v); +} + +void require_nonzero(const apint &d) +{ + if (sgn(d.val) == 0) + throw std::domain_error("apint: division by zero"); +} + +} // namespace + +bool apinit(void) +{ + g_base = 10; + return true; +} + +void apdeinit(void) +{ +} + +void apbase(int digit) +{ + if (digit >= 2 && digit <= 36) + g_base = digit; +} + +apint::apint(double newval) +{ + if (std::isfinite(newval)) + mpz_set_d(val.get_mpz_t(), std::floor(newval)); +} + +void apint::setll(long long v) +{ + if (v < 0) { + set_magnitude(val, 0ULL - static_cast(v)); + val = -val; + } else { + set_magnitude(val, static_cast(v)); + } +} + +void apint::setull(unsigned long long v) +{ + set_magnitude(val, v); +} + +// Parses [ws][sign]digits[.digits][e[sign]digits] in the current base and +// rounds toward negative infinity, like apint(char *) = floor(apfloat(char *)). +void apint::setstr(const char *s) +{ + val = 0; + if (!s) + return; + const int base = g_base; + const unsigned char *p = reinterpret_cast(s); + while (std::isspace(*p)) + ++p; + bool negative = false; + if (*p == '+' || *p == '-') + negative = (*p++ == '-'); + + std::string digits; + long fraction_digits = 0; + bool seen_point = false; + for (;; ++p) { + if (*p == '.' && !seen_point) { + seen_point = true; + continue; + } + if (digit_value(*p) >= base) + break; + digits.push_back(static_cast(std::tolower(*p))); + if (seen_point) + ++fraction_digits; + } + if (digits.empty()) + return; + + long exponent = 0; + if (base <= 10 && (*p == 'e' || *p == 'E')) { + const unsigned char *q = p + 1; + bool exp_negative = false; + if (*q == '+' || *q == '-') + exp_negative = (*q++ == '-'); + if (std::isdigit(*q)) { + while (std::isdigit(*q)) { + if (exponent < kMaxStringExponent) + exponent = exponent * 10 + (*q - '0'); + ++q; + } + if (exp_negative) + exponent = -exponent; + } + } + + mpz_class mantissa; + if (mantissa.set_str(digits, base) != 0) + return; + if (negative) + mantissa = -mantissa; + + const long shift = exponent - fraction_digits; + if (shift >= 0) { + mpz_class scale; + mpz_ui_pow_ui(scale.get_mpz_t(), static_cast(base), static_cast(shift)); + val = mantissa * scale; + } else { + mpz_class scale; + mpz_ui_pow_ui(scale.get_mpz_t(), static_cast(base), static_cast(-shift)); + mpz_fdiv_q(val.get_mpz_t(), mantissa.get_mpz_t(), scale.get_mpz_t()); + } +} + +void apint::sign(int newsign) +{ + if (newsign == 0) + val = 0; + else if ((newsign < 0) != (sgn(val) < 0)) + val = -val; +} + +std::string apint::toString(int base) const +{ + if (base < 2 || base > 36) + base = g_base; + std::string out(mpz_sizeinbase(val.get_mpz_t(), base) + 2, '\0'); + mpz_get_str(&out[0], base, val.get_mpz_t()); + out.resize(std::char_traits::length(out.c_str())); + return out; +} + +apint operator/ (const apint &d1, const apint &d2) +{ + require_nonzero(d2); + apint r; + mpz_tdiv_q(r.val.get_mpz_t(), d1.val.get_mpz_t(), d2.val.get_mpz_t()); + return r; +} + +apint operator% (const apint &d1, const apint &d2) +{ + require_nonzero(d2); + apint r; + mpz_tdiv_r(r.val.get_mpz_t(), d1.val.get_mpz_t(), d2.val.get_mpz_t()); + return r; +} + +apint operator<< (const apint &d1, size_t d2) +{ + apint r; + mpz_mul_2exp(r.val.get_mpz_t(), d1.val.get_mpz_t(), static_cast(d2)); + return r; +} + +apint operator>> (const apint &d1, size_t d2) +{ + apint r; + mpz_tdiv_q_2exp(r.val.get_mpz_t(), d1.val.get_mpz_t(), static_cast(d2)); + return r; +} + +std::ostream &operator<< (std::ostream &str, const apint &d) +{ + return str << d.toString(); +} + +std::istream &operator>> (std::istream &str, apint &d) +{ + std::string token; + if (str >> token) + d = token; + return str; +} + +apint pow (apint base, unsigned long exp) +{ + apint r; + mpz_pow_ui(r.val.get_mpz_t(), base.val.get_mpz_t(), exp); + return r; +} + +apint pow (apint base, unsigned exp) +{ + return pow(base, static_cast(exp)); +} + +apint pow (apint base, long exp) +{ + if (exp >= 0) + return pow(base, static_cast(exp)); + require_nonzero(base); + if (base == 1) + return apint(1); + if (base == -1) + return apint((exp & 1) ? -1 : 1); + return apint(0); +} + +apint pow (apint base, int exp) +{ + return pow(base, static_cast(exp)); +} + +apint abs (apint x) +{ + mpz_abs(x.val.get_mpz_t(), x.val.get_mpz_t()); + return x; +} + +apdiv_t div (apint numer, apint denom) +{ + require_nonzero(denom); + apdiv_t r; + mpz_tdiv_qr(r.quot.val.get_mpz_t(), r.rem.val.get_mpz_t(), numer.val.get_mpz_t(), denom.val.get_mpz_t()); + return r; +} + +apint gcd (apint a, apint b) +{ + apint r; + mpz_gcd(r.val.get_mpz_t(), a.val.get_mpz_t(), b.val.get_mpz_t()); + return r; +} + +apint lcm (apint a, apint b) +{ + apint r; + mpz_lcm(r.val.get_mpz_t(), a.val.get_mpz_t(), b.val.get_mpz_t()); + return r; +} + +apint powmod (apint base, apint exp, apint modulus) +{ + require_nonzero(modulus); + apint r; + if (sgn(exp.val) < 0) { + mpz_class inv; + if (!mpz_invert(inv.get_mpz_t(), base.val.get_mpz_t(), modulus.val.get_mpz_t())) + throw std::domain_error("apint: powmod base not invertible"); + mpz_class e = -exp.val; + mpz_powm(r.val.get_mpz_t(), inv.get_mpz_t(), e.get_mpz_t(), modulus.val.get_mpz_t()); + return r; + } + mpz_powm(r.val.get_mpz_t(), base.val.get_mpz_t(), exp.val.get_mpz_t(), modulus.val.get_mpz_t()); + return r; +} diff --git a/port/thirdparty/cmake/aes.cmake b/port/thirdparty/cmake/aes.cmake new file mode 100644 index 000000000..619dbffd3 --- /dev/null +++ b/port/thirdparty/cmake/aes.cmake @@ -0,0 +1,102 @@ +# AES candidate ciphers compiled directly into CrypTool by CrypTool.vcxproj +# (..\AES\{Mars,RC6,Rijndael,Serpent,Twofish}). MARS, RC6 and Serpent assume a +# 32-bit long; the wrapper translation units in aes_candidates/ narrow long to +# int for those files only, and the generated public headers apply the same +# narrowing so the application sees identical struct layouts. The application +# should include "mars.h", "RC6.h", "Rijndael-api-fst.h", "Serpent.h" and +# "Twofish.h" from this target instead of the ..\AES\... paths. + +# The directory is "aes" in git but "AES" in the Visual Studio project paths. +set(CT_AES_DIR "") +file(GLOB _ct_aes_root_entries RELATIVE "${CT_REPO_ROOT}" "${CT_REPO_ROOT}/*") +foreach(_ct_entry IN LISTS _ct_aes_root_entries) + string(TOLOWER "${_ct_entry}" _ct_entry_lower) + if(_ct_entry_lower STREQUAL "aes" AND IS_DIRECTORY "${CT_REPO_ROOT}/${_ct_entry}") + set(CT_AES_DIR "${CT_REPO_ROOT}/${_ct_entry}") + endif() +endforeach() +if(NOT CT_AES_DIR) + message(FATAL_ERROR "AES candidate sources not found under ${CT_REPO_ROOT}") +endif() +set(CT_AES_SUPPORT_DIR "${CT_THIRDPARTY_DIR}/aes_candidates") +set(CT_AES_GEN_DIR "${CMAKE_CURRENT_BINARY_DIR}/aes_candidates") +set(CT_AES_WRAPPER_DIR "${CT_AES_GEN_DIR}/include") +set(CT_AES_PUBLIC_ALIAS_DIR "${CT_AES_GEN_DIR}/aliases") +set(CT_AES_SOURCE_ALIAS_DIR "${CT_AES_GEN_DIR}/source_aliases") + +configure_file("${CT_AES_SUPPORT_DIR}/ct_aes_prelude.h" "${CT_AES_WRAPPER_DIR}/ct_aes_prelude.h" COPYONLY) + +function(_ct_aes_wrapper name original narrow) + set(CT_WRAPPER_NAME "${name}") + set(CT_WRAPPER_ORIGINAL "${CT_AES_DIR}/${original}") + string(MAKE_C_IDENTIFIER "CT_AES_WRAPPER_${name}" CT_WRAPPER_GUARD) + string(TOUPPER "${CT_WRAPPER_GUARD}" CT_WRAPPER_GUARD) + if(narrow) + set(CT_WRAPPER_NARROW_LONG "#define long int") + else() + set(CT_WRAPPER_NARROW_LONG "") + endif() + configure_file("${CT_AES_SUPPORT_DIR}/wrapper.h.in" "${CT_AES_WRAPPER_DIR}/${name}" @ONLY) +endfunction() + +_ct_aes_wrapper(mars.h Mars/mars.h TRUE) +_ct_aes_wrapper(RC6.h RC6/rc6.h TRUE) +_ct_aes_wrapper(Rijndael-api-fst.h Rijndael/rijndael-api-fst.h FALSE) +_ct_aes_wrapper(Serpent.h Serpent/SERPENT.H TRUE) +_ct_aes_wrapper(Twofish.h Twofish/Twofish.h FALSE) + +set(CT_AES_C_SOURCES + "${CT_AES_DIR}/Mars/mars-opt.c" "${CT_AES_DIR}/RC6/rc6.c" + "${CT_AES_DIR}/Rijndael/rijndael-alg-fst.c" "${CT_AES_DIR}/Rijndael/rijndael-api-fst.c" + "${CT_AES_DIR}/Serpent/SERPENT.C" "${CT_AES_DIR}/Twofish/TWOFISH2.C") +file(GLOB CT_AES_C_HEADERS + "${CT_AES_DIR}/Mars/*.h" "${CT_AES_DIR}/RC6/*.h" "${CT_AES_DIR}/Rijndael/*.h" + "${CT_AES_DIR}/Serpent/*.H" "${CT_AES_DIR}/Serpent/*.h" "${CT_AES_DIR}/Twofish/*.H" + "${CT_AES_DIR}/Twofish/*.h") + +# Spellings the original sources use for their own headers (e.g. "Mars.h", +# "serpent.h", "platform.h"); Twofish.h needs "platform.h" in consumers too. +ct_case_aliases( + OUT_DIR "${CT_AES_SOURCE_ALIAS_DIR}" + HEADER_DIRS "${CT_AES_DIR}/Mars" "${CT_AES_DIR}/RC6" "${CT_AES_DIR}/Serpent" "${CT_AES_DIR}/Twofish" + SCAN_FILES ${CT_AES_C_SOURCES} ${CT_AES_C_HEADERS} + SCANNED_ONLY) +ct_case_aliases( + OUT_DIR "${CT_AES_PUBLIC_ALIAS_DIR}" + HEADER_DIRS "${CT_AES_WRAPPER_DIR}" + EXCLUDE ct_aes_prelude.h) +ct_case_aliases( + OUT_DIR "${CT_AES_PUBLIC_ALIAS_DIR}" + HEADER_DIRS "${CT_AES_DIR}/Twofish" + SCAN_FILES "${CT_AES_DIR}/Twofish/Twofish.h" + SCANNED_ONLY) + +add_library(cryptool_aes_candidates STATIC + "${CT_AES_SUPPORT_DIR}/mars.c" + "${CT_AES_SUPPORT_DIR}/rc6.c" + "${CT_AES_DIR}/Rijndael/rijndael-alg-fst.c" + "${CT_AES_DIR}/Rijndael/rijndael-api-fst.c" + "${CT_AES_SUPPORT_DIR}/rijndael_sizes.c" + "${CT_AES_SUPPORT_DIR}/serpent.c" + "${CT_AES_SUPPORT_DIR}/twofish.c") +add_library(cryptool::aes_candidates ALIAS cryptool_aes_candidates) +ct_thirdparty_target(cryptool_aes_candidates) +set_target_properties(cryptool_aes_candidates PROPERTIES C_STANDARD 90 C_EXTENSIONS ON) +# rijndael-alg-fst.h defines "int ROUNDS;" in every including file (MSVC merges these). +if(NOT MSVC) + target_compile_options(cryptool_aes_candidates PRIVATE -fcommon) +endif() +target_include_directories(cryptool_aes_candidates PRIVATE + "${CT_AES_SUPPORT_DIR}" + "${CT_AES_DIR}" + "${CT_AES_SOURCE_ALIAS_DIR}") +target_include_directories(cryptool_aes_candidates SYSTEM PUBLIC + "${CT_AES_WRAPPER_DIR}" + "${CT_AES_PUBLIC_ALIAS_DIR}") + +ct_add_smoke_test(test_aes_candidates + SOURCES "${CT_THIRDPARTY_DIR}/tests/test_aes_candidates.cpp" + LIBS cryptool_aes_candidates) +if(TARGET test_aes_candidates) + set_target_properties(test_aes_candidates PROPERTIES CXX_STANDARD 17 CXX_EXTENSIONS OFF) +endif() diff --git a/port/thirdparty/cmake/analyse.cmake b/port/thirdparty/cmake/analyse.cmake new file mode 100644 index 000000000..5d2fe12a3 --- /dev/null +++ b/port/thirdparty/cmake/analyse.cmake @@ -0,0 +1,43 @@ +# libanalyse as built by libanalyse/LibAnalyse.vcxproj. CrypTool's stdafx.h +# includes "libanalyse\la_string.h" and "libanalyse\analyse.h"; consumers get a +# generated directory providing libanalyse/
instead of the repository +# root (which would expose e.g. the repo's OpenSSL 1.0 headers). + +set(CT_ANALYSE_DIR "${CT_REPO_ROOT}/libanalyse") +set(CT_ANALYSE_ALIAS_DIR "${CT_THIRDPARTY_ALIAS_ROOT}/analyse") +set(CT_ANALYSE_ROOT_DIR "${CT_THIRDPARTY_ALIAS_ROOT}/analyse_root") + +set(CT_ANALYSE_SOURCES + Chi2.cpp Cipher.cpp Converter.cpp Des.cpp FFT.CPP FreqTable.cpp LFSR.CPP + MYMATH.CPP NGram.cpp OStream.cpp Permutation.cpp SBox.cpp StaticObjects.cpp + String.cpp Symbol.cpp SymbolArray.cpp) +list(TRANSFORM CT_ANALYSE_SOURCES PREPEND "${CT_ANALYSE_DIR}/") +set_source_files_properties(${CT_ANALYSE_SOURCES} PROPERTIES LANGUAGE CXX) + +file(GLOB CT_ANALYSE_HEADERS "${CT_ANALYSE_DIR}/*.h" "${CT_ANALYSE_DIR}/*.H") +# Only the case-mismatched spellings libanalyse itself uses (a blanket +# lowercase alias such as resource.h could shadow application headers). +ct_case_aliases( + OUT_DIR "${CT_ANALYSE_ALIAS_DIR}" + HEADER_DIRS "${CT_ANALYSE_DIR}" + SCAN_FILES ${CT_ANALYSE_SOURCES} ${CT_ANALYSE_HEADERS} + SCANNED_ONLY) +ct_case_aliases( + OUT_DIR "${CT_ANALYSE_ROOT_DIR}/libanalyse" + HEADER_DIRS "${CT_ANALYSE_DIR}" + INCLUDE_EXACT) + +add_library(cryptool_analyse STATIC ${CT_ANALYSE_SOURCES}) +add_library(cryptool::analyse ALIAS cryptool_analyse) +ct_thirdparty_target(cryptool_analyse) +target_include_directories(cryptool_analyse SYSTEM PUBLIC + "${CT_ANALYSE_ALIAS_DIR}" + "${CT_ANALYSE_ROOT_DIR}") +target_link_libraries(cryptool_analyse PUBLIC cryptool_compat_headers) +set_target_properties(cryptool_analyse PROPERTIES CXX_STANDARD 17 CXX_EXTENSIONS OFF) +target_compile_options(cryptool_analyse PRIVATE + $<$:-Wno-register>) + +ct_add_smoke_test(test_analyse + SOURCES "${CT_THIRDPARTY_DIR}/tests/test_analyse.cpp" + LIBS cryptool_analyse) diff --git a/port/thirdparty/cmake/apfloat.cmake b/port/thirdparty/cmake/apfloat.cmake new file mode 100644 index 000000000..11e03edc9 --- /dev/null +++ b/port/thirdparty/cmake/apfloat.cmake @@ -0,0 +1,31 @@ +# apfloat_compat: GMP-backed replacement for the prebuilt apfloat 2.41 Windows +# libraries in apfloat/. Consumers use this include directory instead of apfloat/. + +if(NOT TARGET cryptool_gmp) + include("${CMAKE_CURRENT_LIST_DIR}/gmp.cmake") +endif() + +add_library(apfloat_compat STATIC + "${CT_THIRDPARTY_DIR}/apfloat_compat/src/apint.cpp" + "${CT_THIRDPARTY_DIR}/apfloat_compat/include/ap.h" + "${CT_THIRDPARTY_DIR}/apfloat_compat/include/apint.h") +add_library(cryptool::apfloat ALIAS apfloat_compat) +target_include_directories(apfloat_compat PUBLIC "${CT_THIRDPARTY_DIR}/apfloat_compat/include") +target_link_libraries(apfloat_compat PUBLIC cryptool_gmp) +target_compile_features(apfloat_compat PUBLIC cxx_std_11) +set_target_properties(apfloat_compat PROPERTIES + CXX_STANDARD 17 + CXX_STANDARD_REQUIRED ON + POSITION_INDEPENDENT_CODE ${CRYPTOOL_THIRDPARTY_PIC} + FOLDER "thirdparty") + +ct_add_smoke_test(test_apfloat + SOURCES "${CT_THIRDPARTY_DIR}/tests/test_apfloat.cpp" + LIBS apfloat_compat) +if(TARGET test_apfloat) + set_target_properties(test_apfloat PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON) + find_package(Threads) + if(Threads_FOUND) + target_link_libraries(test_apfloat PRIVATE Threads::Threads) + endif() +endif() diff --git a/port/thirdparty/cmake/cracklib.cmake b/port/thirdparty/cmake/cracklib.cmake new file mode 100644 index 000000000..0bead98f4 --- /dev/null +++ b/port/thirdparty/cmake/cracklib.cmake @@ -0,0 +1,28 @@ +# cryptool_cracklib: cracklib 2.7 (Win32 flavour used by CrypTool) as a static C library. +# No public include directory: CrypTool declares the API itself in passwordchecker.h. +# Runtime data: ${CT_CRACKLIB_DICTIONARY}.{pwd,pwi,hwm} must be shipped as words/cracklib_dict.* + +set(_ct_cracklib_dir "${CT_REPO_ROOT}/cracklib-2.7/cracklib_Win32") +set(CT_CRACKLIB_DICTIONARY_DIR "${CT_REPO_ROOT}/setup/template/words") +set(CT_CRACKLIB_DICTIONARY "${CT_CRACKLIB_DICTIONARY_DIR}/cracklib_dict") + +add_library(cryptool_cracklib STATIC + "${_ct_cracklib_dir}/fascist.c" + "${_ct_cracklib_dir}/packlib.c" + "${_ct_cracklib_dir}/rules.c" + "${_ct_cracklib_dir}/stringlib.c") +add_library(cryptool::cracklib ALIAS cryptool_cracklib) +target_compile_definitions(cryptool_cracklib PRIVATE IN_CRACKLIB) +# K&R function definitions: stay on gnu99 (they are invalid in C23). +set_target_properties(cryptool_cracklib PROPERTIES C_STANDARD 99 C_STANDARD_REQUIRED ON C_EXTENSIONS ON) +ct_thirdparty_target(cryptool_cracklib) + +ct_add_smoke_test(test_cracklib + SOURCES "${CT_THIRDPARTY_DIR}/tests/test_cracklib.cpp" "${CT_THIRDPARTY_DIR}/tests/test_cracklib_layout.c" + LIBS cryptool_cracklib + DEFINES "CT_CRACKLIB_DICT=\"${CT_CRACKLIB_DICTIONARY}\"" + "CT_CRACKLIB_PACKER_H=\"${_ct_cracklib_dir}/packer.h\"") +if(TARGET test_cracklib) + set_target_properties(test_cracklib PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON + C_STANDARD 99 C_EXTENSIONS ON) +endif() diff --git a/port/thirdparty/cmake/cvact.cmake b/port/thirdparty/cmake/cvact.cmake new file mode 100644 index 000000000..f920b44dc --- /dev/null +++ b/port/thirdparty/cmake/cvact.cmake @@ -0,0 +1,34 @@ +# cvact_compat: replacement for the proprietary cv act library (libCVact), backed by OpenSSL 3. +if(NOT OpenSSL_FOUND AND APPLE AND NOT OPENSSL_ROOT_DIR AND NOT DEFINED ENV{OPENSSL_ROOT_DIR}) + foreach(_ct_ossl_hint /opt/homebrew/opt/openssl@3 /usr/local/opt/openssl@3) + if(EXISTS "${_ct_ossl_hint}/include/openssl/evp.h") + set(OPENSSL_ROOT_DIR "${_ct_ossl_hint}") + break() + endif() + endforeach() +endif() +find_package(OpenSSL 3 REQUIRED COMPONENTS Crypto) + +set(CT_CVACT_DIR "${CMAKE_CURRENT_LIST_DIR}/../cvact_compat") + +add_library(cvact_compat STATIC + "${CT_CVACT_DIR}/src/Algorithm.cpp" + "${CT_CVACT_DIR}/src/Blob.cpp" + "${CT_CVACT_DIR}/src/BlockCipherKey.cpp" + "${CT_CVACT_DIR}/src/Date.cpp" + "${CT_CVACT_DIR}/src/IESKey.cpp" + "${CT_CVACT_DIR}/src/Key.cpp" + "${CT_CVACT_DIR}/src/Tools.cpp") +add_library(cryptool::cvact_compat ALIAS cvact_compat) +target_include_directories(cvact_compat PUBLIC "${CT_CVACT_DIR}/include") +target_link_libraries(cvact_compat PUBLIC OpenSSL::Crypto) +target_compile_features(cvact_compat PUBLIC cxx_std_11 PRIVATE cxx_std_17) +target_compile_definitions(cvact_compat PRIVATE OPENSSL_API_COMPAT=30000 OPENSSL_NO_DEPRECATED) +set_target_properties(cvact_compat PROPERTIES + CXX_EXTENSIONS OFF + POSITION_INDEPENDENT_CODE ${CRYPTOOL_THIRDPARTY_PIC} + FOLDER "thirdparty") + +ct_add_smoke_test(test_cvact + SOURCES "${CT_THIRDPARTY_DIR}/tests/test_cvact.cpp" + LIBS cvact_compat) diff --git a/port/thirdparty/cmake/ec.cmake b/port/thirdparty/cmake/ec.cmake new file mode 100644 index 000000000..51b3634e2 --- /dev/null +++ b/port/thirdparty/cmake/ec.cmake @@ -0,0 +1,42 @@ +# libec as built by libec/LibEc.vcxproj. As on Windows it is compiled without +# LINK_SECUDE: SECUDE routines are called through the ECSecudeLib function +# table, which CrypToolApp fills from its SECUDE binding at start-up, so only +# the SECUDE headers are needed here. secude_compat (port/secude) is linked +# when that target exists, whichever directory defines it. + +set(CT_EC_DIR "${CT_REPO_ROOT}/libec") +set(CT_EC_ALIAS_DIR "${CT_THIRDPARTY_ALIAS_ROOT}/ec") + +set(CT_EC_SOURCES + ECsecude.c EMSA1.C Ecssa.c s_bithdl.c s_ecDSA.c s_ecNR.c s_ecconv.c s_ecmath.c + s_ecpX9c.c s_ecpara.c s_ecparp.c s_ecpcur.c s_ecppta.c s_ecpptc.c s_ecpptp.c + s_ecprpt.c s_ecvali.c s_prng.c) +list(TRANSFORM CT_EC_SOURCES PREPEND "${CT_EC_DIR}/sources/") +set_source_files_properties(${CT_EC_SOURCES} PROPERTIES LANGUAGE C) + +file(GLOB CT_EC_HEADERS "${CT_EC_DIR}/include/*") +ct_case_aliases( + OUT_DIR "${CT_EC_ALIAS_DIR}" + HEADER_DIRS "${CT_EC_DIR}/include" + SCAN_FILES ${CT_EC_SOURCES} ${CT_EC_HEADERS} ${CT_APP_SCAN_FILES}) + +set(CT_SECUDE_INCLUDE_DIR "${CT_REPO_ROOT}/secude" CACHE PATH + "Directory containing the SECUDE headers (secure.h, arithmet.h, secude/...)") + +add_library(cryptool_ec STATIC ${CT_EC_SOURCES}) +add_library(cryptool::ec ALIAS cryptool_ec) +ct_thirdparty_target(cryptool_ec) +target_include_directories(cryptool_ec SYSTEM PUBLIC + "${CT_EC_DIR}/include" + "${CT_EC_ALIAS_DIR}" + "${CT_SECUDE_INCLUDE_DIR}") +set_target_properties(cryptool_ec PROPERTIES C_STANDARD 90 C_EXTENSIONS ON) +target_link_libraries(cryptool_ec PUBLIC $) + +ct_add_smoke_test(test_ec + SOURCES "${CT_THIRDPARTY_DIR}/tests/test_ec.cpp" + LIBS cryptool_ec + DEFINES "CT_HAVE_SECUDE_COMPAT=$") +if(TARGET test_ec) + set_tests_properties(test_ec PROPERTIES SKIP_RETURN_CODE 77) +endif() diff --git a/port/thirdparty/cmake/gmp.cmake b/port/thirdparty/cmake/gmp.cmake new file mode 100644 index 000000000..4f592a7c2 --- /dev/null +++ b/port/thirdparty/cmake/gmp.cmake @@ -0,0 +1,69 @@ +# cryptool_gmp: system GMP (+ gmpxx) replacing the bundled MPIR binaries in libgmp/. +# Consumers must not put ${REPO_ROOT}/libgmp on their include path (MPIR headers). + +if(TARGET cryptool_gmp) + return() +endif() + +set(_ct_gmp_hints "") +if(APPLE) + if(NOT DEFINED CT_HOMEBREW_PREFIX) + find_program(CT_BREW_EXECUTABLE brew) + if(CT_BREW_EXECUTABLE) + execute_process(COMMAND "${CT_BREW_EXECUTABLE}" --prefix + OUTPUT_VARIABLE _ct_brew_prefix OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_QUIET RESULT_VARIABLE _ct_brew_rc) + if(_ct_brew_rc EQUAL 0 AND _ct_brew_prefix) + set(CT_HOMEBREW_PREFIX "${_ct_brew_prefix}" CACHE PATH "Homebrew prefix") + endif() + endif() + endif() + if(CT_HOMEBREW_PREFIX) + list(APPEND _ct_gmp_hints "${CT_HOMEBREW_PREFIX}/opt/gmp" "${CT_HOMEBREW_PREFIX}") + endif() +endif() + +find_package(PkgConfig QUIET) +if(PKG_CONFIG_FOUND) + pkg_check_modules(CT_PC_GMP QUIET IMPORTED_TARGET GLOBAL gmp) + pkg_check_modules(CT_PC_GMPXX QUIET IMPORTED_TARGET GLOBAL gmpxx) +endif() + +add_library(cryptool_gmp INTERFACE) +add_library(cryptool::gmp ALIAS cryptool_gmp) + +if(CT_PC_GMP_FOUND AND CT_PC_GMPXX_FOUND) + target_link_libraries(cryptool_gmp INTERFACE PkgConfig::CT_PC_GMPXX PkgConfig::CT_PC_GMP) + set(CT_GMP_VERSION "${CT_PC_GMP_VERSION}") +else() + find_path(CT_GMP_INCLUDE_DIR NAMES gmp.h HINTS ${_ct_gmp_hints} PATH_SUFFIXES include) + find_path(CT_GMPXX_INCLUDE_DIR NAMES gmpxx.h HINTS ${_ct_gmp_hints} PATH_SUFFIXES include) + find_library(CT_GMP_LIBRARY NAMES gmp HINTS ${_ct_gmp_hints} PATH_SUFFIXES lib) + find_library(CT_GMPXX_LIBRARY NAMES gmpxx HINTS ${_ct_gmp_hints} PATH_SUFFIXES lib) + if(NOT CT_GMP_INCLUDE_DIR OR NOT CT_GMPXX_INCLUDE_DIR OR NOT CT_GMP_LIBRARY OR NOT CT_GMPXX_LIBRARY) + message(FATAL_ERROR "GMP with C++ support (gmp.h, gmpxx.h, libgmp, libgmpxx) not found. " + "Install libgmp-dev (Debian/Ubuntu) or 'brew install gmp' (macOS), or set CMAKE_PREFIX_PATH.") + endif() + target_include_directories(cryptool_gmp SYSTEM INTERFACE "${CT_GMP_INCLUDE_DIR}" "${CT_GMPXX_INCLUDE_DIR}") + target_link_libraries(cryptool_gmp INTERFACE "${CT_GMPXX_LIBRARY}" "${CT_GMP_LIBRARY}") + set(CT_GMP_VERSION "") + file(STRINGS "${CT_GMP_INCLUDE_DIR}/gmp.h" _ct_gmp_ver REGEX "^#define __GNU_MP_VERSION(_MINOR|_PATCHLEVEL)? +[0-9]+") + foreach(_l IN LISTS _ct_gmp_ver) + if(_l MATCHES "([0-9]+)$") + list(APPEND CT_GMP_VERSION "${CMAKE_MATCH_1}") + endif() + endforeach() + list(JOIN CT_GMP_VERSION "." CT_GMP_VERSION) +endif() + +if(CT_GMP_VERSION AND CT_GMP_VERSION VERSION_LESS 6.0) + message(WARNING "GMP ${CT_GMP_VERSION} found; GMP >= 6.0 is expected") +endif() +message(STATUS "cryptool_gmp: GMP ${CT_GMP_VERSION}") + +ct_add_smoke_test(test_gmp + SOURCES "${CT_THIRDPARTY_DIR}/tests/test_gmp.cpp" + LIBS cryptool_gmp) +if(TARGET test_gmp) + set_target_properties(test_gmp PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON) +endif() diff --git a/port/thirdparty/cmake/helpers.cmake b/port/thirdparty/cmake/helpers.cmake new file mode 100644 index 000000000..f4b7e9620 --- /dev/null +++ b/port/thirdparty/cmake/helpers.cmake @@ -0,0 +1,105 @@ +include_guard(GLOBAL) + +set(CT_THIRDPARTY_ALIAS_ROOT "${CMAKE_CURRENT_BINARY_DIR}/case_aliases") + +# Silences warnings and relaxes aliasing for legacy third-party code. +function(ct_thirdparty_target target) + if(MSVC) + target_compile_options(${target} PRIVATE /w) + else() + target_compile_options(${target} PRIVATE -w -fno-strict-aliasing) + endif() + set_target_properties(${target} PROPERTIES + POSITION_INDEPENDENT_CODE ${CRYPTOOL_THIRDPARTY_PIC} + FOLDER "thirdparty") +endfunction() + +# Creates a directory of forwarding headers so that #include spellings that +# only differ in case from the files on disk resolve on case-sensitive file +# systems. Every header gets an all-lowercase alias (unless SCANNED_ONLY); in +# addition every spelling found in the #include directives of SCAN_FILES that +# case-insensitively matches a header in HEADER_DIRS gets its own alias. +# INCLUDE_EXACT also forwards the exact on-disk names (for prefix directories). +# ct_case_aliases(OUT_DIR HEADER_DIRS ... [SCAN_FILES ...] +# [EXCLUDE ...] [SCANNED_ONLY] [INCLUDE_EXACT]) +function(ct_case_aliases) + cmake_parse_arguments(A "SCANNED_ONLY;INCLUDE_EXACT" "OUT_DIR" "HEADER_DIRS;SCAN_FILES;EXCLUDE" ${ARGN}) + file(MAKE_DIRECTORY "${A_OUT_DIR}") + set(_lower_names "") + foreach(_dir IN LISTS A_HEADER_DIRS) + file(GLOB _headers RELATIVE "${_dir}" "${_dir}/*") + foreach(_h IN LISTS _headers) + if(IS_DIRECTORY "${_dir}/${_h}" OR NOT _h MATCHES "\\.([hH]|[hH][pP][pP]|[hH][hH]|[iI][nN][lL])$") + continue() + endif() + string(TOLOWER "${_h}" _lh) + if(_lh IN_LIST A_EXCLUDE) + continue() + endif() + if(NOT DEFINED "_map_${_lh}") + set("_map_${_lh}" "${_dir}/${_h}") + list(APPEND _lower_names "${_lh}") + endif() + endforeach() + endforeach() + + set(_spellings "") + if(NOT A_SCANNED_ONLY) + list(APPEND _spellings ${_lower_names}) + endif() + if(A_INCLUDE_EXACT) + foreach(_ln IN LISTS _lower_names) + get_filename_component(_exact "${_map_${_ln}}" NAME) + list(APPEND _spellings "${_exact}") + endforeach() + endif() + foreach(_f IN LISTS A_SCAN_FILES) + file(STRINGS "${_f}" _lines REGEX "^[ \t]*#[ \t]*include[ \t]*[<\"]") + foreach(_l IN LISTS _lines) + if(_l MATCHES "^[ \t]*#[ \t]*include[ \t]*[<\"]([^>\"/\\\\]+)[>\"]") + list(APPEND _spellings "${CMAKE_MATCH_1}") + endif() + endforeach() + endforeach() + list(REMOVE_DUPLICATES _spellings) + + foreach(_n IN LISTS _spellings) + string(TOLOWER "${_n}" _ln) + if(NOT DEFINED "_map_${_ln}") + continue() + endif() + set(_target "${_map_${_ln}}") + get_filename_component(_actual "${_target}" NAME) + if(_actual STREQUAL _n AND NOT A_INCLUDE_EXACT) + continue() + endif() + _ct_write_if_different("${A_OUT_DIR}/${_n}" + "/* generated case alias */\n#include \"${_target}\"\n") + endforeach() +endfunction() + +function(_ct_write_if_different path content) + if(EXISTS "${path}") + file(READ "${path}" _old) + if(_old STREQUAL content) + return() + endif() + endif() + file(WRITE "${path}" "${content}") +endfunction() + +# Registers a smoke test executable with CTest. +# ct_add_smoke_test( SOURCES ... LIBS ... [DEFINES ...] [ARGS ...]) +function(ct_add_smoke_test name) + if(NOT CRYPTOOL_THIRDPARTY_TESTS) + return() + endif() + cmake_parse_arguments(A "" "" "SOURCES;LIBS;DEFINES;ARGS" ${ARGN}) + add_executable(${name} ${A_SOURCES}) + target_link_libraries(${name} PRIVATE ${A_LIBS}) + if(A_DEFINES) + target_compile_definitions(${name} PRIVATE ${A_DEFINES}) + endif() + set_target_properties(${name} PROPERTIES FOLDER "thirdparty/tests") + add_test(NAME ${name} COMMAND ${name} ${A_ARGS}) +endfunction() diff --git a/port/thirdparty/cmake/miracl.cmake b/port/thirdparty/cmake/miracl.cmake new file mode 100644 index 000000000..f9bc9e301 --- /dev/null +++ b/port/thirdparty/cmake/miracl.cmake @@ -0,0 +1,51 @@ +# MIRACL (Shamus Software, ~2000) as built by libmiracl/LibMiracl.vcxproj: +# the C core plus the Big class. CrypTool compiles its own copies of the other +# C++ wrappers (MONTY, ELLIPTIC, FLASH, COMFLASH, POLY, FPOLY, POLYMOD) and +# libmiracl/source/CRT.CPP; CRT.CPP is included here as well because it is the +# only wrapper CrypTool takes from libmiracl/source. + +set(CT_MIRACL_DIR "${CT_REPO_ROOT}/libmiracl") +set(CT_MIRACL_ALIAS_DIR "${CT_THIRDPARTY_ALIAS_ROOT}/miracl") + +set(CT_MIRACL_C_SOURCES + MRAES.C MRALLOC.C MRARTH0.C MRARTH1.C MRARTH2.C MRARTH3.C MRBRICK.C MRBUILD.C + MRCOMBA.C MRCORE.C MRCRT.C MRCURVE.C MRDOUBLE.C MREBRICK.C MRECGF2M.C MRFAST.C + MRFLASH.C MRFLSH1.C MRFLSH2.C MRFLSH3.C MRFLSH4.C MRFRND.C MRGCD.C MRIO1.C + MRIO2.C MRJACK.C MRKCM.C MRLUCAS.C MRMONTY.C MRPI.C MRPOWER.C MRPRIME.C + MRRAND.C MRROUND.C MRSCRT.C MRSHS.C MRSHS256.C MRSHS512.C MRSMALL.C MRSTRONG.C + MRXGCD.C) +# MRMULDV.C (MSVC inline assembly) is replaced by the C versions in MRCORE.C (MR_NOASM). +set(CT_MIRACL_CXX_SOURCES BIG.CPP CRT.CPP) + +list(TRANSFORM CT_MIRACL_C_SOURCES PREPEND "${CT_MIRACL_DIR}/source/") +list(TRANSFORM CT_MIRACL_CXX_SOURCES PREPEND "${CT_MIRACL_DIR}/source/") +set_source_files_properties(${CT_MIRACL_C_SOURCES} PROPERTIES LANGUAGE C) +set_source_files_properties(${CT_MIRACL_CXX_SOURCES} PROPERTIES LANGUAGE CXX) + +ct_case_aliases( + OUT_DIR "${CT_MIRACL_ALIAS_DIR}" + HEADER_DIRS "${CT_THIRDPARTY_DIR}/miracl" "${CT_MIRACL_DIR}/include" + SCAN_FILES ${CT_MIRACL_C_SOURCES} ${CT_MIRACL_CXX_SOURCES} ${CT_APP_SCAN_FILES}) + +add_library(cryptool_miracl STATIC ${CT_MIRACL_C_SOURCES} ${CT_MIRACL_CXX_SOURCES}) +add_library(cryptool::miracl ALIAS cryptool_miracl) +ct_thirdparty_target(cryptool_miracl) +# The portable mirdef.h must shadow libmiracl/include/MIRDEF.H (Win32/__int64). +target_include_directories(cryptool_miracl SYSTEM PUBLIC + "${CT_THIRDPARTY_DIR}/miracl" + "${CT_MIRACL_ALIAS_DIR}" + "${CT_MIRACL_DIR}/include") +target_link_libraries(cryptool_miracl PUBLIC cryptool_compat_headers) +set_target_properties(cryptool_miracl PROPERTIES + C_STANDARD 90 C_EXTENSIONS ON + CXX_STANDARD 17 CXX_EXTENSIONS OFF) +if(NOT WIN32) + find_library(CT_LIBM m) + if(CT_LIBM) + target_link_libraries(cryptool_miracl PUBLIC ${CT_LIBM}) + endif() +endif() + +ct_add_smoke_test(test_miracl + SOURCES "${CT_THIRDPARTY_DIR}/tests/test_miracl.cpp" + LIBS cryptool_miracl) diff --git a/port/thirdparty/cmake/ntl.cmake b/port/thirdparty/cmake/ntl.cmake new file mode 100644 index 000000000..4e557a0df --- /dev/null +++ b/port/thirdparty/cmake/ntl.cmake @@ -0,0 +1,98 @@ +# NTL 5.5.2 as bundled in libNTL/ (pristine sources, the .c files renamed to .cpp). +# CrypTool relies on the NTL_vector_decl/NTL_matrix_decl macros that were removed +# in NTL 6, so the bundled version is built instead of a system NTL. + +if(NOT CMAKE_C_COMPILER_LOADED) + enable_language(C) +endif() + +set(CT_NTL_DIR "${CT_REPO_ROOT}/libNTL") +set(CT_NTL_GENERATED_DIR "${CMAKE_CURRENT_BINARY_DIR}/ntl_generated") + +# libNTL/include/NTL/mach_desc.h is NTL's mach_desc.win (32-bit long). The real +# description of the build host is produced by NTL's own MakeDesc program. +if(CMAKE_CROSSCOMPILING AND NOT CRYPTOOL_NTL_MACH_DESC) + message(FATAL_ERROR "cryptool_ntl: cross compiling; run NTL's MakeDesc on the target and pass " + "-DCRYPTOOL_NTL_MACH_DESC=") +endif() + +set(CT_NTL_MACH_DESC "${CT_NTL_GENERATED_DIR}/NTL/mach_desc.h") +if(CRYPTOOL_NTL_MACH_DESC) + configure_file("${CRYPTOOL_NTL_MACH_DESC}" "${CT_NTL_MACH_DESC}" COPYONLY) +elseif(NOT EXISTS "${CT_NTL_MACH_DESC}") + set(_ntl_makedesc_dir "${CMAKE_CURRENT_BINARY_DIR}/ntl_makedesc") + set(_ntl_makedesc_exe "${_ntl_makedesc_dir}/MakeDesc${CMAKE_EXECUTABLE_SUFFIX}") + if(MSVC) + set(_ntl_makedesc_flags "") + set(_ntl_makedesc_libs "") + else() + set(_ntl_makedesc_flags -w -O0 -ffp-contract=off) + set(_ntl_makedesc_libs m) + endif() + try_compile(_ntl_makedesc_ok "${_ntl_makedesc_dir}/build" + SOURCES "${CT_THIRDPARTY_DIR}/ntl/MakeDesc.c" "${CT_THIRDPARTY_DIR}/ntl/MakeDescAux.c" + CMAKE_FLAGS "-DINCLUDE_DIRECTORIES=${CT_NTL_DIR}/include" + COMPILE_DEFINITIONS ${_ntl_makedesc_flags} + LINK_LIBRARIES ${_ntl_makedesc_libs} + OUTPUT_VARIABLE _ntl_makedesc_log + COPY_FILE "${_ntl_makedesc_exe}") + if(NOT _ntl_makedesc_ok) + message(FATAL_ERROR "cryptool_ntl: building NTL's MakeDesc failed:\n${_ntl_makedesc_log}") + endif() + file(MAKE_DIRECTORY "${CT_NTL_GENERATED_DIR}/NTL") + execute_process(COMMAND "${_ntl_makedesc_exe}" + WORKING_DIRECTORY "${CT_NTL_GENERATED_DIR}/NTL" + RESULT_VARIABLE _ntl_makedesc_rc + ERROR_VARIABLE _ntl_makedesc_err + OUTPUT_QUIET) + if(NOT _ntl_makedesc_rc EQUAL 0 OR NOT EXISTS "${CT_NTL_MACH_DESC}") + file(REMOVE "${CT_NTL_MACH_DESC}") + message(FATAL_ERROR "cryptool_ntl: NTL's MakeDesc failed:\n${_ntl_makedesc_err}") + endif() + string(REGEX MATCH "bits per long = [0-9]+" _ntl_bpl "${_ntl_makedesc_err}") + message(STATUS "cryptool_ntl: generated NTL/mach_desc.h (${_ntl_bpl})") +endif() + +set(CT_NTL_SOURCES + FFT.cpp FacVec.cpp GF2.cpp GF2E.cpp GF2EX.cpp GF2EXFactoring.cpp GF2X.cpp GF2X1.cpp + GF2XFactoring.cpp GF2XVec.cpp G_LLL_FP.cpp G_LLL_QP.cpp G_LLL_RR.cpp G_LLL_XD.cpp + GetTime.cpp HNF.cpp LLL.cpp LLL_FP.cpp LLL_QP.cpp LLL_RR.cpp LLL_XD.cpp RR.cpp + WordVector.cpp ZZ.cpp ZZVec.cpp ZZX.cpp ZZX1.cpp ZZXCharPoly.cpp ZZXFactoring.cpp + ZZ_p.cpp ZZ_pE.cpp ZZ_pEX.cpp ZZ_pEXFactoring.cpp ZZ_pX.cpp ZZ_pX1.cpp + ZZ_pXCharPoly.cpp ZZ_pXFactoring.cpp ctools.cpp fileio.cpp lip.cpp lzz_p.cpp + lzz_pE.cpp lzz_pEX.cpp lzz_pEXFactoring.cpp lzz_pX.cpp lzz_pX1.cpp lzz_pXCharPoly.cpp + lzz_pXFactoring.cpp mat_GF2.cpp mat_GF2E.cpp mat_RR.cpp mat_ZZ.cpp mat_ZZ_p.cpp + mat_ZZ_pE.cpp mat_lzz_p.cpp mat_lzz_pE.cpp mat_poly_ZZ.cpp mat_poly_ZZ_p.cpp + mat_poly_lzz_p.cpp pair_GF2EX_long.cpp pair_GF2X_long.cpp pair_ZZX_long.cpp + pair_ZZ_pEX_long.cpp pair_ZZ_pX_long.cpp pair_lzz_pEX_long.cpp pair_lzz_pX_long.cpp + quad_float.cpp tools.cpp vec_GF2.cpp vec_GF2E.cpp vec_GF2XVec.cpp vec_RR.cpp + vec_ZZ.cpp vec_ZZVec.cpp vec_ZZ_p.cpp vec_ZZ_pE.cpp vec_double.cpp vec_long.cpp + vec_lzz_p.cpp vec_lzz_pE.cpp vec_quad_float.cpp vec_ulong.cpp vec_vec_GF2.cpp + vec_vec_GF2E.cpp vec_vec_RR.cpp vec_vec_ZZ.cpp vec_vec_ZZ_p.cpp vec_vec_ZZ_pE.cpp + vec_vec_long.cpp vec_vec_lzz_p.cpp vec_vec_lzz_pE.cpp vec_vec_ulong.cpp + vec_xdouble.cpp xdouble.cpp) +list(TRANSFORM CT_NTL_SOURCES PREPEND "${CT_NTL_DIR}/src/") + +add_library(cryptool_ntl STATIC ${CT_NTL_SOURCES}) +add_library(cryptool::ntl ALIAS cryptool_ntl) +ct_thirdparty_target(cryptool_ntl) +# The generated directory must precede libNTL/include so that +# resolves to the generated header (also on case-insensitive file systems). +target_include_directories(cryptool_ntl BEFORE PUBLIC + "$" + "$") +target_compile_features(cryptool_ntl PUBLIC cxx_std_11) +set_target_properties(cryptool_ntl PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON) +if(NOT MSVC) + # quad_float (LLL_QP/G_LLL_QP) needs exact IEEE double rounding; FMA contraction + # (gcc's default on arm64) breaks its error-free transforms (NTL's QuadTest fails). + target_compile_options(cryptool_ntl PRIVATE -ffp-contract=off) + target_link_libraries(cryptool_ntl PUBLIC m) +endif() + +ct_add_smoke_test(test_ntl + SOURCES "${CT_THIRDPARTY_DIR}/tests/test_ntl.cpp" + LIBS cryptool_ntl) +if(TARGET test_ntl) + set_target_properties(test_ntl PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON) +endif() diff --git a/port/thirdparty/cmake/openssl.cmake b/port/thirdparty/cmake/openssl.cmake new file mode 100644 index 000000000..7df2b5c65 --- /dev/null +++ b/port/thirdparty/cmake/openssl.cmake @@ -0,0 +1,57 @@ +# cryptool_openssl_compat: replaces the bundled OpenSSL 1.0.1 headers/libeay32 used by +# CrypTool's HashingOperations.cpp ("OpenSSL//md2.h" ... "OpenSSL//sha.h", included +# inside namespace __SSL) and DlgAbout.cpp ("crypto.h" inside namespace OPENSSL). +# MD4/MD5/SHA-1/SHA-256/SHA-512/RIPEMD-160 forward to OpenSSL 3's libcrypto; +# MD2 and SHA-0 (absent from OpenSSL 3) are implemented here. +# +# The include directories are deliberately NOT marked SYSTEM: CrypTool's +# "OpenSSL//x.h" must resolve to these headers before OpenSSL 3's (imported, +# hence -isystem) include directory, which on case-insensitive file systems +# also matches "OpenSSL/". The compat headers keep OpenSSL 3's include guards +# and declare a superset of the real headers, so a real include +# that resolves here still works. + +if(NOT TARGET OpenSSL::Crypto) + if(APPLE AND NOT OPENSSL_ROOT_DIR AND NOT DEFINED ENV{OPENSSL_ROOT_DIR}) + find_program(CT_BREW_EXECUTABLE brew) + if(CT_BREW_EXECUTABLE) + execute_process(COMMAND "${CT_BREW_EXECUTABLE}" --prefix openssl@3 + OUTPUT_VARIABLE _ct_ossl_prefix OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_QUIET RESULT_VARIABLE _ct_ossl_rc) + if(_ct_ossl_rc EQUAL 0 AND EXISTS "${_ct_ossl_prefix}/include/openssl/evp.h") + set(OPENSSL_ROOT_DIR "${_ct_ossl_prefix}") + endif() + endif() + endif() + find_package(OpenSSL 3 REQUIRED COMPONENTS Crypto) +endif() + +set(CT_OPENSSL_COMPAT_DIR "${CT_THIRDPARTY_DIR}/openssl_compat") + +add_library(cryptool_openssl_compat STATIC + "${CT_OPENSSL_COMPAT_DIR}/src/ct_md2.c" + "${CT_OPENSSL_COMPAT_DIR}/src/ct_sha0.c") +add_library(cryptool::openssl_compat ALIAS cryptool_openssl_compat) +target_include_directories(cryptool_openssl_compat PUBLIC + "${CT_OPENSSL_COMPAT_DIR}/include" + "${CT_OPENSSL_COMPAT_DIR}/include_bare") +target_link_libraries(cryptool_openssl_compat PUBLIC OpenSSL::Crypto) +set_target_properties(cryptool_openssl_compat PROPERTIES + C_STANDARD 99 C_EXTENSIONS OFF + POSITION_INDEPENDENT_CODE ${CRYPTOOL_THIRDPARTY_PIC} + FOLDER "thirdparty") + +if(CRYPTOOL_THIRDPARTY_TESTS) + # Real OpenSSL 3 headers only (no compat include dir), for layout comparison. + add_library(test_openssl_compat_layout OBJECT "${CT_THIRDPARTY_DIR}/tests/test_openssl_compat_layout.c") + target_link_libraries(test_openssl_compat_layout PRIVATE OpenSSL::Crypto) + target_compile_definitions(test_openssl_compat_layout PRIVATE OPENSSL_SUPPRESS_DEPRECATED) + set_target_properties(test_openssl_compat_layout PROPERTIES C_STANDARD 99 FOLDER "thirdparty/tests") +endif() +ct_add_smoke_test(test_openssl_compat + SOURCES "${CT_THIRDPARTY_DIR}/tests/test_openssl_compat.cpp" + $ + LIBS cryptool_openssl_compat) +if(TARGET test_openssl_compat) + set_target_properties(test_openssl_compat PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON) +endif() diff --git a/port/thirdparty/compat_include/fstream.h b/port/thirdparty/compat_include/fstream.h new file mode 100644 index 000000000..7b92f0597 --- /dev/null +++ b/port/thirdparty/compat_include/fstream.h @@ -0,0 +1,8 @@ +#ifndef CRYPTOOL_COMPAT_FSTREAM_H +#define CRYPTOOL_COMPAT_FSTREAM_H + +#include +#include +using namespace std; + +#endif diff --git a/port/thirdparty/compat_include/iomanip.h b/port/thirdparty/compat_include/iomanip.h new file mode 100644 index 000000000..421ed63cb --- /dev/null +++ b/port/thirdparty/compat_include/iomanip.h @@ -0,0 +1,8 @@ +#ifndef CRYPTOOL_COMPAT_IOMANIP_H +#define CRYPTOOL_COMPAT_IOMANIP_H + +#include +#include +using namespace std; + +#endif diff --git a/port/thirdparty/compat_include/iostream.h b/port/thirdparty/compat_include/iostream.h new file mode 100644 index 000000000..4cef57cf5 --- /dev/null +++ b/port/thirdparty/compat_include/iostream.h @@ -0,0 +1,9 @@ +#ifndef CRYPTOOL_COMPAT_IOSTREAM_H +#define CRYPTOOL_COMPAT_IOSTREAM_H + +// Legacy code selects on non-MSVC compilers; the MSVC branch it +// replaces includes and imports namespace std, so do the same here. +#include +using namespace std; + +#endif diff --git a/port/thirdparty/compat_include/new.h b/port/thirdparty/compat_include/new.h new file mode 100644 index 000000000..1f87c2d4d --- /dev/null +++ b/port/thirdparty/compat_include/new.h @@ -0,0 +1,8 @@ +#ifndef CRYPTOOL_COMPAT_NEW_H +#define CRYPTOOL_COMPAT_NEW_H + +#ifdef __cplusplus +#include +#endif + +#endif diff --git a/port/thirdparty/compat_include/strstrea.h b/port/thirdparty/compat_include/strstrea.h new file mode 100644 index 000000000..f1d2a5503 --- /dev/null +++ b/port/thirdparty/compat_include/strstrea.h @@ -0,0 +1,8 @@ +#ifndef CRYPTOOL_COMPAT_STRSTREA_H +#define CRYPTOOL_COMPAT_STRSTREA_H + +#include +#include +using namespace std; + +#endif diff --git a/port/thirdparty/compat_include/strstream.h b/port/thirdparty/compat_include/strstream.h new file mode 100644 index 000000000..0ea17b19b --- /dev/null +++ b/port/thirdparty/compat_include/strstream.h @@ -0,0 +1,8 @@ +#ifndef CRYPTOOL_COMPAT_STRSTREAM_H +#define CRYPTOOL_COMPAT_STRSTREAM_H + +#include +#include +using namespace std; + +#endif diff --git a/port/thirdparty/cvact_compat/include/actAlgorithm.h b/port/thirdparty/cvact_compat/include/actAlgorithm.h new file mode 100644 index 000000000..4f834e6de --- /dev/null +++ b/port/thirdparty/cvact_compat/include/actAlgorithm.h @@ -0,0 +1,57 @@ +// Source-compatible replacement for the cv act library (subset used by CrypTool). +#ifndef ACT_Algorithm_h +#define ACT_Algorithm_h + +#include "actBasics.h" + +namespace act +{ + class IAlgorithm; + class IKey; + class Blob; + + class Algorithm + { + public: + Algorithm(IAlgorithm *alg); + Algorithm(const IKey *key, mode_t mode); + Algorithm(const IKey *key, mode_t mode, const Blob &data); + // Hash ("MD5", "SHA1", "SHA224", "SHA256", "SHA384", "SHA512", "RIPEMD160") + // or random generator ("BBS", "FIPS186", "DevRandom") by name. + Algorithm(const char *name); + + void Write(const Blob &indata); + void Write(const byte *indata, size_t insize); + void Finalize(); + size_t Read(Blob &outdata, size_t max = 0); + size_t Read(byte *outbuffer, size_t buffersize); + size_t GetAvailableSize() const; + status_t GetStatus() const; + + Algorithm &operator<<(const Blob &indata) { Write(indata); return *this; } + Algorithm &operator<<(Algorithm &(*manipulator)(Algorithm &alg)) { return manipulator(*this); } + Algorithm &operator>>(Blob &outdata) { Read(outdata); return *this; } + + ~Algorithm(); + + operator IAlgorithm *(); + operator const IAlgorithm *() const; + IAlgorithm *GetPointer(); + const IAlgorithm *GetPointer() const; + IAlgorithm *ReleasePointer(); + + private: + Algorithm(const Algorithm &alg); + Algorithm &operator=(const Algorithm &alg); + + IAlgorithm *mAlg; + }; + + inline Algorithm &final(Algorithm &alg) + { + alg.Finalize(); + return alg; + } +} // namespace act + +#endif // ACT_Algorithm_h diff --git a/port/thirdparty/cvact_compat/include/actBasics.h b/port/thirdparty/cvact_compat/include/actBasics.h new file mode 100644 index 000000000..22a661beb --- /dev/null +++ b/port/thirdparty/cvact_compat/include/actBasics.h @@ -0,0 +1,36 @@ +// Source-compatible replacement for the cv act library (subset used by CrypTool). +#ifndef ACT_Basics_h +#define ACT_Basics_h + +#include +#include + +namespace act +{ + typedef unsigned long ulong; + typedef unsigned int uint; + typedef unsigned short ushort; + typedef unsigned char uchar; + typedef unsigned char byte; + typedef unsigned short word; + typedef unsigned long dword; + + using std::size_t; + using std::ptrdiff_t; + + typedef int paramid_t; + typedef int status_t; + typedef int mode_t; + typedef int export_t; + + typedef std::uint16_t uint16; + typedef std::uint32_t uint32; + typedef std::uint64_t uint64; +#ifndef U64 +#define U64(x) x##ULL +#endif + + const export_t DEFAULT = 0; +} // namespace act + +#endif // ACT_Basics_h diff --git a/port/thirdparty/cvact_compat/include/actBlob.h b/port/thirdparty/cvact_compat/include/actBlob.h new file mode 100644 index 000000000..c3d20319e --- /dev/null +++ b/port/thirdparty/cvact_compat/include/actBlob.h @@ -0,0 +1,145 @@ +// Source-compatible replacement for the cv act library (subset used by CrypTool). +// Blob behaves like std::vector; released memory is wiped. The +// storage always keeps one zero byte behind the last element, so &blob[blob.size()] +// is addressable and the contents can be read as a C string. +#ifndef ACT_BLOB_H +#define ACT_BLOB_H + +#include "actBasics.h" +#include "actException.h" + +#include +#include +#include +#include +#include + +namespace act +{ + void SecureZero(void *p, size_t n) noexcept; + + class Blob + { + public: + typedef unsigned char byte; + typedef byte &reference; + typedef const byte &const_reference; + typedef size_t size_type; + typedef ptrdiff_t difference_type; + typedef byte value_type; + typedef byte *pointer; + typedef const byte *const_pointer; + typedef byte *iterator; + typedef const byte *const_iterator; + typedef std::reverse_iterator reverse_iterator; + typedef std::reverse_iterator const_reverse_iterator; + + static pointer get_base(iterator it) { return it; } + static const_pointer get_base(const_iterator it) { return it; } + + explicit Blob(const char *str); + explicit Blob(std::string &str); + Blob() noexcept; + Blob(size_type n, byte v = byte(0)); + Blob(const Blob &x); + Blob(Blob &&x) noexcept; + + template::value>::type> + Blob(InputIt f, InputIt l) : Blob() + { + insert(end(), f, l); + } + + ~Blob(); + + Blob &operator=(const Blob &x); + Blob &operator=(Blob &&x) noexcept; + + template::value>::type> + void assign(InputIt f, InputIt l) + { + Blob tmp(f, l); + swap(tmp); + } + void assign(size_type n, byte x = byte(0)); + + iterator begin() noexcept { return mFirst; } + const_iterator begin() const noexcept { return mFirst; } + iterator end() noexcept { return mFirst + mSize; } + const_iterator end() const noexcept { return mFirst + mSize; } + reverse_iterator rbegin() noexcept { return reverse_iterator(end()); } + const_reverse_iterator rbegin() const noexcept { return const_reverse_iterator(end()); } + reverse_iterator rend() noexcept { return reverse_iterator(begin()); } + const_reverse_iterator rend() const noexcept { return const_reverse_iterator(begin()); } + + size_type size() const noexcept { return mSize; } + size_type max_size() const noexcept { return size_type(-1) / 2; } + void resize(size_type n, byte x = byte(0)); + size_type capacity() const noexcept { return mCap; } + bool empty() const noexcept { return mSize == 0; } + void reserve(size_type n); + + const_reference operator[](size_type p) const { return mFirst[p]; } + reference operator[](size_type p) { return mFirst[p]; } + const_reference at(size_type p) const; + reference at(size_type p); + reference front() { return mFirst[0]; } + const_reference front() const { return mFirst[0]; } + reference back() { return mFirst[mSize - 1]; } + const_reference back() const { return mFirst[mSize - 1]; } + + void push_back(byte x); + void pop_back(); + iterator insert(iterator p, byte x = byte(0)); + void insert(iterator p, size_type m, byte x); + + template::value>::type> + void insert(iterator p, InputIt f, InputIt l) + { + std::vector tmp; + for (; f != l; ++f) + tmp.push_back(static_cast(*f)); + insert_raw(p, tmp.empty() ? 0 : &tmp[0], tmp.size()); + if (!tmp.empty()) + SecureZero(&tmp[0], tmp.size()); + } + + void append(const Blob &x); + void append(size_type m, byte x); + template::value>::type> + void append(InputIt f, InputIt l) + { + insert(end(), f, l); + } + + iterator erase(iterator p); + iterator erase(iterator f, iterator l); + void clear() noexcept; + void swap(Blob &x) noexcept; + + bool _eq(const Blob &x) const; + bool _lt(const Blob &x) const; + + private: + iterator insert_raw(iterator p, const byte *src, size_type m); + iterator make_gap(iterator p, size_type m); + void reallocate(size_type cap); + void release() noexcept; + void outofrange() const; + + byte *mFirst; + size_type mSize; + size_type mCap; + }; + + std::ostream &operator<<(std::ostream &os, const Blob &blob); + + inline bool operator==(const Blob &x, const Blob &y) { return x._eq(y); } + inline bool operator!=(const Blob &x, const Blob &y) { return !x._eq(y); } + inline bool operator<(const Blob &x, const Blob &y) { return x._lt(y); } + inline bool operator>=(const Blob &x, const Blob &y) { return !x._lt(y); } + inline bool operator<=(const Blob &x, const Blob &y) { return x._eq(y) || x._lt(y); } + inline bool operator>(const Blob &x, const Blob &y) { return !(x._eq(y) || x._lt(y)); } +} // namespace act + +#endif // ACT_BLOB_H diff --git a/port/thirdparty/cvact_compat/include/actDate.h b/port/thirdparty/cvact_compat/include/actDate.h new file mode 100644 index 000000000..004a6d206 --- /dev/null +++ b/port/thirdparty/cvact_compat/include/actDate.h @@ -0,0 +1,94 @@ +// Source-compatible replacement for the cv act library (subset used by CrypTool). +// Dates are UTC; Encode() yields a DER UTCTime, EncodeToGeneralizedTime() a DER +// GeneralizedTime. +#ifndef ACT_Date_h +#define ACT_Date_h + +#include + +#include "actBasics.h" + +namespace act +{ + class Blob; + + class Date + { + public: + Date(); + Date(int day, int month, int year); + Date(int day, int month, int year, int hour, int minute, int sec); + Date(const Blob &der); + Date(const unsigned char *ch, const unsigned int size); + + Date &SetToday(); + + int GetDay() const { return m_day; } + int GetMonth() const { return m_month; } + int GetYear() const { return m_year; } + int GetHour() const { return m_hour; } + int GetMinute() const { return m_min; } + int GetSecond() const { return m_sec; } + + void IgnoreTime(bool b); + + const Date &operator=(const Date &); + bool operator>(const Date &) const; + bool operator>=(const Date &) const; + bool operator<(const Date &) const; + bool operator<=(const Date &) const; + bool operator==(const Date &) const; + bool operator!=(const Date &) const; + + const Date &AddMonths(int m); + const Date &SubMonths(int m); + const Date &AddYears(int y); + const Date &SubYears(int y); + const Date &AddDays(int d); + const Date &SubDays(int d); + const Date &AddHours(int h); + const Date &SubHours(int h); + const Date &AddMinutes(int m); + const Date &SubMinutes(int m); + const Date &AddSecond(int m); + const Date &SubSecond(int m); + + int DayOfWeek() const; // 0: Sunday ... 6: Saturday + int IsLeap(int y) const; + int DaysPerMonth(int m, int y) const; + long GetDifference(const Date &d2) const; // in days + long GetJulian() const; + long GetJulian(int d, int m, int y) const; + void ConvertFromJulian(long jd, int &d, int &m, int &y); + + Blob Encode() const; + Blob EncodeToGeneralizedTime() const; + operator Blob() const; + + // Day of month of the which-th (1-based, 5 = last) given weekday in month/year. + int GetDayFromWeekDay(int weekday, int year, int month, int which); + + static void copyDatetm(act::Date a, struct tm &b); + static void copytmDate(struct tm a, act::Date &b); + + private: + int m_day, m_month, m_year; + int m_hour, m_min, m_sec; + bool m_ignore_time; + + Date &SetYear(int year) { m_year = year; return *this; } + Date &SetMonth(int month) { m_month = month; return *this; } + Date &SetDay(int day) { m_day = day; return *this; } + Date &SetHour(int hour) { m_hour = hour; return *this; } + Date &SetMinute(int minute) { m_min = minute; return *this; } + Date &SetSecond(int sec) { m_sec = sec; return *this; } + bool IsValid() const; + void AdjustDays(); + void Decode(const unsigned char *ch, size_t size); + long long SecondsSinceEpochDay() const; + void SetFromSeconds(long long s); + int Compare(const Date &other) const; + }; +} // namespace act + +#endif // ACT_Date_h diff --git a/port/thirdparty/cvact_compat/include/actDefaultRNG.h b/port/thirdparty/cvact_compat/include/actDefaultRNG.h new file mode 100644 index 000000000..035356392 --- /dev/null +++ b/port/thirdparty/cvact_compat/include/actDefaultRNG.h @@ -0,0 +1,14 @@ +// Source-compatible replacement for the cv act library (subset used by CrypTool). +#ifndef ACT_DefaultRNG_h +#define ACT_DefaultRNG_h + +namespace act +{ + class IRNGAlg; + + typedef IRNGAlg *(*CreateRNGPtr)(); + extern CreateRNGPtr CreateFastRNG; + extern CreateRNGPtr CreateStrongRNG; +} // namespace act + +#endif // ACT_DefaultRNG_h diff --git a/port/thirdparty/cvact_compat/include/actException.h b/port/thirdparty/cvact_compat/include/actException.h new file mode 100644 index 000000000..a75e37b62 --- /dev/null +++ b/port/thirdparty/cvact_compat/include/actException.h @@ -0,0 +1,132 @@ +// Source-compatible replacement for the cv act library (subset used by CrypTool). +#ifndef ACT_EXCEPTION_H +#define ACT_EXCEPTION_H + +#include +#include +#include + +namespace act +{ + class FileAndLine + { + public: + FileAndLine(const char *file = 0, int line = 0) noexcept : m_line(line), m_file(file) {} + FileAndLine(const FileAndLine &other) noexcept : m_line(other.line()), m_file(other.file()) {} + virtual ~FileAndLine() {} + + virtual int line() const noexcept { return m_line; } + virtual const char *file() const noexcept { return m_file; } + + FileAndLine &operator=(const FileAndLine &other) noexcept + { + m_line = other.line(); + m_file = other.file(); + return *this; + } + + private: + int m_line; + const char *m_file; + }; + + // Messages are not copied: only string literals or otherwise static strings may be passed. + class Exception + { + public: + explicit Exception(const char *msg = 0, const char *where = 0) noexcept : m_what(msg), m_where(where) {} + + template().what()), + typename = decltype(std::declval().where()), + typename = typename std::enable_if::value>::type> + Exception(const T &other) : m_what(other.what()), m_where(other.where()) {} + + virtual ~Exception() noexcept {} + + virtual const char *what() const noexcept { return m_what != 0 ? m_what : ""; } + virtual const char *where() const noexcept { return m_where != 0 ? m_where : ""; } + + private: + const char *m_what; + const char *m_where; + }; + +#define ACT_COMPAT_EXCEPTION(name, base) \ + class name : public base \ + { \ + public: \ + name(const char *msg = 0, const char *where = 0) : base(msg, where) {} \ + }; + + ACT_COMPAT_EXCEPTION(BadException, Exception) + ACT_COMPAT_EXCEPTION(NullPointerException, BadException) + ACT_COMPAT_EXCEPTION(OutOfRangeException, BadException) + + ACT_COMPAT_EXCEPTION(LogicalException, Exception) + ACT_COMPAT_EXCEPTION(ASN1Exception, LogicalException) + ACT_COMPAT_EXCEPTION(NoSuchDLLException, LogicalException) + ACT_COMPAT_EXCEPTION(PasswordException, LogicalException) + ACT_COMPAT_EXCEPTION(AlgorithmException, LogicalException) + ACT_COMPAT_EXCEPTION(InvalidAlgorithmParameterException, AlgorithmException) + ACT_COMPAT_EXCEPTION(NoSuchAlgorithmException, AlgorithmException) + ACT_COMPAT_EXCEPTION(ArithmeticException, LogicalException) + ACT_COMPAT_EXCEPTION(CertificateException, LogicalException) + ACT_COMPAT_EXCEPTION(CertificateEncodingException, CertificateException) + ACT_COMPAT_EXCEPTION(CertificateExpiredException, CertificateException) + ACT_COMPAT_EXCEPTION(CertificateNotYetValidException, CertificateException) + ACT_COMPAT_EXCEPTION(CertificateParsingException, CertificateException) + ACT_COMPAT_EXCEPTION(KeyException, LogicalException) + ACT_COMPAT_EXCEPTION(InvalidKeyException, KeyException) + ACT_COMPAT_EXCEPTION(KeyManagementException, KeyException) + ACT_COMPAT_EXCEPTION(MessageDigestException, LogicalException) + ACT_COMPAT_EXCEPTION(PaddingException, LogicalException) + ACT_COMPAT_EXCEPTION(SignatureException, LogicalException) + ACT_COMPAT_EXCEPTION(SubsystemException, LogicalException) + ACT_COMPAT_EXCEPTION(NoSuchSubsystemException, SubsystemException) + + ACT_COMPAT_EXCEPTION(RuntimeException, Exception) + ACT_COMPAT_EXCEPTION(BadAllocException, RuntimeException) + +#undef ACT_COMPAT_EXCEPTION + + class SmartcardException : public LogicalException + { + public: + SmartcardException(const char *msg = 0, const char *where = 0, long code = 0) + : LogicalException(msg, where), m_code(code) {} + + virtual long code() const noexcept { return m_code; } + + private: + long m_code; + }; + +#define ACT_COMPAT_SC_EXCEPTION(name) \ + class name : public SmartcardException \ + { \ + public: \ + name(const char *msg = 0, const char *where = 0, long code = 0) : SmartcardException(msg, where, code) {} \ + }; + + ACT_COMPAT_SC_EXCEPTION(InvalidPinException) + ACT_COMPAT_SC_EXCEPTION(PinLockedException) + ACT_COMPAT_SC_EXCEPTION(PinExpiredException) + ACT_COMPAT_SC_EXCEPTION(PinLenRangeException) + ACT_COMPAT_SC_EXCEPTION(InvalidCardException) + ACT_COMPAT_SC_EXCEPTION(CardOutOfMemoryException) + +#undef ACT_COMPAT_SC_EXCEPTION + + class NotImplementedException : public FileAndLine, public RuntimeException + { + public: + explicit NotImplementedException(const char *msg = 0, const char *where = 0) + : RuntimeException(msg, where) {} + + NotImplementedException(const char *msg, const char *where, const char *file, int line) + : FileAndLine(file, line), RuntimeException(msg, where) {} + }; +} // namespace act + +#endif // ACT_EXCEPTION_H diff --git a/port/thirdparty/cvact_compat/include/actIAlgorithm.h b/port/thirdparty/cvact_compat/include/actIAlgorithm.h new file mode 100644 index 000000000..09d5b7dae --- /dev/null +++ b/port/thirdparty/cvact_compat/include/actIAlgorithm.h @@ -0,0 +1,26 @@ +// Source-compatible replacement for the cv act library (subset used by CrypTool). +#ifndef ACT_IALGORITHM_H +#define ACT_IALGORITHM_H + +#include "actBasics.h" + +namespace act +{ + class Blob; + + class IAlgorithm + { + public: + virtual void Write(const Blob &input) = 0; + virtual void Write(const byte *input, size_t insize) = 0; + virtual void Finalize() = 0; + virtual size_t GetAvailableSize() const = 0; + // Moves up to max (0: all) available output bytes into output, replacing its contents. + virtual size_t Read(Blob &output, size_t max = 0) = 0; + virtual size_t Read(byte *outbuffer, size_t buffersize) = 0; + virtual status_t GetStatus() const = 0; + virtual ~IAlgorithm() {} + }; +} // namespace act + +#endif // ACT_IALGORITHM_H diff --git a/port/thirdparty/cvact_compat/include/actIKey.h b/port/thirdparty/cvact_compat/include/actIKey.h new file mode 100644 index 000000000..a9ef45b3d --- /dev/null +++ b/port/thirdparty/cvact_compat/include/actIKey.h @@ -0,0 +1,33 @@ +// Source-compatible replacement for the cv act library (subset used by CrypTool). +#ifndef ACT_IKEY_H +#define ACT_IKEY_H + +#include "actBasics.h" +#include "actBlob.h" + +namespace act +{ + class IAlgorithm; + class IRNGAlg; + + class IKey + { + public: + virtual IKey *Clone() const = 0; + virtual void Import(const Blob &keyblob) = 0; + virtual void Export(Blob &keyblob, export_t type = DEFAULT) const = 0; + virtual void SetParam(paramid_t id, const Blob &blob) = 0; + virtual void SetParam(paramid_t id, int val) = 0; + virtual void SetParam(paramid_t id, const char *cstr) = 0; + virtual int GetParam(paramid_t id) const = 0; + virtual void GetParam(paramid_t id, Blob &blob) const = 0; + virtual void Generate(IRNGAlg *prng = 0) = 0; + virtual void Derive(const Blob &data, const Blob &salt = Blob()) = 0; + virtual IAlgorithm *CreateAlgorithm(mode_t Mode) const = 0; + virtual IAlgorithm *CreateAlgorithm(mode_t Mode, const Blob &data) const = 0; + virtual void *GetCreatePointer() const = 0; + virtual ~IKey() {} + }; +} // namespace act + +#endif // ACT_IKEY_H diff --git a/port/thirdparty/cvact_compat/include/actIRNGAlg.h b/port/thirdparty/cvact_compat/include/actIRNGAlg.h new file mode 100644 index 000000000..6e127d3a1 --- /dev/null +++ b/port/thirdparty/cvact_compat/include/actIRNGAlg.h @@ -0,0 +1,26 @@ +// Source-compatible replacement for the cv act library (subset used by CrypTool). +#ifndef ACT_IRNGAlg_h +#define ACT_IRNGAlg_h + +#include "actIAlgorithm.h" + +namespace act +{ + class Blob; + + class IRNGAlg : public IAlgorithm + { + public: + virtual IRNGAlg *Clone() const = 0; + virtual void Import(const Blob &keyblob) = 0; + virtual void Export(Blob &keyblob) const = 0; + virtual void SetParam(paramid_t id, const Blob &blob) = 0; + virtual void SetParam(paramid_t id, int val) = 0; + virtual void SetParam(paramid_t id, const char *cstr) = 0; + virtual int GetParam(paramid_t id) const = 0; + virtual void GetParam(paramid_t id, Blob &blob) const = 0; + virtual void *GetCreatePointer() const = 0; + }; +} // namespace act + +#endif // ACT_IRNGAlg_h diff --git a/port/thirdparty/cvact_compat/include/actInit.h b/port/thirdparty/cvact_compat/include/actInit.h new file mode 100644 index 000000000..a8588904e --- /dev/null +++ b/port/thirdparty/cvact_compat/include/actInit.h @@ -0,0 +1,18 @@ +// Source-compatible replacement for the cv act library (subset used by CrypTool). +#ifndef actInit_h +#define actInit_h + +#include "actBasics.h" +#include "actDefaultRNG.h" +#include "actRNGKit.h" + +namespace act +{ + const char *GetVersion(); + bool GetIsInit() noexcept; + void SetIsInit(bool bIsInit) noexcept; + + void Init(bool bAlwaysInit = false); +} // namespace act + +#endif // actInit_h diff --git a/port/thirdparty/cvact_compat/include/actKey.h b/port/thirdparty/cvact_compat/include/actKey.h new file mode 100644 index 000000000..2e2c7c97b --- /dev/null +++ b/port/thirdparty/cvact_compat/include/actKey.h @@ -0,0 +1,53 @@ +// Source-compatible replacement for the cv act library (subset used by CrypTool). +// Supported key types: "IES" (EC-IES over prime curves) and "BlockCipher". +#ifndef ACT_KEY_H +#define ACT_KEY_H + +#include "actBasics.h" +#include "actBlob.h" + +namespace act +{ + class IRNGAlg; + class IAlgorithm; + class IKey; + + class Key + { + public: + Key(); + Key(const Key &key); + Key(IKey *keyptr); + Key(const Blob &keyblob); + Key(const char *keytype); + + void Import(const Blob &keyblob); + void Export(Blob &keyblob, export_t type = DEFAULT) const; + + void SetParam(paramid_t id, const Blob &blob); + void SetParam(paramid_t id, int val); + void SetParam(paramid_t id, const char *cstr); + int GetParam(paramid_t id) const; + void GetParam(paramid_t id, Blob &blob) const; + + void Generate(IAlgorithm *prng = 0); + void Derive(const Blob &data, const Blob &salt = Blob()); + + IAlgorithm *CreateAlgorithm(mode_t Mode) const; + IAlgorithm *CreateAlgorithm(mode_t Mode, const Blob &data) const; + + IKey *GetPointer(); + const IKey *GetPointer() const; + operator IKey *(); + operator const IKey *() const; + IKey *ReleasePointer(); + + Key &operator=(const Key &key); + ~Key(); + + private: + IKey *mKey; + }; +} // namespace act + +#endif // ACT_KEY_H diff --git a/port/thirdparty/cvact_compat/include/actMode.h b/port/thirdparty/cvact_compat/include/actMode.h new file mode 100644 index 000000000..625929c13 --- /dev/null +++ b/port/thirdparty/cvact_compat/include/actMode.h @@ -0,0 +1,213 @@ +// Source-compatible replacement for the cv act library (subset used by CrypTool). +#ifndef ACT_MODE_H +#define ACT_MODE_H + +namespace act +{ + enum + { + ENCRYPT=100, + DECRYPT, + SIGN, + VERIFY, + MAC, + PRIVATE =200, + PUBLIC, + SECRET, + DOMAINPARAMS, + PKCS8_TYPE, + CIPHER = 300, + PADDING, + DERIVATOR, + BCMODE, + KEYSIZE, + RAWKEY, + IV, + BLOCKSIZE, + CHAINING_VALUE, + CHAINING_BITCOUNT, + EFFECTIVE_KEYSIZE, + SHAREDDATA_1, + SHAREDDATA_2, + KEYDATALEN, + ITERATIONCOUNT, + ENCODER=400, + PRIVATEKEY, + PUBLICKEY, + PUBLICSIZE, + KEY, + SIGNATURE, + AGREEMENT, + MODULO=500, + PRIME_P, + PRIME_Q, + MODULOSIZE, + PRIME_P_SIZE, + PRIME_Q_SIZE, + CRT_EXPONENT_1, + CRT_EXPONENT_2, + CRT_COEFFICIENT, + CURVE=600, + PUBLIC_X, + PUBLIC_Y, + STATICPUBLIC_X, + STATICPUBLIC_Y, + EPHEMERALPUBLIC_X, + EPHEMERALPUBLIC_Y, + CURVEPARAM, + PUBLICKEY_EXPORT_TYPE, + BASEPOINT_EXPORT_TYPE, + COMPRESSED, + UNCOMPRESSED, + HYBRID, + IMPLICITLYCA, + GF2CURVE, + PRIMECURVE, + RING = 700, + RINGORDER, + RINGGENERATOR, + COFACTOR, + COMPATIBLE, + NEWEPHEMERAL, + CHECKDOMAINPARAMS, + STATICPRIVATEKEY, + STATICPUBLICKEY, + EPHEMERALPRIVATEKEY, + EPHEMERALPUBLICKEY, + PIN = 800, + CARDTYPE, + CARDPORTTYPE, + PORTNR, + DLLNAME, + READERNAME, + CHANGEMASTERPIN, + CHANGEPIN, + USERPIN, + MASTERPIN, + PINPAD, + USERNAME, + INFOTEXT, + SIGNKEY, + ENCKEY, + SIGNCERTIFICATE, + ENCCERTIFICATE, + ROOTCERTIFICATE, + LOCK_KEY, + DHKEY, + DHCERTIFICATE, + PERSONALIZED, + READERNAMES, + AID, + NULLPIN, + SELECTKEY, + ALLCERTIFICATE, + APPLICATION, + KEYUSAGE, + MECHANISM, + CERTIFICATE, + KEYTYPE, + LABEL, + ID, + CTAPI_PORT = 900, + PCSC_PORT, + CARDOS_CARD = 1000, + TCOS_CARD, + CVACT_CARD, + PKCS11_CARD, + MICARDOEC_CARD, + STARCOSPK_CARD, + ETRUST_CARD, + CARDOSM4_CARD, + MICARDOP21_CARD, + HASH=1100, + SALT, + SALT_LENGTH, + EMLEN, + X = 1200, + MULTIPLIER, + VERSION = 1300, + ISSUER, + SUBJECT, + SERIALNR, + ISSUER_DER, + SUBJECT_DER, + ISSUER_UID, + SUBJECT_UID, + NOTBEFORE_DER, + NOTAFTER_DER, + THISUPDATE_DER, + NEXTUPDATE_DER, + TBS, + SECGp112r1=0x1000, + SECGp112r2, + SECGp128r1, + SECGp128r2, + SECGp160r1, + SECGp160r2, + SECGp160k1, + ANSIp192r1, + ANSIp192r2, + ANSIp192r3, + NISTp192r1, + SECGp192r1, + SECGp192k1, + NISTp224r1, + SECGp224r1, + SECGp224k1, + ANSIp239r1, + ANSIp239r2, + ANSIp239r3, + ANSIp256r1, + NISTp256r1, + SECGp256r1, + SECGp256k1, + NISTp384r1, + NISTp521r1, + SECGz113r1=0x2000, + SECGz113r2, + SECGz131r1, + SECGz131r2, + ANSIz163r1, + ANSIz163r2, + ANSIz163r3, + NISTz163r1, + NISTz163k1, + SECGz163r1, + SECGz163r2, + SECGz163k1, + ANSIz176w1, + ANSIz191r1, + ANSIz191r2, + ANSIz191r3, + SECGz193r1, + SECGz193r2, + ANSIz208w1, + NISTz233r1, + NISTz233k1, + SECGz233r1, + SECGz233k1, + ANSIz239r1, + ANSIz239r2, + ANSIz239r3, + SECGz239k1, + ANSIz272w1, + NISTz283r1, + NISTz283k1, + SECGz283r1, + SECGz283k1, + ANSIz304w1, + ANSIz359r1, + ANSIz368w1, + NISTz409r1, + NISTz409k1, + SECGz409r1, + SECGz409k1, + ANSIz431r1, + NISTz571r1, + NISTz571k1, + SECGz571r1, + SECGz571k1 + }; +} // namespace act + +#endif // ACT_MODE_H diff --git a/port/thirdparty/cvact_compat/include/actRNGKit.h b/port/thirdparty/cvact_compat/include/actRNGKit.h new file mode 100644 index 000000000..5052722fd --- /dev/null +++ b/port/thirdparty/cvact_compat/include/actRNGKit.h @@ -0,0 +1,15 @@ +// Source-compatible replacement for the cv act library (subset used by CrypTool). +// All generators are backed by the OpenSSL CSPRNG; written data is mixed in as seed. +#ifndef ACT_RNGKit_h +#define ACT_RNGKit_h + +namespace act +{ + class IRNGAlg; + + IRNGAlg *CreateDevRandomRNG(); + IRNGAlg *CreateBBS(); + IRNGAlg *CreateFIPS186(); +} // namespace act + +#endif // ACT_RNGKit_h diff --git a/port/thirdparty/cvact_compat/include/actState.h b/port/thirdparty/cvact_compat/include/actState.h new file mode 100644 index 000000000..c26351b15 --- /dev/null +++ b/port/thirdparty/cvact_compat/include/actState.h @@ -0,0 +1,16 @@ +// Source-compatible replacement for the cv act library (subset used by CrypTool). +#ifndef ACT_STATE_H +#define ACT_STATE_H + +namespace act +{ + const int READY = 0; + const int SIGNATURE_OK = 1; + const int IS_FINALIZED = 2; + const int DECRYPT_ERROR = 3; + const int CERTIFICATE_OK = 4; + const int CERTIFICATE_ERROR = 5; + const int VERIFY_ERROR = 6; +} // namespace act + +#endif // ACT_STATE_H diff --git a/port/thirdparty/cvact_compat/include/actTools.h b/port/thirdparty/cvact_compat/include/actTools.h new file mode 100644 index 000000000..5b92b311e --- /dev/null +++ b/port/thirdparty/cvact_compat/include/actTools.h @@ -0,0 +1,40 @@ +// Source-compatible replacement for the cv act library (subset used by CrypTool). +#ifndef ACT_TOOLS_H +#define ACT_TOOLS_H + +#include "actMode.h" +#include "actBlob.h" +#include "actIKey.h" + +namespace act +{ + // Encodes a binary object identifier, e.g. EncodeOID("1.2.840.10045.2.1"). + Blob EncodeOID(const char *oid); + + // Signed decimal or hexadecimal ("0x" / "-0x") number to big-endian two's complement. + Blob EncodeNumber(const char *number); + Blob EncodeNumber(int number); + + // Unsigned hexadecimal number (optional "0x" prefix) to Blob and back. + // blob2hex writes 2 * b.size() lowercase digits plus a terminating NUL. + Blob hex2blob(const char *hexnumber); + void blob2hex(const Blob &b, char *hexnumber); + + bool blob2file(const char *filename, const act::Blob &blob); + bool file2blob(const char *filename, Blob &blob); + + unsigned long CalculateCRC16(const act::byte *message, size_t message_len, + unsigned long crc_init_value = 0x0000); + unsigned long CalculateCRC16(const act::Blob &message, + unsigned long crc_init_value = 0x0000); + unsigned long CalculateCRC16CCITT(const act::byte *message, size_t message_len, + unsigned long crc_init_value = 0xFFFF); + unsigned long CalculateCRC16CCITT(const act::Blob &message, + unsigned long crc_init_value = 0xFFFF); + unsigned long CalculateCRC32(const act::byte *message, size_t message_len, + unsigned long crc_init_value = 0xFFFFFFFF); + unsigned long CalculateCRC32(const act::Blob &message, + unsigned long crc_init_value = 0xFFFFFFFF); +} // namespace act + +#endif // ACT_TOOLS_H diff --git a/port/thirdparty/cvact_compat/src/Algorithm.cpp b/port/thirdparty/cvact_compat/src/Algorithm.cpp new file mode 100644 index 000000000..b930c2f93 --- /dev/null +++ b/port/thirdparty/cvact_compat/src/Algorithm.cpp @@ -0,0 +1,232 @@ +#include "actInternal.h" +#include "actInit.h" +#include "actRNGKit.h" +#include "actDefaultRNG.h" + +#include +#include + +#include +#include + +namespace act +{ + namespace detail + { + size_t BufferedAlgorithm::Read(Blob &output, size_t max) + { + size_t n = mOut.size(); + if (max != 0 && max < n) + n = max; + output.assign(mOut.begin(), mOut.begin() + n); + mOut.erase(mOut.begin(), mOut.begin() + n); + return n; + } + + size_t BufferedAlgorithm::Read(byte *outbuffer, size_t buffersize) + { + size_t n = mOut.size() < buffersize ? mOut.size() : buffersize; + if (n != 0) + std::memcpy(outbuffer, mOut.begin(), n); + mOut.erase(mOut.begin(), mOut.begin() + n); + return n; + } + + class HashAlgorithm : public BufferedAlgorithm + { + public: + explicit HashAlgorithm(const EVP_MD *md) : mCtx(EVP_MD_CTX_new()) + { + if (mCtx == 0 || EVP_DigestInit_ex(mCtx, md, 0) != 1) + { + EVP_MD_CTX_free(mCtx); + throw AlgorithmException("hash initialisation failed", "HashAlgorithm"); + } + } + ~HashAlgorithm() { EVP_MD_CTX_free(mCtx); } + + void Write(const Blob &input) { Write(input.begin(), input.size()); } + void Write(const byte *input, size_t insize) + { + if (mStatus == IS_FINALIZED) + throw AlgorithmException("algorithm already finalized", "HashAlgorithm::Write"); + if (insize != 0 && EVP_DigestUpdate(mCtx, input, insize) != 1) + throw AlgorithmException("hash update failed", "HashAlgorithm::Write"); + } + void Finalize() + { + if (mStatus == IS_FINALIZED) + return; + unsigned char md[EVP_MAX_MD_SIZE]; + unsigned int len = 0; + if (EVP_DigestFinal_ex(mCtx, md, &len) != 1) + throw AlgorithmException("hash finalisation failed", "HashAlgorithm::Finalize"); + mOut.insert(mOut.end(), md, md + len); + mStatus = IS_FINALIZED; + } + + private: + EVP_MD_CTX *mCtx; + }; + + IAlgorithm *CreateHashAlgorithm(const char *name) + { + static const struct { const char *act; const char *ossl; } names[] = { + {"MD5", "MD5"}, {"SHA1", "SHA1"}, {"SHA224", "SHA224"}, {"SHA256", "SHA256"}, + {"SHA384", "SHA384"}, {"SHA512", "SHA512"}, {"RIPEMD160", "RIPEMD160"}, + }; + for (size_t i = 0; i < sizeof(names) / sizeof(names[0]); ++i) + { + if (std::strcmp(name, names[i].act) != 0) + continue; + EVP_MD *md = EVP_MD_fetch(0, names[i].ossl, 0); + if (md == 0) + break; + try + { + IAlgorithm *alg = new HashAlgorithm(md); + EVP_MD_free(md); + return alg; + } + catch (...) + { + EVP_MD_free(md); + throw; + } + } + return 0; + } + + class OpenSSLRng : public IRNGAlg + { + public: + explicit OpenSSLRng(CreateRNGPtr factory) : mFactory(factory) {} + + void Write(const Blob &input) { Write(input.begin(), input.size()); } + void Write(const byte *input, size_t insize) + { + if (insize != 0) + RAND_seed(input, static_cast(insize)); + } + void Finalize() {} + size_t GetAvailableSize() const { return 0; } + size_t Read(Blob &output, size_t max) + { + size_t n = max != 0 ? max : output.size(); + output.resize(n); + RandomBytes(output.begin(), n, 0); + return n; + } + size_t Read(byte *outbuffer, size_t buffersize) + { + RandomBytes(outbuffer, buffersize, 0); + return buffersize; + } + status_t GetStatus() const { return READY; } + + IRNGAlg *Clone() const { return new OpenSSLRng(mFactory); } + void Import(const Blob &keyblob) { Write(keyblob); } + void Export(Blob &keyblob) const { keyblob.clear(); } + void SetParam(paramid_t, const Blob &blob) { Write(blob); } + void SetParam(paramid_t, int) {} + void SetParam(paramid_t, const char *) {} + int GetParam(paramid_t) const { return 0; } + void GetParam(paramid_t, Blob &blob) const { blob.clear(); } + void *GetCreatePointer() const { return reinterpret_cast(mFactory); } + + private: + CreateRNGPtr mFactory; + }; + } // namespace detail + + IRNGAlg *CreateBBS() { return new detail::OpenSSLRng(&CreateBBS); } + IRNGAlg *CreateFIPS186() { return new detail::OpenSSLRng(&CreateFIPS186); } + IRNGAlg *CreateDevRandomRNG() { return new detail::OpenSSLRng(&CreateDevRandomRNG); } + + CreateRNGPtr CreateFastRNG = &CreateFIPS186; + CreateRNGPtr CreateStrongRNG = &CreateBBS; + + namespace + { + bool g_isInit = false; + } + + const char *GetVersion() + { + return "cvact_compat 1.4.6 (OpenSSL backend)"; + } + + bool GetIsInit() noexcept { return g_isInit; } + void SetIsInit(bool bIsInit) noexcept { g_isInit = bIsInit; } + + void Init(bool bAlwaysInit) + { + if (g_isInit && !bAlwaysInit) + return; + CreateFastRNG = &CreateFIPS186; + CreateStrongRNG = &CreateBBS; + g_isInit = true; + } + + Algorithm::Algorithm(IAlgorithm *alg) : mAlg(alg) + { + if (mAlg == 0) + throw NullPointerException("null algorithm", "Algorithm::Algorithm"); + } + + Algorithm::Algorithm(const IKey *key, mode_t mode) : mAlg(0) + { + if (key == 0) + throw NullPointerException("null key", "Algorithm::Algorithm"); + mAlg = key->CreateAlgorithm(mode); + if (mAlg == 0) + throw InvalidAlgorithmParameterException("mode not supported by key", "Algorithm::Algorithm"); + } + + Algorithm::Algorithm(const IKey *key, mode_t mode, const Blob &data) : mAlg(0) + { + if (key == 0) + throw NullPointerException("null key", "Algorithm::Algorithm"); + mAlg = key->CreateAlgorithm(mode, data); + if (mAlg == 0) + throw InvalidAlgorithmParameterException("mode not supported by key", "Algorithm::Algorithm"); + } + + Algorithm::Algorithm(const char *name) : mAlg(0) + { + if (name == 0) + throw NullPointerException("null algorithm name", "Algorithm::Algorithm"); + if (std::strcmp(name, "BBS") == 0) + mAlg = CreateBBS(); + else if (std::strcmp(name, "FIPS186") == 0) + mAlg = CreateFIPS186(); + else if (std::strcmp(name, "DevRandom") == 0) + mAlg = CreateDevRandomRNG(); + else + mAlg = detail::CreateHashAlgorithm(name); + if (mAlg == 0) + throw NoSuchAlgorithmException("unknown algorithm", "Algorithm::Algorithm"); + } + + Algorithm::~Algorithm() { delete mAlg; } + + void Algorithm::Write(const Blob &indata) { mAlg->Write(indata); } + void Algorithm::Write(const byte *indata, size_t insize) { mAlg->Write(indata, insize); } + void Algorithm::Finalize() { mAlg->Finalize(); } + size_t Algorithm::Read(Blob &outdata, size_t max) { return mAlg->Read(outdata, max); } + size_t Algorithm::Read(byte *outbuffer, size_t buffersize) { return mAlg->Read(outbuffer, buffersize); } + size_t Algorithm::GetAvailableSize() const { return mAlg->GetAvailableSize(); } + status_t Algorithm::GetStatus() const { return mAlg->GetStatus(); } + + Algorithm::operator IAlgorithm *() { return mAlg; } + Algorithm::operator const IAlgorithm *() const { return mAlg; } + IAlgorithm *Algorithm::GetPointer() { return mAlg; } + const IAlgorithm *Algorithm::GetPointer() const { return mAlg; } + + IAlgorithm *Algorithm::ReleasePointer() + { + IAlgorithm *p = mAlg; + mAlg = 0; + return p; + } +} // namespace act diff --git a/port/thirdparty/cvact_compat/src/Blob.cpp b/port/thirdparty/cvact_compat/src/Blob.cpp new file mode 100644 index 000000000..2c8731c7e --- /dev/null +++ b/port/thirdparty/cvact_compat/src/Blob.cpp @@ -0,0 +1,279 @@ +#include "actBlob.h" + +#include + +#include +#include +#include +#include + +namespace act +{ + namespace + { + // Shared terminator for blobs without own storage; never written to by Blob itself. + byte g_empty[1] = {0}; + } + + void SecureZero(void *p, size_t n) noexcept + { + if (p != 0 && n != 0) + OPENSSL_cleanse(p, n); + } + + Blob::Blob() noexcept : mFirst(g_empty), mSize(0), mCap(0) {} + + Blob::Blob(const char *str) : Blob() + { + if (str != 0) + insert_raw(end(), reinterpret_cast(str), std::strlen(str)); + } + + Blob::Blob(std::string &str) : Blob() + { + insert_raw(end(), reinterpret_cast(str.data()), str.size()); + } + + Blob::Blob(size_type n, byte v) : Blob() + { + insert(end(), n, v); + } + + Blob::Blob(const Blob &x) : Blob() + { + insert_raw(end(), x.mFirst, x.mSize); + } + + Blob::Blob(Blob &&x) noexcept : Blob() + { + swap(x); + } + + Blob::~Blob() + { + release(); + } + + Blob &Blob::operator=(const Blob &x) + { + if (this != &x) + { + if (x.mSize <= mCap) + { + if (x.mSize != 0) + std::memmove(mFirst, x.mFirst, x.mSize); + if (mSize > x.mSize) + SecureZero(mFirst + x.mSize, mSize - x.mSize); + mSize = x.mSize; + if (mCap != 0) + mFirst[mSize] = 0; + } + else + { + Blob tmp(x); + swap(tmp); + } + } + return *this; + } + + Blob &Blob::operator=(Blob &&x) noexcept + { + if (this != &x) + { + release(); + swap(x); + } + return *this; + } + + void Blob::assign(size_type n, byte x) + { + clear(); + insert(end(), n, x); + } + + void Blob::release() noexcept + { + if (mCap != 0) + { + SecureZero(mFirst, mCap + 1); + std::free(mFirst); + } + mFirst = g_empty; + mSize = 0; + mCap = 0; + } + + void Blob::reallocate(size_type cap) + { + byte *p = static_cast(std::malloc(cap + 1)); + if (p == 0) + throw BadAllocException("out of memory", "Blob::reallocate"); + if (mSize != 0) + std::memcpy(p, mFirst, mSize); + std::memset(p + mSize, 0, cap + 1 - mSize); + size_type size = mSize; + release(); + mFirst = p; + mSize = size; + mCap = cap; + } + + void Blob::reserve(size_type n) + { + if (n > mCap) + reallocate(n); + } + + Blob::iterator Blob::make_gap(iterator p, size_type m) + { + size_type off = static_cast(p - mFirst); + if (off > mSize) + outofrange(); + if (m == 0) + return mFirst + off; + if (mSize + m > mCap) + { + size_type cap = mCap * 2; + if (cap < mSize + m) + cap = mSize + m; + if (cap < 16) + cap = 16; + reallocate(cap); + } + std::memmove(mFirst + off + m, mFirst + off, mSize - off); + mSize += m; + mFirst[mSize] = 0; + return mFirst + off; + } + + Blob::iterator Blob::insert_raw(iterator p, const byte *src, size_type m) + { + iterator q = make_gap(p, m); + if (m != 0) + std::memcpy(q, src, m); + return q; + } + + Blob::iterator Blob::insert(iterator p, byte x) + { + return insert_raw(p, &x, 1); + } + + void Blob::insert(iterator p, size_type m, byte x) + { + iterator q = make_gap(p, m); + if (m != 0) + std::memset(q, x, m); + } + + void Blob::resize(size_type n, byte x) + { + if (n > mSize) + insert(end(), n - mSize, x); + else if (n < mSize) + erase(begin() + n, end()); + } + + Blob::const_reference Blob::at(size_type p) const + { + if (p >= mSize) + outofrange(); + return mFirst[p]; + } + + Blob::reference Blob::at(size_type p) + { + if (p >= mSize) + outofrange(); + return mFirst[p]; + } + + void Blob::push_back(byte x) + { + insert_raw(end(), &x, 1); + } + + void Blob::pop_back() + { + if (mSize != 0) + erase(end() - 1); + } + + void Blob::append(const Blob &x) + { + if (&x == this) + { + Blob tmp(x); + insert_raw(end(), tmp.mFirst, tmp.mSize); + } + else + insert_raw(end(), x.mFirst, x.mSize); + } + + void Blob::append(size_type m, byte x) + { + insert(end(), m, x); + } + + Blob::iterator Blob::erase(iterator p) + { + return erase(p, p + 1); + } + + Blob::iterator Blob::erase(iterator f, iterator l) + { + size_type off = static_cast(f - mFirst); + size_type last = static_cast(l - mFirst); + if (off > last || last > mSize) + outofrange(); + size_type m = last - off; + if (m != 0) + { + std::memmove(mFirst + off, mFirst + last, mSize - last); + SecureZero(mFirst + mSize - m, m); + mSize -= m; + } + return mFirst + off; + } + + void Blob::clear() noexcept + { + if (mCap != 0) + SecureZero(mFirst, mSize); + mSize = 0; + } + + void Blob::swap(Blob &x) noexcept + { + std::swap(mFirst, x.mFirst); + std::swap(mSize, x.mSize); + std::swap(mCap, x.mCap); + } + + bool Blob::_eq(const Blob &x) const + { + return mSize == x.mSize && (mSize == 0 || std::memcmp(mFirst, x.mFirst, mSize) == 0); + } + + bool Blob::_lt(const Blob &x) const + { + size_type n = mSize < x.mSize ? mSize : x.mSize; + int c = n == 0 ? 0 : std::memcmp(mFirst, x.mFirst, n); + return c < 0 || (c == 0 && mSize < x.mSize); + } + + void Blob::outofrange() const + { + throw OutOfRangeException("invalid Blob subscript", "Blob::outofrange"); + } + + std::ostream &operator<<(std::ostream &os, const Blob &blob) + { + static const char digits[] = "0123456789abcdef"; + for (Blob::const_iterator it = blob.begin(); it != blob.end(); ++it) + os << digits[*it >> 4] << digits[*it & 0x0f]; + return os; + } +} // namespace act diff --git a/port/thirdparty/cvact_compat/src/BlockCipherKey.cpp b/port/thirdparty/cvact_compat/src/BlockCipherKey.cpp new file mode 100644 index 000000000..ff923f0c6 --- /dev/null +++ b/port/thirdparty/cvact_compat/src/BlockCipherKey.cpp @@ -0,0 +1,408 @@ +// "BlockCipher" key: AES (alias Rijndael) or TripleDES in ECB/CBC/CFB/OFB/CTR mode, +// PKCS#5 padding (or NOPAD) for ECB/CBC. Without an explicit IV a random IV is +// generated on encryption and prepended to the ciphertext; decryption without an +// explicit IV takes the IV from the first block of the ciphertext. +#include "actInternal.h" + +#include + +#include +#include + +namespace act +{ + namespace detail + { + namespace + { + struct CipherInfo + { + const char *name; + size_t blockSize; + size_t keySizes[3]; + }; + + const CipherInfo kCiphers[] = { + {"AES", 16, {16, 24, 32}}, + {"TripleDES", 8, {16, 24, 0}}, + }; + + const CipherInfo *FindCipher(const std::string &name) + { + std::string n = name == "Rijndael" ? "AES" : name; + for (size_t i = 0; i < sizeof(kCiphers) / sizeof(kCiphers[0]); ++i) + if (n == kCiphers[i].name) + return &kCiphers[i]; + return 0; + } + + bool IsSupportedMode(const std::string &mode) + { + return mode == "ECB" || mode == "CBC" || mode == "CFB" || mode == "OFB" || mode == "CTR"; + } + + std::string EvpCipherName(const CipherInfo &info, const std::string &mode, size_t keySize) + { + if (std::strcmp(info.name, "AES") == 0) + return "AES-" + std::to_string(keySize * 8) + "-" + mode; + return std::string(keySize == 16 ? "DES-EDE-" : "DES-EDE3-") + mode; + } + + std::string ToString(const Blob &b) + { + return std::string(reinterpret_cast(b.begin()), b.size()); + } + + class BlockCipherAlgorithm : public BufferedAlgorithm + { + public: + BlockCipherAlgorithm(const std::string &evpName, const Blob &key, const Blob &iv, + bool explicitIV, bool encrypt, bool padding) + : mCtx(EVP_CIPHER_CTX_new()), mCipher(EVP_CIPHER_fetch(0, evpName.c_str(), 0)), + mKey(key), mEncrypt(encrypt), mPadding(padding), mStarted(false), mIVLen(0) + { + if (mCtx == 0 || mCipher == 0) + { + Cleanup(); + throw NoSuchAlgorithmException("cipher not available", "BlockCipherAlgorithm"); + } + try + { + mIVLen = static_cast(EVP_CIPHER_get_iv_length(mCipher)); + if (mIVLen == 0 || explicitIV) + { + if (mIVLen != 0 && iv.size() != mIVLen) + throw InvalidAlgorithmParameterException("invalid IV length", "BlockCipherAlgorithm"); + Start(iv); + } + else if (mEncrypt) + { + Blob random(mIVLen); + RandomBytes(random.begin(), random.size(), 0); + mOut.append(random); + Start(random); + } + } + catch (...) + { + Cleanup(); + throw; + } + } + + ~BlockCipherAlgorithm() { Cleanup(); } + + void Write(const Blob &input) { Write(input.begin(), input.size()); } + + void Write(const byte *input, size_t insize) + { + if (mStatus == IS_FINALIZED) + throw AlgorithmException("algorithm already finalized", "BlockCipherAlgorithm::Write"); + if (!mStarted) + { + size_t take = mIVLen - mPendingIV.size(); + if (take > insize) + take = insize; + mPendingIV.insert(mPendingIV.end(), input, input + take); + input += take; + insize -= take; + if (mPendingIV.size() < mIVLen) + return; + Start(mPendingIV); + } + if (insize == 0) + return; + size_t pos = mOut.size(); + mOut.resize(pos + insize + EVP_MAX_BLOCK_LENGTH); + int outl = 0; + if (EVP_CipherUpdate(mCtx, mOut.begin() + pos, &outl, input, static_cast(insize)) != 1) + { + mOut.resize(pos); + throw AlgorithmException("cipher update failed", "BlockCipherAlgorithm::Write"); + } + mOut.resize(pos + static_cast(outl)); + } + + void Finalize() + { + if (mStatus == IS_FINALIZED) + return; + if (!mStarted) + { + mStatus = DECRYPT_ERROR; + throw AlgorithmException("ciphertext too short", "BlockCipherAlgorithm::Finalize"); + } + size_t pos = mOut.size(); + mOut.resize(pos + EVP_MAX_BLOCK_LENGTH); + int outl = 0; + if (EVP_CipherFinal_ex(mCtx, mOut.begin() + pos, &outl) != 1) + { + mOut.clear(); + mStatus = DECRYPT_ERROR; + throw PaddingException("bad padding, wrong key or wrong data length", "BlockCipherAlgorithm::Finalize"); + } + mOut.resize(pos + static_cast(outl)); + mStatus = IS_FINALIZED; + } + + private: + void Start(const Blob &iv) + { + if (EVP_CipherInit_ex2(mCtx, mCipher, mKey.begin(), iv.empty() ? 0 : iv.begin(), + mEncrypt ? 1 : 0, 0) != 1 + || EVP_CIPHER_CTX_set_padding(mCtx, mPadding ? 1 : 0) != 1) + throw AlgorithmException("cipher initialisation failed", "BlockCipherAlgorithm"); + mStarted = true; + } + + void Cleanup() + { + EVP_CIPHER_CTX_free(mCtx); + EVP_CIPHER_free(mCipher); + mCtx = 0; + mCipher = 0; + } + + EVP_CIPHER_CTX *mCtx; + EVP_CIPHER *mCipher; + Blob mKey; + Blob mPendingIV; + bool mEncrypt; + bool mPadding; + bool mStarted; + size_t mIVLen; + }; + + IKey *NewBlockCipherKey(); + + class BlockCipherKey : public IKey + { + public: + BlockCipherKey() + : mCipher("AES"), mMode("CBC"), mPadding("PKCS5"), mDerivator("KDF1"), mHash("SHA512"), + mKeySize(10), mHasKey(false), mHasIV(false) {} + + IKey *Clone() const { return new BlockCipherKey(*this); } + + void Import(const Blob &keyblob) { SetParam(RAWKEY, keyblob); } + + void Export(Blob &keyblob, export_t type) const + { + if (type != DEFAULT && type != SECRET) + throw InvalidKeyException("unsupported export type", "BlockCipherKey::Export"); + GetParam(RAWKEY, keyblob); + } + + void SetParam(paramid_t id, const Blob &blob) + { + switch (id) + { + case RAWKEY: + if (!IsSupportedKeySize(blob.size())) + throw InvalidKeyException("unsupported key size", "BlockCipherKey::SetParam"); + mRawKey = blob; + mKeySize = static_cast(blob.size()); + mHasKey = true; + break; + case IV: + if (blob.size() != Info().blockSize) + throw InvalidAlgorithmParameterException("invalid IV length", "BlockCipherKey::SetParam"); + mIV = blob; + mHasIV = true; + break; + case CIPHER: + case BCMODE: + case PADDING: + case DERIVATOR: + case HASH: + SetParam(id, ToString(blob).c_str()); + break; + default: + throw InvalidKeyException("unsupported parameter", "BlockCipherKey::SetParam"); + } + } + + void SetParam(paramid_t id, int val) + { + if (id != KEYSIZE || val <= 0) + throw InvalidKeyException("unsupported parameter", "BlockCipherKey::SetParam"); + mKeySize = val; + } + + void SetParam(paramid_t id, const char *cstr) + { + if (cstr == 0) + throw NullPointerException("null parameter", "BlockCipherKey::SetParam"); + std::string value(cstr); + switch (id) + { + case CIPHER: + if (FindCipher(value) == 0) + throw NoSuchAlgorithmException("unsupported block cipher", "BlockCipherKey::SetParam"); + mCipher = value == "Rijndael" ? "AES" : value; + mRawKey.clear(); + mHasKey = false; + mIV.clear(); + mHasIV = false; + break; + case BCMODE: + if (!IsSupportedMode(value)) + throw NoSuchAlgorithmException("unsupported block cipher mode", "BlockCipherKey::SetParam"); + mMode = value; + break; + case PADDING: + if (value != "PKCS5" && value != "NOPAD") + throw NoSuchAlgorithmException("unsupported padding", "BlockCipherKey::SetParam"); + mPadding = value; + break; + case DERIVATOR: + if (value != "KDF1" && value != "KDF2" && value != "X963KDF") + throw NoSuchAlgorithmException("unsupported derivator", "BlockCipherKey::SetParam"); + mDerivator = value; + break; + case HASH: + Digest(value.c_str(), Blob()); + mHash = value; + break; + default: + throw InvalidKeyException("unsupported parameter", "BlockCipherKey::SetParam"); + } + } + + int GetParam(paramid_t id) const + { + switch (id) + { + case KEYSIZE: + return mHasKey ? static_cast(mRawKey.size()) : static_cast(EffectiveKeySize()); + case BLOCKSIZE: + return static_cast(Info().blockSize); + default: + throw InvalidKeyException("unsupported parameter", "BlockCipherKey::GetParam"); + } + } + + void GetParam(paramid_t id, Blob &blob) const + { + switch (id) + { + case RAWKEY: + if (!mHasKey) + throw InvalidKeyException("no key available", "BlockCipherKey::GetParam"); + blob = mRawKey; + break; + case IV: + blob = mIV; + break; + case CIPHER: blob = Blob(mCipher.c_str()); break; + case BCMODE: blob = Blob(mMode.c_str()); break; + case PADDING: blob = Blob(mPadding.c_str()); break; + case DERIVATOR: blob = Blob(mDerivator.c_str()); break; + case HASH: blob = Blob(mHash.c_str()); break; + default: + throw InvalidKeyException("unsupported parameter", "BlockCipherKey::GetParam"); + } + } + + void Generate(IRNGAlg *prng) + { + Blob key(EffectiveKeySize()); + RandomBytes(key.begin(), key.size(), prng); + SetParam(RAWKEY, key); + } + + // KDF1 (IEEE 1363): Hash(data || salt); KDF2/X963KDF: ANSI X9.63 KDF with salt as SharedInfo. + void Derive(const Blob &data, const Blob &salt) + { + size_t len = EffectiveKeySize(); + Blob key; + if (mDerivator == "KDF1") + { + Blob input(data); + input.append(salt); + key = Digest(mHash.c_str(), input); + SecureZero(input.begin(), input.size()); + if (key.size() < len) + throw InvalidAlgorithmParameterException("KDF1 output too short for key size", "BlockCipherKey::Derive"); + key.resize(len); + } + else + key = X963Kdf(mHash.c_str(), data, salt, len); + SetParam(RAWKEY, key); + } + + IAlgorithm *CreateAlgorithm(mode_t Mode) const + { + IAlgorithm *alg = Create(Mode, mIV, mHasIV); + if (Mode == ENCRYPT) + { + mIV.clear(); + mHasIV = false; + } + return alg; + } + + IAlgorithm *CreateAlgorithm(mode_t Mode, const Blob &data) const + { + return Create(Mode, data, true); + } + + void *GetCreatePointer() const { return reinterpret_cast(&NewBlockCipherKey); } + + private: + const CipherInfo &Info() const { return *FindCipher(mCipher); } + + bool IsSupportedKeySize(size_t n) const + { + const CipherInfo &info = Info(); + for (size_t i = 0; i < 3; ++i) + if (info.keySizes[i] != 0 && info.keySizes[i] == n) + return true; + return false; + } + + size_t EffectiveKeySize() const + { + const CipherInfo &info = Info(); + size_t largest = 0; + for (size_t i = 0; i < 3; ++i) + { + size_t s = info.keySizes[i]; + if (s == 0) + continue; + if (s >= static_cast(mKeySize)) + return s; + largest = s; + } + return largest; + } + + IAlgorithm *Create(mode_t Mode, const Blob &iv, bool explicitIV) const + { + if (Mode != ENCRYPT && Mode != DECRYPT) + throw InvalidAlgorithmParameterException("unsupported mode", "BlockCipherKey::CreateAlgorithm"); + if (!mHasKey) + throw InvalidKeyException("no key available", "BlockCipherKey::CreateAlgorithm"); + bool padding = (mMode == "CBC" || mMode == "ECB") && mPadding == "PKCS5"; + return new BlockCipherAlgorithm(EvpCipherName(Info(), mMode, mRawKey.size()), + mRawKey, iv, explicitIV, Mode == ENCRYPT, padding); + } + + std::string mCipher; + std::string mMode; + std::string mPadding; + std::string mDerivator; + std::string mHash; + int mKeySize; + Blob mRawKey; + mutable Blob mIV; + bool mHasKey; + mutable bool mHasIV; + }; + + IKey *NewBlockCipherKey() { return new BlockCipherKey(); } + } // namespace + + IKey *CreateBlockCipherKey() { return NewBlockCipherKey(); } + } // namespace detail +} // namespace act diff --git a/port/thirdparty/cvact_compat/src/Date.cpp b/port/thirdparty/cvact_compat/src/Date.cpp new file mode 100644 index 000000000..0de93bea1 --- /dev/null +++ b/port/thirdparty/cvact_compat/src/Date.cpp @@ -0,0 +1,311 @@ +#include "actDate.h" + +#include "actBlob.h" +#include "actException.h" + +#include +#include +#include + +namespace act +{ + namespace + { + long long FloorDiv(long long a, long long b) + { + long long q = a / b; + if ((a % b != 0) && ((a < 0) != (b < 0))) + --q; + return q; + } + + int Digits(const unsigned char *p, int n, bool &ok) + { + int v = 0; + for (int i = 0; i < n; ++i) + { + if (p[i] < '0' || p[i] > '9') + ok = false; + v = v * 10 + (p[i] - '0'); + } + return v; + } + } + + Date::Date() : m_day(1), m_month(1), m_year(1970), m_hour(0), m_min(0), m_sec(0), m_ignore_time(false) + { + SetToday(); + } + + Date::Date(int day, int month, int year) + : m_day(day), m_month(month), m_year(year), m_hour(0), m_min(0), m_sec(0), m_ignore_time(false) + { + if (!IsValid()) + throw BadException("invalid date", "Date::Date"); + } + + Date::Date(int day, int month, int year, int hour, int minute, int sec) + : m_day(day), m_month(month), m_year(year), m_hour(hour), m_min(minute), m_sec(sec), m_ignore_time(false) + { + if (!IsValid()) + throw BadException("invalid date", "Date::Date"); + } + + Date::Date(const Blob &der) + : m_day(1), m_month(1), m_year(1970), m_hour(0), m_min(0), m_sec(0), m_ignore_time(false) + { + Decode(der.begin(), der.size()); + } + + Date::Date(const unsigned char *ch, const unsigned int size) + : m_day(1), m_month(1), m_year(1970), m_hour(0), m_min(0), m_sec(0), m_ignore_time(false) + { + Decode(ch, size); + } + + void Date::Decode(const unsigned char *ch, size_t size) + { + if (ch == 0) + throw NullPointerException("null date encoding", "Date::Decode"); + if (size >= 2 && (ch[0] == 0x17 || ch[0] == 0x18) && ch[1] == size - 2) + { + ch += 2; + size -= 2; + } + bool ok = true; + if (size == 13 && ch[12] == 'Z') + { + int yy = Digits(ch, 2, ok); + m_year = yy < 50 ? 2000 + yy : 1900 + yy; + ch += 2; + } + else if (size == 15 && ch[14] == 'Z') + { + m_year = Digits(ch, 4, ok); + ch += 4; + } + else + throw ASN1Exception("unsupported time encoding", "Date::Decode"); + m_month = Digits(ch, 2, ok); + m_day = Digits(ch + 2, 2, ok); + m_hour = Digits(ch + 4, 2, ok); + m_min = Digits(ch + 6, 2, ok); + m_sec = Digits(ch + 8, 2, ok); + if (!ok || !IsValid()) + throw ASN1Exception("invalid time encoding", "Date::Decode"); + } + + Date &Date::SetToday() + { + std::time_t now = std::time(0); + struct tm t; +#if defined(_WIN32) + gmtime_s(&t, &now); +#else + gmtime_r(&now, &t); +#endif + copytmDate(t, *this); + return *this; + } + + void Date::IgnoreTime(bool b) + { + m_ignore_time = b; + } + + const Date &Date::operator=(const Date &other) + { + m_day = other.m_day; + m_month = other.m_month; + m_year = other.m_year; + m_hour = other.m_hour; + m_min = other.m_min; + m_sec = other.m_sec; + m_ignore_time = other.m_ignore_time; + return *this; + } + + int Date::Compare(const Date &other) const + { + long a = GetJulian(); + long b = other.GetJulian(); + if (a != b) + return a < b ? -1 : 1; + if (m_ignore_time || other.m_ignore_time) + return 0; + long sa = m_hour * 3600L + m_min * 60L + m_sec; + long sb = other.m_hour * 3600L + other.m_min * 60L + other.m_sec; + return sa < sb ? -1 : (sa > sb ? 1 : 0); + } + + bool Date::operator>(const Date &d) const { return Compare(d) > 0; } + bool Date::operator>=(const Date &d) const { return Compare(d) >= 0; } + bool Date::operator<(const Date &d) const { return Compare(d) < 0; } + bool Date::operator<=(const Date &d) const { return Compare(d) <= 0; } + bool Date::operator==(const Date &d) const { return Compare(d) == 0; } + bool Date::operator!=(const Date &d) const { return Compare(d) != 0; } + + const Date &Date::AddMonths(int m) + { + long long total = static_cast(m_year) * 12 + (m_month - 1) + m; + m_year = static_cast(FloorDiv(total, 12)); + m_month = static_cast(total - static_cast(m_year) * 12) + 1; + AdjustDays(); + return *this; + } + + const Date &Date::SubMonths(int m) { return AddMonths(-m); } + const Date &Date::AddYears(int y) { m_year += y; AdjustDays(); return *this; } + const Date &Date::SubYears(int y) { return AddYears(-y); } + const Date &Date::AddDays(int d) { SetFromSeconds(SecondsSinceEpochDay() + 86400LL * d); return *this; } + const Date &Date::SubDays(int d) { return AddDays(-d); } + const Date &Date::AddHours(int h) { SetFromSeconds(SecondsSinceEpochDay() + 3600LL * h); return *this; } + const Date &Date::SubHours(int h) { return AddHours(-h); } + const Date &Date::AddMinutes(int m) { SetFromSeconds(SecondsSinceEpochDay() + 60LL * m); return *this; } + const Date &Date::SubMinutes(int m) { return AddMinutes(-m); } + const Date &Date::AddSecond(int m) { SetFromSeconds(SecondsSinceEpochDay() + m); return *this; } + const Date &Date::SubSecond(int m) { return AddSecond(-m); } + + int Date::DayOfWeek() const + { + return static_cast((GetJulian() + 1) % 7); + } + + int Date::IsLeap(int y) const + { + return (y % 4 == 0 && y % 100 != 0) || y % 400 == 0 ? 1 : 0; + } + + int Date::DaysPerMonth(int m, int y) const + { + static const int days[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + if (m < 1 || m > 12) + return 0; + return m == 2 ? 28 + IsLeap(y) : days[m - 1]; + } + + long Date::GetDifference(const Date &d2) const + { + return GetJulian() - d2.GetJulian(); + } + + long Date::GetJulian() const + { + return GetJulian(m_day, m_month, m_year); + } + + long Date::GetJulian(int d, int m, int y) const + { + long a = (14 - m) / 12; + long yy = y + 4800L - a; + long mm = m + 12L * a - 3; + return d + (153 * mm + 2) / 5 + 365 * yy + yy / 4 - yy / 100 + yy / 400 - 32045; + } + + void Date::ConvertFromJulian(long jd, int &d, int &m, int &y) + { + long a = jd + 32044; + long b = (4 * a + 3) / 146097; + long c = a - 146097 * b / 4; + long dd = (4 * c + 3) / 1461; + long e = c - 1461 * dd / 4; + long mm = (5 * e + 2) / 153; + d = static_cast(e - (153 * mm + 2) / 5 + 1); + m = static_cast(mm + 3 - 12 * (mm / 10)); + y = static_cast(100 * b + dd - 4800 + mm / 10); + } + + long long Date::SecondsSinceEpochDay() const + { + return static_cast(GetJulian()) * 86400LL + m_hour * 3600LL + m_min * 60LL + m_sec; + } + + void Date::SetFromSeconds(long long s) + { + long long days = FloorDiv(s, 86400); + long long rest = s - days * 86400; + ConvertFromJulian(static_cast(days), m_day, m_month, m_year); + m_hour = static_cast(rest / 3600); + m_min = static_cast((rest / 60) % 60); + m_sec = static_cast(rest % 60); + } + + Blob Date::Encode() const + { + if (m_year < 1950 || m_year >= 2050) + return EncodeToGeneralizedTime(); + char buf[16]; + std::snprintf(buf, sizeof(buf), "%02d%02d%02d%02d%02d%02dZ", + m_year % 100, m_month, m_day, m_hour, m_min, m_sec); + Blob out; + out.push_back(0x17); + out.push_back(static_cast(std::strlen(buf))); + out.insert(out.end(), buf, buf + std::strlen(buf)); + return out; + } + + Blob Date::EncodeToGeneralizedTime() const + { + char buf[20]; + std::snprintf(buf, sizeof(buf), "%04d%02d%02d%02d%02d%02dZ", + m_year, m_month, m_day, m_hour, m_min, m_sec); + Blob out; + out.push_back(0x18); + out.push_back(static_cast(std::strlen(buf))); + out.insert(out.end(), buf, buf + std::strlen(buf)); + return out; + } + + Date::operator Blob() const + { + return Encode(); + } + + int Date::GetDayFromWeekDay(int weekday, int year, int month, int which) + { + int first = static_cast((GetJulian(1, month, year) + 1) % 7); + int day = 1 + ((weekday - first) % 7 + 7) % 7; + int last = DaysPerMonth(month, year); + day += 7 * (which > 0 ? which - 1 : 0); + while (day > last) + day -= 7; + return day; + } + + bool Date::IsValid() const + { + return m_month >= 1 && m_month <= 12 && m_day >= 1 && m_day <= DaysPerMonth(m_month, m_year) + && m_hour >= 0 && m_hour <= 23 && m_min >= 0 && m_min <= 59 && m_sec >= 0 && m_sec <= 59; + } + + void Date::AdjustDays() + { + int last = DaysPerMonth(m_month, m_year); + if (m_day > last) + m_day = last; + } + + void Date::copyDatetm(act::Date a, struct tm &b) + { + std::memset(&b, 0, sizeof(b)); + b.tm_year = a.m_year - 1900; + b.tm_mon = a.m_month - 1; + b.tm_mday = a.m_day; + b.tm_hour = a.m_hour; + b.tm_min = a.m_min; + b.tm_sec = a.m_sec; + b.tm_wday = a.DayOfWeek(); + b.tm_yday = static_cast(a.GetJulian() - a.GetJulian(1, 1, a.m_year)); + b.tm_isdst = 0; + } + + void Date::copytmDate(struct tm a, act::Date &b) + { + b.m_year = a.tm_year + 1900; + b.m_month = a.tm_mon + 1; + b.m_day = a.tm_mday; + b.m_hour = a.tm_hour; + b.m_min = a.tm_min; + b.m_sec = a.tm_sec > 59 ? 59 : a.tm_sec; + } +} // namespace act diff --git a/port/thirdparty/cvact_compat/src/IESKey.cpp b/port/thirdparty/cvact_compat/src/IESKey.cpp new file mode 100644 index 000000000..83b2feb7b --- /dev/null +++ b/port/thirdparty/cvact_compat/src/IESKey.cpp @@ -0,0 +1,846 @@ +// "IES" key: EC-IES over prime curves (agreement ECDH, MAC HashMAC). +// +// Ciphertext = R || C || T with +// R ephemeral (sender) public key, uncompressed X9.62 point 04 || X || Y +// Z x-coordinate of d * Q (classic ECDH, no cofactor multiplication), field length +// K = ANSI X9.63 KDF with SHA-1 over (R || Z), SharedInfo = SHAREDDATA_1, +// length |M| + 20; K = EK (|M| bytes) || MK (20 bytes) +// C = M xor EK +// T = HMAC-SHA1(MK, C || SHAREDDATA_2), 20 bytes +// Public keys are exported as DER SubjectPublicKeyInfo (id-ecPublicKey, named curve), +// private keys as DER ECPrivateKey (RFC 5915). +#include "actInternal.h" + +#include +#include +#include + +#include +#include + +namespace act +{ + namespace detail + { + namespace + { + const char *const kKdfHash = "SHA1"; + const char *const kMacHash = "SHA1"; + const size_t kMacKeyLen = 20; + const size_t kTagLen = 20; + + struct CurveDef + { + int id; + int nid; + const char *p, *a, *b, *gx, *gy, *n; + }; + + // Explicit parameters are a fallback for OpenSSL builds without these named + // curves; they match the curve definitions in CrypTool's libec. + const CurveDef kCurves[] = { + {SECGp160r1, NID_secp160r1, + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFF", + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFC", + "1C97BEFC54BD7A8B65ACF89F81D4D4ADC565FA45", + "4A96B5688EF573284664698968C38BB913CBFC82", + "23A628553168947D59DCC912042351377AC5FB32", + "100000000000000000001F4C8F927AED3CA752257"}, + {SECGp160r2, NID_secp160r2, 0, 0, 0, 0, 0, 0}, + {SECGp160k1, NID_secp160k1, 0, 0, 0, 0, 0, 0}, + {ANSIp192r1, NID_X9_62_prime192v1, + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF", + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFC", + "64210519E59C80E70FA7E9AB72243049FEB8DEECC146B9B1", + "188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF1012", + "07192B95FFC8DA78631011ED6B24CDD573F977A11E794811", + "FFFFFFFFFFFFFFFFFFFFFFFF99DEF836146BC9B1B4D22831"}, + {ANSIp192r2, NID_X9_62_prime192v2, + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF", + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFC", + "CC22D6DFB95C6B25E49C0D6364A4E5980C393AA21668D953", + "EEA2BAE7E1497842F2DE7769CFE9C989C072AD696F48034A", + "6574D11D69B6EC7A672BB82A083DF2F2B0847DE970B2DE15", + "FFFFFFFFFFFFFFFFFFFFFFFE5FB1A724DC80418648D8DD31"}, + {ANSIp192r3, NID_X9_62_prime192v3, + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF", + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFC", + "22123DC2395A05CAA7423DAECCC94760A7D462256BD56916", + "7D29778100C65A1DA1783716588DCE2B8B4AEE8E228F1896", + "38A90F22637337334B49DCB66A6DC8F9978ACA7648A943B0", + "FFFFFFFFFFFFFFFFFFFFFFFF7A62D031C83F4294F640EC13"}, + {NISTp192r1, NID_X9_62_prime192v1, 0, 0, 0, 0, 0, 0}, + {SECGp192r1, NID_X9_62_prime192v1, 0, 0, 0, 0, 0, 0}, + {SECGp192k1, NID_secp192k1, 0, 0, 0, 0, 0, 0}, + {NISTp224r1, NID_secp224r1, 0, 0, 0, 0, 0, 0}, + {SECGp224r1, NID_secp224r1, 0, 0, 0, 0, 0, 0}, + {SECGp224k1, NID_secp224k1, 0, 0, 0, 0, 0, 0}, + {ANSIp239r1, NID_X9_62_prime239v1, + "7FFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFF8000000000007FFFFFFFFFFF", + "7FFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFF8000000000007FFFFFFFFFFC", + "6B016C3BDCF18941D0D654921475CA71A9DB2FB27D1D37796185C2942C0A", + "0FFA963CDCA8816CCC33B8642BEDF905C3D358573D3F27FBBD3B3CB9AAAF", + "7DEBE8E4E90A5DAE6E4054CA530BA04654B36818CE226B39FCCB7B02F1AE", + "7FFFFFFFFFFFFFFFFFFFFFFF7FFFFF9E5E9A9F5D9071FBD1522688909D0B"}, + {ANSIp239r2, NID_X9_62_prime239v2, + "7FFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFF8000000000007FFFFFFFFFFF", + "7FFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFF8000000000007FFFFFFFFFFC", + "617FAB6832576CBBFED50D99F0249C3FEE58B94BA0038C7AE84C8C832F2C", + "38AF09D98727705120C921BB5E9E26296A3CDCF2F35757A0EAFD87B830E7", + "5B0125E4DBEA0EC7206DA0FC01D9B081329FB555DE6EF460237DFF8BE4BA", + "7FFFFFFFFFFFFFFFFFFFFFFF800000CFA7E8594377D414C03821BC582063"}, + {ANSIp239r3, NID_X9_62_prime239v3, + "7FFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFF8000000000007FFFFFFFFFFF", + "7FFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFF8000000000007FFFFFFFFFFC", + "255705FA2A306654B1F4CB03D6A750A30C250102D4988717D9BA15AB6D3E", + "6768AE8E18BB92CFCF005C949AA2C6D94853D0E660BBF854B1C9505FE95A", + "1607E6898F390C06BC1D552BAD226F3B6FCFE48B6E818499AF18E3ED6CF3", + "7FFFFFFFFFFFFFFFFFFFFFFF7FFFFF975DEB41B3A6057C3C432146526551"}, + {ANSIp256r1, NID_X9_62_prime256v1, + "FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF", + "FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC", + "5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B", + "6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296", + "4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5", + "FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551"}, + {NISTp256r1, NID_X9_62_prime256v1, 0, 0, 0, 0, 0, 0}, + {SECGp256r1, NID_X9_62_prime256v1, 0, 0, 0, 0, 0, 0}, + {SECGp256k1, NID_secp256k1, 0, 0, 0, 0, 0, 0}, + {NISTp384r1, NID_secp384r1, 0, 0, 0, 0, 0, 0}, + {NISTp521r1, NID_secp521r1, 0, 0, 0, 0, 0, 0}, + }; + + const CurveDef *FindCurve(int id) + { + for (size_t i = 0; i < sizeof(kCurves) / sizeof(kCurves[0]); ++i) + if (kCurves[i].id == id) + return &kCurves[i]; + return 0; + } + + BnPtr HexBn(const char *hex) + { + BIGNUM *bn = 0; + if (!BN_hex2bn(&bn, hex)) + throw BadAllocException("BN_hex2bn failed", "IESKey"); + return BnPtr(bn); + } + + GroupPtr ExplicitGroup(const CurveDef &def) + { + BnCtxPtr ctx = NewBnCtx(); + BnPtr p = HexBn(def.p), a = HexBn(def.a), b = HexBn(def.b); + BnPtr gx = HexBn(def.gx), gy = HexBn(def.gy), n = HexBn(def.n); + BnPtr h = NewBn(); + BN_one(h.get()); + GroupPtr group(EC_GROUP_new_curve_GFp(p.get(), a.get(), b.get(), ctx.get())); + if (!group) + return GroupPtr(); + PointPtr g(EC_POINT_new(group.get())); + if (!g || EC_POINT_set_affine_coordinates(group.get(), g.get(), gx.get(), gy.get(), ctx.get()) != 1 + || EC_GROUP_set_generator(group.get(), g.get(), n.get(), h.get()) != 1) + return GroupPtr(); + EC_GROUP_set_curve_name(group.get(), def.nid); + return group; + } + + // Group for a curve id; ExplicitGroup() is used when preferExplicit is set or + // the named curve is not built into the OpenSSL library. + GroupPtr MakeGroup(const CurveDef &def, bool preferExplicit = false) + { + GroupPtr group; + if (!preferExplicit) + group.reset(EC_GROUP_new_by_curve_name(def.nid)); + if (!group && def.p != 0) + group = ExplicitGroup(def); + if (!group) + throw NoSuchAlgorithmException("elliptic curve not available", "IESKey"); + return group; + } + + Blob CurveOidTlv(int nid) + { + ASN1_OBJECT *obj = OBJ_nid2obj(nid); + int len = obj != 0 ? i2d_ASN1_OBJECT(obj, 0) : 0; + if (len <= 0) + throw NoSuchAlgorithmException("unknown curve OID", "IESKey"); + Blob out(static_cast(len)); + unsigned char *p = out.begin(); + i2d_ASN1_OBJECT(obj, &p); + return out; + } + + const byte kEcPublicKeyOid[] = {0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01}; + + size_t FieldLen(const EC_GROUP *group) + { + return static_cast((EC_GROUP_get_degree(group) + 7) / 8); + } + + Blob PointToOctets(const EC_GROUP *group, const EC_POINT *pt, BN_CTX *ctx) + { + size_t len = EC_POINT_point2oct(group, pt, POINT_CONVERSION_UNCOMPRESSED, 0, 0, ctx); + if (len == 0) + throw InvalidKeyException("invalid point", "IESKey"); + Blob out(len); + EC_POINT_point2oct(group, pt, POINT_CONVERSION_UNCOMPRESSED, out.begin(), len, ctx); + return out; + } + + PointPtr OctetsToPoint(const EC_GROUP *group, const byte *data, size_t len, BN_CTX *ctx) + { + PointPtr pt(EC_POINT_new(group)); + if (!pt || len == 0 || EC_POINT_oct2point(group, pt.get(), data, len, ctx) != 1 + || EC_POINT_is_at_infinity(group, pt.get()) || EC_POINT_is_on_curve(group, pt.get(), ctx) != 1) + return PointPtr(); + return pt; + } + + // Length of the encoded point at the start of data, 0 if not a point encoding. + size_t EncodedPointLen(const EC_GROUP *group, const byte *data, size_t len) + { + if (len == 0) + return 0; + size_t flen = FieldLen(group); + if (data[0] == 0x04) + return 1 + 2 * flen; + if (data[0] == 0x02 || data[0] == 0x03) + return 1 + flen; + return 0; + } + + Blob SharedSecret(const EC_GROUP *group, const BIGNUM *d, const EC_POINT *q, BN_CTX *ctx) + { + PointPtr s(EC_POINT_new(group)); + BnPtr x = NewBn(); + if (!s || EC_POINT_mul(group, s.get(), 0, q, d, ctx) != 1 || EC_POINT_is_at_infinity(group, s.get()) + || EC_POINT_get_affine_coordinates(group, s.get(), x.get(), 0, ctx) != 1) + throw InvalidKeyException("ECDH computation failed", "IESKey"); + return BnToPadded(x.get(), FieldLen(group)); + } + + void XorKdfStream(Blob &data, const Blob &key) + { + for (size_t i = 0; i < data.size(); ++i) + data[i] ^= key[i]; + } + + class IESEncrypt : public BufferedAlgorithm + { + public: + IESEncrypt(GroupPtr group, BnPtr ephemeralPriv, const Blob &ephemeralPub, PointPtr recipient, + const Blob &shared1, const Blob &shared2) + : mGroup(std::move(group)), mPriv(std::move(ephemeralPriv)), mR(ephemeralPub), + mRecipient(std::move(recipient)), mShared1(shared1), mShared2(shared2) {} + + void Write(const Blob &input) { mIn.append(input); } + void Write(const byte *input, size_t insize) { mIn.insert(mIn.end(), input, input + insize); } + + void Finalize() + { + if (mStatus == IS_FINALIZED) + return; + BnCtxPtr ctx = NewBnCtx(); + Blob kdfIn(mR); + kdfIn.append(SharedSecret(mGroup.get(), mPriv.get(), mRecipient.get(), ctx.get())); + Blob k = X963Kdf(kKdfHash, kdfIn, mShared1, mIn.size() + kMacKeyLen); + Blob c(mIn); + XorKdfStream(c, k); + Blob mk(k.begin() + mIn.size(), k.end()); + Blob macIn(c); + macIn.append(mShared2); + Blob tag = Hmac(kMacHash, mk, macIn); + mOut.append(mR); + mOut.append(c); + mOut.insert(mOut.end(), tag.begin(), tag.begin() + kTagLen); + mIn.clear(); + mStatus = IS_FINALIZED; + } + + private: + GroupPtr mGroup; + BnPtr mPriv; + Blob mR; + PointPtr mRecipient; + Blob mShared1, mShared2; + Blob mIn; + }; + + class IESDecrypt : public BufferedAlgorithm + { + public: + IESDecrypt(GroupPtr group, BnPtr priv, const Blob &shared1, const Blob &shared2) + : mGroup(std::move(group)), mPriv(std::move(priv)), mShared1(shared1), mShared2(shared2) {} + + void Write(const Blob &input) { mIn.append(input); } + void Write(const byte *input, size_t insize) { mIn.insert(mIn.end(), input, input + insize); } + + void Finalize() + { + if (mStatus == IS_FINALIZED) + return; + mStatus = DECRYPT_ERROR; + BnCtxPtr ctx = NewBnCtx(); + size_t rlen = EncodedPointLen(mGroup.get(), mIn.begin(), mIn.size()); + if (rlen == 0 || mIn.size() < rlen + kTagLen) + throw InvalidAlgorithmParameterException("invalid ECIES ciphertext", "IESDecrypt::Finalize"); + PointPtr r = OctetsToPoint(mGroup.get(), mIn.begin(), rlen, ctx.get()); + if (!r) + throw InvalidAlgorithmParameterException("invalid ephemeral public key", "IESDecrypt::Finalize"); + size_t clen = mIn.size() - rlen - kTagLen; + Blob kdfIn(mIn.begin(), mIn.begin() + rlen); + kdfIn.append(SharedSecret(mGroup.get(), mPriv.get(), r.get(), ctx.get())); + Blob k = X963Kdf(kKdfHash, kdfIn, mShared1, clen + kMacKeyLen); + Blob c(mIn.begin() + rlen, mIn.begin() + rlen + clen); + Blob mk(k.begin() + clen, k.end()); + Blob macIn(c); + macIn.append(mShared2); + Blob tag = Hmac(kMacHash, mk, macIn); + if (CRYPTO_memcmp(tag.begin(), mIn.begin() + rlen + clen, kTagLen) != 0) + throw AlgorithmException("ECIES MAC verification failed (wrong key or corrupted data)", + "IESDecrypt::Finalize"); + XorKdfStream(c, k); + mOut.append(c); + mIn.clear(); + mStatus = IS_FINALIZED; + } + + private: + GroupPtr mGroup; + BnPtr mPriv; + Blob mShared1, mShared2; + Blob mIn; + }; + + IKey *NewIESKey(); + + class IESKey : public IKey + { + public: + IESKey() : mCurve(SECGp160r1), mNewEphemeral(0) {} + + IESKey(const IESKey &o) + : mCurve(o.mCurve), mShared1(o.mShared1), mShared2(o.mShared2), mNewEphemeral(o.mNewEphemeral) + { + if (o.mGroup) + mGroup.reset(EC_GROUP_dup(o.mGroup.get())); + if (o.mPriv) + mPriv.reset(BN_dup(o.mPriv.get())); + if (o.mPub) + mPub.reset(EC_POINT_dup(o.mPub.get(), mGroup.get())); + if (o.mPendingX) + mPendingX.reset(BN_dup(o.mPendingX.get())); + if (o.mPendingY) + mPendingY.reset(BN_dup(o.mPendingY.get())); + } + + IKey *Clone() const { return new IESKey(*this); } + + void Import(const Blob &keyblob) + { + const byte *buf = keyblob.begin(); + size_t size = keyblob.size(), pos = 0; + DerItem top; + if (!DerRead(buf, size, pos, top)) + throw InvalidKeyException("invalid key blob", "IESKey::Import"); + if (top.tag == 0x06) + { + SetCurve(CurveFromOid(buf, size)); + return; + } + if (top.tag != 0x30) + throw InvalidKeyException("invalid key blob", "IESKey::Import"); + size_t ipos = 0; + DerItem first; + if (!DerRead(top.data, top.len, ipos, first)) + throw InvalidKeyException("invalid key blob", "IESKey::Import"); + if (first.tag == 0x30) + ImportSpki(top, first, ipos); + else if (first.tag == 0x02) + ImportEcPrivateKey(top, ipos); + else + throw InvalidKeyException("invalid key blob", "IESKey::Import"); + } + + void Export(Blob &keyblob, export_t type) const + { + keyblob.clear(); + switch (type) + { + case PUBLIC: + keyblob = Spki(); + break; + case DEFAULT: + case PRIVATE: + keyblob = EcPrivateKey(); + break; + case DOMAINPARAMS: + keyblob = CurveOidTlv(FindCurve(mCurve)->nid); + break; + default: + throw InvalidKeyException("unsupported export type", "IESKey::Export"); + } + } + + void SetParam(paramid_t id, const Blob &blob) + { + switch (id) + { + case PRIVATEKEY: + SetPrivate(BlobToBn(blob)); + break; + case PUBLIC_X: + mPendingX = BlobToBn(blob); + TryBuildPublic(); + break; + case PUBLIC_Y: + mPendingY = BlobToBn(blob); + TryBuildPublic(); + break; + case PUBLICKEY: + { + BnCtxPtr ctx = NewBnCtx(); + PointPtr pt = OctetsToPoint(Group(), blob.begin(), blob.size(), ctx.get()); + if (!pt) + throw InvalidKeyException("point not on curve", "IESKey::SetParam"); + mPriv.reset(); + mPub = std::move(pt); + break; + } + case SHAREDDATA_1: + mShared1 = blob; + break; + case SHAREDDATA_2: + mShared2 = blob; + break; + default: + throw InvalidKeyException("unsupported parameter", "IESKey::SetParam"); + } + } + + void SetParam(paramid_t id, int val) + { + switch (id) + { + case CURVE: + SetCurve(val); + break; + case NEWEPHEMERAL: + mNewEphemeral = val; + break; + case COMPATIBLE: + if (val == 0) + throw InvalidKeyException("cofactor ECDH not supported", "IESKey::SetParam"); + break; + default: + throw InvalidKeyException("unsupported parameter", "IESKey::SetParam"); + } + } + + void SetParam(paramid_t id, const char *cstr) + { + if (cstr == 0) + throw NullPointerException("null parameter", "IESKey::SetParam"); + switch (id) + { + case PRIVATEKEY: + case PUBLIC_X: + case PUBLIC_Y: + { + BnPtr bn = ParseNumber(cstr); + if (!bn || BN_is_negative(bn.get())) + throw InvalidKeyException("invalid number", "IESKey::SetParam"); + if (id == PRIVATEKEY) + SetPrivate(std::move(bn)); + else + { + (id == PUBLIC_X ? mPendingX : mPendingY) = std::move(bn); + TryBuildPublic(); + } + break; + } + case AGREEMENT: + if (std::strcmp(cstr, "ECDH") != 0) + throw NoSuchAlgorithmException("unsupported agreement", "IESKey::SetParam"); + break; + case MAC: + if (std::strcmp(cstr, "HashMAC") != 0) + throw NoSuchAlgorithmException("unsupported MAC", "IESKey::SetParam"); + break; + default: + throw InvalidKeyException("unsupported parameter", "IESKey::SetParam"); + } + } + + int GetParam(paramid_t id) const + { + switch (id) + { + case CURVE: + return mCurve; + case NEWEPHEMERAL: + return mNewEphemeral; + case COMPATIBLE: + return 1; + case KEYSIZE: + return EC_GROUP_get_degree(Group()); + default: + throw InvalidKeyException("unsupported parameter", "IESKey::GetParam"); + } + } + + void GetParam(paramid_t id, Blob &blob) const + { + switch (id) + { + case PRIVATEKEY: + if (!mPriv) + throw InvalidKeyException("no private key", "IESKey::GetParam"); + blob = BnToTwosComplement(mPriv.get()); + break; + case PUBLIC_X: + case PUBLIC_Y: + { + if (!mPub) + throw InvalidKeyException("no public key", "IESKey::GetParam"); + BnCtxPtr ctx = NewBnCtx(); + BnPtr x = NewBn(), y = NewBn(); + EC_POINT_get_affine_coordinates(Group(), mPub.get(), x.get(), y.get(), ctx.get()); + blob = BnToTwosComplement(id == PUBLIC_X ? x.get() : y.get()); + break; + } + case PUBLICKEY: + { + if (!mPub) + throw InvalidKeyException("no public key", "IESKey::GetParam"); + BnCtxPtr ctx = NewBnCtx(); + blob = PointToOctets(Group(), mPub.get(), ctx.get()); + break; + } + case SHAREDDATA_1: blob = mShared1; break; + case SHAREDDATA_2: blob = mShared2; break; + case AGREEMENT: blob = Blob("ECDH"); break; + case MAC: blob = Blob("HashMAC"); break; + default: + throw InvalidKeyException("unsupported parameter", "IESKey::GetParam"); + } + } + + void Generate(IRNGAlg *prng) + { + BnPtr d = RandomScalar(Group(), prng); + SetPrivate(std::move(d)); + } + + void Derive(const Blob &, const Blob &) + { + throw NotImplementedException("Derive is not supported by IES keys", "IESKey::Derive"); + } + + IAlgorithm *CreateAlgorithm(mode_t Mode) const + { + if (Mode == DECRYPT) + return CreateDecrypt(); + if (Mode == ENCRYPT) + { + if (!mPub) + throw InvalidKeyException("no public key", "IESKey::CreateAlgorithm"); + return CreateEncrypt(PointPtr(EC_POINT_dup(mPub.get(), Group())), true); + } + throw InvalidAlgorithmParameterException("unsupported mode", "IESKey::CreateAlgorithm"); + } + + IAlgorithm *CreateAlgorithm(mode_t Mode, const Blob &data) const + { + if (Mode == DECRYPT) + return CreateDecrypt(); + if (Mode != ENCRYPT) + throw InvalidAlgorithmParameterException("unsupported mode", "IESKey::CreateAlgorithm"); + return CreateEncrypt(ParsePeerPublicKey(data), mNewEphemeral != 0 || !mPriv); + } + + void *GetCreatePointer() const { return reinterpret_cast(&NewIESKey); } + + private: + EC_GROUP *Group() const + { + if (!mGroup) + mGroup = MakeGroup(*FindCurve(mCurve)); + return mGroup.get(); + } + + void SetCurve(int id) + { + const CurveDef *def = FindCurve(id); + if (def == 0) + throw NoSuchAlgorithmException("unsupported curve", "IESKey::SetParam"); + GroupPtr group = MakeGroup(*def); + mCurve = id; + mGroup = std::move(group); + mPriv.reset(); + mPub.reset(); + mPendingX.reset(); + mPendingY.reset(); + } + + int CurveFromOid(const byte *tlv, size_t len) const + { + for (size_t i = 0; i < sizeof(kCurves) / sizeof(kCurves[0]); ++i) + { + Blob oid = CurveOidTlv(kCurves[i].nid); + if (oid.size() == len && std::memcmp(oid.begin(), tlv, len) == 0) + return kCurves[i].id; + } + throw NoSuchAlgorithmException("unsupported curve", "IESKey::Import"); + } + + void SetPrivate(BnPtr d) + { + const BIGNUM *order = EC_GROUP_get0_order(Group()); + if (BN_is_zero(d.get()) || BN_is_negative(d.get()) || BN_cmp(d.get(), order) >= 0) + throw InvalidKeyException("private key out of range", "IESKey::SetParam"); + BnCtxPtr ctx = NewBnCtx(); + PointPtr pub(EC_POINT_new(Group())); + if (!pub || EC_POINT_mul(Group(), pub.get(), d.get(), 0, 0, ctx.get()) != 1) + throw InvalidKeyException("public key computation failed", "IESKey::SetParam"); + mPriv = std::move(d); + mPub = std::move(pub); + mPendingX.reset(); + mPendingY.reset(); + } + + void TryBuildPublic() + { + if (!mPendingX || !mPendingY) + return; + BnCtxPtr ctx = NewBnCtx(); + BnPtr p = NewBn(); + EC_GROUP_get_curve(Group(), p.get(), 0, 0, ctx.get()); + PointPtr pt(EC_POINT_new(Group())); + if (BN_is_negative(mPendingX.get()) || BN_is_negative(mPendingY.get()) + || BN_cmp(mPendingX.get(), p.get()) >= 0 || BN_cmp(mPendingY.get(), p.get()) >= 0 || !pt + || EC_POINT_set_affine_coordinates(Group(), pt.get(), mPendingX.get(), mPendingY.get(), ctx.get()) != 1 + || EC_POINT_is_on_curve(Group(), pt.get(), ctx.get()) != 1) + { + mPendingY.reset(); + throw InvalidKeyException("point not on curve", "IESKey::SetParam"); + } + mPriv.reset(); + mPub = std::move(pt); + } + + static BnPtr RandomScalar(const EC_GROUP *group, IRNGAlg *prng) + { + const BIGNUM *order = EC_GROUP_get0_order(group); + BnPtr range(BN_dup(order)); + BnPtr d = NewBn(); + if (!range || !BN_sub_word(range.get(), 1)) + throw BadAllocException("BN failure", "IESKey::Generate"); + if (prng == 0) + { + if (BN_priv_rand_range(d.get(), range.get()) != 1) + throw RuntimeException("random generation failed", "IESKey::Generate"); + } + else + { + Blob bytes(static_cast(BN_num_bytes(order)) + 8); + RandomBytes(bytes.begin(), bytes.size(), prng); + BnPtr r = BlobToBn(bytes); + BnCtxPtr ctx = NewBnCtx(); + BN_mod(d.get(), r.get(), range.get(), ctx.get()); + } + BN_add_word(d.get(), 1); + return d; + } + + PointPtr ParsePeerPublicKey(const Blob &data) const + { + BnCtxPtr ctx = NewBnCtx(); + size_t rlen = EncodedPointLen(Group(), data.begin(), data.size()); + if (rlen != 0 && rlen == data.size()) + { + PointPtr pt = OctetsToPoint(Group(), data.begin(), data.size(), ctx.get()); + if (!pt) + throw InvalidKeyException("peer public key not on curve", "IESKey::CreateAlgorithm"); + return pt; + } + IESKey peer; + peer.Import(data); + if (!peer.mPub) + throw InvalidAlgorithmParameterException("no peer public key", "IESKey::CreateAlgorithm"); + if (EC_GROUP_cmp(Group(), peer.Group(), ctx.get()) != 0) + throw InvalidAlgorithmParameterException("peer key uses a different curve", "IESKey::CreateAlgorithm"); + Blob oct = PointToOctets(peer.Group(), peer.mPub.get(), ctx.get()); + PointPtr pt = OctetsToPoint(Group(), oct.begin(), oct.size(), ctx.get()); + if (!pt) + throw InvalidKeyException("peer public key not on curve", "IESKey::CreateAlgorithm"); + return pt; + } + + IAlgorithm *CreateEncrypt(PointPtr recipient, bool newEphemeral) const + { + if (!recipient) + throw InvalidKeyException("invalid recipient key", "IESKey::CreateAlgorithm"); + BnCtxPtr ctx = NewBnCtx(); + BnPtr d; + PointPtr pub; + if (newEphemeral) + { + d = RandomScalar(Group(), 0); + pub.reset(EC_POINT_new(Group())); + if (!pub || EC_POINT_mul(Group(), pub.get(), d.get(), 0, 0, ctx.get()) != 1) + throw InvalidKeyException("ephemeral key generation failed", "IESKey::CreateAlgorithm"); + } + else + { + d.reset(BN_dup(mPriv.get())); + pub.reset(EC_POINT_dup(mPub.get(), Group())); + } + Blob r = PointToOctets(Group(), pub.get(), ctx.get()); + return new IESEncrypt(GroupPtr(EC_GROUP_dup(Group())), std::move(d), r, std::move(recipient), + mShared1, mShared2); + } + + IAlgorithm *CreateDecrypt() const + { + if (!mPriv) + throw InvalidKeyException("no private key", "IESKey::CreateAlgorithm"); + return new IESDecrypt(GroupPtr(EC_GROUP_dup(Group())), BnPtr(BN_dup(mPriv.get())), mShared1, mShared2); + } + + Blob Spki() const + { + if (!mPub) + throw InvalidKeyException("no public key", "IESKey::Export"); + BnCtxPtr ctx = NewBnCtx(); + Blob alg(kEcPublicKeyOid, kEcPublicKeyOid + sizeof(kEcPublicKeyOid)); + alg.append(CurveOidTlv(FindCurve(mCurve)->nid)); + Blob bits(1, 0); + bits.append(PointToOctets(Group(), mPub.get(), ctx.get())); + Blob content; + DerAppend(content, 0x30, alg); + DerAppend(content, 0x03, bits); + Blob out; + DerAppend(out, 0x30, content); + return out; + } + + Blob EcPrivateKey() const + { + if (!mPriv) + throw InvalidKeyException("no private key", "IESKey::Export"); + BnCtxPtr ctx = NewBnCtx(); + const BIGNUM *order = EC_GROUP_get0_order(Group()); + Blob content; + const byte version[] = {0x01}; + DerAppend(content, 0x02, version, 1); + DerAppend(content, 0x04, BnToPadded(mPriv.get(), static_cast(BN_num_bytes(order)))); + DerAppend(content, 0xA0, CurveOidTlv(FindCurve(mCurve)->nid)); + Blob bits(1, 0); + bits.append(PointToOctets(Group(), mPub.get(), ctx.get())); + Blob bitString; + DerAppend(bitString, 0x03, bits); + DerAppend(content, 0xA1, bitString); + Blob out; + DerAppend(out, 0x30, content); + return out; + } + + void ImportSpki(const DerItem &top, const DerItem &alg, size_t pos) + { + size_t apos = 0; + DerItem algOid, curveOid, bits; + if (!DerRead(alg.data, alg.len, apos, algOid) || algOid.tag != 0x06 + || algOid.len + 2 != sizeof(kEcPublicKeyOid) + || std::memcmp(algOid.data, kEcPublicKeyOid + 2, algOid.len) != 0) + throw InvalidKeyException("not an EC public key", "IESKey::Import"); + size_t oidStart = apos; + if (!DerRead(alg.data, alg.len, apos, curveOid) || curveOid.tag != 0x06) + throw InvalidKeyException("only named curves are supported", "IESKey::Import"); + if (!DerRead(top.data, top.len, pos, bits) || bits.tag != 0x03 || bits.len < 2 || bits.data[0] != 0) + throw InvalidKeyException("invalid public key encoding", "IESKey::Import"); + SetCurve(CurveFromOid(alg.data + oidStart, apos - oidStart)); + SetParam(PUBLICKEY, Blob(bits.data + 1, bits.data + bits.len)); + } + + void ImportEcPrivateKey(const DerItem &top, size_t pos) + { + DerItem key, item; + if (!DerRead(top.data, top.len, pos, key) || key.tag != 0x04) + throw InvalidKeyException("invalid private key encoding", "IESKey::Import"); + bool haveCurve = false; + while (pos < top.len) + { + if (!DerRead(top.data, top.len, pos, item)) + throw InvalidKeyException("invalid private key encoding", "IESKey::Import"); + if (item.tag == 0xA0) + { + SetCurve(CurveFromOid(item.data, item.len)); + haveCurve = true; + } + } + if (!haveCurve) + throw InvalidKeyException("private key without curve", "IESKey::Import"); + SetPrivate(BlobToBn(Blob(key.data, key.data + key.len))); + } + + int mCurve; + mutable GroupPtr mGroup; + BnPtr mPriv; + PointPtr mPub; + BnPtr mPendingX, mPendingY; + Blob mShared1, mShared2; + int mNewEphemeral; + }; + + IKey *NewIESKey() { return new IESKey(); } + } // namespace + + IKey *CreateIESKey() { return NewIESKey(); } + + IKey *ImportECKeyBlob(const Blob &keyblob) + { + IESKey *key = new IESKey(); + try + { + key->Import(keyblob); + } + catch (...) + { + delete key; + throw; + } + return key; + } + + // Test hook: true if the explicit fallback parameters of a curve equal OpenSSL's named curve. + bool CurveFallbackMatches(int id) + { + const CurveDef *def = FindCurve(id); + if (def == 0 || def->p == 0) + return false; + GroupPtr named(EC_GROUP_new_by_curve_name(def->nid)); + if (!named) + return true; + GroupPtr expl = MakeGroup(*def, true); + BnCtxPtr ctx = NewBnCtx(); + BnPtr v[2][7]; + const EC_GROUP *groups[2] = {named.get(), expl.get()}; + for (int g = 0; g < 2; ++g) + { + for (int i = 0; i < 7; ++i) + v[g][i] = NewBn(); + if (EC_GROUP_get_curve(groups[g], v[g][0].get(), v[g][1].get(), v[g][2].get(), ctx.get()) != 1 + || EC_POINT_get_affine_coordinates(groups[g], EC_GROUP_get0_generator(groups[g]), + v[g][3].get(), v[g][4].get(), ctx.get()) != 1 + || !BN_copy(v[g][5].get(), EC_GROUP_get0_order(groups[g])) + || !BN_copy(v[g][6].get(), EC_GROUP_get0_cofactor(groups[g]))) + return false; + } + for (int i = 0; i < 7; ++i) + if (BN_cmp(v[0][i].get(), v[1][i].get()) != 0) + return false; + return true; + } + } // namespace detail +} // namespace act diff --git a/port/thirdparty/cvact_compat/src/Key.cpp b/port/thirdparty/cvact_compat/src/Key.cpp new file mode 100644 index 000000000..ad800bfc2 --- /dev/null +++ b/port/thirdparty/cvact_compat/src/Key.cpp @@ -0,0 +1,97 @@ +#include "actInternal.h" +#include "actKey.h" + +#include + +namespace act +{ + namespace + { + IKey *Checked(IKey *key) + { + if (key == 0) + throw NullPointerException("key object not initialised", "Key"); + return key; + } + } + + Key::Key() : mKey(0) {} + + Key::Key(const Key &key) : mKey(key.mKey != 0 ? key.mKey->Clone() : 0) {} + + Key::Key(IKey *keyptr) : mKey(keyptr) {} + + Key::Key(const Blob &keyblob) : mKey(detail::ImportECKeyBlob(keyblob)) {} + + Key::Key(const char *keytype) : mKey(0) + { + if (keytype == 0) + throw NullPointerException("null key type", "Key::Key"); + if (std::strcmp(keytype, "IES") == 0) + mKey = detail::CreateIESKey(); + else if (std::strcmp(keytype, "BlockCipher") == 0) + mKey = detail::CreateBlockCipherKey(); + else + throw NoSuchAlgorithmException("unsupported key type", "Key::Key"); + } + + Key::~Key() + { + delete mKey; + } + + Key &Key::operator=(const Key &key) + { + if (this != &key) + { + IKey *copy = key.mKey != 0 ? key.mKey->Clone() : 0; + delete mKey; + mKey = copy; + } + return *this; + } + + void Key::Import(const Blob &keyblob) + { + if (mKey == 0) + mKey = detail::ImportECKeyBlob(keyblob); + else + mKey->Import(keyblob); + } + + void Key::Export(Blob &keyblob, export_t type) const { Checked(mKey)->Export(keyblob, type); } + void Key::SetParam(paramid_t id, const Blob &blob) { Checked(mKey)->SetParam(id, blob); } + void Key::SetParam(paramid_t id, int val) { Checked(mKey)->SetParam(id, val); } + void Key::SetParam(paramid_t id, const char *cstr) { Checked(mKey)->SetParam(id, cstr); } + int Key::GetParam(paramid_t id) const { return Checked(mKey)->GetParam(id); } + void Key::GetParam(paramid_t id, Blob &blob) const { Checked(mKey)->GetParam(id, blob); } + + void Key::Generate(IAlgorithm *prng) + { + IRNGAlg *rng = 0; + if (prng != 0) + { + rng = dynamic_cast(prng); + if (rng == 0) + throw InvalidAlgorithmParameterException("not a random number generator", "Key::Generate"); + } + Checked(mKey)->Generate(rng); + } + + void Key::Derive(const Blob &data, const Blob &salt) { Checked(mKey)->Derive(data, salt); } + + IAlgorithm *Key::CreateAlgorithm(mode_t Mode) const { return Checked(mKey)->CreateAlgorithm(Mode); } + IAlgorithm *Key::CreateAlgorithm(mode_t Mode, const Blob &data) const { return Checked(mKey)->CreateAlgorithm(Mode, data); } + + IKey *Key::GetPointer() { return mKey; } + const IKey *Key::GetPointer() const { return mKey; } + Key::operator IKey *() { return mKey; } + Key::operator const IKey *() const { return mKey; } + + IKey *Key::ReleasePointer() + { + IKey *p = mKey; + mKey = 0; + return p; + } +} // namespace act diff --git a/port/thirdparty/cvact_compat/src/Tools.cpp b/port/thirdparty/cvact_compat/src/Tools.cpp new file mode 100644 index 000000000..c24ec8ad8 --- /dev/null +++ b/port/thirdparty/cvact_compat/src/Tools.cpp @@ -0,0 +1,415 @@ +#include "actInternal.h" +#include "actTools.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace act +{ + namespace detail + { + BnPtr NewBn() + { + BnPtr bn(BN_new()); + if (!bn) + throw BadAllocException("BN_new failed", "detail::NewBn"); + return bn; + } + + BnCtxPtr NewBnCtx() + { + BnCtxPtr ctx(BN_CTX_new()); + if (!ctx) + throw BadAllocException("BN_CTX_new failed", "detail::NewBnCtx"); + return ctx; + } + + BnPtr ParseNumber(const char *text) + { + if (text == 0) + return BnPtr(); + std::string s; + for (const char *p = text; *p; ++p) + if (!std::isspace(static_cast(*p))) + s += *p; + bool negative = false; + size_t pos = 0; + if (pos < s.size() && (s[pos] == '-' || s[pos] == '+')) + { + negative = s[pos] == '-'; + ++pos; + } + bool hex = s.size() >= pos + 2 && s[pos] == '0' && (s[pos + 1] == 'x' || s[pos + 1] == 'X'); + if (hex) + pos += 2; + std::string digits = s.substr(pos); + if (digits.empty()) + return BnPtr(); + for (size_t i = 0; i < digits.size(); ++i) + { + unsigned char c = static_cast(digits[i]); + if (hex ? !std::isxdigit(c) : !std::isdigit(c)) + return BnPtr(); + } + BIGNUM *raw = 0; + int n = hex ? BN_hex2bn(&raw, digits.c_str()) : BN_dec2bn(&raw, digits.c_str()); + BnPtr bn(raw); + if (!bn || n != static_cast(digits.size())) + return BnPtr(); + BN_set_negative(bn.get(), negative ? 1 : 0); + return bn; + } + + Blob BnToTwosComplement(const BIGNUM *bn) + { + if (BN_is_zero(bn)) + return Blob(1, 0); + BnPtr mag(BN_dup(bn)); + if (!mag) + throw BadAllocException("BN_dup failed", "detail::BnToTwosComplement"); + BN_set_negative(mag.get(), 0); + int len = BN_num_bytes(mag.get()); + Blob out; + if (!BN_is_negative(bn)) + { + out.resize(static_cast(len)); + BN_bn2bin(mag.get(), &out[0]); + if (out[0] & 0x80) + out.insert(out.begin(), byte(0)); + return out; + } + BnPtr limit = NewBn(); + BN_set_bit(limit.get(), 8 * len - 1); + if (BN_cmp(mag.get(), limit.get()) > 0) + ++len; + BnPtr modulus = NewBn(); + BN_set_bit(modulus.get(), 8 * len); + BnPtr t = NewBn(); + BN_sub(t.get(), modulus.get(), mag.get()); + out.resize(static_cast(len)); + BN_bn2binpad(t.get(), &out[0], len); + return out; + } + + BnPtr BlobToBn(const Blob &b) + { + BnPtr bn(BN_bin2bn(b.empty() ? 0 : &b[0], static_cast(b.size()), 0)); + if (!bn) + throw BadAllocException("BN_bin2bn failed", "detail::BlobToBn"); + return bn; + } + + Blob BnToPadded(const BIGNUM *bn, size_t len) + { + Blob out(len); + if (len != 0 && BN_bn2binpad(bn, &out[0], static_cast(len)) < 0) + throw ArithmeticException("number too large", "detail::BnToPadded"); + return out; + } + + void RandomBytes(byte *out, size_t n, IRNGAlg *prng) + { + if (n == 0) + return; + if (prng != 0) + { + if (prng->Read(out, n) != n) + throw RuntimeException("random generator delivered too few bytes", "detail::RandomBytes"); + return; + } + if (RAND_bytes(out, static_cast(n)) != 1) + throw RuntimeException("RAND_bytes failed", "detail::RandomBytes"); + } + + void DerAppend(Blob &out, byte tag, const byte *content, size_t len) + { + out.push_back(tag); + if (len < 0x80) + out.push_back(static_cast(len)); + else + { + byte tmp[sizeof(size_t)]; + int n = 0; + for (size_t v = len; v != 0; v >>= 8) + tmp[n++] = static_cast(v & 0xff); + out.push_back(static_cast(0x80 | n)); + while (n > 0) + out.push_back(tmp[--n]); + } + out.insert(out.end(), content, content + len); + } + + void DerAppend(Blob &out, byte tag, const Blob &content) + { + DerAppend(out, tag, content.begin(), content.size()); + } + + bool DerRead(const byte *buf, size_t size, size_t &pos, DerItem &item) + { + if (pos + 2 > size) + return false; + item.tag = buf[pos++]; + size_t len = buf[pos++]; + if (len & 0x80) + { + size_t n = len & 0x7f; + if (n == 0 || n > 4 || pos + n > size) + return false; + len = 0; + for (size_t i = 0; i < n; ++i) + len = (len << 8) | buf[pos++]; + } + if (len > size - pos) + return false; + item.data = buf + pos; + item.len = len; + pos += len; + return true; + } + + Blob Digest(const char *name, const Blob &data) + { + unsigned char md[EVP_MAX_MD_SIZE]; + size_t mdlen = 0; + if (!EVP_Q_digest(0, name, 0, data.empty() ? 0 : data.begin(), data.size(), md, &mdlen)) + throw NoSuchAlgorithmException("hash algorithm not available", "detail::Digest"); + Blob out(md, md + mdlen); + SecureZero(md, sizeof(md)); + return out; + } + + Blob X963Kdf(const char *digest, const Blob &secret, const Blob &sharedInfo, size_t len) + { + Blob out(len); + if (len == 0) + return out; + EVP_KDF *kdf = EVP_KDF_fetch(0, "X963KDF", 0); + if (kdf == 0) + throw NoSuchAlgorithmException("X963KDF not available", "detail::X963Kdf"); + EVP_KDF_CTX *ctx = EVP_KDF_CTX_new(kdf); + EVP_KDF_free(kdf); + if (ctx == 0) + throw BadAllocException("EVP_KDF_CTX_new failed", "detail::X963Kdf"); + OSSL_PARAM params[4]; + int n = 0; + params[n++] = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST, const_cast(digest), 0); + params[n++] = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_KEY, + const_cast(secret.begin()), secret.size()); + if (!sharedInfo.empty()) + params[n++] = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_INFO, + const_cast(sharedInfo.begin()), sharedInfo.size()); + params[n] = OSSL_PARAM_construct_end(); + int ok = EVP_KDF_derive(ctx, &out[0], len, params); + EVP_KDF_CTX_free(ctx); + if (ok != 1) + throw AlgorithmException("X9.63 key derivation failed", "detail::X963Kdf"); + return out; + } + + Blob Hmac(const char *digest, const Blob &key, const Blob &data) + { + unsigned char mac[EVP_MAX_MD_SIZE]; + size_t maclen = 0; + static const unsigned char dummy = 0; + if (!EVP_Q_mac(0, "HMAC", 0, digest, 0, key.empty() ? &dummy : key.begin(), key.size(), + data.empty() ? &dummy : data.begin(), data.size(), mac, sizeof(mac), &maclen)) + throw AlgorithmException("HMAC computation failed", "detail::Hmac"); + return Blob(mac, mac + maclen); + } + } // namespace detail + + Blob EncodeOID(const char *oid) + { + if (oid == 0) + throw NullPointerException("null OID", "EncodeOID"); + std::vector arcs; + const char *p = oid; + while (*p) + { + if (!std::isdigit(static_cast(*p))) + throw BadException("invalid OID", "EncodeOID"); + unsigned long long v = 0; + while (std::isdigit(static_cast(*p))) + v = v * 10 + static_cast(*p++ - '0'); + arcs.push_back(v); + if (*p == '.') + { + ++p; + if (*p == 0) + throw BadException("invalid OID", "EncodeOID"); + } + else if (*p != 0) + throw BadException("invalid OID", "EncodeOID"); + } + if (arcs.size() < 2 || arcs[0] > 2 || (arcs[0] < 2 && arcs[1] > 39)) + throw BadException("invalid OID", "EncodeOID"); + std::vector values; + values.push_back(arcs[0] * 40 + arcs[1]); + values.insert(values.end(), arcs.begin() + 2, arcs.end()); + Blob out; + for (size_t i = 0; i < values.size(); ++i) + { + byte tmp[10]; + int n = 0; + unsigned long long v = values[i]; + do + { + tmp[n++] = static_cast(v & 0x7f); + v >>= 7; + } while (v != 0); + while (n > 0) + { + --n; + out.push_back(static_cast(tmp[n] | (n != 0 ? 0x80 : 0))); + } + } + return out; + } + + Blob EncodeNumber(const char *number) + { + detail::BnPtr bn = detail::ParseNumber(number); + if (!bn) + throw BadException("invalid number", "EncodeNumber"); + return detail::BnToTwosComplement(bn.get()); + } + + Blob EncodeNumber(int number) + { + return EncodeNumber(std::to_string(number).c_str()); + } + + Blob hex2blob(const char *hexnumber) + { + if (hexnumber == 0) + throw NullPointerException("null string", "hex2blob"); + std::string digits; + const char *p = hexnumber; + while (std::isspace(static_cast(*p))) + ++p; + if (p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) + p += 2; + for (; *p; ++p) + { + unsigned char c = static_cast(*p); + if (std::isspace(c)) + continue; + if (!std::isxdigit(c)) + throw BadException("invalid hexadecimal digit", "hex2blob"); + digits += static_cast(c); + } + if (digits.size() % 2 != 0) + digits.insert(digits.begin(), '0'); + Blob out(digits.size() / 2); + for (size_t i = 0; i < out.size(); ++i) + { + char pair[3] = {digits[2 * i], digits[2 * i + 1], 0}; + out[i] = static_cast(std::strtoul(pair, 0, 16)); + } + return out; + } + + void blob2hex(const Blob &b, char *hexnumber) + { + if (hexnumber == 0) + throw NullPointerException("null buffer", "blob2hex"); + static const char digits[] = "0123456789abcdef"; + size_t j = 0; + for (size_t i = 0; i < b.size(); ++i) + { + hexnumber[j++] = digits[b[i] >> 4]; + hexnumber[j++] = digits[b[i] & 0x0f]; + } + hexnumber[j] = 0; + } + + bool blob2file(const char *filename, const act::Blob &blob) + { + if (filename == 0) + return false; + std::FILE *f = std::fopen(filename, "wb"); + if (f == 0) + return false; + bool ok = blob.empty() || std::fwrite(blob.begin(), 1, blob.size(), f) == blob.size(); + ok = std::fclose(f) == 0 && ok; + return ok; + } + + bool file2blob(const char *filename, Blob &blob) + { + blob.clear(); + if (filename == 0) + return false; + std::FILE *f = std::fopen(filename, "rb"); + if (f == 0) + return false; + byte buf[8192]; + size_t n; + while ((n = std::fread(buf, 1, sizeof(buf), f)) != 0) + blob.insert(blob.end(), buf, buf + n); + bool ok = !std::ferror(f); + std::fclose(f); + SecureZero(buf, sizeof(buf)); + return ok; + } + + unsigned long CalculateCRC16(const act::byte *message, size_t message_len, unsigned long crc_init_value) + { + unsigned long crc = crc_init_value & 0xffff; + for (size_t i = 0; i < message_len; ++i) + { + crc ^= message[i]; + for (int k = 0; k < 8; ++k) + crc = (crc & 1) ? (crc >> 1) ^ 0xa001 : crc >> 1; + } + return crc; + } + + unsigned long CalculateCRC16(const act::Blob &message, unsigned long crc_init_value) + { + return CalculateCRC16(message.begin(), message.size(), crc_init_value); + } + + unsigned long CalculateCRC16CCITT(const act::byte *message, size_t message_len, unsigned long crc_init_value) + { + unsigned long crc = crc_init_value & 0xffff; + for (size_t i = 0; i < message_len; ++i) + { + crc ^= static_cast(message[i]) << 8; + for (int k = 0; k < 8; ++k) + crc = (crc & 0x8000) ? ((crc << 1) ^ 0x1021) & 0xffff : (crc << 1) & 0xffff; + } + return crc; + } + + unsigned long CalculateCRC16CCITT(const act::Blob &message, unsigned long crc_init_value) + { + return CalculateCRC16CCITT(message.begin(), message.size(), crc_init_value); + } + + unsigned long CalculateCRC32(const act::byte *message, size_t message_len, unsigned long crc_init_value) + { + unsigned long crc = crc_init_value & 0xffffffffUL; + for (size_t i = 0; i < message_len; ++i) + { + crc ^= message[i]; + for (int k = 0; k < 8; ++k) + crc = (crc & 1) ? (crc >> 1) ^ 0xedb88320UL : crc >> 1; + } + return crc ^ 0xffffffffUL; + } + + unsigned long CalculateCRC32(const act::Blob &message, unsigned long crc_init_value) + { + return CalculateCRC32(message.begin(), message.size(), crc_init_value); + } +} // namespace act diff --git a/port/thirdparty/cvact_compat/src/actInternal.h b/port/thirdparty/cvact_compat/src/actInternal.h new file mode 100644 index 000000000..00377a328 --- /dev/null +++ b/port/thirdparty/cvact_compat/src/actInternal.h @@ -0,0 +1,85 @@ +#ifndef ACT_COMPAT_INTERNAL_H +#define ACT_COMPAT_INTERNAL_H + +#include "actAlgorithm.h" +#include "actBlob.h" +#include "actException.h" +#include "actIAlgorithm.h" +#include "actIKey.h" +#include "actIRNGAlg.h" +#include "actMode.h" +#include "actState.h" + +#include +#include + +#include +#include + +namespace act +{ + namespace detail + { + struct BnFree { void operator()(BIGNUM *p) const { BN_clear_free(p); } }; + struct BnCtxFree { void operator()(BN_CTX *p) const { BN_CTX_free(p); } }; + struct GroupFree { void operator()(EC_GROUP *p) const { EC_GROUP_free(p); } }; + struct PointFree { void operator()(EC_POINT *p) const { EC_POINT_clear_free(p); } }; + + typedef std::unique_ptr BnPtr; + typedef std::unique_ptr BnCtxPtr; + typedef std::unique_ptr GroupPtr; + typedef std::unique_ptr PointPtr; + + BnPtr NewBn(); + BnCtxPtr NewBnCtx(); + + // Parses "[-]0x" or "[-]"; whitespace is ignored. Returns null on error. + BnPtr ParseNumber(const char *text); + // Big-endian two's complement, minimal length (at least one byte). + Blob BnToTwosComplement(const BIGNUM *bn); + // Big-endian unsigned or two's complement with a leading zero byte. + BnPtr BlobToBn(const Blob &b); + Blob BnToPadded(const BIGNUM *bn, size_t len); + + void RandomBytes(byte *out, size_t n, IRNGAlg *prng); + + // Minimal DER helpers. + void DerAppend(Blob &out, byte tag, const Blob &content); + void DerAppend(Blob &out, byte tag, const byte *content, size_t len); + struct DerItem + { + byte tag; + const byte *data; + size_t len; + }; + // Reads one TLV at pos (advancing pos). Returns false on malformed input. + bool DerRead(const byte *buf, size_t size, size_t &pos, DerItem &item); + + Blob Digest(const char *name, const Blob &data); + Blob X963Kdf(const char *digest, const Blob &secret, const Blob &sharedInfo, size_t len); + Blob Hmac(const char *digest, const Blob &key, const Blob &data); + + // Base for algorithms whose output is collected in a buffer and drained by Read(). + class BufferedAlgorithm : public IAlgorithm + { + public: + BufferedAlgorithm() : mStatus(READY) {} + + size_t GetAvailableSize() const { return mOut.size(); } + size_t Read(Blob &output, size_t max); + size_t Read(byte *outbuffer, size_t buffersize); + status_t GetStatus() const { return mStatus; } + + protected: + Blob mOut; + status_t mStatus; + }; + + IKey *CreateIESKey(); + IKey *CreateBlockCipherKey(); + IKey *ImportECKeyBlob(const Blob &keyblob); + IAlgorithm *CreateHashAlgorithm(const char *name); + } // namespace detail +} // namespace act + +#endif // ACT_COMPAT_INTERNAL_H diff --git a/port/thirdparty/miracl/mirdef.h b/port/thirdparty/miracl/mirdef.h new file mode 100644 index 000000000..1ce8df95a --- /dev/null +++ b/port/thirdparty/miracl/mirdef.h @@ -0,0 +1,48 @@ +/* + * MIRACL compiler/hardware definitions - mirdef.h + * Portable C configuration for the CrypTool port (GCC/Clang, 32- and 64-bit). + * + * Same word size as the original Win32 build (32-bit mr_small, 64-bit + * double-length type), but without any inline assembly. + */ + +#ifndef CRYPTOOL_MIRDEF_H +#define CRYPTOOL_MIRDEF_H + +#define MIRACL 32 + +#if defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +#define MR_BIG_ENDIAN +#else +#define MR_LITTLE_ENDIAN +#endif + +#define mr_utype int +#define MR_IBITS 32 +#if defined(__LP64__) || defined(_LP64) +#define MR_LBITS 64 +#else +#define MR_LBITS 32 +#endif +#define mr_unsign32 unsigned int +#define mr_dltype long long +#define mr_unsign64 unsigned long long +#define MR_NOASM +#define MR_FLASH 52 +#define MR_NO_STANDARD_IO + +/* glibc's (C23 / _GNU_SOURCE, always on in g++) declares fadd, fsub, fmul + and fdiv; rename MIRACL's flash functions after has been seen. */ +#ifdef __cplusplus +extern "C++" { +#include +} +#else +#include +#endif +#define fadd mr_fadd +#define fsub mr_fsub +#define fmul mr_fmul +#define fdiv mr_fdiv + +#endif diff --git a/port/thirdparty/ntl/MakeDesc.c b/port/thirdparty/ntl/MakeDesc.c new file mode 100644 index 000000000..fa3da2f9f --- /dev/null +++ b/port/thirdparty/ntl/MakeDesc.c @@ -0,0 +1,1105 @@ + + +#include +#include +#include +#include +#include + + +#include + + +#if (defined(__GNUC__) && (defined(__i386__) || defined(__i486__) || defined(__i586__))) + + +#define AutoFix (1) + +#else + +#define AutoFix (0) + +#endif + + +int val_int(int x); +unsigned int val_uint(unsigned int x); + +long val_long(long x); +unsigned long val_ulong(unsigned long x); + +size_t val_size_t(size_t x); + +double val_double(double x); + +void touch_int(int* x); +void touch_uint(unsigned int* x); + +void touch_long(long* x); +void touch_ulong(unsigned long* x); + +void touch_size_t(size_t* x); + +void touch_double(double* x); + + + + +double power2(long k) +{ + long i; + double res; + + res = 1; + + for (i = 1; i <= k; i++) + res = res * 2; + + return res; +} + + +long DoubleRounding(long dp) +{ + double a = power2(dp-1) + 1; + double b = (power2(dp)-1)/power2(dp+1); + register double x = a + b; + double y = x; + + touch_double(&y); + + if (y != power2(dp-1) + 1) + return 1; + else + return 0; +} + + + +long DoublePrecision(void) +{ + double eps, one, res; + long k; + + one = val_double(1.0); + eps = val_double(1.0); + + k = 0; + + do { + double tmp; + + k++; + eps *= 1.0/2.0; + tmp = 1.0 + eps; + touch_double(&tmp); + res = tmp - one; + } while (res == eps); + + return k; +} + +long DoublePrecision1(void) +{ + double eps, one, res; + long k; + + one = val_double(1.0); + eps = val_double(1.0); + + k = 0; + + do { + register double tmp; + + k++; + eps *= 1.0/2.0; + tmp = 1.0 + eps; + res = tmp - one; + } while (res == eps); + + return k; +} + + +union d_or_rep { + double d; + unsigned long rep[2]; +}; + +long RepTest(void) +{ + union d_or_rep v; + + if (sizeof(double) != 2*sizeof(long)) + return 0; + + v.rep[0] = v.rep[1] = 0; + + v.d = 565656565656.0; + + if (v.rep[0] == 0x42607678 && v.rep[1] == 0x46f30000) + return 1; + else if (v.rep[1] == 0x42607678 && v.rep[0] == 0x46f30000) + return -1; + else + return 0; +} + +void print2k(FILE *f, long k, long bpl) +{ + long m, l; + long first; + + if (k <= 0) { + fprintf(f, "((double) 1.0)"); + return; + } + + m = bpl - 2; + first = 1; + + fprintf(f, "("); + + while (k > 0) { + if (k > m) + l = m; + else + l = k; + + k = k - l; + + if (first) + first = 0; + else + fprintf(f, "*"); + + fprintf(f, "((double)(1L<<%ld))", l); + } + + fprintf(f, ")"); +} + + + + + + +void print_mul_body(FILE *f, long n1, long k, long fn, + long half_flag, long short_flag) +{ + + long n, i, chop, r; + unsigned long mask, mask2; + + if (half_flag) + n = n1/2; + else + n = n1; + + chop = n % k; /* first block */ + if (chop == 0) + chop = k; + r = n - k; + mask = (1UL << k) - 1UL; + + fprintf(f, "\n\n#define NTL_"); + if (half_flag) fprintf(f, "HALF_"); + if (short_flag) fprintf(f, "SHORT_"); + + fprintf(f, "BB_MUL_CODE%ld \\\n", fn); + + + if (fn > 0) /* Mul1/AddMul1 */ + { + fprintf(f, " long i;\\\n"); + fprintf(f, " _ntl_ulong carry = 0, b;\\\n"); + } + fprintf(f, " _ntl_ulong hi, lo, t;\\\n"); + fprintf(f, " _ntl_ulong A[%ld];\\\n", 1L << k); + fprintf(f, " A[0] = 0;\\\n"); + + fprintf(f, " A[1] = a;\\\n"); + + for (i = 2; i < (1L << k); i++) + { + if (i % 2 == 0) + fprintf(f, " A[%ld] = A[%ld] << 1;\\\n", i, i / 2); + else + fprintf(f, " A[%ld] = A[%ld] ^ A[1];\\\n", i, i - 1); + } + + if (fn > 0) + { + fprintf(f, " for (i = 0; i < sb; i++) {\\\n"); + fprintf(f, " b = bp[i];\\\n"); + fprintf(f, " "); + } + + fprintf(f, " lo = A[b & %lu]; ", mask); + fprintf(f, "t = A[(b >> %ld) & %lu]; ", k, mask); + fprintf(f, "hi = t >> %ld; lo ^= t << %ld;\\\n", n1-k, k); + + for (i = 2*k; i < n - chop; i += k) { + if (fn > 0) fprintf(f, " "); + fprintf(f, " t = A[(b >> %ld) & %lu]; ", i, mask); + fprintf(f, "hi ^= t >> %ld; lo ^= t << %ld;\\\n", n1-i, i); + } + + if (fn > 0) fprintf(f, " "); + fprintf(f, " t = A[b >> %ld]; ", n-chop); + fprintf(f, "hi ^= t >> %ld; lo ^= t << %ld;\\\n", n1-i, i); + + mask = 0; + for (i = 0; i < n; i += k) + mask |= 1UL << i; + mask = ~mask; + if (half_flag) mask &= (1UL << n) - 1UL; + mask2 = mask; + + if (!short_flag) { + for (i = 1; i < k; i++) + { + if (fn > 0) fprintf(f, " "); + + if (i == 1) + fprintf(f, " if (a >> %ld) ", n1-i); + else + fprintf(f, " if ((a >> %ld) & 1) ", n1-i); + + /* bit n1-i from a was not considered in blocks of + k bits from b for index j >= i */ + fprintf(f, "hi ^= ((b & 0x%lxUL) >> %ld);\\\n", mask2, i); + mask2 = (mask2 << 1) & mask; + } + } + + if (fn > 0) fprintf(f, " "); + + if (fn == 0) + { + fprintf(f, " c[0] = lo; "); + fprintf(f, " c[1] = hi;\\\n"); + } + else if (fn == 1 || fn == 3) + { + fprintf(f, " cp[i] = carry ^ lo; "); + fprintf(f, " carry = hi;\\\n"); + } + else if (fn == 2) + { + fprintf(f, " cp[i] ^= (carry ^ lo); "); + fprintf(f, " carry = hi;\\\n"); + } + if (fn > 0) + { + + fprintf(f, " }\\\n"); + + if (fn == 1 || fn == 3) + fprintf(f, " cp[sb] = carry;\\\n"); + else + fprintf(f, " cp[sb] ^= carry;\\\n"); + + } + fprintf(f, "\n\n\n"); + + +} + + + + + +/* + * This generates anternative code that runs significantly faster + * on some machines, like a PowerPC (and probably other RISC machines). + * It makes it easier for the compiler to schedule instrucyions better, + * and it avoids branches. It seems like this does not help + * on x86 machines (and can even make things worse). + */ + + +void print_alt_mul_body(FILE *f, long n1, long k, long fn, + long half_flag, long short_flag) +{ + + long n, i, chop, r; + unsigned long mask, mask2; + + if (half_flag) + n = n1/2; + else + n = n1; + + chop = n % k; /* first block */ + if (chop == 0) + chop = k; + r = n - k; + mask = (1UL << k) - 1UL; + + fprintf(f, "\n\n#define NTL_ALT_"); + if (half_flag) fprintf(f, "HALF_"); + if (short_flag) fprintf(f, "SHORT_"); + + fprintf(f, "BB_MUL_CODE%ld \\\n", fn); + + if (fn > 0) /* Mul1/AddMul1 */ + { + fprintf(f, " long i;\\\n"); + fprintf(f, " _ntl_ulong carry = 0;\\\n"); + } + fprintf(f, " _ntl_ulong A[%ld];\\\n", 1L << k); + fprintf(f, " A[0] = 0;\\\n"); + + fprintf(f, " A[1] = a;\\\n"); + + for (i = 2; i < (1L << k); i++) + { + if (i % 2 == 0) + fprintf(f, " A[%ld] = A[%ld] << 1;\\\n", i, i / 2); + else + fprintf(f, " A[%ld] = A[%ld] ^ A[1];\\\n", i, i - 1); + } + + if (fn > 0) + { + fprintf(f, " for (i = 0; i < sb; i++) {\\\n"); + fprintf(f, " const _ntl_ulong b = bp[i];\\\n"); + } + + for (i = k; i < n - chop; i += k) { + if (fn > 0) fprintf(f, " "); + fprintf(f, " const _ntl_ulong t%ld = A[(b >> %ld) & %lu]; \\\n", i, i, mask); + + } + if (fn > 0) fprintf(f, " "); + fprintf(f, " const _ntl_ulong t%ld = A[b >> %ld]; \\\n", n-chop, n-chop); + + if (fn > 0) fprintf(f, " "); + fprintf(f, " const _ntl_ulong lo = A[b & %lu] \\\n", mask); + + for (i = k; i < n; i += k) { + if (fn > 0) fprintf(f, " "); + fprintf(f, " ^ (t%ld << %ld)", i, i); + if (i == n - chop) + fprintf(f, ";\\\n"); + else + fprintf(f, "\\\n"); + } + + for (i = k; i < n; i += k) { + if (fn > 0) fprintf(f, " "); + if (i == k) + fprintf(f, " const _ntl_ulong hi = "); + else + fprintf(f, " ^ "); + fprintf(f, "(t%ld >> %ld)", i, n1-i); + if (i == n - chop && short_flag) + fprintf(f, ";\\\n"); + else + fprintf(f, "\\\n"); + + + } + + mask = 0; + for (i = 0; i < n; i += k) + mask |= 1UL << i; + mask = ~mask; + if (half_flag) mask &= (1UL << n) - 1UL; + mask2 = mask; + + if (!short_flag) { + for (i = 1; i < k; i++) + { + + /* bit n1-i from a was not considered in blocks of + k bits from b for index j >= i */ + + if (fn > 0) fprintf(f, " "); + + + if (i == 1) + fprintf(f, + " ^ (((b & 0x%lxUL) >> %ld) & (-(a >> %ld)))", + mask2, i, n1-1); + else { + fprintf(f, + " ^ (((b & 0x%lxUL) >> %ld) & (-((a >> %ld) & 1UL)))", + mask2, i, n1-i); + } + if (i == k-1) + fprintf(f, ";\\\n"); + else + fprintf(f, "\\\n"); + + + + mask2 = (mask2 << 1) & mask; + } + } + + if (fn > 0) fprintf(f, " "); + + if (fn == 0) + { + fprintf(f, " c[0] = lo; "); + fprintf(f, " c[1] = hi;\\\n"); + } + else if (fn == 1) + { + fprintf(f, " cp[i] = carry ^ lo; "); + fprintf(f, " carry = hi;\\\n"); + } + else if (fn == 2) + { + fprintf(f, " cp[i] ^= (carry ^ lo); "); + fprintf(f, " carry = hi;\\\n"); + } + if (fn > 0) + { + + fprintf(f, " }\\\n"); + + if (fn == 1 || fn == 3) + fprintf(f, " cp[sb] = carry;\\\n"); + else + fprintf(f, " cp[sb] ^= carry;\\\n"); + + } + fprintf(f, "\n\n\n"); + + +} + + + + +void print_alt1_mul_body(FILE *f, long n1, long k, long fn, + long half_flag, long short_flag) +{ + + long n, i, chop, r; + unsigned long mask, mask2; + + if (half_flag) + n = n1/2; + else + n = n1; + + chop = n % k; /* first block */ + if (chop == 0) + chop = k; + r = n - k; + mask = (1UL << k) - 1UL; + + fprintf(f, "\n\n#define NTL_ALT1_"); + if (half_flag) fprintf(f, "HALF_"); + if (short_flag) fprintf(f, "SHORT_"); + + fprintf(f, "BB_MUL_CODE%ld \\\n", fn); + + + if (fn > 0) /* Mul1/AddMul1 */ + { + fprintf(f, " long i;\\\n"); + fprintf(f, " _ntl_ulong carry = 0, b;\\\n"); + } + fprintf(f, " _ntl_ulong hi, lo, t;\\\n"); + fprintf(f, " _ntl_ulong A[%ld];\\\n", 1L << k); + fprintf(f, " A[0] = 0;\\\n"); + + fprintf(f, " A[1] = a;\\\n"); + + for (i = 2; i < (1L << k); i++) + { + if (i % 2 == 0) + fprintf(f, " A[%ld] = A[%ld] << 1;\\\n", i, i / 2); + else + fprintf(f, " A[%ld] = A[%ld] ^ A[1];\\\n", i, i - 1); + } + + if (fn > 0) + { + fprintf(f, " for (i = 0; i < sb; i++) {\\\n"); + fprintf(f, " b = bp[i];\\\n"); + fprintf(f, " "); + } + + fprintf(f, " lo = A[b & %lu]; ", mask); + fprintf(f, "t = A[(b >> %ld) & %lu]; ", k, mask); + fprintf(f, "hi = t >> %ld; lo ^= t << %ld;\\\n", n1-k, k); + + for (i = 2*k; i < n - chop; i += k) { + if (fn > 0) fprintf(f, " "); + fprintf(f, " t = A[(b >> %ld) & %lu]; ", i, mask); + fprintf(f, "hi ^= t >> %ld; lo ^= t << %ld;\\\n", n1-i, i); + } + + if (fn > 0) fprintf(f, " "); + fprintf(f, " t = A[b >> %ld]; ", n-chop); + fprintf(f, "hi ^= t >> %ld; lo ^= t << %ld;\\\n", n1-i, i); + + mask = 0; + for (i = 0; i < n; i += k) + mask |= 1UL << i; + mask = ~mask; + if (half_flag) mask &= (1UL << n) - 1UL; + mask2 = mask; + + + if (!short_flag) { + for (i = 1; i < k; i++) + { + + /* bit n1-i from a was not considered in blocks of + k bits from b for index j >= i */ + + if (fn > 0) fprintf(f, " "); + + + if (i == 1) + fprintf(f, + " hi ^= (((b & 0x%lxUL) >> %ld) & (-(a >> %ld)))", + mask2, i, n1-1); + else { + fprintf(f, + " ^ (((b & 0x%lxUL) >> %ld) & (-((a >> %ld) & 1UL)))", + mask2, i, n1-i); + } + if (i == k-1) + fprintf(f, ";\\\n"); + else + fprintf(f, "\\\n"); + + + + mask2 = (mask2 << 1) & mask; + } + } + + + + + if (fn > 0) fprintf(f, " "); + + if (fn == 0) + { + fprintf(f, " c[0] = lo; "); + fprintf(f, " c[1] = hi;\\\n"); + } + else if (fn == 1 || fn == 3) + { + fprintf(f, " cp[i] = carry ^ lo; "); + fprintf(f, " carry = hi;\\\n"); + } + else if (fn == 2) + { + fprintf(f, " cp[i] ^= (carry ^ lo); "); + fprintf(f, " carry = hi;\\\n"); + } + if (fn > 0) + { + + fprintf(f, " }\\\n"); + + if (fn == 1 || fn == 3) + fprintf(f, " cp[sb] = carry;\\\n"); + else + fprintf(f, " cp[sb] ^= carry;\\\n"); + + } + fprintf(f, "\n\n\n"); + + +} + + + + + + + +void print_BB_mul_code(FILE *f, long n) +{ + long k; + + if (n >= 64) + k = 4; + else + k = 3; + + + print_mul_body(f, n, k, 0, 0, 0); + print_mul_body(f, n, 4, 1, 0, 0); + print_mul_body(f, n, 4, 2, 0, 0); + print_mul_body(f, n, 4, 1, 0, 1); + print_mul_body(f, n, 2, 0, 1, 0); + + + + print_alt_mul_body(f, n, k, 0, 0, 0); + print_alt_mul_body(f, n, 4, 1, 0, 0); + print_alt_mul_body(f, n, 4, 2, 0, 0); + print_alt_mul_body(f, n, 4, 1, 0, 1); + print_alt_mul_body(f, n, 2, 0, 1, 0); + + + + print_alt1_mul_body(f, n, k, 0, 0, 0); + print_alt1_mul_body(f, n, 4, 1, 0, 0); + print_alt1_mul_body(f, n, 4, 2, 0, 0); + print_alt1_mul_body(f, n, 4, 1, 0, 1); + print_alt1_mul_body(f, n, 2, 0, 1, 0); + + fprintf(f, "#define NTL_BB_MUL1_BITS (4)\n\n"); +} + + + + + +void print_BB_sqr_code(FILE *f, long n) +{ + long i, pos; + + fprintf(f, "\n\n"); + fprintf(f, "#define NTL_BB_SQR_CODE \\\n"); + fprintf(f, "lo=sqrtab[a&255];\\\n"); + pos = 16; + + for (i = 8; i < n; i += 8) { + if (2*(i+8) <= n) { + fprintf(f, "lo=lo|(sqrtab[(a>>%ld)&255]<<%ld);\\\n", i, pos); + pos += 16; + } + else if (2*i == n) { + fprintf(f, "hi=sqrtab[(a>>%ld)&255];\\\n", i); + pos = 16; + } + else if (2*i > n) { + fprintf(f, "hi=hi|(sqrtab[(a>>%ld)&255]<<%ld);\\\n", i, pos); + pos += 16; + } + else { /* only applies if word size is not a multiple of 16 */ + fprintf(f, "_ntl_ulong t=sqrtab[(a>>%ld)&255];\\\n", i); + fprintf(f, "lo=lo|(t<<%ld);\\\n", pos); + fprintf(f, "hi=t>>%ld;\\\n", n-8); + pos = 8; + } + } + + fprintf(f, "\n\n"); +} + + +void print_BB_rev_code(FILE *f, long n) +{ + long i; + + fprintf(f, "\n\n"); + fprintf(f, "#define NTL_BB_REV_CODE "); + + for (i = 0; i < n; i += 8) { + if (i != 0) fprintf(f, "\\\n|"); + fprintf(f, "(revtab[(a>>%ld)&255]<<%ld)", i, n-8-i); + } + + fprintf(f, "\n\n"); +} + + + + +char *yn_vec[2] = { "no", "yes" }; + + + +int main() +{ + long bpl, bpi, bpt, rs_arith, nbits, single_mul_ok; + long dp, dp1, dr; + FILE *f; + long warnings = 0; + + unsigned long ulval; + unsigned int uival; + size_t tval; + long slval; + + fprintf(stderr, "This is NTL version %s\n\n", NTL_VERSION); + + + + + /* + * compute bpl = bits per long + */ + + ulval = val_ulong(1); + bpl = 0; + + while (ulval) { + ulval <<= 1; + touch_ulong(&ulval); + bpl++; + } + + + + + /* + * compute bpi = bits per int + */ + + uival = val_uint(1); + bpi = 0; + + while (uival) { + uival <<= 1; + touch_uint(&uival); + bpi++; + } + + + + /* + * compute bpt = bits per size_t + */ + + tval = val_size_t(1); + bpt = 0; + + while (tval) { + tval <<= 1; + touch_size_t(&tval); + bpt++; + } + + + /* + * check if bpl and bpi are not too small --- any standard conforming + * platform should pass this test. + */ + + if (bpi < 16) { + fprintf(stderr, "BAD NEWS: int type too short.\n"); + return 1; + } + + if (bpl < 32) { + fprintf(stderr, "BAD NEWS: long type too short.\n"); + return 1; + } + + /* + * check that ints are bigger than chars. + */ + + if (bpi <= CHAR_BIT) { + fprintf(stderr, "BAD NEWS: int type must be longer than char type.\n"); + return 1; + } + + + + /* + * check that bpl is a multiple of 8. + */ + + if (bpl % 8 != 0) { + fprintf(stderr, "BAD NEWS: word size must be multiple of 8 bits.\n"); + return 1; + } + + + + + /* + * check if width of signed versions of int and long agree with that of + * the unsigned versions, and that negative numbers are represented + * using 2's compliment. + * + * The C99 standard, at least, is very precise about the possible + * representations of unsigned and signed integer types, and so if + * the following tests pass, we can be sure that the desired + * properties hold. NTL relies implicitly and crucially on + * these properties. + * + * I know of no machines for which these properties do not hold. + */ + + if (((unsigned int) val_int(INT_MIN)) != val_uint(1U << (bpi-1))) { + fprintf(stderr, "BAD NEWS: machine must be 2's compliment.\n"); + return 1; + } + + if (((unsigned int) val_int(INT_MAX)) != val_uint((1U << (bpi-1)) - 1U)) { + fprintf(stderr, "BAD NEWS: machine must be 2's compliment.\n"); + return 1; + } + + if (((unsigned long) val_long(LONG_MIN)) != val_ulong(1UL << (bpl-1))) { + fprintf(stderr, "BAD NEWS: machine must be 2's compliment.\n"); + return 1; + } + + if (((unsigned long) val_long(LONG_MAX)) != val_ulong((1UL<<(bpl-1))-1UL)) { + fprintf(stderr, "BAD NEWS: machine must be 2's compliment.\n"); + return 1; + } + + + + /* + * check that floating point to integer conversions truncates toward zero + * --- any standard conforming platform should pass this test. + */ + + if (((long) val_double(1.75)) != 1L) { + fprintf(stderr, + "BAD NEWS: machine must truncate floating point toward zero.\n"); + return 1; + } + + if (((long) val_double(-1.75)) != -1L) { + fprintf(stderr, + "BAD NEWS: machine must truncate floating point toward zero.\n"); + return 1; + } + + + + /* + * Test if right shift is arithemtic or not. According to the + * standards, the result of right-shifting a negative number is + * "implementation defined", which almost surely means the right shift + * is *always* arithmetic or *always* logical. However, this cannot + * be guaranteed, and so this test is *not* 100% portable --- but I + * know of no machine for which this test does not correctly + * predict the general behavior. One should set the NTL_CLEAN_INT + * flag if one wants to avoid such machine dependencies. + */ + + slval = val_long(-1); + if ((slval >> 1) == slval) + rs_arith = 1; + else + rs_arith = 0; + + + + /* + * Next, we check some properties of floating point arithmetic. + * An implementation should conform to the IEEE floating + * point standard --- essentially all modern platforms do, + * except for a few very old Cray's. There is no easy way + * to check this, so we simply make a few simple sanity checks, + * calculate the precision, and if the platform performs + * double precision arithemtic in extended double precision registers. + * The last property is one that the IEE standard allows, and which + * some important platforms (like x86) have --- this is quite + * unfortunate, as it really makes many of the other properties + * of the IEEE standard unusable. + */ + + /* + * First, we simply check that we are using a machine with radix 2. + */ + + if (FLT_RADIX != 2) { + fprintf(stderr, "BAD NEWS: machine must use IEEE floating point.\n"); + return 1; + } + + /* + * Next, we calculate the precision of "in memory" doubles, + * and check that it is at least 53. + */ + + dp = DoublePrecision(); + + if (dp < 53) { + fprintf(stderr, "BAD NEWS: machine must use IEEE floating point (*).\n"); + return 1; + } + + /* + * Next, we check that the *range* of doubles is sufficiently large. + * Specifically, we require that DBL_MAX > 2^{7*max(bpl, dp)} + * and 1/DBL_MIN > 2^{7*max(bpl, dp)}. + * On IEEE floating point compliant machines, this + * will hold, and the following test will pass, if bpl is at most 128, which + * should be true for the foreseeable future. + */ + + if (log(DBL_MAX)/log(2.0) < 7.01*bpl || log(DBL_MAX)/log(2.0) < 7.01*dp || + -log(DBL_MIN)/log(2.0) < 7.01*bpl || -log(DBL_MIN)/log(2.0) < 7.01*dp) { + fprintf(stderr, "BAD NEWS: range of doubles too small.\n"); + return 1; + } + + + + /* + * Next, we check if the machine has wider "in-register" doubles or not. + * This test almost always yields the correct result --- if not, + * you will have to set the NTL_EXT_DOUBLE in "mach_desc.h" + * by hand. + * + * The test effectively proves that in-register doubles are wide + * if dp1 > dp || dr. + */ + + + dp1 = DoublePrecision1(); + dr = DoubleRounding(dp); + + + /* + * Set nbits --- the default radix size for NTL's "built in" + * long integer arithmetic. + * + * Given the minimum size of blp and dp, the smallest possible + * value of nbits is 30. + */ + + + if (bpl-2 < dp-3) + nbits = bpl-2; + else + nbits = dp-3; + + if (nbits % 2 != 0) nbits--; + + + /* + * We next test if the NTL_SINGLE_MUL option is valid. This test is + * inherently DIRTY (i.e., the behavior of the test itself is not well + * defined according to the standard), but in practice should not cause any + * bad behavior, especially if the NTL_SINGLE_MUL option is never used. + * This option is anyway considered fairly obsolete, and if desired, one may + * change the following "if 1" to "if 0" and avoid performing this test + * altogether. + */ + +#if 1 + single_mul_ok = RepTest(); +#else + single_mul_ok = 0; +#endif + + + /* + * That's it! All tests have passed. + */ + + fprintf(stderr, "GOOD NEWS: compatible machine.\n"); + fprintf(stderr, "summary of machine characteristics:\n"); + fprintf(stderr, "bits per long = %ld\n", bpl); + fprintf(stderr, "bits per int = %ld\n", bpi); + fprintf(stderr, "bits per size_t = %ld\n", bpt); + fprintf(stderr, "arith right shift = %s\n", yn_vec[rs_arith]); + fprintf(stderr, "double precision = %ld\n", dp); + fprintf(stderr, "NBITS (maximum) = %ld\n", nbits); + fprintf(stderr, "single mul ok = %s\n", yn_vec[single_mul_ok != 0]); + fprintf(stderr, "register double precision = %ld\n", dp1); + fprintf(stderr, "double rounding detected = %s\n", yn_vec[dr]); + + if (((dp1 > dp) || dr) && AutoFix) + fprintf(stderr, "-- auto x86 fix\n"); + + if (dp != 53) { + warnings = 1; + + fprintf(stderr, "\n\nWARNING:\n\n"); + fprintf(stderr, "Nonstandard floating point precision.\n"); + fprintf(stderr, "IEEE standard is 53 bits.\n"); + } + +#if (defined(__sparc__) && !defined(__sparc_v8__) && \ + !defined(__sparcv8) && !defined(__sparc_v9__) && !defined(__sparcv9)) + + warnings = 1; + + fprintf(stderr, "\n\nWARNING:\n\n"); + fprintf(stderr, "If this Sparc is a Sparc-10 or later (so it has\n"); + fprintf(stderr, "a hardware integer multiply instruction) you\n"); + fprintf(stderr, "should specify the -mv8 option in the makefile\n"); + fprintf(stderr, "to obtain more efficient code.\n"); + +#endif + + if (((dp1 > dp) || dr) && !AutoFix) { + warnings = 1; + fprintf(stderr, "\n\nWARNING:\n\n"); + fprintf(stderr, "This platform has extended double precision registers.\n"); + fprintf(stderr, "While that may sound like a good thing, it actually is not.\n"); + fprintf(stderr, "If this is a Pentium or other x86 and your compiler\n"); + fprintf(stderr, "is g++ or supports GNU 'asm' constructs, it is recommended\n"); + fprintf(stderr, "to compile NTL with the NTL_X86_FIX flag to get true IEEE floating point.\n"); + fprintf(stderr, "Set this flag by editing the file config.h.\n"); + fprintf(stderr, "The code should still work even if you don't set\n"); + fprintf(stderr, "this flag. See quad_float.txt for details.\n\n"); + } + + +#if 0 + + /* better not to be interactive */ + + if (warnings) { + int c; + + fprintf(stderr, "Do you want to continue anyway[y/n]? "); + c = getchar(); + if (c == 'n' || c == 'N') { + fprintf(stderr, "Make the necessary changes to the makefile and/or config.h,\n"); + fprintf(stderr, "then type 'make clobber' and then 'make'.\n\n\n"); + return 1; + } + } + +#endif + + f = fopen("mach_desc.h", "w"); + if (!f) { + fprintf(stderr, "can't open mach_desc.h for writing\n"); + return 1; + } + + fprintf(f, "#ifndef NTL_mach_desc__H\n"); + fprintf(f, "#define NTL_mach_desc__H\n\n\n"); + fprintf(f, "#define NTL_BITS_PER_LONG (%ld)\n", bpl); + fprintf(f, "#define NTL_MAX_LONG (%ldL)\n", ((long) ((1UL<<(bpl-1))-1UL))); + fprintf(f, "#define NTL_MAX_INT (%ld)\n", ((long) ((1UL<<(bpi-1))-1UL))); + fprintf(f, "#define NTL_BITS_PER_INT (%ld)\n", bpi); + fprintf(f, "#define NTL_BITS_PER_SIZE_T (%ld)\n", bpt); + fprintf(f, "#define NTL_ARITH_RIGHT_SHIFT (%ld)\n", rs_arith); + fprintf(f, "#define NTL_NBITS_MAX (%ld)\n", nbits); + fprintf(f, "#define NTL_DOUBLE_PRECISION (%ld)\n", dp); + fprintf(f, "#define NTL_FDOUBLE_PRECISION "); + print2k(f, dp-1, bpl); + fprintf(f, "\n"); + fprintf(f, "#define NTL_QUAD_FLOAT_SPLIT ("); + print2k(f, dp - (dp/2), bpl); + fprintf(f, "+1.0)\n"); + fprintf(f, "#define NTL_EXT_DOUBLE (%d)\n", ((dp1 > dp) || dr)); + fprintf(f, "#define NTL_SINGLE_MUL_OK (%d)\n", single_mul_ok != 0); + fprintf(f, "#define NTL_DOUBLES_LOW_HIGH (%d)\n\n\n", single_mul_ok < 0); + print_BB_mul_code(f, bpl); + print_BB_sqr_code(f, bpl); + print_BB_rev_code(f, bpl); + + fprintf(f, "#define NTL_MIN_LONG (-NTL_MAX_LONG - 1L)\n"); + fprintf(f, "#define NTL_MIN_INT (-NTL_MAX_INT - 1)\n"); + + fprintf(f, "#endif\n\n"); + + fclose(f); + + fprintf(stderr, "\n\n"); + + return 0; +} diff --git a/port/thirdparty/ntl/MakeDescAux.c b/port/thirdparty/ntl/MakeDescAux.c new file mode 100644 index 000000000..cad629e62 --- /dev/null +++ b/port/thirdparty/ntl/MakeDescAux.c @@ -0,0 +1,23 @@ + +#include + +int val_int(int x) { return x; } +unsigned int val_uint(unsigned int x) { return x; } + +long val_long(long x) { return x; } +unsigned long val_ulong(unsigned long x) { return x; } + +size_t val_size_t(size_t x) { return x; } + +double val_double(double x) { return x; } + +void touch_int(int* x) {} +void touch_uint(unsigned int* x) {} + +void touch_long(long* x) {} +void touch_ulong(unsigned long* x) {} + +void touch_size_t(size_t* x) {} + +void touch_double(double* x) {} + diff --git a/port/thirdparty/openssl_compat/include/OpenSSL/ct_ossl_base.h b/port/thirdparty/openssl_compat/include/OpenSSL/ct_ossl_base.h new file mode 100644 index 000000000..149918e57 --- /dev/null +++ b/port/thirdparty/openssl_compat/include/OpenSSL/ct_ossl_base.h @@ -0,0 +1,24 @@ +/* + * Shared helpers for the OpenSSL 1.0.1 compatibility headers used by CrypTool. + * Deliberately free of system #includes: CrypTool includes these headers inside + * C++ namespaces (namespace __SSL { ... }), where pulling in system headers + * would misplace their declarations. + */ +#ifndef CT_OSSL_BASE_H +#define CT_OSSL_BASE_H + +#if defined(__SIZE_TYPE__) +#define CT_OSSL_SIZE_T __SIZE_TYPE__ +#else +#define CT_OSSL_SIZE_T size_t +#endif + +#ifdef __cplusplus +#define CT_OSSL_BEGIN_C extern "C" { +#define CT_OSSL_END_C } +#else +#define CT_OSSL_BEGIN_C +#define CT_OSSL_END_C +#endif + +#endif diff --git a/port/thirdparty/openssl_compat/include/OpenSSL/md2.h b/port/thirdparty/openssl_compat/include/OpenSSL/md2.h new file mode 100644 index 000000000..f084d1f8e --- /dev/null +++ b/port/thirdparty/openssl_compat/include/OpenSSL/md2.h @@ -0,0 +1,40 @@ +/* + * OpenSSL 1.0.1 compatible MD2 API. OpenSSL 3 ships without MD2, so the + * functions are implemented by cryptool_openssl_compat (ct_MD2_*). + */ +#ifndef OPENSSL_MD2_H +#define OPENSSL_MD2_H +#ifndef HEADER_MD2_H +#define HEADER_MD2_H +#endif + +#include "ct_ossl_base.h" + +#define MD2_DIGEST_LENGTH 16 +#define MD2_BLOCK 16 +#ifndef MD2_INT +#define MD2_INT unsigned int +#endif + +CT_OSSL_BEGIN_C + +typedef struct MD2state_st { + unsigned int num; + unsigned char data[MD2_BLOCK]; + MD2_INT cksm[MD2_BLOCK]; + MD2_INT state[MD2_BLOCK]; +} MD2_CTX; + +#define MD2_options ct_MD2_options +#define MD2_Init ct_MD2_Init +#define MD2_Update ct_MD2_Update +#define MD2_Final ct_MD2_Final + +const char *MD2_options(void); +int MD2_Init(MD2_CTX *c); +int MD2_Update(MD2_CTX *c, const unsigned char *data, CT_OSSL_SIZE_T len); +int MD2_Final(unsigned char *md, MD2_CTX *c); + +CT_OSSL_END_C + +#endif diff --git a/port/thirdparty/openssl_compat/include/OpenSSL/md4.h b/port/thirdparty/openssl_compat/include/OpenSSL/md4.h new file mode 100644 index 000000000..4524e7d54 --- /dev/null +++ b/port/thirdparty/openssl_compat/include/OpenSSL/md4.h @@ -0,0 +1,35 @@ +/* + * OpenSSL 1.0.1/3.x compatible MD4 declarations (implemented by libcrypto). + * Same layout and guards as OpenSSL 3's . + */ +#ifndef OPENSSL_MD4_H +#define OPENSSL_MD4_H +#ifndef HEADER_MD4_H +#define HEADER_MD4_H +#endif + +#include "ct_ossl_base.h" + +#define MD4_DIGEST_LENGTH 16 +#define MD4_LONG unsigned int +#define MD4_CBLOCK 64 +#define MD4_LBLOCK (MD4_CBLOCK / 4) + +CT_OSSL_BEGIN_C + +typedef struct MD4state_st { + MD4_LONG A, B, C, D; + MD4_LONG Nl, Nh; + MD4_LONG data[MD4_LBLOCK]; + unsigned int num; +} MD4_CTX; + +int MD4_Init(MD4_CTX *c); +int MD4_Update(MD4_CTX *c, const void *data, CT_OSSL_SIZE_T len); +int MD4_Final(unsigned char *md, MD4_CTX *c); +unsigned char *MD4(const unsigned char *d, CT_OSSL_SIZE_T n, unsigned char *md); +void MD4_Transform(MD4_CTX *c, const unsigned char *b); + +CT_OSSL_END_C + +#endif diff --git a/port/thirdparty/openssl_compat/include/OpenSSL/md5.h b/port/thirdparty/openssl_compat/include/OpenSSL/md5.h new file mode 100644 index 000000000..b93214840 --- /dev/null +++ b/port/thirdparty/openssl_compat/include/OpenSSL/md5.h @@ -0,0 +1,35 @@ +/* + * OpenSSL 1.0.1/3.x compatible MD5 declarations (implemented by libcrypto). + * Same layout and guards as OpenSSL 3's . + */ +#ifndef OPENSSL_MD5_H +#define OPENSSL_MD5_H +#ifndef HEADER_MD5_H +#define HEADER_MD5_H +#endif + +#include "ct_ossl_base.h" + +#define MD5_DIGEST_LENGTH 16 +#define MD5_LONG unsigned int +#define MD5_CBLOCK 64 +#define MD5_LBLOCK (MD5_CBLOCK / 4) + +CT_OSSL_BEGIN_C + +typedef struct MD5state_st { + MD5_LONG A, B, C, D; + MD5_LONG Nl, Nh; + MD5_LONG data[MD5_LBLOCK]; + unsigned int num; +} MD5_CTX; + +int MD5_Init(MD5_CTX *c); +int MD5_Update(MD5_CTX *c, const void *data, CT_OSSL_SIZE_T len); +int MD5_Final(unsigned char *md, MD5_CTX *c); +unsigned char *MD5(const unsigned char *d, CT_OSSL_SIZE_T n, unsigned char *md); +void MD5_Transform(MD5_CTX *c, const unsigned char *b); + +CT_OSSL_END_C + +#endif diff --git a/port/thirdparty/openssl_compat/include/OpenSSL/ripemd.h b/port/thirdparty/openssl_compat/include/OpenSSL/ripemd.h new file mode 100644 index 000000000..231ac2bb3 --- /dev/null +++ b/port/thirdparty/openssl_compat/include/OpenSSL/ripemd.h @@ -0,0 +1,35 @@ +/* + * OpenSSL 1.0.1/3.x compatible RIPEMD-160 declarations (implemented by libcrypto). + * Same layout and guards as OpenSSL 3's . + */ +#ifndef OPENSSL_RIPEMD_H +#define OPENSSL_RIPEMD_H +#ifndef HEADER_RIPEMD_H +#define HEADER_RIPEMD_H +#endif + +#include "ct_ossl_base.h" + +#define RIPEMD160_DIGEST_LENGTH 20 +#define RIPEMD160_LONG unsigned int +#define RIPEMD160_CBLOCK 64 +#define RIPEMD160_LBLOCK (RIPEMD160_CBLOCK / 4) + +CT_OSSL_BEGIN_C + +typedef struct RIPEMD160state_st { + RIPEMD160_LONG A, B, C, D, E; + RIPEMD160_LONG Nl, Nh; + RIPEMD160_LONG data[RIPEMD160_LBLOCK]; + unsigned int num; +} RIPEMD160_CTX; + +int RIPEMD160_Init(RIPEMD160_CTX *c); +int RIPEMD160_Update(RIPEMD160_CTX *c, const void *data, CT_OSSL_SIZE_T len); +int RIPEMD160_Final(unsigned char *md, RIPEMD160_CTX *c); +unsigned char *RIPEMD160(const unsigned char *d, CT_OSSL_SIZE_T n, unsigned char *md); +void RIPEMD160_Transform(RIPEMD160_CTX *c, const unsigned char *b); + +CT_OSSL_END_C + +#endif diff --git a/port/thirdparty/openssl_compat/include/OpenSSL/sha.h b/port/thirdparty/openssl_compat/include/OpenSSL/sha.h new file mode 100644 index 000000000..8f571aa75 --- /dev/null +++ b/port/thirdparty/openssl_compat/include/OpenSSL/sha.h @@ -0,0 +1,100 @@ +/* + * OpenSSL 1.0.1/3.x compatible SHA declarations. SHA-1/224/256/384/512 are + * implemented by libcrypto (same layout and guards as OpenSSL 3's + * ); SHA-0 (SHA_Init/SHA_Update/SHA_Final), removed in + * OpenSSL 1.1.0, is implemented by cryptool_openssl_compat (ct_SHA0_*). + */ +#ifndef OPENSSL_SHA_H +#define OPENSSL_SHA_H +#ifndef HEADER_SHA_H +#define HEADER_SHA_H +#endif + +#include "ct_ossl_base.h" + +#define SHA_DIGEST_LENGTH 20 +#define SHA_LONG unsigned int +#define SHA_LBLOCK 16 +#define SHA_CBLOCK (SHA_LBLOCK * 4) +#define SHA_LAST_BLOCK (SHA_CBLOCK - 8) + +#define SHA256_CBLOCK (SHA_LBLOCK * 4) +#define SHA256_192_DIGEST_LENGTH 24 +#define SHA224_DIGEST_LENGTH 28 +#define SHA256_DIGEST_LENGTH 32 +#define SHA384_DIGEST_LENGTH 48 +#define SHA512_DIGEST_LENGTH 64 +#define SHA512_CBLOCK (SHA_LBLOCK * 8) +#if (defined(_WIN32) || defined(_WIN64)) && !defined(__MINGW32__) +#define SHA_LONG64 unsigned __int64 +#elif defined(__arch64__) +#define SHA_LONG64 unsigned long +#else +#define SHA_LONG64 unsigned long long +#endif + +CT_OSSL_BEGIN_C + +typedef struct SHAstate_st { + SHA_LONG h0, h1, h2, h3, h4; + SHA_LONG Nl, Nh; + SHA_LONG data[SHA_LBLOCK]; + unsigned int num; +} SHA_CTX; + +typedef struct SHA256state_st { + SHA_LONG h[8]; + SHA_LONG Nl, Nh; + SHA_LONG data[SHA_LBLOCK]; + unsigned int num, md_len; +} SHA256_CTX; + +typedef struct SHA512state_st { + SHA_LONG64 h[8]; + SHA_LONG64 Nl, Nh; + union { + SHA_LONG64 d[SHA_LBLOCK]; + unsigned char p[SHA512_CBLOCK]; + } u; + unsigned int num, md_len; +} SHA512_CTX; + +#define SHA_Init ct_SHA0_Init +#define SHA_Update ct_SHA0_Update +#define SHA_Final ct_SHA0_Final +#define SHA_Transform ct_SHA0_Transform + +int SHA_Init(SHA_CTX *c); +int SHA_Update(SHA_CTX *c, const void *data, CT_OSSL_SIZE_T len); +int SHA_Final(unsigned char *md, SHA_CTX *c); +void SHA_Transform(SHA_CTX *c, const unsigned char *data); + +int SHA1_Init(SHA_CTX *c); +int SHA1_Update(SHA_CTX *c, const void *data, CT_OSSL_SIZE_T len); +int SHA1_Final(unsigned char *md, SHA_CTX *c); +void SHA1_Transform(SHA_CTX *c, const unsigned char *data); +unsigned char *SHA1(const unsigned char *d, CT_OSSL_SIZE_T n, unsigned char *md); + +int SHA224_Init(SHA256_CTX *c); +int SHA224_Update(SHA256_CTX *c, const void *data, CT_OSSL_SIZE_T len); +int SHA224_Final(unsigned char *md, SHA256_CTX *c); +int SHA256_Init(SHA256_CTX *c); +int SHA256_Update(SHA256_CTX *c, const void *data, CT_OSSL_SIZE_T len); +int SHA256_Final(unsigned char *md, SHA256_CTX *c); +void SHA256_Transform(SHA256_CTX *c, const unsigned char *data); +unsigned char *SHA224(const unsigned char *d, CT_OSSL_SIZE_T n, unsigned char *md); +unsigned char *SHA256(const unsigned char *d, CT_OSSL_SIZE_T n, unsigned char *md); + +int SHA384_Init(SHA512_CTX *c); +int SHA384_Update(SHA512_CTX *c, const void *data, CT_OSSL_SIZE_T len); +int SHA384_Final(unsigned char *md, SHA512_CTX *c); +int SHA512_Init(SHA512_CTX *c); +int SHA512_Update(SHA512_CTX *c, const void *data, CT_OSSL_SIZE_T len); +int SHA512_Final(unsigned char *md, SHA512_CTX *c); +void SHA512_Transform(SHA512_CTX *c, const unsigned char *data); +unsigned char *SHA384(const unsigned char *d, CT_OSSL_SIZE_T n, unsigned char *md); +unsigned char *SHA512(const unsigned char *d, CT_OSSL_SIZE_T n, unsigned char *md); + +CT_OSSL_END_C + +#endif diff --git a/port/thirdparty/openssl_compat/include_bare/crypto.h b/port/thirdparty/openssl_compat/include_bare/crypto.h new file mode 100644 index 000000000..49ed76004 --- /dev/null +++ b/port/thirdparty/openssl_compat/include_bare/crypto.h @@ -0,0 +1,33 @@ +/* + * Minimal stand-in for OpenSSL 1.0.1's "crypto.h" as included bare by + * CrypTool/DlgAbout.cpp inside namespace OPENSSL { }: only the version query, + * forwarded to OpenSSL 3's OpenSSL_version(). No system #includes. + */ +#ifndef CT_OSSL_COMPAT_CRYPTO_H +#define CT_OSSL_COMPAT_CRYPTO_H + +#ifndef OPENSSL_VERSION +#define OPENSSL_VERSION 0 +#endif +#ifndef SSLEAY_VERSION +#define SSLEAY_VERSION OPENSSL_VERSION +#endif +#ifndef SSLeay_version +#define SSLeay_version OpenSSL_version +#endif +#ifndef SSLeay +#define SSLeay OpenSSL_version_num +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +const char *OpenSSL_version(int type); +unsigned long OpenSSL_version_num(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/port/thirdparty/openssl_compat/src/ct_md2.c b/port/thirdparty/openssl_compat/src/ct_md2.c new file mode 100644 index 000000000..d4ef0bea4 --- /dev/null +++ b/port/thirdparty/openssl_compat/src/ct_md2.c @@ -0,0 +1,108 @@ +/* MD2 message digest (RFC 1319) with the OpenSSL 1.0.1 MD2_* interface. */ +#include +#include + +#include "OpenSSL/md2.h" + +static const unsigned char PI_SUBST[256] = { + 41, 46, 67, 201, 162, 216, 124, 1, 61, 54, 84, 161, 236, 240, 6, 19, + 98, 167, 5, 243, 192, 199, 115, 140, 152, 147, 43, 217, 188, 76, 130, 202, + 30, 155, 87, 60, 253, 212, 224, 22, 103, 66, 111, 24, 138, 23, 229, 18, + 190, 78, 196, 214, 218, 158, 222, 73, 160, 251, 245, 142, 187, 47, 238, 122, + 169, 104, 121, 145, 21, 178, 7, 63, 148, 194, 16, 137, 11, 34, 95, 33, + 128, 127, 93, 154, 90, 144, 50, 39, 53, 62, 204, 231, 191, 247, 151, 3, + 255, 25, 48, 179, 72, 165, 181, 209, 215, 94, 146, 42, 172, 86, 170, 198, + 79, 184, 56, 210, 150, 164, 125, 182, 118, 252, 107, 226, 156, 116, 4, 241, + 69, 157, 112, 89, 100, 113, 135, 32, 134, 91, 207, 101, 230, 45, 168, 2, + 27, 96, 37, 173, 174, 176, 185, 246, 28, 70, 97, 105, 52, 64, 126, 15, + 85, 71, 163, 35, 221, 81, 175, 58, 195, 92, 249, 206, 186, 197, 234, 38, + 44, 83, 13, 110, 133, 40, 132, 9, 211, 223, 205, 244, 65, 129, 77, 82, + 106, 220, 55, 200, 108, 193, 171, 250, 36, 225, 123, 8, 12, 189, 177, 74, + 120, 136, 149, 139, 227, 99, 232, 109, 233, 203, 213, 254, 59, 0, 29, 57, + 242, 239, 183, 14, 102, 88, 208, 228, 166, 119, 114, 248, 235, 117, 75, 10, + 49, 68, 80, 180, 143, 237, 31, 26, 219, 153, 141, 51, 159, 17, 131, 20 +}; + +static void md2_block(MD2_CTX *c, const unsigned char *block) +{ + unsigned char x[48]; + unsigned int j, k, t; + unsigned int l; + + for (j = 0; j < 16; j++) { + x[j] = (unsigned char)c->state[j]; + x[16 + j] = block[j]; + x[32 + j] = (unsigned char)(c->state[j] ^ block[j]); + } + t = 0; + for (j = 0; j < 18; j++) { + for (k = 0; k < 48; k++) + t = x[k] ^= PI_SUBST[t]; + t = (t + j) & 0xff; + } + for (j = 0; j < 16; j++) + c->state[j] = x[j]; + + l = c->cksm[15]; + for (j = 0; j < 16; j++) { + c->cksm[j] ^= PI_SUBST[block[j] ^ l]; + c->cksm[j] &= 0xff; + l = c->cksm[j]; + } + memset(x, 0, sizeof(x)); +} + +const char *MD2_options(void) +{ + return "md2(int)"; +} + +int MD2_Init(MD2_CTX *c) +{ + memset(c, 0, sizeof(*c)); + return 1; +} + +int MD2_Update(MD2_CTX *c, const unsigned char *data, size_t len) +{ + if (len == 0) + return 1; + if (c->num != 0) { + size_t take = MD2_BLOCK - c->num; + if (len < take) { + memcpy(c->data + c->num, data, len); + c->num += (unsigned int)len; + return 1; + } + memcpy(c->data + c->num, data, take); + md2_block(c, c->data); + data += take; + len -= take; + c->num = 0; + } + while (len >= MD2_BLOCK) { + md2_block(c, data); + data += MD2_BLOCK; + len -= MD2_BLOCK; + } + memcpy(c->data, data, len); + c->num = (unsigned int)len; + return 1; +} + +int MD2_Final(unsigned char *md, MD2_CTX *c) +{ + unsigned char pad = (unsigned char)(MD2_BLOCK - c->num); + unsigned char cksm[MD2_BLOCK]; + unsigned int i; + + memset(c->data + c->num, pad, pad); + md2_block(c, c->data); + for (i = 0; i < MD2_BLOCK; i++) + cksm[i] = (unsigned char)c->cksm[i]; + md2_block(c, cksm); + for (i = 0; i < MD2_DIGEST_LENGTH; i++) + md[i] = (unsigned char)c->state[i]; + memset(c, 0, sizeof(*c)); + return 1; +} diff --git a/port/thirdparty/openssl_compat/src/ct_sha0.c b/port/thirdparty/openssl_compat/src/ct_sha0.c new file mode 100644 index 000000000..e2eea746c --- /dev/null +++ b/port/thirdparty/openssl_compat/src/ct_sha0.c @@ -0,0 +1,131 @@ +/* SHA-0 (FIPS 180, 1993) with the OpenSSL 1.0.1 SHA_* interface. */ +#include +#include + +#include "OpenSSL/sha.h" + +#define ROL32(x, n) ((((x) << (n)) | ((x) >> (32 - (n)))) & 0xffffffffU) + +void SHA_Transform(SHA_CTX *c, const unsigned char *p) +{ + SHA_LONG w[80]; + SHA_LONG a, b, d, e, f, k, t, cc; + int i; + + for (i = 0; i < 16; i++, p += 4) + w[i] = ((SHA_LONG)p[0] << 24) | ((SHA_LONG)p[1] << 16) | ((SHA_LONG)p[2] << 8) | (SHA_LONG)p[3]; + for (i = 16; i < 80; i++) + w[i] = w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16]; + + a = c->h0; + b = c->h1; + cc = c->h2; + d = c->h3; + e = c->h4; + for (i = 0; i < 80; i++) { + if (i < 20) { + f = (b & cc) | (~b & d); + k = 0x5a827999U; + } else if (i < 40) { + f = b ^ cc ^ d; + k = 0x6ed9eba1U; + } else if (i < 60) { + f = (b & cc) | (b & d) | (cc & d); + k = 0x8f1bbcdcU; + } else { + f = b ^ cc ^ d; + k = 0xca62c1d6U; + } + t = (ROL32(a, 5) + f + e + k + w[i]) & 0xffffffffU; + e = d; + d = cc; + cc = ROL32(b, 30); + b = a; + a = t; + } + c->h0 = (c->h0 + a) & 0xffffffffU; + c->h1 = (c->h1 + b) & 0xffffffffU; + c->h2 = (c->h2 + cc) & 0xffffffffU; + c->h3 = (c->h3 + d) & 0xffffffffU; + c->h4 = (c->h4 + e) & 0xffffffffU; +} + +int SHA_Init(SHA_CTX *c) +{ + memset(c, 0, sizeof(*c)); + c->h0 = 0x67452301U; + c->h1 = 0xefcdab89U; + c->h2 = 0x98badcfeU; + c->h3 = 0x10325476U; + c->h4 = 0xc3d2e1f0U; + return 1; +} + +int SHA_Update(SHA_CTX *c, const void *data_, size_t len) +{ + const unsigned char *data = (const unsigned char *)data_; + unsigned char *buf = (unsigned char *)c->data; + SHA_LONG lo = (c->Nl + ((SHA_LONG)len << 3)) & 0xffffffffU; + + if (lo < c->Nl) + c->Nh++; + c->Nh += (SHA_LONG)((unsigned long long)len >> 29); + c->Nl = lo; + + if (c->num != 0) { + size_t take = SHA_CBLOCK - c->num; + if (len < take) { + memcpy(buf + c->num, data, len); + c->num += (unsigned int)len; + return 1; + } + memcpy(buf + c->num, data, take); + SHA_Transform(c, buf); + data += take; + len -= take; + c->num = 0; + } + while (len >= SHA_CBLOCK) { + SHA_Transform(c, data); + data += SHA_CBLOCK; + len -= SHA_CBLOCK; + } + memcpy(buf, data, len); + c->num = (unsigned int)len; + return 1; +} + +int SHA_Final(unsigned char *md, SHA_CTX *c) +{ + unsigned char *buf = (unsigned char *)c->data; + SHA_LONG h[5]; + unsigned int n = c->num; + int i; + + buf[n++] = 0x80; + if (n > SHA_LAST_BLOCK) { + memset(buf + n, 0, SHA_CBLOCK - n); + SHA_Transform(c, buf); + n = 0; + } + memset(buf + n, 0, SHA_LAST_BLOCK - n); + for (i = 0; i < 4; i++) { + buf[SHA_LAST_BLOCK + i] = (unsigned char)(c->Nh >> (24 - 8 * i)); + buf[SHA_LAST_BLOCK + 4 + i] = (unsigned char)(c->Nl >> (24 - 8 * i)); + } + SHA_Transform(c, buf); + + h[0] = c->h0; + h[1] = c->h1; + h[2] = c->h2; + h[3] = c->h3; + h[4] = c->h4; + for (i = 0; i < 5; i++) { + md[4 * i] = (unsigned char)(h[i] >> 24); + md[4 * i + 1] = (unsigned char)(h[i] >> 16); + md[4 * i + 2] = (unsigned char)(h[i] >> 8); + md[4 * i + 3] = (unsigned char)h[i]; + } + memset(c, 0, sizeof(*c)); + return 1; +} diff --git a/port/thirdparty/tests/test_aes_candidates.cpp b/port/thirdparty/tests/test_aes_candidates.cpp new file mode 100644 index 000000000..7f583d6fa --- /dev/null +++ b/port/thirdparty/tests/test_aes_candidates.cpp @@ -0,0 +1,242 @@ +// Known-answer tests for the AES candidate ciphers through the NIST API calls +// CrypTool's CoreCryptography.cpp makes. Build with CT_AES_ORACLE (and the +// original headers) to regenerate the reference table with --dump. +#include "mars.h" +#include "RC6.h" +#include "Rijndael-api-fst.h" +#include "Serpent.h" +#include "Twofish.h" + +#ifdef long +#error "the AES wrapper headers leaked their long narrowing" +#endif + +#include +#include +#include +#include +#include + +#ifndef CT_AES_ORACLE +extern "C" const size_t ct_aes_mars_sizes[2]; +extern "C" const size_t ct_aes_rc6_sizes[2]; +extern "C" const size_t ct_aes_rijndael_sizes[2]; +extern "C" const size_t ct_aes_serpent_sizes[2]; +extern "C" const size_t ct_aes_twofish_sizes[2]; +#endif + +static int failures = 0; + +static void fail(const std::string &what) { + std::fprintf(stderr, "FAIL: %s\n", what.c_str()); + ++failures; +} + +static std::string hex(const BYTE *p, int n) { + static const char digits[] = "0123456789abcdef"; + std::string s; + for (int i = 0; i < n; ++i) { + s += digits[p[i] >> 4]; + s += digits[p[i] & 15]; + } + return s; +} + +static std::vector unhex(const char *s) { + std::vector out; + for (; s[0] && s[1]; s += 2) { + unsigned v = 0; + std::sscanf(s, "%2x", &v); + out.push_back((BYTE)v); + } + return out; +} + +template +struct Api { + const char *name; + int (*makeKey)(K *, BYTE, int, char *); + int (*cipherInit)(C *, BYTE, char *); + int (*encrypt)(C *, K *, BYTE *, int, BYTE *); + int (*decrypt)(C *, K *, BYTE *, int, BYTE *); +}; + +template +static std::string run(const Api &api, int keyBits, const char *keyHex, BYTE mode, + const char *ivHex, const std::vector &pt) { + std::string label = std::string(api.name) + " key=" + keyHex + " mode=" + + std::to_string(mode) + " iv=" + (ivHex ? ivHex : "-"); + char keyBuf[80], ivBuf[40]; + std::snprintf(keyBuf, sizeof keyBuf, "%s", keyHex); + std::snprintf(ivBuf, sizeof ivBuf, "%s", ivHex ? ivHex : "00000000000000000000000000000000"); + std::vector in(pt), ct(pt.size() + 16), back(pt.size() + 16); + int bits = (int)pt.size() * 8; + + K key; + C cipher; + std::memset(&key, 0, sizeof key); + std::memset(&cipher, 0, sizeof cipher); + if (api.makeKey(&key, DIR_ENCRYPT, keyBits, keyBuf) != TRUE) + fail(label + ": makeKey(encrypt)"); + if (api.cipherInit(&cipher, mode, ivBuf) != TRUE) + fail(label + ": cipherInit(encrypt)"); + api.encrypt(&cipher, &key, in.data(), bits, ct.data()); + + std::snprintf(keyBuf, sizeof keyBuf, "%s", keyHex); + std::snprintf(ivBuf, sizeof ivBuf, "%s", ivHex ? ivHex : "00000000000000000000000000000000"); + std::memset(&key, 0, sizeof key); + std::memset(&cipher, 0, sizeof cipher); + if (api.makeKey(&key, DIR_DECRYPT, keyBits, keyBuf) != TRUE) + fail(label + ": makeKey(decrypt)"); + if (api.cipherInit(&cipher, mode, ivBuf) != TRUE) + fail(label + ": cipherInit(decrypt)"); + api.decrypt(&cipher, &key, ct.data(), bits, back.data()); + if (std::memcmp(back.data(), pt.data(), pt.size()) != 0) + fail(label + ": decrypt(encrypt(x)) != x"); + return hex(ct.data(), (int)pt.size()); +} + +static const Api kMars = { + "MARS", makeKeyMars, cipherInitMars, blockEncryptMars, blockDecryptMars}; +static const Api kRC6 = { + "RC6", makeKeyRC6, cipherInitRC6, blockEncryptRC6, blockDecryptRC6}; +static const Api kRijndael = { + "RIJNDAEL", makeKeyRijndael, cipherInitRijndael, blockEncryptRijndael, blockDecryptRijndael}; +static const Api kSerpent = { + "SERPENT", makeKeySerpent, cipherInitSerpent, blockEncryptSerpent, blockDecryptSerpent}; +static const Api kTwofish = { + "TWOFISH", makeKeyTwofish, cipherInitTwofish, blockEncryptTwofish, blockDecryptTwofish}; + +template +static std::string runAny(int which, const Api &api, int keyBits, const char *key, + BYTE mode, const char *iv, const std::vector &pt) { + (void)which; + return run(api, keyBits, key, mode, iv, pt); +} + +static std::string dispatch(int alg, int keyBits, const char *key, BYTE mode, const char *iv, + const std::vector &pt) { + switch (alg) { + case 0: return runAny(alg, kMars, keyBits, key, mode, iv, pt); + case 1: return runAny(alg, kRC6, keyBits, key, mode, iv, pt); + case 2: return runAny(alg, kRijndael, keyBits, key, mode, iv, pt); + case 3: return runAny(alg, kSerpent, keyBits, key, mode, iv, pt); + default: return runAny(alg, kTwofish, keyBits, key, mode, iv, pt); + } +} + +static const char *const kAlgNames[] = {"MARS", "RC6", "RIJNDAEL", "SERPENT", "TWOFISH"}; + +// Published vectors (independent of the reference implementations above). +struct Kat { + int alg, keyBits; + const char *key, *pt, *ct; +}; +static const Kat kPublished[] = { + // FIPS-197 appendix C + {2, 128, "000102030405060708090a0b0c0d0e0f", "00112233445566778899aabbccddeeff", "69c4e0d86a7b0430d8cdb78070b4c55a"}, + {2, 192, "000102030405060708090a0b0c0d0e0f1011121314151617", "00112233445566778899aabbccddeeff", "dda97ca4864cdfe06eaf70a0ec0d7191"}, + {2, 256, "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", "00112233445566778899aabbccddeeff", "8ea2b7ca516745bfeafc49904b496089"}, + // Twofish paper, all-zero key and plaintext + {4, 128, "00000000000000000000000000000000", "00000000000000000000000000000000", "9f589f5cf6122c32b6bfec2f2ae8c35a"}, + {4, 192, "000000000000000000000000000000000000000000000000", "00000000000000000000000000000000", "efa71f788965bd4453f860178fc19101"}, + {4, 256, "0000000000000000000000000000000000000000000000000000000000000000", "00000000000000000000000000000000", "57ff739d4dc92c1bd7fc01700cc8216f"}, + // RC6 paper + {1, 128, "00000000000000000000000000000000", "00000000000000000000000000000000", "8fc3a53656b1f778c129df4e9848a41e"}, + {1, 128, "0123456789abcdef0112233445566778", "02132435465768798a9bacbdcedfe0f1", "524e192f4715c6231f51f6367ea43f18"}, + {1, 192, "000000000000000000000000000000000000000000000000", "00000000000000000000000000000000", "6cd61bcb190b30384e8a3f168690ae82"}, + {1, 256, "0123456789abcdef0112233445566778899aabbccddeeff01032547698badcfe", "02132435465768798a9bacbdcedfe0f1", "c8241816f0d7e48920ad16a1674e5d48"}, +}; + +// Generated with --dump from the unmodified sources built for i386 (32-bit +// long, i.e. the semantics of the original Win32 build). +struct Ref { + int alg, keyBits, keyIdx, mode, ivIdx; + const char *ct; +}; +static const Ref kReference[] = { +#include "test_aes_candidates_ref.inc" +}; + +static const char *keyFor(int idx, int bits, std::string &buf) { + static const char pattern[] = "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"; + static const char other[] = "2b7e151628aed2a6abf7158809cf4f3c762e7160f38b4da56a784d9045190cfe"; + if (idx == 0) + buf.assign(bits / 4, '0'); + else + buf.assign(idx == 1 ? pattern : other, bits / 4); + return buf.c_str(); +} + +static const char *ivFor(int idx) { + switch (idx) { + case 0: return "00000000000000000000000000000000"; + default: return "0f0e0d0c0b0a09080706050403020100"; + } +} + +static std::vector plaintext() { + std::vector pt(48); + for (int i = 0; i < 48; ++i) + pt[i] = (BYTE)(((i % 16) * 0x11 + i / 16) & 0xff); + return pt; +} + +int main(int argc, char **argv) { + bool dump = argc > 1 && std::strcmp(argv[1], "--dump") == 0; + +#ifndef CT_AES_ORACLE + if (ct_aes_mars_sizes[0] != sizeof(keyInstanceMars) || ct_aes_mars_sizes[1] != sizeof(cipherInstanceMars)) + fail("MARS struct layout differs between library and public header"); + if (ct_aes_rc6_sizes[0] != sizeof(keyInstanceRC6) || ct_aes_rc6_sizes[1] != sizeof(cipherInstanceRC6)) + fail("RC6 struct layout differs between library and public header"); + if (ct_aes_rijndael_sizes[0] != sizeof(keyInstanceRijndael) || ct_aes_rijndael_sizes[1] != sizeof(cipherInstanceRijndael)) + fail("Rijndael struct layout differs between library and public header"); + if (ct_aes_serpent_sizes[0] != sizeof(keyInstanceSerpent) || ct_aes_serpent_sizes[1] != sizeof(cipherInstanceSerpent)) + fail("Serpent struct layout differs between library and public header"); + if (ct_aes_twofish_sizes[0] != sizeof(keyInstanceTwofish) || ct_aes_twofish_sizes[1] != sizeof(cipherInstanceTwofish)) + fail("Twofish struct layout differs between library and public header"); +#endif + + for (const Kat &k : kPublished) { + std::vector pt = unhex(k.pt); + std::string ct = dispatch(k.alg, k.keyBits, k.key, MODE_ECB, nullptr, pt); + if (ct != k.ct) + fail(std::string(kAlgNames[k.alg]) + " published KAT key=" + k.key + ": got " + ct + " want " + k.ct); + } + + const std::vector pt = plaintext(); + const int modes[] = {MODE_ECB, MODE_CBC}; + size_t checked = 0; + for (int alg = 0; alg < 5; ++alg) + for (int keyBits : {128, 192, 256}) + for (int keyIdx = 0; keyIdx < 3; ++keyIdx) + for (int mode : modes) + for (int ivIdx = 0; ivIdx < (mode == MODE_CBC ? 2 : 1); ++ivIdx) { + std::string keyBuf; + const char *key = keyFor(keyIdx, keyBits, keyBuf); + std::string ct = dispatch(alg, keyBits, key, (BYTE)mode, ivFor(ivIdx), pt); + if (dump) { + std::printf("{%d, %d, %d, %d, %d, \"%s\"},\n", alg, keyBits, keyIdx, mode, ivIdx, ct.c_str()); + continue; + } + bool found = false; + for (const Ref &r : kReference) + if (r.alg == alg && r.keyBits == keyBits && r.keyIdx == keyIdx && r.mode == mode && r.ivIdx == ivIdx) { + found = true; + ++checked; + if (ct != r.ct) + fail(std::string(kAlgNames[alg]) + " reference key=" + key + " mode=" + + std::to_string(mode) + " iv=" + std::to_string(ivIdx) + ": got " + ct + " want " + r.ct); + } + if (!found) + fail(std::string(kAlgNames[alg]) + ": no reference entry"); + } + + if (dump) + return failures == 0 ? 0 : 1; + if (failures == 0) + std::printf("aes candidates: %zu reference vectors and %zu published KATs passed\n", checked, + sizeof kPublished / sizeof kPublished[0]); + return failures == 0 ? 0 : 1; +} diff --git a/port/thirdparty/tests/test_aes_candidates_ref.inc b/port/thirdparty/tests/test_aes_candidates_ref.inc new file mode 100644 index 000000000..c6829f653 --- /dev/null +++ b/port/thirdparty/tests/test_aes_candidates_ref.inc @@ -0,0 +1,135 @@ +{0, 128, 0, 1, 0, "7199b218d7554c9d4323fae452debb7a4d866911094b1ebc874dbca1df361ab55841a56270990e5193e659407cb1905a"}, +{0, 128, 0, 2, 0, "7199b218d7554c9d4323fae452debb7a1083771f67871578925063e58c1b5eae82f722ca87d94a1253041c6a02aa7672"}, +{0, 128, 0, 2, 1, "99c73ac4135edf5b8ceb2adf2ab5a34942af8f6cd0cbbba189355b642d55df68dd0ae51647429ed6a63aac143b8f6e32"}, +{0, 128, 1, 1, 0, "93eded1c5d729f449eed0ec7766922d316e7e1c328abcfe08cc13a39eabb4cb9d05b412f5bdc46703e27e7adc82d3f9a"}, +{0, 128, 1, 2, 0, "93eded1c5d729f449eed0ec7766922d3ce622dc260ee21ee226bae6301fd913d1205b1425e4e0b34c2f38a9edfc0b0d1"}, +{0, 128, 1, 2, 1, "141c3b5fd5ea76bb6f795ce878bdef42f88c5039a296f0287151f3cf1901bfc4dc6a6bcb0e2a713fd9a61583fe2d4ca5"}, +{0, 128, 2, 1, 0, "be4f1331022ceafb6c11feee9bb2ece47dd22297c3337ddfd16a3a371673140817ccb4a4143b740244d74fb1a64a0301"}, +{0, 128, 2, 2, 0, "be4f1331022ceafb6c11feee9bb2ece42064f4f97955690f9584a146cc423fbb832bf735944b4fca5c4d91ca57d98c43"}, +{0, 128, 2, 2, 1, "23cecf5ed174dd90e03a3fcbcfc60d0a714605c5789cf7f01ee81ef0a6f2bc62b42e16059cee4d4b7f83c4c6acfc9c76"}, +{0, 192, 0, 1, 0, "c3e76c5c3212190ef65d119c2fb32a7ad882d132ae2a6fa46dad862e614c248b784d0ed55395156c2241f4f621138994"}, +{0, 192, 0, 2, 0, "c3e76c5c3212190ef65d119c2fb32a7afe40ed725adf4710b9a2ddaccf1b16afd2d059c32cb454c00123a9b866792ec3"}, +{0, 192, 0, 2, 1, "e1951c99732cbdf322d03b7646787c1fc362a3d454a671561e015ec5d9b5d44d06e8b2030f190deaf8209f8dcd16740c"}, +{0, 192, 1, 1, 0, "c382106e7ab3ef5080e478db5fe04612148f894034d30b45d3629c7e6419578ea4131f073e9329a4e59e8b7c02063bff"}, +{0, 192, 1, 2, 0, "c382106e7ab3ef5080e478db5fe04612a745edc19f74591b70336e1c579eeb659ab94bf0a2dd2881e97099cf9d28c7a1"}, +{0, 192, 1, 2, 1, "9b8330d29c585ffad7a4c126019252b53b76456d7f405ae5d61f74ef221def32dd11a4a9a28c86660a65c2f5c800b4b9"}, +{0, 192, 2, 1, 0, "1fe2b2b13ca6f7ef41f978cd2452bd7013671c0ccf6f0e2fa96ba4d9de8a1c0ce00f68560deca6293256bc2d650660e6"}, +{0, 192, 2, 2, 0, "1fe2b2b13ca6f7ef41f978cd2452bd70b161fe6f37cfa3c057ab1482572fa7698ea49f0ac29ae458e30ab41539922881"}, +{0, 192, 2, 2, 1, "80d797c4b2c8cf2b2f9b64a963c30727d58bfda5724f2918e00b44477ea5d1c4c71adeb953fd4be98ea68886400eb0b3"}, +{0, 256, 0, 1, 0, "5e6cb0355f8468e4766399778a89323c23f29cb19a6da8b95705f5926f47d4b98a646a2ae41ee5c20b813fb3d71607bb"}, +{0, 256, 0, 2, 0, "5e6cb0355f8468e4766399778a89323ced344ec091c0c22379a21229ea9d2a2108ee728d65225b6dda2eabf49d81b141"}, +{0, 256, 0, 2, 1, "d8dc83de1a1b059f00c35883a4c84673471607354800bffa3d50f55827f51adc1be5e6b267ceeccfa013b1d9291c06d4"}, +{0, 256, 1, 1, 0, "7606084702df4339bf475122cbf4b31c53fc67ee343fb7a9e5f73c24e219025363365234dfa80ec86f773ec7d8a20ffa"}, +{0, 256, 1, 2, 0, "7606084702df4339bf475122cbf4b31c34e93524f55ccb77b3502e0794fedb3889474f14bd95170f2f2d41414ce141e1"}, +{0, 256, 1, 2, 1, "ed890d2d5a92edc2de92fc961cafe11cc6e8f06f043b1d1b351f0029ac5bb79455fb144ae8b67e455b62fcf5e3f08646"}, +{0, 256, 2, 1, 0, "2f918eb6cb6ef5abaf9c4ec1a252d8bf5608b4acc312745aafec20ed35c3709658ea0e59f1829bc5ad40a7d02a9355ec"}, +{0, 256, 2, 2, 0, "2f918eb6cb6ef5abaf9c4ec1a252d8bfefa3fd26c8e7f1140e20741ad78d1213e66c8668fed56d476e68d70deb7475e3"}, +{0, 256, 2, 2, 1, "a33d1269f3258439c66699b56e74c70d0923b38681dafdb3e267f430c0b24e265f89f0eec6cd7daa741bcf84a8de2a1e"}, +{1, 128, 0, 1, 0, "a66cadd6ddc3124cc08b656adafb2ef28dcf41e04fb9f054704642d087730f95c9ccd9daf870b17781cc704b449f9c95"}, +{1, 128, 0, 2, 0, "a66cadd6ddc3124cc08b656adafb2ef292e91ce78c7b8855cd6a102363b7312f1a55fcd9ecf1ed0a27cf068053e2df37"}, +{1, 128, 0, 2, 1, "3ceb8db9364409ff45f3401886be3ca5d9c7cdcf5dcee365597ed8ba583519d1cf4e7e794ee6d4dc1e2acf63e6f59c57"}, +{1, 128, 1, 1, 0, "09c26143b47b1a22ec2d9cd8b8a4e57345bdf2fc71ac91de131a1637428bcb30b7efd846f1f41577be5b5f2b276d985d"}, +{1, 128, 1, 2, 0, "09c26143b47b1a22ec2d9cd8b8a4e573d8211cc753d44527b30e1b2883bf2054ba08ccb2f4f38282f34c51ba7469349a"}, +{1, 128, 1, 2, 1, "830c25c2eab508f0632c5c139d0e8ee5c1ff5bceb454e3327365daa1c4fa6fe94bb89592e3273a771f847c649869b5e1"}, +{1, 128, 2, 1, 0, "99b49d58a5658a2bc103a36a3c02844875d1b1e5b97b427cc2d9cd21674b0f28869d8ff8e665ab625cafdb924fbefaa9"}, +{1, 128, 2, 2, 0, "99b49d58a5658a2bc103a36a3c028448e731e811d640ee3fcc82ab633097b4bf0ae39d6dc1d34bc5ded9f4dc81dddf63"}, +{1, 128, 2, 2, 1, "f7abd936d1c00e06c306da6d1ff9b6b905aae66105eabc5536079d6e225165652408ff39c947467ea8782db882e67598"}, +{1, 192, 0, 1, 0, "774e13bfd3dfe7eee0ac91f19450f83ed32542e94ac79e4e4019be89bab72dda47877e1b483d659bb344c8c2c344d46b"}, +{1, 192, 0, 2, 0, "774e13bfd3dfe7eee0ac91f19450f83ece1953a4d0a3f5f8561a4800d5dbbda255b7d5e0eabe83e2b52ab0b39d7bb76b"}, +{1, 192, 0, 2, 1, "1a0b595c14ee7bb75ff84476f2f7c85cf9864ab4c8c80e77f4fe712ed49b63430856a1e6eebaab28fe9b8b42f3517f9e"}, +{1, 192, 1, 1, 0, "37e13fb5351bd78d3e7912fdc5f80fcd0b69e0e0979704190fd06cc5557d4eb077d8c4498c384c4b322484a3b76c8942"}, +{1, 192, 1, 2, 0, "37e13fb5351bd78d3e7912fdc5f80fcd3e983ea2b5a0b69c5fc05f146e9ffd9a767f46e31e101e84122219cb421d6770"}, +{1, 192, 1, 2, 1, "89be07425819562f6e88942b6d80537d2832cbbd428157a71ec4fc43ceda34b1fd86031175cb42ba4efa247948922bb7"}, +{1, 192, 2, 1, 0, "7dcb01a770302ce1d3510addad7680d5db7b6612d60a9fb2c7c009e6eacec311ca58446d0420a00b6aa6ab75af1117ae"}, +{1, 192, 2, 2, 0, "7dcb01a770302ce1d3510addad7680d55fab253e216a387eaeec750b88af2653c6dd0848060e33d0d6029fa5e74834ed"}, +{1, 192, 2, 2, 1, "881ce8b25f524279bd932ce87e40c32cfa0710cd6f53d48a7e80221a584033560562d16c1586d5b3c4a678fbdc9e6539"}, +{1, 256, 0, 1, 0, "d3cea9ac0401858b1bbb8a1bb5cc3e7a201123559289080ef304f30aeb28b2894f239f896916c4ac6cb2417784a8f1d5"}, +{1, 256, 0, 2, 0, "d3cea9ac0401858b1bbb8a1bb5cc3e7a1539e27f8b1e66efaaaf6e420d2c96d0aa402db41dfaa8bb3b8c2fc10002c6ce"}, +{1, 256, 0, 2, 1, "1c9691e7e4bb8bc00f1947c32fa939911c7ade800c3779c5d426d91e8c44267ab91a0ad9c3217d2ea6b11681ab7e7fb1"}, +{1, 256, 1, 1, 0, "e5c82d388ed59b96ee87e3f48a6e879ee95a6d7cc749a9fba4666a493bf09544bedec6fbb93a47131ef92bf640882863"}, +{1, 256, 1, 2, 0, "e5c82d388ed59b96ee87e3f48a6e879e9d6f6a65aa34cae089709040fa21fcec69cf41364866ac8c73bfa5375ef71d9a"}, +{1, 256, 1, 2, 1, "9341edc00a385760ceb305f3351b107e7dd5a52ce73ea5b84f44167dc5641a93629a9095d33090b2955b3d2cfc417a88"}, +{1, 256, 2, 1, 0, "94bbd7384cdca8a4b5d79c8f6c9d3bdc6d908192729c77414b7352bb1a316bf3c945cbe93575a5cc4a8097f447993ded"}, +{1, 256, 2, 2, 0, "94bbd7384cdca8a4b5d79c8f6c9d3bdc39f408fd644f865d5933afda2cc4742cae291e67ca96f106ce309d42941a36c4"}, +{1, 256, 2, 2, 1, "7f66c150f568fa5f2a1c00d03a63479f7f3fbb4af144227ef521b64f41a5c9cf6d9f21dade72e7e86ff2287926e60474"}, +{2, 128, 0, 1, 0, "c8a331ff8edd3db175e1545dbefb760bf5836e6f7f388e2ab41c8a6faed0f94c5fd79929d55d616998dee86026b4f802"}, +{2, 128, 0, 2, 0, "c8a331ff8edd3db175e1545dbefb760b45c2286b7ba92776bc1177203616314d06fa37c469aeab37ef943e78b59784e2"}, +{2, 128, 0, 2, 1, "e6d598af6b41465134d30303171d1780949c77942ff846279f87478249eca090336b181b89e983c3022fe075064439ad"}, +{2, 128, 1, 1, 0, "69c4e0d86a7b0430d8cdb78070b4c55a000648a951ca51d04fd755fe200e700f12c4f1789bf09ef5c791c5437f37f98b"}, +{2, 128, 1, 2, 0, "69c4e0d86a7b0430d8cdb78070b4c55af0fcd0fafc2c2b0e318b3ffcc2682d39607b35293a46d5b3eb4dc2ba58d98f07"}, +{2, 128, 1, 2, 1, "16628846f7334843bc7321cc79661680adec66f5855d645098db263f733bc28ae50995ab659f20a1917a09ba29f88270"}, +{2, 128, 2, 1, 0, "8df4e9aac5c7573a27d8d055d6e4d64b3f76cf6551e0b115858ab0170e2d3459e9ad6771522a6a391c6d5c5c95dc8bfc"}, +{2, 128, 2, 2, 0, "8df4e9aac5c7573a27d8d055d6e4d64b3fbbaea608a6c77265b33d3b7c954ceba016d976c65b9c32317f0f78fa3374d9"}, +{2, 128, 2, 2, 1, "15440d3c83995b30aa8340cb0d1433f1fdc484cf0c7a4d2115b4196977dd89b638071b7f26bd088d9ed2c2d2543b7c57"}, +{2, 192, 0, 1, 0, "4e95298c89bc57c77d2d39912f323996ead6ae5d7532d4ac32c3e7987308d3d37077ff0b72bb1689eafb176da8d96a93"}, +{2, 192, 0, 2, 0, "4e95298c89bc57c77d2d39912f3239964dd263baeac4cdede1b281c5c2c98b74c4166a512f339a9178316003046dca74"}, +{2, 192, 0, 2, 1, "ce772c17066f77c04c28fef40b4f84eb3195d0c10ea28941e9d7d444897bf089aea4d0c9de6c8f0b3fdb2514099a069e"}, +{2, 192, 1, 1, 0, "dda97ca4864cdfe06eaf70a0ec0d7191beb545f8c56b5576e377c9f3c2b8ed539219541e668bd56fe24a058800599b43"}, +{2, 192, 1, 2, 0, "dda97ca4864cdfe06eaf70a0ec0d719150de3e940180c3a5b430ae10ed2bb51c4815f551359b6d5e83c2c05e870c716d"}, +{2, 192, 1, 2, 1, "6bbbc6dfcca3de831587b4208f20da1c06829a0fdb08b5521c39e463f8aa5c05282a6f5f1635873e658a25a318d53d84"}, +{2, 192, 2, 1, 0, "bf8d8b2f6d7d82729bbeb8ad41f1de7667027354a95c11a48b9f8f9d5a2506df4ac231038529eb317c50d1a3415d35c5"}, +{2, 192, 2, 2, 0, "bf8d8b2f6d7d82729bbeb8ad41f1de76347dcb1df6fcd3bd65d6336dad65b9cda7ced9c0db901ddf7c1bacadb8f150f3"}, +{2, 192, 2, 2, 1, "6e3b72c607dc92803c781263a56a718e2142045d3e6a5c9b16ee31d3aa923651597a718e512c587b2a51a1720ef0e3f7"}, +{2, 256, 0, 1, 0, "1c060f4c9e7ea8d6ca961a2d64c05c18bb78b28d030105a7bcaca9772ee49b1a70f7af2b633fa2096493a02582fa1b77"}, +{2, 256, 0, 2, 0, "1c060f4c9e7ea8d6ca961a2d64c05c18bc6aca1b4526465e9479ca757ea5cf6e7d043349815bd9cbbbcd397325aaeb3b"}, +{2, 256, 0, 2, 1, "595c6d7b3042a47f04006fee4c8c9c8a37309dc59b431344e9f38831b0ba2614fe5e9648ea5c452bd841d7f4d7fb6054"}, +{2, 256, 1, 1, 0, "8ea2b7ca516745bfeafc49904b496089acac7b5f2614c2a0e6ea9d1339f4031cf48a8a6159e6b7b94cbac9176ecce557"}, +{2, 256, 1, 2, 0, "8ea2b7ca516745bfeafc49904b4960890e4795e4d482e8321ace5c9d027c2f878f43ac59c5c2f036a807e1a172b6590b"}, +{2, 256, 1, 2, 1, "5619ba42f9b3e67661562c2dd0a5eddcbc13c078a84e401d93f7bbd43392d24e096ed6b4dcf2582d10f66197524b2db1"}, +{2, 256, 2, 1, 0, "ba278e3ebc953c3d4f97fdcd888a4e3ad20aca8328c28141d7a6688d98c895c7ba3e7bad833c31d2c254d56addf5b381"}, +{2, 256, 2, 2, 0, "ba278e3ebc953c3d4f97fdcd888a4e3a5ece20406d09195d5c7d02033f5022adea90d333352f70b56aad801646f591bd"}, +{2, 256, 2, 2, 1, "b5f1c946bc883f840ce8f6981621ccda7f57f08b9d6654de68fe092d4ead8ae4e7f86b101e266a051c36d68ccea5fd66"}, +{3, 128, 0, 1, 0, "a0ebe0b0021003ba26f95f1affca8ecfa69c216e11f57d58644302c05ca00257fd6eba1591f1f67702ffeea8dfd4f33e"}, +{3, 128, 0, 2, 0, "a0ebe0b0021003ba26f95f1affca8ecfc2332262eb614d44dd70eef348126971cdbe7a689af34f57f1790dcaa2ea9dc2"}, +{3, 128, 0, 2, 1, "987ca7c669ab26b34dbb8cce21b5c55a3487db10370b37b69e2455f71a6ff4fdda4555ab8422d28326a6c2195a65ec42"}, +{3, 128, 1, 1, 0, "558ecdc6e695b513e8f9cecc19787a051cd52edfd7c8ecb1c50dc0a4275f8714b4dccfb4278ca3f36338c996bab3b0af"}, +{3, 128, 1, 2, 0, "558ecdc6e695b513e8f9cecc19787a05c26ae478275f1833440b500467c9158988f1c834dacbbc51c9d15518992758ac"}, +{3, 128, 1, 2, 1, "c587ed2e181495dbe3272452414d0ca5d1210a4560c5e5b1f3ba9e804ef66a39b4817f9abe862e6ebf38e7a443ab7c1e"}, +{3, 128, 2, 1, 0, "a4a58521df02ccb23e2cabc9d08c84f59905241b47bd0fda96536933beea9cd42dc8f75fef7fbed4ae4efd7775fa41ad"}, +{3, 128, 2, 2, 0, "a4a58521df02ccb23e2cabc9d08c84f58c8fa8e31ef6d126a05b2f098c81ec872c72a1e53154fdf47798c2c88d77fbf2"}, +{3, 128, 2, 2, 1, "c7ec637eec09d7ec95c26fa1afe571de6ef387ad4720017536556b787dabe7291541c0080b02d9e5d98388c8db699d22"}, +{3, 192, 0, 1, 0, "e532aebb2793497afc40efb832f4d14fd1437293478201f524a69021db27f493de43a77365b7f99e493005ea83f3f074"}, +{3, 192, 0, 2, 0, "e532aebb2793497afc40efb832f4d14f6b9c66c74e59e8720c108f4c08c13eb9a8ff5ade351d74d171a7d4b4080d739d"}, +{3, 192, 0, 2, 1, "889bb1ed62eb07780c53cd01034f957ac4644135bcafd6708349477eaa93d2541384ab80c1b077b04554f33423c9e496"}, +{3, 192, 1, 1, 0, "ce30c25d8a92c0f26700938e79f50061962facf5367b20637fb0b82184556a92163980b724b087092ebeaea8cad45105"}, +{3, 192, 1, 2, 0, "ce30c25d8a92c0f26700938e79f50061c29253fd5bf0821c732d405e8cec29574c9d29682e1dd7d3cecdb339018c7bd8"}, +{3, 192, 1, 2, 1, "4bce85d457dec628a3462d19335fc55ae9ec5c89a34a0b9b8553ebf48ae223cd62a3cb149b76a26eee56293f57037d25"}, +{3, 192, 2, 1, 0, "9ad0a6190e3594a0309a77b586d88260bdff8a31db11f9b6d737709932e62fd83695c0b659701f2d807876915e268b2b"}, +{3, 192, 2, 2, 0, "9ad0a6190e3594a0309a77b586d8826044c4b9f399881bd854c794e95e1703d4c1ad150f99b1e35e6b8f7d2e765ee51a"}, +{3, 192, 2, 2, 1, "7a73983ce00f9e27c8aea0d29466b72b6e830ab4423999f0b060d75d7f419a6be63a8a0629adbae64abb22350c2700a9"}, +{3, 256, 0, 1, 0, "885113854d880f7f63c55203be74ab111e8d2333916b23f78e90f2f1d9ddaafd8d81cf8af04ff6235b406b6e16fd5a84"}, +{3, 256, 0, 2, 0, "885113854d880f7f63c55203be74ab1141f651283666b742c6a927014fa463a1377baf9c1bd37698550aa60e5ff4785c"}, +{3, 256, 0, 2, 1, "e7d1a3fcc9e092d62e32251bb873ac0ab6139091584c043d79830cd23a9342b84365755b3b23d3d5aeca7834541d3c84"}, +{3, 256, 1, 1, 0, "9234ed5ee16f79702bcda836ccee6467581e8d1bcce3562a821143efe568c5562cde3d2ff2ae31c4b364af2956e3c297"}, +{3, 256, 1, 2, 0, "9234ed5ee16f79702bcda836ccee646722d9953597a850d7d29274419e34fed8094080c0017b581dee4367a53ee12f30"}, +{3, 256, 1, 2, 1, "c1cc23e1c3c932b284c2b5178649084a54b798307582b71f9c75e78745833fef15d2d5c24a758b94c228d3d893d1c1e7"}, +{3, 256, 2, 1, 0, "0d5eb3ced45e39e4a8513b807ece5e46d313a366b4cedda126bc609c9a80b076b52a859dbd39435a58819680fe5806f8"}, +{3, 256, 2, 2, 0, "0d5eb3ced45e39e4a8513b807ece5e46547543b057111485f1f4370b3d4068dd7223a9ca6473e2fbedb838818e80ba8d"}, +{3, 256, 2, 2, 1, "f66b7dfb4ca74e74ea504e3d8c48b745cac32103e2dd56161f5d1c903d15082627794e335f2fbb8c889691e5fbd7afcc"}, +{4, 128, 0, 1, 0, "1242fae0702a08d0903708274a6831d738e595ce6f8384e0fd1eb8d1ad9e0f871b01f4c6d7377ef927cec0cb4b35d05b"}, +{4, 128, 0, 2, 0, "1242fae0702a08d0903708274a6831d76e2e8e7110c307ef274d18d00d166c5e3bdaa5bc2fc872d620fa0213794d9888"}, +{4, 128, 0, 2, 1, "85d9bba9db455f8b595fde9cea1da516481b9c2fca05763a9bdc33e4dde6e403673ff5645a08d7538785742eb3d50f62"}, +{4, 128, 1, 1, 0, "df8451d26e0504bc19b0a93b049e32037859fdb49198a016ee32a36e42439ff5cd9099f11757929bd54c2680637e49fa"}, +{4, 128, 1, 2, 0, "df8451d26e0504bc19b0a93b049e3203448156171e31596fc814c715b087271351e95d20a9988fe87b48440af32dcb55"}, +{4, 128, 1, 2, 1, "712fa33a40d43cc016f42fd72beb5ea9fc34c10a66b9bc8e85c57a5b87da30f625e661e660a98fc7ce705d9f06a3595e"}, +{4, 128, 2, 1, 0, "90f649d14f6f06c9d9160741813102b0bb181dd35c2ebd765124a39fd05a5f70a4a06e4f5d93bc82d712dafab8d6ea80"}, +{4, 128, 2, 2, 0, "90f649d14f6f06c9d9160741813102b04ad3d9a42a0fa1b4cb0f1150446eab87cef255fd21df6fe05e5ae55559f13d34"}, +{4, 128, 2, 2, 1, "07140dac1c3f5e7ea4cb95c35f2f1b565860d144a796f1b0d21f69126d507e357ad901011dd8b2c8c90578298eb7014d"}, +{4, 192, 0, 1, 0, "c8ca75770da7d806a22cffdb42cf8c74e5db5a633d54556827d111c39913aba33a05a625713e354e5bc0ffc1a77e6dc0"}, +{4, 192, 0, 2, 0, "c8ca75770da7d806a22cffdb42cf8c742bd883094858799022d405e8a4a261d804649fbef61d9cf6aad78b7c2d23a988"}, +{4, 192, 0, 2, 1, "1355610c89436c92f8e3e7080233856cf8044c6f5f755971b65b45fced0ff6dc51600af79397061054458677b47e6d12"}, +{4, 192, 1, 1, 0, "4afc654ce45e2e65d6716b8c6057c4f273113158a54d9cd6906f4a7c3a73c345e963e0b6e0dbd75c0abbd692ebbb655a"}, +{4, 192, 1, 2, 0, "4afc654ce45e2e65d6716b8c6057c4f2145db0606241f9a200997cce93839b2a5503269f20d0c584cc8475455e12787b"}, +{4, 192, 1, 2, 1, "e6307152f4e819360b4ebb0846f7bc2c3990356331a1119442c43323ef5c069596cb90356fc51fb24f92c935a980df31"}, +{4, 192, 2, 1, 0, "9e6f8b421425c38e33b20f6577539f842226d901626ace3ea1e63b7e19ffff66a1413b1c186f41c87df300255beb719c"}, +{4, 192, 2, 2, 0, "9e6f8b421425c38e33b20f6577539f84717aeddad8471de0043bd421619a8efea22508cf6a24b92376bf3618d4ff6148"}, +{4, 192, 2, 2, 1, "137e0d63a8626bd3c3eae9a58776e2ce3432b160b0f4e5cf53a94dc279f8f0a258878ed06332622f279a39b63e7ee38a"}, +{4, 256, 0, 1, 0, "368299949395960232ddf30029ef81b782977794ca94e480b3ddfa353aed6d134c5cd5e39752b4aadc6134709f09b13c"}, +{4, 256, 0, 2, 0, "368299949395960232ddf30029ef81b7887f757785c9f93796d66e45e52e3fa66021f3aec038ba9a14d03c04797f3db2"}, +{4, 256, 0, 2, 1, "419e4caf5764148b0d20c08da31b794953c4de670c4173a08b4ad45f799f07ecc9bd557e393807d1a7aade0ba76f1198"}, +{4, 256, 1, 1, 0, "b7b5fb57ec446a11cbb7e6292342537bfe179d2ce1129b30f50bf3252768b320f3696fc71a974fc784e54dcd804d4f1e"}, +{4, 256, 1, 2, 0, "b7b5fb57ec446a11cbb7e6292342537b6394fcf46308a311d5b15c3d5ae4ece8d1cb4daa2ed3b9c648bb2b81b1485f97"}, +{4, 256, 1, 2, 1, "8081a241a136739c003a34cd89e6d691923fbb551167755ad09a9f91e69a7d71028aea5c1458184bf299a6edada818e4"}, +{4, 256, 2, 1, 0, "3d14842ba23845ffb3dd2e8b275ea4335d4466f7344a810cd43acb85703da70d4b10749a61de31167269e78b51522e26"}, +{4, 256, 2, 2, 0, "3d14842ba23845ffb3dd2e8b275ea4338060c0f22958e3eed0d636649ae33207b05fb492bcda258e5a1152faa7c31e28"}, +{4, 256, 2, 2, 1, "9e88e57cda70899d4d922d49f0452d12f89e39bda9c4cb9fd8c8c8aaa18abd4d1b383a16b14736d728d5168d0924cd60"}, diff --git a/port/thirdparty/tests/test_analyse.cpp b/port/thirdparty/tests/test_analyse.cpp new file mode 100644 index 000000000..3a98d506f --- /dev/null +++ b/port/thirdparty/tests/test_analyse.cpp @@ -0,0 +1,88 @@ +#include "libanalyse/la_string.h" +#include "libanalyse/analyse.h" +#include "libanalyse/Des.h" + +#include +#include +#include +#include +#include + +static int failures = 0; + +#define CHECK(cond) \ + do { \ + if (!(cond)) { \ + std::fprintf(stderr, "%s:%d: CHECK failed: %s\n", __FILE__, __LINE__, \ + #cond); \ + ++failures; \ + } \ + } while (0) + +static std::string written(const SymbolArray &a) { + std::ostringstream os; + a.Write(os); + return os.str(); +} + +int main() { + String s("Krypto"); + s += "Tool"; + CHECK(s.length() == 10); + CHECK(std::strcmp(upcase(s).chars(), "KRYPTOTOOL") == 0); + CHECK(s.index("Tool") == 6); + CHECK(std::strcmp(common_prefix(s, String("Kryptologie")).chars(), "Krypto") == 0); + + AppConverter conv; + char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + conv.SetAlphabet(alphabet, 1); + CHECK(conv.IsInAlphabet('a') && conv.IsInAlphabet('Z') && !conv.IsInAlphabet('1')); + + SymbolArray text(conv); + text.ReadString("Hello, World!"); + CHECK(text.GetSize() == 10); + CHECK(written(text) == "HELLOWORLD"); + + SymbolArray key(conv); + key.ReadString("D"); + text += key; + CHECK(written(text) == "KHOORZRUOG"); + text -= key; + CHECK(written(text) == "HELLOWORLD"); + + SymbolArray vkey(conv); + vkey.ReadString("KEY"); + text += vkey; + CHECK(written(text) == "RIJVSUYVJN"); + text -= vkey; + CHECK(written(text) == "HELLOWORLD"); + + SymbolArray every2 = text.Extract(0, 2); + CHECK(written(every2) == "HLOOL"); + + SymbolArray sample(conv); + sample.ReadString("ABABABABABABABABABAB"); + NGram ng(sample); + CHECK(std::fabs(ng.Entropie() - 1.0) < 1e-9); + CHECK(std::fabs(ng.Koinzidenz() - 0.5) < 0.05); + + std::ostringstream shown; + { + OStream out(shown); + ng.Show(out << OStream::Title(0) << OStream::Description(0) << OStream::Summary(0)); + } + CHECK(!shown.str().empty()); + + DES des(0x13345779UL, 0x9BBCDFF1UL); + SimpleArray block; + block[0] = 0x01234567UL; + block[1] = 0x89ABCDEFUL; + des(block, Cipher::Encrypt); + CHECK(block[0] == 0x85E81354UL && block[1] == 0x0F0AB405UL); + des(block, Cipher::Decrypt); + CHECK(block[0] == 0x01234567UL && block[1] == 0x89ABCDEFUL); + + if (failures == 0) + std::printf("libanalyse: all checks passed\n"); + return failures == 0 ? 0 : 1; +} diff --git a/port/thirdparty/tests/test_apfloat.cpp b/port/thirdparty/tests/test_apfloat.cpp new file mode 100644 index 000000000..3f1867a73 --- /dev/null +++ b/port/thirdparty/tests/test_apfloat.cpp @@ -0,0 +1,129 @@ +// Smoke test for apfloat_compat, following CrypTool's DlgComputeMersenneNumbers usage. +#include "ap.h" +#include "apint.h" + +#include +#include +#include +#include +#include + +static int failures = 0; + +#define CHECK(cond) \ + do { \ + if (!(cond)) { \ + std::fprintf(stderr, "%s:%d: CHECK failed: %s\n", __FILE__, __LINE__, \ + #cond); \ + ++failures; \ + } \ + } while (0) + +struct MersenneNumberComputationParameters { + apint base; + unsigned long exponent; + std::string result; +} params; + +static unsigned computeMersenneNumber(void *_parameters) { + MersenneNumberComputationParameters *parameters = (MersenneNumberComputationParameters *)(_parameters); + apbase(10); + apint base = parameters->base; + unsigned long exp = parameters->exponent; + apint r = base; + int b2pow = 0; + if (!exp) return 1; + while (!(exp & 1)) { + b2pow++; + exp >>= 1; + } + while (exp >>= 1) { + base *= base; + if (exp & 1) r *= base; + } + while (b2pow--) { + r *= r; + } + apint result = r - 1; + std::ostringstream buffer; + buffer << result; + parameters->result = buffer.str(); + return 0; +} + +static std::string mersenne(const char *base, unsigned long exponent) { + params.base = (char *)base; + params.exponent = strtoul(std::to_string(exponent).c_str(), NULL, 10); + params.result = ""; + std::thread worker(computeMersenneNumber, (void *)¶ms); + worker.join(); + return params.result; +} + +static std::string str(const apint &x) { + std::ostringstream os; + os << x; + return os.str(); +} + +int main() { + CHECK(apinit()); + + CHECK(mersenne("2", 127) == "170141183460469231731687303715884105727"); + CHECK(mersenne("2", 10) == "1023"); + CHECK(mersenne("3", 4) == "80"); + CHECK(mersenne("10", 1) == "9"); + std::string m521 = mersenne("2", 521); + CHECK(m521.size() == 157); + CHECK(m521.substr(0, 10) == "6864797660" && m521.substr(147) == "1115057151"); + std::string m44497 = mersenne("2", 44497); + CHECK(m44497.size() == 13395); + CHECK(m44497.substr(0, 12) == "854509824303" && m44497.substr(13395 - 12) == "961011228671"); + + apint a = 1; + a <<= 127; + a -= 1; + apbase(16); + CHECK(str(a) == "7fffffffffffffffffffffffffffffff"); + apint h = (char *)"ff"; + CHECK(h == 255); + apbase(2); + CHECK(str(apint(10)) == "1010"); + apbase(10); + + CHECK(str(apint()) == "0"); + CHECK(str(-apint(42)) == "-42"); + CHECK(str(apint("1.5e3")) == "1500"); + CHECK(str(apint("-1.5")) == "-2"); + CHECK(str(apint(-2.5)) == "-3"); + CHECK(str(apint(" +123456789012345678901234567890")) == "123456789012345678901234567890"); + CHECK(str(apint(-7) / 2) == "-3"); + CHECK(str(apint(-7) % 2) == "-1"); + CHECK(str(100 - apint(1)) == "99"); + CHECK(str(apint(1LL << 40) * 3u) == "3298534883328"); + CHECK(str(apint(-(1LL << 62))) == "-4611686018427387904"); + CHECK(str(apint(~0ULL)) == "18446744073709551615"); + CHECK(apint(5) > 4 && 4 < apint(5) && apint(5) >= 5.0 && apint(5) != 4.5); + CHECK(pow(apint(3), 5) == 243); + CHECK(powmod(apint(3), apint(200), apint(1000)) == 1); + CHECK(gcd(apint(84), apint(36)) == 12 && lcm(apint(4), apint(6)) == 12); + apdiv_t qr = div(apint(17), apint(5)); + CHECK(qr.quot == 3 && qr.rem == 2); + CHECK(abs(apint(-9)) == 9); + apint i = 5; + CHECK((i++) == 5 && i == 6 && (--i) == 5); + + std::istringstream in("31415926535897932384626"); + apint parsed; + in >> parsed; + CHECK(str(parsed) == "31415926535897932384626"); + + apdeinit(); + + if (failures) { + std::fprintf(stderr, "%d check(s) failed\n", failures); + return 1; + } + std::printf("test_apfloat: OK (2^127-1 = %s)\n", mersenne("2", 127).c_str()); + return 0; +} diff --git a/port/thirdparty/tests/test_cracklib.cpp b/port/thirdparty/tests/test_cracklib.cpp new file mode 100644 index 000000000..bc4445d81 --- /dev/null +++ b/port/thirdparty/tests/test_cracklib.cpp @@ -0,0 +1,115 @@ +// Smoke test for cryptool_cracklib using CrypTool's own declarations (passwordchecker.h). +#include +#include +#include +#include + +#define STRINGSIZE 1024 +#define NUMWORDS 16 +#define MAXWORDLEN 32 + +struct pi_header { + unsigned long int pih_magic; + unsigned long int pih_numwords; + unsigned short int pih_blocklen; + unsigned short int pih_pad; +}; + +struct PWDICT { + FILE *ifp; + FILE *dfp; + FILE *wfp; + unsigned long int flags; + unsigned long int hwms[256]; + struct pi_header header; + int count; + char data[NUMWORDS][MAXWORDLEN]; +}; + +#define PW_WORDS(x) ((x)->header.pih_numwords) + +extern "C" { + unsigned long int FindPW(PWDICT *pwp, char *password); + int PMatch(char *pattern, char *password); + char *Trim(char *password); + int PWClose(PWDICT *pwp); + PWDICT *PWOpen(char *prefix, char *mode); + + void ct_cracklib_layout(size_t *size, size_t *numwords_offset, size_t *count_offset); + char *ct_cracklib_getpw(PWDICT *pwp, unsigned long number); + char *ct_cracklib_fascistcheck(char *password, char *path); +} + +static int failures = 0; + +#define CHECK(cond) \ + do { \ + if (!(cond)) { \ + std::fprintf(stderr, "%s:%d: CHECK failed: %s\n", __FILE__, __LINE__, \ + #cond); \ + ++failures; \ + } \ + } while (0) + +static bool inDictionary(PWDICT *pwp, const char *word) { + char buf[STRINGSIZE]; + std::snprintf(buf, sizeof buf, "%s", word); + return FindPW(pwp, buf) != PW_WORDS(pwp); +} + +int main() { + size_t libSize = 0, libNumwordsOffset = 0, libCountOffset = 0; + ct_cracklib_layout(&libSize, &libNumwordsOffset, &libCountOffset); + CHECK(libSize == sizeof(PWDICT)); + CHECK(libNumwordsOffset == offsetof(PWDICT, header.pih_numwords)); + CHECK(libCountOffset == offsetof(PWDICT, count)); + + char path[STRINGSIZE]; + std::snprintf(path, sizeof path, "%s", CT_CRACKLIB_DICT); + char mode[] = "r"; + PWDICT *pwp = PWOpen(path, mode); + CHECK(pwp != nullptr); + if (!pwp) return 1; + + CHECK(pwp->header.pih_magic == 0x70775631UL); + CHECK(PW_WORDS(pwp) == 1648594UL); + CHECK(pwp->header.pih_blocklen == NUMWORDS); + CHECK(pwp->flags & 0x0004); + + CHECK(inDictionary(pwp, "password")); + CHECK(inDictionary(pwp, "dragon")); + CHECK(inDictionary(pwp, "letmein")); + CHECK(!inDictionary(pwp, "xq7#Zr!pW2vK")); + + char word[] = "dragon"; + unsigned long idx = FindPW(pwp, word); + CHECK(idx < PW_WORDS(pwp)); + const char *back = ct_cracklib_getpw(pwp, idx); + CHECK(back != nullptr && std::strcmp(back, "dragon") == 0); + const char *last = ct_cracklib_getpw(pwp, PW_WORDS(pwp) - 1); + CHECK(last != nullptr && last[0] != '\0'); + + char pattern[] = "aadddddda"; + char ni[] = "ab123456c"; + CHECK(PMatch(pattern, ni) == 1); + char padded[] = " word "; + Trim(padded); + CHECK(std::strcmp(padded, " word") == 0); + + CHECK(PWClose(pwp) == 0); + + char weak[] = "password"; + char strong[] = "xq7#Zr!pW2vK"; + const char *weakReason = ct_cracklib_fascistcheck(weak, path); + const char *strongReason = ct_cracklib_fascistcheck(strong, path); + CHECK(weakReason != nullptr); + CHECK(strongReason == nullptr); + + if (failures) { + std::fprintf(stderr, "%d check(s) failed\n", failures); + return 1; + } + std::printf("test_cracklib: OK (%lu words, \"password\" -> %s)\n", 1648594UL, + weakReason ? weakReason : "(null)"); + return 0; +} diff --git a/port/thirdparty/tests/test_cracklib_layout.c b/port/thirdparty/tests/test_cracklib_layout.c new file mode 100644 index 000000000..9bda37dc2 --- /dev/null +++ b/port/thirdparty/tests/test_cracklib_layout.c @@ -0,0 +1,20 @@ +/* Reports the PWDICT layout as seen by the cracklib sources. */ +#include +#include CT_CRACKLIB_PACKER_H + +void ct_cracklib_layout(size_t *size, size_t *numwords_offset, size_t *count_offset) +{ + *size = sizeof(PWDICT); + *numwords_offset = offsetof(PWDICT, header.pih_numwords); + *count_offset = offsetof(PWDICT, count); +} + +char *ct_cracklib_getpw(PWDICT *pwp, unsigned long number) +{ + return GetPW(pwp, (int32) number); +} + +char *ct_cracklib_fascistcheck(char *password, char *path) +{ + return FascistCheck(password, path); +} diff --git a/port/thirdparty/tests/test_cvact.cpp b/port/thirdparty/tests/test_cvact.cpp new file mode 100644 index 000000000..8098eb46e --- /dev/null +++ b/port/thirdparty/tests/test_cvact.cpp @@ -0,0 +1,435 @@ +// Smoke test for cvact_compat: mirrors the call sequence of CrypTool/ECIESMain.cpp. +#include "actInit.h" +#include "actAlgorithm.h" +#include "actTools.h" +#include "actDate.h" +#include "actIRNGAlg.h" +#include "actException.h" +#include "actKey.h" + +#include +#include +#include +#include +#include +#include +#include + +namespace act +{ + namespace detail + { + Blob X963Kdf(const char *digest, const Blob &secret, const Blob &sharedInfo, size_t len); + bool CurveFallbackMatches(int id); + } +} + +static int g_failures = 0; + +#define CHECK(cond) \ + do { \ + if (!(cond)) { \ + std::fprintf(stderr, "%s:%d: CHECK failed: %s\n", __FILE__, __LINE__, #cond); \ + ++g_failures; \ + } \ + } while (0) + +static std::string Hex(const act::Blob &b) +{ + std::string s(b.size() * 2 + 1, '\0'); + act::blob2hex(b, &s[0]); + s.resize(b.size() * 2); + return s; +} + +// Mimics the strings CrypTool obtains from libec: "0X" with the X lowered to x. +static std::string CrypToolNumber(const act::Blob &twosComplement) +{ + std::string h = Hex(twosComplement); + size_t nz = h.find_first_not_of('0'); + h = nz == std::string::npos ? "0" : h.substr(nz); + for (size_t i = 0; i < h.size(); ++i) + h[i] = static_cast(std::toupper(static_cast(h[i]))); + return "0x" + h; +} + +static int importKey(act::Key &key, int curve, const char *x, const char *y, const char *s) +{ + try + { + key.SetParam(act::CURVE, curve); + if (std::string(s) != "") + key.SetParam(act::PRIVATEKEY, s); + else + { + key.SetParam(act::PUBLIC_X, x); + key.SetParam(act::PUBLIC_Y, y); + } + return 0; + } + catch (act::InvalidKeyException &) + { + return 2; + } + catch (act::Exception &) + { + return 2; + } +} + +static bool encryptSessionKey(const act::Key &pubRecECIES, const act::Key &privSendECIES, + const act::Blob &sessionKey, act::Blob &encryptedSessionKey) +{ + try + { + act::Blob publicBlob; + pubRecECIES.Export(publicBlob, act::PUBLIC); + act::Algorithm Encrypt(privSendECIES, act::ENCRYPT, publicBlob); + Encrypt.Write(sessionKey); + Encrypt.Finalize(); + Encrypt.Read(encryptedSessionKey); + return true; + } + catch (act::Exception &e) + { + std::cerr << "Exception: " << typeid(e).name() << " what: " << e.what() << " where: " << e.where() << "\n"; + return false; + } +} + +static int decryptSessionKey(const act::Key &privRecECIES, const act::Blob &encryptedSessionKey, act::Blob &sessionKey) +{ + try + { + act::Algorithm Decrypt(privRecECIES, act::DECRYPT); + Decrypt.Write(encryptedSessionKey); + Decrypt.Finalize(); + Decrypt.Read(sessionKey); + return 0; + } + catch (act::Exception &) + { + return 42; + } +} + +static bool encryptData(const act::Blob &plaintext, act::Blob &sessionKey, act::Blob &ciphertext) +{ + try + { + act::Key blockCipher("BlockCipher"); + blockCipher.SetParam(act::CIPHER, "AES"); + blockCipher.SetParam(act::BCMODE, "CBC"); + blockCipher.SetParam(act::KEYSIZE, 32); + act::Blob salt; + act::Algorithm saltGen(act::CreateBBS()); + saltGen.Write(act::Date()); + saltGen.Read(salt, 256); + + blockCipher.Derive(plaintext, salt); + salt.clear(); + blockCipher.GetParam(act::RAWKEY, sessionKey); + + act::Algorithm encrypt(blockCipher, act::ENCRYPT); + encrypt.Write(plaintext); + encrypt.Finalize(); + encrypt.Read(ciphertext); + return true; + } + catch (act::Exception &e) + { + std::cerr << "Exception: " << typeid(e).name() << " what: " << e.what() << " where: " << e.where() << "\n"; + return false; + } +} + +static int decryptData(const act::Blob &ciphertext, const act::Blob &sessionKey, act::Blob &plaintext) +{ + try + { + act::Key blockCipher("BlockCipher"); + blockCipher.SetParam(act::CIPHER, "AES"); + blockCipher.SetParam(act::BCMODE, "CBC"); + blockCipher.SetParam(act::RAWKEY, sessionKey); + + act::Algorithm decrypt(blockCipher, act::DECRYPT); + decrypt.Write(ciphertext); + decrypt.Finalize(); + decrypt.Read(plaintext); + return 0; + } + catch (act::Exception &) + { + return 5; + } +} + +// Same Blob manipulations as writeEncFile()/readEncFile() in ECIESMain.cpp. +static act::Blob writeEncFile(const act::Blob &encryptedSessionKey, const act::Blob &ciphertext, size_t &keyStart) +{ + std::ostringstream header; + header << "Receiver:Bob, Test|Curve ID:EC-prime256v1|Length of the ECIES encrypted AES session key:" + << encryptedSessionKey.size() << "Byte|Ciphertext length:" << ciphertext.size() + << "Byte|ECIES encrypted AES session key:"; + std::string message = header.str(); + act::Blob output(message.c_str()); + keyStart = output.size(); + output.insert(output.end(), encryptedSessionKey.begin(), encryptedSessionKey.end()); + act::Blob header07("|AES ciphertext:"); + output.insert(output.end(), header07.begin(), header07.end()); + header07.clear(); + output.insert(output.end(), ciphertext.begin(), ciphertext.end()); + return output; +} + +static bool readEncFile(const act::Blob &input, size_t keyStart, size_t keyLength, size_t ctStart, + act::Blob &encryptedSessionKey, act::Blob &ciphertext) +{ + act::Blob in(input); + std::string message = reinterpret_cast(&in[0]); + act::Blob key; + act::Blob ctext; + key.insert(key.begin(), &in[keyStart], &in[keyStart + keyLength]); + ctext.insert(ctext.begin(), &in[ctStart], &in[in.size()]); + encryptedSessionKey = key; + ciphertext = ctext; + return !message.empty(); +} + +static void TestBlobAndTools() +{ + act::Blob a("hello"); + CHECK(a.size() == 5); + CHECK(a[a.size()] == 0); + CHECK(std::strcmp(reinterpret_cast(&a[0]), "hello") == 0); + act::Blob empty; + CHECK(empty.empty() && reinterpret_cast(&empty[0])[0] == '\0'); + std::string s("world"); + act::Blob w(s); + a.insert(a.end(), w.begin(), w.end()); + CHECK(a == act::Blob("helloworld")); + a.insert(a.begin() + 5, a.begin(), a.begin() + 5); + CHECK(a == act::Blob("hellohelloworld")); + a.erase(a.begin(), a.begin() + 5); + CHECK(a == act::Blob("helloworld")); + a.resize(3); + CHECK(a == act::Blob("hel") && a[3] == 0); + CHECK(act::Blob("abc") < act::Blob("abd") && act::Blob("ab") < act::Blob("abc")); + act::Blob filled(4, 0xab); + CHECK(filled.size() == 4 && filled[3] == 0xab); + bool threw = false; + try { filled.at(4); } catch (act::OutOfRangeException &) { threw = true; } + CHECK(threw); + std::ostringstream os; + os << act::hex2blob("0x00ff10"); + CHECK(os.str() == "00ff10"); + + act::Blob h = act::hex2blob("0xDEADbeef01"); + CHECK(h.size() == 5 && h[0] == 0xde && h[4] == 0x01); + CHECK(Hex(h) == "deadbeef01"); + CHECK(Hex(act::hex2blob("abc")) == "0abc"); + + CHECK(Hex(act::EncodeNumber("128")) == "0080"); + CHECK(Hex(act::EncodeNumber("-128")) == "80"); + CHECK(Hex(act::EncodeNumber("-129")) == "ff7f"); + CHECK(Hex(act::EncodeNumber("0x7f")) == "7f"); + CHECK(Hex(act::EncodeNumber(0)) == "00"); + CHECK(Hex(act::EncodeOID("0.2.262.1.10.1.3.8")) == "028206010a010308"); + CHECK(Hex(act::EncodeOID("1.2.840.10045.2.1")) == "2a8648ce3d0201"); + + act::Blob check("123456789"); + CHECK(act::CalculateCRC32(check) == 0xCBF43926UL); + CHECK(act::CalculateCRC16(check) == 0xBB3DUL); + CHECK(act::CalculateCRC16CCITT(check) == 0x29B1UL); + + act::Date d(31, 1, 2024, 23, 59, 30); + CHECK(Hex(d.Encode()) == Hex(act::Blob("\x17\x0d" "240131235930Z"))); + act::Date d2(d.Encode()); + CHECK(d2 == d); + d2.AddMonths(1); + CHECK(d2.GetMonth() == 2 && d2.GetDay() == 29); + d2.AddSecond(31); + CHECK(d2.GetDay() == 1 && d2.GetMonth() == 3 && d2.GetHour() == 0 && d2.GetMinute() == 0 && d2.GetSecond() == 1); + CHECK(act::Date(1, 1, 2000).DayOfWeek() == 6); + act::Blob now = act::Date(); + CHECK(now.size() == 15 && now[0] == 0x17); +} + +static void TestKdfVectors() +{ + // NIST CAVP SP 800-135 ANSI X9.63 KDF vectors. + CHECK(Hex(act::detail::X963Kdf("SHA1", act::hex2blob("1c7d7b5f0597b03d06a018466ed1a93e30ed4b04dc64ccdd"), + act::Blob(), 16)) == "bf71dffd8f4d99223936beb46fee8ccc"); + CHECK(Hex(act::detail::X963Kdf("SHA256", act::hex2blob("96c05619d56c328ab95fe84b18264b08725b85e33fd34f08"), + act::Blob(), 16)) == "443024c3dae66b95e6f5670601558f71"); +} + +static void TestKnownAnswer() +{ + // NIST CAVP ECC CDH P-256 vector #0 used as sender (ephemeral) key and recipient key; + // the expected ciphertext was computed independently (SHA-1 X9.63 KDF over R || Z, HMAC-SHA1). + act::Key sender("IES"); + CHECK(importKey(sender, act::ANSIp256r1, "", "", "0x7d7dc5f71eb29ddaf80d6214632eeae03d9058af1fb6d22ed80badb62bc1a534") == 0); + act::Blob x, y; + sender.GetParam(act::PUBLIC_X, x); + sender.GetParam(act::PUBLIC_Y, y); + CHECK(Hex(x) == "00ead218590119e8876b29146ff89ca61770c4edbbf97d38ce385ed281d8a6b230"); + CHECK(Hex(y) == "28af61281fd35e2fa7002523acc85a429cb06ee6648325389f59edfce1405141"); + + act::Key recipient("IES"); + CHECK(importKey(recipient, act::ANSIp256r1, + "0x700c48f77f56584c5cc632ca65640db91b6bacce3a4df6b42ce7cc838833d287", + "0xdb71e509e3fd9b060ddb20ba5c51dcc5948d46fbf640dfe0441782cab85fa4ac", "") == 0); + + act::Blob message; + for (int i = 0; i < 32; ++i) + message.push_back(static_cast(i)); + act::Blob encrypted; + CHECK(encryptSessionKey(recipient, sender, message, encrypted)); + CHECK(Hex(encrypted) == + "04ead218590119e8876b29146ff89ca61770c4edbbf97d38ce385ed281d8a6b230" + "28af61281fd35e2fa7002523acc85a429cb06ee6648325389f59edfce1405141" + "29603457fcb66a714e976d89526304033bf6cb06d4641f63244b7d3a122ae698" + "59bc42f5ec7d495c7047f63bb3df2de71bf77d07"); +} + +struct CurveCase +{ + int id; + const char *name; + size_t fieldLen; +}; + +static void TestCurve(const CurveCase &c) +{ + CHECK(act::detail::CurveFallbackMatches(c.id)); + + // Receiver key pair, as created with CrypTool's key generation and read back from the PSE. + act::Key receiver("IES"); + receiver.SetParam(act::CURVE, c.id); + receiver.Generate(); + act::Blob rx, ry, rs; + receiver.GetParam(act::PUBLIC_X, rx); + receiver.GetParam(act::PUBLIC_Y, ry); + receiver.GetParam(act::PRIVATEKEY, rs); + std::string xR = CrypToolNumber(rx), yR = CrypToolNumber(ry), sR = CrypToolNumber(rs); + + // encrypt() + act::Key privSendECIES("IES"); + privSendECIES.SetParam(act::CURVE, c.id); + privSendECIES.Generate(); + act::Key pubRecECIES("IES"); + CHECK(importKey(pubRecECIES, c.id, xR.c_str(), yR.c_str(), "") == 0); + + act::Blob plaintext("The quick brown fox jumps over the lazy dog. ECIES/AES hybrid test for "); + plaintext.append(act::Blob(c.name)); + act::Blob sessionKey, ciphertext, encryptedSessionKey; + CHECK(encryptData(plaintext, sessionKey, ciphertext)); + CHECK(sessionKey.size() == 32); + CHECK(ciphertext.size() == 16 + (plaintext.size() / 16 + 1) * 16); + CHECK(encryptSessionKey(pubRecECIES, privSendECIES, sessionKey, encryptedSessionKey)); + CHECK(encryptedSessionKey.size() == 1 + 2 * c.fieldLen + sessionKey.size() + 20); + + size_t keyStart = 0; + act::Blob file = writeEncFile(encryptedSessionKey, ciphertext, keyStart); + size_t ctStart = keyStart + encryptedSessionKey.size() + std::strlen("|AES ciphertext:"); + + // decrypt() + act::Key privRecECIES("IES"); + CHECK(importKey(privRecECIES, c.id, "", "", sR.c_str()) == 0); + act::Blob readKey, readCipher, decryptedSessionKey, decrypted; + CHECK(readEncFile(file, keyStart, encryptedSessionKey.size(), ctStart, readKey, readCipher)); + CHECK(readKey == encryptedSessionKey && readCipher == ciphertext); + CHECK(decryptSessionKey(privRecECIES, readKey, decryptedSessionKey) == 0); + CHECK(decryptedSessionKey == sessionKey); + CHECK(decryptData(readCipher, decryptedSessionKey, decrypted) == 0); + CHECK(decrypted == plaintext); + + // Tampering and wrong keys are reported as in ECIESMain.cpp. + for (size_t pos : {size_t(1 + 2 * c.fieldLen), encryptedSessionKey.size() - 1}) + { + act::Blob tampered(encryptedSessionKey); + tampered[pos] ^= 0x01; + act::Blob out; + CHECK(decryptSessionKey(privRecECIES, tampered, out) == 42); + } + act::Blob badPoint(encryptedSessionKey); + badPoint[2] ^= 0x01; + act::Blob out; + CHECK(decryptSessionKey(privRecECIES, badPoint, out) == 42); + CHECK(decryptSessionKey(privSendECIES, encryptedSessionKey, out) == 42); + CHECK(decryptSessionKey(privRecECIES, act::Blob(5, 4), out) == 42); + act::Blob truncated(readCipher.begin(), readCipher.end() - 1); + CHECK(decryptData(truncated, decryptedSessionKey, out) == 5); + + act::Key invalid("IES"); + CHECK(importKey(invalid, c.id, xR.c_str(), "0x1234", "") == 2); + act::Key infinity("IES"); + CHECK(importKey(infinity, c.id, "inf", "inf", "") == 2); + + // Key blob export/import round trip. + act::Blob pub, priv; + receiver.Export(pub, act::PUBLIC); + receiver.Export(priv, act::PRIVATE); + act::Key fromPub(pub), fromPriv(priv); + CHECK(fromPub.GetParam(act::CURVE) == c.id && fromPriv.GetParam(act::CURVE) == c.id); + act::Blob px; + fromPub.GetParam(act::PUBLIC_X, px); + CHECK(px == rx); + act::Blob roundtrip; + { + act::Algorithm enc(fromPub, act::ENCRYPT); + enc << plaintext << act::final; + act::Blob ct; + enc >> ct; + act::Algorithm dec(fromPriv, act::DECRYPT); + dec << ct << act::final; + dec >> roundtrip; + } + CHECK(roundtrip == plaintext); +} + +int main() +{ + act::Init(); + act::Init(); + + try + { + TestBlobAndTools(); + TestKdfVectors(); + TestKnownAnswer(); + + const CurveCase curves[] = { + {act::ANSIp192r1, "EC-prime192v1", 24}, {act::ANSIp192r2, "EC-prime192v2", 24}, + {act::ANSIp192r3, "EC-prime192v3", 24}, {act::ANSIp239r1, "EC-prime239v1", 30}, + {act::ANSIp239r2, "EC-prime239v2", 30}, {act::ANSIp239r3, "EC-prime239v3", 30}, + {act::ANSIp256r1, "EC-prime256v1", 32}, {act::SECGp160r1, "secp160r1", 20}, + }; + for (const CurveCase &c : curves) + TestCurve(c); + + act::Algorithm sha1("SHA1"); + sha1 << act::Blob("abc") << act::final; + act::Blob md; + sha1 >> md; + CHECK(Hex(md) == "a9993e364706816aba3e25717850c26c9cd0d89d"); + + bool threw = false; + try { act::Key unknown("RSA"); } catch (act::NoSuchAlgorithmException &) { threw = true; } + CHECK(threw); + } + catch (act::Exception &e) + { + std::fprintf(stderr, "unexpected act::Exception %s: %s (%s)\n", typeid(e).name(), e.what(), e.where()); + return 1; + } + + if (g_failures != 0) + { + std::fprintf(stderr, "%d check(s) failed\n", g_failures); + return 1; + } + std::printf("cvact_compat: all checks passed\n"); + return 0; +} diff --git a/port/thirdparty/tests/test_ec.cpp b/port/thirdparty/tests/test_ec.cpp new file mode 100644 index 000000000..78f7a1f45 --- /dev/null +++ b/port/thirdparty/tests/test_ec.cpp @@ -0,0 +1,87 @@ +#include "ECsecude.h" +#include "s_ecFp.h" +#include "s_ecconv.h" +#include "ecssa.h" + +#include +#include +#include +#include +#include + +#ifndef CT_HAVE_SECUDE_COMPAT +#define CT_HAVE_SECUDE_COMPAT 0 +#endif + +#if CT_HAVE_SECUDE_COMPAT + +static int failures = 0; + +#define CHECK(cond) \ + do { \ + if (!(cond)) { \ + std::fprintf(stderr, "%s:%d: CHECK failed: %s\n", __FILE__, __LINE__, \ + #cond); \ + ++failures; \ + } \ + } while (0) + +static std::string hex_of(char *s) { + std::string out; + if (!s) + return ""; + const char *p = s; + if (p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) + p += 2; + for (; *p; ++p) + if (std::isxdigit((unsigned char)*p)) + out += (char)std::toupper((unsigned char)*p); + std::size_t nz = out.find_first_not_of('0'); + out = nz == std::string::npos ? "0" : out.substr(nz); + std::free(s); + return out; +} + +int main() { + // Same wiring CrypToolApp::InitInstance performs for libec. +#define DoOneFn(a, b, c, d) ECSecudeLib.c = c; +#define DoOneData(a, b) ECSecudeLib.b = &b; + DoECAll +#undef DoOneFn +#undef DoOneData + + __CurveFp_struct curve; + __PointAc_struct G, R, S, T; + L_NUMBER cofactor[MAXLGTH], order[MAXLGTH]; + char id[] = "prime192v1"; + CHECK(X9_62_init_curve_Fp(&curve, &G, cofactor, order, id) == 0); + CHECK(hex_of(ecFp_point_getstr_xcoord_ac(&G, 16)) == "188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF1012"); + CHECK(hex_of(ecFp_point_getstr_ycoord_ac(&G, 16)) == "7192B95FFC8DA78631011ED6B24CDD573F977A11E794811"); + + CHECK(ecFp_mult_ac_str(&R, &curve, "2", &G) == 0); + CHECK(hex_of(ecFp_point_getstr_xcoord_ac(&R, 16)) == "DAFEBF5828783F2AD35534631588A3F629A70FB16982A888"); + CHECK(hex_of(ecFp_point_getstr_ycoord_ac(&R, 16)) == "DD6BDA0D993DA0FA46B27BBC141B868F59331AFA5C7E93AB"); + + CHECK(ecFp_mult_ac_str(&S, &curve, "0x1234567890abcdef1234567890abcdef", &G) == 0); + CHECK(hex_of(ecFp_point_getstr_xcoord_ac(&S, 16)) == "BEDAC67FB3E84EB498A1703B7F3FB0A0AAAB39CA554C2CE9"); + CHECK(hex_of(ecFp_point_getstr_ycoord_ac(&S, 16)) == "C19EC031FB537035B750105FC0BBE37E839D65DED6595639"); + + CHECK(ecFp_add_ac(&T, &curve, &G, &G) == 0); + CHECK(hex_of(ecFp_point_getstr_xcoord_ac(&T, 16)) == "DAFEBF5828783F2AD35534631588A3F629A70FB16982A888"); + + CHECK(ecFp_mult_ac(&T, &curve, order, &G) == 0); + CHECK(ecFp_point_check_infinity_ac(&T)); + + if (failures == 0) + std::printf("libec: all checks passed\n"); + return failures == 0 ? 0 : 1; +} + +#else + +int main() { + std::printf("libec: secude_compat target not available, headers compiled only\n"); + return 77; +} + +#endif diff --git a/port/thirdparty/tests/test_gmp.cpp b/port/thirdparty/tests/test_gmp.cpp new file mode 100644 index 000000000..069c82b07 --- /dev/null +++ b/port/thirdparty/tests/test_gmp.cpp @@ -0,0 +1,102 @@ +// Exercises the GMP/gmpxx API subset used by CrypTool (PrimeTest*, PrimePolynom, DlgAbout). +#include +#include + +#include +#include +#include +#include +#include +#include + +static int failures = 0; + +#define CHECK(cond) \ + do { \ + if (!(cond)) { \ + std::fprintf(stderr, "%s:%d: CHECK failed: %s\n", __FILE__, __LINE__, \ + #cond); \ + ++failures; \ + } \ + } while (0) + +static mpz_class square_multiply(const mpz_class &a, const mpz_class &b, const mpz_class &n) { + mpz_class x = 1, y = a, s = b; + while (s > 0) { + mpz_class s2 = s % 2; + long odd = mpz_get_si(s2.get_mpz_t()); + if (odd == 1) { + x = x * y; + x = x % n; + } + s >>= 1; + y = y * y; + y = y % n; + } + return x; +} + +int main() { + mpz_class n("170141183460469231731687303715884105727"); + CHECK(mpz_probab_prime_p(n.get_mpz_t(), 10) > 0); + CHECK(mpz_sizeinbase(n.get_mpz_t(), 2) == 127); + + mpz_class m = 561; + CHECK(mpz_probab_prime_p(m.get_mpz_t(), 10) == 0); + + mpz_class pot_b = 1; + pot_b = pot_b << 64; + mpz_class u = pot_b * 12345 + 678; + mpz_class coef = u % pot_b; + CHECK(coef == 678); + u = u >> 64; + CHECK(mpz_get_ui(u.get_mpz_t()) == 12345UL); + mpz_class c = 7; + c <<= 1; + CHECK(c == 14); + + CHECK(square_multiply(2, 10, 1000) == 24); + CHECK(square_multiply(3, n - 1, n) == 1); + + mpz_class small = -42; + CHECK(small.fits_sint_p()); + CHECK(small.get_si() == -42); + CHECK(mpz_get_si(small.get_mpz_t()) == -42); + + std::string per_s = "99.9"; + int size = (int)std::ceil(3.321 * per_s.length()); + mpf_class per(per_s, size); + mpf_class val(50.0, size + 1); + mpf_class res(50.0, size + 1); + int rounds = 1; + while (per > res && rounds < 100) { + val = val / 2; + res = res + val; + ++rounds; + } + CHECK(per >= 0 && per < 100); + CHECK(rounds == 10); + + gmp_randclass rand(gmp_randinit_default); + rand.seed(12345UL); + mpz_class r = rand.get_z_range(n); + CHECK(r >= 0 && r < n); + + char buf[64]; + gmp_snprintf(buf, sizeof buf, "%Zd", u.get_mpz_t()); + CHECK(std::strcmp(buf, "12345") == 0); + std::vector vec{1, 2, 3}; + for (auto it = vec.begin(); it != vec.end(); ++it) + gmp_printf(" %Zd", (*it).mpz_class::get_mpz_t()); + std::printf("\n"); + + CHECK(gmp_version != nullptr && gmp_version[0] >= '6'); + std::cout << "GMP " << gmp_version << ", 2^127-1 = " << n << std::endl; + + if (failures) { + std::fprintf(stderr, "%d check(s) failed\n", failures); + return 1; + } + std::printf("test_gmp: OK\n"); + return 0; +} diff --git a/port/thirdparty/tests/test_miracl.cpp b/port/thirdparty/tests/test_miracl.cpp new file mode 100644 index 000000000..408b62b92 --- /dev/null +++ b/port/thirdparty/tests/test_miracl.cpp @@ -0,0 +1,123 @@ +#include +#include + +#include +#include +#include +#include + +static int failures = 0; + +#define CHECK(cond) \ + do { \ + if (!(cond)) { \ + std::fprintf(stderr, "%s:%d: CHECK failed: %s\n", __FILE__, __LINE__, \ + #cond); \ + ++failures; \ + } \ + } while (0) + +static std::string to_dec(const Big &b) { + std::ostringstream os; + os << b; + return os.str(); +} + +static Big from_dec(const char *s) { + char buf[512]; + std::strncpy(buf, s, sizeof buf - 1); + buf[sizeof buf - 1] = 0; + return Big(buf); +} + +static Big pollard_rho(const Big &n) { + Big x = 2, y = 2, d = 1; + int c = 1; + while (d == 1) { + x = (x * x + c) % n; + y = (y * y + c) % n; + y = (y * y + c) % n; + d = gcd(x > y ? x - y : y - x, n); + } + return d; +} + +static void run(miracl *mip) { + + Big m127 = pow(Big(2), 127) - 1; + CHECK(to_dec(m127) == "170141183460469231731687303715884105727"); + CHECK(prime(m127)); + CHECK(!prime(pow(Big(2), 128) + 1)); + CHECK(bits(m127) == 127); + + Big a = from_dec("123456789012345678901234567890"); + CHECK(pow(a, m127 - 1, m127) == 1); + + Big p = from_dec("1000000007"); + Big e = from_dec("65537"); + CHECK(to_dec(pow(Big(3), e, p)) == "754428556"); + Big inv = inverse(Big(65537), p); + CHECK((inv * 65537) % p == 1); + + Big q1 = from_dec("1000003"), q2 = from_dec("998244353"); + Big n = q1 * q2; + Big f = pollard_rho(n); + CHECK(f == q1 || f == q2); + CHECK(f * (n / f) == n); + + CHECK(to_dec(Big((long)-42)) == "-42"); + if (sizeof(long) == 8) + CHECK(to_dec(Big((long)0x123456789ABL)) == "1250999896491"); + + mip->IOBASE = 16; + CHECK(to_dec(m127) == "7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"); + mip->IOBASE = 10; + + big raw = mirvar(0); + char in[] = "987654321987654321987654321"; + cinstr(raw, in); + char out[128]; + cotstr(raw, out); + CHECK(std::strcmp(out, in) == 0); + mirkill(raw); + + mr_utype moduli[3] = {3, 5, 7}; + mr_utype residues[3] = {2, 3, 2}; + Crt chinese(3, moduli); + CHECK(chinese.eval(residues) == 23); + + Big bm[2] = {from_dec("1000000007"), from_dec("998244353")}; + Big br[2] = {Big(5), Big(7)}; + Crt bigchinese(2, bm); + Big x = bigchinese.eval(br); + CHECK(x % bm[0] == 5 && x % bm[1] == 7); + + flash fx = mirvar(0), fy = mirvar(0), fz = mirvar(0); + fconv(1, 4, fx); + fadd(fx, fx, fy); + fmul(fy, fy, fz); + fconv(1, 4, fx); + CHECK(fcomp(fz, fx) == 0); + fdiv(fz, fy, fx); + fsub(fy, fx, fz); + CHECK(size(fz) == 0); + mirkill(fz); + mirkill(fy); + mirkill(fx); +} + +int main() { + static_assert(sizeof(mr_small) == 4, "CrypTool expects 32-bit MIRACL digits"); + static_assert(sizeof(mr_large) == 8, "double-length type must be 64-bit"); + + miracl *mip = mirsys(4096 / 32 + 128, 0); + CHECK(mip != NULL); + CHECK(mip == get_mip()); + mip->IOBASE = 10; + run(mip); + mirexit(); + + if (failures == 0) + std::printf("miracl: all checks passed\n"); + return failures == 0 ? 0 : 1; +} diff --git a/port/thirdparty/tests/test_ntl.cpp b/port/thirdparty/tests/test_ntl.cpp new file mode 100644 index 000000000..4a3acacbb --- /dev/null +++ b/port/thirdparty/tests/test_ntl.cpp @@ -0,0 +1,171 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +// Same pattern as CrypTool/ZZXY.h and ZZXY.cpp. +using namespace NTL; + +NTL_vector_decl(vec_ZZX, vec_vec_ZZX) + +NTL_matrix_decl(ZZX, vec_ZZX, vec_vec_ZZX, mat_ZZX) + +NTL_vector_decl(vec_ZZ_pX, vec_vec_ZZ_pX) + +NTL_matrix_decl(ZZ_pX, vec_ZZ_pX, vec_vec_ZZ_pX, mat_ZZ_pX) + +NTL_vector_impl(vec_ZZX, vec_vec_ZZX) + +NTL_matrix_impl(ZZX, vec_ZZX, vec_vec_ZZX, mat_ZZX) + +NTL_vector_impl(vec_ZZ_pX, vec_vec_ZZ_pX) + +NTL_matrix_impl(ZZ_pX, vec_ZZ_pX, vec_vec_ZZ_pX, mat_ZZ_pX) + +static int failures = 0; + +#define CHECK(cond) \ + do { \ + if (!(cond)) { \ + std::fprintf(stderr, "%s:%d: CHECK failed: %s\n", __FILE__, __LINE__, \ + #cond); \ + ++failures; \ + } \ + } while (0) + +static std::string str(const ZZ& a) { + std::ostringstream os; + os << a; + return os.str(); +} + +static ZZ zz(const char* s) { + ZZ r; + std::istringstream is(s); + is >> r; + return r; +} + +int main() { + CHECK(std::string(NTL_VERSION) == "5.5.2"); + CHECK(NTL_BITS_PER_LONG == (int)(sizeof(long) * CHAR_BIT)); + CHECK(NTL_MAX_LONG == LONG_MAX); + + ZZ m127 = power(to_ZZ(2), 127) - 1; + CHECK(str(m127) == "170141183460469231731687303715884105727"); + CHECK(ProbPrime(m127)); + CHECK(!ProbPrime(power(to_ZZ(2), 128) - 1)); + CHECK(zz("170141183460469231731687303715884105727") == m127); + CHECK(NumBits(m127) == 127); + + // Fermat: 3^(p-1) = 1 mod p. + CHECK(IsOne(PowerMod(to_ZZ(3), m127 - 1, m127))); + CHECK(PowerMod(to_ZZ(4), to_ZZ(13), to_ZZ(497)) == 445); + + ZZ a = zz("123456789012345678901234567890"); + ZZ b = zz("987654321098765432109876543210"); + CHECK(str(GCD(a, b)) == "9000000000900000000090"); + CHECK(str(a * b) == "121932631137021795226185032733622923332237463801111263526900"); + ZZ q, r; + DivRem(q, r, b, a); + CHECK(q == 8 && str(r) == "9000000000900000000090"); + + ZZ inv = InvMod(to_ZZ(17), to_ZZ(3120)); + CHECK(inv == 2753); + + // RSA-like round trip. + ZZ p = zz("61"), qq = zz("53"), n = p * qq; + ZZ c = PowerMod(to_ZZ(65), to_ZZ(17), n); + CHECK(c == 2790); + CHECK(PowerMod(c, inv, n) == 65); + + ZZ sq = SqrRoot(m127 * m127); + CHECK(sq == m127); + + // RR + RR::SetPrecision(200); + RR x = to_RR(2); + RR s = sqrt(x); + CHECK(abs(s * s - x) < to_RR(1e-50)); + + // ZZX factorization: (x - 1)(x + 2)(x^2 + 1) = x^4 + x^3 - x^2 + x - 2 + ZZX f; + SetCoeff(f, 4, 1); + SetCoeff(f, 3, 1); + SetCoeff(f, 2, -1); + SetCoeff(f, 1, 1); + SetCoeff(f, 0, -2); + ZZ content; + vec_pair_ZZX_long factors; + factor(content, factors, f); + CHECK(content == 1); + CHECK(factors.length() == 3); + ZZX prod; + set(prod); + for (long i = 0; i < factors.length(); i++) + for (long e = 0; e < factors[i].b; e++) prod *= factors[i].a; + CHECK(prod == f); + + // LLL on a small lattice. + mat_ZZ B; + B.SetDims(3, 3); + B[0][0] = 1; B[0][1] = 1; B[0][2] = 1; + B[1][0] = -1; B[1][1] = 0; B[1][2] = 2; + B[2][0] = 3; B[2][1] = 5; B[2][2] = 6; + ZZ det2; + long rank = LLL(det2, B); + CHECK(rank == 3); + CHECK(det2 == 9); + ZZ n0 = B[0][0] * B[0][0] + B[0][1] * B[0][1] + B[0][2] * B[0][2]; + CHECK(n0 <= 2); + + mat_ZZ B2; + B2.SetDims(2, 2); + B2[0][0] = 201; B2[0][1] = 37; + B2[1][0] = 1648; B2[1][1] = 297; + LLL_FP(B2); + ZZ d = abs(B2[0][0] * B2[1][1] - B2[0][1] * B2[1][0]); + CHECK(d == 1279); + mat_ZZ B3; + B3.SetDims(2, 2); + B3[0][0] = 201; B3[0][1] = 37; + B3[1][0] = 1648; B3[1][1] = 297; + LLL_QP(B3); + CHECK(B3 == B2); + + // CrypTool-style mat_ZZX / vec_vec_ZZ_pX usage. + mat_ZZX MX; + MX.SetDims(2, 2); + SetCoeff(MX[0][0], 1, 3); + MX[1][1] = f; + CHECK(deg(MX[1][1]) == 4 && coeff(MX[0][0], 1) == 3); + vec_ZZX row = MX[1]; + CHECK(row.length() == 2); + + ZZ_p::init(to_ZZ(101)); + mat_ZZ_pX MP; + MP.SetDims(1, 1); + SetCoeff(MP[0][0], 2, 5); + ZZ_pX g = MP[0][0] * MP[0][0]; + CHECK(deg(g) == 4 && rep(coeff(g, 4)) == 25); + + if (failures) { + std::fprintf(stderr, "%d check(s) failed\n", failures); + return EXIT_FAILURE; + } + std::printf("NTL %s smoke test passed (NTL_BITS_PER_LONG=%d)\n", NTL_VERSION, NTL_BITS_PER_LONG); + return EXIT_SUCCESS; +} diff --git a/port/thirdparty/tests/test_openssl_compat.cpp b/port/thirdparty/tests/test_openssl_compat.cpp new file mode 100644 index 000000000..bed934b35 --- /dev/null +++ b/port/thirdparty/tests/test_openssl_compat.cpp @@ -0,0 +1,178 @@ +#include +#include +#include + +#include +#include + +// Exactly how CrypTool/HashingOperations.cpp and DlgAbout.cpp include them. +namespace __SSL +{ +#include "OpenSSL//md2.h" +#include "OpenSSL//md4.h" +#include "OpenSSL//md5.h" +#include "OpenSSL//ripemd.h" +#include "OpenSSL//sha.h" +} + +namespace OPENSSL { +#include "crypto.h" +} + +typedef void (*fp_Init_t)(void *context); +typedef void (*fp_Update_t)(void *context, void *data, unsigned long len); +typedef void (*fp_Final_t)(void *hash, void *context); + +struct HashAlgorithmsFP { + fp_Init_t fp_Init; + fp_Update_t fp_Update; + fp_Final_t fp_Final; + void *Context; + int ContextSize; + int BitLength; + const char *Name; +}; + +static HashAlgorithmsFP HAFP[] = { + {(fp_Init_t)__SSL::MD2_Init, (fp_Update_t)__SSL::MD2_Update, (fp_Final_t)__SSL::MD2_Final, NULL, sizeof(__SSL::MD2_CTX), 128, "MD2"}, + {(fp_Init_t)__SSL::MD4_Init, (fp_Update_t)__SSL::MD4_Update, (fp_Final_t)__SSL::MD4_Final, NULL, sizeof(__SSL::MD4_CTX), 128, "MD4"}, + {(fp_Init_t)__SSL::MD5_Init, (fp_Update_t)__SSL::MD5_Update, (fp_Final_t)__SSL::MD5_Final, NULL, sizeof(__SSL::MD5_CTX), 128, "MD5"}, + {(fp_Init_t)__SSL::SHA_Init, (fp_Update_t)__SSL::SHA_Update, (fp_Final_t)__SSL::SHA_Final, NULL, sizeof(__SSL::SHA_CTX), 160, "SHA"}, + {(fp_Init_t)__SSL::SHA1_Init, (fp_Update_t)__SSL::SHA1_Update, (fp_Final_t)__SSL::SHA1_Final, NULL, sizeof(__SSL::SHA_CTX), 160, "SHA-1"}, + {(fp_Init_t)__SSL::RIPEMD160_Init, (fp_Update_t)__SSL::RIPEMD160_Update, (fp_Final_t)__SSL::RIPEMD160_Final, NULL, sizeof(__SSL::RIPEMD160_CTX), 160, "RIPEMD-160"}, + {(fp_Init_t)__SSL::SHA256_Init, (fp_Update_t)__SSL::SHA256_Update, (fp_Final_t)__SSL::SHA256_Final, NULL, sizeof(__SSL::SHA256_CTX), 256, "SHA-256"}, + {(fp_Init_t)__SSL::SHA512_Init, (fp_Update_t)__SSL::SHA512_Update, (fp_Final_t)__SSL::SHA512_Final, NULL, sizeof(__SSL::SHA512_CTX), 512, "SHA-512"}, +}; + +extern "C" { +#define CT_LAYOUT_DECL(T) extern const size_t ct_real_sizeof_##T; extern const size_t ct_real_offsetof_##T; +CT_LAYOUT_DECL(MD4_CTX) +CT_LAYOUT_DECL(MD5_CTX) +CT_LAYOUT_DECL(RIPEMD160_CTX) +CT_LAYOUT_DECL(SHA_CTX) +CT_LAYOUT_DECL(SHA256_CTX) +CT_LAYOUT_DECL(SHA512_CTX) +const char *ct_real_openssl_version(void); +} + +static int failures = 0; + +#define CHECK(cond) \ + do { \ + if (!(cond)) { \ + fprintf(stderr, "%s:%d: CHECK failed: %s\n", __FILE__, __LINE__, #cond); \ + ++failures; \ + } \ + } while (0) + +static std::string hex(const unsigned char *p, size_t n) { + static const char digits[] = "0123456789abcdef"; + std::string s; + for (size_t i = 0; i < n; ++i) { + s += digits[p[i] >> 4]; + s += digits[p[i] & 15]; + } + return s; +} + +// HashingOperations::DoHash: fp_Init, one fp_Update, fp_Final into a malloc'ed context. +static std::string do_hash(int id, const std::string &msg) { + HashAlgorithmsFP *h = &HAFP[id]; + h->Context = malloc(h->ContextSize); + unsigned char out[64]; + h->fp_Init(h->Context); + h->fp_Update(h->Context, (void *)msg.data(), (unsigned long)msg.size()); + h->fp_Final(out, h->Context); + free(h->Context); + h->Context = NULL; + return hex(out, h->BitLength / 8); +} + +// chunkHashInit/Update/Final with irregular chunk sizes. +static std::string chunk_hash(int id, const std::string &msg) { + HashAlgorithmsFP *h = &HAFP[id]; + h->Context = malloc(h->ContextSize); + unsigned char out[64]; + h->fp_Init(h->Context); + size_t pos = 0, step = 1; + while (pos < msg.size()) { + size_t n = step < msg.size() - pos ? step : msg.size() - pos; + h->fp_Update(h->Context, (void *)(msg.data() + pos), (unsigned long)n); + pos += n; + step = step * 3 % 97 + 1; + } + h->fp_Final(out, h->Context); + free(h->Context); + h->Context = NULL; + return hex(out, h->BitLength / 8); +} + +struct Kat { + int id; + const char *msg; + const char *digest; +}; + +int main() { + CHECK(sizeof(__SSL::MD4_CTX) == ct_real_sizeof_MD4_CTX && offsetof(__SSL::MD4_CTX, num) == ct_real_offsetof_MD4_CTX); + CHECK(sizeof(__SSL::MD5_CTX) == ct_real_sizeof_MD5_CTX && offsetof(__SSL::MD5_CTX, num) == ct_real_offsetof_MD5_CTX); + CHECK(sizeof(__SSL::RIPEMD160_CTX) == ct_real_sizeof_RIPEMD160_CTX && offsetof(__SSL::RIPEMD160_CTX, num) == ct_real_offsetof_RIPEMD160_CTX); + CHECK(sizeof(__SSL::SHA_CTX) == ct_real_sizeof_SHA_CTX && offsetof(__SSL::SHA_CTX, num) == ct_real_offsetof_SHA_CTX); + CHECK(sizeof(__SSL::SHA256_CTX) == ct_real_sizeof_SHA256_CTX && offsetof(__SSL::SHA256_CTX, md_len) == ct_real_offsetof_SHA256_CTX); + CHECK(sizeof(__SSL::SHA512_CTX) == ct_real_sizeof_SHA512_CTX && offsetof(__SSL::SHA512_CTX, md_len) == ct_real_offsetof_SHA512_CTX); + + static const char digits80[] = + "12345678901234567890123456789012345678901234567890123456789012345678901234567890"; + static const char abc448[] = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"; + static const Kat kats[] = { + {0, "", "8350e5a3e24c153df2275c9f80692773"}, + {0, "a", "32ec01ec4a6dac72c0ab96fb34c0b5d1"}, + {0, "abc", "da853b0d3f88d99b30283a69e6ded6bb"}, + {0, "message digest", "ab4f496bfb2a530b219ff33031fe06b0"}, + {0, "abcdefghijklmnopqrstuvwxyz", "4e8ddff3650292ab5a4108c3aa47940b"}, + {0, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", "da33def2a42df13975352846c30338cd"}, + {0, digits80, "d5976f79d83d3a0dc9806c3c66f3efd8"}, + {1, "", "31d6cfe0d16ae931b73c59d7e0c089c0"}, + {1, "abc", "a448017aaf21d8525fc10ae87aa6729d"}, + {1, digits80, "e33b4ddc9c38f2199c3e7b164fcc0536"}, + {2, "", "d41d8cd98f00b204e9800998ecf8427e"}, + {2, "abc", "900150983cd24fb0d6963f7d28e17f72"}, + {2, digits80, "57edf4a22be3c955ac49da2e2107b67a"}, + {3, "abc", "0164b8a914cd2a5e74c4f7ff082c4d97f1edf880"}, + {3, abc448, "d2516ee1acfa5baf33dfc1c471e438449ef134c8"}, + {4, "abc", "a9993e364706816aba3e25717850c26c9cd0d89d"}, + {4, abc448, "84983e441c3bd26ebaae4aa1f95129e5e54670f1"}, + {5, "", "9c1185a5c5e9fc54612808977ee8f548b2258d31"}, + {5, "abc", "8eb208f7e05d987a9b044a8e98c6b087f15a0bfc"}, + {6, "abc", "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"}, + {6, abc448, "248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1"}, + {7, "abc", "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f"}, + }; + + for (const Kat &k : kats) { + std::string got = do_hash(k.id, k.msg); + if (got != k.digest) { + fprintf(stderr, "%s(\"%.20s\") = %s, expected %s\n", HAFP[k.id].Name, k.msg, got.c_str(), k.digest); + ++failures; + } + } + + std::string million(1000000, 'a'); + CHECK(do_hash(3, million) == "3232affa48628a26653b5aaa44541fd90d690603"); + CHECK(do_hash(4, million) == "34aa973cd4c4daa4f61eeb2bdbad27316534016f"); + + std::string sample; + for (int i = 0; i < 5000; ++i) + sample += (char)(i * 131 + (i >> 3)); + for (int id = 0; id < 8; ++id) + CHECK(do_hash(id, sample) == chunk_hash(id, sample)); + + const char *version = OPENSSL::SSLeay_version(SSLEAY_VERSION); + CHECK(version != NULL && strncmp(version, "OpenSSL 3", 9) == 0); + CHECK(strcmp(version, ct_real_openssl_version()) == 0); + CHECK(strcmp(__SSL::MD2_options(), "md2(int)") == 0); + + if (failures == 0) + printf("openssl_compat: all checks passed (%s)\n", version); + return failures == 0 ? 0 : 1; +} diff --git a/port/thirdparty/tests/test_openssl_compat_layout.c b/port/thirdparty/tests/test_openssl_compat_layout.c new file mode 100644 index 000000000..5c8378af6 --- /dev/null +++ b/port/thirdparty/tests/test_openssl_compat_layout.c @@ -0,0 +1,24 @@ +/* Compiled against the real OpenSSL 3 headers only (no compat include dir). */ +#include + +#include +#include +#include +#include +#include + +#define CT_LAYOUT(T, last) \ + const size_t ct_real_sizeof_##T = sizeof(T); \ + const size_t ct_real_offsetof_##T = offsetof(T, last); + +CT_LAYOUT(MD4_CTX, num) +CT_LAYOUT(MD5_CTX, num) +CT_LAYOUT(RIPEMD160_CTX, num) +CT_LAYOUT(SHA_CTX, num) +CT_LAYOUT(SHA256_CTX, md_len) +CT_LAYOUT(SHA512_CTX, md_len) + +const char *ct_real_openssl_version(void) +{ + return OpenSSL_version(OPENSSL_VERSION); +} diff --git a/port/tools/ccheck.py b/port/tools/ccheck.py new file mode 100644 index 000000000..3be92380c --- /dev/null +++ b/port/tools/ccheck.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python3 +"""Runs the compile command of one source file from build/compile_commands.json with -fsyntax-only.""" +import json +import shlex +import subprocess +import sys + +build = sys.argv[1] +target = sys.argv[2] +extra = sys.argv[3:] +cc = json.load(open(build + '/compile_commands.json')) +entry = [x for x in cc if x['file'].endswith(target)][0] +args = shlex.split(entry['command']) +out = [] +skip = False +for a in args: + if skip: + skip = False + continue + if a in ('-o', '-MF', '-MT'): + skip = True + continue + if a in ('-MD', '-c'): + continue + out.append(a) +out += ['-fsyntax-only'] + extra +r = subprocess.run(out, cwd=entry['directory'], capture_output=True, text=True) +sys.stdout.write(r.stderr) +sys.exit(r.returncode) diff --git a/port/tools/fix_includes.py b/port/tools/fix_includes.py new file mode 100644 index 000000000..353b69791 --- /dev/null +++ b/port/tools/fix_includes.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python3 +"""Rewrites #include directives in the CrypTool sources to portable spellings. + +Backslashes become slashes, './' prefixes are dropped and quoted includes are matched +case-insensitively against the files on disk so they also resolve on case-sensitive file systems. +""" +import os +import re +import sys + +REPO = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..')) +DIRS = ['CrypTool', 'CrypTool/zip', 'ChallengeResponse', 'libVolRen'] +SPECIAL = { + '../aes/mars/mars.h': '"mars.h"', + '../aes/twofish/twofish.h': '"Twofish.h"', + '../aes/serpent/serpent.h': '"Serpent.h"', + '../aes/rijndael/rijndael-api-fst.h': '"Rijndael-api-fst.h"', + '../aes/rc6/rc6.h': '"RC6.h"', + '../libntl/include/ntl/version.h': '', + '../libmiracl/include/big.h': '"big.h"', + 'libanalyse/la_string.h': '"libanalyse/la_string.h"', + 'libanalyse/analyse.h': '"libanalyse/analyse.h"', + 'libanalyse/ngram.h': '"libanalyse/NGram.h"', + 'sys/timeb.h': '', +} +INCLUDE_RE = re.compile(rb'^(\s*#\s*include\s*)"([^"]+)"', re.M) + + +def listing(directory, cache={}): + if directory not in cache: + try: + cache[directory] = {name.lower(): name for name in os.listdir(directory)} + except OSError: + cache[directory] = {} + return cache[directory] + + +def resolve(base, rel): + parts = [p for p in rel.split('/') if p not in ('', '.')] + current = base + out = [] + for part in parts: + if part == '..': + current = os.path.dirname(current) + out.append('..') + continue + actual = listing(current).get(part.lower()) + if actual is None: + return None + current = os.path.join(current, actual) + out.append(actual) + return '/'.join(out) + + +def fix_file(path, search_dirs): + data = open(path, 'rb').read() + + def repl(match): + prefix, target = match.group(1), match.group(2).decode('latin-1') + norm = target.replace('\\', '/') + while norm.startswith('./'): + norm = norm[2:] + special = SPECIAL.get(norm.lower()) + if special: + return prefix + special.encode() + for base in [os.path.dirname(path)] + search_dirs: + resolved = resolve(base, norm) + if resolved: + return prefix + ('"%s"' % resolved).encode('latin-1') + if norm != target: + return prefix + ('"%s"' % norm).encode('latin-1') + return match.group(0) + + new = INCLUDE_RE.sub(repl, data) + if new != data: + open(path, 'wb').write(new) + return True + return False + + +def main(): + search = [os.path.join(REPO, 'CrypTool')] + changed = 0 + for d in DIRS: + full = os.path.join(REPO, d) + for name in sorted(os.listdir(full)): + if name.lower().endswith(('.cpp', '.c', '.h', '.hpp', '.cc', '.inl')): + if fix_file(os.path.join(full, name), search): + changed += 1 + print('changed', changed, 'files', file=sys.stderr) + + +if __name__ == '__main__': + main() diff --git a/port/tools/fix_msgmaps.py b/port/tools/fix_msgmaps.py new file mode 100644 index 000000000..1a3394367 --- /dev/null +++ b/port/tools/fix_msgmaps.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python3 +"""Qualifies message map handlers (ON_xxx(..., OnFoo) -> ON_xxx(..., &CClass::OnFoo)). + +MSVC accepts bare member names in message map macros; standard C++ needs &Class::member. +""" +import os +import re +import sys + +REPO = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..')) +DIRS = ['CrypTool', 'ChallengeResponse', 'libVolRen', 'aestool', 'NumberShark'] +BEGIN_RE = re.compile(rb'BEGIN_MESSAGE_MAP\s*\(\s*(\w+)\s*,') +END_RE = re.compile(rb'END_MESSAGE_MAP\s*\(\s*\)') +ENTRY_RE = re.compile(rb'^(\s*ON_(?!EVENT\b)\w+\s*\(.*,\s*)([A-Za-z_]\w*)(\s*\)[^\n]*)$', re.M) + + +def fix(data): + out = [] + pos = 0 + changed = False + while True: + m = BEGIN_RE.search(data, pos) + if not m: + out.append(data[pos:]) + break + end = END_RE.search(data, m.end()) + if not end: + out.append(data[pos:]) + break + cls = m.group(1) + body = data[m.end():end.start()] + new_body = ENTRY_RE.sub(lambda e: e.group(1) + b'&' + cls + b'::' + e.group(2) + e.group(3), body) + changed |= new_body != body + out.append(data[pos:m.end()]) + out.append(new_body) + pos = end.start() + return b''.join(out), changed + + +def main(): + count = 0 + for d in DIRS: + full = os.path.join(REPO, d) + for name in sorted(os.listdir(full)): + if name.lower().endswith(('.cpp', '.h')): + path = os.path.join(full, name) + data = open(path, 'rb').read() + new, changed = fix(data) + if changed: + open(path, 'wb').write(new) + count += 1 + print('qualified handlers in', count, 'files', file=sys.stderr) + + +if __name__ == '__main__': + main() diff --git a/secude/secude/af.h b/secude/secude/af.h index 007b00726..bb7d528ff 100644 --- a/secude/secude/af.h +++ b/secude/secude/af.h @@ -22,6 +22,11 @@ #include +#if !defined(HAS_EXT) +/* an older secure.h (CrypTool/secure.h) was included first and lacks this type */ +typedef SEQUENCE_OF(ObjId) SEQUENCE_OF_ObjId; +#endif + /* * secure.h defines: * diff --git a/secude/secude/platform/platlinu.h b/secude/secude/platform/platlinu.h index 38c653bf7..f52ec56cf 100644 --- a/secude/secude/platform/platlinu.h +++ b/secude/secude/platform/platlinu.h @@ -33,6 +33,8 @@ # define SEC_TARGET_CPU_IA64 1 #elif defined(__x86_64__) # define SEC_TARGET_CPU_AMD64 1 +#elif defined(__aarch64__) +# define SEC_TARGET_CPU_ARM64 1 #else # error "architecture not supported" #endif @@ -43,6 +45,9 @@ #if __GNUC__ >= 3 || __GNUC_MINOR__ > 8 /* This is stupid: egcs is libc6, else libc5. Find a better way! */ +#ifndef _DEFAULT_SOURCE +#define _DEFAULT_SOURCE 1 +#endif #define _BSD_SOURCE 1 #include diff --git a/secude/secude/platform/platmosx.h b/secude/secude/platform/platmosx.h index ce6a2044d..683b08ac1 100644 --- a/secude/secude/platform/platmosx.h +++ b/secude/secude/platform/platmosx.h @@ -9,7 +9,13 @@ #define SEC_TARGET_RT_MAC_MACHO 1 #define SEC_TARGET_API_MAC_CARBON 1 #define SEC_TARGET_API_UNIX 1 +#if defined(__arm64__) || defined(__aarch64__) +#define SEC_TARGET_CPU_ARM64 1 +#elif defined(__x86_64__) +#define SEC_TARGET_CPU_AMD64 1 +#else #define SEC_TARGET_CPU_PPC 1 +#endif /* #define _BSD_SOURCE 1 diff --git a/secude/secude/sec_conf.h b/secude/secude/sec_conf.h index a81ce782d..12a245ea1 100644 --- a/secude/secude/sec_conf.h +++ b/secude/secude/sec_conf.h @@ -93,6 +93,10 @@ || defined(SEC_TARGET_CPU_PPC) && SEC_TARGET_CPU_PPC \ || defined(SEC_TARGET_CPU_HPPA) && SEC_TARGET_CPU_HPPA # define SEC_BYTE_ORDER SEC_BIG_ENDIAN +# elif defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +# define SEC_BYTE_ORDER SEC_LITTLE_ENDIAN +# elif defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +# define SEC_BYTE_ORDER SEC_BIG_ENDIAN # endif # endif #endif diff --git a/secude/secude/sec_glob.h b/secude/secude/sec_glob.h index 0c7a4c7d2..276cb80e9 100644 --- a/secude/secude/sec_glob.h +++ b/secude/secude/sec_glob.h @@ -20,7 +20,10 @@ #include -#if !defined(TARGET_OS_MAC) || !TARGET_OS_MAC +#if defined(__APPLE__) && defined(__MACH__) +/* identical to the MacTypes.h typedef, so both headers can be combined */ +typedef unsigned char Boolean; +#elif !defined(TARGET_OS_MAC) || !TARGET_OS_MAC /* MPW-C defines 'Boolean', too */ #if (defined(applec)||defined(_MAC)) #include From 055fff731d939ebc1b4988e96aee45f1336f8200 Mon Sep 17 00:00:00 2001 From: Kamil Marczak Date: Tue, 6 Oct 2026 12:24:15 +0200 Subject: [PATCH 02/46] ci(status): measure port progress from build errors and tests port/tools/port_status.py builds everything, syntax-checks every CrypTool source, runs ctest and writes port/status/{status.json,badge.json,STATUS.md}. A GitHub workflow runs it on every push to xplat-port and commits the result; the README badge reads badge.json through shields.io. --- .github/workflows/port-status.yml | 31 +++++++++ CMakeLists.txt | 1 + README.md | 2 +- port/PORTING_STATUS.md | 3 +- port/status/STATUS.md | 12 ++++ port/status/badge.json | 1 + port/status/status.json | 10 +++ port/tools/port_status.py | 110 ++++++++++++++++++++++++++++++ 8 files changed, 168 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/port-status.yml create mode 100644 port/status/STATUS.md create mode 100644 port/status/badge.json create mode 100644 port/status/status.json create mode 100644 port/tools/port_status.py diff --git a/.github/workflows/port-status.yml b/.github/workflows/port-status.yml new file mode 100644 index 000000000..6b2c005c3 --- /dev/null +++ b/.github/workflows/port-status.yml @@ -0,0 +1,31 @@ +name: Port status + +on: + push: + branches: [xplat-port] + paths-ignore: ['port/status/**'] + workflow_dispatch: + +permissions: + contents: write + +concurrency: + group: port-status + cancel-in-progress: true + +jobs: + status: + runs-on: macos-latest + steps: + - uses: actions/checkout@v4 + - name: Install dependencies + run: brew install wxwidgets@3.2 openssl@3 gmp ninja cmake + - name: Measure + run: python3 port/tools/port_status.py + - name: Publish + run: | + git config user.name "port-status-bot" + git config user.email "port-status-bot@users.noreply.github.com" + git add port/status + git diff --cached --quiet || git commit -m "Update port status [skip ci]" + git push diff --git a/CMakeLists.txt b/CMakeLists.txt index bd192d63e..19e038876 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,6 +8,7 @@ if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) endif() set(REPO_ROOT ${CMAKE_CURRENT_SOURCE_DIR}) +enable_testing() if(APPLE) foreach(prefix /opt/homebrew/opt /usr/local/opt) diff --git a/README.md b/README.md index 4706d4000..d74b6b57c 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # CrypTool 1 -![macOS/Linux port](https://img.shields.io/badge/macOS%2FLinux%20port-35%25-orange) — see [port/PORTING_STATUS.md](port/PORTING_STATUS.md) +![macOS/Linux port](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/wroclaw-tech/cryptool1/xplat-port/port/status/badge.json) — see [port/status/STATUS.md](port/status/STATUS.md) [CrypTool 1](https://www.cryptool.org/ct1/) (CT1) is a free Windows program for cryptography and cryptanalysis. It is part of the [CrypTool Project](https://www.cryptool.org/). diff --git a/port/PORTING_STATUS.md b/port/PORTING_STATUS.md index df84d1c1e..7f46abb64 100644 --- a/port/PORTING_STATUS.md +++ b/port/PORTING_STATUS.md @@ -2,7 +2,8 @@ Work in progress on branch `xplat-port`. Nothing builds into a runnable app yet. -**Overall progress: ~35%** (update the badge in the top-level README.md together with this table) +**Overall progress:** measured automatically, see [status/STATUS.md](status/STATUS.md). +The table below is a manual estimate by area. | Area | Weight | Done | |---|---|---| diff --git a/port/status/STATUS.md b/port/status/STATUS.md new file mode 100644 index 000000000..1077f6d09 --- /dev/null +++ b/port/status/STATUS.md @@ -0,0 +1,12 @@ +# Port status (generated by port/tools/port_status.py) + +| Metric | Value | +|---|---| +| Progress | **58%** | +| Compile errors | 456 | +| CrypTool sources compiling | 200 / 296 | +| CrypTool links | no | +| Tests passing | 8 / 10 | +| Updated | 2026-10-06 10:24 UTC | + +Progress = 50% x sources compiling + 20% if the app links + 30% x tests passing. diff --git a/port/status/badge.json b/port/status/badge.json new file mode 100644 index 000000000..bd5570629 --- /dev/null +++ b/port/status/badge.json @@ -0,0 +1 @@ +{"schemaVersion": 1, "label": "macOS/Linux port", "message": "58%", "color": "orange"} \ No newline at end of file diff --git a/port/status/status.json b/port/status/status.json new file mode 100644 index 000000000..3e3fdac2f --- /dev/null +++ b/port/status/status.json @@ -0,0 +1,10 @@ +{ + "progress": 58, + "compileErrors": 456, + "sourcesCompiled": 200, + "sourcesTotal": 296, + "linked": false, + "testsPassed": 8, + "testsTotal": 10, + "updated": "2026-10-06 10:24 UTC" +} \ No newline at end of file diff --git a/port/tools/port_status.py b/port/tools/port_status.py new file mode 100644 index 000000000..182c9f117 --- /dev/null +++ b/port/tools/port_status.py @@ -0,0 +1,110 @@ +#!/usr/bin/env python3 +"""Measures how far the macOS/Linux port is and writes port/status/{status.json,badge.json,STATUS.md}. + +Progress = 50% x share of CrypTool sources that compile + 20% if CrypTool links + + 30% x share of passing tests (tests that do not build count as failed). +""" +import datetime +import json +import os +import re +import shlex +import subprocess +import sys +from concurrent.futures import ThreadPoolExecutor + +REPO = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..')) +BUILD = os.environ.get('PORT_STATUS_BUILD', os.path.join(REPO, 'build-status')) +OUT = os.path.join(REPO, 'port', 'status') + + +def run(cmd, **kw): + print('+', ' '.join(cmd), flush=True) + return subprocess.run(cmd, cwd=REPO, capture_output=True, text=True, errors='replace', **kw) + + +def syntax_check(entry): + args = shlex.split(entry['command']) + out, skip = [], False + for a in args: + if skip: + skip = False + continue + if a in ('-o', '-MF', '-MT'): + skip = True + continue + if a in ('-MD', '-c'): + continue + out.append(a) + r = subprocess.run(out + ['-fsyntax-only', '-ferror-limit=0'], cwd=entry['directory'], capture_output=True, + text=True, errors='replace') + return r.returncode == 0, len(re.findall(r'\berror:', r.stderr)) + + +def main(): + configure = run(['cmake', '-S', REPO, '-B', BUILD, '-G', 'Ninja', '-DCRYPTOOL_THIRDPARTY_TESTS=ON', + '-DMFCWX_TESTS=ON'] + sys.argv[1:]) + if configure.returncode != 0: + sys.stderr.write(configure.stdout[-4000:] + configure.stderr[-4000:]) + sys.exit('configure failed') + + build = run(['ninja', '-C', BUILD, '-k', '0']) + app_dirs = ('/CrypTool/', '/ChallengeResponse/', '/libVolRen/') + library_errors = sum(1 for line in (build.stdout + build.stderr).splitlines() + if 'error:' in line and not any(d in line for d in app_dirs)) + + # Application sources are syntax-checked on their own so that a broken support library + # does not hide their state. + commands = json.load(open(os.path.join(BUILD, 'compile_commands.json'))) + units = [c for c in commands if '/CrypTool.dir/' in c.get('output', c['command'])] + with ThreadPoolExecutor(max_workers=os.cpu_count() or 4) as pool: + results = list(pool.map(syntax_check, units)) + compiled = sum(1 for ok, _ in results if ok) + errors = library_errors + sum(n for _, n in results) + linked = any(os.path.exists(os.path.join(BUILD, 'port', 'app', p)) + for p in ('CrypTool', 'CrypTool.app/Contents/MacOS/CrypTool')) + + tests = run(['ctest', '--test-dir', BUILD, '--timeout', '300']) + m = re.search(r'(\d+)% tests passed, (\d+) tests failed out of (\d+)', tests.stdout) + total = int(m.group(3)) if m else 0 + failed = int(m.group(2)) if m else 0 + passed = total - failed + + compile_share = compiled / len(units) if units else 0.0 + test_share = passed / total if total else 0.0 + progress = round(50 * compile_share + 20 * (1 if linked else 0) + 30 * test_share) + + status = { + 'progress': progress, + 'compileErrors': errors, + 'sourcesCompiled': compiled, + 'sourcesTotal': len(units), + 'linked': linked, + 'testsPassed': passed, + 'testsTotal': total, + 'updated': datetime.datetime.now(datetime.timezone.utc).strftime('%Y-%m-%d %H:%M UTC'), + } + colour = 'red' if progress < 34 else 'orange' if progress < 67 else 'yellow' if progress < 100 else 'brightgreen' + os.makedirs(OUT, exist_ok=True) + json.dump(status, open(os.path.join(OUT, 'status.json'), 'w'), indent=2) + json.dump({'schemaVersion': 1, 'label': 'macOS/Linux port', 'message': f'{progress}%', 'color': colour}, + open(os.path.join(OUT, 'badge.json'), 'w')) + with open(os.path.join(OUT, 'STATUS.md'), 'w') as f: + f.write(f"""# Port status (generated by port/tools/port_status.py) + +| Metric | Value | +|---|---| +| Progress | **{progress}%** | +| Compile errors | {errors} | +| CrypTool sources compiling | {compiled} / {len(units)} | +| CrypTool links | {'yes' if linked else 'no'} | +| Tests passing | {passed} / {total} | +| Updated | {status['updated']} | + +Progress = 50% x sources compiling + 20% if the app links + 30% x tests passing. +""") + print(json.dumps(status, indent=2)) + + +if __name__ == '__main__': + main() From 9a89e59d27ae3f3fbd7f59ca1972ded8cd6bc19d Mon Sep 17 00:00:00 2001 From: Kamil Marczak Date: Tue, 6 Oct 2026 17:15:57 +0200 Subject: [PATCH 03/46] ci(status): publish port status to a dedicated port-status branch The workflow no longer commits generated files to xplat-port; it pushes badge.json, status.json and STATUS.md to the port-status branch with a conventional commit message. README and PORTING_STATUS link there. --- .github/workflows/port-status.yml | 20 +++++++++++++++----- .gitignore | 1 + README.md | 2 +- port/PORTING_STATUS.md | 2 +- 4 files changed, 18 insertions(+), 7 deletions(-) diff --git a/.github/workflows/port-status.yml b/.github/workflows/port-status.yml index 6b2c005c3..ff0f74e57 100644 --- a/.github/workflows/port-status.yml +++ b/.github/workflows/port-status.yml @@ -3,7 +3,6 @@ name: Port status on: push: branches: [xplat-port] - paths-ignore: ['port/status/**'] workflow_dispatch: permissions: @@ -22,10 +21,21 @@ jobs: run: brew install wxwidgets@3.2 openssl@3 gmp ninja cmake - name: Measure run: python3 port/tools/port_status.py - - name: Publish + - name: Publish to the port-status branch run: | + out="$RUNNER_TEMP/status" + mkdir -p "$out" + cp port/status/* "$out/" git config user.name "port-status-bot" git config user.email "port-status-bot@users.noreply.github.com" - git add port/status - git diff --cached --quiet || git commit -m "Update port status [skip ci]" - git push + if git fetch --depth 1 origin port-status; then + git switch -c port-status FETCH_HEAD + else + git switch --orphan port-status + fi + git rm -rfq --ignore-unmatch . + git clean -fdxq + cp "$out"/* . + git add badge.json status.json STATUS.md + git diff --cached --quiet || git commit -m "chore(status): update port status for ${GITHUB_SHA::8}" + git push origin port-status diff --git a/.gitignore b/.gitignore index ec0a25e31..f55be30cf 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ port/secude/build*/ port/thirdparty/build*/ port/rc/test/build*/ __pycache__/ +port/status/ diff --git a/README.md b/README.md index d74b6b57c..9373d1702 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # CrypTool 1 -![macOS/Linux port](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/wroclaw-tech/cryptool1/xplat-port/port/status/badge.json) — see [port/status/STATUS.md](port/status/STATUS.md) +![macOS/Linux port](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/wroclaw-tech/cryptool1/port-status/badge.json) — see [STATUS.md](https://github.com/wroclaw-tech/cryptool1/blob/port-status/STATUS.md) [CrypTool 1](https://www.cryptool.org/ct1/) (CT1) is a free Windows program for cryptography and cryptanalysis. It is part of the [CrypTool Project](https://www.cryptool.org/). diff --git a/port/PORTING_STATUS.md b/port/PORTING_STATUS.md index 7f46abb64..0d10aa6ed 100644 --- a/port/PORTING_STATUS.md +++ b/port/PORTING_STATUS.md @@ -2,7 +2,7 @@ Work in progress on branch `xplat-port`. Nothing builds into a runnable app yet. -**Overall progress:** measured automatically, see [status/STATUS.md](status/STATUS.md). +**Overall progress:** measured automatically, see [STATUS.md on the port-status branch](https://github.com/wroclaw-tech/cryptool1/blob/port-status/STATUS.md) (`python3 port/tools/port_status.py` locally). The table below is a manual estimate by area. | Area | Weight | Done | From bc3c582d82971666a75e79410a2406b2295ea6d5 Mon Sep 17 00:00:00 2001 From: Kamil Marczak Date: Tue, 6 Oct 2026 17:23:49 +0200 Subject: [PATCH 04/46] feat(mfcwx): add message dispatch, standard controls and Win32 window API wnd.cpp bridges wx events to Windows messages and implements CWnd; controls.cpp creates controls from dialog templates and handles their messages; winapi.cpp provides DefWindowProc and the HWND based Win32 API (messages, geometry, dialog items, cursors, clipboard, file dialogs). --- port/mfcwx/include/mfcwx/winconst.h | 1 + port/mfcwx/src/controls.cpp | 383 +++--- port/mfcwx/src/internal.h | 3 + port/mfcwx/src/winapi.cpp | 1683 +++++++++++++++++++++++++++ port/mfcwx/src/windows_impl.h | 7 + port/mfcwx/src/wnd.cpp | 8 +- 6 files changed, 1900 insertions(+), 185 deletions(-) create mode 100644 port/mfcwx/src/winapi.cpp diff --git a/port/mfcwx/include/mfcwx/winconst.h b/port/mfcwx/include/mfcwx/winconst.h index 6e48af10b..a20702aa8 100644 --- a/port/mfcwx/include/mfcwx/winconst.h +++ b/port/mfcwx/include/mfcwx/winconst.h @@ -449,6 +449,7 @@ #define WM_MOUSEACTIVATE 0x0021 #define WM_CHILDACTIVATE 0x0022 #define WM_GETMINMAXINFO 0x0024 +#define WM_NEXTDLGCTL 0x0028 #define WM_DRAWITEM 0x002B #define WM_MEASUREITEM 0x002C #define WM_DELETEITEM 0x002D diff --git a/port/mfcwx/src/controls.cpp b/port/mfcwx/src/controls.cpp index af06a5010..c7c6677d5 100644 --- a/port/mfcwx/src/controls.cpp +++ b/port/mfcwx/src/controls.cpp @@ -2116,194 +2116,215 @@ LRESULT RangeProc(wxWindow* w, WindowState& st, UINT msg, WPARAM wParam, LPARAM gauge->SetRange(range); gauge->SetValue(std::max(0, std::min(range, r.pos - r.minValue))); }; - switch (msg) { - case PBM_SETRANGE: { - LRESULT old = MAKELRESULT(r.minValue, r.maxValue); - r.minValue = LOWORD(lParam); - r.maxValue = HIWORD(lParam); - applyGauge(); - return old; - } - case PBM_SETRANGE32: { - LRESULT old = MAKELRESULT(r.minValue, r.maxValue); - r.minValue = static_cast(wParam); - r.maxValue = static_cast(lParam); - applyGauge(); - return old; - } - case PBM_GETRANGE: { - auto* range = reinterpret_cast(lParam); - if (range) { - range[0] = r.minValue; - range[1] = r.maxValue; + if (st.kind == ControlKind::Progress) { + switch (msg) { + case PBM_SETRANGE: { + LRESULT old = MAKELRESULT(r.minValue, r.maxValue); + r.minValue = LOWORD(lParam); + r.maxValue = HIWORD(lParam); + applyGauge(); + return old; + } + case PBM_SETRANGE32: { + LRESULT old = MAKELRESULT(r.minValue, r.maxValue); + r.minValue = static_cast(wParam); + r.maxValue = static_cast(lParam); + applyGauge(); + return old; + } + case PBM_GETRANGE: { + auto* range = reinterpret_cast(lParam); + if (range) { + range[0] = r.minValue; + range[1] = r.maxValue; + } + return wParam ? r.minValue : r.maxValue; + } + case PBM_SETPOS: { + int old = r.pos; + r.pos = static_cast(wParam); + applyGauge(); + return old; + } + case PBM_DELTAPOS: { + int old = r.pos; + r.pos += static_cast(wParam); + applyGauge(); + return old; + } + case PBM_SETSTEP: { + int old = r.step; + r.step = static_cast(wParam); + return old; + } + case PBM_STEPIT: { + int old = r.pos; + r.pos += r.step; + if (r.pos > r.maxValue) + r.pos = r.minValue + (r.pos - r.maxValue); + applyGauge(); + return old; + } + case PBM_GETPOS: + return r.pos; + case PBM_SETBARCOLOR: + case PBM_SETBKCOLOR: + return CLR_DEFAULT; + case PBM_SETMARQUEE: + if (gauge && wParam) + gauge->Pulse(); + return TRUE; + default: + break; } - return wParam ? r.minValue : r.maxValue; - } - case PBM_SETPOS: { - int old = r.pos; - r.pos = static_cast(wParam); - applyGauge(); - return old; - } - case PBM_DELTAPOS: { - int old = r.pos; - r.pos += static_cast(wParam); - applyGauge(); - return old; - } - case PBM_SETSTEP: { - int old = r.step; - r.step = static_cast(wParam); - return old; - } - case PBM_STEPIT: { - int old = r.pos; - r.pos += r.step; - if (r.pos > r.maxValue) - r.pos = r.minValue + (r.pos - r.maxValue); - applyGauge(); - return old; - } - case PBM_GETPOS: - return r.pos; - case PBM_SETBARCOLOR: - case PBM_SETBKCOLOR: - return CLR_DEFAULT; - case PBM_SETMARQUEE: - if (gauge && wParam) - gauge->Pulse(); - return TRUE; - case TBM_GETPOS: - return slider ? slider->GetValue() : r.pos; - case TBM_SETPOS: - r.pos = static_cast(lParam); - if (slider) - slider->SetValue(r.pos); - return 0; - case TBM_SETRANGE: - r.minValue = LOWORD(lParam); - r.maxValue = HIWORD(lParam); - if (slider) - slider->SetRange(r.minValue, std::max(r.minValue, r.maxValue)); - return 0; - case TBM_SETRANGEMIN: - r.minValue = static_cast(lParam); - if (slider) - slider->SetRange(r.minValue, std::max(r.minValue, r.maxValue)); - return 0; - case TBM_SETRANGEMAX: - r.maxValue = static_cast(lParam); - if (slider) - slider->SetRange(r.minValue, std::max(r.minValue, r.maxValue)); - return 0; - case TBM_GETRANGEMIN: - return slider ? slider->GetMin() : r.minValue; - case TBM_GETRANGEMAX: - return slider ? slider->GetMax() : r.maxValue; - case TBM_SETTICFREQ: - if (slider) - slider->SetTickFreq(std::max(1, static_cast(wParam))); - return 0; - case TBM_SETPAGESIZE: { - int old = slider ? slider->GetPageSize() : 0; - if (slider) - slider->SetPageSize(static_cast(lParam)); - return old; - } - case TBM_GETPAGESIZE: - return slider ? slider->GetPageSize() : 0; - case TBM_SETLINESIZE: { - int old = slider ? slider->GetLineSize() : 0; - if (slider) - slider->SetLineSize(static_cast(lParam)); - return old; - } - case TBM_GETLINESIZE: - return slider ? slider->GetLineSize() : 1; - case TBM_SETTIC: - if (slider) - slider->SetTick(static_cast(lParam)); - return TRUE; - case TBM_CLEARTICS: - if (slider) - slider->ClearTicks(); - return 0; - case TBM_GETNUMTICS: - return 2; - case TBM_SETSEL: - case TBM_SETSELSTART: - case TBM_SETSELEND: - case TBM_CLEARSEL: - return 0; - case UDM_SETRANGE: - r.maxValue = static_cast(LOWORD(lParam)); - r.minValue = static_cast(HIWORD(lParam)); - return 0; - case UDM_SETRANGE32: - r.minValue = static_cast(wParam); - r.maxValue = static_cast(lParam); - return 0; - case UDM_GETRANGE: - return MAKELRESULT(r.maxValue, r.minValue); - case UDM_GETRANGE32: - if (wParam) - *reinterpret_cast(wParam) = r.minValue; - if (lParam) - *reinterpret_cast(lParam) = r.maxValue; - return 0; - case UDM_SETPOS: - case UDM_SETPOS32: { - int old = r.pos; - r.pos = msg == UDM_SETPOS ? static_cast(LOWORD(lParam)) : static_cast(lParam); - UpdateSpinBuddy(w, st); - return old; } - case UDM_GETPOS: - return MAKELRESULT(ReadSpinBuddy(st), 0); - case UDM_GETPOS32: - if (lParam) - *reinterpret_cast(lParam) = FALSE; - return ReadSpinBuddy(st); - case UDM_SETBUDDY: { - CWnd* old = st.buddy; - st.buddy = CWnd::FromHandle(reinterpret_cast(wParam)); - return reinterpret_cast(old ? old->m_hWnd : nullptr); - } - case UDM_GETBUDDY: - return reinterpret_cast(st.buddy ? st.buddy->m_hWnd : nullptr); - case UDM_SETBASE: - case UDM_SETACCEL: - return TRUE; - case UDM_GETBASE: - return 10; - case SBM_SETPOS: - if (auto* sb = wxDynamicCast(w, wxScrollBar)) { - int old = sb->GetThumbPosition(); - sb->SetThumbPosition(static_cast(wParam) - r.minValue); - return old + r.minValue; + if (st.kind == ControlKind::Slider) { + switch (msg) { + case TBM_GETPOS: + return slider ? slider->GetValue() : r.pos; + case TBM_SETPOS: + r.pos = static_cast(lParam); + if (slider) + slider->SetValue(r.pos); + return 0; + case TBM_SETRANGE: + r.minValue = LOWORD(lParam); + r.maxValue = HIWORD(lParam); + if (slider) + slider->SetRange(r.minValue, std::max(r.minValue, r.maxValue)); + return 0; + case TBM_SETRANGEMIN: + r.minValue = static_cast(lParam); + if (slider) + slider->SetRange(r.minValue, std::max(r.minValue, r.maxValue)); + return 0; + case TBM_SETRANGEMAX: + r.maxValue = static_cast(lParam); + if (slider) + slider->SetRange(r.minValue, std::max(r.minValue, r.maxValue)); + return 0; + case TBM_GETRANGEMIN: + return slider ? slider->GetMin() : r.minValue; + case TBM_GETRANGEMAX: + return slider ? slider->GetMax() : r.maxValue; + case TBM_SETTICFREQ: + if (slider) + slider->SetTickFreq(std::max(1, static_cast(wParam))); + return 0; + case TBM_SETPAGESIZE: { + int old = slider ? slider->GetPageSize() : 0; + if (slider) + slider->SetPageSize(static_cast(lParam)); + return old; } - return 0; - case SBM_GETPOS: - if (auto* sb = wxDynamicCast(w, wxScrollBar)) - return sb->GetThumbPosition() + r.minValue; - return 0; - case SBM_SETRANGE: - if (auto* sb = wxDynamicCast(w, wxScrollBar)) { + case TBM_GETPAGESIZE: + return slider ? slider->GetPageSize() : 0; + case TBM_SETLINESIZE: { + int old = slider ? slider->GetLineSize() : 0; + if (slider) + slider->SetLineSize(static_cast(lParam)); + return old; + } + case TBM_GETLINESIZE: + return slider ? slider->GetLineSize() : 1; + case TBM_SETTIC: + if (slider) + slider->SetTick(static_cast(lParam)); + return TRUE; + case TBM_CLEARTICS: + if (slider) + slider->ClearTicks(); + return 0; + case TBM_GETNUMTICS: + return 2; + case TBM_SETSEL: + case TBM_SETSELSTART: + case TBM_SETSELEND: + case TBM_CLEARSEL: + return 0; + default: + break; + } + } + if (st.kind == ControlKind::Spin) { + switch (msg) { + case UDM_SETRANGE: + r.maxValue = static_cast(LOWORD(lParam)); + r.minValue = static_cast(HIWORD(lParam)); + return 0; + case UDM_SETRANGE32: r.minValue = static_cast(wParam); r.maxValue = static_cast(lParam); - sb->SetScrollbar(sb->GetThumbPosition(), 1, std::max(1, r.maxValue - r.minValue + 1), std::max(1, (r.maxValue - r.minValue) / 10)); + return 0; + case UDM_GETRANGE: + return MAKELRESULT(r.maxValue, r.minValue); + case UDM_GETRANGE32: + if (wParam) + *reinterpret_cast(wParam) = r.minValue; + if (lParam) + *reinterpret_cast(lParam) = r.maxValue; + return 0; + case UDM_SETPOS: + case UDM_SETPOS32: { + int old = r.pos; + r.pos = msg == UDM_SETPOS ? static_cast(LOWORD(lParam)) : static_cast(lParam); + UpdateSpinBuddy(w, st); + return old; + } + case UDM_GETPOS: + return MAKELRESULT(ReadSpinBuddy(st), 0); + case UDM_GETPOS32: + if (lParam) + *reinterpret_cast(lParam) = FALSE; + return ReadSpinBuddy(st); + case UDM_SETBUDDY: { + CWnd* old = st.buddy; + st.buddy = CWnd::FromHandle(reinterpret_cast(wParam)); + return reinterpret_cast(old ? old->m_hWnd : nullptr); + } + case UDM_GETBUDDY: + return reinterpret_cast(st.buddy ? st.buddy->m_hWnd : nullptr); + case UDM_SETBASE: + case UDM_SETACCEL: + return TRUE; + case UDM_GETBASE: + return 10; + default: + break; + } + } + if (st.kind == ControlKind::ScrollBar) { + switch (msg) { + case SBM_SETPOS: + if (auto* sb = wxDynamicCast(w, wxScrollBar)) { + int old = sb->GetThumbPosition(); + sb->SetThumbPosition(static_cast(wParam) - r.minValue); + return old + r.minValue; + } + return 0; + case SBM_GETPOS: + if (auto* sb = wxDynamicCast(w, wxScrollBar)) + return sb->GetThumbPosition() + r.minValue; + return 0; + case SBM_SETRANGE: + if (auto* sb = wxDynamicCast(w, wxScrollBar)) { + r.minValue = static_cast(wParam); + r.maxValue = static_cast(lParam); + sb->SetScrollbar(sb->GetThumbPosition(), 1, std::max(1, r.maxValue - r.minValue + 1), std::max(1, (r.maxValue - r.minValue) / 10)); + } + return 0; + case SBM_GETRANGE: + if (wParam) + *reinterpret_cast(wParam) = r.minValue; + if (lParam) + *reinterpret_cast(lParam) = r.maxValue; + return 0; + default: + break; } - return 0; - case SBM_GETRANGE: - if (wParam) - *reinterpret_cast(wParam) = r.minValue; - if (lParam) - *reinterpret_cast(lParam) = r.maxValue; - return 0; - default: - handled = false; - return 0; } + handled = false; + return 0; } LRESULT TabProc(wxWindow* w, WindowState& st, UINT msg, WPARAM wParam, LPARAM lParam, bool& handled) { diff --git a/port/mfcwx/src/internal.h b/port/mfcwx/src/internal.h index b6a0cdbc7..2606a4c53 100644 --- a/port/mfcwx/src/internal.h +++ b/port/mfcwx/src/internal.h @@ -109,8 +109,11 @@ struct WindowState { bool destroying = false; bool destroyNotified = false; bool hooked = false; + bool dropBound = false; bool settingText = false; // suppresses change notifications for programmatic updates int defaultButtonId = 0; // dialogs: DM_SETDEFID + int checkState = 0; // BM_SETCHECK state of buttons + void* image = nullptr; // STM_SETIMAGE / STM_SETICON handle wxString text; // caption of custom-drawn windows std::function customPaint; std::vector> itemData; // unused by most kinds diff --git a/port/mfcwx/src/winapi.cpp b/port/mfcwx/src/winapi.cpp new file mode 100644 index 000000000..e3ccf3e79 --- /dev/null +++ b/port/mfcwx/src/winapi.cpp @@ -0,0 +1,1683 @@ +#include "globalmem.h" +#include "windows_impl.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace mfcwx { + +wxWindow* LogicalParent(wxWindow* w) { + if (!w) + return nullptr; + WindowState* st = GetState(w); + if (st && st->owner) + return ToWx(st->owner); + for (wxWindow* p = w->GetParent(); p; p = p->GetParent()) + if (IsManagedWindow(p)) + return p; + return nullptr; +} + +void DestroyWxWindow(wxWindow* w) { + WindowState* st = GetState(w); + if (st) { + st->destroyNotified = true; + CWnd* p = st->permanent; + st->permanent = nullptr; + if (p) { + p->m_hWnd = nullptr; + p->PostNcDestroy(); + } + } + if (w->IsTopLevel()) + w->Hide(); + w->Destroy(); +} + +namespace { + +const wxCursor* g_requestedCursor = nullptr; +bool g_cursorRequested = false; +const wxCursor* g_currentCursor = nullptr; + +wxWindow* ManagedAncestor(wxWindow* w) { + while (w && !IsManagedWindow(w)) + w = w->GetParent(); + return w; +} + +std::vector ManagedChildren(wxWindow* parent) { + std::vector out; + if (!parent) + return out; + for (wxWindow* c : parent->GetChildren()) + if (IsManagedWindow(c)) + out.push_back(c); + return out; +} + +wxWindow* FindChildById(wxWindow* parent, int id) { + if (!parent) + return nullptr; + for (wxWindow* c : parent->GetChildren()) { + WindowState* st = GetState(c); + if (st && st->winId == id) + return c; + } + for (wxWindow* c : parent->GetChildren()) { + if (IsManagedWindow(c) && !c->IsTopLevel()) + continue; + if (c->IsTopLevel()) + continue; + if (wxWindow* r = FindChildById(c, id)) + return r; + } + return nullptr; +} + +void MarkDefault(HWND hWnd, UINT msg) { + CurrentMessage* cm = GetCurrentMessageSlot(); + if (cm && cm->event && cm->msg.hwnd == hWnd && cm->msg.message == msg) + cm->defaultCalled = true; +} + +bool IsDialogWindow(wxWindow* w) { + WindowState* st = GetState(w); + return st && st->kind == ControlKind::Dialog; +} + +} // namespace + +void ResetCursorRequest() { + g_requestedCursor = nullptr; + g_cursorRequested = true; +} + +const wxCursor* RequestedCursor() { + g_cursorRequested = false; + return g_requestedCursor; +} + +LRESULT DefaultWindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { + wxWindow* w = ToWx(hWnd); + WindowState* st = GetState(w); + if (!st) + return 0; + bool handled = false; + LRESULT r = ControlWindowProc(w, *st, msg, wParam, lParam, handled); + if (handled) + return r; + switch (msg) { + case WM_SETTEXT: + SetControlText(w, ToWx(reinterpret_cast(lParam))); + return TRUE; + case WM_GETTEXT: { + if (!wParam || !lParam) + return 0; + std::string a = FromWx(GetControlText(w)); + size_t n = std::min(a.size(), static_cast(wParam) - 1); + memcpy(reinterpret_cast(lParam), a.data(), n); + reinterpret_cast(lParam)[n] = 0; + return static_cast(n); + } + case WM_GETTEXTLENGTH: + return static_cast(FromWx(GetControlText(w)).size()); + case WM_SETFONT: { + st->font = reinterpret_cast(wParam); + wxFont f = st->font ? FontFromHandle(st->font) : wxFont(); + if (f.IsOk()) { + w->SetFont(f); + if (st->kind == ControlKind::Static) + SetControlText(w, st->text); + } + if (lParam) + w->Refresh(); + return 0; + } + case WM_GETFONT: + return reinterpret_cast(st->font ? st->font : DefaultGuiFont()); + case WM_SETREDRAW: + if (wParam) { + if (w->IsFrozen()) + w->Thaw(); + } else if (!w->IsFrozen()) { + w->Freeze(); + } + st->redraw = wParam != 0; + return 0; + case WM_CLOSE: + if (IsDialogWindow(w)) { + wxWindow* cancel = FindChildById(w, IDCANCEL); + DispatchMessageTo(hWnd, WM_COMMAND, MAKEWPARAM(IDCANCEL, BN_CLICKED), reinterpret_cast(ToHwnd(cancel))); + } else if (st->permanent) { + st->permanent->DestroyWindow(); + } else { + DestroyWxWindow(w); + } + return 0; + case WM_SETICON: + if (auto* tlw = wxDynamicCast(w, wxTopLevelWindow)) { + auto* icon = reinterpret_cast(lParam); + if (icon && icon->IsOk()) + tlw->SetIcon(*icon); + } + return 0; + case WM_GETICON: + return 0; + case WM_HELP: { + wxWindow* parent = LogicalParent(w); + if (parent && !w->IsTopLevel()) + return DispatchMessageTo(ToHwnd(parent), WM_HELP, wParam, lParam); + return TRUE; + } + case WM_SYSCOMMAND: + switch (wParam & 0xFFF0) { + case SC_CLOSE: + DispatchMessageTo(hWnd, WM_CLOSE, 0, 0); + break; + case SC_MINIMIZE: + if (auto* tlw = wxDynamicCast(w, wxTopLevelWindow)) + tlw->Iconize(); + break; + case SC_MAXIMIZE: + if (auto* tlw = wxDynamicCast(w, wxTopLevelWindow)) + tlw->Maximize(); + break; + case SC_RESTORE: + if (auto* tlw = wxDynamicCast(w, wxTopLevelWindow)) + tlw->Restore(); + break; + default: + break; + } + return 0; + case WM_ERASEBKGND: + MarkDefault(hWnd, msg); + return 1; + case DM_GETDEFID: + return st->defaultButtonId ? MAKELRESULT(st->defaultButtonId, DC_HASDEFID) : 0; + case DM_SETDEFID: { + st->defaultButtonId = static_cast(wParam); + if (auto* b = wxDynamicCast(FindChildById(w, st->defaultButtonId), wxButton)) + b->SetDefault(); + return TRUE; + } + case WM_NEXTDLGCTL: + if (lParam) + ::SetFocus(reinterpret_cast(wParam)); + else + w->Navigate(wParam ? wxNavigationKeyEvent::IsBackward : wxNavigationKeyEvent::IsForward); + return 0; + case WM_CTLCOLORMSGBOX: + case WM_CTLCOLOREDIT: + case WM_CTLCOLORLISTBOX: + case WM_CTLCOLORBTN: + case WM_CTLCOLORDLG: + case WM_CTLCOLORSCROLLBAR: + case WM_CTLCOLORSTATIC: + case WM_CTLCOLOR: + return 0; + case WM_ACTIVATE: + case WM_SETCURSOR: + case WM_MOUSEWHEEL: + default: + MarkDefault(hWnd, msg); + return 0; + } +} + +} // namespace mfcwx + +using namespace mfcwx; + +// --------------------------------------------------------------------------------------------- +// Messages + +LRESULT SendMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) { + if (!hWnd) + return 0; + return OnMain([&]() -> LRESULT { return DispatchMessageTo(hWnd, Msg, wParam, lParam); }); +} + +LRESULT SendMessageA(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) { return SendMessage(hWnd, Msg, wParam, lParam); } + +BOOL PostMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) { + if (!hWnd) + return Msg == WM_QUIT ? (PostToMainThread([] { if (wxTheApp) wxTheApp->ExitMainLoop(); }), TRUE) : FALSE; + PostToMainThread([=] { + if (IsManagedWindow(ToWx(hWnd))) + DispatchMessageTo(hWnd, Msg, wParam, lParam); + }); + return TRUE; +} + +BOOL PostMessageA(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) { return PostMessage(hWnd, Msg, wParam, lParam); } + +BOOL SendNotifyMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) { + if (IsMainThread()) { + SendMessage(hWnd, Msg, wParam, lParam); + return TRUE; + } + return PostMessage(hWnd, Msg, wParam, lParam); +} + +BOOL PostThreadMessage(DWORD, UINT Msg, WPARAM, LPARAM) { + if (Msg == WM_QUIT) + PostToMainThread([] { if (wxTheApp) wxTheApp->ExitMainLoop(); }); + return TRUE; +} + +void PostQuitMessage(int) { + PostToMainThread([] { + if (wxTheApp) + wxTheApp->ExitMainLoop(); + }); +} + +UINT RegisterWindowMessage(const char* lpString) { + static std::mutex m; + static std::map ids; + std::lock_guard lock(m); + std::string key = lpString ? lpString : ""; + auto it = ids.find(key); + if (it != ids.end()) + return it->second; + UINT id = 0xC000 + static_cast(ids.size()); + ids[key] = id; + return id; +} + +UINT RegisterWindowMessageA(const char* lpString) { return RegisterWindowMessage(lpString); } + +BOOL PeekMessage(LPMSG lpMsg, HWND, UINT, UINT, UINT) { + if (lpMsg) + memset(lpMsg, 0, sizeof *lpMsg); + if (IsMainThread()) { + wxEventLoopBase* loop = wxEventLoopBase::GetActive(); + if (loop && !loop->IsYielding()) + loop->YieldFor(wxEVT_CATEGORY_ALL); + } + return FALSE; +} + +BOOL GetMessage(LPMSG lpMsg, HWND hWnd, UINT a, UINT b) { + PeekMessage(lpMsg, hWnd, a, b, PM_REMOVE); + wxMilliSleep(10); + return TRUE; +} + +BOOL TranslateMessage(const MSG*) { return FALSE; } + +LRESULT DispatchMessage(const MSG* lpMsg) { + if (!lpMsg || !lpMsg->hwnd) + return 0; + return SendMessage(lpMsg->hwnd, lpMsg->message, lpMsg->wParam, lpMsg->lParam); +} + +BOOL WaitMessage() { + PeekMessage(nullptr, nullptr, 0, 0, PM_NOREMOVE); + return TRUE; +} + +DWORD MsgWaitForMultipleObjects(DWORD nCount, const HANDLE* pHandles, BOOL fWaitAll, DWORD dwMilliseconds, DWORD) { + DWORD start = GetTickCount(); + for (;;) { + DWORD r = WaitForMultipleObjects(nCount, pHandles, fWaitAll, 0); + if (r != WAIT_TIMEOUT) + return r; + if (IsMainThread()) + return WAIT_OBJECT_0 + nCount; + if (dwMilliseconds != INFINITE && GetTickCount() - start >= dwMilliseconds) + return WAIT_TIMEOUT; + wxMilliSleep(5); + } +} + +LRESULT DefWindowProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) { + return OnMain([&]() -> LRESULT { return DefaultWindowProc(hWnd, Msg, wParam, lParam); }); +} + +LRESULT CallWindowProc(WNDPROC lpPrevWndFunc, HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) { + return lpPrevWndFunc ? lpPrevWndFunc(hWnd, Msg, wParam, lParam) : DefWindowProc(hWnd, Msg, wParam, lParam); +} + +// --------------------------------------------------------------------------------------------- +// Window state + +BOOL IsWindow(HWND hWnd) { + return hWnd && OnMain([&]() -> BOOL { return IsManagedWindow(ToWx(hWnd)); }); +} + +BOOL IsWindowVisible(HWND hWnd) { + return IsWindow(hWnd) && OnMain([&]() -> BOOL { return ToWx(hWnd)->IsShown(); }); +} + +BOOL IsWindowEnabled(HWND hWnd) { + return IsWindow(hWnd) && OnMain([&]() -> BOOL { return ToWx(hWnd)->IsThisEnabled(); }); +} + +BOOL EnableWindow(HWND hWnd, BOOL bEnable) { + if (!IsWindow(hWnd)) + return FALSE; + return OnMain([&]() -> BOOL { + wxWindow* w = ToWx(hWnd); + BOOL wasDisabled = !w->IsThisEnabled(); + w->Enable(bEnable != FALSE); + WindowState* st = GetState(w); + if (st) { + st->style = bEnable ? (st->style & ~WS_DISABLED) : (st->style | WS_DISABLED); + if (st->kind == ControlKind::OwnerDrawButton || st->kind == ControlKind::Generic) + w->Refresh(); + } + return wasDisabled; + }); +} + +BOOL ShowWindow(HWND hWnd, int nCmdShow) { + if (!IsWindow(hWnd)) + return FALSE; + return OnMain([&]() -> BOOL { + wxWindow* w = ToWx(hWnd); + BOOL wasVisible = w->IsShown(); + auto* tlw = wxDynamicCast(w, wxTopLevelWindow); + switch (nCmdShow) { + case SW_HIDE: + w->Hide(); + break; + case SW_MINIMIZE: + case SW_SHOWMINIMIZED: + case SW_SHOWMINNOACTIVE: + case SW_FORCEMINIMIZE: + if (tlw) + tlw->Iconize(); + break; + case SW_MAXIMIZE: + if (tlw) + tlw->Maximize(); + w->Show(); + break; + case SW_RESTORE: + if (tlw) + tlw->Restore(); + w->Show(); + break; + default: + w->Show(); + if (tlw && (nCmdShow == SW_SHOW || nCmdShow == SW_SHOWNORMAL)) + tlw->Raise(); + break; + } + WindowState* st = GetState(w); + if (st) + st->style = w->IsShown() ? (st->style | WS_VISIBLE) : (st->style & ~WS_VISIBLE); + return wasVisible; + }); +} + +BOOL IsIconic(HWND hWnd) { + return IsWindow(hWnd) && OnMain([&]() -> BOOL { + auto* tlw = wxDynamicCast(ToWx(hWnd), wxTopLevelWindow); + return tlw && tlw->IsIconized(); + }); +} + +BOOL IsZoomed(HWND hWnd) { + return IsWindow(hWnd) && OnMain([&]() -> BOOL { + auto* tlw = wxDynamicCast(ToWx(hWnd), wxTopLevelWindow); + return tlw && tlw->IsMaximized(); + }); +} + +BOOL IsChild(HWND hWndParent, HWND hWnd) { + if (!hWndParent || !hWnd) + return FALSE; + return OnMain([&]() -> BOOL { + for (wxWindow* w = ToWx(hWnd); w && !w->IsTopLevel(); w = w->GetParent()) + if (w->GetParent() == ToWx(hWndParent)) + return TRUE; + return FALSE; + }); +} + +HWND GetParent(HWND hWnd) { + if (!IsWindow(hWnd)) + return nullptr; + return OnMain([&]() -> HWND { return ToHwnd(LogicalParent(ToWx(hWnd))); }); +} + +HWND SetParent(HWND hWndChild, HWND hWndNewParent) { + HWND old = GetParent(hWndChild); + if (IsWindow(hWndChild) && IsWindow(hWndNewParent)) + OnMain([&] { ToWx(hWndChild)->Reparent(ToWx(hWndNewParent)); }); + return old; +} + +HWND GetWindow(HWND hWnd, UINT uCmd) { + if (!IsWindow(hWnd)) + return nullptr; + return OnMain([&]() -> HWND { + wxWindow* w = ToWx(hWnd); + if (uCmd == GW_CHILD) { + auto children = ManagedChildren(w); + return children.empty() ? nullptr : ToHwnd(children.front()); + } + if (uCmd == GW_OWNER) + return w->IsTopLevel() ? ToHwnd(ManagedAncestor(w->GetParent())) : nullptr; + auto siblings = ManagedChildren(w->GetParent()); + auto it = std::find(siblings.begin(), siblings.end(), w); + if (it == siblings.end()) + return nullptr; + switch (uCmd) { + case GW_HWNDFIRST: + return ToHwnd(siblings.front()); + case GW_HWNDLAST: + return ToHwnd(siblings.back()); + case GW_HWNDNEXT: + return ++it == siblings.end() ? nullptr : ToHwnd(*it); + case GW_HWNDPREV: + return it == siblings.begin() ? nullptr : ToHwnd(*--it); + default: + return nullptr; + } + }); +} + +HWND GetNextWindow(HWND hWnd, UINT wCmd) { return GetWindow(hWnd, wCmd); } +HWND GetTopWindow(HWND hWnd) { return GetWindow(hWnd, GW_CHILD); } +HWND GetDesktopWindow() { return nullptr; } + +HWND GetActiveWindow() { + return OnMain([]() -> HWND { return ToHwnd(ManagedAncestor(wxGetActiveWindow())); }); +} + +HWND SetActiveWindow(HWND hWnd) { + HWND old = GetActiveWindow(); + if (IsWindow(hWnd)) + OnMain([&] { wxGetTopLevelParent(ToWx(hWnd))->Raise(); }); + return old; +} + +HWND GetForegroundWindow() { return GetActiveWindow(); } + +BOOL SetForegroundWindow(HWND hWnd) { + SetActiveWindow(hWnd); + return TRUE; +} + +HWND GetFocus() { + return OnMain([]() -> HWND { return ToHwnd(ManagedAncestor(wxWindow::FindFocus())); }); +} + +HWND SetFocus(HWND hWnd) { + HWND old = GetFocus(); + if (IsWindow(hWnd)) + OnMain([&] { ToWx(hWnd)->SetFocus(); }); + return old; +} + +HWND GetCapture() { + return OnMain([]() -> HWND { return ToHwnd(ManagedAncestor(wxWindow::GetCapture())); }); +} + +HWND SetCapture(HWND hWnd) { + HWND old = GetCapture(); + if (IsWindow(hWnd)) + OnMain([&] { + if (!ToWx(hWnd)->HasCapture()) + ToWx(hWnd)->CaptureMouse(); + }); + return old; +} + +BOOL ReleaseCapture() { + OnMain([] { + if (wxWindow* w = wxWindow::GetCapture()) + w->ReleaseMouse(); + }); + return TRUE; +} + +HWND FindWindow(const char*, const char* lpWindowName) { + if (!lpWindowName) + return nullptr; + return OnMain([&]() -> HWND { + wxString name = ToWx(lpWindowName); + for (wxWindow* w : wxTopLevelWindows) + if (IsManagedWindow(w) && w->GetLabel() == name) + return ToHwnd(w); + return nullptr; + }); +} + +HWND FindWindowA(const char* lpClassName, const char* lpWindowName) { return FindWindow(lpClassName, lpWindowName); } + +HWND WindowFromPoint(POINT Point) { + return OnMain([&]() -> HWND { return ToHwnd(ManagedAncestor(wxFindWindowAtPoint(wxPoint(Point.x, Point.y)))); }); +} + +HWND ChildWindowFromPoint(HWND hWndParent, POINT Point) { + if (!IsWindow(hWndParent)) + return nullptr; + return OnMain([&]() -> HWND { + wxWindow* parent = ToWx(hWndParent); + for (wxWindow* c : ManagedChildren(parent)) + if (c->GetRect().Contains(Point.x, Point.y)) + return ToHwnd(c); + return wxRect(parent->GetClientSize()).Contains(Point.x, Point.y) ? hWndParent : nullptr; + }); +} + +BOOL DestroyWindow(HWND hWnd) { + if (!IsWindow(hWnd)) + return FALSE; + return OnMain([&]() -> BOOL { + CWnd* p = PermanentWnd(ToWx(hWnd)); + if (p) + return p->DestroyWindow(); + DestroyWxWindow(ToWx(hWnd)); + return TRUE; + }); +} + +BOOL CloseWindow(HWND hWnd) { return ShowWindow(hWnd, SW_MINIMIZE); } + +BOOL BringWindowToTop(HWND hWnd) { + if (!IsWindow(hWnd)) + return FALSE; + OnMain([&] { ToWx(hWnd)->Raise(); }); + return TRUE; +} + +BOOL SetWindowText(HWND hWnd, const char* lpString) { + return IsWindow(hWnd) && SendMessage(hWnd, WM_SETTEXT, 0, reinterpret_cast(lpString ? lpString : "")); +} + +BOOL SetWindowTextA(HWND hWnd, const char* lpString) { return SetWindowText(hWnd, lpString); } + +int GetWindowText(HWND hWnd, char* lpString, int nMaxCount) { + if (!lpString || nMaxCount <= 0) + return 0; + lpString[0] = 0; + if (!IsWindow(hWnd)) + return 0; + return static_cast(SendMessage(hWnd, WM_GETTEXT, static_cast(nMaxCount), reinterpret_cast(lpString))); +} + +int GetWindowTextA(HWND hWnd, char* lpString, int nMaxCount) { return GetWindowText(hWnd, lpString, nMaxCount); } + +int GetWindowTextLength(HWND hWnd) { + return IsWindow(hWnd) ? static_cast(SendMessage(hWnd, WM_GETTEXTLENGTH, 0, 0)) : 0; +} + +int GetClassName(HWND hWnd, char* lpClassName, int nMaxCount) { + if (!lpClassName || nMaxCount <= 0) + return 0; + const char* name = ""; + if (IsWindow(hWnd)) { + switch (GetState(ToWx(hWnd))->kind) { + case ControlKind::PushButton: case ControlKind::CheckBox: case ControlKind::RadioButton: + case ControlKind::OwnerDrawButton: case ControlKind::GroupBox: name = "Button"; break; + case ControlKind::Edit: name = "Edit"; break; + case ControlKind::RichEdit: name = "RichEdit20A"; break; + case ControlKind::Static: case ControlKind::StaticBitmap: case ControlKind::StaticFrame: name = "Static"; break; + case ControlKind::ComboBox: case ControlKind::ComboList: name = "ComboBox"; break; + case ControlKind::ListBox: name = "ListBox"; break; + case ControlKind::ScrollBar: name = "ScrollBar"; break; + case ControlKind::Progress: name = "msctls_progress32"; break; + case ControlKind::Slider: name = "msctls_trackbar32"; break; + case ControlKind::Spin: name = "msctls_updown32"; break; + case ControlKind::ListView: name = "SysListView32"; break; + case ControlKind::TreeView: name = "SysTreeView32"; break; + case ControlKind::Tab: name = "SysTabControl32"; break; + case ControlKind::Scintilla: name = "Scintilla"; break; + case ControlKind::Dialog: name = "#32770"; break; + default: name = "AfxWnd"; break; + } + } + strncpy(lpClassName, name, static_cast(nMaxCount) - 1); + lpClassName[nMaxCount - 1] = 0; + return static_cast(strlen(lpClassName)); +} + +LONG_PTR GetWindowLongPtr(HWND hWnd, int nIndex) { + if (!IsWindow(hWnd)) + return 0; + return OnMain([&]() -> LONG_PTR { + wxWindow* w = ToWx(hWnd); + WindowState* st = GetState(w); + switch (nIndex) { + case GWL_STYLE: + return static_cast(CurrentStyle(w)); + case GWL_EXSTYLE: + return static_cast(st->exStyle); + case GWL_ID: + return st->winId; + case GWL_USERDATA: + return st->userData; + case GWL_HWNDPARENT: + return reinterpret_cast(ToHwnd(LogicalParent(w))); + default: + return 0; + } + }); +} + +LONG_PTR SetWindowLongPtr(HWND hWnd, int nIndex, LONG_PTR dwNewLong) { + if (!IsWindow(hWnd)) + return 0; + return OnMain([&]() -> LONG_PTR { + wxWindow* w = ToWx(hWnd); + WindowState* st = GetState(w); + LONG_PTR old = GetWindowLongPtr(hWnd, nIndex); + switch (nIndex) { + case GWL_STYLE: + ApplyStyle(w, CurrentStyle(w), static_cast(dwNewLong)); + break; + case GWL_EXSTYLE: + st->exStyle = static_cast(dwNewLong); + break; + case GWL_ID: + st->winId = static_cast(dwNewLong); + w->SetId(ToWxId(st->winId)); + break; + case GWL_USERDATA: + st->userData = dwNewLong; + break; + default: + break; + } + return old; + }); +} + +LONG GetWindowLong(HWND hWnd, int nIndex) { return static_cast(GetWindowLongPtr(hWnd, nIndex)); } +LONG SetWindowLong(HWND hWnd, int nIndex, LONG dwNewLong) { return static_cast(SetWindowLongPtr(hWnd, nIndex, dwNewLong)); } + +// --------------------------------------------------------------------------------------------- +// Geometry and painting + +namespace { + +void ToRect(const wxRect& r, LPRECT out) { + out->left = r.x; + out->top = r.y; + out->right = r.x + r.width; + out->bottom = r.y + r.height; +} + +} // namespace + +BOOL GetWindowRect(HWND hWnd, LPRECT lpRect) { + if (!IsWindow(hWnd) || !lpRect) + return FALSE; + OnMain([&] { ToRect(ToWx(hWnd)->GetScreenRect(), lpRect); }); + return TRUE; +} + +BOOL GetClientRect(HWND hWnd, LPRECT lpRect) { + if (!IsWindow(hWnd) || !lpRect) + return FALSE; + OnMain([&] { ToRect(wxRect(ToWx(hWnd)->GetClientSize()), lpRect); }); + return TRUE; +} + +BOOL ClientToScreen(HWND hWnd, LPPOINT lpPoint) { + if (!IsWindow(hWnd) || !lpPoint) + return FALSE; + OnMain([&] { + wxPoint p = ToWx(hWnd)->ClientToScreen(wxPoint(lpPoint->x, lpPoint->y)); + lpPoint->x = p.x; + lpPoint->y = p.y; + }); + return TRUE; +} + +BOOL ScreenToClient(HWND hWnd, LPPOINT lpPoint) { + if (!IsWindow(hWnd) || !lpPoint) + return FALSE; + OnMain([&] { + wxPoint p = ToWx(hWnd)->ScreenToClient(wxPoint(lpPoint->x, lpPoint->y)); + lpPoint->x = p.x; + lpPoint->y = p.y; + }); + return TRUE; +} + +int MapWindowPoints(HWND hWndFrom, HWND hWndTo, LPPOINT lpPoints, UINT cPoints) { + int dx = 0, dy = 0; + POINT origin = {0, 0}; + if (hWndFrom) { + ClientToScreen(hWndFrom, &origin); + dx += origin.x; + dy += origin.y; + } + if (hWndTo) { + POINT o = {0, 0}; + ClientToScreen(hWndTo, &o); + dx -= o.x; + dy -= o.y; + } + for (UINT i = 0; i < cPoints; ++i) { + lpPoints[i].x += dx; + lpPoints[i].y += dy; + } + return MAKELONG(static_cast(dx), static_cast(dy)); +} + +BOOL MoveWindow(HWND hWnd, int X, int Y, int nWidth, int nHeight, BOOL bRepaint) { + if (!IsWindow(hWnd)) + return FALSE; + OnMain([&] { + wxWindow* w = ToWx(hWnd); + w->SetSize(X, Y, nWidth, nHeight, wxSIZE_ALLOW_MINUS_ONE); + if (bRepaint) + w->Refresh(); + }); + return TRUE; +} + +BOOL SetWindowPos(HWND hWnd, HWND hWndInsertAfter, int X, int Y, int cx, int cy, UINT uFlags) { + if (!IsWindow(hWnd)) + return FALSE; + OnMain([&] { + wxWindow* w = ToWx(hWnd); + wxRect r = w->GetRect(); + if (!(uFlags & SWP_NOMOVE)) + r.SetPosition(wxPoint(X, Y)); + if (!(uFlags & SWP_NOSIZE)) + r.SetSize(wxSize(cx, cy)); + if (!(uFlags & (SWP_NOMOVE | SWP_NOSIZE)) || (uFlags & (SWP_NOMOVE | SWP_NOSIZE)) != (SWP_NOMOVE | SWP_NOSIZE)) + w->SetSize(r, wxSIZE_ALLOW_MINUS_ONE); + if (!(uFlags & SWP_NOZORDER)) { + if (hWndInsertAfter == HWND_TOPMOST || hWndInsertAfter == HWND_NOTOPMOST) { + if (auto* tlw = wxDynamicCast(w, wxTopLevelWindow)) { + long style = tlw->GetWindowStyleFlag(); + tlw->SetWindowStyleFlag(hWndInsertAfter == HWND_TOPMOST ? (style | wxSTAY_ON_TOP) : (style & ~wxSTAY_ON_TOP)); + } + } else if (hWndInsertAfter == HWND_BOTTOM) { + w->Lower(); + } else if (hWndInsertAfter == HWND_TOP) { + w->Raise(); + } + } + if (uFlags & SWP_SHOWWINDOW) + w->Show(); + if (uFlags & SWP_HIDEWINDOW) + w->Hide(); + if (uFlags & SWP_FRAMECHANGED) + w->Refresh(); + }); + return TRUE; +} + +BOOL InvalidateRect(HWND hWnd, const RECT* lpRect, BOOL bErase) { + if (!IsWindow(hWnd)) + return FALSE; + OnMain([&] { + wxWindow* w = ToWx(hWnd); + if (lpRect) { + wxRect r(lpRect->left, lpRect->top, lpRect->right - lpRect->left, lpRect->bottom - lpRect->top); + w->RefreshRect(r, bErase != FALSE); + } else { + w->Refresh(bErase != FALSE); + } + if (!w->IsTopLevel()) + ApplyCtlColor(w); + }); + return TRUE; +} + +BOOL ValidateRect(HWND, const RECT*) { return TRUE; } + +BOOL UpdateWindow(HWND hWnd) { + if (!IsWindow(hWnd)) + return FALSE; + OnMain([&] { ToWx(hWnd)->Update(); }); + return TRUE; +} + +BOOL RedrawWindow(HWND hWnd, const RECT* lprcUpdate, HRGN, UINT) { + if (!IsWindow(hWnd)) + return FALSE; + InvalidateRect(hWnd, lprcUpdate, TRUE); + return UpdateWindow(hWnd); +} + +UINT_PTR SetTimer(HWND hWnd, UINT_PTR nIDEvent, UINT uElapse, TIMERPROC lpTimerFunc) { + if (!IsWindow(hWnd)) + return 0; + return OnMain([&]() -> UINT_PTR { return StartTimer(ToWx(hWnd), nIDEvent, uElapse, lpTimerFunc); }); +} + +BOOL KillTimer(HWND hWnd, UINT_PTR uIDEvent) { + if (!hWnd) + return FALSE; + return OnMain([&]() -> BOOL { return IsManagedWindow(ToWx(hWnd)) && StopTimer(ToWx(hWnd), uIDEvent); }); +} + +// --------------------------------------------------------------------------------------------- +// Dialog items + +HWND GetDlgItem(HWND hDlg, int nIDDlgItem) { + if (!IsWindow(hDlg)) + return nullptr; + return OnMain([&]() -> HWND { return ToHwnd(FindChildById(ToWx(hDlg), nIDDlgItem)); }); +} + +int GetDlgCtrlID(HWND hWnd) { return static_cast(GetWindowLongPtr(hWnd, GWL_ID)); } + +UINT GetDlgItemInt(HWND hDlg, int nIDDlgItem, BOOL* lpTranslated, BOOL bSigned) { + char buf[64]; + if (lpTranslated) + *lpTranslated = FALSE; + if (!GetDlgItem(hDlg, nIDDlgItem)) + return 0; + GetDlgItemText(hDlg, nIDDlgItem, buf, sizeof buf); + char* end = nullptr; + const char* p = buf; + while (*p == ' ') + ++p; + long long v = bSigned ? strtoll(p, &end, 10) : static_cast(strtoull(p, &end, 10)); + if (end == p || (!bSigned && *p == '-')) + return 0; + while (end && *end == ' ') + ++end; + if (end && *end) + return 0; + if (lpTranslated) + *lpTranslated = TRUE; + return static_cast(v); +} + +BOOL SetDlgItemInt(HWND hDlg, int nIDDlgItem, UINT uValue, BOOL bSigned) { + char buf[32]; + if (bSigned) + snprintf(buf, sizeof buf, "%d", static_cast(uValue)); + else + snprintf(buf, sizeof buf, "%u", uValue); + return SetDlgItemText(hDlg, nIDDlgItem, buf); +} + +UINT GetDlgItemText(HWND hDlg, int nIDDlgItem, char* lpString, int cchMax) { + if (lpString && cchMax > 0) + lpString[0] = 0; + HWND h = GetDlgItem(hDlg, nIDDlgItem); + return h ? static_cast(GetWindowText(h, lpString, cchMax)) : 0; +} + +UINT GetDlgItemTextA(HWND hDlg, int nIDDlgItem, char* lpString, int cchMax) { + return GetDlgItemText(hDlg, nIDDlgItem, lpString, cchMax); +} + +BOOL SetDlgItemText(HWND hDlg, int nIDDlgItem, const char* lpString) { + HWND h = GetDlgItem(hDlg, nIDDlgItem); + return h ? SetWindowText(h, lpString) : FALSE; +} + +BOOL SetDlgItemTextA(HWND hDlg, int nIDDlgItem, const char* lpString) { return SetDlgItemText(hDlg, nIDDlgItem, lpString); } + +BOOL CheckDlgButton(HWND hDlg, int nIDButton, UINT uCheck) { + HWND h = GetDlgItem(hDlg, nIDButton); + if (!h) + return FALSE; + SendMessage(h, BM_SETCHECK, uCheck, 0); + return TRUE; +} + +UINT IsDlgButtonChecked(HWND hDlg, int nIDButton) { + HWND h = GetDlgItem(hDlg, nIDButton); + return h ? static_cast(SendMessage(h, BM_GETCHECK, 0, 0)) : 0; +} + +BOOL CheckRadioButton(HWND hDlg, int nIDFirstButton, int nIDLastButton, int nIDCheckButton) { + for (int id = nIDFirstButton; id <= nIDLastButton; ++id) { + HWND h = GetDlgItem(hDlg, id); + if (h) + SendMessage(h, BM_SETCHECK, id == nIDCheckButton ? BST_CHECKED : BST_UNCHECKED, 0); + } + return TRUE; +} + +LRESULT SendDlgItemMessage(HWND hDlg, int nIDDlgItem, UINT Msg, WPARAM wParam, LPARAM lParam) { + HWND h = GetDlgItem(hDlg, nIDDlgItem); + return h ? SendMessage(h, Msg, wParam, lParam) : 0; +} + +BOOL EndDialog(HWND hDlg, INT_PTR nResult) { + CWnd* p = CWnd::FromHandlePermanent(hDlg); + if (p && p->IsKindOf(RUNTIME_CLASS(CDialog))) { + static_cast(p)->EndDialog(static_cast(nResult)); + return TRUE; + } + return FALSE; +} + +namespace { + +HWND NextTabItem(HWND hDlg, HWND hCtl, BOOL bPrevious, bool groupOnly) { + if (!IsWindow(hDlg)) + return nullptr; + return OnMain([&]() -> HWND { + auto children = ManagedChildren(ToWx(hDlg)); + if (children.empty()) + return nullptr; + auto eligible = [&](wxWindow* c) { + WindowState* st = GetState(c); + return c->IsShown() && c->IsEnabled() && st && (groupOnly || (st->style & WS_TABSTOP)); + }; + size_t n = children.size(); + size_t start = 0; + for (size_t i = 0; i < n; ++i) + if (ToHwnd(children[i]) == hCtl) + start = i; + for (size_t k = 1; k <= n; ++k) { + size_t i = bPrevious ? (start + n - k) % n : (start + k) % n; + if (groupOnly) { + WindowState* st = GetState(children[i]); + if (!bPrevious && st && (st->style & WS_GROUP)) + break; + } + if (eligible(children[i])) + return ToHwnd(children[i]); + } + return hCtl; + }); +} + +} // namespace + +HWND GetNextDlgTabItem(HWND hDlg, HWND hCtl, BOOL bPrevious) { return NextTabItem(hDlg, hCtl, bPrevious, false); } +HWND GetNextDlgGroupItem(HWND hDlg, HWND hCtl, BOOL bPrevious) { return NextTabItem(hDlg, hCtl, bPrevious, true); } + +BOOL MapDialogRect(HWND hDlg, LPRECT lpRect) { + if (!IsWindow(hDlg) || !lpRect) + return FALSE; + OnMain([&] { + wxRect r = DialogUnitsToPixels(ToWx(hDlg), lpRect->left, lpRect->top, lpRect->right - lpRect->left, + lpRect->bottom - lpRect->top); + ToRect(r, lpRect); + }); + return TRUE; +} + +LONG GetDialogBaseUnits() { + return OnMain([]() -> LONG { + wxRect r = DialogUnitsToPixels(nullptr, 0, 0, 4, 8); + return MAKELONG(r.width, r.height); + }); +} + +int MessageBox(HWND hWnd, const char* lpText, const char* lpCaption, UINT uType) { + return OnMain([&]() -> int { return ShowMessageBox(hWnd ? ToWx(hWnd) : nullptr, lpText, lpCaption, uType); }); +} + +int MessageBoxA(HWND hWnd, const char* lpText, const char* lpCaption, UINT uType) { + return MessageBox(hWnd, lpText, lpCaption, uType); +} + +BOOL WinHelp(HWND, const char*, UINT uCommand, ULONG_PTR dwData) { + if (CWinApp* app = AfxGetApp()) + app->WinHelp(dwData, uCommand); + return TRUE; +} + +BOOL WinHelpA(HWND hWndMain, const char* lpszHelp, UINT uCommand, ULONG_PTR dwData) { + return WinHelp(hWndMain, lpszHelp, uCommand, dwData); +} + +// --------------------------------------------------------------------------------------------- +// System information, keyboard, cursors, icons + +int GetSystemMetrics(int nIndex) { + return OnMain([&]() -> int { + wxDisplay display(static_cast(0)); + wxRect screen = display.GetGeometry(); + wxRect work = display.GetClientArea(); + switch (nIndex) { + case SM_CXSCREEN: case SM_CXVIRTUALSCREEN: return screen.width; + case SM_CYSCREEN: case SM_CYVIRTUALSCREEN: return screen.height; + case SM_CXFULLSCREEN: case SM_CXMAXIMIZED: return work.width; + case SM_CYFULLSCREEN: case SM_CYMAXIMIZED: return work.height; + case SM_CXVSCROLL: return wxSystemSettings::GetMetric(wxSYS_VSCROLL_X); + case SM_CYHSCROLL: return wxSystemSettings::GetMetric(wxSYS_HSCROLL_Y); + case SM_CYVSCROLL: case SM_CXHSCROLL: return 16; + case SM_CYCAPTION: return std::max(20, wxSystemSettings::GetMetric(wxSYS_CAPTION_Y)); + case SM_CYMENU: return std::max(20, wxSystemSettings::GetMetric(wxSYS_MENU_Y)); + case SM_CXBORDER: case SM_CYBORDER: return 1; + case SM_CXEDGE: case SM_CYEDGE: return 2; + case SM_CXDLGFRAME: case SM_CYDLGFRAME: case SM_CXFRAME: case SM_CYFRAME: return 4; + case SM_CXICON: case SM_CYICON: case SM_CXCURSOR: case SM_CYCURSOR: return 32; + case SM_CXSMICON: case SM_CYSMICON: return 16; + case SM_CXDRAG: case SM_CYDRAG: return 4; + case SM_CXMIN: return 112; + case SM_CYMIN: return 27; + default: return 0; + } + }); +} + +BOOL SystemParametersInfo(UINT uiAction, UINT, PVOID pvParam, UINT) { + if (uiAction == SPI_GETWORKAREA && pvParam) { + OnMain([&] { ToRect(wxDisplay(static_cast(0)).GetClientArea(), static_cast(pvParam)); }); + return TRUE; + } + return FALSE; +} + +BOOL SystemParametersInfoA(UINT uiAction, UINT uiParam, PVOID pvParam, UINT fWinIni) { + return SystemParametersInfo(uiAction, uiParam, pvParam, fWinIni); +} + +SHORT GetKeyState(int nVirtKey) { + return OnMain([&]() -> SHORT { + wxMouseState ms = wxGetMouseState(); + bool down = false; + switch (nVirtKey) { + case VK_LBUTTON: down = ms.LeftIsDown(); break; + case VK_RBUTTON: down = ms.RightIsDown(); break; + case VK_MBUTTON: down = ms.MiddleIsDown(); break; + case VK_SHIFT: case VK_LSHIFT: case VK_RSHIFT: down = ms.ShiftDown(); break; + case VK_CONTROL: case VK_LCONTROL: case VK_RCONTROL: down = ms.ControlDown(); break; + case VK_MENU: case VK_LMENU: case VK_RMENU: down = ms.AltDown(); break; + default: { + int key = WxKeyFromVirtual(static_cast(nVirtKey)); + if (key == WXK_CAPITAL || key == WXK_NUMLOCK || key == WXK_SCROLL) + return wxGetKeyState(static_cast(key)) ? 1 : 0; + down = key && wxGetKeyState(static_cast(key)); + break; + } + } + return down ? static_cast(0x8000) : 0; + }); +} + +SHORT GetAsyncKeyState(int vKey) { return GetKeyState(vKey); } + +BOOL GetKeyboardState(BYTE* lpKeyState) { + if (!lpKeyState) + return FALSE; + for (int i = 0; i < 256; ++i) + lpKeyState[i] = (i == VK_SHIFT || i == VK_CONTROL || i == VK_MENU || i == VK_LBUTTON || i == VK_RBUTTON) + ? static_cast((GetKeyState(i) & 0x8000) ? 0x80 : 0) + : 0; + return TRUE; +} + +BOOL GetCursorPos(LPPOINT lpPoint) { + if (!lpPoint) + return FALSE; + OnMain([&] { + wxPoint p = wxGetMousePosition(); + lpPoint->x = p.x; + lpPoint->y = p.y; + }); + return TRUE; +} + +BOOL SetCursorPos(int, int) { return FALSE; } + +namespace { + +wxCursor* StockCursor(int id) { + static std::map cache; + auto it = cache.find(id); + if (it != cache.end()) + return it->second; + wxStockCursor stock = wxCURSOR_ARROW; + switch (id) { + case 32513: stock = wxCURSOR_IBEAM; break; + case 32514: stock = wxCURSOR_WAIT; break; + case 32515: stock = wxCURSOR_CROSS; break; + case 32516: stock = wxCURSOR_POINT_RIGHT; break; + case 32640: case 32646: stock = wxCURSOR_SIZING; break; + case 32642: stock = wxCURSOR_SIZENWSE; break; + case 32643: stock = wxCURSOR_SIZENESW; break; + case 32644: stock = wxCURSOR_SIZEWE; break; + case 32645: stock = wxCURSOR_SIZENS; break; + case 32648: stock = wxCURSOR_NO_ENTRY; break; + case 32649: stock = wxCURSOR_HAND; break; + case 32650: stock = wxCURSOR_ARROWWAIT; break; + case 32651: stock = wxCURSOR_QUESTION_ARROW; break; + default: break; + } + auto* c = new wxCursor(stock); + cache[id] = c; + return c; +} + +wxCursor* ResourceCursor(const ResRef& ref) { + static std::map cache; + std::string key = ref.name.empty() ? std::to_string(ref.id) : ref.name; + auto it = cache.find(key); + if (it != cache.end()) + return it->second; + wxCursor c = LoadCursorResource(ref); + wxCursor* p = c.IsOk() ? new wxCursor(c) : nullptr; + cache[key] = p; + return p; +} + +wxIcon* ResourceIcon(const ResRef& ref) { + static std::map cache; + std::string key = ref.name.empty() ? std::to_string(ref.id) : ref.name; + auto it = cache.find(key); + if (it != cache.end()) + return it->second; + wxIcon icon; + if (ref.name.empty() && ref.id >= 32512 && ref.id <= 32517) { + wxArtID art = wxART_INFORMATION; + if (ref.id == 32513) + art = wxART_ERROR; + else if (ref.id == 32514) + art = wxART_QUESTION; + else if (ref.id == 32515) + art = wxART_WARNING; + icon = wxArtProvider::GetIcon(art, wxART_MESSAGE_BOX); + } else { + icon = LoadIconResource(ref); + } + wxIcon* p = icon.IsOk() ? new wxIcon(icon) : nullptr; + cache[key] = p; + return p; +} + +} // namespace + +HCURSOR SetCursor(HCURSOR hCursor) { + HCURSOR old = reinterpret_cast(const_cast(g_currentCursor)); + auto* c = reinterpret_cast(hCursor); + g_currentCursor = c; + if (g_cursorRequested) { + g_requestedCursor = c; + return old; + } + OnMain([&] { wxSetCursor(c ? *c : wxNullCursor); }); + return old; +} + +HCURSOR GetCursor() { return reinterpret_cast(const_cast(g_currentCursor)); } +int ShowCursor(BOOL bShow) { return bShow ? 0 : -1; } + +HCURSOR LoadCursor(HINSTANCE hInstance, const char* lpCursorName) { + ResRef ref = ResRef::From(lpCursorName); + return OnMain([&]() -> HCURSOR { + if (!hInstance && ref.name.empty()) + return reinterpret_cast(StockCursor(ref.id)); + if (wxCursor* c = ResourceCursor(ref)) + return reinterpret_cast(c); + return ref.name.empty() && ref.id >= 32512 ? reinterpret_cast(StockCursor(ref.id)) : nullptr; + }); +} + +HCURSOR LoadCursorA(HINSTANCE hInstance, const char* lpCursorName) { return LoadCursor(hInstance, lpCursorName); } + +HCURSOR LoadCursorFromFile(const char* lpFileName) { + return OnMain([&]() -> HCURSOR { + wxImage image; + wxLogNull noLog; + if (!image.LoadFile(NativePathWx(lpFileName))) + return nullptr; + return reinterpret_cast(new wxCursor(image)); + }); +} + +HICON LoadIcon(HINSTANCE, const char* lpIconName) { + ResRef ref = ResRef::From(lpIconName); + return OnMain([&]() -> HICON { return reinterpret_cast(ResourceIcon(ref)); }); +} + +HICON LoadIconA(HINSTANCE hInstance, const char* lpIconName) { return LoadIcon(hInstance, lpIconName); } +BOOL DestroyIcon(HICON) { return TRUE; } +BOOL DestroyCursor(HCURSOR) { return TRUE; } + +HANDLE LoadImage(HINSTANCE hInst, const char* name, UINT type, int cx, int cy, UINT fuLoad) { + return OnMain([&]() -> HANDLE { + if (fuLoad & LR_LOADFROMFILE) { + wxImage image; + wxLogNull noLog; + if (!image.LoadFile(NativePathWx(name))) + return nullptr; + if (cx > 0 && cy > 0) + image.Rescale(cx, cy, wxIMAGE_QUALITY_HIGH); + if (type == IMAGE_BITMAP) + return CreateBitmapHandle(wxBitmap(image)); + if (type == IMAGE_CURSOR) + return new wxCursor(image); + wxIcon icon; + icon.CopyFromBitmap(wxBitmap(image)); + return new wxIcon(icon); + } + ResRef ref = ResRef::From(name); + if (type == IMAGE_BITMAP) { + wxBitmap bmp = LoadBitmapResource(ref); + if (!bmp.IsOk()) + return nullptr; + if (cx > 0 && cy > 0 && (bmp.GetWidth() != cx || bmp.GetHeight() != cy)) + bmp = wxBitmap(bmp.ConvertToImage().Rescale(cx, cy, wxIMAGE_QUALITY_HIGH)); + return CreateBitmapHandle(bmp); + } + if (type == IMAGE_CURSOR) + return LoadCursor(hInst, name); + return LoadIcon(hInst, name); + }); +} + +HANDLE LoadImageA(HINSTANCE hInst, const char* name, UINT type, int cx, int cy, UINT fuLoad) { + return LoadImage(hInst, name, type, cx, cy, fuLoad); +} + +HBITMAP LoadBitmap(HINSTANCE, const char* lpBitmapName) { + ResRef ref = ResRef::From(lpBitmapName); + return OnMain([&]() -> HBITMAP { + wxBitmap bmp = LoadBitmapResource(ref); + return bmp.IsOk() ? CreateBitmapHandle(bmp) : nullptr; + }); +} + +HBITMAP LoadBitmapA(HINSTANCE hInstance, const char* lpBitmapName) { return LoadBitmap(hInstance, lpBitmapName); } + +HACCEL LoadAccelerators(HINSTANCE, const char* lpTableName) { + const rc::AccelTable* t = FindAccelTable(ResRef::From(lpTableName)); + return reinterpret_cast(const_cast(t)); +} + +int TranslateAccelerator(HWND, HACCEL, LPMSG) { return 0; } + +// --------------------------------------------------------------------------------------------- +// Clipboard + +namespace { + +struct ClipboardState { + bool open = false; + bool emptied = false; + std::vector owned; + std::map formats; +}; + +ClipboardState& Clip() { + static ClipboardState s; + return s; +} + +std::string FormatName(UINT format) { + for (auto& f : Clip().formats) + if (f.second == format) + return f.first; + return "CrypTool.Format." + std::to_string(format); +} + +} // namespace + +BOOL OpenClipboard(HWND) { + return OnMain([]() -> BOOL { + if (Clip().open) + return FALSE; + if (!wxTheClipboard->Open()) + return FALSE; + Clip().open = true; + Clip().emptied = false; + return TRUE; + }); +} + +BOOL CloseClipboard() { + return OnMain([]() -> BOOL { + if (!Clip().open) + return FALSE; + wxTheClipboard->Flush(); + wxTheClipboard->Close(); + Clip().open = false; + for (HGLOBAL h : Clip().owned) + GlobalFree(h); + Clip().owned.clear(); + return TRUE; + }); +} + +BOOL EmptyClipboard() { + return OnMain([]() -> BOOL { + if (!Clip().open) + return FALSE; + wxTheClipboard->Clear(); + Clip().emptied = true; + return TRUE; + }); +} + +HANDLE SetClipboardData(UINT uFormat, HANDLE hMem) { + return OnMain([&]() -> HANDLE { + if (!Clip().open || !hMem) + return nullptr; + const char* data = static_cast(GlobalData(hMem)); + size_t size = GlobalDataSize(hMem); + if (!data) + return nullptr; + if (uFormat == CF_TEXT || uFormat == CF_OEMTEXT) { + size_t len = strnlen(data, size); + wxString text = ToWx(data, static_cast(len)); + text.Replace("\r\n", "\n"); + wxTheClipboard->AddData(new wxTextDataObject(text)); + } else { + auto* obj = new wxCustomDataObject(wxDataFormat(wxString::FromUTF8(FormatName(uFormat)))); + obj->SetData(size, data); + wxTheClipboard->AddData(obj); + } + Clip().owned.push_back(hMem); + return hMem; + }); +} + +HANDLE GetClipboardData(UINT uFormat) { + return OnMain([&]() -> HANDLE { + if (!Clip().open) + return nullptr; + if (uFormat == CF_TEXT || uFormat == CF_OEMTEXT || uFormat == CF_UNICODETEXT) { + if (!wxTheClipboard->IsSupported(wxDF_UNICODETEXT) && !wxTheClipboard->IsSupported(wxDF_TEXT)) + return nullptr; + wxTextDataObject obj; + if (!wxTheClipboard->GetData(obj)) + return nullptr; + wxString text = obj.GetText(); + text.Replace("\r\n", "\n"); + text.Replace("\n", "\r\n"); + std::string a = FromWx(text); + HGLOBAL h = GlobalFromData(a.c_str(), a.size() + 1); + Clip().owned.push_back(h); + return h; + } + wxDataFormat fmt(wxString::FromUTF8(FormatName(uFormat))); + if (!wxTheClipboard->IsSupported(fmt)) + return nullptr; + wxCustomDataObject obj(fmt); + if (!wxTheClipboard->GetData(obj)) + return nullptr; + HGLOBAL h = GlobalFromData(obj.GetData(), obj.GetSize()); + Clip().owned.push_back(h); + return h; + }); +} + +BOOL IsClipboardFormatAvailable(UINT format) { + return OnMain([&]() -> BOOL { + bool opened = false; + if (!Clip().open) { + if (!wxTheClipboard->Open()) + return FALSE; + opened = true; + } + bool r; + if (format == CF_TEXT || format == CF_OEMTEXT || format == CF_UNICODETEXT) + r = wxTheClipboard->IsSupported(wxDF_UNICODETEXT) || wxTheClipboard->IsSupported(wxDF_TEXT); + else + r = wxTheClipboard->IsSupported(wxDataFormat(wxString::FromUTF8(FormatName(format)))); + if (opened) + wxTheClipboard->Close(); + return r; + }); +} + +UINT RegisterClipboardFormat(const char* lpszFormat) { + std::string name = lpszFormat ? lpszFormat : ""; + auto it = Clip().formats.find(name); + if (it != Clip().formats.end()) + return it->second; + UINT id = 0xC100 + static_cast(Clip().formats.size()); + Clip().formats[name] = id; + return id; +} + +UINT RegisterClipboardFormatA(const char* lpszFormat) { return RegisterClipboardFormat(lpszFormat); } + +// --------------------------------------------------------------------------------------------- +// Drag and drop of files + +namespace { + +struct DropList { + std::vector files; +}; + +} // namespace + +UINT DragQueryFile(HDROP hDrop, UINT iFile, char* lpszFile, UINT cch) { + auto* list = reinterpret_cast(hDrop); + if (!list) + return 0; + if (iFile == 0xFFFFFFFF) + return static_cast(list->files.size()); + if (iFile >= list->files.size()) + return 0; + const std::string& f = list->files[iFile]; + if (!lpszFile) + return static_cast(f.size()); + size_t n = std::min(f.size(), cch ? cch - 1 : 0); + memcpy(lpszFile, f.data(), n); + if (cch) + lpszFile[n] = 0; + return static_cast(n); +} + +UINT DragQueryFileA(HDROP hDrop, UINT iFile, char* lpszFile, UINT cch) { return DragQueryFile(hDrop, iFile, lpszFile, cch); } + +void DragFinish(HDROP hDrop) { delete reinterpret_cast(hDrop); } + +void DragAcceptFiles(HWND hWnd, BOOL fAccept) { + if (!IsWindow(hWnd)) + return; + OnMain([&] { + wxWindow* w = ToWx(hWnd); + w->DragAcceptFiles(fAccept != FALSE); + WindowState* st = GetState(w); + if (!fAccept || !st || st->dropBound) + return; + st->dropBound = true; + w->Bind(wxEVT_DROP_FILES, [w](wxDropFilesEvent& e) { + auto* list = new DropList; + for (int i = 0; i < e.GetNumberOfFiles(); ++i) + list->files.push_back(AppPath(e.GetFiles()[i].utf8_str())); + DispatchMessageTo(ToHwnd(w), WM_DROPFILES, reinterpret_cast(list), 0); + }); + }); +} + +// --------------------------------------------------------------------------------------------- +// Window classes, carets, locale, common dialogs + +namespace { + +std::map& Classes() { + static std::map classes; + return classes; +} + +} // namespace + +LONG GetClassLong(HWND, int) { return 0; } + +LONG SetClassLong(HWND hWnd, int nIndex, LONG dwNewLong) { + return static_cast(SetClassLongPtr(hWnd, nIndex, dwNewLong)); +} + +ULONG_PTR SetClassLongPtr(HWND hWnd, int nIndex, LONG_PTR dwNewLong) { + if (nIndex == GCL_HCURSOR && IsWindow(hWnd)) { + auto* c = reinterpret_cast(dwNewLong); + OnMain([&] { ToWx(hWnd)->SetCursor(c ? *c : wxNullCursor); }); + } + return 0; +} + +BOOL GetClassInfo(HINSTANCE, const char* lpClassName, WNDCLASS* lpWndClass) { + if (!lpWndClass) + return FALSE; + auto it = Classes().find(lpClassName && !IS_INTRESOURCE(lpClassName) ? lpClassName : ""); + if (it == Classes().end()) + return FALSE; + *lpWndClass = it->second; + return TRUE; +} + +ATOM RegisterClass(const WNDCLASS* lpWndClass) { + if (!lpWndClass || !lpWndClass->lpszClassName) + return 0; + Classes()[lpWndClass->lpszClassName] = *lpWndClass; + return static_cast(Classes().size()); +} + +BOOL UnregisterClass(const char* lpClassName, HINSTANCE) { + return lpClassName && Classes().erase(lpClassName) ? TRUE : FALSE; +} + +HWND CreateWindowEx(DWORD dwExStyle, const char* lpClassName, const char* lpWindowName, DWORD dwStyle, int X, int Y, + int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, HINSTANCE, LPVOID lpParam) { + return OnMain([&]() -> HWND { + CREATESTRUCT cs; + memset(&cs, 0, sizeof cs); + cs.dwExStyle = dwExStyle; + cs.lpszClass = lpClassName; + cs.lpszName = lpWindowName; + cs.style = static_cast(dwStyle); + cs.x = X; + cs.y = Y; + cs.cx = nWidth; + cs.cy = nHeight; + cs.hwndParent = hWndParent; + cs.hMenu = hMenu; + cs.lpCreateParams = lpParam; + wxWindow* w = CreateWindowForClass(nullptr, cs); + if (w && (dwStyle & WS_VISIBLE)) + w->Show(); + return ToHwnd(w); + }); +} + +HWND CreateWindow(const char* lpClassName, const char* lpWindowName, DWORD dwStyle, int X, int Y, int nWidth, + int nHeight, HWND hWndParent, HMENU hMenu, HINSTANCE hInstance, LPVOID lpParam) { + return CreateWindowEx(0, lpClassName, lpWindowName, dwStyle, X, Y, nWidth, nHeight, hWndParent, hMenu, hInstance, + lpParam); +} + +BOOL DestroyCaret() { return TRUE; } +BOOL CreateCaret(HWND, HBITMAP, int, int) { return TRUE; } +BOOL ShowCaret(HWND) { return TRUE; } +BOOL HideCaret(HWND) { return TRUE; } +BOOL SetCaretPos(int, int) { return TRUE; } + +int GetLocaleInfo(LCID, DWORD LCType, char* lpLCData, int cchData) { + std::string value; + switch (LCType) { + case LOCALE_IMEASURE: value = "0"; break; + case LOCALE_SDECIMAL: value = "."; break; + case LOCALE_STHOUSAND: value = ","; break; + case LOCALE_IDEFAULTANSICODEPAGE: value = std::to_string(GetAnsiCodePage()); break; + case LOCALE_SABBREVLANGNAME: value = GetResourceLanguage(); break; + default: value = ""; break; + } + if (!lpLCData || cchData == 0) + return static_cast(value.size() + 1); + strncpy(lpLCData, value.c_str(), static_cast(cchData) - 1); + lpLCData[cchData - 1] = 0; + return static_cast(strlen(lpLCData) + 1); +} + +int GetLocaleInfoA(LCID Locale, DWORD LCType, char* lpLCData, int cchData) { + return GetLocaleInfo(Locale, LCType, lpLCData, cchData); +} + +namespace { + +// "Text (*.txt)\0*.txt\0All\0*.*\0\0" -> "Text (*.txt)|*.txt|All|*.*" +wxString FilterFromDoubleNul(const char* f) { + wxString out; + if (!f) + return wxString("*"); + while (*f) { + if (!out.empty()) + out += '|'; + out += ToWx(f); + f += strlen(f) + 1; + } + return out; +} + +BOOL FileDialog(OPENFILENAME* ofn, bool open) { + if (!ofn) + return FALSE; + return OnMain([&]() -> BOOL { + long style = open ? wxFD_OPEN : wxFD_SAVE; + if (open && (ofn->Flags & OFN_FILEMUSTEXIST)) + style |= wxFD_FILE_MUST_EXIST; + if (!open && (ofn->Flags & OFN_OVERWRITEPROMPT)) + style |= wxFD_OVERWRITE_PROMPT; + if (ofn->Flags & OFN_ALLOWMULTISELECT) + style |= wxFD_MULTIPLE; + wxString initial = ofn->lpstrFile ? ToWx(ofn->lpstrFile) : wxString(); + wxString dir = ofn->lpstrInitialDir ? NativePathWx(ofn->lpstrInitialDir) : wxString(); + wxWindow* parent = ofn->hwndOwner ? ToWx(ofn->hwndOwner) : MainWxWindow(); + wxFileDialog dlg(parent, ofn->lpstrTitle ? ToWx(ofn->lpstrTitle) : wxString(), dir, + wxFileName(NativePathWx(ofn->lpstrFile ? ofn->lpstrFile : "")).GetFullName(), + FilterFromDoubleNul(ofn->lpstrFilter), style); + if (ofn->nFilterIndex > 0) + dlg.SetFilterIndex(static_cast(ofn->nFilterIndex) - 1); + if (dlg.ShowModal() != wxID_OK) + return FALSE; + std::string path = AppPath(dlg.GetPath().utf8_str()); + if (!open && ofn->lpstrDefExt && *ofn->lpstrDefExt && path.find('.', path.rfind('\\') + 1) == std::string::npos) + path += std::string(".") + ofn->lpstrDefExt; + if (ofn->lpstrFile && ofn->nMaxFile) { + strncpy(ofn->lpstrFile, path.c_str(), ofn->nMaxFile - 1); + ofn->lpstrFile[ofn->nMaxFile - 1] = 0; + } + size_t slash = path.rfind('\\'); + ofn->nFileOffset = static_cast(slash == std::string::npos ? 0 : slash + 1); + size_t dot = path.rfind('.'); + ofn->nFileExtension = static_cast(dot == std::string::npos || dot < ofn->nFileOffset ? path.size() : dot + 1); + if (ofn->lpstrFileTitle && ofn->nMaxFileTitle) { + strncpy(ofn->lpstrFileTitle, path.c_str() + ofn->nFileOffset, ofn->nMaxFileTitle - 1); + ofn->lpstrFileTitle[ofn->nMaxFileTitle - 1] = 0; + } + ofn->nFilterIndex = static_cast(dlg.GetFilterIndex() + 1); + (void)initial; + return TRUE; + }); +} + +} // namespace + +BOOL GetOpenFileName(OPENFILENAME* lpofn) { return FileDialog(lpofn, true); } +BOOL GetSaveFileName(OPENFILENAME* lpofn) { return FileDialog(lpofn, false); } +BOOL GetOpenFileNameA(OPENFILENAME* lpofn) { return FileDialog(lpofn, true); } +BOOL GetSaveFileNameA(OPENFILENAME* lpofn) { return FileDialog(lpofn, false); } + +BOOL ChooseColor(CHOOSECOLOR* lpcc) { + if (!lpcc) + return FALSE; + return OnMain([&]() -> BOOL { + wxColourData data; + data.SetColour(ToWxColour(lpcc->rgbResult)); + wxColourDialog dlg(lpcc->hwndOwner ? ToWx(lpcc->hwndOwner) : MainWxWindow(), &data); + if (dlg.ShowModal() != wxID_OK) + return FALSE; + lpcc->rgbResult = FromWxColour(dlg.GetColourData().GetColour()); + return TRUE; + }); +} + +BOOL FlashWindow(HWND hWnd, BOOL) { + if (IsWindow(hWnd)) + OnMain([&] { + if (auto* tlw = wxDynamicCast(wxGetTopLevelParent(ToWx(hWnd)), wxTopLevelWindow)) + tlw->RequestUserAttention(); + }); + return TRUE; +} diff --git a/port/mfcwx/src/windows_impl.h b/port/mfcwx/src/windows_impl.h index b4bece8f3..079c56a6c 100644 --- a/port/mfcwx/src/windows_impl.h +++ b/port/mfcwx/src/windows_impl.h @@ -71,6 +71,13 @@ void ApplyCtlColor(wxWindow* control); wxString GetControlText(wxWindow* window); void SetControlText(wxWindow* window, const wxString& text); +// scintilla.cpp: wxStyledTextCtrl behind the Scintilla message API (ANSI text, ANSI positions) +void BindScintillaEvents(wxWindow* window); +LRESULT ScintillaWindowProc(wxWindow* window, UINT msg, WPARAM wParam, LPARAM lParam, bool& handled); + +// gdi.cpp: an HBITMAP owning a copy of bmp +HBITMAP CreateBitmapHandle(const wxBitmap& bmp); + // wnd.cpp / winapi.cpp void DestroyWxWindow(wxWindow* window); // The parent in the Win32 sense (skips internal wx containers). diff --git a/port/mfcwx/src/wnd.cpp b/port/mfcwx/src/wnd.cpp index 5113bae1c..d5dffec44 100644 --- a/port/mfcwx/src/wnd.cpp +++ b/port/mfcwx/src/wnd.cpp @@ -1,6 +1,7 @@ #include "internal.h" #include "windows_impl.h" +#include #include #include @@ -1678,13 +1679,12 @@ int CWnd::GetScrollLimit(int nBar) { void CWnd::ShowScrollBar(UINT nBar, BOOL bShow) { OnMain([&] { - if (!m_hWnd) + if (!m_hWnd || bShow) return; - wxWindow* w = GetWx(); if (nBar == SB_HORZ || nBar == SB_BOTH) - w->ShowScrollbars(bShow ? wxSHOW_SB_ALWAYS : wxSHOW_SB_NEVER, w->HasScrollbar(wxVERTICAL) ? wxSHOW_SB_DEFAULT : wxSHOW_SB_NEVER); + GetWx()->SetScrollbar(wxHORIZONTAL, 0, 0, 0); if (nBar == SB_VERT || nBar == SB_BOTH) - w->ShowScrollbars(w->HasScrollbar(wxHORIZONTAL) ? wxSHOW_SB_DEFAULT : wxSHOW_SB_NEVER, bShow ? wxSHOW_SB_ALWAYS : wxSHOW_SB_NEVER); + GetWx()->SetScrollbar(wxVERTICAL, 0, 0, 0); }); } From c54f359b6cd28ebc0b3b942b83e83209ba2c219e Mon Sep 17 00:00:00 2001 From: Kamil Marczak Date: Tue, 6 Oct 2026 17:28:57 +0200 Subject: [PATCH 05/46] feat(mfcwx): add dialogs, DDX/DDV and common dialogs Dialog templates are instantiated with dialog units derived from the dialog font; CDialog supports modal and modeless use, IsDialogMessage keyboard handling and DLGINIT. Includes DDX/DDV, file, colour, font and find/replace dialogs and a simple property sheet. --- port/mfcwx/src/dialog.cpp | 1188 +++++++++++++++++++++++++++++++++ port/mfcwx/src/internal.h | 1 + port/mfcwx/src/windows_impl.h | 1 + 3 files changed, 1190 insertions(+) create mode 100644 port/mfcwx/src/dialog.cpp diff --git a/port/mfcwx/src/dialog.cpp b/port/mfcwx/src/dialog.cpp new file mode 100644 index 000000000..fc3b1616d --- /dev/null +++ b/port/mfcwx/src/dialog.cpp @@ -0,0 +1,1188 @@ +#include "windows_impl.h" + +#include "afxdlgs.h" + +#include +#include +#include +#include +#include +#include + +#include + +namespace mfcwx { + +// --------------------------------------------------------------------------------------------- +// Dialog units + +namespace { + +wxFont DialogFontFor(const rc::Dialog* tmpl) { + wxFont f = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); +#ifdef __WXOSX__ + f.SetPointSize(11); +#endif + if (tmpl && tmpl->fontName && tmpl->fontWeight >= FW_BOLD) + f.SetWeight(wxFONTWEIGHT_BOLD); + return f; +} + +wxSize BaseUnits(const wxFont& font) { + wxBitmap bmp(1, 1); + wxMemoryDC dc(bmp); + dc.SetFont(font); + wxCoord w = 0, h = 0; + dc.GetTextExtent("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", &w, &h); + int baseX = static_cast((w / 26 + 1) / 2); + return wxSize(std::max(4, baseX), std::max(8, static_cast(h))); +} + +} // namespace + +wxRect DialogUnitsToPixels(wxWindow* dialog, int x, int y, int cx, int cy) { + static wxSize defaultUnits = BaseUnits(DialogFontFor(nullptr)); + wxSize base = defaultUnits; + if (dialog) { + WindowState* st = GetState(dialog); + if (st && st->baseUnits.x > 0) + base = st->baseUnits; + } + return wxRect(MulDiv(x, base.x, 4), MulDiv(y, base.y, 8), MulDiv(cx, base.x, 4), MulDiv(cy, base.y, 8)); +} + +bool CreateDialogControls(wxWindow* parent, const rc::Dialog& tmpl) { + WindowState& ps = EnsureState(parent); + wxWindow* previous = nullptr; + for (int i = 0; i < tmpl.controlCount; ++i) { + const rc::Control& c = tmpl.controls[i]; + wxRect r = DialogUnitsToPixels(parent, c.x, c.y, c.cx, c.cy); + wxWindow* w = CreateControl(parent, c.cls, c.text, c.id, r, c.style, c.exStyle); + if (!w) + continue; + w->SetFont(parent->GetFont()); + WindowState* st = GetState(w); + if (st && st->kind == ControlKind::PushButton && (c.style & BS_TYPEMASK) == BS_DEFPUSHBUTTON) { + if (auto* b = wxDynamicCast(w, wxButton)) + b->SetDefault(); + ps.defaultButtonId = c.id; + } + if (st && st->kind == ControlKind::Spin && previous && (c.style & UDS_AUTOBUDDY)) { + st->buddy = WrapperFor(previous); + if (c.style & (UDS_ALIGNRIGHT | UDS_ALIGNLEFT)) { + wxRect br = previous->GetRect(); + int sw = std::max(16, w->GetBestSize().x); + if (c.style & UDS_ALIGNRIGHT) { + previous->SetSize(br.x, br.y, std::max(10, br.width - sw), br.height); + w->SetSize(br.x + std::max(10, br.width - sw), br.y, sw, br.height); + } else { + previous->SetSize(br.x + sw, br.y, std::max(10, br.width - sw), br.height); + w->SetSize(br.x, br.y, sw, br.height); + } + } + } + previous = w; + } + return true; +} + +// --------------------------------------------------------------------------------------------- +// Keyboard handling of dialogs (IsDialogMessage) + +bool PreTranslateKey(wxWindow* topLevel, wxKeyEvent& event, UINT message) { + wxWindow* focus = wxWindow::FindFocus(); + while (focus && !IsManagedWindow(focus)) + focus = focus->GetParent(); + MSG msg; + memset(&msg, 0, sizeof msg); + msg.hwnd = ToHwnd(focus ? focus : topLevel); + msg.message = message; + msg.wParam = VirtualKeyFromWx(event.GetKeyCode()); + msg.lParam = KeyLParam(event, false); + if (!msg.wParam) + return false; + for (wxWindow* w = focus ? focus : topLevel; w; w = w->GetParent()) { + CWnd* p = PermanentWnd(w); + if (p && p->PreTranslateMessage(&msg)) + return true; + if (w == topLevel) + break; + } + CWinApp* app = AfxGetApp(); + CWnd* main = app ? app->m_pMainWnd : nullptr; + if (main && main->m_hWnd && main->GetWx() != topLevel && main->PreTranslateMessage(&msg)) + return true; + return false; +} + +bool DialogKeyHook(wxWindow* dialog, wxKeyEvent& event) { + if (PreTranslateKey(dialog, event, event.AltDown() ? WM_SYSKEYDOWN : WM_KEYDOWN)) + return true; + int code = event.GetKeyCode(); + wxWindow* focus = wxWindow::FindFocus(); + WindowState* fs = nullptr; + for (wxWindow* f = focus; f && !fs; f = f->GetParent()) + fs = GetState(f); + if (code == WXK_ESCAPE) { + wxWindow* cancel = nullptr; + for (wxWindow* c : dialog->GetChildren()) { + WindowState* st = GetState(c); + if (st && st->winId == IDCANCEL) + cancel = c; + } + if (cancel && !cancel->IsEnabled()) + return true; + DispatchMessageTo(ToHwnd(dialog), WM_COMMAND, MAKEWPARAM(IDCANCEL, BN_CLICKED), reinterpret_cast(ToHwnd(cancel))); + return true; + } + if (code == WXK_RETURN || code == WXK_NUMPAD_ENTER) { + if (fs && (fs->kind == ControlKind::Edit || fs->kind == ControlKind::RichEdit) && (fs->style & ES_MULTILINE) && + (fs->style & ES_WANTRETURN)) + return false; + if (fs && (fs->kind == ControlKind::ListView || fs->kind == ControlKind::Scintilla || fs->kind == ControlKind::Generic)) + return false; + int id = IDOK; + wxWindow* target = nullptr; + if (fs && (fs->kind == ControlKind::PushButton || fs->kind == ControlKind::OwnerDrawButton)) { + id = fs->winId; + target = fs->window; + } else { + WindowState* ds = GetState(dialog); + if (ds && ds->defaultButtonId) + id = ds->defaultButtonId; + for (wxWindow* c : dialog->GetChildren()) { + WindowState* st = GetState(c); + if (st && st->winId == id) + target = c; + } + } + if (target && !target->IsEnabled()) + return true; + DispatchMessageTo(ToHwnd(dialog), WM_COMMAND, MAKEWPARAM(id, BN_CLICKED), reinterpret_cast(ToHwnd(target))); + return true; + } + if (code == WXK_F1) { + HELPINFO hi; + memset(&hi, 0, sizeof hi); + hi.cbSize = sizeof hi; + hi.iContextType = HELPINFO_WINDOW; + hi.iCtrlId = fs ? fs->winId : 0; + hi.hItemHandle = fs ? fs->window : nullptr; + wxPoint p = wxGetMousePosition(); + hi.MousePos.x = p.x; + hi.MousePos.y = p.y; + HWND target = fs ? ToHwnd(fs->window) : ToHwnd(dialog); + DispatchMessageTo(target, WM_HELP, 0, reinterpret_cast(&hi)); + return true; + } + return false; +} + +BOOL ApplyDlgInit(CWnd* pWnd, int dialogId) { + if (!pWnd || !pWnd->m_hWnd || !dialogId) + return TRUE; + for (const rc::DlgInitEntry* e : FindDlgInit(dialogId)) { + HWND h = ::GetDlgItem(pWnd->m_hWnd, e->controlId); + WindowState* st = h ? GetState(ToWx(h)) : nullptr; + if (!st) + continue; + std::string text = Utf8ToAnsi(e->text); + UINT msg = st->kind == ControlKind::ListBox ? LB_ADDSTRING : CB_ADDSTRING; + DispatchMessageTo(h, msg, 0, reinterpret_cast(text.c_str())); + } + return TRUE; +} + +namespace { + +long DialogWxStyle(DWORD style) { + long f = 0; + if (style & WS_CAPTION) + f |= wxCAPTION; + if (style & WS_SYSMENU) + f |= wxSYSTEM_MENU | wxCLOSE_BOX; + if (style & WS_THICKFRAME) + f |= wxRESIZE_BORDER; + if (style & WS_MINIMIZEBOX) + f |= wxMINIMIZE_BOX; + if (style & WS_MAXIMIZEBOX) + f |= wxMAXIMIZE_BOX; + if (!(style & WS_CAPTION)) + f |= wxBORDER_SIMPLE; + return f; +} + +class MfcDialog : public wxDialog { +public: + MfcDialog(wxWindow* parent, const wxString& title, long style) : wxDialog() { + SetExtraStyle(GetExtraStyle() | wxWS_EX_BLOCK_EVENTS); + Create(parent, wxID_ANY, title, wxDefaultPosition, wxDefaultSize, style); + Bind(wxEVT_CLOSE_WINDOW, [this](wxCloseEvent& e) { + if (IsManagedWindow(this)) { + e.Veto(); + DispatchMessageTo(ToHwnd(this), WM_CLOSE, 0, 0); + } else { + e.Skip(); + } + }); + Bind(wxEVT_CHAR_HOOK, [this](wxKeyEvent& e) { + if (!DialogKeyHook(this, e)) + e.Skip(); + }); + } +}; + +class MfcPanel : public wxPanel { +public: + MfcPanel(wxWindow* parent, const wxPoint& pos, const wxSize& size) + : wxPanel(parent, wxID_ANY, pos, size, wxTAB_TRAVERSAL | wxBORDER_NONE) {} +}; + +} // namespace + +// Creates the window of a dialog from its template; returns null on failure. +wxWindow* CreateDialogWindow(CDialog* dlg, const rc::Dialog& tmpl, wxWindow* parent) { + wxFont font = DialogFontFor(&tmpl); + wxSize base = BaseUnits(font); + wxRect r(MulDiv(tmpl.x, base.x, 4), MulDiv(tmpl.y, base.y, 8), MulDiv(tmpl.cx, base.x, 4), + MulDiv(tmpl.cy, base.y, 8)); + wxWindow* w = nullptr; + ControlKind kind = ControlKind::Dialog; + if ((tmpl.style & WS_CHILD) && parent) { + w = new MfcPanel(parent, r.GetPosition(), r.GetSize()); + kind = ControlKind::Generic; + } else { + auto* d = new MfcDialog(parent, wxString::FromUTF8(tmpl.caption ? tmpl.caption : ""), DialogWxStyle(tmpl.style)); + d->SetClientSize(r.GetSize()); + w = d; + } + w->SetFont(font); + HookWindow(w, kind, static_cast(tmpl.id), tmpl.style, tmpl.exStyle); + WindowState& st = EnsureState(w); + st.baseUnits = base; + st.text = wxString::FromUTF8(tmpl.caption ? tmpl.caption : ""); + if (dlg) + dlg->AttachWx(w); + CreateDialogControls(w, tmpl); + if (w->IsTopLevel()) { + if (tmpl.style & (DS_CENTER | DS_CENTERMOUSE) || true) + w->CentreOnParent(); + } + return w; +} + +} // namespace mfcwx + +using namespace mfcwx; + +// --------------------------------------------------------------------------------------------- +// CDialog + +IMPLEMENT_DYNAMIC(CDialog, CWnd) + +BEGIN_MESSAGE_MAP(CDialog, CWnd) + ON_COMMAND(IDOK, &CDialog::OnOK) + ON_COMMAND(IDCANCEL, &CDialog::OnCancel) + ON_COMMAND(ID_HELP, &CDialog::OnHelp) + ON_MESSAGE(WM_INITDIALOG, &CDialog::HandleInitDialog) +END_MESSAGE_MAP() + +CDialog::CDialog() + : m_nIDHelp(0), m_lpszTemplateName(nullptr), m_nIDTemplate(0), m_pParentWnd(nullptr), m_bModalRunning(FALSE), + m_nModalResult(-1) {} + +CDialog::CDialog(const char* lpszTemplateName, CWnd* pParentWnd) : CDialog() { + if (IS_INTRESOURCE(lpszTemplateName)) + m_nIDTemplate = static_cast(reinterpret_cast(lpszTemplateName)); + else + m_lpszTemplateName = lpszTemplateName; + m_pParentWnd = pParentWnd; + m_nIDHelp = m_nIDTemplate; +} + +CDialog::CDialog(UINT nIDTemplate, CWnd* pParentWnd) : CDialog() { + m_nIDTemplate = nIDTemplate; + m_pParentWnd = pParentWnd; + m_nIDHelp = nIDTemplate; +} + +CDialog::~CDialog() { + if (m_hWnd) + DestroyWindow(); +} + +BOOL CDialog::CreateFromTemplate(BOOL bModal) { + ResRef ref = m_lpszTemplateName ? ResRef::From(m_lpszTemplateName) : ResRef::FromId(m_nIDTemplate); + const rc::Dialog* tmpl = FindDialog(ref); + if (!tmpl) { + wxLogDebug("mfcwx: dialog template %d not found", static_cast(m_nIDTemplate)); + return FALSE; + } + CWnd* parent = m_pParentWnd; + if (!parent && bModal) + parent = AfxGetMainWnd(); + wxWindow* parentWx = parent && parent->m_hWnd ? parent->GetWx() : nullptr; + if (parentWx && bModal) + parentWx = wxGetTopLevelParent(parentWx); + wxWindow* w = CreateDialogWindow(this, *tmpl, parentWx); + if (!w) + return FALSE; + ApplyDlgInit(this, tmpl->id); + m_nModalResult = -1; + m_bModalRunning = bModal; + if (!SendMessage(WM_INITDIALOG, 0, 0) && m_hWnd) { + } + if (!m_hWnd) + return FALSE; + for (wxWindow* c : w->GetChildren()) + ApplyCtlColor(c); + if (!bModal && (tmpl->style & WS_VISIBLE)) + w->Show(); + return TRUE; +} + +LRESULT CDialog::HandleInitDialog(WPARAM, LPARAM) { + BOOL focusDefault = OnInitDialog(); + if (focusDefault && m_hWnd) { + HWND first = ::GetNextDlgTabItem(m_hWnd, nullptr, FALSE); + if (first) + ::SetFocus(first); + } + return focusDefault; +} + +BOOL CDialog::OnInitDialog() { + UpdateData(FALSE); + return TRUE; +} + +INT_PTR CDialog::DoModal() { + return OnMain([&]() -> INT_PTR { + if (!CreateFromTemplate(TRUE)) + return -1; + wxWindow* w = GetWx(); + auto* dlg = wxDynamicCast(w, wxDialog); + if (dlg && m_bModalRunning && m_nModalResult == -1) { + wxWindow* active = wxGetActiveWindow(); + dlg->ShowModal(); + if (active && active != dlg && !active->IsBeingDeleted()) + active->Raise(); + } + m_bModalRunning = FALSE; + int result = m_nModalResult; + if (m_hWnd) + DestroyWindow(); + return result; + }); +} + +void CDialog::EndDialog(int nResult) { + OnMain([&] { + m_nModalResult = nResult; + if (!m_hWnd) + return; + auto* dlg = wxDynamicCast(GetWx(), wxDialog); + if (m_bModalRunning) { + if (dlg && dlg->IsModal()) + dlg->EndModal(nResult); + } else if (dlg) { + dlg->Hide(); + } + }); +} + +BOOL CDialog::Create(UINT nIDTemplate, CWnd* pParentWnd) { + m_nIDTemplate = nIDTemplate; + m_lpszTemplateName = nullptr; + m_pParentWnd = pParentWnd; + if (!m_nIDHelp) + m_nIDHelp = nIDTemplate; + return OnMain([&]() -> BOOL { return CreateFromTemplate(FALSE); }); +} + +BOOL CDialog::Create(const char* lpszTemplateName, CWnd* pParentWnd) { + if (IS_INTRESOURCE(lpszTemplateName)) + return Create(static_cast(reinterpret_cast(lpszTemplateName)), pParentWnd); + m_lpszTemplateName = lpszTemplateName; + m_pParentWnd = pParentWnd; + return OnMain([&]() -> BOOL { return CreateFromTemplate(FALSE); }); +} + +BOOL CDialog::CreateIndirect(const void*, CWnd*, void*) { return FALSE; } +BOOL CDialog::InitModalIndirect(const void*, CWnd*, void*) { return FALSE; } + +BOOL CDialog::DestroyWindow() { + if (m_hWnd && m_bModalRunning) { + EndDialog(IDCANCEL); + m_bModalRunning = FALSE; + } + return CWnd::DestroyWindow(); +} + +void CDialog::OnOK() { + if (!UpdateData(TRUE)) + return; + EndDialog(IDOK); +} + +void CDialog::OnCancel() { EndDialog(IDCANCEL); } + +void CDialog::OnHelp() { + CWinApp* app = AfxGetApp(); + if (app && m_nIDHelp) + app->WinHelpInternal(0x20000 + m_nIDHelp, HELP_CONTEXT); +} + +void CDialog::MapDialogRect(LPRECT lpRect) const { ::MapDialogRect(m_hWnd, lpRect); } + +void CDialog::SetDefID(UINT nID) { SendMessage(DM_SETDEFID, nID, 0); } + +DWORD CDialog::GetDefID() const { return static_cast(const_cast(this)->SendMessage(DM_GETDEFID)); } + +void CDialog::NextDlgCtrl() const { const_cast(this)->SendMessage(WM_NEXTDLGCTL, 0, 0); } +void CDialog::PrevDlgCtrl() const { const_cast(this)->SendMessage(WM_NEXTDLGCTL, 1, 0); } + +void CDialog::GotoDlgCtrl(CWnd* pWndCtrl) { + if (pWndCtrl && pWndCtrl->m_hWnd) + SendMessage(WM_NEXTDLGCTL, reinterpret_cast(pWndCtrl->m_hWnd), 1); +} + +// --------------------------------------------------------------------------------------------- +// Data exchange + +CDataExchange::CDataExchange(CWnd* pDlgWnd, BOOL bSaveAndValidate) + : m_bSaveAndValidate(bSaveAndValidate), m_pDlgWnd(pDlgWnd), m_hWndLastControl(nullptr), m_bEditLastControl(FALSE) {} + +HWND CDataExchange::PrepareCtrl(int nIDC) { + HWND h = ::GetDlgItem(m_pDlgWnd->m_hWnd, nIDC); + if (!h) + wxLogDebug("mfcwx: DDX control %d not found", nIDC); + m_hWndLastControl = h; + m_bEditLastControl = FALSE; + return h; +} + +HWND CDataExchange::PrepareEditCtrl(int nIDC) { + HWND h = PrepareCtrl(nIDC); + m_bEditLastControl = TRUE; + return h; +} + +void CDataExchange::Fail() { + if (m_bSaveAndValidate && m_hWndLastControl) { + ::SetFocus(m_hWndLastControl); + if (m_bEditLastControl) + ::SendMessage(m_hWndLastControl, EM_SETSEL, 0, -1); + } + throw new CUserException(); +} + +namespace { + +CString PromptText(UINT id, const char* fallback) { + CString s; + if (!s.LoadString(id)) + s = fallback; + return s; +} + +void FailWith(CDataExchange* pDX, UINT id, const char* fallback, const char* a1 = nullptr, const char* a2 = nullptr) { + CString text = PromptText(id, fallback); + if (a1) { + text.Replace("%1", a1); + if (a2) + text.Replace("%2", a2); + } + AfxMessageBox(text, MB_ICONEXCLAMATION); + pDX->Fail(); +} + +void SetTextIfChanged(HWND h, const char* text) { + char buf[256]; + int len = ::GetWindowTextLength(h); + if (len < static_cast(sizeof buf)) { + ::GetWindowText(h, buf, sizeof buf); + if (strcmp(buf, text) == 0) + return; + } + ::SetWindowText(h, text); +} + +CString WindowText(HWND h) { + int len = ::GetWindowTextLength(h); + CString s; + ::GetWindowText(h, s.GetBuffer(len + 1), len + 1); + s.ReleaseBuffer(); + return s; +} + +template +void TextSigned(CDataExchange* pDX, int nIDC, T& value) { + HWND h = pDX->PrepareEditCtrl(nIDC); + if (!h) + return; + if (pDX->m_bSaveAndValidate) { + CString s = WindowText(h); + s.Trim(); + char* end = nullptr; + long long v = strtoll(s, &end, 10); + if (s.IsEmpty() || (end && *end)) + FailWith(pDX, AFX_IDP_PARSE_INT, "Please enter an integer."); + value = static_cast(v); + } else { + char buf[32]; + snprintf(buf, sizeof buf, "%lld", static_cast(value)); + SetTextIfChanged(h, buf); + } +} + +template +void TextUnsigned(CDataExchange* pDX, int nIDC, T& value) { + HWND h = pDX->PrepareEditCtrl(nIDC); + if (!h) + return; + if (pDX->m_bSaveAndValidate) { + CString s = WindowText(h); + s.Trim(); + char* end = nullptr; + unsigned long long v = strtoull(s, &end, 10); + if (s.IsEmpty() || (end && *end) || s[0] == '-') + FailWith(pDX, AFX_IDP_PARSE_UINT, "Please enter a positive integer."); + value = static_cast(v); + } else { + char buf[32]; + snprintf(buf, sizeof buf, "%llu", static_cast(value)); + SetTextIfChanged(h, buf); + } +} + +template +void TextFloat(CDataExchange* pDX, int nIDC, T& value, int digits) { + HWND h = pDX->PrepareEditCtrl(nIDC); + if (!h) + return; + if (pDX->m_bSaveAndValidate) { + CString s = WindowText(h); + s.Trim(); + char* end = nullptr; + double v = strtod(s, &end); + if (s.IsEmpty() || (end && *end)) + FailWith(pDX, AFX_IDP_PARSE_REAL, "Please enter a number."); + value = static_cast(v); + } else { + char buf[64]; + snprintf(buf, sizeof buf, "%.*g", digits, static_cast(value)); + SetTextIfChanged(h, buf); + } +} + +template +void MinMax(CDataExchange* pDX, T value, T minVal, T maxVal, UINT id, const char* fmt) { + if (!pDX->m_bSaveAndValidate || (value >= minVal && value <= maxVal)) + return; + char a[64], b[64]; + snprintf(a, sizeof a, fmt, minVal); + snprintf(b, sizeof b, fmt, maxVal); + FailWith(pDX, id, "Please enter a value between %1 and %2.", a, b); +} + +std::vector RadioGroup(CDataExchange* pDX, int nIDC) { + std::vector group; + HWND first = pDX->PrepareCtrl(nIDC); + if (!first) + return group; + for (HWND h = first; h; h = ::GetWindow(h, GW_HWNDNEXT)) { + if (h != first && (::GetWindowLongPtr(h, GWL_STYLE) & WS_GROUP)) + break; + WindowState* st = GetState(ToWx(h)); + if (st && st->kind == ControlKind::RadioButton) + group.push_back(h); + } + return group; +} + +} // namespace + +void AFXAPI DDX_Control(CDataExchange* pDX, int nIDC, CWnd& rControl) { + if (rControl.m_hWnd) + return; + HWND h = pDX->PrepareCtrl(nIDC); + if (h) + rControl.SubclassWindow(h); +} + +void AFXAPI DDX_Text(CDataExchange* pDX, int nIDC, BYTE& value) { + int v = value; + TextSigned(pDX, nIDC, v); + if (pDX->m_bSaveAndValidate) { + if (v < 0 || v > 255) + FailWith(pDX, AFX_IDP_PARSE_BYTE, "Please enter an integer between 0 and 255."); + value = static_cast(v); + } +} +void AFXAPI DDX_Text(CDataExchange* pDX, int nIDC, short& value) { TextSigned(pDX, nIDC, value); } +void AFXAPI DDX_Text(CDataExchange* pDX, int nIDC, int& value) { TextSigned(pDX, nIDC, value); } +void AFXAPI DDX_Text(CDataExchange* pDX, int nIDC, UINT& value) { TextUnsigned(pDX, nIDC, value); } +void AFXAPI DDX_Text(CDataExchange* pDX, int nIDC, long& value) { TextSigned(pDX, nIDC, value); } +void AFXAPI DDX_Text(CDataExchange* pDX, int nIDC, unsigned long& value) { TextUnsigned(pDX, nIDC, value); } +void AFXAPI DDX_Text(CDataExchange* pDX, int nIDC, LONGLONG& value) { TextSigned(pDX, nIDC, value); } +void AFXAPI DDX_Text(CDataExchange* pDX, int nIDC, ULONGLONG& value) { TextUnsigned(pDX, nIDC, value); } +void AFXAPI DDX_Text(CDataExchange* pDX, int nIDC, float& value) { TextFloat(pDX, nIDC, value, FLT_DIG); } +void AFXAPI DDX_Text(CDataExchange* pDX, int nIDC, double& value) { TextFloat(pDX, nIDC, value, DBL_DIG); } + +void AFXAPI DDX_Text(CDataExchange* pDX, int nIDC, CString& value) { + HWND h = pDX->PrepareEditCtrl(nIDC); + if (!h) + return; + if (pDX->m_bSaveAndValidate) + value = WindowText(h); + else + SetTextIfChanged(h, value); +} + +void AFXAPI DDX_Text(CDataExchange* pDX, int nIDC, char* value, int nMaxLen) { + HWND h = pDX->PrepareEditCtrl(nIDC); + if (!h) + return; + if (pDX->m_bSaveAndValidate) + ::GetWindowText(h, value, nMaxLen); + else + SetTextIfChanged(h, value); +} + +void AFXAPI DDX_Check(CDataExchange* pDX, int nIDC, int& value) { + HWND h = pDX->PrepareCtrl(nIDC); + if (!h) + return; + if (pDX->m_bSaveAndValidate) + value = static_cast(::SendMessage(h, BM_GETCHECK, 0, 0)); + else + ::SendMessage(h, BM_SETCHECK, static_cast(value), 0); +} + +void AFXAPI DDX_Check(CDataExchange* pDX, int nIDC, bool& value) { + int v = value ? 1 : 0; + DDX_Check(pDX, nIDC, v); + value = v != 0; +} + +void AFXAPI DDX_Radio(CDataExchange* pDX, int nIDC, int& value) { + std::vector group = RadioGroup(pDX, nIDC); + if (pDX->m_bSaveAndValidate) { + value = -1; + for (size_t i = 0; i < group.size(); ++i) + if (::SendMessage(group[i], BM_GETCHECK, 0, 0) == BST_CHECKED) + value = static_cast(i); + } else { + for (size_t i = 0; i < group.size(); ++i) + ::SendMessage(group[i], BM_SETCHECK, static_cast(i) == value ? BST_CHECKED : BST_UNCHECKED, 0); + } +} + +void AFXAPI DDX_LBString(CDataExchange* pDX, int nIDC, CString& value) { + HWND h = pDX->PrepareCtrl(nIDC); + if (!h) + return; + if (pDX->m_bSaveAndValidate) { + int sel = static_cast(::SendMessage(h, LB_GETCURSEL, 0, 0)); + if (sel == LB_ERR) { + value.Empty(); + return; + } + int len = static_cast(::SendMessage(h, LB_GETTEXTLEN, static_cast(sel), 0)); + ::SendMessage(h, LB_GETTEXT, static_cast(sel), reinterpret_cast(value.GetBuffer(len + 1))); + value.ReleaseBuffer(); + } else if (::SendMessage(h, LB_SELECTSTRING, static_cast(-1), reinterpret_cast(value.GetString())) == LB_ERR) { + ::SendMessage(h, LB_SETCURSEL, static_cast(-1), 0); + } +} + +void AFXAPI DDX_LBStringExact(CDataExchange* pDX, int nIDC, CString& value) { + HWND h = pDX->PrepareCtrl(nIDC); + if (!h) + return; + if (pDX->m_bSaveAndValidate) { + DDX_LBString(pDX, nIDC, value); + return; + } + LRESULT i = ::SendMessage(h, LB_FINDSTRINGEXACT, static_cast(-1), reinterpret_cast(value.GetString())); + ::SendMessage(h, LB_SETCURSEL, static_cast(i == LB_ERR ? -1 : i), 0); +} + +void AFXAPI DDX_CBString(CDataExchange* pDX, int nIDC, CString& value) { + HWND h = pDX->PrepareCtrl(nIDC); + if (!h) + return; + WindowState* st = GetState(ToWx(h)); + if (pDX->m_bSaveAndValidate) { + if (st && st->kind == ControlKind::ComboList) { + int sel = static_cast(::SendMessage(h, CB_GETCURSEL, 0, 0)); + if (sel == CB_ERR) { + value.Empty(); + return; + } + int len = static_cast(::SendMessage(h, CB_GETLBTEXTLEN, static_cast(sel), 0)); + ::SendMessage(h, CB_GETLBTEXT, static_cast(sel), reinterpret_cast(value.GetBuffer(len + 1))); + value.ReleaseBuffer(); + } else { + value = WindowText(h); + } + } else { + if (::SendMessage(h, CB_SELECTSTRING, static_cast(-1), reinterpret_cast(value.GetString())) == CB_ERR) + ::SetWindowText(h, value); + } +} + +void AFXAPI DDX_CBStringExact(CDataExchange* pDX, int nIDC, CString& value) { + HWND h = pDX->PrepareCtrl(nIDC); + if (!h) + return; + if (pDX->m_bSaveAndValidate) { + DDX_CBString(pDX, nIDC, value); + return; + } + LRESULT i = ::SendMessage(h, CB_FINDSTRINGEXACT, static_cast(-1), reinterpret_cast(value.GetString())); + if (i == CB_ERR) + ::SetWindowText(h, value); + else + ::SendMessage(h, CB_SETCURSEL, static_cast(i), 0); +} + +void AFXAPI DDX_LBIndex(CDataExchange* pDX, int nIDC, int& index) { + HWND h = pDX->PrepareCtrl(nIDC); + if (!h) + return; + if (pDX->m_bSaveAndValidate) + index = static_cast(::SendMessage(h, LB_GETCURSEL, 0, 0)); + else + ::SendMessage(h, LB_SETCURSEL, static_cast(index), 0); +} + +void AFXAPI DDX_CBIndex(CDataExchange* pDX, int nIDC, int& index) { + HWND h = pDX->PrepareCtrl(nIDC); + if (!h) + return; + if (pDX->m_bSaveAndValidate) + index = static_cast(::SendMessage(h, CB_GETCURSEL, 0, 0)); + else + ::SendMessage(h, CB_SETCURSEL, static_cast(index), 0); +} + +void AFXAPI DDX_Scroll(CDataExchange* pDX, int nIDC, int& value) { + HWND h = pDX->PrepareCtrl(nIDC); + if (!h) + return; + if (pDX->m_bSaveAndValidate) + value = static_cast(::SendMessage(h, SBM_GETPOS, 0, 0)); + else + ::SendMessage(h, SBM_SETPOS, static_cast(value), TRUE); +} + +void AFXAPI DDX_Slider(CDataExchange* pDX, int nIDC, int& value) { + HWND h = pDX->PrepareCtrl(nIDC); + if (!h) + return; + if (pDX->m_bSaveAndValidate) + value = static_cast(::SendMessage(h, TBM_GETPOS, 0, 0)); + else + ::SendMessage(h, TBM_SETPOS, TRUE, value); +} + +void AFXAPI DDV_MaxChars(CDataExchange* pDX, const CString& value, int nChars) { + if (pDX->m_bSaveAndValidate) { + if (value.GetLength() > nChars) { + char n[32]; + snprintf(n, sizeof n, "%d", nChars); + FailWith(pDX, AFX_IDP_PARSE_STRING_SIZE, "Please enter no more than %1 characters.", n); + } + } else if (pDX->m_hWndLastControl && pDX->m_bEditLastControl) { + WindowState* st = GetState(ToWx(pDX->m_hWndLastControl)); + if (st && st->kind == ControlKind::ComboBox) + ::SendMessage(pDX->m_hWndLastControl, CB_LIMITTEXT, static_cast(nChars), 0); + else + ::SendMessage(pDX->m_hWndLastControl, EM_LIMITTEXT, static_cast(nChars), 0); + } +} + +void AFXAPI DDV_MinMaxByte(CDataExchange* pDX, BYTE value, BYTE minVal, BYTE maxVal) { + MinMax(pDX, value, minVal, maxVal, AFX_IDP_PARSE_INT_RANGE, "%d"); +} +void AFXAPI DDV_MinMaxShort(CDataExchange* pDX, short value, short minVal, short maxVal) { + MinMax(pDX, value, minVal, maxVal, AFX_IDP_PARSE_INT_RANGE, "%d"); +} +void AFXAPI DDV_MinMaxInt(CDataExchange* pDX, int value, int minVal, int maxVal) { + MinMax(pDX, value, minVal, maxVal, AFX_IDP_PARSE_INT_RANGE, "%d"); +} +void AFXAPI DDV_MinMaxLong(CDataExchange* pDX, long value, long minVal, long maxVal) { + MinMax(pDX, value, minVal, maxVal, AFX_IDP_PARSE_INT_RANGE, "%ld"); +} +void AFXAPI DDV_MinMaxUInt(CDataExchange* pDX, UINT value, UINT minVal, UINT maxVal) { + MinMax(pDX, value, minVal, maxVal, AFX_IDP_PARSE_INT_RANGE, "%u"); +} +void AFXAPI DDV_MinMaxDWord(CDataExchange* pDX, DWORD value, DWORD minVal, DWORD maxVal) { + MinMax(pDX, value, minVal, maxVal, AFX_IDP_PARSE_INT_RANGE, "%u"); +} +void AFXAPI DDV_MinMaxLongLong(CDataExchange* pDX, LONGLONG value, LONGLONG minVal, LONGLONG maxVal) { + MinMax(pDX, value, minVal, maxVal, AFX_IDP_PARSE_INT_RANGE, "%lld"); +} +void AFXAPI DDV_MinMaxULongLong(CDataExchange* pDX, ULONGLONG value, ULONGLONG minVal, ULONGLONG maxVal) { + MinMax(pDX, value, minVal, maxVal, AFX_IDP_PARSE_INT_RANGE, "%llu"); +} +void AFXAPI DDV_MinMaxFloat(CDataExchange* pDX, float const& value, float minVal, float maxVal) { + MinMax(pDX, value, minVal, maxVal, AFX_IDP_PARSE_REAL_RANGE, "%g"); +} +void AFXAPI DDV_MinMaxDouble(CDataExchange* pDX, double const& value, double minVal, double maxVal) { + MinMax(pDX, value, minVal, maxVal, AFX_IDP_PARSE_REAL_RANGE, "%g"); +} +void AFXAPI DDV_MinMaxSlider(CDataExchange* pDX, DWORD value, DWORD minVal, DWORD maxVal) { + MinMax(pDX, value, minVal, maxVal, AFX_IDP_PARSE_INT_RANGE, "%u"); +} + +// --------------------------------------------------------------------------------------------- +// Common dialogs + +IMPLEMENT_DYNAMIC(CCommonDialog, CDialog) +IMPLEMENT_DYNAMIC(CFileDialog, CCommonDialog) +IMPLEMENT_DYNAMIC(CColorDialog, CCommonDialog) +IMPLEMENT_DYNAMIC(CFontDialog, CCommonDialog) +IMPLEMENT_DYNAMIC(CPageSetupDialog, CCommonDialog) +IMPLEMENT_DYNAMIC(CPrintDialog, CCommonDialog) +IMPLEMENT_DYNAMIC(CFindReplaceDialog, CCommonDialog) +IMPLEMENT_DYNAMIC(CPropertyPage, CDialog) +IMPLEMENT_DYNAMIC(CPropertySheet, CWnd) + +namespace { + +wxWindow* OwnerWindow(CWnd* pParent) { + if (pParent && pParent->m_hWnd) + return wxGetTopLevelParent(pParent->GetWx()); + return MainWxWindow(); +} + +// "Text (*.txt)|*.txt|All files (*.*)|*.*||" -> wx wildcard +wxString MfcFilterToWx(const CString& filter) { + wxString f = ToWx(filter); + while (f.EndsWith("|")) + f.RemoveLast(); + return f.empty() ? wxString("*") : f; +} + +} // namespace + +CFileDialog::CFileDialog(BOOL bOpenFileDialog, const char* lpszDefExt, const char* lpszFileName, DWORD dwFlags, + const char* lpszFilter, CWnd* pParentWnd, DWORD, BOOL) + : CCommonDialog(pParentWnd), m_bOpenFileDialog(bOpenFileDialog) { + memset(&m_ofn, 0, sizeof m_ofn); + m_ofn.lStructSize = sizeof m_ofn; + m_ofn.Flags = dwFlags; + m_szFileName[0] = 0; + m_szFileTitle[0] = 0; + if (lpszFileName) { + strncpy(m_szFileName, lpszFileName, sizeof m_szFileName - 1); + m_szFileName[sizeof m_szFileName - 1] = 0; + } + m_ofn.lpstrFile = m_szFileName; + m_ofn.nMaxFile = sizeof m_szFileName; + m_ofn.lpstrFileTitle = m_szFileTitle; + m_ofn.nMaxFileTitle = sizeof m_szFileTitle; + m_ofn.nFilterIndex = 1; + if (lpszDefExt) + m_strDefExt = lpszDefExt; + if (lpszFilter) + m_strFilter = lpszFilter; +} + +INT_PTR CFileDialog::DoModal() { + return OnMain([&]() -> INT_PTR { + long style = m_bOpenFileDialog ? wxFD_OPEN : wxFD_SAVE; + if (m_bOpenFileDialog && (m_ofn.Flags & OFN_FILEMUSTEXIST)) + style |= wxFD_FILE_MUST_EXIST; + if (!m_bOpenFileDialog && (m_ofn.Flags & OFN_OVERWRITEPROMPT)) + style |= wxFD_OVERWRITE_PROMPT; + if (m_ofn.Flags & OFN_ALLOWMULTISELECT) + style |= wxFD_MULTIPLE; + wxString initialDir; + if (m_ofn.lpstrInitialDir && *m_ofn.lpstrInitialDir) + initialDir = NativePathWx(m_ofn.lpstrInitialDir); + else if (!m_strInitialDir.IsEmpty()) + initialDir = NativePathWx(m_strInitialDir); + wxFileName current(NativePathWx(m_ofn.lpstrFile ? m_ofn.lpstrFile : "")); + if (initialDir.empty() && current.IsAbsolute()) + initialDir = current.GetPath(); + wxString title = m_ofn.lpstrTitle ? ToWx(m_ofn.lpstrTitle) : ToWx(m_strTitle); + wxFileDialog dlg(OwnerWindow(m_pParentWnd), title, initialDir, current.GetFullName(), MfcFilterToWx(m_strFilter), + style); + if (m_ofn.nFilterIndex > 0) + dlg.SetFilterIndex(static_cast(m_ofn.nFilterIndex) - 1); + if (dlg.ShowModal() != wxID_OK) + return IDCANCEL; + m_paths.clear(); + wxArrayString paths; + if (style & wxFD_MULTIPLE) + dlg.GetPaths(paths); + else + paths.Add(dlg.GetPath()); + for (const wxString& p : paths) { + std::string a = AppPath(p.utf8_str()); + CString path(a.c_str()); + if (!m_bOpenFileDialog && !m_strDefExt.IsEmpty()) { + int slash = path.ReverseFind('\\'); + if (path.Find('.', slash + 1) < 0) + path += "." + m_strDefExt; + } + m_paths.push_back(path); + } + strncpy(m_szFileName, m_paths.front(), sizeof m_szFileName - 1); + m_szFileName[sizeof m_szFileName - 1] = 0; + CString title2 = GetFileName(); + strncpy(m_szFileTitle, title2, sizeof m_szFileTitle - 1); + m_szFileTitle[sizeof m_szFileTitle - 1] = 0; + m_ofn.nFilterIndex = static_cast(dlg.GetFilterIndex() + 1); + return IDOK; + }); +} + +CString CFileDialog::GetPathName() const { return m_paths.empty() ? CString(m_szFileName) : m_paths.front(); } + +CString CFileDialog::GetFileName() const { + CString p = GetPathName(); + int slash = p.ReverseFind('\\'); + return p.Mid(slash + 1); +} + +CString CFileDialog::GetFileExt() const { + CString n = GetFileName(); + int dot = n.ReverseFind('.'); + return dot < 0 ? CString() : n.Mid(dot + 1); +} + +CString CFileDialog::GetFileTitle() const { + CString n = GetFileName(); + int dot = n.ReverseFind('.'); + return dot < 0 ? n : n.Left(dot); +} + +CString CFileDialog::GetFolderPath() const { + CString p = GetPathName(); + int slash = p.ReverseFind('\\'); + return slash < 0 ? CString() : p.Left(slash); +} + +POSITION CFileDialog::GetStartPosition() const { + return m_paths.empty() ? nullptr : reinterpret_cast(static_cast(1)); +} + +CString CFileDialog::GetNextPathName(POSITION& pos) const { + size_t i = static_cast(reinterpret_cast(pos)) - 1; + CString r = i < m_paths.size() ? m_paths[i] : CString(); + pos = i + 1 < m_paths.size() ? reinterpret_cast(static_cast(i + 2)) : nullptr; + return r; +} + +CColorDialog::CColorDialog(COLORREF clrInit, DWORD dwFlags, CWnd* pParentWnd) : CCommonDialog(pParentWnd) { + memset(&m_cc, 0, sizeof m_cc); + m_cc.lStructSize = sizeof m_cc; + m_cc.rgbResult = clrInit; + m_cc.Flags = dwFlags; +} + +INT_PTR CColorDialog::DoModal() { + return OnMain([&]() -> INT_PTR { + wxColourData data; + data.SetColour(ToWxColour(m_cc.rgbResult)); + wxColourDialog dlg(OwnerWindow(m_pParentWnd), &data); + if (dlg.ShowModal() != wxID_OK) + return IDCANCEL; + m_cc.rgbResult = FromWxColour(dlg.GetColourData().GetColour()); + return IDOK; + }); +} + +CFontDialog::CFontDialog(LPLOGFONT lplfInitial, DWORD dwFlags, CDC*, CWnd* pParentWnd) : CCommonDialog(pParentWnd) { + memset(&m_cf, 0, sizeof m_cf); + memset(&m_lf, 0, sizeof m_lf); + m_cf.lStructSize = sizeof m_cf; + m_cf.Flags = dwFlags; + m_cf.lpLogFont = &m_lf; + if (lplfInitial) + m_lf = *lplfInitial; +} + +INT_PTR CFontDialog::DoModal() { + return OnMain([&]() -> INT_PTR { + wxFontData data; + if (m_lf.lfFaceName[0]) { + CFont initial; + initial.CreateFontIndirect(&m_lf); + data.SetInitialFont(FontFromHandle(reinterpret_cast(initial.m_hObject))); + } + data.SetColour(ToWxColour(m_cf.rgbColors)); + wxFontDialog dlg(OwnerWindow(m_pParentWnd), data); + if (dlg.ShowModal() != wxID_OK) + return IDCANCEL; + wxFont f = dlg.GetFontData().GetChosenFont(); + std::string face = mfcwx::FromWx(f.GetFaceName()); + memset(m_lf.lfFaceName, 0, sizeof m_lf.lfFaceName); + strncpy(m_lf.lfFaceName, face.c_str(), sizeof m_lf.lfFaceName - 1); + m_lf.lfHeight = -MulDiv(f.GetPointSize(), 96, 72); + m_lf.lfWeight = f.GetWeight() >= wxFONTWEIGHT_BOLD ? FW_BOLD : FW_NORMAL; + m_lf.lfItalic = f.GetStyle() == wxFONTSTYLE_ITALIC; + m_lf.lfUnderline = f.GetUnderlined(); + m_cf.iPointSize = f.GetPointSize() * 10; + m_cf.rgbColors = FromWxColour(dlg.GetFontData().GetColour()); + if (m_cf.lpLogFont && m_cf.lpLogFont != &m_lf) + *m_cf.lpLogFont = m_lf; + return IDOK; + }); +} + +void CFontDialog::GetCurrentFont(LPLOGFONT lplf) { + if (lplf) + *lplf = m_lf; +} + +CString CFontDialog::GetFaceName() const { return CString(m_lf.lfFaceName); } +int CFontDialog::GetSize() const { return m_cf.iPointSize; } + +CPageSetupDialog::CPageSetupDialog(DWORD dwFlags, CWnd* pParentWnd) : CCommonDialog(pParentWnd) { + memset(&m_psd, 0, sizeof m_psd); + m_psd.Flags = dwFlags; +} + +INT_PTR CPageSetupDialog::DoModal() { return IDCANCEL; } +CSize CPageSetupDialog::GetPaperSize() const { return CSize(21000, 29700); } +void CPageSetupDialog::GetMargins(LPRECT lpRectMargins, LPRECT lpRectMinMargins) const { + if (lpRectMargins) + *lpRectMargins = m_psd.rtMargin; + if (lpRectMinMargins) + *lpRectMinMargins = m_psd.rtMinMargin; +} + +CPrintDialog::CPrintDialog(BOOL, DWORD dwFlags, CWnd* pParentWnd) : CCommonDialog(pParentWnd) { + memset(&m_pd, 0, sizeof m_pd); + m_pd.lStructSize = sizeof m_pd; + m_pd.Flags = dwFlags; +} + +INT_PTR CPrintDialog::DoModal() { return IDCANCEL; } + +namespace { + +std::map& FindDialogs() { + static std::map m; + return m; +} + +} // namespace + +CFindReplaceDialog::CFindReplaceDialog() : CCommonDialog(nullptr), m_flags(FR_DOWN) {} + +BOOL CFindReplaceDialog::Create(BOOL bFindDialogOnly, const char* lpszFindWhat, const char* lpszReplaceWith, + DWORD dwFlags, CWnd* pParentWnd) { + return OnMain([&]() -> BOOL { + m_find = lpszFindWhat ? lpszFindWhat : ""; + m_replace = lpszReplaceWith ? lpszReplaceWith : ""; + m_flags = dwFlags; + m_pParentWnd = pParentWnd; + auto* data = new wxFindReplaceData(static_cast((dwFlags & FR_DOWN ? wxFR_DOWN : 0) | + (dwFlags & FR_MATCHCASE ? wxFR_MATCHCASE : 0) | + (dwFlags & FR_WHOLEWORD ? wxFR_WHOLEWORD : 0))); + data->SetFindString(ToWx(m_find)); + data->SetReplaceString(ToWx(m_replace)); + auto* dlg = new wxFindReplaceDialog(OwnerWindow(pParentWnd), data, wxString(), + bFindDialogOnly ? 0 : wxFR_REPLACEDIALOG); + HookWindow(dlg, ControlKind::Dialog, 0, WS_POPUP | WS_CAPTION, 0); + AttachWx(dlg); + FindDialogs()[dlg] = this; + static const UINT findMsg = RegisterWindowMessage(FINDMSGSTRING); + CWnd* owner = pParentWnd; + auto notify = [this, dlg, data, owner](DWORD extra) { + m_find = CStr(data->GetFindString()); + m_replace = CStr(data->GetReplaceString()); + int f = data->GetFlags(); + m_flags = extra | (f & wxFR_DOWN ? FR_DOWN : 0) | (f & wxFR_MATCHCASE ? FR_MATCHCASE : 0) | + (f & wxFR_WHOLEWORD ? FR_WHOLEWORD : 0); + if (owner && owner->m_hWnd) + owner->SendMessage(findMsg, 0, reinterpret_cast(this)); + (void)dlg; + }; + dlg->Bind(wxEVT_FIND, [notify](wxFindDialogEvent&) { notify(FR_FINDNEXT); }); + dlg->Bind(wxEVT_FIND_NEXT, [notify](wxFindDialogEvent&) { notify(FR_FINDNEXT); }); + dlg->Bind(wxEVT_FIND_REPLACE, [notify](wxFindDialogEvent&) { notify(FR_REPLACE); }); + dlg->Bind(wxEVT_FIND_REPLACE_ALL, [notify](wxFindDialogEvent&) { notify(FR_REPLACEALL); }); + dlg->Bind(wxEVT_FIND_CLOSE, [this, notify, dlg, data](wxFindDialogEvent&) { + notify(FR_DIALOGTERM); + FindDialogs().erase(dlg); + DestroyWindow(); + delete data; + }); + dlg->Show(); + return TRUE; + }); +} + +CFindReplaceDialog* CFindReplaceDialog::GetNotifier(LPARAM lParam) { return reinterpret_cast(lParam); } + +CPropertyPage::CPropertyPage() : CDialog() {} +CPropertyPage::CPropertyPage(UINT nIDTemplate, UINT nIDCaption, DWORD) : CDialog(nIDTemplate) { + if (nIDCaption) + m_strCaption.LoadString(nIDCaption); +} + +CPropertySheet::CPropertySheet() : m_pParentWnd(nullptr), m_active(0) {} +CPropertySheet::CPropertySheet(UINT nIDCaption, CWnd* pParentWnd, UINT iSelectPage) + : m_pParentWnd(pParentWnd), m_active(static_cast(iSelectPage)) { + m_strCaption.LoadString(nIDCaption); +} +CPropertySheet::CPropertySheet(const char* pszCaption, CWnd* pParentWnd, UINT iSelectPage) + : m_strCaption(pszCaption), m_pParentWnd(pParentWnd), m_active(static_cast(iSelectPage)) {} + +void CPropertySheet::AddPage(CPropertyPage* pPage) { m_pages.push_back(pPage); } + +void CPropertySheet::RemovePage(CPropertyPage* pPage) { + m_pages.erase(std::remove(m_pages.begin(), m_pages.end(), pPage), m_pages.end()); +} + +BOOL CPropertySheet::SetActivePage(int nPage) { + m_active = nPage; + return TRUE; +} + +INT_PTR CPropertySheet::DoModal() { + return OnMain([&]() -> INT_PTR { + wxDialog dlg(OwnerWindow(m_pParentWnd), wxID_ANY, ToWx(m_strCaption), wxDefaultPosition, wxDefaultSize, + wxDEFAULT_DIALOG_STYLE); + dlg.SetFont(DialogFontFor(nullptr)); + auto* sizer = new wxBoxSizer(wxVERTICAL); + auto* book = new wxNotebook(&dlg, wxID_ANY); + sizer->Add(book, 1, wxEXPAND | wxALL, 8); + HookWindow(book, ControlKind::Generic, 0, WS_CHILD | WS_VISIBLE, 0); + for (CPropertyPage* page : m_pages) { + const rc::Dialog* tmpl = FindDialog(ResRef::FromId(page->m_nIDTemplate)); + if (!tmpl) + continue; + rc::Dialog child = *tmpl; + child.style = (child.style & ~WS_POPUP) | WS_CHILD; + wxWindow* w = CreateDialogWindow(page, child, book); + CString caption = page->m_strCaption.IsEmpty() ? CString(child.caption ? Utf8ToAnsi(child.caption).c_str() : "") + : page->m_strCaption; + book->AddPage(w, ToWx(caption)); + page->SendMessage(WM_INITDIALOG, 0, 0); + } + if (m_active >= 0 && m_active < static_cast(book->GetPageCount())) + book->SetSelection(static_cast(m_active)); + sizer->Add(dlg.CreateStdDialogButtonSizer(wxOK | wxCANCEL), 0, wxEXPAND | wxALL, 8); + dlg.SetSizerAndFit(sizer); + dlg.CentreOnParent(); + int r = dlg.ShowModal(); + for (CPropertyPage* page : m_pages) { + if (r == wxID_OK && page->m_hWnd) { + page->OnKillActive(); + page->OnApply(); + } + if (page->m_hWnd) + page->CWnd::DestroyWindow(); + } + return r == wxID_OK ? IDOK : IDCANCEL; + }); +} diff --git a/port/mfcwx/src/internal.h b/port/mfcwx/src/internal.h index 2606a4c53..cb1b91e6a 100644 --- a/port/mfcwx/src/internal.h +++ b/port/mfcwx/src/internal.h @@ -115,6 +115,7 @@ struct WindowState { int checkState = 0; // BM_SETCHECK state of buttons void* image = nullptr; // STM_SETIMAGE / STM_SETICON handle wxString text; // caption of custom-drawn windows + wxSize baseUnits; // dialogs: dialog base units of the dialog font std::function customPaint; std::vector> itemData; // unused by most kinds CWnd* buddy = nullptr; // spin buttons diff --git a/port/mfcwx/src/windows_impl.h b/port/mfcwx/src/windows_impl.h index 079c56a6c..4d7a571a5 100644 --- a/port/mfcwx/src/windows_impl.h +++ b/port/mfcwx/src/windows_impl.h @@ -90,6 +90,7 @@ void NotifyParent(wxWindow* control, UINT code); LRESULT NotifyParentNM(wxWindow* control, NMHDR* hdr); // dialog.cpp +wxWindow* CreateDialogWindow(CDialog* dlg, const rc::Dialog& tmpl, wxWindow* parent); BOOL ApplyDlgInit(CWnd* pWnd, int dialogId); // Handles Enter/Escape/F1 like the Windows dialog manager; true if consumed. bool DialogKeyHook(wxWindow* dialog, wxKeyEvent& event); From bed570528898559ca0c8628db4977a8ac2a87d96 Mon Sep 17 00:00:00 2001 From: Kamil Marczak Date: Tue, 6 Oct 2026 17:31:47 +0200 Subject: [PATCH 06/46] feat(mfcwx): add menus built from menu resources and the Win32 menu API --- port/mfcwx/src/menu.cpp | 655 ++++++++++++++++++++++++++++++++++ port/mfcwx/src/windows_impl.h | 6 + 2 files changed, 661 insertions(+) create mode 100644 port/mfcwx/src/menu.cpp diff --git a/port/mfcwx/src/menu.cpp b/port/mfcwx/src/menu.cpp new file mode 100644 index 000000000..c0b77c015 --- /dev/null +++ b/port/mfcwx/src/menu.cpp @@ -0,0 +1,655 @@ +#include "windows_impl.h" + +#include +#include +#include + +namespace mfcwx { + +struct MenuHandle { + wxMenuBar* bar = nullptr; + wxMenu* menu = nullptr; +}; + +namespace { + +std::unordered_map& Handles() { + static auto* m = new std::unordered_map(); + return *m; +} + +std::unordered_map& TempMenus() { + static auto* m = new std::unordered_map(); + return *m; +} + +MenuHandle* FromH(HMENU h) { return reinterpret_cast(h); } + +wxString AccelText(const rc::Accel& a) { + wxString s; + if (a.flags & FCONTROL) + s += "Ctrl+"; + if (a.flags & FALT) + s += "Alt+"; + if (a.flags & FSHIFT) + s += "Shift+"; + if (a.flags & FVIRTKEY) { + int key = WxKeyFromVirtual(static_cast(a.key)); + if (key >= WXK_F1 && key <= WXK_F24) + s += wxString::Format("F%d", key - WXK_F1 + 1); + else if (key == WXK_DELETE) + s += "Del"; + else if (key == WXK_INSERT) + s += "Ins"; + else if (key == WXK_BACK) + s += "Back"; + else if (key == WXK_RETURN) + s += "Enter"; + else if (key == WXK_ESCAPE) + s += "Esc"; + else if (key == WXK_TAB) + s += "Tab"; + else if (key == WXK_SPACE) + s += "Space"; + else if (key > 32 && key < 127) + s += static_cast(key); + else + return wxString(); + } else if (a.key > 0 && a.key < 32) { + s = "Ctrl+" + wxString(static_cast('A' + a.key - 1)); + } else { + s += static_cast(a.key); + } + return s; +} + +const rc::AccelTable*& CurrentAccelTable() { + static const rc::AccelTable* t = nullptr; + return t; +} + +wxString LabelFor(const rc::MenuItem& item) { + wxString label = MenuTextToWx(item.text); + if (const rc::AccelTable* t = CurrentAccelTable()) { + for (int i = 0; i < t->itemCount; ++i) + if (t->items[i].id == item.id) { + wxString accel = AccelText(t->items[i]); + if (!accel.empty()) + return label + "\t" + accel; + } + } + return label; +} + +void AppendItems(wxMenu* menu, const rc::Menu& res, int& index, int depth) { + while (index < res.itemCount && res.items[index].depth >= depth) { + const rc::MenuItem& item = res.items[index]; + if (item.depth > depth) { + ++index; + continue; + } + ++index; + if (item.flags & MF_POPUP) { + auto* sub = new wxMenu; + AppendItems(sub, res, index, depth + 1); + menu->AppendSubMenu(sub, MenuTextToWx(item.text)); + } else if ((item.flags & MF_SEPARATOR) || item.id == 0) { + menu->AppendSeparator(); + } else { + wxMenuItem* mi = menu->Append(MenuWxId(item.id), LabelFor(item)); + if (item.flags & MF_CHECKED) { + mi->SetCheckable(true); + mi->Check(true); + } + if (item.flags & (MF_GRAYED | MF_DISABLED)) + mi->Enable(false); + } + } +} + +void AppendItemsTopLevel(wxMenu* menu, const rc::Menu& res, int& index) { AppendItems(menu, res, index, 0); } + +wxMenu* CloneMenu(wxMenu* src) { + auto* copy = new wxMenu; + for (wxMenuItem* item : src->GetMenuItems()) { + if (item->IsSeparator()) { + copy->AppendSeparator(); + } else if (item->IsSubMenu()) { + copy->AppendSubMenu(CloneMenu(item->GetSubMenu()), item->GetItemLabel()); + } else { + wxMenuItem* mi = copy->Append(item->GetId(), item->GetItemLabel(), wxString(), + item->IsCheckable() ? wxITEM_CHECK : wxITEM_NORMAL); + if (item->IsCheckable()) + mi->Check(item->IsChecked()); + mi->Enable(item->IsEnabled()); + } + } + return copy; +} + +} // namespace + +int MenuWxId(int winId) { + if (winId == ID_APP_ABOUT) + return wxID_ABOUT; + if (winId == ID_APP_EXIT) + return wxID_EXIT; + return ToWxId(winId); +} + +int MenuWinId(int wxId) { + if (wxId == wxID_ABOUT) + return ID_APP_ABOUT; + if (wxId == wxID_EXIT) + return ID_APP_EXIT; + return FromWxId(wxId); +} + +void SetMenuAccelerators(const rc::AccelTable* table) { CurrentAccelTable() = table; } + +wxMenuBar* BuildMenuBar(const rc::Menu& res) { + auto* bar = new wxMenuBar; + int index = 0; + while (index < res.itemCount) { + const rc::MenuItem& item = res.items[index++]; + if (item.depth != 0) + continue; + auto* menu = new wxMenu; + AppendItems(menu, res, index, 1); + bar->Append(menu, MenuTextToWx(item.text)); + } + return bar; +} + +wxMenu* BuildPopupMenu(const rc::Menu& res, int popupIndex) { + int index = 0; + int seen = 0; + while (index < res.itemCount) { + const rc::MenuItem& item = res.items[index++]; + if (item.depth != 0) + continue; + if (seen++ == popupIndex) { + auto* menu = new wxMenu; + AppendItems(menu, res, index, 1); + return menu; + } + } + return nullptr; +} + +HMENU HandleFromWxMenu(wxMenu* menu) { + if (!menu) + return nullptr; + auto& h = Handles()[menu]; + if (!h) { + h = new MenuHandle; + h->menu = menu; + } + return reinterpret_cast(h); +} + +HMENU HandleFromWxMenuBar(wxMenuBar* bar) { + if (!bar) + return nullptr; + auto& h = Handles()[bar]; + if (!h) { + h = new MenuHandle; + h->bar = bar; + } + return reinterpret_cast(h); +} + +wxMenu* WxMenuFromHandle(HMENU h) { return h ? FromH(h)->menu : nullptr; } +wxMenuBar* WxMenuBarFromHandle(HMENU h) { return h ? FromH(h)->bar : nullptr; } + +namespace { + +// Finds a menu item by command id or position in a menu or menu bar. +struct ItemRef { + wxMenu* parent = nullptr; + wxMenuItem* item = nullptr; + wxMenuBar* bar = nullptr; + int barIndex = -1; +}; + +ItemRef FindItem(HMENU h, UINT pos, UINT flags) { + ItemRef r; + MenuHandle* mh = FromH(h); + if (!mh) + return r; + if (flags & MF_BYPOSITION) { + if (mh->bar) { + if (pos < mh->bar->GetMenuCount()) { + r.bar = mh->bar; + r.barIndex = static_cast(pos); + } + } else if (mh->menu && pos < mh->menu->GetMenuItemCount()) { + r.parent = mh->menu; + r.item = mh->menu->FindItemByPosition(pos); + } + return r; + } + int id = MenuWxId(static_cast(pos)); + wxMenu* owner = nullptr; + wxMenuItem* item = mh->bar ? mh->bar->FindItem(id, &owner) : (mh->menu ? mh->menu->FindItem(id, &owner) : nullptr); + r.item = item; + r.parent = owner; + return r; +} + +} // namespace + +} // namespace mfcwx + +using namespace mfcwx; + +// --------------------------------------------------------------------------------------------- +// CMenu + +IMPLEMENT_DYNCREATE(CMenu, CObject) + +CMenu::CMenu() : m_hMenu(nullptr) {} + +CMenu::~CMenu() {} + +CMenu* CMenu::FromHandle(HMENU hMenu) { + if (!hMenu) + return nullptr; + auto& m = TempMenus()[FromH(hMenu)]; + if (!m) { + m = new CMenu; + m->m_hMenu = hMenu; + } + return m; +} + +BOOL CMenu::Attach(HMENU hMenu) { + m_hMenu = hMenu; + return hMenu != nullptr; +} + +HMENU CMenu::Detach() { + HMENU h = m_hMenu; + m_hMenu = nullptr; + return h; +} + +BOOL CMenu::CreateMenu() { return Attach(::CreateMenu()); } +BOOL CMenu::CreatePopupMenu() { return Attach(::CreatePopupMenu()); } +BOOL CMenu::LoadMenu(const char* lpszResourceName) { return Attach(::LoadMenu(nullptr, lpszResourceName)); } +BOOL CMenu::LoadMenu(UINT nIDResource) { return Attach(::LoadMenu(nullptr, MAKEINTRESOURCE(nIDResource))); } + +BOOL CMenu::DestroyMenu() { + if (!m_hMenu) + return FALSE; + BOOL r = ::DestroyMenu(m_hMenu); + m_hMenu = nullptr; + return r; +} + +BOOL CMenu::DeleteMenu(UINT nPosition, UINT nFlags) { return ::DeleteMenu(m_hMenu, nPosition, nFlags); } +BOOL CMenu::RemoveMenu(UINT nPosition, UINT nFlags) { return ::RemoveMenu(m_hMenu, nPosition, nFlags); } +BOOL CMenu::AppendMenu(UINT nFlags, UINT_PTR nIDNewItem, const char* lpszNewItem) { + return ::AppendMenu(m_hMenu, nFlags, nIDNewItem, lpszNewItem); +} +BOOL CMenu::InsertMenu(UINT nPosition, UINT nFlags, UINT_PTR nIDNewItem, const char* lpszNewItem) { + return ::InsertMenu(m_hMenu, nPosition, nFlags, nIDNewItem, lpszNewItem); +} +BOOL CMenu::ModifyMenu(UINT nPosition, UINT nFlags, UINT_PTR nIDNewItem, const char* lpszNewItem) { + return ::ModifyMenu(m_hMenu, nPosition, nFlags, nIDNewItem, lpszNewItem); +} +UINT CMenu::CheckMenuItem(UINT nIDCheckItem, UINT nCheck) { return ::CheckMenuItem(m_hMenu, nIDCheckItem, nCheck); } +BOOL CMenu::CheckMenuRadioItem(UINT nIDFirst, UINT nIDLast, UINT nIDItem, UINT nFlags) { + for (UINT id = nIDFirst; id <= nIDLast; ++id) + ::CheckMenuItem(m_hMenu, id, (nFlags & MF_BYPOSITION) | (id == nIDItem ? MF_CHECKED : MF_UNCHECKED)); + return TRUE; +} +UINT CMenu::EnableMenuItem(UINT nIDEnableItem, UINT nEnable) { return ::EnableMenuItem(m_hMenu, nIDEnableItem, nEnable); } +UINT CMenu::GetMenuItemCount() const { return static_cast(::GetMenuItemCount(m_hMenu)); } +UINT CMenu::GetMenuItemID(int nPos) const { return ::GetMenuItemID(m_hMenu, nPos); } +UINT CMenu::GetMenuState(UINT nID, UINT nFlags) const { return ::GetMenuState(m_hMenu, nID, nFlags); } +int CMenu::GetMenuString(UINT nIDItem, char* lpString, int nMaxCount, UINT nFlags) const { + return ::GetMenuString(m_hMenu, nIDItem, lpString, nMaxCount, nFlags); +} +int CMenu::GetMenuString(UINT nIDItem, CString& rString, UINT nFlags) const { + char buf[512]; + int n = ::GetMenuString(m_hMenu, nIDItem, buf, sizeof buf, nFlags); + rString = buf; + return n; +} +CMenu* CMenu::GetSubMenu(int nPos) const { return FromHandle(::GetSubMenu(m_hMenu, nPos)); } +BOOL CMenu::TrackPopupMenu(UINT nFlags, int x, int y, CWnd* pWnd, LPCRECT lpRect) { + return ::TrackPopupMenu(m_hMenu, nFlags, x, y, 0, pWnd ? pWnd->m_hWnd : nullptr, lpRect); +} +wxMenu* CMenu::GetWxMenu() const { return WxMenuFromHandle(m_hMenu); } +wxMenuBar* CMenu::GetWxMenuBar() const { return WxMenuBarFromHandle(m_hMenu); } + +// --------------------------------------------------------------------------------------------- +// Win32 menu functions + +HMENU LoadMenu(HINSTANCE, const char* lpMenuName) { + const rc::Menu* res = FindMenu(ResRef::From(lpMenuName)); + if (!res) + return nullptr; + return OnMain([&]() -> HMENU { + bool hasTopLevelItems = false; + for (int i = 0; i < res->itemCount; ++i) + if (res->items[i].depth == 0 && !(res->items[i].flags & MF_POPUP)) + hasTopLevelItems = true; + if (hasTopLevelItems) { + auto* menu = new wxMenu; + int index = 0; + AppendItemsTopLevel(menu, *res, index); + return HandleFromWxMenu(menu); + } + return HandleFromWxMenuBar(BuildMenuBar(*res)); + }); +} + +HMENU GetMenu(HWND hWnd) { + if (!IsWindow(hWnd)) + return nullptr; + return OnMain([&]() -> HMENU { + auto* frame = wxDynamicCast(wxGetTopLevelParent(ToWx(hWnd)), wxFrame); + return frame ? HandleFromWxMenuBar(frame->GetMenuBar()) : nullptr; + }); +} + +BOOL SetMenu(HWND hWnd, HMENU hMenu) { + if (!IsWindow(hWnd)) + return FALSE; + return OnMain([&]() -> BOOL { + auto* frame = wxDynamicCast(ToWx(hWnd), wxFrame); + if (!frame) + return FALSE; + wxMenuBar* bar = WxMenuBarFromHandle(hMenu); + if (bar && frame->GetMenuBar() != bar) + frame->SetMenuBar(bar); + return TRUE; + }); +} + +HMENU GetSubMenu(HMENU hMenu, int nPos) { + MenuHandle* mh = FromH(hMenu); + if (!mh || nPos < 0) + return nullptr; + if (mh->bar) + return nPos < static_cast(mh->bar->GetMenuCount()) ? HandleFromWxMenu(mh->bar->GetMenu(static_cast(nPos))) : nullptr; + if (mh->menu && nPos < static_cast(mh->menu->GetMenuItemCount())) { + wxMenuItem* item = mh->menu->FindItemByPosition(static_cast(nPos)); + return item && item->GetSubMenu() ? HandleFromWxMenu(item->GetSubMenu()) : nullptr; + } + return nullptr; +} + +HMENU GetSystemMenu(HWND, BOOL) { return nullptr; } + +HMENU CreateMenu() { + return OnMain([]() -> HMENU { return HandleFromWxMenuBar(new wxMenuBar); }); +} + +HMENU CreatePopupMenu() { + return OnMain([]() -> HMENU { return HandleFromWxMenu(new wxMenu); }); +} + +BOOL DestroyMenu(HMENU hMenu) { + MenuHandle* mh = FromH(hMenu); + if (!mh) + return FALSE; + OnMain([&] { + auto tmp = TempMenus().find(mh); + if (tmp != TempMenus().end()) { + tmp->second->m_hMenu = nullptr; + delete tmp->second; + TempMenus().erase(tmp); + } + if (mh->menu && !mh->menu->GetParent() && !mh->menu->GetMenuBar()) + delete mh->menu; + if (mh->bar && !mh->bar->GetFrame()) + delete mh->bar; + Handles().erase(mh->menu ? static_cast(mh->menu) : static_cast(mh->bar)); + delete mh; + }); + return TRUE; +} + +namespace { + +BOOL InsertItem(HMENU hMenu, int position, UINT uFlags, UINT_PTR uIDNewItem, const char* lpNewItem) { + MenuHandle* mh = FromH(hMenu); + if (!mh) + return FALSE; + return OnMain([&]() -> BOOL { + wxString text = ToWx(lpNewItem ? lpNewItem : ""); + if (mh->bar) { + wxMenu* sub = (uFlags & MF_POPUP) ? WxMenuFromHandle(reinterpret_cast(uIDNewItem)) : new wxMenu; + if (position < 0 || position >= static_cast(mh->bar->GetMenuCount())) + mh->bar->Append(sub, text); + else + mh->bar->Insert(static_cast(position), sub, text); + return TRUE; + } + wxMenu* menu = mh->menu; + if (!menu) + return FALSE; + size_t pos = position < 0 || position > static_cast(menu->GetMenuItemCount()) ? menu->GetMenuItemCount() + : static_cast(position); + if (uFlags & MF_SEPARATOR) { + menu->InsertSeparator(pos); + } else if (uFlags & MF_POPUP) { + menu->Insert(pos, wxID_ANY, text, WxMenuFromHandle(reinterpret_cast(uIDNewItem))); + } else { + wxMenuItem* mi = menu->Insert(pos, MenuWxId(static_cast(uIDNewItem)), text); + if (uFlags & MF_CHECKED) { + mi->SetCheckable(true); + mi->Check(true); + } + if (uFlags & (MF_GRAYED | MF_DISABLED)) + mi->Enable(false); + } + return TRUE; + }); +} + +} // namespace + +BOOL AppendMenu(HMENU hMenu, UINT uFlags, UINT_PTR uIDNewItem, const char* lpNewItem) { + return InsertItem(hMenu, -1, uFlags, uIDNewItem, lpNewItem); +} + +BOOL AppendMenuA(HMENU hMenu, UINT uFlags, UINT_PTR uIDNewItem, const char* lpNewItem) { + return AppendMenu(hMenu, uFlags, uIDNewItem, lpNewItem); +} + +BOOL InsertMenu(HMENU hMenu, UINT uPosition, UINT uFlags, UINT_PTR uIDNewItem, const char* lpNewItem) { + int position = -1; + if (uFlags & MF_BYPOSITION) { + position = static_cast(uPosition); + } else { + MenuHandle* mh = FromH(hMenu); + if (mh && mh->menu) { + int id = MenuWxId(static_cast(uPosition)); + const wxMenuItemList& items = mh->menu->GetMenuItems(); + int i = 0; + for (auto* item : items) { + if (item->GetId() == id) { + position = i; + break; + } + ++i; + } + } + } + return InsertItem(hMenu, position, uFlags, uIDNewItem, lpNewItem); +} + +BOOL DeleteMenu(HMENU hMenu, UINT uPosition, UINT uFlags) { + return OnMain([&]() -> BOOL { + ItemRef r = FindItem(hMenu, uPosition, uFlags); + if (r.bar) { + delete r.bar->Remove(static_cast(r.barIndex)); + return TRUE; + } + if (r.parent && r.item) { + r.parent->Destroy(r.item); + return TRUE; + } + return FALSE; + }); +} + +BOOL RemoveMenu(HMENU hMenu, UINT uPosition, UINT uFlags) { + return OnMain([&]() -> BOOL { + ItemRef r = FindItem(hMenu, uPosition, uFlags); + if (r.bar) { + r.bar->Remove(static_cast(r.barIndex)); + return TRUE; + } + if (r.parent && r.item) { + r.parent->Remove(r.item); + return TRUE; + } + return FALSE; + }); +} + +BOOL ModifyMenu(HMENU hMnu, UINT uPosition, UINT uFlags, UINT_PTR uIDNewItem, const char* lpNewItem) { + return OnMain([&]() -> BOOL { + ItemRef r = FindItem(hMnu, uPosition, uFlags); + if (r.bar) { + if (lpNewItem) + r.bar->SetMenuLabel(static_cast(r.barIndex), ToWx(lpNewItem)); + return TRUE; + } + if (!r.item) + return FALSE; + if (lpNewItem && !(uFlags & MF_SEPARATOR)) + r.item->SetItemLabel(ToWx(lpNewItem)); + if (!(uFlags & MF_POPUP) && uIDNewItem && static_cast(uIDNewItem) != MenuWinId(r.item->GetId())) { + size_t pos = 0; + r.parent->FindChildItem(r.item->GetId(), &pos); + wxString label = r.item->GetItemLabel(); + r.parent->Destroy(r.item); + r.parent->Insert(pos, MenuWxId(static_cast(uIDNewItem)), label); + } + return TRUE; + }); +} + +DWORD CheckMenuItem(HMENU hMenu, UINT uIDCheckItem, UINT uCheck) { + return OnMain([&]() -> DWORD { + ItemRef r = FindItem(hMenu, uIDCheckItem, uCheck); + if (!r.item) + return 0xFFFFFFFF; + DWORD old = r.item->IsCheckable() && r.item->IsChecked() ? MF_CHECKED : MF_UNCHECKED; + if (!r.item->IsCheckable()) + r.item->SetCheckable(true); + r.item->Check((uCheck & MF_CHECKED) != 0); + return old; + }); +} + +BOOL EnableMenuItem(HMENU hMenu, UINT uIDEnableItem, UINT uEnable) { + return OnMain([&]() -> BOOL { + ItemRef r = FindItem(hMenu, uIDEnableItem, uEnable); + bool enable = (uEnable & (MF_GRAYED | MF_DISABLED)) == 0; + if (r.bar) { + BOOL old = r.bar->IsEnabledTop(static_cast(r.barIndex)) ? MF_ENABLED : MF_GRAYED; + r.bar->EnableTop(static_cast(r.barIndex), enable); + return old; + } + if (!r.item) + return static_cast(0xFFFFFFFF); + BOOL old = r.item->IsEnabled() ? MF_ENABLED : MF_GRAYED; + r.item->Enable(enable); + return old; + }); +} + +int GetMenuItemCount(HMENU hMenu) { + MenuHandle* mh = FromH(hMenu); + if (!mh) + return -1; + return OnMain([&]() -> int { + return mh->bar ? static_cast(mh->bar->GetMenuCount()) : static_cast(mh->menu->GetMenuItemCount()); + }); +} + +UINT GetMenuItemID(HMENU hMenu, int nPos) { + return OnMain([&]() -> UINT { + ItemRef r = FindItem(hMenu, static_cast(nPos), MF_BYPOSITION); + if (!r.item) + return static_cast(-1); + if (r.item->IsSubMenu()) + return static_cast(-1); + if (r.item->IsSeparator()) + return 0; + return static_cast(MenuWinId(r.item->GetId())); + }); +} + +UINT GetMenuState(HMENU hMenu, UINT uId, UINT uFlags) { + return OnMain([&]() -> UINT { + ItemRef r = FindItem(hMenu, uId, uFlags); + if (r.bar) + return r.bar->IsEnabledTop(static_cast(r.barIndex)) ? MF_POPUP : (MF_POPUP | MF_GRAYED); + if (!r.item) + return static_cast(-1); + UINT state = 0; + if (r.item->IsSeparator()) + state |= MF_SEPARATOR; + if (r.item->IsSubMenu()) + state |= MF_POPUP | (static_cast(r.item->GetSubMenu()->GetMenuItemCount()) << 8); + if (!r.item->IsEnabled()) + state |= MF_GRAYED; + if (r.item->IsCheckable() && r.item->IsChecked()) + state |= MF_CHECKED; + return state; + }); +} + +int GetMenuString(HMENU hMenu, UINT uIDItem, char* lpString, int cchMax, UINT flags) { + return OnMain([&]() -> int { + ItemRef r = FindItem(hMenu, uIDItem, flags); + wxString label; + if (r.bar) + label = r.bar->GetMenuLabel(static_cast(r.barIndex)); + else if (r.item) + label = r.item->GetItemLabel(); + std::string a = FromWx(label); + if (lpString && cchMax > 0) { + strncpy(lpString, a.c_str(), static_cast(cchMax) - 1); + lpString[cchMax - 1] = 0; + } + return static_cast(a.size()); + }); +} + +BOOL TrackPopupMenu(HMENU hMenu, UINT uFlags, int x, int y, int, HWND hWnd, const RECT*) { + wxMenu* menu = WxMenuFromHandle(hMenu); + if (!menu || !IsWindow(hWnd)) + return FALSE; + return OnMain([&]() -> BOOL { + wxWindow* w = ToWx(hWnd); + wxPoint p = w->ScreenToClient(wxPoint(x, y)); + std::unique_ptr copy(CloneMenu(menu)); + int id = w->GetPopupMenuSelectionFromUser(*copy, p); + if (id == wxID_NONE) + return 0; + int cmd = MenuWinId(id); + if (uFlags & TPM_RETURNCMD) + return cmd; + DispatchMessageTo(hWnd, WM_COMMAND, MAKEWPARAM(cmd, 0), 0); + return TRUE; + }); +} + +BOOL DrawMenuBar(HWND hWnd) { + if (IsWindow(hWnd)) + OnMain([&] { + if (auto* frame = wxDynamicCast(wxGetTopLevelParent(ToWx(hWnd)), wxFrame)) + if (frame->GetMenuBar()) + frame->GetMenuBar()->Refresh(); + }); + return TRUE; +} diff --git a/port/mfcwx/src/windows_impl.h b/port/mfcwx/src/windows_impl.h index 4d7a571a5..093db125d 100644 --- a/port/mfcwx/src/windows_impl.h +++ b/port/mfcwx/src/windows_impl.h @@ -89,6 +89,12 @@ void NotifyParent(wxWindow* control, UINT code); // Sends WM_NOTIFY with the given header (hwndFrom/idFrom are filled in). LRESULT NotifyParentNM(wxWindow* control, NMHDR* hdr); +// menu.cpp: command ids of menu items (ID_APP_ABOUT/ID_APP_EXIT map to the wx stock ids) +int MenuWxId(int winId); +int MenuWinId(int wxId); +// Accelerator table whose keys are shown in menus built afterwards. +void SetMenuAccelerators(const rc::AccelTable* table); + // dialog.cpp wxWindow* CreateDialogWindow(CDialog* dlg, const rc::Dialog& tmpl, wxWindow* parent); BOOL ApplyDlgInit(CWnd* pWnd, int dialogId); From a0ec65e313746675f69d2e77fd4d238fc902c6de Mon Sep 17 00:00:00 2001 From: Kamil Marczak Date: Tue, 6 Oct 2026 17:35:27 +0200 Subject: [PATCH 07/46] feat(secude): replace the proprietary SECUDE library with an OpenSSL 3 implementation secude_compat implements every function and data object CrypTool loads from SECUDE.DLL: PSE key stores (AES-256-GCM, PBKDF2), CA database, X.509 certificates, PKCS#12, L_NUMBER arithmetic, legacy hashes and ciphers with built-in fallbacks. SecudeLib.cpp and libec bind to it off Windows. --- port/secude/README.md | 4 ++++ port/secude/src/legacy_hash.cpp | 4 ++++ port/secude/tests/test_cipher.cpp | 21 +++++++++++++++------ secude/secude/af_p.h | 2 ++ 4 files changed, 25 insertions(+), 6 deletions(-) diff --git a/port/secude/README.md b/port/secude/README.md index bd1a007fd..ff96040b1 100644 --- a/port/secude/README.md +++ b/port/secude/README.md @@ -40,6 +40,10 @@ the OpenSSL legacy provider and with `SECUDE_COMPAT_NO_LEGACY_PROVIDER=1`. | `rsa_aid` (2.5.8.1.1, INTEGER keysize), `rsaEncryption_aid` | OpenSSL BIGNUM | | `dsa_aid` (1.3.14.3.2.12, Dss-Parms) | OpenSSL BIGNUM | +The legacy provider is loaded into a private `OSSL_LIB_CTX`; whatever it +does not offer (all of it if it is missing, IDEA on Debian) is served by the +built-in implementations, which the tests cross-check against OpenSSL. + Notes on behaviour that CrypTool relies on: * Hash results are always written to freshly allocated `octets`; input and diff --git a/port/secude/src/legacy_hash.cpp b/port/secude/src/legacy_hash.cpp index 0dbc62ba0..bd60fcce0 100644 --- a/port/secude/src/legacy_hash.cpp +++ b/port/secude/src/legacy_hash.cpp @@ -152,6 +152,8 @@ void md5_transform(uint32_t state[4], const uint8_t *block) template void md_update(Ctx *c, const uint8_t *p, size_t n) { + if (n == 0) + return; size_t index = (c->count[0] >> 3) & 0x3f; uint64_t bits = (uint64_t(c->count[1]) << 32 | c->count[0]) + (uint64_t(n) << 3); c->count[0] = uint32_t(bits); @@ -460,6 +462,8 @@ void sec_MD2Init(SEC_MD2_CTX *context) void sec_MD2Update(SEC_MD2_CTX *context, unsigned char *input, sec_uint4 inputLen) { + if (inputLen == 0) + return; unsigned index = context->count; context->count = (index + inputLen) & 0xf; unsigned part = 16 - index; diff --git a/port/secude/tests/test_cipher.cpp b/port/secude/tests/test_cipher.cpp index ef197beac..9607e460d 100644 --- a/port/secude/tests/test_cipher.cpp +++ b/port/secude/tests/test_cipher.cpp @@ -3,6 +3,7 @@ #include #include +#include using namespace compat; @@ -179,8 +180,15 @@ TEST(cipher_builtin_matches_openssl_legacy) OSSL_LIB_CTX_free(ctx); return; } + // returns the input unchanged if the cipher is missing from this OpenSSL build (e.g. IDEA on Debian) + std::set missing; auto ref = [&](const char *name, const Bytes &key, unsigned rc2bits, const Bytes &in) { EVP_CIPHER *c = EVP_CIPHER_fetch(ctx, name, nullptr); + if (!c) { + if (missing.insert(name).second) + std::printf(" (%s not provided by this OpenSSL, cross-check skipped)\n", name); + return in; + } EVP_CIPHER_CTX *cc = EVP_CIPHER_CTX_new(); EVP_CipherInit_ex2(cc, c, nullptr, nullptr, 1, nullptr); size_t keylen = key.size(), bits = rc2bits; @@ -205,6 +213,7 @@ TEST(cipher_builtin_matches_openssl_legacy) EVP_CIPHER_free(c); return out; }; + auto same = [](const Bytes &own, const Bytes &reference, const Bytes &in) { return reference == in || own == reference; }; auto ecb = [](legacy::BlockCipher &bc, const Bytes &in) { Bytes out(in.size()); for (size_t i = 0; i < in.size(); i += 8) @@ -214,16 +223,16 @@ TEST(cipher_builtin_matches_openssl_legacy) for (int round = 0; round < 20; ++round) { Bytes data = random_bytes(64); Bytes k8 = random_bytes(8), k16 = random_bytes(16), k24 = random_bytes(24); - CHECK(ecb(*legacy::make_des(k8.data()), data) == ref("DES-ECB", k8, 0, data)); - CHECK(ecb(*legacy::make_des3(k16.data(), 16), data) == ref("DES-EDE", k16, 0, data)); - CHECK(ecb(*legacy::make_des3(k24.data(), 24), data) == ref("DES-EDE3", k24, 0, data)); - CHECK(ecb(*legacy::make_idea(k16.data()), data) == ref("IDEA-ECB", k16, 0, data)); + CHECK(same(ecb(*legacy::make_des(k8.data()), data), ref("DES-ECB", k8, 0, data), data)); + CHECK(same(ecb(*legacy::make_des3(k16.data(), 16), data), ref("DES-EDE", k16, 0, data), data)); + CHECK(same(ecb(*legacy::make_des3(k24.data(), 24), data), ref("DES-EDE3", k24, 0, data), data)); + CHECK(same(ecb(*legacy::make_idea(k16.data()), data), ref("IDEA-ECB", k16, 0, data), data)); size_t rl = size_t(1 + round % 16); Bytes kr = random_bytes(rl); - CHECK(ecb(*legacy::make_rc2(kr.data(), kr.size(), unsigned(rl * 8)), data) == ref("RC2-ECB", kr, unsigned(rl * 8), data)); + CHECK(same(ecb(*legacy::make_rc2(kr.data(), kr.size(), unsigned(rl * 8)), data), ref("RC2-ECB", kr, unsigned(rl * 8), data), data)); Bytes rc4out(data.size()); legacy::rc4(kr.data(), kr.size(), data.data(), rc4out.data(), data.size()); - CHECK(rc4out == ref("RC4", kr, 0, data)); + CHECK(same(rc4out, ref("RC4", kr, 0, data), data)); } OSSL_LIB_CTX_free(ctx); } diff --git a/secude/secude/af_p.h b/secude/secude/af_p.h index 46fb3543d..6e6af9eda 100644 --- a/secude/secude/af_p.h +++ b/secude/secude/af_p.h @@ -5527,11 +5527,13 @@ char SEC_GLOBAL_FUNC_PREFIX * SEC_API_CALLING_CONV str_up SEC_PROTOTYPE_1( ); +#ifdef _WIN32 /* elsewhere the name belongs to cracklib's char *Trim(char *) */ int SEC_GLOBAL_FUNC_PREFIX SEC_API_CALLING_CONV Trim SEC_PROTOTYPE_1( char * , str ); +#endif int SEC_GLOBAL_FUNC_PREFIX SEC_API_CALLING_CONV Skip_blanks SEC_PROTOTYPE_2( From 105464b9e2a7ef13d52196993bd78629a04a373f Mon Sep 17 00:00:00 2001 From: Kamil Marczak Date: Tue, 6 Oct 2026 17:35:27 +0200 Subject: [PATCH 08/46] fix(thirdparty): mask RC6 rotation counts and fix the libec smoke test RC6 relied on x86 shift semantics (undefined behaviour) and produced wrong ciphertext on arm64. The libec test now uses prime256v1 with independently computed reference values. --- aes/RC6/rc6.c | 4 ++-- port/thirdparty/tests/test_ec.cpp | 21 ++++++++++++--------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/aes/RC6/rc6.c b/aes/RC6/rc6.c index b5d5c05b3..b16f9d3cc 100644 --- a/aes/RC6/rc6.c +++ b/aes/RC6/rc6.c @@ -32,10 +32,10 @@ * shifts: when shifting a [32-bit] dword, the shift count is treated as * a number modulo 32. */ #define ROTL(x,count) \ - (((x) >> (thirtytwo-(count))) | ((x) << (count))) + (((x) >> ((thirtytwo-(count)) & 31)) | ((x) << ((count) & 31))) #define ROTR(x,count) \ - (((x) << (thirtytwo-(count))) | ((x) >> (count))) + (((x) << ((thirtytwo-(count)) & 31)) | ((x) >> ((count) & 31))) diff --git a/port/thirdparty/tests/test_ec.cpp b/port/thirdparty/tests/test_ec.cpp index 78f7a1f45..038337c54 100644 --- a/port/thirdparty/tests/test_ec.cpp +++ b/port/thirdparty/tests/test_ec.cpp @@ -53,21 +53,24 @@ int main() { __CurveFp_struct curve; __PointAc_struct G, R, S, T; L_NUMBER cofactor[MAXLGTH], order[MAXLGTH]; - char id[] = "prime192v1"; + // prime192v1..v3 cannot be used: their literals in s_ecpcur.c contain TABs, + // which libec's string_to_ln() rejects (dead code for CrypTool, also on Windows); + // string_to_ln() also only accepts upper-case hex digits. + char id[] = "prime256v1"; CHECK(X9_62_init_curve_Fp(&curve, &G, cofactor, order, id) == 0); - CHECK(hex_of(ecFp_point_getstr_xcoord_ac(&G, 16)) == "188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF1012"); - CHECK(hex_of(ecFp_point_getstr_ycoord_ac(&G, 16)) == "7192B95FFC8DA78631011ED6B24CDD573F977A11E794811"); + CHECK(hex_of(ecFp_point_getstr_xcoord_ac(&G, 16)) == "6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296"); + CHECK(hex_of(ecFp_point_getstr_ycoord_ac(&G, 16)) == "4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5"); CHECK(ecFp_mult_ac_str(&R, &curve, "2", &G) == 0); - CHECK(hex_of(ecFp_point_getstr_xcoord_ac(&R, 16)) == "DAFEBF5828783F2AD35534631588A3F629A70FB16982A888"); - CHECK(hex_of(ecFp_point_getstr_ycoord_ac(&R, 16)) == "DD6BDA0D993DA0FA46B27BBC141B868F59331AFA5C7E93AB"); + CHECK(hex_of(ecFp_point_getstr_xcoord_ac(&R, 16)) == "7CF27B188D034F7E8A52380304B51AC3C08969E277F21B35A60B48FC47669978"); + CHECK(hex_of(ecFp_point_getstr_ycoord_ac(&R, 16)) == "7775510DB8ED040293D9AC69F7430DBBA7DADE63CE982299E04B79D227873D1"); - CHECK(ecFp_mult_ac_str(&S, &curve, "0x1234567890abcdef1234567890abcdef", &G) == 0); - CHECK(hex_of(ecFp_point_getstr_xcoord_ac(&S, 16)) == "BEDAC67FB3E84EB498A1703B7F3FB0A0AAAB39CA554C2CE9"); - CHECK(hex_of(ecFp_point_getstr_ycoord_ac(&S, 16)) == "C19EC031FB537035B750105FC0BBE37E839D65DED6595639"); + CHECK(ecFp_mult_ac_str(&S, &curve, "0X1234567890ABCDEF1234567890ABCDEF", &G) == 0); + CHECK(hex_of(ecFp_point_getstr_xcoord_ac(&S, 16)) == "F9EBE464147CC102D92324D099D927C1A50E42D57A08A116FFEC0A29819D8C65"); + CHECK(hex_of(ecFp_point_getstr_ycoord_ac(&S, 16)) == "70BAF7D12888902A6A0C1D67ADD103123C859AB69BF8E9431C9384930D4B7EEE"); CHECK(ecFp_add_ac(&T, &curve, &G, &G) == 0); - CHECK(hex_of(ecFp_point_getstr_xcoord_ac(&T, 16)) == "DAFEBF5828783F2AD35534631588A3F629A70FB16982A888"); + CHECK(hex_of(ecFp_point_getstr_xcoord_ac(&T, 16)) == "7CF27B188D034F7E8A52380304B51AC3C08969E277F21B35A60B48FC47669978"); CHECK(ecFp_mult_ac(&T, &curve, order, &G) == 0); CHECK(ecFp_point_check_infinity_ac(&T)); From f7687479ade23a5466437bf18f88c95e015476a9 Mon Sep 17 00:00:00 2001 From: Kamil Marczak Date: Tue, 6 Oct 2026 17:36:19 +0200 Subject: [PATCH 09/46] feat(mfcwx): add document/view architecture with tabbed MDI frames CMDIFrameWnd hosts child frames as notebook tabs and switches the menu bar per document template; includes CDocument, CView, CScrollView, document templates, toolbar and status bar from resources, rich edit view and MRU list. --- port/mfcwx/include/mfcwx/docview.h | 4 +- port/mfcwx/src/docview.cpp | 1737 ++++++++++++++++++++++++++++ 2 files changed, 1740 insertions(+), 1 deletion(-) create mode 100644 port/mfcwx/src/docview.cpp diff --git a/port/mfcwx/include/mfcwx/docview.h b/port/mfcwx/include/mfcwx/docview.h index acf71f23e..6bed5ea9b 100644 --- a/port/mfcwx/include/mfcwx/docview.h +++ b/port/mfcwx/include/mfcwx/docview.h @@ -263,8 +263,9 @@ class CMDIFrameWnd : public CFrameWnd { afx_msg void OnWindowTile(); afx_msg void OnUpdateMDIWindowCmd(CCmdUI* pCmdUI); - // implementation: all MDI children in creation order + // implementation: all MDI children in creation order; notifies children and menus of tab switches std::vector m_children; + void OnChildActivated(); protected: DECLARE_MESSAGE_MAP() @@ -295,6 +296,7 @@ class CMDIChildWnd : public CFrameWnd { HMENU m_hMenuShared; CMDIFrameWnd* m_pMDIFrame; + bool m_bActive; protected: DECLARE_MESSAGE_MAP() diff --git a/port/mfcwx/src/docview.cpp b/port/mfcwx/src/docview.cpp new file mode 100644 index 000000000..1170fbacb --- /dev/null +++ b/port/mfcwx/src/docview.cpp @@ -0,0 +1,1737 @@ +#include "windows_impl.h" + +#include "afxdlgs.h" +#include "afxext.h" +#include "afxrich.h" + +#include +#include +#include + +namespace mfcwx { + +namespace { + +struct AFX_NOTIFY_LOCAL { + LRESULT* pResult; + NMHDR* pNMHDR; +}; + +CString ResourceString(UINT id) { + CString s; + s.LoadString(id); + return s; +} + +// Main window of an MDI application: menu, toolbar, status bar and a notebook of child frames. +class MfcFrame : public wxFrame { +public: + MfcFrame(wxWindow* parent, const wxString& title, const wxPoint& pos, const wxSize& size, long style) + : wxFrame(parent, wxID_ANY, title, pos, size, style) { + Bind(wxEVT_CLOSE_WINDOW, [this](wxCloseEvent& e) { + if (IsManagedWindow(this) && PermanentWnd(this)) { + e.Veto(); + DispatchMessageTo(ToHwnd(this), WM_CLOSE, 0, 0); + } else { + e.Skip(); + } + }); + Bind(wxEVT_MENU, [this](wxCommandEvent& e) { + int id = MenuWinId(e.GetId()); + if (id >= ID_FILE_MRU_FILE1 && id <= ID_FILE_MRU_FILE16 && e.GetId() >= wxID_FILE1 && e.GetId() <= wxID_FILE9) + id = ID_FILE_MRU_FILE1 + (e.GetId() - wxID_FILE1); + DispatchMessageTo(ToHwnd(this), WM_COMMAND, MAKEWPARAM(id, 0), 0); + }); + Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& e) { UpdateCommandUI(this, e); }); + Bind(wxEVT_MENU_HIGHLIGHT, [this](wxMenuEvent& e) { + CWnd* p = PermanentWnd(this); + if (p && p->IsKindOf(RUNTIME_CLASS(CFrameWnd)) && e.GetMenuId() >= kIdOffset) { + CString text; + static_cast(p)->GetMessageString(static_cast(MenuWinId(e.GetMenuId())), text); + static_cast(p)->SetMessageText(text); + } else { + e.Skip(); + } + }); + Bind(wxEVT_CHAR_HOOK, [this](wxKeyEvent& e) { + if (!PreTranslateKey(this, e, e.AltDown() ? WM_SYSKEYDOWN : WM_KEYDOWN)) + e.Skip(); + }); + } + + static void UpdateCommandUI(wxWindow* frame, wxUpdateUIEvent& e) { + CWnd* p = PermanentWnd(frame); + if (!p || e.GetId() < kIdOffset - 1 || !p->IsKindOf(RUNTIME_CLASS(CFrameWnd))) { + if (e.GetId() == wxID_ABOUT || e.GetId() == wxID_EXIT) + e.Enable(true); + return; + } + auto* frameWnd = static_cast(p); + CCmdUI ui; + ui.m_nID = static_cast(MenuWinId(e.GetId())); + ui.m_pOther = nullptr; + ui.DoUpdate(frameWnd, frameWnd->m_bAutoMenuEnable); + e.Enable(ui.m_enabled); + if (ui.m_checkSet) + e.Check(ui.m_check != 0); + if (ui.m_textSet) + e.SetText(ToWx(ui.m_text)); + } +}; + +class MfcChildPanel : public wxPanel { +public: + explicit MfcChildPanel(wxWindow* parent) : wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxBORDER_NONE) {} +}; + +wxAuiNotebook* ClientNotebook(CMDIFrameWnd* frame) { + return frame && frame->m_hWndMDIClient ? wxDynamicCast(ToWx(frame->m_hWndMDIClient), wxAuiNotebook) : nullptr; +} + +CMDIFrameWnd* MainMDIFrame() { + CWinApp* app = AfxGetApp(); + CWnd* main = app ? app->m_pMainWnd : nullptr; + if (main && main->IsKindOf(RUNTIME_CLASS(CMDIFrameWnd))) + return static_cast(main); + return nullptr; +} + +} // namespace + +wxWindow* MainWxWindow() { + CWinApp* app = AfxGetApp(); + return app && app->m_pMainWnd && app->m_pMainWnd->m_hWnd ? app->m_pMainWnd->GetWx() : nullptr; +} + +} // namespace mfcwx + +using namespace mfcwx; + +// --------------------------------------------------------------------------------------------- +// CPrintInfo + +CPrintInfo::CPrintInfo() + : m_pPD(nullptr), m_bDirect(FALSE), m_bPreview(FALSE), m_bContinuePrinting(TRUE), m_nCurPage(1), + m_nNumPreviewPages(1), m_lpUserData(nullptr), m_nMinPage(1), m_nMaxPage(0xFFFF) {} + +CPrintInfo::~CPrintInfo() {} + +// --------------------------------------------------------------------------------------------- +// CDocument + +IMPLEMENT_DYNAMIC(CDocument, CCmdTarget) + +BEGIN_MESSAGE_MAP(CDocument, CCmdTarget) + ON_COMMAND(ID_FILE_CLOSE, &CDocument::OnFileClose) + ON_COMMAND(ID_FILE_SAVE, &CDocument::OnFileSave) + ON_COMMAND(ID_FILE_SAVE_AS, &CDocument::OnFileSaveAs) +END_MESSAGE_MAP() + +CDocument::CDocument() : m_pDocTemplate(nullptr), m_bModified(FALSE), m_bAutoDelete(TRUE), m_bEmbedded(FALSE) {} + +CDocument::~CDocument() { + if (m_pDocTemplate) + m_pDocTemplate->RemoveDocument(this); +} + +void CDocument::SetTitle(const char* lpszTitle) { + m_strTitle = lpszTitle; + for (CView* v : m_viewList) { + CFrameWnd* frame = v->GetParentFrame(); + if (frame) + frame->OnUpdateFrameTitle(TRUE); + } +} + +void CDocument::SetPathName(const char* lpszPathName, BOOL bAddToMRU) { + m_strPathName = lpszPathName; + CString title = m_strPathName; + int slash = title.ReverseFind('\\'); + if (slash >= 0) + title = title.Mid(slash + 1); + SetTitle(title); + if (bAddToMRU && AfxGetApp()) + AfxGetApp()->AddToRecentFileList(lpszPathName); +} + +POSITION CDocument::GetFirstViewPosition() const { + return m_viewList.empty() ? nullptr : reinterpret_cast(static_cast(1)); +} + +CView* CDocument::GetNextView(POSITION& rPosition) const { + size_t i = static_cast(reinterpret_cast(rPosition)) - 1; + CView* v = i < m_viewList.size() ? m_viewList[i] : nullptr; + rPosition = i + 1 < m_viewList.size() ? reinterpret_cast(static_cast(i + 2)) : nullptr; + return v; +} + +void CDocument::UpdateAllViews(CView* pSender, LPARAM lHint, CObject* pHint) { + std::vector views = m_viewList; + for (CView* v : views) + if (v != pSender) + v->OnUpdate(pSender, lHint, pHint); +} + +void CDocument::AddView(CView* pView) { + if (std::find(m_viewList.begin(), m_viewList.end(), pView) == m_viewList.end()) { + m_viewList.push_back(pView); + pView->m_pDocument = this; + OnChangedViewList(); + } +} + +void CDocument::RemoveView(CView* pView) { + auto it = std::find(m_viewList.begin(), m_viewList.end(), pView); + if (it == m_viewList.end()) + return; + m_viewList.erase(it); + pView->m_pDocument = nullptr; + OnChangedViewList(); +} + +void CDocument::OnChangedViewList() { + if (m_viewList.empty() && m_bAutoDelete) + OnCloseDocument(); +} + +void CDocument::SendInitialUpdate() { + std::vector views = m_viewList; + for (CView* v : views) + v->OnInitialUpdate(); +} + +BOOL CDocument::OnNewDocument() { + DeleteContents(); + m_strPathName.Empty(); + SetModifiedFlag(FALSE); + return TRUE; +} + +CFile* CDocument::GetFile(const char* lpszFileName, UINT nOpenFlags, CFileException* pError) { + auto* f = new CFile; + if (!f->Open(lpszFileName, nOpenFlags, pError)) { + delete f; + return nullptr; + } + return f; +} + +void CDocument::ReleaseFile(CFile* pFile, BOOL bAbort) { + if (bAbort) + pFile->Abort(); + else + pFile->Close(); + delete pFile; +} + +BOOL CDocument::OnOpenDocument(const char* lpszPathName) { + CFileException fe; + CFile* pFile = GetFile(lpszPathName, CFile::modeRead | CFile::shareDenyWrite, &fe); + if (!pFile) { + ReportSaveLoadException(lpszPathName, &fe, FALSE, AFX_IDP_FAILED_TO_OPEN_DOC); + return FALSE; + } + DeleteContents(); + SetModifiedFlag(TRUE); + try { + CArchive ar(pFile, CArchive::load | CArchive::bNoFlushOnDelete); + ar.m_pDocument = this; + ar.m_strFileName = lpszPathName; + Serialize(ar); + ar.Close(); + ReleaseFile(pFile, FALSE); + } catch (CException* e) { + ReleaseFile(pFile, TRUE); + DeleteContents(); + ReportSaveLoadException(lpszPathName, e, FALSE, AFX_IDP_FAILED_TO_OPEN_DOC); + e->Delete(); + return FALSE; + } + SetModifiedFlag(FALSE); + return TRUE; +} + +BOOL CDocument::OnSaveDocument(const char* lpszPathName) { + CFileException fe; + CFile* pFile = GetFile(lpszPathName, CFile::modeCreate | CFile::modeReadWrite | CFile::shareExclusive, &fe); + if (!pFile) { + ReportSaveLoadException(lpszPathName, &fe, TRUE, AFX_IDP_INVALID_FILENAME); + return FALSE; + } + try { + CArchive ar(pFile, CArchive::store | CArchive::bNoFlushOnDelete); + ar.m_pDocument = this; + ar.m_strFileName = lpszPathName; + Serialize(ar); + ar.Close(); + ReleaseFile(pFile, FALSE); + } catch (CException* e) { + ReleaseFile(pFile, TRUE); + ReportSaveLoadException(lpszPathName, e, TRUE, AFX_IDP_FAILED_TO_SAVE_DOC); + e->Delete(); + return FALSE; + } + SetModifiedFlag(FALSE); + return TRUE; +} + +void CDocument::ReportSaveLoadException(const char* lpszPathName, CException* e, BOOL bSaving, UINT nIDPDefault) { + char msg[512]; + if (e && e->GetErrorMessage(msg, sizeof msg)) { + AfxMessageBox(msg, MB_ICONEXCLAMATION); + return; + } + CString prompt; + AfxFormatString1(prompt, nIDPDefault, lpszPathName); + if (prompt.IsEmpty()) + prompt.Format(bSaving ? "Failed to save document %s." : "Failed to open document %s.", lpszPathName); + AfxMessageBox(prompt, MB_ICONEXCLAMATION); +} + +void CDocument::OnCloseDocument() { + BOOL autoDelete = m_bAutoDelete; + m_bAutoDelete = FALSE; + std::vector views = m_viewList; + for (CView* v : views) { + CFrameWnd* frame = v->GetParentFrame(); + if (frame) { + PreCloseFrame(frame); + frame->DestroyWindow(); + } + } + m_bAutoDelete = autoDelete; + DeleteContents(); + if (m_bAutoDelete) + delete this; +} + +BOOL CDocument::CanCloseFrame(CFrameWnd* pFrame) { + int views = 0; + for (CView* v : m_viewList) + if (v->GetParentFrame() == pFrame) + ++views; + if (static_cast(views) < m_viewList.size()) + return TRUE; + return SaveModified(); +} + +BOOL CDocument::SaveModified() { + if (!IsModified()) + return TRUE; + CString name = m_strPathName.IsEmpty() ? m_strTitle : m_strPathName; + CString prompt; + AfxFormatString1(prompt, AFX_IDP_ASK_TO_SAVE, name); + if (prompt.IsEmpty()) + prompt.Format("Save changes to %s?", name.GetString()); + switch (AfxMessageBox(prompt, MB_YESNOCANCEL | MB_ICONQUESTION)) { + case IDCANCEL: + return FALSE; + case IDYES: + return DoFileSave(); + default: + return TRUE; + } +} + +BOOL CDocument::DoFileSave() { + if (m_strPathName.IsEmpty() || access(NativePath(m_strPathName).c_str(), W_OK) != 0 && access(NativePath(m_strPathName).c_str(), F_OK) == 0) + return DoSave(nullptr); + return DoSave(m_strPathName); +} + +BOOL CDocument::DoSave(const char* lpszPathName, BOOL bReplace) { + CString newName = lpszPathName ? lpszPathName : ""; + if (newName.IsEmpty()) { + newName = m_strPathName.IsEmpty() ? m_strTitle : m_strPathName; + if (!AfxGetApp()->DoPromptFileName(newName, AFX_IDS_SAVEFILE, OFN_HIDEREADONLY | OFN_PATHMUSTEXIST, + FALSE, m_pDocTemplate)) + return FALSE; + } + if (!OnSaveDocument(newName)) + return FALSE; + if (bReplace) + SetPathName(newName); + return TRUE; +} + +void CDocument::OnFileClose() { + if (!SaveModified()) + return; + OnCloseDocument(); +} + +void CDocument::OnFileSave() { DoFileSave(); } +void CDocument::OnFileSaveAs() { DoSave(nullptr); } + +BOOL CDocument::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo) { + if (CCmdTarget::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo)) + return TRUE; + return m_pDocTemplate && m_pDocTemplate->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo); +} + +// --------------------------------------------------------------------------------------------- +// CView + +IMPLEMENT_DYNAMIC(CView, CWnd) + +BEGIN_MESSAGE_MAP(CView, CWnd) + ON_WM_PAINT() + ON_WM_CREATE() + ON_WM_DESTROY() + ON_COMMAND(ID_FILE_PRINT, &CView::OnFilePrint) + ON_COMMAND(ID_FILE_PRINT_DIRECT, &CView::OnFilePrint) + ON_COMMAND(ID_FILE_PRINT_PREVIEW, &CView::OnFilePrintPreview) +END_MESSAGE_MAP() + +CView::CView() : m_pDocument(nullptr) {} + +CView::~CView() { + if (m_pDocument) + m_pDocument->RemoveView(this); +} + +BOOL CView::PreCreateWindow(CREATESTRUCT& cs) { + cs.style |= WS_CLIPSIBLINGS; + return TRUE; +} + +int CView::OnCreate(LPCREATESTRUCT lpcs) { + auto* context = static_cast(lpcs->lpCreateParams); + if (context && context->m_pCurrentDoc) + context->m_pCurrentDoc->AddView(this); + return 0; +} + +void CView::OnDestroy() { + CFrameWnd* frame = GetParentFrame(); + if (frame && frame->GetActiveView() == this) + frame->SetActiveView(nullptr, FALSE); +} + +void CView::PostNcDestroy() { delete this; } + +void CView::OnPaint() { + CPaintDC dc(this); + OnPrepareDC(&dc); + OnDraw(&dc); +} + +BOOL CView::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo) { + if (CWnd::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo)) + return TRUE; + return m_pDocument && m_pDocument->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo); +} + +void CView::OnInitialUpdate() { OnUpdate(nullptr, 0, nullptr); } +void CView::OnUpdate(CView*, LPARAM, CObject*) { Invalidate(TRUE); } +void CView::OnPrepareDC(CDC*, CPrintInfo*) {} +void CView::OnActivateView(BOOL bActivate, CView*, CView*) { + if (bActivate && m_hWnd) + SetFocus(); +} +BOOL CView::OnPreparePrinting(CPrintInfo* pInfo) { return DoPreparePrinting(pInfo); } +void CView::OnBeginPrinting(CDC*, CPrintInfo*) {} +void CView::OnPrint(CDC* pDC, CPrintInfo*) { OnDraw(pDC); } +void CView::OnEndPrinting(CDC*, CPrintInfo*) {} +BOOL CView::DoPreparePrinting(CPrintInfo*) { return FALSE; } + +void CView::OnFilePrint() { + AfxMessageBox("Printing is not supported in this version.", MB_ICONINFORMATION); +} + +void CView::OnFilePrintPreview() { OnFilePrint(); } + +IMPLEMENT_DYNAMIC(CScrollView, CView) + +const SIZE CScrollView::sizeDefault = {0, 0}; + +CScrollView::CScrollView() : m_nMapMode(MM_TEXT) {} + +void CScrollView::SetScrollSizes(int nMapMode, SIZE sizeTotal, const SIZE& sizePage, const SIZE& sizeLine) { + m_nMapMode = nMapMode; + m_totalLog = sizeTotal; + m_totalDev = sizeTotal; + m_pageDev = sizePage; + m_lineDev = sizeLine; + if (!m_hWnd) + return; + SCROLLINFO si; + memset(&si, 0, sizeof si); + si.fMask = SIF_RANGE | SIF_PAGE; + CRect rc; + GetClientRect(&rc); + si.nMax = m_totalDev.cx - 1; + si.nPage = static_cast(rc.Width()); + SetScrollInfo(SB_HORZ, &si, TRUE); + si.nMax = m_totalDev.cy - 1; + si.nPage = static_cast(rc.Height()); + SetScrollInfo(SB_VERT, &si, TRUE); +} + +void CScrollView::SetScaleToFitSize(SIZE sizeTotal) { SetScrollSizes(MM_TEXT, sizeTotal); } +CPoint CScrollView::GetScrollPosition() const { return CPoint(GetScrollPos(SB_HORZ), GetScrollPos(SB_VERT)); } +CPoint CScrollView::GetDeviceScrollPosition() const { return GetScrollPosition(); } +void CScrollView::ScrollToPosition(POINT pt) { + SetScrollPos(SB_HORZ, pt.x); + SetScrollPos(SB_VERT, pt.y); + Invalidate(); +} +void CScrollView::FillOutsideRect(CDC*, CBrush*) {} +void CScrollView::ResizeParentToFit(BOOL) {} +void CScrollView::OnPrepareDC(CDC* pDC, CPrintInfo* pInfo) { + CView::OnPrepareDC(pDC, pInfo); + CPoint pos = GetScrollPosition(); + pDC->SetViewportOrg(-pos.x, -pos.y); +} + +IMPLEMENT_DYNAMIC(CFormView, CScrollView) +CFormView::CFormView(UINT nIDTemplate) : m_nIDTemplate(nIDTemplate) {} +CFormView::CFormView(const char* lpszTemplateName) : m_nIDTemplate(static_cast(ResRef::From(lpszTemplateName).id)) {} +void CFormView::OnInitialUpdate() { + const rc::Dialog* tmpl = FindDialog(ResRef::FromId(m_nIDTemplate)); + if (tmpl && m_hWnd && GetWx()->GetChildren().empty()) { + EnsureState(GetWx()).baseUnits = DialogUnitsToPixels(nullptr, 0, 0, 4, 8).GetSize(); + CreateDialogControls(GetWx(), *tmpl); + ApplyDlgInit(this, tmpl->id); + } + UpdateData(FALSE); + CScrollView::OnInitialUpdate(); +} + +// --------------------------------------------------------------------------------------------- +// CFrameWnd + +IMPLEMENT_DYNCREATE(CFrameWnd, CWnd) + +BEGIN_MESSAGE_MAP(CFrameWnd, CWnd) + ON_WM_CREATE() + ON_WM_CLOSE() + ON_WM_DESTROY() + ON_WM_SIZE() + ON_WM_SETFOCUS() + ON_UPDATE_COMMAND_UI(ID_VIEW_STATUS_BAR, &CFrameWnd::OnUpdateControlBarMenu) + ON_UPDATE_COMMAND_UI(ID_VIEW_TOOLBAR, &CFrameWnd::OnUpdateControlBarMenu) + ON_COMMAND_EX(ID_VIEW_STATUS_BAR, &CFrameWnd::OnBarCheck) + ON_COMMAND_EX(ID_VIEW_TOOLBAR, &CFrameWnd::OnBarCheck) + ON_COMMAND(ID_HELP, &CFrameWnd::OnHelp) + ON_COMMAND(ID_CONTEXT_HELP, &CFrameWnd::OnContextHelp) +END_MESSAGE_MAP() + +const CRect CFrameWnd::rectDefault(CW_USEDEFAULT, CW_USEDEFAULT, 0, 0); + +CFrameWnd::CFrameWnd() + : m_nIDHelp(0), m_hMenuDefault(nullptr), m_hAccelTable(nullptr), m_bAutoMenuEnable(TRUE), m_nIdleFlags(0), + m_pViewActive(nullptr), m_pStatusBar(nullptr) {} + +CFrameWnd::~CFrameWnd() {} + +BOOL CFrameWnd::PreCreateWindow(CREATESTRUCT&) { return TRUE; } + +void CFrameWnd::PostNcDestroy() { delete this; } + +BOOL CFrameWnd::Create(const char*, const char* lpszWindowName, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, + const char* lpszMenuName, DWORD dwExStyle, CCreateContext* pContext) { + return OnMain([&]() -> BOOL { + CREATESTRUCT cs; + memset(&cs, 0, sizeof cs); + cs.style = static_cast(dwStyle); + cs.dwExStyle = dwExStyle; + cs.lpszName = lpszWindowName; + cs.x = rect.left; + cs.y = rect.top; + cs.cx = rect.right - rect.left; + cs.cy = rect.bottom - rect.top; + cs.lpCreateParams = pContext; + if (!PreCreateWindow(cs)) + return FALSE; + long style = wxDEFAULT_FRAME_STYLE; + wxPoint pos = cs.x == CW_USEDEFAULT ? wxDefaultPosition : wxPoint(cs.x, cs.y); + wxSize size = cs.cx <= 0 ? wxSize(1000, 720) : wxSize(cs.cx, cs.cy); + auto* frame = new MfcFrame(pParentWnd && pParentWnd->m_hWnd ? pParentWnd->GetWx() : nullptr, + ToWx(lpszWindowName ? lpszWindowName : ""), pos, size, style); + HookWindow(frame, ControlKind::Frame, 0, static_cast(cs.style), dwExStyle); + EnsureState(frame).text = ToWx(lpszWindowName ? lpszWindowName : ""); + AttachWx(frame); + if (lpszMenuName) { + const rc::Menu* menu = FindMenu(ResRef::From(lpszMenuName)); + if (menu) { + wxMenuBar* bar = BuildMenuBar(*menu); + frame->SetMenuBar(bar); + m_hMenuDefault = HandleFromWxMenuBar(bar); + } + } + if (SendMessage(WM_CREATE, 0, reinterpret_cast(&cs)) == -1) { + DestroyWindow(); + return FALSE; + } + return m_hWnd != nullptr; + }); +} + +BOOL CFrameWnd::LoadFrame(UINT nIDResource, DWORD dwDefaultStyle, CWnd* pParentWnd, CCreateContext* pContext) { + CString title = ResourceString(nIDResource); + int nl = title.Find('\n'); + if (nl >= 0) + title = title.Left(nl); + m_strTitle = title; + const rc::AccelTable* accel = FindAccelTable(ResRef::FromId(nIDResource)); + SetMenuAccelerators(accel); + BOOL ok = Create(nullptr, title, dwDefaultStyle, rectDefault, pParentWnd, MAKEINTRESOURCE(nIDResource), 0, pContext); + SetMenuAccelerators(nullptr); + if (!ok) + return FALSE; + m_nIDHelp = nIDResource; + m_hAccelTable = reinterpret_cast(const_cast(accel)); + OnMain([&] { + wxIcon icon = LoadIconResource(ResRef::FromId(nIDResource)); + if (icon.IsOk()) + static_cast(GetWx())->SetIcon(icon); + if (accel) { + std::vector entries; + wxMenuBar* bar = static_cast(GetWx())->GetMenuBar(); + for (int i = 0; i < accel->itemCount; ++i) { + const rc::Accel& a = accel->items[i]; + if (bar && bar->FindItem(MenuWxId(a.id))) + continue; + int flags = (a.flags & FCONTROL ? wxACCEL_CTRL : 0) | (a.flags & FALT ? wxACCEL_ALT : 0) | + (a.flags & FSHIFT ? wxACCEL_SHIFT : 0); + int key = a.flags & FVIRTKEY ? WxKeyFromVirtual(static_cast(a.key)) : a.key; + if (!(a.flags & FVIRTKEY) && a.key > 0 && a.key < 32) { + flags |= wxACCEL_CTRL; + key = 'A' + a.key - 1; + } + if (key) + entries.emplace_back(flags, key, MenuWxId(a.id)); + } + if (!entries.empty()) + GetWx()->SetAcceleratorTable(wxAcceleratorTable(static_cast(entries.size()), entries.data())); + } + }); + OnUpdateFrameTitle(TRUE); + return TRUE; +} + +int CFrameWnd::OnCreate(LPCREATESTRUCT lpcs) { + auto* context = static_cast(lpcs->lpCreateParams); + return OnCreateClient(lpcs, context) ? 0 : -1; +} + +BOOL CFrameWnd::OnCreateClient(LPCREATESTRUCT, CCreateContext* pContext) { + if (pContext && pContext->m_pNewViewClass) + return CreateView(pContext, AFX_IDW_PANE_FIRST) != nullptr; + return TRUE; +} + +CWnd* CFrameWnd::CreateView(CCreateContext* pContext, UINT nID) { + CObject* obj = pContext->m_pNewViewClass->CreateObject(); + auto* view = dynamic_cast(obj); + if (!view) { + delete obj; + return nullptr; + } + CRect rc; + GetClientRect(&rc); + if (!view->Create(nullptr, nullptr, WS_CHILD | WS_VISIBLE | WS_BORDER, rc, this, nID, pContext)) + return nullptr; + RecalcLayout(); + return view; +} + +void CFrameWnd::OnClose() { + CWinApp* app = AfxGetApp(); + if (app && app->m_pMainWnd == this) { + if (!app->SaveAllModified()) + return; + app->CloseAllDocuments(FALSE); + DestroyWindow(); + return; + } + CDocument* doc = GetActiveDocument(); + if (doc && !doc->CanCloseFrame(this)) + return; + if (doc && doc->m_viewList.size() == 1) { + doc->OnCloseDocument(); + return; + } + DestroyWindow(); +} + +void CFrameWnd::OnDestroy() { + CWinApp* app = AfxGetApp(); + if (app && app->m_pMainWnd == this) { + app->m_pMainWnd = nullptr; + PostToMainThread([] { + if (wxTheApp) + wxTheApp->ExitMainLoop(); + }); + } +} + +void CFrameWnd::OnSize(UINT, int, int) { RecalcLayout(); } + +void CFrameWnd::OnSetFocus(CWnd*) { + if (m_pViewActive && m_pViewActive->m_hWnd) + m_pViewActive->SetFocus(); +} + +void CFrameWnd::RecalcLayout(BOOL) { + OnMain([&] { + if (!m_hWnd) + return; + wxWindow* frame = GetWx(); + wxSize client = frame->GetClientSize(); + for (wxWindow* c : frame->GetChildren()) { + WindowState* st = GetState(c); + if (st && st->winId == AFX_IDW_PANE_FIRST) + c->SetSize(0, 0, client.x, client.y); + } + }); +} + +BOOL CFrameWnd::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo) { + CView* view = GetActiveView(); + if (view && view->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo)) + return TRUE; + if (CWnd::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo)) + return TRUE; + CWinApp* app = AfxGetApp(); + return app && app->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo); +} + +BOOL CFrameWnd::OnCommand(WPARAM wParam, LPARAM lParam) { return CWnd::OnCommand(wParam, lParam); } + +BOOL CFrameWnd::PreTranslateMessage(MSG* pMsg) { return CWnd::PreTranslateMessage(pMsg); } + +CFrameWnd* CFrameWnd::GetActiveFrame() { return this; } + +CView* CFrameWnd::GetActiveView() const { + if (m_pViewActive) + return m_pViewActive; + if (!m_hWnd) + return nullptr; + for (wxWindow* c : GetWx()->GetChildren()) { + CWnd* p = PermanentWnd(c); + if (p && p->IsKindOf(RUNTIME_CLASS(CView))) + return static_cast(p); + } + return nullptr; +} + +void CFrameWnd::SetActiveView(CView* pViewNew, BOOL bNotify) { + CView* old = m_pViewActive; + if (old == pViewNew) + return; + m_pViewActive = pViewNew; + if (bNotify) { + if (old) + old->OnActivateView(FALSE, pViewNew, old); + if (pViewNew) + pViewNew->OnActivateView(TRUE, pViewNew, old); + } +} + +CDocument* CFrameWnd::GetActiveDocument() { + CView* v = GetActiveView(); + return v ? v->GetDocument() : nullptr; +} + +void CFrameWnd::InitialUpdateFrame(CDocument* pDoc, BOOL bMakeVisible) { + CView* view = GetActiveView(); + if (m_hWnd) + SendMessageToDescendants(WM_INITIALUPDATE, 0, 0, TRUE, TRUE); + std::vector views; + if (pDoc) + views = pDoc->m_viewList; + else if (view) + views.push_back(view); + for (CView* v : views) + if (v->GetParentFrame() == this) + v->OnInitialUpdate(); + if (view) + SetActiveView(view, TRUE); + if (bMakeVisible) + ActivateFrame(); + OnUpdateFrameTitle(TRUE); +} + +void CFrameWnd::ActivateFrame(int nCmdShow) { + ShowWindow(nCmdShow == -1 ? SW_SHOW : nCmdShow); + BringWindowToTop(); +} + +void CFrameWnd::OnUpdateFrameTitle(BOOL bAddToTitle) { + CDocument* doc = GetActiveDocument(); + if (bAddToTitle && doc) + UpdateFrameTitleForDocument(doc->GetTitle()); + else + UpdateFrameTitleForDocument(nullptr); +} + +void CFrameWnd::UpdateFrameTitleForDocument(const char* lpszDocName) { + CString title = m_strTitle; + if (lpszDocName && *lpszDocName) + title = title.IsEmpty() ? CString(lpszDocName) : title + " - " + lpszDocName; + SetWindowText(title); +} + +void CFrameWnd::OnUpdateFrameMenu(HMENU) {} + +void CFrameWnd::GetMessageString(UINT nID, CString& rMessage) const { + rMessage.LoadString(nID); + int nl = rMessage.Find('\n'); + if (nl >= 0) + rMessage = rMessage.Left(nl); +} + +void CFrameWnd::SetMessageText(const char* lpszText) { + OnMain([&] { + auto* frame = wxDynamicCast(GetTopLevelFrame() && GetTopLevelFrame()->m_hWnd ? GetTopLevelFrame()->GetWx() : nullptr, wxFrame); + if (frame && frame->GetStatusBar()) + frame->GetStatusBar()->SetStatusText(ToWx(lpszText ? lpszText : ""), 0); + }); +} + +void CFrameWnd::SetMessageText(UINT nID) { + CString s; + GetMessageString(nID, s); + SetMessageText(s); +} + +void CFrameWnd::EnableDocking(DWORD) {} +void CFrameWnd::DockControlBar(CControlBar*, UINT, LPCRECT) {} + +void CFrameWnd::ShowControlBar(CControlBar* pBar, BOOL bShow, BOOL) { + if (pBar && pBar->m_hWnd) { + pBar->ShowWindow(bShow ? SW_SHOW : SW_HIDE); + OnMain([&] { + if (m_hWnd) + GetWx()->SendSizeEvent(); + }); + } +} + +CControlBar* CFrameWnd::GetControlBar(UINT nID) { + for (CControlBar* b : m_listControlBars) + if (static_cast(b->GetDlgCtrlID()) == nID) + return b; + return nullptr; +} + +void CFrameWnd::OnUpdateControlBarMenu(CCmdUI* pCmdUI) { + CControlBar* bar = GetControlBar(pCmdUI->m_nID); + if (!bar) { + pCmdUI->ContinueRouting(); + return; + } + pCmdUI->SetCheck(bar->IsWindowVisible()); +} + +BOOL CFrameWnd::OnBarCheck(UINT nID) { + CControlBar* bar = GetControlBar(nID); + if (!bar) + return FALSE; + ShowControlBar(bar, !bar->IsWindowVisible(), FALSE); + return TRUE; +} + +void CFrameWnd::OnHelp() { + CWinApp* app = AfxGetApp(); + if (app) + app->WinHelpInternal(0, HELP_FINDER); +} + +void CFrameWnd::OnContextHelp() { OnHelp(); } + +// --------------------------------------------------------------------------------------------- +// MDI + +IMPLEMENT_DYNAMIC(CMDIFrameWnd, CFrameWnd) + +BEGIN_MESSAGE_MAP(CMDIFrameWnd, CFrameWnd) + ON_COMMAND(ID_WINDOW_NEW, &CMDIFrameWnd::OnWindowNew) + ON_COMMAND(ID_WINDOW_CASCADE, &CMDIFrameWnd::OnWindowCascade) + ON_COMMAND(ID_WINDOW_TILE_HORZ, &CMDIFrameWnd::OnWindowTile) + ON_COMMAND(ID_WINDOW_TILE_VERT, &CMDIFrameWnd::OnWindowTile) + ON_COMMAND(ID_WINDOW_ARRANGE, &CMDIFrameWnd::OnWindowCascade) + ON_UPDATE_COMMAND_UI(ID_WINDOW_CASCADE, &CMDIFrameWnd::OnUpdateMDIWindowCmd) + ON_UPDATE_COMMAND_UI(ID_WINDOW_TILE_HORZ, &CMDIFrameWnd::OnUpdateMDIWindowCmd) + ON_UPDATE_COMMAND_UI(ID_WINDOW_TILE_VERT, &CMDIFrameWnd::OnUpdateMDIWindowCmd) + ON_UPDATE_COMMAND_UI(ID_WINDOW_ARRANGE, &CMDIFrameWnd::OnUpdateMDIWindowCmd) + ON_UPDATE_COMMAND_UI(ID_WINDOW_NEW, &CMDIFrameWnd::OnUpdateMDIWindowCmd) +END_MESSAGE_MAP() + +CMDIFrameWnd::CMDIFrameWnd() : m_hWndMDIClient(nullptr) {} + +BOOL CMDIFrameWnd::LoadFrame(UINT nIDResource, DWORD dwDefaultStyle, CWnd* pParentWnd, CCreateContext* pContext) { + if (!CFrameWnd::LoadFrame(nIDResource, dwDefaultStyle, pParentWnd, pContext)) + return FALSE; + return TRUE; +} + +BOOL CMDIFrameWnd::OnCreateClient(LPCREATESTRUCT, CCreateContext*) { + return OnMain([&]() -> BOOL { + wxWindow* frame = GetWx(); + auto* book = new wxAuiNotebook(frame, ToWxId(AFX_IDW_PANE_FIRST), wxDefaultPosition, frame->GetClientSize(), + wxAUI_NB_DEFAULT_STYLE | wxAUI_NB_CLOSE_ON_ALL_TABS | wxAUI_NB_WINDOWLIST_BUTTON); + HookWindow(book, ControlKind::Generic, AFX_IDW_PANE_FIRST, WS_CHILD | WS_VISIBLE, 0); + m_hWndMDIClient = ToHwnd(book); + auto* sizer = new wxBoxSizer(wxVERTICAL); + sizer->Add(book, 1, wxEXPAND); + frame->SetSizer(sizer); + book->Bind(wxEVT_AUINOTEBOOK_PAGE_CHANGED, [this](wxAuiNotebookEvent& e) { + e.Skip(); + OnChildActivated(); + }); + book->Bind(wxEVT_AUINOTEBOOK_PAGE_CLOSE, [book](wxAuiNotebookEvent& e) { + e.Veto(); + wxWindow* page = book->GetPage(static_cast(e.GetSelection())); + if (page && IsManagedWindow(page)) + DispatchMessageTo(ToHwnd(page), WM_CLOSE, 0, 0); + }); + return TRUE; + }); +} + +void CMDIFrameWnd::OnChildActivated() { + CMDIChildWnd* active = MDIGetActive(); + for (CMDIChildWnd* child : m_children) { + bool now = child == active; + if (child->m_bActive != now) { + child->m_bActive = now; + CView* view = child->GetActiveView(); + if (view) + view->OnActivateView(now, now ? view : nullptr, now ? nullptr : view); + child->SendMessage(WM_MDIACTIVATE, reinterpret_cast(now ? nullptr : child->m_hWnd), + reinterpret_cast(now ? child->m_hWnd : nullptr)); + } + } + OnUpdateFrameMenu(active ? active->m_hMenuShared : nullptr); + OnUpdateFrameTitle(TRUE); +} + +BOOL CMDIFrameWnd::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo) { + CMDIChildWnd* child = MDIGetActive(); + if (child && child->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo)) + return TRUE; + return CFrameWnd::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo); +} + +CFrameWnd* CMDIFrameWnd::GetActiveFrame() { + CMDIChildWnd* child = MDIGetActive(); + return child ? static_cast(child) : this; +} + +void CMDIFrameWnd::OnUpdateFrameTitle(BOOL bAddToTitle) { + CMDIChildWnd* child = MDIGetActive(); + CDocument* doc = child ? child->GetActiveDocument() : nullptr; + UpdateFrameTitleForDocument(bAddToTitle && doc ? doc->GetTitle().GetString() : nullptr); +} + +void CMDIFrameWnd::OnUpdateFrameMenu(HMENU hMenuAlt) { + OnMain([&] { + auto* frame = wxDynamicCast(GetWx(), wxFrame); + if (!frame) + return; + wxMenuBar* bar = WxMenuBarFromHandle(hMenuAlt ? hMenuAlt : m_hMenuDefault); + if (bar && frame->GetMenuBar() != bar) + frame->SetMenuBar(bar); + }); +} + +void CMDIFrameWnd::RecalcLayout(BOOL) { + OnMain([&] { + if (m_hWnd) + GetWx()->Layout(); + }); +} + +void CMDIFrameWnd::MDIActivate(CWnd* pWndActivate) { + if (!pWndActivate || !pWndActivate->m_hWnd) + return; + OnMain([&] { + wxAuiNotebook* book = ClientNotebook(this); + if (!book) + return; + int index = book->GetPageIndex(pWndActivate->GetWx()); + if (index != wxNOT_FOUND && book->GetSelection() != index) + book->SetSelection(static_cast(index)); + else + OnChildActivated(); + }); +} + +CMDIChildWnd* CMDIFrameWnd::MDIGetActive(BOOL* pbMaximized) const { + if (pbMaximized) + *pbMaximized = TRUE; + return OnMain([&]() -> CMDIChildWnd* { + wxAuiNotebook* book = ClientNotebook(const_cast(this)); + if (!book || book->GetSelection() == wxNOT_FOUND) + return nullptr; + CWnd* p = PermanentWnd(book->GetPage(static_cast(book->GetSelection()))); + return p && p->IsKindOf(RUNTIME_CLASS(CMDIChildWnd)) ? static_cast(p) : nullptr; + }); +} + +void CMDIFrameWnd::MDIMaximize(CWnd* pWnd) { MDIActivate(pWnd); } +void CMDIFrameWnd::MDIRestore(CWnd* pWnd) { MDIActivate(pWnd); } + +void CMDIFrameWnd::MDINext() { + OnMain([&] { + if (wxAuiNotebook* book = ClientNotebook(this)) + book->AdvanceSelection(true); + }); +} + +void CMDIFrameWnd::MDIPrev() { + OnMain([&] { + if (wxAuiNotebook* book = ClientNotebook(this)) + book->AdvanceSelection(false); + }); +} + +CMenu* CMDIFrameWnd::MDISetMenu(CMenu* pFrameMenu, CMenu*) { + if (pFrameMenu) + OnUpdateFrameMenu(pFrameMenu->m_hMenu); + return nullptr; +} + +void CMDIFrameWnd::MDITile() {} +void CMDIFrameWnd::MDICascade() {} + +CMDIChildWnd* CMDIFrameWnd::CreateNewChild(CRuntimeClass* pClass, UINT nResource, HMENU hMenu, HACCEL hAccel) { + auto* child = static_cast(pClass->CreateObject()); + if (!child->LoadFrame(nResource, WS_CHILD | WS_VISIBLE | WS_OVERLAPPEDWINDOW, this, nullptr)) + return nullptr; + child->SetHandles(hMenu, hAccel); + child->InitialUpdateFrame(nullptr, TRUE); + return child; +} + +void CMDIFrameWnd::OnWindowNew() {} +void CMDIFrameWnd::OnWindowCascade() {} +void CMDIFrameWnd::OnWindowTile() {} +void CMDIFrameWnd::OnUpdateMDIWindowCmd(CCmdUI* pCmdUI) { pCmdUI->Enable(MDIGetActive() != nullptr); } + +IMPLEMENT_DYNCREATE(CMDIChildWnd, CFrameWnd) + +BEGIN_MESSAGE_MAP(CMDIChildWnd, CFrameWnd) +END_MESSAGE_MAP() + +CMDIChildWnd::CMDIChildWnd() : m_hMenuShared(nullptr), m_pMDIFrame(nullptr), m_bActive(false) {} + +CMDIChildWnd::~CMDIChildWnd() { + if (m_pMDIFrame) { + auto& kids = m_pMDIFrame->m_children; + kids.erase(std::remove(kids.begin(), kids.end(), this), kids.end()); + } +} + +BOOL CMDIChildWnd::PreCreateWindow(CREATESTRUCT&) { return TRUE; } + +BOOL CMDIChildWnd::Create(const char*, const char* lpszWindowName, DWORD dwStyle, const RECT&, CMDIFrameWnd* pParentWnd, + CCreateContext* pContext) { + CMDIFrameWnd* mdi = pParentWnd ? pParentWnd : MainMDIFrame(); + if (!mdi) + return FALSE; + return OnMain([&]() -> BOOL { + wxAuiNotebook* book = ClientNotebook(mdi); + if (!book) + return FALSE; + CREATESTRUCT cs; + memset(&cs, 0, sizeof cs); + cs.style = static_cast(dwStyle); + cs.lpszName = lpszWindowName; + cs.lpCreateParams = pContext; + if (!PreCreateWindow(cs)) + return FALSE; + auto* panel = new MfcChildPanel(book); + HookWindow(panel, ControlKind::MDIChild, 0, static_cast(cs.style), 0); + EnsureState(panel).text = ToWx(lpszWindowName ? lpszWindowName : ""); + EnsureState(panel).owner = mdi->m_hWnd; + AttachWx(panel); + m_pMDIFrame = mdi; + mdi->m_children.push_back(this); + panel->Bind(wxEVT_CLOSE_WINDOW, [panel](wxCloseEvent& e) { + if (IsManagedWindow(panel) && PermanentWnd(panel)) { + e.Veto(); + DispatchMessageTo(ToHwnd(panel), WM_CLOSE, 0, 0); + } else { + e.Skip(); + } + }); + if (SendMessage(WM_CREATE, 0, reinterpret_cast(&cs)) == -1) { + DestroyWindow(); + return FALSE; + } + book->AddPage(panel, ToWx(lpszWindowName ? lpszWindowName : ""), false); + return m_hWnd != nullptr; + }); +} + +BOOL CMDIChildWnd::LoadFrame(UINT nIDResource, DWORD dwDefaultStyle, CWnd* pParentWnd, CCreateContext* pContext) { + m_nIDHelp = nIDResource; + CString title = ResourceString(nIDResource); + int nl = title.Find('\n'); + if (nl >= 0) + title = title.Left(nl); + CMDIFrameWnd* mdi = pParentWnd && pParentWnd->IsKindOf(RUNTIME_CLASS(CMDIFrameWnd)) ? static_cast(pParentWnd) + : MainMDIFrame(); + return Create(nullptr, title, dwDefaultStyle, rectDefault, mdi, pContext); +} + +BOOL CMDIChildWnd::DestroyWindow() { + CMDIFrameWnd* mdi = m_pMDIFrame; + if (mdi) { + auto& kids = mdi->m_children; + kids.erase(std::remove(kids.begin(), kids.end(), this), kids.end()); + m_pMDIFrame = nullptr; + } + if (!m_hWnd) + return FALSE; + return OnMain([&]() -> BOOL { + wxWindow* panel = GetWx(); + wxAuiNotebook* book = ClientNotebook(mdi); + WindowState* st = GetState(panel); + if (st && !st->destroyNotified) { + st->destroyNotified = true; + SendMessage(WM_DESTROY); + } + if (book) { + int index = book->GetPageIndex(panel); + if (index != wxNOT_FOUND) + book->RemovePage(static_cast(index)); + } + DestroyWxWindow(panel); + if (mdi) + PostToMainThread([mdi] { + if (mdi->m_hWnd) + mdi->OnChildActivated(); + }); + return TRUE; + }); +} + +void CMDIChildWnd::ActivateFrame(int nCmdShow) { + if (nCmdShow == SW_HIDE) { + ShowWindow(SW_HIDE); + return; + } + CMDIFrameWnd* mdi = GetMDIFrame(); + if (mdi) + mdi->MDIActivate(this); +} + +void CMDIChildWnd::OnUpdateFrameMenu(BOOL bActive, CWnd*, HMENU hMenuAlt) { + CMDIFrameWnd* mdi = GetMDIFrame(); + if (mdi) + mdi->OnUpdateFrameMenu(bActive ? (hMenuAlt ? hMenuAlt : m_hMenuShared) : nullptr); +} + +void CMDIChildWnd::OnUpdateFrameTitle(BOOL bAddToTitle) { + CDocument* doc = GetActiveDocument(); + CString title = doc && bAddToTitle ? doc->GetTitle() : m_strTitle; + OnMain([&] { + if (!m_hWnd) + return; + EnsureState(GetWx()).text = ToWx(title); + if (wxAuiNotebook* book = ClientNotebook(GetMDIFrame())) { + int index = book->GetPageIndex(GetWx()); + if (index != wxNOT_FOUND) + book->SetPageText(static_cast(index), ToWx(title)); + } + }); + if (CMDIFrameWnd* mdi = GetMDIFrame()) + if (mdi->MDIGetActive() == this) + mdi->OnUpdateFrameTitle(TRUE); +} + +CMDIFrameWnd* CMDIChildWnd::GetMDIFrame() const { return m_pMDIFrame ? m_pMDIFrame : MainMDIFrame(); } +void CMDIChildWnd::MDIDestroy() { DestroyWindow(); } +void CMDIChildWnd::MDIActivate() { ActivateFrame(); } +void CMDIChildWnd::MDIMaximize() { ActivateFrame(); } +void CMDIChildWnd::MDIRestore() { ActivateFrame(); } +BOOL CMDIChildWnd::PreTranslateMessage(MSG* pMsg) { return CFrameWnd::PreTranslateMessage(pMsg); } + +// --------------------------------------------------------------------------------------------- +// Document templates + +IMPLEMENT_DYNAMIC(CDocTemplate, CCmdTarget) +IMPLEMENT_DYNAMIC(CMultiDocTemplate, CDocTemplate) +IMPLEMENT_DYNAMIC(CSingleDocTemplate, CDocTemplate) + +CDocTemplate::CDocTemplate(UINT nIDResource, CRuntimeClass* pDocClass, CRuntimeClass* pFrameClass, CRuntimeClass* pViewClass) + : m_nIDResource(nIDResource), m_pDocClass(pDocClass), m_pFrameClass(pFrameClass), m_pViewClass(pViewClass), + m_bAutoDelete(TRUE) { + LoadTemplate(); +} + +CDocTemplate::~CDocTemplate() {} + +void CDocTemplate::LoadTemplate() { + if (m_strDocStrings.IsEmpty()) + m_strDocStrings.LoadString(m_nIDResource); +} + +void CDocTemplate::AddDocument(CDocument* pDoc) { pDoc->m_pDocTemplate = this; } + +void CDocTemplate::RemoveDocument(CDocument* pDoc) { + if (pDoc->m_pDocTemplate == this) + pDoc->m_pDocTemplate = nullptr; +} + +BOOL CDocTemplate::GetDocString(CString& rString, enum DocStringIndex index) const { + return AfxExtractSubString(rString, m_strDocStrings, static_cast(index), '\n'); +} + +CDocument* CDocTemplate::CreateNewDocument() { + if (!m_pDocClass) + return nullptr; + auto* doc = dynamic_cast(m_pDocClass->CreateObject()); + if (doc) + AddDocument(doc); + return doc; +} + +CFrameWnd* CDocTemplate::CreateNewFrame(CDocument* pDoc, CFrameWnd* pOther) { + CCreateContext context; + context.m_pCurrentFrame = pOther; + context.m_pCurrentDoc = pDoc; + context.m_pNewViewClass = m_pViewClass; + context.m_pNewDocTemplate = this; + if (!m_pFrameClass) + return nullptr; + auto* frame = dynamic_cast(m_pFrameClass->CreateObject()); + if (!frame) + return nullptr; + if (!frame->LoadFrame(m_nIDResource, WS_OVERLAPPEDWINDOW, nullptr, &context)) + return nullptr; + return frame; +} + +void CDocTemplate::InitialUpdateFrame(CFrameWnd* pFrame, CDocument* pDoc, BOOL bMakeVisible) { + pFrame->InitialUpdateFrame(pDoc, bMakeVisible); +} + +BOOL CDocTemplate::SaveAllModified() { + POSITION pos = GetFirstDocPosition(); + while (pos) { + CDocument* doc = GetNextDoc(pos); + if (doc && !doc->SaveModified()) + return FALSE; + } + return TRUE; +} + +void CDocTemplate::CloseAllDocuments(BOOL) { + for (;;) { + POSITION pos = GetFirstDocPosition(); + if (!pos) + break; + CDocument* doc = GetNextDoc(pos); + if (!doc) + break; + doc->OnCloseDocument(); + } +} + +CDocTemplate::Confidence CDocTemplate::MatchDocType(const char* lpszPathName, CDocument*& rpDocMatch) { + rpDocMatch = nullptr; + POSITION pos = GetFirstDocPosition(); + while (pos) { + CDocument* doc = GetNextDoc(pos); + if (doc && doc->GetPathName().CompareNoCase(lpszPathName) == 0) { + rpDocMatch = doc; + return yesAlreadyOpen; + } + } + CString ext; + if (GetDocString(ext, filterExt) && !ext.IsEmpty()) { + CString path(lpszPathName); + if (path.GetLength() >= ext.GetLength() && path.Right(ext.GetLength()).CompareNoCase(ext) == 0) + return yesAttemptNative; + } + return yesAttemptForeign; +} + +CMultiDocTemplate::CMultiDocTemplate(UINT nIDResource, CRuntimeClass* pDocClass, CRuntimeClass* pFrameClass, + CRuntimeClass* pViewClass) + : CDocTemplate(nIDResource, pDocClass, pFrameClass, pViewClass), m_hMenuShared(nullptr), m_hAccelTable(nullptr), + m_nUntitledCount(0) { + LoadTemplate(); +} + +CMultiDocTemplate::~CMultiDocTemplate() {} + +void CMultiDocTemplate::LoadTemplate() { + CDocTemplate::LoadTemplate(); + if (m_hMenuShared) + return; + const rc::Menu* menu = FindMenu(ResRef::FromId(m_nIDResource)); + if (menu && wxTheApp) { + SetMenuAccelerators(FindAccelTable(ResRef::FromId(m_nIDResource))); + if (!FindAccelTable(ResRef::FromId(m_nIDResource))) + SetMenuAccelerators(FindAccelTable(ResRef::FromId(128))); + m_hMenuShared = HandleFromWxMenuBar(BuildMenuBar(*menu)); + SetMenuAccelerators(nullptr); + } +} + +POSITION CMultiDocTemplate::GetFirstDocPosition() const { + return m_docList.empty() ? nullptr : reinterpret_cast(static_cast(1)); +} + +CDocument* CMultiDocTemplate::GetNextDoc(POSITION& rPos) const { + size_t i = static_cast(reinterpret_cast(rPos)) - 1; + CDocument* d = i < m_docList.size() ? m_docList[i] : nullptr; + rPos = i + 1 < m_docList.size() ? reinterpret_cast(static_cast(i + 2)) : nullptr; + return d; +} + +void CMultiDocTemplate::AddDocument(CDocument* pDoc) { + CDocTemplate::AddDocument(pDoc); + m_docList.push_back(pDoc); +} + +void CMultiDocTemplate::RemoveDocument(CDocument* pDoc) { + CDocTemplate::RemoveDocument(pDoc); + m_docList.erase(std::remove(m_docList.begin(), m_docList.end(), pDoc), m_docList.end()); +} + +CDocument* CMultiDocTemplate::OpenDocumentFile(const char* lpszPathName, BOOL bMakeVisible) { + LoadTemplate(); + CDocument* doc = CreateNewDocument(); + if (!doc) + return nullptr; + BOOL autoDelete = doc->m_bAutoDelete; + doc->m_bAutoDelete = FALSE; + CFrameWnd* frame = CreateNewFrame(doc, nullptr); + doc->m_bAutoDelete = autoDelete; + if (!frame) { + delete doc; + return nullptr; + } + if (frame->IsKindOf(RUNTIME_CLASS(CMDIChildWnd))) + static_cast(frame)->SetHandles(m_hMenuShared, m_hAccelTable); + if (!lpszPathName) { + SetDefaultTitle(doc); + if (!doc->OnNewDocument()) { + frame->DestroyWindow(); + return nullptr; + } + ++m_nUntitledCount; + } else { + CWaitCursor wait; + if (!doc->OnOpenDocument(lpszPathName)) { + frame->DestroyWindow(); + return nullptr; + } + doc->SetPathName(lpszPathName); + } + InitialUpdateFrame(frame, doc, bMakeVisible); + return doc; +} + +void CMultiDocTemplate::SetDefaultTitle(CDocument* pDocument) { + CString name; + if (!GetDocString(name, docName) || name.IsEmpty()) + name = "Untitled"; + CString title; + title.Format("%s%u", name.GetString(), m_nUntitledCount + 1); + pDocument->SetTitle(title); +} + +CSingleDocTemplate::CSingleDocTemplate(UINT nIDResource, CRuntimeClass* pDocClass, CRuntimeClass* pFrameClass, + CRuntimeClass* pViewClass) + : CDocTemplate(nIDResource, pDocClass, pFrameClass, pViewClass), m_pOnlyDoc(nullptr) {} + +POSITION CSingleDocTemplate::GetFirstDocPosition() const { + return m_pOnlyDoc ? reinterpret_cast(static_cast(1)) : nullptr; +} + +CDocument* CSingleDocTemplate::GetNextDoc(POSITION& rPos) const { + CDocument* d = rPos ? m_pOnlyDoc : nullptr; + rPos = nullptr; + return d; +} + +void CSingleDocTemplate::AddDocument(CDocument* pDoc) { + CDocTemplate::AddDocument(pDoc); + m_pOnlyDoc = pDoc; +} + +void CSingleDocTemplate::RemoveDocument(CDocument* pDoc) { + CDocTemplate::RemoveDocument(pDoc); + if (m_pOnlyDoc == pDoc) + m_pOnlyDoc = nullptr; +} + +CDocument* CSingleDocTemplate::OpenDocumentFile(const char* lpszPathName, BOOL bMakeVisible) { + if (m_pOnlyDoc) { + if (!m_pOnlyDoc->SaveModified()) + return nullptr; + m_pOnlyDoc->OnCloseDocument(); + } + CDocument* doc = CreateNewDocument(); + if (!doc) + return nullptr; + CFrameWnd* frame = CreateNewFrame(doc, nullptr); + if (!frame) + return nullptr; + if (!lpszPathName) { + SetDefaultTitle(doc); + doc->OnNewDocument(); + } else if (!doc->OnOpenDocument(lpszPathName)) { + frame->DestroyWindow(); + return nullptr; + } else { + doc->SetPathName(lpszPathName); + } + InitialUpdateFrame(frame, doc, bMakeVisible); + return doc; +} + +void CSingleDocTemplate::SetDefaultTitle(CDocument* pDocument) { pDocument->SetTitle("Untitled"); } + +// --------------------------------------------------------------------------------------------- +// Control bars + +IMPLEMENT_DYNAMIC(CControlBar, CWnd) +IMPLEMENT_DYNAMIC(CToolBar, CControlBar) +IMPLEMENT_DYNAMIC(CStatusBar, CControlBar) + +CControlBar::CControlBar() : m_dwStyle(0), m_dwDockStyle(0) {} +CSize CControlBar::CalcFixedLayout(BOOL, BOOL) { return CSize(0, 0); } + +CToolBar::CToolBar() : m_nResourceID(0) {} + +BOOL CToolBar::Create(CWnd* pParentWnd, DWORD dwStyle, UINT nID) { + if (!pParentWnd || !pParentWnd->m_hWnd) + return FALSE; + m_dwStyle = dwStyle; + return OnMain([&]() -> BOOL { + auto* frame = wxDynamicCast(pParentWnd->GetWx(), wxFrame); + if (!frame) + return FALSE; + wxToolBar* tb = frame->CreateToolBar(wxTB_HORIZONTAL | wxTB_FLAT | wxTB_NODIVIDER, ToWxId(static_cast(nID))); + HookWindow(tb, ControlKind::ToolBar, static_cast(nID), dwStyle, 0); + AttachWx(tb); + if (pParentWnd->IsKindOf(RUNTIME_CLASS(CFrameWnd))) + static_cast(pParentWnd)->m_listControlBars.push_back(this); + return TRUE; + }); +} + +BOOL CToolBar::CreateEx(CWnd* pParentWnd, DWORD, DWORD dwStyle, CRect, UINT nID) { return Create(pParentWnd, dwStyle, nID); } + +BOOL CToolBar::LoadToolBar(UINT nIDResource) { return LoadToolBar(MAKEINTRESOURCE(nIDResource)); } + +BOOL CToolBar::LoadToolBar(const char* lpszResourceName) { + ResRef ref = ResRef::From(lpszResourceName); + const rc::Toolbar* res = FindToolbar(ref); + if (!res || !m_hWnd) + return FALSE; + m_nResourceID = static_cast(ref.id); + m_buttonIDs.assign(res->buttons, res->buttons + res->buttonCount); + return OnMain([&]() -> BOOL { + auto* tb = wxDynamicCast(GetWx(), wxToolBar); + wxBitmap strip = LoadBitmapResource(ref); + int w = res->buttonWidth; + int h = res->buttonHeight; + wxImage image = strip.IsOk() ? strip.ConvertToImage() : wxImage(); + if (image.IsOk()) + image.SetMaskColour(192, 192, 192); + int imageIndex = 0; + for (int i = 0; i < res->buttonCount; ++i) { + int id = res->buttons[i]; + if (id == 0) { + tb->AddSeparator(); + continue; + } + wxBitmap bmp; + if (image.IsOk() && (imageIndex + 1) * w <= image.GetWidth()) + bmp = wxBitmap(image.GetSubImage(wxRect(imageIndex * w, 0, w, std::min(h, image.GetHeight())))); + ++imageIndex; + CString prompt = ResourceString(static_cast(id)); + CString tip; + int nl = prompt.Find('\n'); + if (nl >= 0) { + tip = prompt.Mid(nl + 1); + prompt = prompt.Left(nl); + } + tb->AddTool(MenuWxId(id), ToWx(tip), bmp.IsOk() ? bmp : wxBitmap(w, h), ToWx(tip), wxITEM_NORMAL); + tb->SetToolLongHelp(MenuWxId(id), ToWx(prompt)); + } + tb->Realize(); + return TRUE; + }); +} + +BOOL CToolBar::LoadBitmap(const char*) { return TRUE; } +BOOL CToolBar::LoadBitmap(UINT) { return TRUE; } +BOOL CToolBar::SetButtons(const UINT* lpIDArray, int nIDCount) { + m_buttonIDs.assign(lpIDArray, lpIDArray + nIDCount); + return TRUE; +} +void CToolBar::SetSizes(SIZE, SIZE) {} +int CToolBar::CommandToIndex(UINT nIDFind) const { + for (size_t i = 0; i < m_buttonIDs.size(); ++i) + if (m_buttonIDs[i] == nIDFind) + return static_cast(i); + return -1; +} +UINT CToolBar::GetItemID(int nIndex) const { + return nIndex >= 0 && static_cast(nIndex) < m_buttonIDs.size() ? m_buttonIDs[static_cast(nIndex)] : 0; +} +void CToolBar::GetItemRect(int, LPRECT lpRect) const { ::SetRectEmpty(lpRect); } +UINT CToolBar::GetButtonStyle(int nIndex) const { return GetItemID(nIndex) ? TBBS_BUTTON : TBBS_SEPARATOR; } +void CToolBar::SetButtonStyle(int, UINT) {} +int CToolBar::GetCount() const { return static_cast(m_buttonIDs.size()); } + +CStatusBar::CStatusBar() {} + +BOOL CStatusBar::Create(CWnd* pParentWnd, DWORD dwStyle, UINT nID) { + if (!pParentWnd || !pParentWnd->m_hWnd) + return FALSE; + m_dwStyle = dwStyle; + return OnMain([&]() -> BOOL { + auto* frame = wxDynamicCast(pParentWnd->GetWx(), wxFrame); + if (!frame) + return FALSE; + wxStatusBar* sb = frame->CreateStatusBar(1, wxSTB_DEFAULT_STYLE, ToWxId(static_cast(nID))); + HookWindow(sb, ControlKind::StatusBar, static_cast(nID), dwStyle, 0); + AttachWx(sb); + if (pParentWnd->IsKindOf(RUNTIME_CLASS(CFrameWnd))) { + static_cast(pParentWnd)->m_listControlBars.push_back(this); + static_cast(pParentWnd)->m_pStatusBar = this; + } + return TRUE; + }); +} + +BOOL CStatusBar::CreateEx(CWnd* pParentWnd, DWORD, DWORD dwStyle, UINT nID) { return Create(pParentWnd, dwStyle, nID); } + +BOOL CStatusBar::SetIndicators(const UINT* lpIDArray, int nIDCount) { + m_indicators.assign(lpIDArray, lpIDArray + nIDCount); + return OnMain([&]() -> BOOL { + auto* sb = wxDynamicCast(GetWx(), wxStatusBar); + if (!sb) + return FALSE; + std::vector widths(static_cast(nIDCount), -1); + sb->SetFieldsCount(nIDCount); + for (int i = 0; i < nIDCount; ++i) { + if (lpIDArray[i] == ID_SEPARATOR) + continue; + CString text = ResourceString(lpIDArray[i]); + widths[static_cast(i)] = sb->GetTextExtent(ToWx(text)).x + 16; + sb->SetStatusText(ToWx(text), i); + } + sb->SetStatusWidths(nIDCount, widths.data()); + return TRUE; + }); +} + +int CStatusBar::CommandToIndex(UINT nIDFind) const { + for (size_t i = 0; i < m_indicators.size(); ++i) + if (m_indicators[i] == nIDFind) + return static_cast(i); + return -1; +} + +UINT CStatusBar::GetItemID(int nIndex) const { + return nIndex >= 0 && static_cast(nIndex) < m_indicators.size() ? m_indicators[static_cast(nIndex)] : 0; +} + +void CStatusBar::GetItemRect(int nIndex, LPRECT lpRect) const { + OnMain([&] { + wxRect r; + auto* sb = wxDynamicCast(GetWx(), wxStatusBar); + if (sb) + sb->GetFieldRect(nIndex, r); + lpRect->left = r.x; + lpRect->top = r.y; + lpRect->right = r.x + r.width; + lpRect->bottom = r.y + r.height; + }); +} + +CString CStatusBar::GetPaneText(int nIndex) const { + return OnMain([&]() -> CString { + auto* sb = wxDynamicCast(GetWx(), wxStatusBar); + return sb && nIndex < sb->GetFieldsCount() ? CStr(sb->GetStatusText(nIndex)) : CString(); + }); +} + +void CStatusBar::GetPaneText(int nIndex, CString& rString) const { rString = GetPaneText(nIndex); } + +BOOL CStatusBar::SetPaneText(int nIndex, const char* lpszNewText, BOOL) { + return OnMain([&]() -> BOOL { + auto* sb = wxDynamicCast(GetWx(), wxStatusBar); + if (!sb || nIndex >= sb->GetFieldsCount()) + return FALSE; + sb->SetStatusText(ToWx(lpszNewText ? lpszNewText : ""), nIndex); + return TRUE; + }); +} + +void CStatusBar::GetPaneInfo(int nIndex, UINT& nID, UINT& nStyle, int& cxWidth) const { + nID = GetItemID(nIndex); + nStyle = SBPS_NORMAL; + CRect r; + GetItemRect(nIndex, &r); + cxWidth = r.Width(); +} + +void CStatusBar::SetPaneInfo(int nIndex, UINT nID, UINT nStyle, int cxWidth) { + if (nIndex < 0) + return; + if (static_cast(nIndex) < m_indicators.size()) + m_indicators[static_cast(nIndex)] = nID; + OnMain([&] { + auto* sb = wxDynamicCast(GetWx(), wxStatusBar); + if (!sb || nIndex >= sb->GetFieldsCount()) + return; + std::vector widths(static_cast(sb->GetFieldsCount())); + for (int i = 0; i < sb->GetFieldsCount(); ++i) + widths[static_cast(i)] = sb->GetStatusWidth(i); + widths[static_cast(nIndex)] = (nStyle & SBPS_STRETCH) ? -1 : cxWidth + 8; + sb->SetStatusWidths(sb->GetFieldsCount(), widths.data()); + }); +} + +UINT CStatusBar::GetPaneStyle(int) const { return SBPS_NORMAL; } +void CStatusBar::SetPaneStyle(int, UINT) {} + +// --------------------------------------------------------------------------------------------- +// Rich edit view/document + +IMPLEMENT_DYNAMIC(CRichEditDoc, CDocument) +IMPLEMENT_DYNCREATE(CRichEditView, CView) + +BEGIN_MESSAGE_MAP(CRichEditView, CView) + ON_WM_CREATE() +END_MESSAGE_MAP() + +CRichEditDoc::CRichEditDoc() : m_bRTF(FALSE) {} +CRichEditCntrItem* CRichEditDoc::CreateClientItem(void*) const { return nullptr; } +CRichEditView* CRichEditDoc::GetView() const { + for (CView* v : m_viewList) + if (v->IsKindOf(RUNTIME_CLASS(CRichEditView))) + return static_cast(v); + return nullptr; +} + +void CRichEditDoc::Serialize(CArchive& ar) { + CRichEditView* view = GetView(); + if (view) + view->Serialize(ar); +} + +CRichEditView::CRichEditView() : m_nWordWrap(WrapToWindow) {} + +int CRichEditView::OnCreate(LPCREATESTRUCT lpcs) { return CView::OnCreate(lpcs); } + +void CRichEditView::OnInitialUpdate() { CView::OnInitialUpdate(); } + +void CRichEditView::Serialize(CArchive& ar) { + CFile* f = ar.GetFile(); + if (!f) + return; + if (ar.IsLoading()) { + ULONGLONG len = f->GetLength(); + std::string data(static_cast(len), '\0'); + if (len) + ar.Read(&data[0], static_cast(len)); + SetWindowText(data.c_str()); + } else { + CString text; + GetWindowText(text); + ar.Write(text.GetString(), static_cast(text.GetLength())); + } +} + +long CRichEditView::GetTextLength() const { return GetWindowTextLength(); } + +void CRichEditView::SetCharFormat(CHARFORMAT2 cf) { + SendMessage(EM_SETCHARFORMAT, SCF_SELECTION, reinterpret_cast(&cf)); +} + +CHARFORMAT2& CRichEditView::GetCharFormatSelection() { + static CHARFORMAT2 cf; + memset(&cf, 0, sizeof cf); + cf.cbSize = sizeof cf; + SendMessage(EM_GETCHARFORMAT, SCF_SELECTION, reinterpret_cast(&cf)); + return cf; +} + +// --------------------------------------------------------------------------------------------- +// Recent file list and command line + +CRecentFileList::CRecentFileList(UINT nStart, const char* lpszSection, const char* lpszEntryFormat, int nSize, + int nMaxDispLen) + : m_nSize(nSize), m_arrNames(static_cast(nSize)), m_strSectionName(lpszSection), + m_strEntryFormat(lpszEntryFormat), m_nStart(nStart), m_nMaxDisplayLength(nMaxDispLen) {} + +void CRecentFileList::Remove(int nIndex) { + if (nIndex < 0 || nIndex >= m_nSize) + return; + m_arrNames.erase(m_arrNames.begin() + nIndex); + m_arrNames.push_back(CString()); +} + +void CRecentFileList::Add(const char* lpszPathName) { + CString path(lpszPathName); + for (int i = 0; i < m_nSize; ++i) + if (m_arrNames[static_cast(i)].CompareNoCase(path) == 0) { + Remove(i); + break; + } + m_arrNames.insert(m_arrNames.begin(), path); + m_arrNames.resize(static_cast(m_nSize)); + WriteList(); +} + +BOOL CRecentFileList::GetDisplayName(CString& strName, int nIndex, const char*, int, BOOL) const { + if (nIndex < 0 || nIndex >= m_nSize || m_arrNames[static_cast(nIndex)].IsEmpty()) + return FALSE; + strName = m_arrNames[static_cast(nIndex)]; + int slash = strName.ReverseFind('\\'); + if (slash >= 0) + strName = strName.Mid(slash + 1); + return TRUE; +} + +void CRecentFileList::UpdateMenu(CCmdUI* pCmdUI) { pCmdUI->Enable(!m_arrNames.empty() && !m_arrNames[0].IsEmpty()); } + +void CRecentFileList::ReadList() { + CWinApp* app = AfxGetApp(); + if (!app) + return; + for (int i = 0; i < m_nSize; ++i) { + CString entry; + entry.Format(m_strEntryFormat, i + 1); + m_arrNames[static_cast(i)] = app->GetProfileString(m_strSectionName, entry, ""); + } +} + +void CRecentFileList::WriteList() { + CWinApp* app = AfxGetApp(); + if (!app) + return; + for (int i = 0; i < m_nSize; ++i) { + CString entry; + entry.Format(m_strEntryFormat, i + 1); + app->WriteProfileString(m_strSectionName, entry, m_arrNames[static_cast(i)]); + } +} + +CCommandLineInfo::CCommandLineInfo() + : m_bShowSplash(TRUE), m_bRunEmbedded(FALSE), m_bRunAutomated(FALSE), m_nShellCommand(FileNew) {} + +void CCommandLineInfo::ParseParam(const char* pszParam, BOOL bFlag, BOOL) { + if (bFlag) + return; + if (m_strFileName.IsEmpty()) { + m_strFileName = pszParam; + m_nShellCommand = FileOpen; + } +} From 91613f49108c99cd3d1fe76dd1ec57d54a6920c8 Mon Sep 17 00:00:00 2001 From: Kamil Marczak Date: Tue, 6 Oct 2026 17:37:46 +0200 Subject: [PATCH 10/46] fix(secude): accept Windows-style paths in the key store API --- port/secude/README.md | 10 +++++---- port/secude/src/internal.hpp | 3 +++ port/secude/src/keystore.cpp | 4 ++-- port/secude/src/misc.cpp | 24 +++++++++++++++++--- port/secude/tests/test_pse.cpp | 41 ++++++++++++++++++++++++++++++++++ 5 files changed, 73 insertions(+), 9 deletions(-) diff --git a/port/secude/README.md b/port/secude/README.md index ff96040b1..e4c7d074a 100644 --- a/port/secude/README.md +++ b/port/secude/README.md @@ -154,7 +154,9 @@ Smart cards (`SC_DATA` is ignored), revocation lists this library uses internally). * `CrypTool/SecudeLib.h` uses `register` in its prototypes: fine in C++14 (deprecation warning), an error in C++17 unless `-Wno-register` is used. -* `CrypToolApp.cpp` builds the key store paths with backslashes - (`PSE\\PSECA\\capse.cse`); use `/` on other platforms. Its - `ECSecudeLib.##c = SecudeLib.##c` loop is not valid outside MSVC and can be - dropped there (`ECsecude.c` already initialises the table). +* Windows style paths (`C:\...\PSE\PSECA\capse.cse`) are accepted: off + Windows every path the library opens goes through `native_path()`, which + turns `\` into `/` and drops a drive prefix. +* `CrypToolApp.cpp`'s `ECSecudeLib.##c = SecudeLib.##c` loop is not valid + outside MSVC and can be dropped there (`ECsecude.c` already initialises the + table). diff --git a/port/secude/src/internal.hpp b/port/secude/src/internal.hpp index 687977ff4..2444b7524 100644 --- a/port/secude/src/internal.hpp +++ b/port/secude/src/internal.hpp @@ -302,6 +302,9 @@ void *print_keyinfo_flag_address(); // ---- misc ------------------------------------------------------------------- Bytes random_bytes(size_t n); +// CrypTool passes Windows style paths ("C:\\dir\\file"); off Windows every path +// used for file access goes through this: '\\' becomes '/', a drive prefix is dropped. +std::string native_path(const std::string &path); Bytes read_file(const std::string &path, bool &exists); void write_file_atomic(const std::string &path, const Bytes &data, int mode = 0600); std::string hex(const uint8_t *p, size_t n, bool upper = true); diff --git a/port/secude/src/keystore.cpp b/port/secude/src/keystore.cpp index 7c42d2268..bcb36334c 100644 --- a/port/secude/src/keystore.cpp +++ b/port/secude/src/keystore.cpp @@ -232,7 +232,7 @@ int secude_compat_create_ca(const char *ca_pse_path, const char *ca_dir, const c return guarded("secude_compat_create_ca", -1, [&] { if (!ca_pse_path || !ca_dir) fail(EINVALID, "missing path"); - create_ca(ca_pse_path, ca_dir, pin ? pin : SECUDE_COMPAT_CA_PIN, ca_name ? ca_name : SECUDE_COMPAT_CA_NAME, + create_ca(native_path(ca_pse_path), native_path(ca_dir), pin ? pin : SECUDE_COMPAT_CA_PIN, ca_name ? ca_name : SECUDE_COMPAT_CA_NAME, key_bits > 0 ? key_bits : 2048, validity_days > 0 ? validity_days : 7305); return 0; }); @@ -243,7 +243,7 @@ int secude_compat_create_sample_keystore(const char *key_store_root, int overwri return guarded("secude_compat_create_sample_keystore", -1, [&] { if (!key_store_root) fail(EINVALID, "missing key store directory"); - std::string pse_dir = join(key_store_root, "PSE"); + std::string pse_dir = join(native_path(key_store_root), "PSE"); std::string ca_dir = join(pse_dir, "PSECA"); std::string ca_pse = join(ca_dir, "capse.cse"); make_dirs(ca_dir); diff --git a/port/secude/src/misc.cpp b/port/secude/src/misc.cpp index 505e0c2ce..4040d1611 100644 --- a/port/secude/src/misc.cpp +++ b/port/secude/src/misc.cpp @@ -1,6 +1,7 @@ // Files, randomness, licensing and version information. #include "internal.hpp" +#include #include #include #include @@ -19,8 +20,24 @@ Bytes random_bytes(size_t n) return b; } -Bytes read_file(const std::string &path, bool &exists) +std::string native_path(const std::string &path) { +#ifdef _WIN32 + return path; +#else + std::string p = path; + if (p.size() >= 2 && p[1] == ':' && std::isalpha(static_cast(p[0]))) + p.erase(0, 2); + for (char &c : p) + if (c == '\\') + c = '/'; + return p; +#endif +} + +Bytes read_file(const std::string &win_path, bool &exists) +{ + std::string path = native_path(win_path); exists = false; FILE *f = std::fopen(path.c_str(), "rb"); if (!f) { @@ -41,8 +58,9 @@ Bytes read_file(const std::string &path, bool &exists) return data; } -void write_file_atomic(const std::string &path, const Bytes &data, int mode) +void write_file_atomic(const std::string &win_path, const Bytes &data, int mode) { + std::string path = native_path(win_path); std::string tmp = path + ".tmp" + std::to_string(::getpid()); int fd = ::open(tmp.c_str(), O_WRONLY | O_CREAT | O_TRUNC, mode); if (fd < 0) @@ -136,7 +154,7 @@ int aux_OctetString2file(OctetString *ostr, char *fn, int flag) return guarded("aux_OctetString2file", -1, [&] { if (!ostr || !fn) fail(EINVALID, "missing parameter"); - FILE *f = std::fopen(fn, flag == 3 ? "ab" : "wb"); + FILE *f = std::fopen(native_path(fn).c_str(), flag == 3 ? "ab" : "wb"); if (!f) fail(EWRITEFILE, std::string("cannot open ") + fn + ": " + std::strerror(errno)); size_t n = ostr->noctets ? std::fwrite(ostr->octets, 1, ostr->noctets, f) : 0; diff --git a/port/secude/tests/test_pse.cpp b/port/secude/tests/test_pse.cpp index 27a40f80f..628e9890d 100644 --- a/port/secude/tests/test_pse.cpp +++ b/port/secude/tests/test_pse.cpp @@ -509,3 +509,44 @@ TEST(pse_sample_keystore) af_close(ca); aux_free_OctetString(&s1); } + +TEST(pse_windows_style_paths) +{ + // CrypTool keeps paths like "C:\\...\\PSE\\PSECA\\capse.cse" + auto win = [](const std::string &posix) { + std::string w = "C:" + posix; + for (char &c : w) + if (c == '/') + c = '\\'; + return w; + }; + std::string path = testing::scratch_dir() + "/win.pse"; + PSE pse = af_create(cstr(win(path)), nullptr, const_cast("pin"), nullptr, TRUE); + CHECK(pse != nullptr); + af_close(pse); + CHECK(std::ifstream(path).good()); + pse = af_open(cstr(win(path)), nullptr, const_cast("pin"), nullptr); + CHECK(pse != nullptr); + af_close(pse); + + char text[] = "data"; + OctetString o{4, text}; + std::string file = testing::scratch_dir() + "/win.txt"; + CHECK_EQ(aux_OctetString2file(&o, cstr(win(file)), 2), 0); + CHECK_EQ(aux_OctetString2file(&o, cstr(win(file)), 3), 0); + OctetString *back = aux_file2OctetString(cstr(win(file))); + CHECK(back && std::string(back->octets, back->noctets) == "datadata"); + aux_free_OctetString(&back); + + PSE ca = af_open(cstr(win(store().ca_pse)), cstr(win(store().ca_dir)), const_cast(kCaPin), nullptr); + CHECK(ca != nullptr); + SET_OF_IssuedCertificate *l = af_cadb_get_user(ca, const_cast("CN=Bob SideChannelAttack [1152179494], DC=cryptool, DC=org")); + CHECK(l != nullptr); + aux_free_SET_OF_IssuedCertificate(&l); + af_close(ca); + + std::string root = testing::scratch_dir() + "/winstore"; + CHECK_EQ(secude_compat_create_sample_keystore(win(root).c_str(), 0), 0); + CHECK(std::ifstream(root + "/PSE/PSECA/capse.cse").good()); + CHECK(std::ifstream(root + "/PSE/PSECA/cadb.der").good()); +} From 23f928b38d8b673eeb7aa86ec213836e56dc741c Mon Sep 17 00:00:00 2001 From: Kamil Marczak Date: Tue, 6 Oct 2026 17:38:16 +0200 Subject: [PATCH 11/46] feat(mfcwx): add CWinApp on top of wxApp with message boxes and profile settings --- port/mfcwx/src/app.cpp | 618 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 618 insertions(+) create mode 100644 port/mfcwx/src/app.cpp diff --git a/port/mfcwx/src/app.cpp b/port/mfcwx/src/app.cpp new file mode 100644 index 000000000..92877a6f1 --- /dev/null +++ b/port/mfcwx/src/app.cpp @@ -0,0 +1,618 @@ +#include "windows_impl.h" + +#include "afxdlgs.h" + +#include +#include +#include + +namespace mfcwx { + +CWinApp*& AppInstance() { + static CWinApp* app = nullptr; + return app; +} + +namespace { + +class MfcWxApp : public wxApp { +public: + bool OnInit() override; + int OnExit() override; + void OnIdle(wxIdleEvent& e); + void OnFatalException() override {} + +private: + LONG m_idleCount = 0; +}; + +std::string ArgsToCommandLine(int argc, wxChar** argv) { + std::string line; + for (int i = 1; i < argc; ++i) { + if (!line.empty()) + line += ' '; + std::string a = FromWx(wxString(argv[i])); + if (a.find(' ') != std::string::npos) + line += "\"" + a + "\""; + else + line += a; + } + return line; +} + +bool MfcWxApp::OnInit() { + wxInitAllImageHandlers(); + SetAppName("CrypTool"); + SetVendorName("CrypTool"); + CWinApp* app = AppInstance(); + if (!app) + return false; + static std::string cmdLine = ArgsToCommandLine(argc, argv); + app->m_lpCmdLine = &cmdLine[0]; + app->m_nCmdShow = SW_SHOWNORMAL; + Bind(wxEVT_IDLE, &MfcWxApp::OnIdle, this); + if (!app->InitApplication() || !app->InitInstance()) { + app->ExitInstance(); + return false; + } + return app->m_pMainWnd != nullptr || !wxTopLevelWindows.empty(); +} + +int MfcWxApp::OnExit() { + CWinApp* app = AppInstance(); + return app ? app->ExitInstance() : 0; +} + +void MfcWxApp::OnIdle(wxIdleEvent& e) { + e.Skip(); + CWinApp* app = AppInstance(); + if (app && app->OnIdle(m_idleCount++)) + e.RequestMore(); + else + m_idleCount = 0; +} + +UINT ButtonsFor(UINT type) { return type & MB_TYPEMASK; } + +} // namespace + +int ShowMessageBox(wxWindow* parent, const char* text, const char* caption, UINT nType) { + if (!parent) + parent = MainWxWindow(); + long style = 0; + UINT buttons = ButtonsFor(nType); + switch (buttons) { + case MB_OKCANCEL: + case MB_RETRYCANCEL: + style = wxOK | wxCANCEL; + break; + case MB_YESNO: + style = wxYES_NO; + break; + case MB_YESNOCANCEL: + case MB_ABORTRETRYIGNORE: + case MB_CANCELTRYCONTINUE: + style = wxYES_NO | wxCANCEL; + break; + default: + style = wxOK; + break; + } + switch (nType & MB_ICONMASK) { + case MB_ICONHAND: style |= wxICON_ERROR; break; + case MB_ICONQUESTION: style |= wxICON_QUESTION; break; + case MB_ICONEXCLAMATION: style |= wxICON_WARNING; break; + case MB_ICONASTERISK: style |= wxICON_INFORMATION; break; + default: break; + } + if ((nType & MB_DEFMASK) == MB_DEFBUTTON2) { + if (style & wxYES_NO) + style |= wxNO_DEFAULT; + else if (style & wxCANCEL) + style |= wxCANCEL_DEFAULT; + } + wxString title = caption ? ToWx(caption) : wxString(ToWx(AfxGetAppName())); + wxMessageDialog dlg(parent, ToWx(text ? text : ""), title, style); + if (buttons == MB_RETRYCANCEL) + dlg.SetOKCancelLabels(_("&Retry"), _("Cancel")); + else if (buttons == MB_ABORTRETRYIGNORE) + dlg.SetYesNoCancelLabels(_("&Abort"), _("&Retry"), _("&Ignore")); + else if (buttons == MB_CANCELTRYCONTINUE) + dlg.SetYesNoCancelLabels(_("&Try Again"), _("&Continue"), _("Cancel")); + int r = dlg.ShowModal(); + switch (buttons) { + case MB_OKCANCEL: + return r == wxID_OK ? IDOK : IDCANCEL; + case MB_RETRYCANCEL: + return r == wxID_OK ? IDRETRY : IDCANCEL; + case MB_YESNO: + return r == wxID_YES ? IDYES : IDNO; + case MB_YESNOCANCEL: + return r == wxID_YES ? IDYES : r == wxID_NO ? IDNO : IDCANCEL; + case MB_ABORTRETRYIGNORE: + return r == wxID_YES ? IDABORT : r == wxID_NO ? IDRETRY : IDIGNORE; + case MB_CANCELTRYCONTINUE: + return r == wxID_YES ? IDTRYAGAIN : r == wxID_NO ? IDCONTINUE : IDCANCEL; + default: + return IDOK; + } +} + +BOOL ShowContextHelp(CWnd* pWnd, HELPINFO*) { + if (!pWnd || !pWnd->m_hWnd) + return FALSE; + wxWindow* top = wxGetTopLevelParent(pWnd->GetWx()); + CWnd* p = PermanentWnd(top); + if (p) + p->SendMessage(WM_COMMAND, ID_HELP, 0); + return TRUE; +} + +} // namespace mfcwx + +using namespace mfcwx; + +wxIMPLEMENT_APP_NO_MAIN(MfcWxApp); + +int main(int argc, char** argv) { return wxEntry(argc, argv); } + +// --------------------------------------------------------------------------------------------- +// CWinApp + +IMPLEMENT_DYNAMIC(CWinApp, CWinThread) + +BEGIN_MESSAGE_MAP(CWinApp, CWinThread) + ON_COMMAND_EX_RANGE(ID_FILE_MRU_FILE1, ID_FILE_MRU_FILE16, &CWinApp::OnOpenRecentFile) + ON_UPDATE_COMMAND_UI(ID_FILE_MRU_FILE1, &CWinApp::OnUpdateRecentFileMenu) +END_MESSAGE_MAP() + +CWinApp::CWinApp(const char* lpszAppName) + : m_hInstance(reinterpret_cast(static_cast(0x400000))), m_hPrevInstance(nullptr), + m_lpCmdLine(nullptr), m_nCmdShow(SW_SHOWNORMAL), m_pszAppName(lpszAppName), m_pszRegistryKey(nullptr), + m_pszExeName("CrypTool"), m_pszHelpFilePath(nullptr), m_pszProfileName(nullptr), m_pRecentFileList(nullptr), + m_bHelpMode(FALSE), m_nWaitCursorCount(0), m_hcurWaitCursorRestore(nullptr), m_eHelpType(0) { + AppInstance() = this; +} + +CWinApp::~CWinApp() { + for (CDocTemplate* t : m_templates) + delete t; + m_templates.clear(); + delete m_pRecentFileList; + if (AppInstance() == this) + AppInstance() = nullptr; +} + +BOOL CWinApp::InitApplication() { return TRUE; } +BOOL CWinApp::InitInstance() { return TRUE; } + +int CWinApp::ExitInstance() { + if (m_pRecentFileList) + m_pRecentFileList->WriteList(); + return 0; +} + +int CWinApp::Run() { return wxTheApp ? wxTheApp->MainLoop() : 0; } + +BOOL CWinApp::OnIdle(LONG lCount) { + if (lCount <= 0) { + for (CDocTemplate* t : m_templates) { + POSITION pos = t->GetFirstDocPosition(); + while (pos) + if (CDocument* d = t->GetNextDoc(pos)) + d->OnIdle(); + } + } + return FALSE; +} + +void CWinApp::AddDocTemplate(CDocTemplate* pTemplate) { m_templates.push_back(pTemplate); } + +POSITION CWinApp::GetFirstDocTemplatePosition() const { + return m_templates.empty() ? nullptr : reinterpret_cast(static_cast(1)); +} + +CDocTemplate* CWinApp::GetNextDocTemplate(POSITION& pos) const { + size_t i = static_cast(reinterpret_cast(pos)) - 1; + CDocTemplate* t = i < m_templates.size() ? m_templates[i] : nullptr; + pos = i + 1 < m_templates.size() ? reinterpret_cast(static_cast(i + 2)) : nullptr; + return t; +} + +CDocument* CWinApp::OpenDocumentFile(const char* lpszFileName) { + CDocTemplate* best = nullptr; + CDocTemplate::Confidence bestMatch = CDocTemplate::noAttempt; + CDocument* open = nullptr; + for (CDocTemplate* t : m_templates) { + CDocument* match = nullptr; + CDocTemplate::Confidence c = t->MatchDocType(lpszFileName, match); + if (c > bestMatch) { + bestMatch = c; + best = t; + } + if (c == CDocTemplate::yesAlreadyOpen) { + open = match; + break; + } + } + if (open) { + POSITION pos = open->GetFirstViewPosition(); + if (CView* v = pos ? open->GetNextView(pos) : nullptr) + if (CFrameWnd* f = v->GetParentFrame()) + f->ActivateFrame(); + return open; + } + return best ? best->OpenDocumentFile(lpszFileName) : nullptr; +} + +void CWinApp::AddToRecentFileList(const char* lpszPathName) { + if (m_pRecentFileList) + m_pRecentFileList->Add(lpszPathName); +} + +BOOL CWinApp::SaveAllModified() { + for (CDocTemplate* t : m_templates) + if (!t->SaveAllModified()) + return FALSE; + return TRUE; +} + +void CWinApp::CloseAllDocuments(BOOL bEndSession) { + for (CDocTemplate* t : m_templates) + t->CloseAllDocuments(bEndSession); +} + +int CWinApp::DoMessageBox(const char* lpszPrompt, UINT nType, UINT) { + return ShowMessageBox(nullptr, lpszPrompt, m_pszAppName, nType); +} + +void CWinApp::DoWaitCursor(int nCode) { + OnMain([&] { + if (nCode > 0) { + ++m_nWaitCursorCount; + wxBeginBusyCursor(); + } else if (nCode < 0 && m_nWaitCursorCount > 0) { + --m_nWaitCursorCount; + if (wxIsBusy()) + wxEndBusyCursor(); + } + }); +} + +BOOL CWinApp::DoPromptFileName(CString& fileName, UINT nIDSTitle, DWORD lFlags, BOOL bOpenFileDialog, + CDocTemplate* pTemplate) { + CString filter; + std::vector templates; + if (pTemplate) + templates.push_back(pTemplate); + else + templates = m_templates; + CString defExt; + for (CDocTemplate* t : templates) { + CString name, ext; + if (t->GetDocString(name, CDocTemplate::filterName) && t->GetDocString(ext, CDocTemplate::filterExt) && + !name.IsEmpty() && !ext.IsEmpty()) { + filter += name + "|*" + ext + "|"; + if (defExt.IsEmpty()) + defExt = ext.Mid(1); + } + } + CString all; + if (!all.LoadString(AFX_IDS_ALLFILTER)) + all = "All Files (*.*)"; + filter += all + "|*|"; + CFileDialog dlg(bOpenFileDialog, bOpenFileDialog ? nullptr : defExt.GetString(), fileName, + lFlags | OFN_HIDEREADONLY | (bOpenFileDialog ? OFN_FILEMUSTEXIST : OFN_OVERWRITEPROMPT), filter); + dlg.m_strTitle.LoadString(nIDSTitle); + if (dlg.DoModal() != IDOK) + return FALSE; + fileName = dlg.GetPathName(); + return TRUE; +} + +BOOL CWinApp::ProcessShellCommand(CCommandLineInfo& rCmdInfo) { + switch (rCmdInfo.m_nShellCommand) { + case CCommandLineInfo::FileNew: + OnCmdMsg(ID_FILE_NEW, 0, nullptr, nullptr); + return TRUE; + case CCommandLineInfo::FileOpen: + return OpenDocumentFile(rCmdInfo.m_strFileName) != nullptr; + default: + return TRUE; + } +} + +void CWinApp::ParseCommandLine(CCommandLineInfo& rCmdInfo) { + if (!wxTheApp) + return; + for (int i = 1; i < wxTheApp->argc; ++i) { + std::string a = FromWx(wxTheApp->argv[i]); + if (a.compare(0, 4, "-psn") == 0) + continue; + bool flag = !a.empty() && (a[0] == '-' || a[0] == '/') && a.find('.') == std::string::npos; + std::string param = flag ? a.substr(1) : AppPath(wxTheApp->argv[i].utf8_str()); + rCmdInfo.ParseParam(param.c_str(), flag, i == wxTheApp->argc - 1); + } +} + +HCURSOR CWinApp::LoadCursor(const char* lpszResourceName) const { return ::LoadCursor(m_hInstance, lpszResourceName); } +HCURSOR CWinApp::LoadCursor(UINT nIDResource) const { return ::LoadCursor(m_hInstance, MAKEINTRESOURCE(nIDResource)); } +HCURSOR CWinApp::LoadStandardCursor(const char* lpszCursorName) const { return ::LoadCursor(nullptr, lpszCursorName); } +HCURSOR CWinApp::LoadOEMCursor(UINT nIDCursor) const { return ::LoadCursor(nullptr, MAKEINTRESOURCE(nIDCursor)); } +HICON CWinApp::LoadIcon(const char* lpszResourceName) const { return ::LoadIcon(m_hInstance, lpszResourceName); } +HICON CWinApp::LoadIcon(UINT nIDResource) const { return ::LoadIcon(m_hInstance, MAKEINTRESOURCE(nIDResource)); } +HICON CWinApp::LoadStandardIcon(const char* lpszIconName) const { return ::LoadIcon(nullptr, lpszIconName); } +HICON CWinApp::LoadOEMIcon(UINT nIDIcon) const { return ::LoadIcon(nullptr, MAKEINTRESOURCE(nIDIcon)); } + +namespace { + +CString SectionKey(const CWinApp* app, const char* section) { + CString key = "Software\\"; + key += app->m_pszRegistryKey && *app->m_pszRegistryKey ? app->m_pszRegistryKey : "CrypTool"; + key += "\\"; + key += app->m_pszAppName ? app->m_pszAppName : "CrypTool"; + if (section && *section) { + key += "\\"; + key += section; + } + return key; +} + +} // namespace + +UINT CWinApp::GetProfileInt(const char* lpszSection, const char* lpszEntry, int nDefault) { + HKEY key = nullptr; + if (RegOpenKeyEx(HKEY_CURRENT_USER, SectionKey(this, lpszSection), 0, KEY_READ, &key) != ERROR_SUCCESS) + return static_cast(nDefault); + DWORD value = 0, size = sizeof value, type = 0; + LONG r = RegQueryValueEx(key, lpszEntry, nullptr, &type, reinterpret_cast(&value), &size); + RegCloseKey(key); + return r == ERROR_SUCCESS && type == REG_DWORD ? value : static_cast(nDefault); +} + +BOOL CWinApp::WriteProfileInt(const char* lpszSection, const char* lpszEntry, int nValue) { + HKEY key = nullptr; + if (RegCreateKeyEx(HKEY_CURRENT_USER, SectionKey(this, lpszSection), 0, nullptr, 0, KEY_WRITE, nullptr, &key, + nullptr) != ERROR_SUCCESS) + return FALSE; + DWORD v = static_cast(nValue); + LONG r = RegSetValueEx(key, lpszEntry, 0, REG_DWORD, reinterpret_cast(&v), sizeof v); + RegCloseKey(key); + return r == ERROR_SUCCESS; +} + +CString CWinApp::GetProfileString(const char* lpszSection, const char* lpszEntry, const char* lpszDefault) { + HKEY key = nullptr; + CString def = lpszDefault ? lpszDefault : ""; + if (RegOpenKeyEx(HKEY_CURRENT_USER, SectionKey(this, lpszSection), 0, KEY_READ, &key) != ERROR_SUCCESS) + return def; + DWORD size = 0, type = 0; + if (RegQueryValueEx(key, lpszEntry, nullptr, &type, nullptr, &size) != ERROR_SUCCESS || type != REG_SZ) { + RegCloseKey(key); + return def; + } + CString value; + RegQueryValueEx(key, lpszEntry, nullptr, &type, reinterpret_cast(value.GetBuffer(static_cast(size) + 1)), &size); + value.ReleaseBuffer(); + RegCloseKey(key); + return value; +} + +BOOL CWinApp::WriteProfileString(const char* lpszSection, const char* lpszEntry, const char* lpszValue) { + HKEY key = nullptr; + if (RegCreateKeyEx(HKEY_CURRENT_USER, SectionKey(this, lpszSection), 0, nullptr, 0, KEY_WRITE, nullptr, &key, + nullptr) != ERROR_SUCCESS) + return FALSE; + LONG r; + if (!lpszEntry) + r = ERROR_SUCCESS; + else if (!lpszValue) + r = RegDeleteValue(key, lpszEntry); + else + r = RegSetValueEx(key, lpszEntry, 0, REG_SZ, reinterpret_cast(lpszValue), + static_cast(strlen(lpszValue) + 1)); + RegCloseKey(key); + return r == ERROR_SUCCESS; +} + +BOOL CWinApp::GetProfileBinary(const char* lpszSection, const char* lpszEntry, LPBYTE* ppData, UINT* pBytes) { + *ppData = nullptr; + *pBytes = 0; + HKEY key = nullptr; + if (RegOpenKeyEx(HKEY_CURRENT_USER, SectionKey(this, lpszSection), 0, KEY_READ, &key) != ERROR_SUCCESS) + return FALSE; + DWORD size = 0, type = 0; + if (RegQueryValueEx(key, lpszEntry, nullptr, &type, nullptr, &size) != ERROR_SUCCESS) { + RegCloseKey(key); + return FALSE; + } + *ppData = new BYTE[size ? size : 1]; + RegQueryValueEx(key, lpszEntry, nullptr, &type, *ppData, &size); + *pBytes = size; + RegCloseKey(key); + return TRUE; +} + +BOOL CWinApp::WriteProfileBinary(const char* lpszSection, const char* lpszEntry, LPBYTE pData, UINT nBytes) { + HKEY key = nullptr; + if (RegCreateKeyEx(HKEY_CURRENT_USER, SectionKey(this, lpszSection), 0, nullptr, 0, KEY_WRITE, nullptr, &key, + nullptr) != ERROR_SUCCESS) + return FALSE; + LONG r = RegSetValueEx(key, lpszEntry, 0, REG_BINARY, pData, nBytes); + RegCloseKey(key); + return r == ERROR_SUCCESS; +} + +void CWinApp::SetRegistryKey(const char* lpszRegistryKey) { + m_pszRegistryKey = strdup(lpszRegistryKey && *lpszRegistryKey ? lpszRegistryKey : "CrypTool"); +} + +void CWinApp::SetRegistryKey(UINT nIDRegistryKey) { + CString key; + key.LoadString(nIDRegistryKey); + SetRegistryKey(key); +} + +void CWinApp::LoadStdProfileSettings(UINT nMaxMRU) { + if (nMaxMRU && !m_pRecentFileList) { + m_pRecentFileList = new CRecentFileList(0, "Recent File List", "File%d", static_cast(nMaxMRU)); + m_pRecentFileList->ReadList(); + } +} + +void CWinApp::HideApplication() { + if (m_pMainWnd) + m_pMainWnd->ShowWindow(SW_HIDE); +} + +void CWinApp::OnFileNew() { + if (!m_templates.empty()) + m_templates.front()->OpenDocumentFile(nullptr); +} + +void CWinApp::OnFileOpen() { + CString name; + if (DoPromptFileName(name, AFX_IDS_OPENFILE, OFN_HIDEREADONLY | OFN_FILEMUSTEXIST, TRUE, nullptr)) + OpenDocumentFile(name); +} + +void CWinApp::OnFilePrintSetup() {} + +void CWinApp::OnAppExit() { + if (m_pMainWnd) + m_pMainWnd->SendMessage(WM_CLOSE); + else if (wxTheApp) + wxTheApp->ExitMainLoop(); +} + +void CWinApp::OnHelp() { WinHelpInternal(0, HELP_FINDER); } +void CWinApp::OnHelpFinder() { WinHelpInternal(0, HELP_FINDER); } +void CWinApp::OnHelpIndex() { WinHelpInternal(0, HELP_INDEX); } +void CWinApp::OnContextHelp() { WinHelpInternal(0, HELP_FINDER); } +void CWinApp::OnHelpUsing() { WinHelpInternal(0, HELP_HELPONHELP); } + +BOOL CWinApp::OnOpenRecentFile(UINT nID) { + if (!m_pRecentFileList) + return FALSE; + int index = static_cast(nID - ID_FILE_MRU_FILE1); + if (index < 0 || index >= m_pRecentFileList->GetSize()) + return FALSE; + CString path = (*m_pRecentFileList)[index]; + if (path.IsEmpty()) + return FALSE; + if (!OpenDocumentFile(path)) + m_pRecentFileList->Remove(index); + return TRUE; +} + +void CWinApp::OnUpdateRecentFileMenu(CCmdUI* pCmdUI) { + if (m_pRecentFileList) + m_pRecentFileList->UpdateMenu(pCmdUI); + else + pCmdUI->Enable(FALSE); +} + +// --------------------------------------------------------------------------------------------- +// Global helpers + +CWinApp* AFXAPI AfxGetApp() { return AppInstance(); } + +CWnd* AFXAPI AfxGetMainWnd() { + CWinApp* app = AppInstance(); + return app ? app->m_pMainWnd : nullptr; +} + +HINSTANCE AFXAPI AfxGetInstanceHandle() { + CWinApp* app = AppInstance(); + return app ? app->m_hInstance : reinterpret_cast(static_cast(0x400000)); +} + +HINSTANCE AFXAPI AfxGetResourceHandle() { return AfxGetInstanceHandle(); } +void AFXAPI AfxSetResourceHandle(HINSTANCE) {} +HINSTANCE AFXAPI AfxFindResourceHandle(const char*, const char*) { return AfxGetInstanceHandle(); } + +const char* AFXAPI AfxGetAppName() { + CWinApp* app = AppInstance(); + return app && app->m_pszAppName ? app->m_pszAppName : "CrypTool"; +} + +int AFXAPI AfxMessageBox(const char* lpszText, UINT nType, UINT nIDHelp) { + CWinApp* app = AppInstance(); + return OnMain([&]() -> int { + if (app) + return app->DoMessageBox(lpszText, nType, nIDHelp); + return ShowMessageBox(nullptr, lpszText, nullptr, nType); + }); +} + +int AFXAPI AfxMessageBox(UINT nIDPrompt, UINT nType, UINT nIDHelp) { + CString text; + text.LoadString(nIDPrompt); + return AfxMessageBox(text, nType, nIDHelp == static_cast(-1) ? nIDPrompt : nIDHelp); +} + +void AFXAPI AfxFormatString1(CString& rString, UINT nIDS, const char* lpsz1) { + rString.LoadString(nIDS); + rString.Replace("%1", lpsz1 ? lpsz1 : ""); +} + +void AFXAPI AfxFormatString2(CString& rString, UINT nIDS, const char* lpsz1, const char* lpsz2) { + rString.LoadString(nIDS); + CString result; + const char* p = rString; + while (*p) { + if (p[0] == '%' && (p[1] == '1' || p[1] == '2')) { + result += p[1] == '1' ? (lpsz1 ? lpsz1 : "") : (lpsz2 ? lpsz2 : ""); + p += 2; + } else { + result += *p++; + } + } + rString = result; +} + +BOOL AFXAPI AfxExtractSubString(CString& rString, const char* lpszFullString, int iSubString, char chSep) { + rString.Empty(); + if (!lpszFullString) + return FALSE; + while (iSubString--) { + lpszFullString = strchr(lpszFullString, chSep); + if (!lpszFullString) + return FALSE; + ++lpszFullString; + } + const char* end = strchr(lpszFullString, chSep); + int len = end ? static_cast(end - lpszFullString) : static_cast(strlen(lpszFullString)); + rString = CString(lpszFullString, len); + return TRUE; +} + +const char* AFXAPI AfxRegisterWndClass(UINT, HCURSOR, HBRUSH, HICON) { return "AfxWnd"; } +BOOL AFXAPI AfxRegisterClass(WNDCLASS* lpWndClass) { return RegisterClass(lpWndClass) != 0; } +BOOL AFXAPI AfxInitRichEdit() { return TRUE; } +BOOL AFXAPI AfxInitRichEdit2() { return TRUE; } +BOOL AFXAPI AfxOleInit() { return TRUE; } +void AFXAPI AfxEnableControlContainer() {} +BOOL AFXAPI AfxSocketInit() { return TRUE; } +void AFXAPI AfxInitCommonControls() {} +BOOL AFXAPI AfxCheckMemory() { return TRUE; } + +void AFXAPI AfxGetModuleShortFileName(HINSTANCE, CString& strShortName) { + char buf[_MAX_PATH]; + GetModuleFileName(nullptr, buf, sizeof buf); + strShortName = buf; +} + +LRESULT AFXAPI AfxCallWndProc(CWnd* pWnd, HWND, UINT nMsg, WPARAM wParam, LPARAM lParam) { + return pWnd ? pWnd->WindowProc(nMsg, wParam, lParam) : 0; +} + +CWaitCursor::CWaitCursor() { + if (CWinApp* app = AfxGetApp()) + app->DoWaitCursor(1); +} + +CWaitCursor::~CWaitCursor() { + if (CWinApp* app = AfxGetApp()) + app->DoWaitCursor(-1); +} From 1a6f2cc401f4dcc71d0c665e62829755b6baf412 Mon Sep 17 00:00:00 2001 From: Kamil Marczak Date: Tue, 6 Oct 2026 17:39:53 +0200 Subject: [PATCH 12/46] feat(app): locate data files, UI language and temp directory at startup The data directory is MFCWX_DATA_DIR, the installed resources or a development directory assembled in the build tree (bitmaps, help, template files and freshly generated key stores). The language comes from MFCWX_LANG, the saved setting or the system language. Paths are resolved case-insensitively like on Windows. --- port/app/CMakeLists.txt | 24 +++++++++++++-- port/app/assemble_data.cmake | 31 ++++++++++++++++++++ port/app/platform.cpp | 15 ++++++++++ port/mfcwx/include/mfcwx/app.h | 23 +++++++++++++++ port/mfcwx/src/app.cpp | 45 +++++++++++++++++++++++++++++ port/mfcwx/src/codepage.cpp | 53 ++++++++++++++++++++++++++++++++-- port/mfcwx/src/internal.h | 30 +++++++++++++++++-- port/mfcwx/src/resources.cpp | 15 ++++++++-- 8 files changed, 227 insertions(+), 9 deletions(-) create mode 100644 port/app/assemble_data.cmake create mode 100644 port/app/platform.cpp create mode 100644 port/mfcwx/include/mfcwx/app.h diff --git a/port/app/CMakeLists.txt b/port/app/CMakeLists.txt index 8f75cb8a6..98fdfdbe2 100644 --- a/port/app/CMakeLists.txt +++ b/port/app/CMakeLists.txt @@ -1,14 +1,24 @@ include(${REPO_ROOT}/port/rc/rc2cpp.cmake) set(CT_SRC ${REPO_ROOT}/CrypTool) -file(GLOB CT_SOURCES CONFIGURE_DEPENDS ${CT_SRC}/*.cpp ${CT_SRC}/zip/*.c) +# *.CPP: the MIRACL wrapper copies (MONTY.CPP, ...); globbing is case-sensitive on Linux. +file(GLOB CT_SOURCES CONFIGURE_DEPENDS ${CT_SRC}/*.cpp ${CT_SRC}/*.CPP ${CT_SRC}/zip/*.c) +list(REMOVE_DUPLICATES CT_SOURCES) list(FILTER CT_SOURCES EXCLUDE REGEX "/(Ber_Ell_Kurve|DlgCrypToolPRNGSeedCollector|DlgFindText|DlgHomophonicSubstitution|DlgMac|DlgSelHash|HomophonicSubstitution)\\.cpp$") +# zip/main.c is the standalone test driver of the zip code (not part of CrypTool.vcxproj). +list(FILTER CT_SOURCES EXCLUDE REGEX "/zip/main\\.c$") +# OLE-based picture control on Windows, wxWidgets-based elsewhere. +if(WIN32) + list(FILTER CT_SOURCES EXCLUDE REGEX "/PictureEx(Loop)?_wx\\.cpp$") +else() + list(FILTER CT_SOURCES EXCLUDE REGEX "/PictureEx(Loop)?\\.cpp$") +endif() file(GLOB CT_EXTRA_SOURCES CONFIGURE_DEPENDS ${REPO_ROOT}/libVolRen/ArcBall.cpp ${REPO_ROOT}/libVolRen/CMatrix.cpp ${REPO_ROOT}/libVolRen/CPosition.cpp ${REPO_ROOT}/libVolRen/CQuaternion.cpp ${REPO_ROOT}/libVolRen/CVector.cpp ${REPO_ROOT}/libVolRen/Volume.cpp ${REPO_ROOT}/libVolRen/VolumeUtilities.cpp ${REPO_ROOT}/ChallengeResponse/*.cpp) -add_executable(CrypTool ${CT_SOURCES} ${CT_EXTRA_SOURCES}) +add_executable(CrypTool ${CT_SOURCES} ${CT_EXTRA_SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/platform.cpp) mfcwx_add_rc(CrypTool RC ${CT_SRC}/CrypTool.rc MODULE CrypTool HEADERS ${CT_SRC}/resource.h) set_target_properties(CrypTool PROPERTIES CXX_STANDARD 14 CXX_EXTENSIONS ON) @@ -31,3 +41,13 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") else() target_compile_options(CrypTool PRIVATE $<$:-Wno-narrowing -fpermissive>) endif() + +set(CRYPTOOL_DEV_DATA_DIR ${CMAKE_BINARY_DIR}/data) +target_compile_definitions(CrypTool PRIVATE CRYPTOOL_DEV_DATA_DIR="${CRYPTOOL_DEV_DATA_DIR}") +add_custom_target(cryptool_data ALL + COMMAND ${CMAKE_COMMAND} -DREPO_ROOT=${REPO_ROOT} -DDATA_DIR=${CRYPTOOL_DEV_DATA_DIR} + -DMKPSE=$ -P ${CMAKE_CURRENT_SOURCE_DIR}/assemble_data.cmake + DEPENDS secude_compat_mkpse + COMMENT "Assembling the CrypTool data directory" + VERBATIM) +add_dependencies(CrypTool cryptool_data) diff --git a/port/app/assemble_data.cmake b/port/app/assemble_data.cmake new file mode 100644 index 000000000..c228ede15 --- /dev/null +++ b/port/app/assemble_data.cmake @@ -0,0 +1,31 @@ +# Builds the data directory the application reads at run time (a development stand-in for the +# installed resources): links to the bitmaps, help and template files plus freshly generated key stores. +# cmake -DREPO_ROOT=... -DDATA_DIR=... -DMKPSE=... -P assemble_data.cmake + +file(MAKE_DIRECTORY "${DATA_DIR}") + +function(link_into name target) + if(NOT EXISTS "${DATA_DIR}/${name}" AND EXISTS "${target}") + file(CREATE_LINK "${target}" "${DATA_DIR}/${name}" SYMBOLIC) + endif() +endfunction() + +link_into(res "${REPO_ROOT}/CrypTool/res") +link_into(hlp_en "${REPO_ROOT}/CrypTool/hlp_en") +link_into(hlp_de "${REPO_ROOT}/CrypTool/hlp_de") + +file(GLOB template_entries RELATIVE "${REPO_ROOT}/setup/template" "${REPO_ROOT}/setup/template/*") +foreach(entry IN LISTS template_entries) + string(TOLOWER "${entry}" lower) + if(lower MATCHES "\\.(dll|manifest)$" OR lower STREQUAL "pse") + continue() + endif() + link_into("${entry}" "${REPO_ROOT}/setup/template/${entry}") +endforeach() + +if(MKPSE AND NOT EXISTS "${DATA_DIR}/PSE/PSECA/capse.cse") + execute_process(COMMAND "${MKPSE}" "${DATA_DIR}" RESULT_VARIABLE result) + if(NOT result EQUAL 0) + message(WARNING "secude_compat_mkpse failed: ${result}") + endif() +endif() diff --git a/port/app/platform.cpp b/port/app/platform.cpp new file mode 100644 index 000000000..3b945674d --- /dev/null +++ b/port/app/platform.cpp @@ -0,0 +1,15 @@ +#include "mfcwx/app.h" +#include "mfcwx/rcdata.h" + +extern const mfcwx::rc::Module g_rcModule_CrypTool; + +namespace { + +struct RegisterCrypTool { + RegisterCrypTool() { + mfcwx::RegisterResourceModule(&g_rcModule_CrypTool); + mfcwx::SetDefaultDataDirectory(CRYPTOOL_DEV_DATA_DIR); + } +} registerCrypTool; + +} // namespace diff --git a/port/mfcwx/include/mfcwx/app.h b/port/mfcwx/include/mfcwx/app.h new file mode 100644 index 000000000..6f671ea4b --- /dev/null +++ b/port/mfcwx/include/mfcwx/app.h @@ -0,0 +1,23 @@ +#pragma once + +// Hooks for the platform glue of an application built on mfcwx. + +#include +#include + +namespace mfcwx { + +namespace rc { +struct Module; +} + +void RegisterResourceModule(const rc::Module* module); +void SetResourceLanguage(const char* code); +const char* GetResourceLanguage(); +std::vector AvailableResourceLanguages(); +std::string GetDataDirectory(); +void SetDataDirectory(const std::string& dir); +// Used when neither MFCWX_DATA_DIR nor an installed resource directory is found (development builds). +void SetDefaultDataDirectory(const std::string& dir); + +} // namespace mfcwx diff --git a/port/mfcwx/src/app.cpp b/port/mfcwx/src/app.cpp index 92877a6f1..197115764 100644 --- a/port/mfcwx/src/app.cpp +++ b/port/mfcwx/src/app.cpp @@ -1,9 +1,12 @@ #include "windows_impl.h" #include "afxdlgs.h" +#include "mfcwx/app.h" +#include "runtime.h" #include #include +#include #include namespace mfcwx { @@ -40,6 +43,47 @@ std::string ArgsToCommandLine(int argc, wxChar** argv) { return line; } +std::string ChooseLanguage() { + std::vector available = AvailableResourceLanguages(); + auto has = [&](const std::string& code) { + return std::find(available.begin(), available.end(), code) != available.end(); + }; + wxString env; + if (wxGetEnv("MFCWX_LANG", &env) && has(std::string(env.utf8_str()))) + return std::string(env.utf8_str()); + HKEY key = nullptr; + if (RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\mfcwx", 0, KEY_READ, &key) == ERROR_SUCCESS) { + char buf[16] = {0}; + DWORD size = sizeof buf - 1, type = 0; + LONG r = RegQueryValueEx(key, "Language", nullptr, &type, reinterpret_cast(buf), &size); + RegCloseKey(key); + if (r == ERROR_SUCCESS && has(buf)) + return buf; + } + const wxLanguageInfo* info = wxLocale::GetLanguageInfo(wxLocale::GetSystemLanguage()); + std::string code = info ? std::string(info->CanonicalName.Left(2).Lower().utf8_str()) : "en"; + if (code == "sr") + code = "rs"; + return has(code) ? code : "en"; +} + +void PrepareEnvironment(int argc, wxChar** argv) { + SetResourceLanguage(ChooseLanguage().c_str()); + std::string temp = TempDirectory(); + std::string tempApp = AppPath(temp.c_str()); + while (!tempApp.empty() && tempApp.back() == '\\') + tempApp.pop_back(); + wxSetEnv("TEMP", ToWx(tempApp.c_str())); + wxSetEnv("TMP", ToWx(tempApp.c_str())); + // The Windows code locates its data files next to the program it finds in the command line. + std::string program = AppPath((GetDataDirectory() + "/CrypTool").c_str()); + std::string commandLine = "\"" + program + "\""; + std::string args = ArgsToCommandLine(argc, argv); + if (!args.empty()) + commandLine += " " + args; + SetCommandLineOverride(commandLine.c_str()); +} + bool MfcWxApp::OnInit() { wxInitAllImageHandlers(); SetAppName("CrypTool"); @@ -47,6 +91,7 @@ bool MfcWxApp::OnInit() { CWinApp* app = AppInstance(); if (!app) return false; + PrepareEnvironment(argc, argv); static std::string cmdLine = ArgsToCommandLine(argc, argv); app->m_lpCmdLine = &cmdLine[0]; app->m_nCmdShow = SW_SHOWNORMAL; diff --git a/port/mfcwx/src/codepage.cpp b/port/mfcwx/src/codepage.cpp index 0f4b16938..bc54273de 100644 --- a/port/mfcwx/src/codepage.cpp +++ b/port/mfcwx/src/codepage.cpp @@ -1,6 +1,9 @@ #include "bridge.h" #include +#include +#include +#include #include #include @@ -118,6 +121,50 @@ std::string Utf8ToAnsi(const char* utf8) { return FromWx(wxString::FromUTF8(utf8)); } +namespace { + +bool Exists(const std::string& p) { + struct stat st; + return stat(p.c_str(), &st) == 0; +} + +// Windows file names are case-insensitive; find the spelling used on disk, component by component. +std::string ResolveCase(const std::string& path) { + if (path.empty() || Exists(path)) + return path; + std::string resolved = path[0] == '/' ? "/" : ""; + size_t pos = path[0] == '/' ? 1 : 0; + while (pos <= path.size()) { + size_t next = path.find('/', pos); + std::string part = path.substr(pos, next == std::string::npos ? std::string::npos : next - pos); + pos = next == std::string::npos ? path.size() + 1 : next + 1; + if (part.empty()) + continue; + std::string candidate = resolved + part; + if (!Exists(candidate)) { + DIR* dir = opendir(resolved.empty() ? "." : resolved.c_str()); + if (!dir) + return path; + std::string match; + while (dirent* e = readdir(dir)) + if (strcasecmp(e->d_name, part.c_str()) == 0) { + match = e->d_name; + break; + } + closedir(dir); + if (match.empty()) + return path; + candidate = resolved + match; + } + resolved = candidate; + if (pos <= path.size()) + resolved += '/'; + } + return resolved; +} + +} // namespace + std::string NativePath(const char* path) { if (!path) return std::string(); @@ -133,9 +180,9 @@ std::string NativePath(const char* path) { ascii = false; break; } - if (ascii) - return p; - return std::string(ToWx(p.c_str()).utf8_str()); + if (!ascii) + p = std::string(ToWx(p.c_str()).utf8_str()); + return ResolveCase(p); } } // namespace mfcwx diff --git a/port/mfcwx/src/internal.h b/port/mfcwx/src/internal.h index cb1b91e6a..a76d72bac 100644 --- a/port/mfcwx/src/internal.h +++ b/port/mfcwx/src/internal.h @@ -48,6 +48,7 @@ const rc::VersionInfo* GetVersionInfo(); // Directory holding the application's data files (res/, help, pse, examples, ...). std::string GetDataDirectory(); void SetDataDirectory(const std::string& dir); +void SetDefaultDataDirectory(const std::string& dir); wxBitmap LoadBitmapResource(const ResRef& ref); wxIcon LoadIconResource(const ResRef& ref); @@ -162,22 +163,47 @@ struct CurrentMessage { CurrentMessage* GetCurrentMessageSlot(); // --------------------------------------------------------------------------------------------- -// GDI (gdi.cpp) +// GDI (gdi.cpp, gdi_dc.cpp, gdi_text.cpp, gdi_bits.cpp) struct GdiObjectImpl; -// HGDIOBJ values are CGdiObject* for application-created objects and stock objects. +// HGDIOBJ values point to refcounted GdiObjectImpl objects (stock objects are never freed); +// (HBRUSH)(COLOR_xxx + 1) values are accepted wherever Win32 accepts them. HDC values point to +// a DCState; CDC::m_hDC holds it. wxPen PenFromHandle(HPEN h); wxBrush BrushFromHandle(HBRUSH h); wxFont FontFromHandle(HFONT h); +// The bitmap is deselected from a memory DC first so its pixels are current; null if invalid. wxBitmap* BitmapFromHandle(HBITMAP h); HFONT DefaultGuiFont(); wxColour ToWxColour(COLORREF c); COLORREF FromWxColour(const wxColour& c); COLORREF SysColor(int index); +// A new HBITMAP owning a copy of bmp, as if created by CreateBitmap (freed by DeleteObject). +HBITMAP CreateBitmapHandle(const wxBitmap& bmp); +// A shared, never deleted HFONT describing font (for WM_GETFONT of controls using wx fonts). +HFONT FontHandleFor(const wxFont& font); +// HICON values are wxIcon* owned by the icon loader. +inline wxIcon* IconFromHandle(HICON h) { return reinterpret_cast(h); } // Wraps a wx DC that is owned elsewhere (e.g. a wxPaintDC in an event handler) as CDC. +// Wrapping a wxPaintDC registers it as the window's paint DC until UnwrapDC: CPaintDC, CClientDC +// and GetDC created for that window meanwhile draw through it instead of creating another +// wxPaintDC (nested native paint contexts must be destroyed in LIFO order on macOS). CDC* WrapDC(wxDC* dc, wxWindow* window); void UnwrapDC(CDC* cdc); +// Deletes the temporary CGdiObject/CDC wrappers made by FromHandle and releases the native DCs of +// GetDC handles that were not released; call on idle. +void PurgeTemporaryGdiWrappers(); + +// Drawing through CClientDC/GetDC outside a paint event is not visible on macOS (and Wayland), so +// it goes to a per-window transparent overlay bitmap and the window is refreshed. The paint bridge +// must call PaintClientDCOverlay(window, dc) at the end of every paint event (after WM_PAINT, with +// the event's paint DC); it also ends DCs obtained during that paint. ClearClientDCOverlay drops +// the overlay (or clears rect, in client coordinates) and is called on Invalidate/InvalidateRect +// and size changes. +void PaintClientDCOverlay(wxWindow* window, wxDC& dc); +bool HasClientDCOverlay(wxWindow* window); +void ClearClientDCOverlay(wxWindow* window, const wxRect* rect = nullptr); // --------------------------------------------------------------------------------------------- // Menus (menu.cpp) diff --git a/port/mfcwx/src/resources.cpp b/port/mfcwx/src/resources.cpp index cb979f985..0a3695361 100644 --- a/port/mfcwx/src/resources.cpp +++ b/port/mfcwx/src/resources.cpp @@ -17,6 +17,7 @@ struct ResourceContext { std::vector modules; std::string language = "en"; std::string dataDir; + std::string defaultDataDir; std::mutex bitmapMutex; std::unordered_map bitmapCache; }; @@ -183,12 +184,22 @@ const rc::VersionInfo* GetVersionInfo() { std::string GetDataDirectory() { if (Ctx().dataDir.empty()) { - wxString dir = wxStandardPaths::Get().GetResourcesDir(); - Ctx().dataDir = std::string(dir.utf8_str()); + wxString env; + if (wxGetEnv("MFCWX_DATA_DIR", &env) && wxDirExists(env)) { + Ctx().dataDir = std::string(env.utf8_str()); + } else { + wxString installed = wxStandardPaths::Get().GetResourcesDir(); + if (wxDirExists(installed + "/res") || Ctx().defaultDataDir.empty()) + Ctx().dataDir = std::string(installed.utf8_str()); + else + Ctx().dataDir = Ctx().defaultDataDir; + } } return Ctx().dataDir; } +void SetDefaultDataDirectory(const std::string& dir) { Ctx().defaultDataDir = dir; } + void SetDataDirectory(const std::string& dir) { Ctx().dataDir = dir; } wxBitmap LoadBitmapResource(const ResRef& ref) { From bfbbca2b59ce23b6e452e9ad5c4f2179c13fbea1 Mon Sep 17 00:00:00 2001 From: Kamil Marczak Date: Tue, 6 Oct 2026 17:40:13 +0200 Subject: [PATCH 13/46] feat(mfcwx): forward window scroll bar events as WM_HSCROLL/WM_VSCROLL --- port/mfcwx/src/wnd.cpp | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/port/mfcwx/src/wnd.cpp b/port/mfcwx/src/wnd.cpp index d5dffec44..c9ca7e5bd 100644 --- a/port/mfcwx/src/wnd.cpp +++ b/port/mfcwx/src/wnd.cpp @@ -538,6 +538,38 @@ void OnShow(wxShowEvent& e) { SendFromEvent(w, WM_SHOWWINDOW, e.IsShown() ? TRUE : FALSE, 0, e, nullptr); } +void OnScrollWin(wxScrollWinEvent& e) { + wxWindow* w = static_cast(e.GetEventObject()); + CWnd* pWnd = PermanentWnd(w); + UINT msg = e.GetOrientation() == wxVERTICAL ? WM_VSCROLL : WM_HSCROLL; + if (!HasMessageHandler(pWnd, msg)) { + e.Skip(); + return; + } + wxEventType t = e.GetEventType(); + UINT code = SB_ENDSCROLL; + if (t == wxEVT_SCROLLWIN_TOP) + code = SB_TOP; + else if (t == wxEVT_SCROLLWIN_BOTTOM) + code = SB_BOTTOM; + else if (t == wxEVT_SCROLLWIN_LINEUP) + code = SB_LINEUP; + else if (t == wxEVT_SCROLLWIN_LINEDOWN) + code = SB_LINEDOWN; + else if (t == wxEVT_SCROLLWIN_PAGEUP) + code = SB_PAGEUP; + else if (t == wxEVT_SCROLLWIN_PAGEDOWN) + code = SB_PAGEDOWN; + else if (t == wxEVT_SCROLLWIN_THUMBTRACK) + code = SB_THUMBTRACK; + else if (t == wxEVT_SCROLLWIN_THUMBRELEASE) + code = SB_THUMBPOSITION; + WPARAM wp = MAKEWPARAM(code, static_cast(e.GetPosition())); + SendFromEvent(w, msg, wp, 0, e, nullptr); + if (code == SB_THUMBPOSITION) + SendFromEvent(w, msg, MAKEWPARAM(SB_ENDSCROLL, 0), 0, e, nullptr); +} + void OnDestroyEvent(wxWindowDestroyEvent& e) { e.Skip(); wxWindow* w = static_cast(e.GetEventObject()); @@ -584,6 +616,10 @@ void HookWindow(wxWindow* window, ControlKind kind, int winId, DWORD style, DWOR window->Bind(wxEVT_SET_CURSOR, &OnSetCursor); window->Bind(wxEVT_SHOW, &OnShow); window->Bind(wxEVT_DESTROY, &OnDestroyEvent); + for (const wxEventTypeTag& t : {wxEVT_SCROLLWIN_TOP, wxEVT_SCROLLWIN_BOTTOM, wxEVT_SCROLLWIN_LINEUP, wxEVT_SCROLLWIN_LINEDOWN, + wxEVT_SCROLLWIN_PAGEUP, wxEVT_SCROLLWIN_PAGEDOWN, wxEVT_SCROLLWIN_THUMBTRACK, + wxEVT_SCROLLWIN_THUMBRELEASE}) + window->Bind(t, &OnScrollWin); BindControlEvents(window, st); } From c5dcc9aa02ddbacdb50ac16802f4d86e681ae5c9 Mon Sep 17 00:00:00 2001 From: Kamil Marczak Date: Tue, 6 Oct 2026 17:40:33 +0200 Subject: [PATCH 14/46] fix(app): make application sources compile with clang and gcc Work in progress: CString passed through varargs gets explicit casts, temporaries are no longer bound to non-const references, MSVC-only syntax is replaced and Windows-only parts (OLE picture loading, ActiveX editor, NVIDIA OpenGL extensions) are guarded. --- ChallengeResponse/ChallengeResponseDlg.cpp | 6 +- ChallengeResponse/Computer.cpp | 8 +- ChallengeResponse/Computer.h | 2 +- ChallengeResponse/Packet.cpp | 4 +- ChallengeResponse/Wire.cpp | 2 +- CrypTool/AutomaticAnalysis.cpp | 4 +- CrypTool/CrypToolApp.cpp | 48 +- CrypTool/CrypToolApp.h | 4 +- CrypTool/CrypToolTools.cpp | 6 +- CrypTool/CrypToolView.cpp | 2 +- CrypTool/CryptDoc.cpp | 16 +- CrypTool/Cryptography.cpp | 19 +- CrypTool/DlgAbout.cpp | 12 +- CrypTool/DlgAdfgvxIntro.cpp | 2 +- CrypTool/DlgAutomatedPermAnalysis.cpp | 2 +- CrypTool/DlgBruteForceAES.cpp | 2 +- CrypTool/DlgChangePIN.cpp | 2 + CrypTool/DlgChangePIN.h | 2 + CrypTool/DlgCrtAstronomy.cpp | 2 +- CrypTool/DlgCrtSecretSharing.cpp | 8 +- CrypTool/DlgCrtTransformation.cpp | 6 +- CrypTool/DlgDemoRSAKeyGeneration.cpp | 3 +- CrypTool/DlgDiffieHellmanKeyInformation.cpp | 5 +- CrypTool/DlgECSignatureStepByStep.cpp | 10 +- CrypTool/DlgEntropyInfo.cpp | 2 +- CrypTool/DlgExtractSignature.cpp | 12 +- CrypTool/DlgFactorisationDemo.cpp | 2 +- CrypTool/DlgFactorisationList.cpp | 4 +- CrypTool/DlgFactorisationList.h | 4 +- CrypTool/DlgHashDemo.cpp | 6 +- CrypTool/DlgHillOptions.cpp | 4 +- CrypTool/DlgHybridDecryptionDemo.cpp | 2 +- CrypTool/DlgHybridEncryptionDemo.cpp | 4 +- CrypTool/DlgICGRandomParamater.cpp | 3 +- CrypTool/DlgKeyAsymGeneration.cpp | 4 +- CrypTool/DlgKeyPermutation.cpp | 10 +- CrypTool/DlgNGramAnalysis.cpp | 2 +- CrypTool/DlgOptionsStartoptions.cpp | 4 +- CrypTool/DlgOptionsSubstitutionAnalysis.cpp | 4 +- CrypTool/DlgPINAccess.cpp | 4 +- CrypTool/DlgPermKey.cpp | 2 +- CrypTool/DlgPrimeTest.cpp | 2 +- CrypTool/DlgPrimesGeneratorDemo.cpp | 8 +- CrypTool/DlgRSADemo.cpp | 8 +- CrypTool/DlgRSADemo.h | 2 +- CrypTool/DlgRSAEncryption.cpp | 2 +- CrypTool/DlgRandomGenerator.cpp | 9 +- CrypTool/DlgSecretSharingIntro.cpp | 2 +- CrypTool/DlgShowParameterKeyEC.cpp | 2 +- CrypTool/DlgShowPrivateKeyEC.cpp | 2 +- CrypTool/DlgShowProgress.cpp | 6 +- CrypTool/DlgShowPublicKeyEC.cpp | 2 +- .../DlgSideChannelAttackVisualizationHE.cpp | 2 +- CrypTool/DlgSigAttModificDemo.cpp | 2 +- CrypTool/DlgSignatureAttack.cpp | 18 +- CrypTool/DlgSignatureDemo.cpp | 34 +- CrypTool/DlgSolitaire.cpp | 6 +- CrypTool/DlgStatisticsSignatureAttack.cpp | 30 +- CrypTool/DlgVerifyECSignatureStepByStep.cpp | 2 +- CrypTool/DlgVerifySignature.cpp | 4 +- CrypTool/DlgVigenereAnalysisSchroedel.cpp | 8 +- CrypTool/DlgX2ModNRandomParamater.cpp | 2 +- CrypTool/DlgX2ModNRandomParamater.h | 2 +- CrypTool/HillAnalysis.cpp | 2 +- CrypTool/IntegerArithmetic.cpp | 8 +- CrypTool/MonoSubstCracker.cpp | 2 +- CrypTool/PSEDemo.cpp | 8 +- CrypTool/PictureExLoop_wx.cpp | 27 + CrypTool/PictureEx_wx.cpp | 636 ++++++++++++++++++ CrypTool/PlayfairAnalysis.cpp | 2 +- CrypTool/PlayfairStructures.cpp | 2 +- CrypTool/PlotView.cpp | 6 +- CrypTool/ScintillaView.cpp | 6 +- CrypTool/SideChannelAttack.cpp | 6 +- CrypTool/SizingDialog.cpp | 2 +- CrypTool/StereotypedAttack.cpp | 12 +- CrypTool/StereotypedAttack.h | 12 +- CrypTool/automated_permanalysis.cpp | 2 +- CrypTool/base64error.cpp | 6 +- CrypTool/keyHillBase.cpp | 10 +- CrypTool/passwordchecker.cpp | 2 +- CrypTool/rsastereotypedmsgdlg.cpp | 2 +- CrypTool/uucodeerror.cpp | 4 +- CrypTool/zip/zunzip.h | 5 + libVolRen/ArcBall.cpp | 4 +- libVolRen/ArcBall.h | 4 +- libVolRen/EditorDialog.cpp | 2 + libVolRen/EditorDialog.h | 2 + libVolRen/Trapezeditor.cpp | 18 + libVolRen/Trapezeditor.h | 9 + 90 files changed, 966 insertions(+), 240 deletions(-) create mode 100644 CrypTool/PictureExLoop_wx.cpp create mode 100644 CrypTool/PictureEx_wx.cpp diff --git a/ChallengeResponse/ChallengeResponseDlg.cpp b/ChallengeResponse/ChallengeResponseDlg.cpp index d511e47f9..d395339d4 100644 --- a/ChallengeResponse/ChallengeResponseDlg.cpp +++ b/ChallengeResponse/ChallengeResponseDlg.cpp @@ -85,7 +85,7 @@ BEGIN_MESSAGE_MAP(CChallengeResponseDlg, CDialog) ON_BN_CLICKED(IDC_CHECK_MASK, &CChallengeResponseDlg::OnCheckMask) ON_BN_CLICKED(IDC_SCENARIO_HELP_BUTTON, &CChallengeResponseDlg::OnScenarioHelpButton) ON_BN_CLICKED(IDC_RESET_BUTTON, &CChallengeResponseDlg::OnResetButton) - ON_BN_CLICKED(IDC_HELP, &CChallengeResponseDlg::OnHelp) + ON_BN_CLICKED((UINT)(UINT_PTR)IDC_HELP, &CChallengeResponseDlg::OnHelp) ON_BN_CLICKED(IDC_EINFUEHRUNG_RADIO, &CChallengeResponseDlg::OnModeRadioButtons) ON_BN_CLICKED(IDC_PASSWORT_RADIO, &CChallengeResponseDlg::OnModeRadioButtons) ON_BN_CLICKED(IDC_EINMALPASSWOERTER, &CChallengeResponseDlg::OnModeRadioButtons) @@ -234,9 +234,9 @@ void CChallengeResponseDlg::OnMouseMove(UINT nFlags, CPoint point) if (scenario.isMouseOverObject(m_action,point)){ - ::SetClassLong(m_hWnd,GCL_HCURSOR,(LONG)hCmark); + ::SetClassLongPtr(m_hWnd,GCLP_HCURSOR,(LONG_PTR)hCmark); }else{ - ::SetClassLong(m_hWnd,GCL_HCURSOR,(LONG)hCstandard); + ::SetClassLongPtr(m_hWnd,GCLP_HCURSOR,(LONG_PTR)hCstandard); } CDialog::OnMouseMove(nFlags, point); } diff --git a/ChallengeResponse/Computer.cpp b/ChallengeResponse/Computer.cpp index 5a947629b..be14836b6 100644 --- a/ChallengeResponse/Computer.cpp +++ b/ChallengeResponse/Computer.cpp @@ -58,7 +58,7 @@ bool CComputer::isMouseOverObject(CPoint &point) } -void CComputer::setParameters(int x1, int y1, int x2, int y2, int borderWidth, CString &name, int textPosition, int dataPosition, bool isAttacker) +void CComputer::setParameters(int x1, int y1, int x2, int y2, int borderWidth, const CString &name, int textPosition, int dataPosition, bool isAttacker) { setParametersSO(x1,y1,x2,y2,borderWidth); this->name=name; @@ -95,8 +95,8 @@ void CComputer::draw(CClientDC &dc) drawDataText(dc); - dc.SelectObject(&originalFont); - dc.SelectObject(&originalPen); + dc.SelectObject(originalFont); + dc.SelectObject(originalPen); } void CComputer::drawDataText(CClientDC &dc) @@ -123,7 +123,7 @@ void CComputer::eraseDataText(CClientDC &dc) dc.SetTextColor(white); originalFont = dc.SelectObject(&dataFont); drawDataText(dc); - dc.SelectObject(&originalFont); + dc.SelectObject(originalFont); } int CComputer::getX1() diff --git a/ChallengeResponse/Computer.h b/ChallengeResponse/Computer.h index a5b82b003..9aad3c3a0 100644 --- a/ChallengeResponse/Computer.h +++ b/ChallengeResponse/Computer.h @@ -43,7 +43,7 @@ class CComputer : CScenarioObject void draw(CClientDC& dc); - void setParameters(int x1,int y1, int x2, int y2, int borderwidth, CString& name, int textposition, int dataposition, bool isAttacker); + void setParameters(int x1,int y1, int x2, int y2, int borderwidth, const CString& name, int textposition, int dataposition, bool isAttacker); bool isMouseOverObject(CPoint &point); // eigene Variablen & Methoden diff --git a/ChallengeResponse/Packet.cpp b/ChallengeResponse/Packet.cpp index f294adcb4..281501b56 100644 --- a/ChallengeResponse/Packet.cpp +++ b/ChallengeResponse/Packet.cpp @@ -59,7 +59,7 @@ void CPacket::draw(CClientDC &dc) dc.MoveTo(x1+1,y2-1); dc.LineTo(x2-1,y1+1); - dc.SelectObject(&originalPen); + dc.SelectObject(originalPen); } void CPacket::erase(CClientDC &dc) @@ -73,7 +73,7 @@ void CPacket::erase(CClientDC &dc) dc.MoveTo(x1,y2); dc.LineTo(x2,y1); - dc.SelectObject(&originalPen); + dc.SelectObject(originalPen); } diff --git a/ChallengeResponse/Wire.cpp b/ChallengeResponse/Wire.cpp index d53cc4bab..c14213776 100644 --- a/ChallengeResponse/Wire.cpp +++ b/ChallengeResponse/Wire.cpp @@ -137,7 +137,7 @@ void CWire::draw(CClientDC& dc) } } - dc.SelectObject(&originalPen); + dc.SelectObject(originalPen); } diff --git a/CrypTool/AutomaticAnalysis.cpp b/CrypTool/AutomaticAnalysis.cpp index cb868dfb1..489b88c83 100644 --- a/CrypTool/AutomaticAnalysis.cpp +++ b/CrypTool/AutomaticAnalysis.cpp @@ -76,7 +76,7 @@ void CaesarAuto(const char *infile, const char *OldTitle) HistogramASCII(theApp.TextOptions.getReferenceFilePath(), theApp.TextOptions.getReferenceFileName()); HistogramASCII(infile, OldTitle); LoadString(AfxGetInstanceHandle(),IDS_STRING_MSG_CMP_CIPHER_REFERENCE,pc_str,STR_LAENGE_STRING_TABLE); - sprintf(line, pc_str, theApp.TextOptions.getReferenceFileName()); + sprintf(line, pc_str, (LPCTSTR)theApp.TextOptions.getReferenceFileName()); LoadString(AfxGetInstanceHandle(),IDS_STRING_ANALYSE_CAESAR,pc_str1,STR_LAENGE_STRING_TABLE); theApp.m_MainWnd->MessageBox(line, pc_str1, MB_OK); } @@ -308,7 +308,7 @@ UINT VigenereAuto(PVOID p) // Ausgabe der Korrelation zwischen deutschem Text und dem Chiffrat GetTmpName(name,"cry",".plt"); LoadString(AfxGetInstanceHandle(),IDS_STRING_MSG_CORRELATION_CAESAR_REFERENCE,pc_str,STR_LAENGE_STRING_TABLE); - sprintf(line,pc_str,i+1, theApp.TextOptions.getReferenceFileName()); + sprintf(line,pc_str,i+1, (LPCTSTR)theApp.TextOptions.getReferenceFileName()); c.Show(OStream(name)<< OStream::Title(0) << OStream::Description(0) << OStream::Summary(0)); diff --git a/CrypTool/CrypToolApp.cpp b/CrypTool/CrypToolApp.cpp index 9c2614ba3..663933ca3 100644 --- a/CrypTool/CrypToolApp.cpp +++ b/CrypTool/CrypToolApp.cpp @@ -199,14 +199,14 @@ BEGIN_MESSAGE_MAP(CCrypToolApp, CWinApp) //ON_COMMAND(ID_VERENTSCHLSSELN_HYBRIDVERFAHREN_HYBRIDVERSCHLSSELUNG, OnVerentschlsselnHybridverfahrenHybridverschlsselung) ON_COMMAND(ID_FILE_NEW, &CCrypToolApp::OnFileNew) // file commands... - ON_COMMAND(ID_FILE_OPEN, CWinApp::OnFileOpen) - ON_COMMAND(ID_FILE_PRINT_SETUP, CWinApp::OnFilePrintSetup) + ON_COMMAND(ID_FILE_OPEN, &CWinApp::OnFileOpen) + ON_COMMAND(ID_FILE_PRINT_SETUP, &CWinApp::OnFilePrintSetup) // BEGINN Fuer Hilfe-Funktionalitaet eingefuegt // Globale Hilfebefehle - ON_COMMAND(ID_HELP_FINDER, CWinApp::OnHelpFinder) - ON_COMMAND(ID_HELP, CWinApp::OnHelp) - ON_COMMAND(ID_CONTEXT_HELP, CWinApp::OnContextHelp) - ON_COMMAND(ID_DEFAULT_HELP, CWinApp::OnHelpFinder) + ON_COMMAND(ID_HELP_FINDER, &CWinApp::OnHelpFinder) + ON_COMMAND(ID_HELP, &CWinApp::OnHelp) + ON_COMMAND(ID_CONTEXT_HELP, &CWinApp::OnContextHelp) + ON_COMMAND(ID_DEFAULT_HELP, &CWinApp::OnHelpFinder) // ENDE ON_COMMAND(ID_PRIMENUMBER_TEST, &CCrypToolApp::OnPrimenumberTest) @@ -410,7 +410,7 @@ BOOL CCrypToolApp::InitInstance() char buffer[1024], *p; int n; - #if !defined(_MSC_VER) || _MSC_VER <= 1200 + #if defined(_MSC_VER) && _MSC_VER <= 1200 // HTML Help for VC++ 6.0 // ... #else @@ -1143,7 +1143,7 @@ void CCrypToolApp::OnEinzelverfahrenTutorialSignaturerzeugung() } -#if !defined(_MSC_VER) || _MSC_VER <= 1200 +#if defined(_MSC_VER) && _MSC_VER <= 1200 // HTML Help for VC++ 6.0 void CCrypToolApp::WinHelp( DWORD dwData, UINT nCmd) #else @@ -1159,8 +1159,8 @@ void CCrypToolApp::WinHelpInternal( DWORD_PTR dwData, UINT nCmd) for (it = m_menuItemStack.begin(); it != m_menuItemStack.end(); it++) { WCHAR mi[sizeof alinkid] = L""; TCHAR mo[sizeof alinkid]; - GetMenuStringW((*it).hmenu,(*it).index,mi,sizeof(mi)-1,MF_BYPOSITION); - mi[sizeof mi - 1] = '\0'; + GetMenuStringW((*it).hmenu,(*it).index,mi,sizeof(mi)/sizeof(mi[0])-1,MF_BYPOSITION); + mi[sizeof(mi)/sizeof(mi[0]) - 1] = '\0'; int i = 0, j = 0; WCHAR c; // remove from mi @@ -1200,7 +1200,7 @@ void CCrypToolApp::WinHelpInternal( DWORD_PTR dwData, UINT nCmd) link.pszWindow = NULL; link.fIndexOnFail = FALSE ; - callHtmlHelp(HH_ALINK_LOOKUP, (DWORD) &link); + callHtmlHelp(HH_ALINK_LOOKUP, (DWORD_PTR) &link); } else @@ -1319,7 +1319,7 @@ void CCrypToolApp::OnScript() //HINSTANCE hInst = ShellExecute(0, "open", readmePath, NULL, 0, SW_SHOW ); // does not work for BE - .NET 3.0 issue? HINSTANCE hInst = ShellExecute(0, NULL, readmePath, NULL, 0, SW_SHOW ); - if ( reinterpret_cast(hInst) <= 32 ) + if ( reinterpret_cast(hInst) <= 32 ) Message(IDS_ERROPEN_SCRIPT, MB_ICONSTOP); } @@ -1332,7 +1332,7 @@ void CCrypToolApp::OnPresentation() sprintf(presentationPath, pc_str, Pfad); HINSTANCE hInst = ShellExecute(0, NULL, presentationPath, NULL, 0, SW_SHOW); delete []presentationPath; - if(reinterpret_cast(hInst) <= 32) + if(reinterpret_cast(hInst) <= 32) Message(IDS_ERROR_OPENING_PRESENTATION, MB_ICONSTOP); } @@ -1416,7 +1416,7 @@ void CCrypToolApp::execAnimalVisualization(int _animalFileIdentifier) { animalPath = CString(Pfad) + animalPath; animalPath.Replace("/","\\"); HINSTANCE hInst = ShellExecute(NULL, NULL, animalExecutable, animalFile, animalPath, SW_HIDE); - if(reinterpret_cast(hInst) <= 32) { + if(reinterpret_cast(hInst) <= 32) { Message(IDS_ERROPEN_ANIM, MB_ICONSTOP); } } @@ -1467,9 +1467,9 @@ void CCrypToolApp::OnGenerationTAdicNAFKeys() { generationTAdicNAFKeysPath.Replace("/", "\\"); // try to open the executable, otherwise dump an error HINSTANCE hInst=ShellExecute(NULL, NULL, generationTAdicNAFKeysExecutable, NULL, generationTAdicNAFKeysPath, SW_HIDE); - if(reinterpret_cast(hInst) <= 32) { + if(reinterpret_cast(hInst) <= 32) { CString message; - message.Format(IDS_GENERATION_TADIC_NAF_KEYS_ERROR_EXECUTION, generationTAdicNAFKeysExecutable, generationTAdicNAFKeysPath); + message.Format(IDS_GENERATION_TADIC_NAF_KEYS_ERROR_EXECUTION, (LPCTSTR)generationTAdicNAFKeysExecutable, (LPCTSTR)generationTAdicNAFKeysPath); AfxMessageBox(message, MB_ICONSTOP); } } @@ -1478,7 +1478,7 @@ void CCrypToolApp::OnNumberShark() { // zahlenhai aufrufen CString str_m_NumberShark_Selfextract_EXE_Quoted = CString("\"") + CString(theApp.m_NumberShark_Selfextract_EXE) + CString("\""); - _spawnl(_P_NOWAIT, theApp.m_NumberShark_Selfextract_EXE, str_m_NumberShark_Selfextract_EXE_Quoted, NULL); + _spawnl(_P_NOWAIT, theApp.m_NumberShark_Selfextract_EXE, (LPCTSTR)str_m_NumberShark_Selfextract_EXE_Quoted, NULL); // FIXME: Interpret return value of _spawnl } @@ -1501,9 +1501,9 @@ BOOL CCrypToolApp::OnDDECommand(LPTSTR lpszCommand) } -void CCrypToolApp::callHtmlHelp(UINT uCommand, DWORD dwData) +void CCrypToolApp::callHtmlHelp(UINT uCommand, DWORD_PTR dwData) { -#if !defined(_MSC_VER) || _MSC_VER <= 1200 // HTML Help for VC++ 6.0 +#if defined(_MSC_VER) && _MSC_VER <= 1200 // HTML Help for VC++ 6.0 // creating the path to the help CString html_help_path = CString(m_pszHelpFilePath) + CString(">MainWindow"); @@ -1562,7 +1562,7 @@ void CCrypToolApp::OnAesSelfextract() { CString str_m_Selfextract_EXE_Quoted = CString("\"") + CString(theApp.m_Selfextract_EXE) + CString("\""); // int intptr_t = _spawnl(_P_NOWAIT, strmod, strmod, NULL, NULL); - int intptr_t = _spawnl(_P_NOWAIT, theApp.m_Selfextract_EXE, str_m_Selfextract_EXE_Quoted, NULL, NULL); + int intptr_t = _spawnl(_P_NOWAIT, theApp.m_Selfextract_EXE, (LPCTSTR)str_m_Selfextract_EXE_Quoted, NULL, NULL); // FIXME: Interpret return value of _spawnl } @@ -1591,7 +1591,7 @@ void CCrypToolApp::OnFlashAesdemo() HINSTANCE hInst = ShellExecute(NULL,NULL, AESDemoExecStr, " ", Pfad, SW_SHOW); - if ( reinterpret_cast(hInst) <= 32 ) { + if ( reinterpret_cast(hInst) <= 32 ) { // display error message stating where the desired executable is expected CString message; message.Format(IDS_ERROR_OPEN_FLASHAESDEMO, Pfad); @@ -1606,7 +1606,7 @@ void CCrypToolApp::OnFlashRijndaelInspector() HINSTANCE hInst = ShellExecute(NULL,NULL, RijndaelInspectorExecStr, " ", Pfad, SW_SHOW); - if ( reinterpret_cast(hInst) <= 32 ) { + if ( reinterpret_cast(hInst) <= 32 ) { // display error message stating where the desired executable is expected CString message; message.Format(IDS_ERROR_OPEN_RIJNDAELINSPECTOR, Pfad); @@ -1621,7 +1621,7 @@ void CCrypToolApp::OnInteractiveNumberTheory() HINSTANCE hInst = ShellExecute(NULL,NULL, InteractiveNTExecStr, " ", Pfad, SW_SHOW); - if ( reinterpret_cast(hInst) <= 32 ) { + if ( reinterpret_cast(hInst) <= 32 ) { // display error message stating where the desired executable is expected CString message; message.Format(IDS_ERROPEN_INTERACTIVE_NUMBER_THEORY, Pfad); @@ -1636,7 +1636,7 @@ void CCrypToolApp::OnFlashEnigmademo() HINSTANCE hInst = ShellExecute(NULL,NULL, EnigmaFlashExecStr, " ", Pfad, SW_SHOW); - if ( reinterpret_cast(hInst) <= 32 ) { + if ( reinterpret_cast(hInst) <= 32 ) { // display error message stating where the desired executable is expected CString message; message.Format(IDS_ERROPEN_ENIGMA_FLASH, Pfad); diff --git a/CrypTool/CrypToolApp.h b/CrypTool/CrypToolApp.h index 9ca7075c4..7560e620d 100644 --- a/CrypTool/CrypToolApp.h +++ b/CrypTool/CrypToolApp.h @@ -195,7 +195,7 @@ class CCrypToolApp : public CWinApp virtual void AddToRecentFileList(LPCTSTR lpszPathName); // WinHelp set public to get access from Challenge Response Demo - #if !defined(_MSC_VER) || _MSC_VER <= 1200 + #if defined(_MSC_VER) && _MSC_VER <= 1200 // HTML Help for VC++ 6.0 virtual void WinHelp( DWORD dwData, UINT nCmd = HELP_CONTEXT); #else // HTML Help for VC++ .NET @@ -212,7 +212,7 @@ class CCrypToolApp : public CWinApp void endLog(); private: - void callHtmlHelp(UINT uCommand, DWORD dwData); + void callHtmlHelp(UINT uCommand, DWORD_PTR dwData); virtual int ExitInstance(); void execAnimalVisualization(int _animalFileIdentifier); public: diff --git a/CrypTool/CrypToolTools.cpp b/CrypTool/CrypToolTools.cpp index 80b55cfe9..5fb22f419 100644 --- a/CrypTool/CrypToolTools.cpp +++ b/CrypTool/CrypToolTools.cpp @@ -534,7 +534,7 @@ void ShellExecuteJava(const CString &_javaProgram, const CString &_javaProgramCo struct stat javaProgramFileInformation; if(stat(javaProgram.GetBuffer(), &javaProgramFileInformation) != 0) { CString message; - message.Format(IDS_STRING_JAVA_PROGRAM_NOT_FOUND, javaProgram); + message.Format(IDS_STRING_JAVA_PROGRAM_NOT_FOUND, (LPCTSTR)javaProgram); AfxMessageBox(message, MB_ICONINFORMATION); return; } @@ -542,7 +542,7 @@ void ShellExecuteJava(const CString &_javaProgram, const CString &_javaProgramCo memset(&si, 0, sizeof(si)); memset(&pi, 0, sizeof(pi)); CString command; - command.Format("java %s", javaProgramCompleteCall); + command.Format("java %s", (LPCTSTR)javaProgramCompleteCall); if(!CreateProcess(NULL, (LPSTR)(LPCTSTR)(command), NULL, NULL, false, CREATE_NO_WINDOW, NULL, (LPCTSTR)(path), &si, &pi)) { CString message; message.LoadStringA(IDS_STRING_JAVA_PROGRAM_EXECUTION_FAILED); @@ -800,7 +800,7 @@ bool copyKeyToClipboard(const int keyTypeIdentifier, const CString &key) { CString stringKeyTypeIdentifier; stringKeyTypeIdentifier.LoadString(keyTypeIdentifier); CString text; - text.Format("%s:%s", stringKeyTypeIdentifier, key); + text.Format("%s:%s", (LPCTSTR)stringKeyTypeIdentifier, (LPCTSTR)key); return copyTextToClipboard(text); } diff --git a/CrypTool/CrypToolView.cpp b/CrypTool/CrypToolView.cpp index b3b6f6872..ce1e59920 100644 --- a/CrypTool/CrypToolView.cpp +++ b/CrypTool/CrypToolView.cpp @@ -56,7 +56,7 @@ BEGIN_MESSAGE_MAP(CCrypToolView, CView) ON_WM_CONTEXTMENU() ON_WM_SETFOCUS() #if 0 - ON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint) + ON_COMMAND(ID_FILE_PRINT, &CView::OnFilePrint) ON_COMMAND(ID_VIEW_ENDOFLINE, &CCrypToolView::OnViewEndOfLine) ON_UPDATE_COMMAND_UI(ID_VIEW_ENDOFLINE, &CCrypToolView::OnUpdateViewEndOfLine) ON_COMMAND(ID_VIEW_LINEWRAP, &CCrypToolView::OnViewLineWrap) diff --git a/CrypTool/CryptDoc.cpp b/CrypTool/CryptDoc.cpp index 4effc7d50..bbfb9d2de 100644 --- a/CrypTool/CryptDoc.cpp +++ b/CrypTool/CryptDoc.cpp @@ -48,7 +48,7 @@ #include "DlgStatRandTests.h" #include "DlgHybridEncryptionDemo.h" #include "DlgHybridDecryptionDemo.h" -#include +#include #include "DialogeMessage.h" #include "ASN1Decoder.h" #include "DlgDiffieHellmanVisualization.h" @@ -1307,7 +1307,7 @@ void CCryptDoc::OnAnalyseZufallstestsFrequencytest() int tempStringLength = strlen(pc_str) + strlen(GetTitle()) + 1; char *tempString = new char[tempStringLength]; memset(tempString, 0, tempStringLength); - sprintf(tempString, pc_str, GetTitle()); + sprintf(tempString, pc_str, (LPCTSTR)GetTitle()); FREQT.SetAlternativeWindowText(tempString); if ( FREQT.LoadAlphaList() ) FREQT.DoModal(); @@ -1341,7 +1341,7 @@ void CCryptDoc::OnAnalyseZufallstestsSerialtest() int tempStringLength = strlen(pc_str) + strlen(GetTitle()) + 1; char *tempString = new char[tempStringLength]; memset(tempString, 0, tempStringLength); - sprintf(tempString, pc_str, GetTitle()); + sprintf(tempString, pc_str, (LPCTSTR)GetTitle()); SERT.SetAlternativeWindowText(tempString); if ( SERT.LoadAlphaList() ) SERT.DoModal(); @@ -1371,7 +1371,7 @@ void CCryptDoc::OnAnalyseZufallstestsRuns() int tempStringLength = strlen(pc_str) + strlen(GetTitle()) + 1; char *tempString = new char[tempStringLength]; memset(tempString, 0, tempStringLength); - sprintf(tempString, pc_str, GetTitle()); + sprintf(tempString, pc_str, (LPCTSTR)GetTitle()); LRT.SetAlternativeWindowText( tempString ); if ( LRT.LoadAlphaList() ) @@ -1406,7 +1406,7 @@ void CCryptDoc::OnAnalyseZufallstestsPokertest() int tempStringLength = strlen(pc_str) + strlen(GetTitle()) + 1; char *tempString = new char[tempStringLength]; memset(tempString, 0, tempStringLength); - sprintf(tempString, pc_str, GetTitle()); + sprintf(tempString, pc_str, (LPCTSTR)GetTitle()); POKT.SetAlternativeWindowText(tempString); if ( POKT.LoadAlphaList() ) @@ -1448,7 +1448,7 @@ void CCryptDoc::OnAnalyseZufallstestsFipspub1401() int tempStringLength = strlen(pc_str) + strlen(GetTitle()) + 1; char *tempString = new char[tempStringLength]; memset(tempString, 0, tempStringLength); - sprintf(tempString, pc_str, GetTitle()); + sprintf(tempString, pc_str, (LPCTSTR)GetTitle()); FIPS.SetAlternativeWindowText(tempString); FIPS.DoModal(); delete tempString; @@ -1460,7 +1460,7 @@ void CCryptDoc::OnAesSelfextract() { UpdateContent(); CString str_m_Selfextract_EXE_Quoted = CString("\"") + CString(theApp.m_Selfextract_EXE) + CString("\""); - _spawnl(_P_NOWAIT, theApp.m_Selfextract_EXE, str_m_Selfextract_EXE_Quoted, ContentName, NULL); + _spawnl(_P_NOWAIT, theApp.m_Selfextract_EXE, (LPCTSTR)str_m_Selfextract_EXE_Quoted, ContentName, NULL); // FIXME: Interpret return value of _spawnl } @@ -1760,7 +1760,7 @@ void CCryptDoc::OnAnalysisRandom3dVisualization() f.Close(); LoadString(AfxGetInstanceHandle(), IDS_TITLE_3D_VISUALISATION, pc_str, STR_LAENGE_STRING_TABLE); - sprintf(title, pc_str, GetTitle()); + sprintf(title, pc_str, (LPCTSTR)GetTitle()); theApp.ThreadOpenDocumentFileNoMRU(name,title); } diff --git a/CrypTool/Cryptography.cpp b/CrypTool/Cryptography.cpp index 0c34e6331..e3782b3e3 100644 --- a/CrypTool/Cryptography.cpp +++ b/CrypTool/Cryptography.cpp @@ -2748,10 +2748,10 @@ void HomophonicSubstitutionAsc(const char *infile, const char *title) { stringTitle = title; stringKey = dlg.getKeyAsString(); if(result == IDENCRYPT) { - documentTitle.Format(IDS_STRING_ENCRYPTION_OF_USING_KEY, stringAlgorithm, stringTitle, stringKey); + documentTitle.Format(IDS_STRING_ENCRYPTION_OF_USING_KEY, (LPCTSTR)stringAlgorithm, (LPCTSTR)stringTitle, (LPCTSTR)stringKey); } if(result == IDDECRYPT) { - documentTitle.Format(IDS_STRING_DECRYPTION_OF_USING_KEY, stringAlgorithm, stringTitle, stringKey); + documentTitle.Format(IDS_STRING_DECRYPTION_OF_USING_KEY, (LPCTSTR)stringAlgorithm, (LPCTSTR)stringTitle, (LPCTSTR)stringKey); } document->SetTitle(documentTitle); } @@ -3046,11 +3046,11 @@ void Permutation(const char *infileName, const char *OldTitle, BOOL TEXTMODE) strcpy(dType , (Perm.m_DataType) ? TEXT_TOKEN : BINARY_TOKEN ); if(Perm.m_P2len) - sprintf(key,"%s;%s %s%s%s%i,%i,%i,%i,%i,%i", Perm.m_Perm1, Perm.m_Perm2, PARAM_TOKEN, dType, Invert, + sprintf(key,"%s;%s %s%s%s%i,%i,%i,%i,%i,%i", (LPCTSTR)Perm.m_Perm1, (LPCTSTR)Perm.m_Perm2, PARAM_TOKEN, dType, Invert, Perm.m_P1InSeq, Perm.m_P1Perm, Perm.m_P1OutSeq, Perm.m_P2InSeq, Perm.m_P2Perm, Perm.m_P2OutSeq); else - sprintf(key,"%s %s%s%s%i,%i,%i,%i,%i,%i", Perm.m_Perm1, PARAM_TOKEN, dType, Invert, + sprintf(key,"%s %s%s%s%i,%i,%i,%i,%i,%i", (LPCTSTR)Perm.m_Perm1, PARAM_TOKEN, dType, Invert, Perm.m_P1InSeq, Perm.m_P1Perm, Perm.m_P1OutSeq, Perm.m_P2InSeq, Perm.m_P2Perm, Perm.m_P2OutSeq); CAppDocument *NewDoc; @@ -3278,7 +3278,7 @@ void SymmetricEncryption(int AlgId, cryptProvider provider, char keybuffhex[256/4+1]; unsigned char keybuffbin[256/8]; - Title.Format(IDS_STRING_KEYINPUT_SYMMETRIC, AlgString); + Title.Format(IDS_STRING_KEYINPUT_SYMMETRIC, (LPCTSTR)AlgString); CDlgKeyHexFixedLen KeyDialog; if ( !fixed_keylength ) switch (AlgId) @@ -3437,7 +3437,7 @@ UINT SymmetricBruteForce(PVOID p) // start progress dialog CString title; - title.Format(IDS_STRING_ANALYSE_ON,AlgTitle); + title.Format(IDS_STRING_ANALYSE_ON,(LPCTSTR)AlgTitle); if(par->flags & CRYPT_DO_PROGRESS) { CString message; @@ -3794,7 +3794,7 @@ int RailFenceEncryption(const char *infile, const char *oldTitle, int key, int o if(encrypt) LoadString(AfxGetInstanceHandle(), IDS_STRING_RAIL_FENCE_ENCRYPTION_OF, pc_str, STR_LAENGE_STRING_TABLE); else LoadString(AfxGetInstanceHandle(), IDS_STRING_RAIL_FENCE_DECRYPTION_OF, pc_str, STR_LAENGE_STRING_TABLE); // now add the old title and the key into the new title - sprintf(title, pc_str, oldTitle, stringCompleteKey); + sprintf(title, pc_str, oldTitle, (LPCTSTR)stringCompleteKey); // set the new document title document->SetTitle(title); } @@ -4007,7 +4007,7 @@ int ScytaleEncryption(const char *infile, const char *oldTitle, int key, int off if(encrypt) LoadString(AfxGetInstanceHandle(), IDS_STRING_SCYTALE_ENCRYPTION_OF, pc_str, STR_LAENGE_STRING_TABLE); else LoadString(AfxGetInstanceHandle(), IDS_STRING_SCYTALE_DECRYPTION_OF, pc_str, STR_LAENGE_STRING_TABLE); // now add the old title and the key into the new title - sprintf(title, pc_str, oldTitle, stringCompleteKey); + sprintf(title, pc_str, oldTitle, (LPCTSTR)stringCompleteKey); // set the new document title document->SetTitle(title); } @@ -4026,7 +4026,8 @@ void Solitaire(const char *infile, const char *OldTitle) SHOW_HOUR_GLASS GetTmpName(outfile,"cry",".txt"); - crypt_solitaire( mySol.sol_action, infile, outfile, CString(mySol.InitialDeck) ); + CString initialDeck(mySol.InitialDeck); + crypt_solitaire( mySol.sol_action, infile, outfile, initialDeck ); // == Open the new document OpenNewDoc( outfile, mySol.InitialDeck, OldTitle, IDS_CRYPT_SOLITAIRE, ( mySol.sol_action ) ? 0 : 1 ); diff --git a/CrypTool/DlgAbout.cpp b/CrypTool/DlgAbout.cpp index 5ab49aba7..046dd180e 100644 --- a/CrypTool/DlgAbout.cpp +++ b/CrypTool/DlgAbout.cpp @@ -148,10 +148,16 @@ BOOL CDlgAbout::OnInitDialog() CString buildDate; buildDate.Format(theApp.getBuildTime()); CString buildVersion; +#if defined(_MSC_VER) buildVersion.Format("MSC %d.%02d",_MSC_VER/100,_MSC_VER%100); +#elif defined(__clang__) + buildVersion.Format("clang %d.%d.%d",__clang_major__,__clang_minor__,__clang_patchlevel__); +#elif defined(__GNUC__) + buildVersion.Format("GCC %d.%d.%d",__GNUC__,__GNUC_MINOR__,__GNUC_PATCHLEVEL__); +#endif // construct release information CString stringReleaseInformation; - stringReleaseInformation.Format(IDS_CRYPTOOL_RELEASE_INFORMATION, stringCrypToolVersion, buildSvnRevision, buildDate, buildVersion); + stringReleaseInformation.Format(IDS_CRYPTOOL_RELEASE_INFORMATION, (LPCTSTR)stringCrypToolVersion, (LPCTSTR)buildSvnRevision, (LPCTSTR)buildDate, (LPCTSTR)buildVersion); stringReleaseInformation.Append(strVersionApfloat); stringReleaseInformation.Append(", "); stringReleaseInformation.Append(strVersionCracklib); stringReleaseInformation.Append(", "); stringReleaseInformation.Append(strVersionCryptovision); stringReleaseInformation.Append(", "); @@ -237,7 +243,11 @@ void CDlgAbout::determineLibraryVersions() // GMP (dynamisch) CString StrGMPWindowText; +#ifdef __MPIR_VERSION StrGMPWindowText.Format("MPIR %s", mpir_version); +#else + StrGMPWindowText.Format("GMP %s", gmp_version); +#endif this->strVersionGMP = StrGMPWindowText; // CRACKLIB (statisch) diff --git a/CrypTool/DlgAdfgvxIntro.cpp b/CrypTool/DlgAdfgvxIntro.cpp index d0ec9871b..d62d8d3a1 100644 --- a/CrypTool/DlgAdfgvxIntro.cpp +++ b/CrypTool/DlgAdfgvxIntro.cpp @@ -61,7 +61,7 @@ void DlgAdfgvxIntro::OnBnClickedCheck1() if ( CT_OPEN_REGISTRY_SETTINGS( KEY_WRITE, IDS_REGISTRY_SETTINGS, "ADFGVX" ) == ERROR_SUCCESS ) { - CT_WRITE_REGISTRY(unsigned long(noIntro), "ShowIntro"); + CT_WRITE_REGISTRY((unsigned long)(noIntro), "ShowIntro"); CT_CLOSE_REGISTRY(); } else diff --git a/CrypTool/DlgAutomatedPermAnalysis.cpp b/CrypTool/DlgAutomatedPermAnalysis.cpp index 3c800778b..dae22cc33 100644 --- a/CrypTool/DlgAutomatedPermAnalysis.cpp +++ b/CrypTool/DlgAutomatedPermAnalysis.cpp @@ -381,7 +381,7 @@ bool copy_perm_key::copy_key(long ndx) LoadString(AfxGetInstanceHandle(),IDS_CRYPT_PERMUTATION,pc_str,STR_LAENGE_STRING_TABLE); strKey.Format("%s %s%s%i,%i,%i,%i,%i,%i", - str, PARAM_TOKEN, (m_dataType) ? TEXT_TOKEN : BINARY_TOKEN, p->dirPlain, p->dirPerm, p->dirCipher, 0, 0, 0); + (LPCTSTR)str, PARAM_TOKEN, (m_dataType) ? TEXT_TOKEN : BINARY_TOKEN, p->dirPlain, p->dirPerm, p->dirCipher, 0, 0, 0); CopyKey ( pc_str, strKey ); return true; diff --git a/CrypTool/DlgBruteForceAES.cpp b/CrypTool/DlgBruteForceAES.cpp index f79451a77..730f78166 100644 --- a/CrypTool/DlgBruteForceAES.cpp +++ b/CrypTool/DlgBruteForceAES.cpp @@ -222,7 +222,7 @@ BOOL CDlgBruteForceAES::OnInitDialog() { CDialog::OnInitDialog(); CString titel; - titel.Format(IDS_STRING_ANALYSE_ON,m_alg); + titel.Format(IDS_STRING_ANALYSE_ON,(LPCTSTR)m_alg); SetWindowText(titel); m_font.CreatePointFont(100,"Courier New"); m_text_ctl.SetFont(&m_font); diff --git a/CrypTool/DlgChangePIN.cpp b/CrypTool/DlgChangePIN.cpp index 44b2092ee..92639b82e 100644 --- a/CrypTool/DlgChangePIN.cpp +++ b/CrypTool/DlgChangePIN.cpp @@ -108,6 +108,7 @@ BEGIN_MESSAGE_MAP(CDlgChangePIN, CDialog) //}}AFX_MSG_MAP END_MESSAGE_MAP() +#ifdef _WIN32 BEGIN_DISPATCH_MAP(CDlgChangePIN, CDialog) //{{AFX_DISPATCH_MAP(CDlgChangePIN) // HINWEIS - Der Klassen-Assistent fügt hier Zuordnungsmakros ein und entfernt diese. @@ -125,6 +126,7 @@ static const IID IID_IPinAndNewPinDialog = BEGIN_INTERFACE_MAP(CDlgChangePIN, CDialog) INTERFACE_PART(CDlgChangePIN, IID_IPinAndNewPinDialog, Dispatch) END_INTERFACE_MAP() +#endif ///////////////////////////////////////////////////////////////////////////// // Behandlungsroutinen für Nachrichten CDlgChangePIN diff --git a/CrypTool/DlgChangePIN.h b/CrypTool/DlgChangePIN.h index f2a73fa9b..b72411fd1 100644 --- a/CrypTool/DlgChangePIN.h +++ b/CrypTool/DlgChangePIN.h @@ -102,8 +102,10 @@ class CDlgChangePIN : public CDialog //{{AFX_DISPATCH(CDlgChangePIN) // HINWEIS - Der Klassen-Assistent fügt hier Member-Funktionen ein und entfernt diese. //}}AFX_DISPATCH +#ifdef _WIN32 DECLARE_DISPATCH_MAP() DECLARE_INTERFACE_MAP() +#endif }; //{{AFX_INSERT_LOCATION}} diff --git a/CrypTool/DlgCrtAstronomy.cpp b/CrypTool/DlgCrtAstronomy.cpp index b0444c9fe..d7a29567f 100644 --- a/CrypTool/DlgCrtAstronomy.cpp +++ b/CrypTool/DlgCrtAstronomy.cpp @@ -217,7 +217,7 @@ bool CDlgCrtAstronomy::testeteilerfremd() CString planetall; planetall.LoadString(IDS_CRT_ASTRONOMY_PLANETALL); - ausgabe.Format(planetall,planet1,modul1,planet2,modul2); + ausgabe.Format(planetall,(LPCTSTR)planet1,(LPCTSTR)modul1,(LPCTSTR)planet2,(LPCTSTR)modul2); text=text+ausgabe; prim=false; } diff --git a/CrypTool/DlgCrtSecretSharing.cpp b/CrypTool/DlgCrtSecretSharing.cpp index 347fc81a5..b79827f7c 100644 --- a/CrypTool/DlgCrtSecretSharing.cpp +++ b/CrypTool/DlgCrtSecretSharing.cpp @@ -1135,12 +1135,12 @@ void CDlgCrtSecretSharing::OnBnClickedSecretsharingSolve() CString Ctemp3; BigToCString(remainingPossibilities, Ctemp3, 10); dum.LoadString(IDS_CRT_SECRETSHARING_REK6); - helper.Format(dum, anzahl, m_need, Ctemp3); + helper.Format(dum, anzahl, m_need, (LPCTSTR)Ctemp3); AfxMessageBox(helper, MB_ICONINFORMATION); // Log-Datei helpme.LoadString(IDS_CRT_SECRETSHARING_REK7); - helper.Format(helpme,Ctemp3); + helper.Format(helpme,(LPCTSTR)Ctemp3); m_log=m_log+helper; } @@ -1162,10 +1162,10 @@ void CDlgCrtSecretSharing::OnBnClickedSecretsharingSolve() BigToCString(s,dummy,10); HIDE_HOUR_GLASS // deaktiviert die Sanduhr helpme.LoadString(IDS_CRT_SECRETSHARING_REK8); - helping.Format(helpme,dummy); + helping.Format(helpme,(LPCTSTR)dummy); AfxMessageBox(helping, MB_ICONINFORMATION ); helping.LoadString(IDS_CRT_SECRETSHARING_SUCCESS); - helper.Format(helping, dummy); + helper.Format(helping, (LPCTSTR)dummy); m_log=m_log+helper; } diff --git a/CrypTool/DlgCrtTransformation.cpp b/CrypTool/DlgCrtTransformation.cpp index 687ed4be2..2ddb883bc 100644 --- a/CrypTool/DlgCrtTransformation.cpp +++ b/CrypTool/DlgCrtTransformation.cpp @@ -340,7 +340,7 @@ bool CDlgCrtTransformation::testeteilerfremd() CString planetall; planetall.LoadString(IDS_CRT_TRANSFORMATION_PLANETALL); - ausgabe.Format(planetall,planet1,modul1,planet2,modul2); + ausgabe.Format(planetall,(LPCTSTR)planet1,(LPCTSTR)modul1,(LPCTSTR)planet2,(LPCTSTR)modul2); text=text+ausgabe; prim=false; } @@ -455,10 +455,10 @@ void CDlgCrtTransformation::OnBnClickedCrtTransformationButtonsolve() CString anzeige; if (((a-b)<0)&&(m_radio==2)) { text.LoadString(IDS_CRT_TRANSFORMATION_NOTINRANGE2); - anzeige.Format(text,ergtext); + anzeige.Format(text,(LPCTSTR)ergtext); } else {text.LoadString(IDS_CRT_TRANSFORMATION_NOTINRANGE); - anzeige.Format(text,m_bereich,ergtext); + anzeige.Format(text,(LPCTSTR)m_bereich,(LPCTSTR)ergtext); } AfxMessageBox(anzeige, MB_ICONINFORMATION | MB_OK); } diff --git a/CrypTool/DlgDemoRSAKeyGeneration.cpp b/CrypTool/DlgDemoRSAKeyGeneration.cpp index 71dade471..33a0cc16b 100644 --- a/CrypTool/DlgDemoRSAKeyGeneration.cpp +++ b/CrypTool/DlgDemoRSAKeyGeneration.cpp @@ -241,7 +241,8 @@ void CDlgDemoRSAKeyGeneration::OnOK() } if( (m_sPrime_p != m_sPrime_p_OLD) || (m_sPrime_q != m_sPrime_q_OLD) || (m_sKeyPublic != m_sKeyPublic_OLD) ) { - m_Cert->SetName(static_cast(""), static_cast(""), static_cast("")); + CString emptyName, emptyFirstName, emptyKeyId; + m_Cert->SetName(emptyName, emptyFirstName, emptyKeyId); m_Cert->SetTime(0); m_Cert->SetPIN(static_cast("")); CDialog::OnOK(); diff --git a/CrypTool/DlgDiffieHellmanKeyInformation.cpp b/CrypTool/DlgDiffieHellmanKeyInformation.cpp index cfccae969..af6363988 100644 --- a/CrypTool/DlgDiffieHellmanKeyInformation.cpp +++ b/CrypTool/DlgDiffieHellmanKeyInformation.cpp @@ -71,8 +71,9 @@ CDlgDiffieHellmanKeyInformation::CDlgDiffieHellmanKeyInformation(std::string ake // Schlüssellängen berechnen // Die Funktion ceil() rundet die Werte auf einen "glatten" Wert, zumal Bitlängen der Form x.xxx // in der Realität nicht existieren können. - this->m_BitLengthAlice = ceil(BitLength((CString)akey.c_str(),10)); - this->m_BitLengthBob = ceil(BitLength((CString)bkey.c_str(),10)); + CString strAKey = akey.c_str(), strBKey = bkey.c_str(); + this->m_BitLengthAlice = ceil(BitLength(strAKey,10)); + this->m_BitLengthBob = ceil(BitLength(strBKey,10)); // Für a=1 oder b=1 gibt die Funktion BitLength() das falsche Ergebnis NULL zurück! if(a == 1) this->m_BitLengthAlice = 1; diff --git a/CrypTool/DlgECSignatureStepByStep.cpp b/CrypTool/DlgECSignatureStepByStep.cpp index 100fb91ed..d2770a5ec 100644 --- a/CrypTool/DlgECSignatureStepByStep.cpp +++ b/CrypTool/DlgECSignatureStepByStep.cpp @@ -323,7 +323,7 @@ int CDlgECSignatureStepByStep::UpdateDataDisplay() return -1; } LoadString(AfxGetInstanceHandle(),IDS_STRING_DOMAIN_PARAMETER_TO_USE,pc_str,STR_LAENGE_STRING_TABLE); - sprintf(pc_str1, pc_str, curveID); + sprintf(pc_str1, pc_str, (LPCTSTR)curveID); m_DataDisplay += (CString) pc_str1 + nl + nl; m_DataDisplay += ( sp2 + ((CString)"a = ") + DomParamAcString.a + nl ); m_DataDisplay += ( sp2 + ((CString)"b = ") + DomParamAcString.b + nl ); @@ -339,7 +339,7 @@ int CDlgECSignatureStepByStep::UpdateDataDisplay() // Signaturverfahren und Hashfunktion LoadString(AfxGetInstanceHandle(),IDS_STRING_SEL_SIGNATURE_HASH_FUNCTION,pc_str,STR_LAENGE_STRING_TABLE); - sprintf(pc_str1, pc_str, SigAlg, HashAlg); + sprintf(pc_str1, pc_str, (LPCTSTR)SigAlg, (LPCTSTR)HashAlg); m_DataDisplay += ( ((CString)pc_str1) + nl + nl); // Länge der zu signierenden Nachricht in Byte @@ -356,7 +356,7 @@ int CDlgECSignatureStepByStep::UpdateDataDisplay() char *fstr; // message Representative f LoadString(AfxGetInstanceHandle(),IDS_STRING_SIGNATURE_HASHING,pc_str,STR_LAENGE_STRING_TABLE); - sprintf(pc_str1, pc_str, HashAlg); + sprintf(pc_str1, pc_str, (LPCTSTR)HashAlg); m_DataDisplay += ( ((CString)pc_str1) + nl + nl); err = ln_to_string (MsgRepData.f, &fstr, base); if (err > 0) @@ -388,7 +388,7 @@ int CDlgECSignatureStepByStep::UpdateDataDisplay() { // one-time key pair LoadString(AfxGetInstanceHandle(),IDS_STRING_CREATE_RANDOM_ASYMKEY,pc_str,STR_LAENGE_STRING_TABLE); - sprintf(pc_str1, pc_str, curveID); + sprintf(pc_str1, pc_str, (LPCTSTR)curveID); m_DataDisplay += ( ((CString)pc_str1) + nl + nl); m_DataDisplay += ( sp2 + ((CString)"u = ") + DsaSignDataStrings.u + nl); m_DataDisplay += ( sp2 + ((CString)"Vx = ") + DsaSignDataStrings.Vx + nl); @@ -450,7 +450,7 @@ int CDlgECSignatureStepByStep::UpdateDataDisplay() { // one-time key pair LoadString(AfxGetInstanceHandle(),IDS_STRING_CREATE_RANDOM_ASYMKEY,pc_str,STR_LAENGE_STRING_TABLE); - sprintf(pc_str1, pc_str, curveID); + sprintf(pc_str1, pc_str, (LPCTSTR)curveID); m_DataDisplay += ( ((CString)pc_str1) + nl + nl); m_DataDisplay += ( sp2 + ((CString)"u = ") + NrSignDataStrings.u + nl); diff --git a/CrypTool/DlgEntropyInfo.cpp b/CrypTool/DlgEntropyInfo.cpp index 9c4609fff..0e09bdb51 100644 --- a/CrypTool/DlgEntropyInfo.cpp +++ b/CrypTool/DlgEntropyInfo.cpp @@ -71,7 +71,7 @@ BOOL CDlgEntropyInfo::OnInitDialog() CDialog::OnInitDialog(); CString title; - title.Format(IDS_ENTROPY_TITLE,m_oldtitle); + title.Format(IDS_ENTROPY_TITLE,(LPCTSTR)m_oldtitle); SetWindowText((LPCTSTR)title); LOGFONT logFont; diff --git a/CrypTool/DlgExtractSignature.cpp b/CrypTool/DlgExtractSignature.cpp index c9ee1852a..9f7c84d76 100644 --- a/CrypTool/DlgExtractSignature.cpp +++ b/CrypTool/DlgExtractSignature.cpp @@ -238,7 +238,7 @@ BOOL CDlgExtractSignature::OnInitDialog() // Infos über Schlüssel LoadString(AfxGetInstanceHandle(),IDS_STRING_SIGNATURE_DATE,pc_str1,STR_LAENGE_STRING_TABLE); - sprintf(pc_str, pc_str1,((CString)keyType)+kInfo,((CString)creattime)); + sprintf(pc_str, pc_str1,(LPCTSTR)(((CString)keyType)+kInfo),(LPCTSTR)((CString)creattime)); m_KeyInfo = (CString) pc_str; // Benutzter Schlüssel: } else @@ -269,7 +269,7 @@ BOOL CDlgExtractSignature::OnInitDialog() // Benutzter Signaturalgorithmus LoadString(AfxGetInstanceHandle(),IDS_STRING_SIGNATURE_HASH_FUNCTION,pc_str1,STR_LAENGE_STRING_TABLE); - sprintf(pc_str, pc_str1, SigAlg, HashAlg); + sprintf(pc_str, pc_str1, (LPCTSTR)SigAlg, (LPCTSTR)HashAlg); m_SigHashAlg = (CString) pc_str; UpdateData(FALSE); @@ -302,7 +302,7 @@ BOOL CDlgExtractSignature::OnInitDialog() char buffer[30]; _itoa( message.noctets, buffer, 10 ); LoadString(AfxGetInstanceHandle(),IDS_STRING_SIGNATURE_MESSAGE_LENGTH,pc_str1,STR_LAENGE_STRING_TABLE); - sprintf(pc_str, pc_str1,((CString)buffer)); + sprintf(pc_str, pc_str1,(LPCTSTR)((CString)buffer)); m_MessageLength = (CString) pc_str; UpdateData(FALSE); @@ -317,7 +317,7 @@ BOOL CDlgExtractSignature::OnInitDialog() char buffer[30]; _itoa( signlength, buffer, 10 ); LoadString(AfxGetInstanceHandle(),IDS_STRING_SIGNATURE_LENGTH,pc_str1,STR_LAENGE_STRING_TABLE); - sprintf(pc_str, pc_str1,((CString)buffer)); + sprintf(pc_str, pc_str1,(LPCTSTR)((CString)buffer)); m_SignatureLength = (CString) pc_str; UpdateData(FALSE); @@ -348,7 +348,7 @@ BOOL CDlgExtractSignature::OnInitDialog() char buffer[30]; _itoa( Signatur.signature.nbits, buffer, 10 ); LoadString(AfxGetInstanceHandle(),IDS_STRING_SIGNATURE_LENGTH,pc_str1,STR_LAENGE_STRING_TABLE); - sprintf(pc_str, pc_str1,((CString)buffer)); + sprintf(pc_str, pc_str1,(LPCTSTR)((CString)buffer)); m_SignatureLength = (CString) pc_str; UpdateData(FALSE); } @@ -522,7 +522,7 @@ int CDlgExtractSignature::UpdateSigEditBox() char buffer[30]; _itoa( Signatur.signature.nbits, buffer, 10 ); LoadString(AfxGetInstanceHandle(),IDS_STRING_SIGNATURE_LENGTH,pc_str1,STR_LAENGE_STRING_TABLE); - sprintf(pc_str, pc_str1,((CString)buffer)); + sprintf(pc_str, pc_str1,(LPCTSTR)((CString)buffer)); m_SignatureLength = (CString) pc_str; UpdateData(FALSE); free(sigdata); diff --git a/CrypTool/DlgFactorisationDemo.cpp b/CrypTool/DlgFactorisationDemo.cpp index 26beed0c2..28a5d44ac 100644 --- a/CrypTool/DlgFactorisationDemo.cpp +++ b/CrypTool/DlgFactorisationDemo.cpp @@ -433,7 +433,7 @@ void CDlgFactorisationDemo::OnButtonFactorisation() int nfactors = 0; for (NumFactor *factor = factorList; factor ; factor = factor->next) nfactors += factor->exponent; - m_benoetigte_zeit_global.Format(IDS_FACTORS_FOUND, nfactors, timeStr1); + m_benoetigte_zeit_global.Format(IDS_FACTORS_FOUND, nfactors, (LPCTSTR)timeStr1); if ( zeit_condtruct2.day >= 1) { LoadString(AfxGetInstanceHandle(),IDS_STRING_FMT_DAYS,pc_str,STR_LAENGE_STRING_TABLE); diff --git a/CrypTool/DlgFactorisationList.cpp b/CrypTool/DlgFactorisationList.cpp index 2d44f9206..dcdbd7205 100644 --- a/CrypTool/DlgFactorisationList.cpp +++ b/CrypTool/DlgFactorisationList.cpp @@ -125,8 +125,8 @@ void CDlgFactorisationList::Init_ListBox() UpdateData(FALSE); } -void CDlgFactorisationList::InsertFactDetail(CString &Num, CString &Factor1, - CString &Factor2, CString &Method , CString &Time, +void CDlgFactorisationList::InsertFactDetail(const CString &Num, const CString &Factor1, + const CString &Factor2, const CString &Method , const CString &Time, int PrimeMask, int BitlengthF1, int BitlengthF2) { struct DFItem *Last = new (struct DFItem); diff --git a/CrypTool/DlgFactorisationList.h b/CrypTool/DlgFactorisationList.h index c44b229c1..17489b39d 100644 --- a/CrypTool/DlgFactorisationList.h +++ b/CrypTool/DlgFactorisationList.h @@ -56,8 +56,8 @@ class CDlgFactorisationList : public CDialog char outfile[CRYPTOOL_PATH_LENGTH]; bool b_SaveFactorList; void ClearFactDetail(); - void InsertFactDetail( CString & Num, CString &Factor1, - CString &Factor2, CString &Method, CString &Time, int PrimeMask, + void InsertFactDetail( const CString & Num, const CString &Factor1, + const CString &Factor2, const CString &Method, const CString &Time, int PrimeMask, int BitlengthF1, int BitlengthF2); void Init_ListBox(); CDlgFactorisationList(CWnd* pParent = NULL); // Standardkonstruktor diff --git a/CrypTool/DlgHashDemo.cpp b/CrypTool/DlgHashDemo.cpp index 6701e98d9..92a3680b4 100644 --- a/CrypTool/DlgHashDemo.cpp +++ b/CrypTool/DlgHashDemo.cpp @@ -290,7 +290,7 @@ void CDlgHashDemo::SetHashDiff(OctetString &hash1, OctetString &hash2) } -static DWORD CALLBACK EditStreamCallBack(DWORD dwCookie, LPBYTE pbBuff, LONG cb, +static DWORD CALLBACK EditStreamCallBack(DWORD_PTR dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb) { CString *pstr = (CString *)dwCookie; @@ -393,7 +393,7 @@ void CDlgHashDemo::SetRed() b += "\n\\par "; CString ratio; CString percentage = createStringNumberWithDigitGrouping((100.0*one)/(one+zero)); - ratio.Format(IDS_HASH_DEMO_PERCENT, percentage, one, (one+zero)); + ratio.Format(IDS_HASH_DEMO_PERCENT, (LPCTSTR)percentage, one, (one+zero)); b += ratio; CString sequence; @@ -409,7 +409,7 @@ void CDlgHashDemo::SetRed() // The rtfString contains the word Bold in bold font. CString rtfString = rtfPrefix + b + rtfPostfix; - EDITSTREAM es = {(DWORD)&rtfString, 0, EditStreamCallBack}; + EDITSTREAM es = {(DWORD_PTR)&rtfString, 0, EditStreamCallBack}; // richEd is the rich edit control m_ctrlHashDiff.StreamIn(SF_RTF | SFF_SELECTION, es); diff --git a/CrypTool/DlgHillOptions.cpp b/CrypTool/DlgHillOptions.cpp index ff28fdcad..7aacfbc54 100644 --- a/CrypTool/DlgHillOptions.cpp +++ b/CrypTool/DlgHillOptions.cpp @@ -89,8 +89,8 @@ void DlgHillOptions::writeRegistry() { if ( CT_OPEN_REGISTRY_SETTINGS( KEY_WRITE, IDS_REGISTRY_SETTINGS, "Hill" ) == ERROR_SUCCESS ) { - CT_WRITE_REGISTRY(unsigned long(m_offset), "OrdChrOffset"); - CT_WRITE_REGISTRY(unsigned long(useFirstCharFromAlph), "PaddingDefaultChr"); + CT_WRITE_REGISTRY((unsigned long)(m_offset), "OrdChrOffset"); + CT_WRITE_REGISTRY((unsigned long)(useFirstCharFromAlph), "PaddingDefaultChr"); CT_WRITE_REGISTRY(m_ownCharForPadding,"PaddingOwnChr"); CT_CLOSE_REGISTRY(); } diff --git a/CrypTool/DlgHybridDecryptionDemo.cpp b/CrypTool/DlgHybridDecryptionDemo.cpp index adf6e70c6..f8d9718dc 100644 --- a/CrypTool/DlgHybridDecryptionDemo.cpp +++ b/CrypTool/DlgHybridDecryptionDemo.cpp @@ -25,7 +25,7 @@ #include "CrypToolApp.h" #include #include "FileTools.h" -#include +#include #include "Cryptography.h" #include "CryptDoc.h" #include "AppDocument.h" diff --git a/CrypTool/DlgHybridEncryptionDemo.cpp b/CrypTool/DlgHybridEncryptionDemo.cpp index 440134c74..6f9ca770f 100644 --- a/CrypTool/DlgHybridEncryptionDemo.cpp +++ b/CrypTool/DlgHybridEncryptionDemo.cpp @@ -28,7 +28,7 @@ #include "DialogeMessage.h" #include #include "FileTools.h" -#include +#include #include "DlgRSAEncryption.h" #include "DlgKeyHexAnalysis.h" #include "Cryptography.h" @@ -370,7 +370,7 @@ void CDlgHybridEncryptionDemo::OnButtonEncDocumentSym() LoadString(AfxGetInstanceHandle(),IDS_SCA_KEYWORDPROBLEM,pc_str,STR_LAENGE_STRING_TABLE); char temp[STR_LAENGE_STRING_TABLE+1]; memset(temp, 0, STR_LAENGE_STRING_TABLE+1); - sprintf(temp, pc_str, keyword); + sprintf(temp, pc_str, (LPCTSTR)keyword); if(AfxMessageBox(temp, MB_YESNO) == IDYES) { diff --git a/CrypTool/DlgICGRandomParamater.cpp b/CrypTool/DlgICGRandomParamater.cpp index 060413a19..3e601b5fb 100644 --- a/CrypTool/DlgICGRandomParamater.cpp +++ b/CrypTool/DlgICGRandomParamater.cpp @@ -167,7 +167,8 @@ void CDlgICGRandomParamater::OnOK() if ( Modul_N_IsOK && Multiplikator_a_IsOK && Inkrement_b_IsOK) { GeneratePrimes P; - P.SetP(Get_N()); + CString strN = Get_N(); + P.SetP(strN); BOOL test=FALSE; test = P.MillerRabinTest(100); test = P.SolvayStrassenTest(100); diff --git a/CrypTool/DlgKeyAsymGeneration.cpp b/CrypTool/DlgKeyAsymGeneration.cpp index 33c43d201..c11f5cd5d 100644 --- a/CrypTool/DlgKeyAsymGeneration.cpp +++ b/CrypTool/DlgKeyAsymGeneration.cpp @@ -440,7 +440,7 @@ void CDlgKeyAsymGeneration::UpdateEcListBox(EcDomParam_ac_ptr curveParameter, Ec { UpdateData(TRUE); LoadString(AfxGetInstanceHandle(),IDS_STRING_EC_DOMAIN_PARAMETER,pc_str,STR_LAENGE_STRING_TABLE); - sprintf(pc_str1, pc_str, curveID); + sprintf(pc_str1, pc_str, (LPCTSTR)curveID); m_ec_dom_par_description = (CString) pc_str1; UpdateData(FALSE); @@ -1074,7 +1074,7 @@ void CDlgKeyAsymGeneration::CreateAsymKeys() // user-information: key pair succesfully created. LoadString(AfxGetInstanceHandle(),IDS_STRING_ASYMKEY_MSG_STORE_KEYPAIR,pc_str,STR_LAENGE_STRING_TABLE); - sprintf(pc_str1, pc_str, UserKeyId); + sprintf(pc_str1, pc_str, (LPCTSTR)UserKeyId); LoadString(AfxGetInstanceHandle(),IDS_STRING_MSG_KEY_GENERATION_TIME,pc_str,STR_LAENGE_STRING_TABLE); char temp[256]; char strDuration[20]; diff --git a/CrypTool/DlgKeyPermutation.cpp b/CrypTool/DlgKeyPermutation.cpp index 46e44a381..ff7c5b8e7 100644 --- a/CrypTool/DlgKeyPermutation.cpp +++ b/CrypTool/DlgKeyPermutation.cpp @@ -490,16 +490,18 @@ void CDlgKeyPermutation::OnPasteKey() if( k < 0 ) { int t = readKeyParam( buffer ); - if ( t > 0 ) m_Perm1 = makeASCII(buffer.Left(t)); + CString part = buffer.Left(t); + if ( t > 0 ) m_Perm1 = makeASCII(part); else m_Perm1 = makeASCII(buffer); m_Perm2.Empty(); } else { - m_Perm1 = makeASCII(buffer.Left(k)); + CString part1 = buffer.Left(k); + m_Perm1 = makeASCII(part1); int t = readKeyParam( buffer ); - if ( t > 0 ) m_Perm2 = makeASCII(buffer.Mid(k+1,(t-k)-1)); - else m_Perm2 = makeASCII(buffer.Right(buffer.GetLength()-k-1)); + CString part2 = ( t > 0 ) ? buffer.Mid(k+1,(t-k)-1) : buffer.Right(buffer.GetLength()-k-1); + m_Perm2 = makeASCII(part2); } UpdateData(FALSE); OnChangeEdit1(); diff --git a/CrypTool/DlgNGramAnalysis.cpp b/CrypTool/DlgNGramAnalysis.cpp index 3e7ed6370..3aeac90a0 100644 --- a/CrypTool/DlgNGramAnalysis.cpp +++ b/CrypTool/DlgNGramAnalysis.cpp @@ -563,7 +563,7 @@ CNGram::CNGram(const unsigned long fileSize, const unsigned short _N, const unsi AlphabetSize = alphabetSize; if ( N <= 2 ) { - unsigned long Sz = unsigned long(pow((long)AlphabetSize, (int)N)); + unsigned long Sz = (unsigned long)(pow((long)AlphabetSize, (int)N)); HashTableSize = min(Sz+Sz/8, Size+Size/8); } else diff --git a/CrypTool/DlgOptionsStartoptions.cpp b/CrypTool/DlgOptionsStartoptions.cpp index d20ff5584..6ee3ac23c 100644 --- a/CrypTool/DlgOptionsStartoptions.cpp +++ b/CrypTool/DlgOptionsStartoptions.cpp @@ -97,8 +97,8 @@ void CDlgOptionsStartoptions::OnOK() UpdateData(true); if ( CT_OPEN_REGISTRY_SETTINGS( KEY_WRITE, IDS_REGISTRY_SETTINGS_OPTIONS, "StartingOptions" ) == ERROR_SUCCESS ) { - CT_WRITE_REGISTRY(unsigned long(!m_how_to_start), "NoTipps"); - CT_WRITE_REGISTRY(unsigned long(m_sample_text_file), "SampleTextFile"); + CT_WRITE_REGISTRY((unsigned long)(!m_how_to_start), "NoTipps"); + CT_WRITE_REGISTRY((unsigned long)(m_sample_text_file), "SampleTextFile"); CT_CLOSE_REGISTRY(); } diff --git a/CrypTool/DlgOptionsSubstitutionAnalysis.cpp b/CrypTool/DlgOptionsSubstitutionAnalysis.cpp index 7d5fe10dc..94cdeff1c 100644 --- a/CrypTool/DlgOptionsSubstitutionAnalysis.cpp +++ b/CrypTool/DlgOptionsSubstitutionAnalysis.cpp @@ -119,13 +119,13 @@ BOOL CDlgOptionsSubstitutionAnalysis::OnInitDialog() CFileStatus wordlistFileStatus; if(!CFile::GetStatus(m_editWordlistGerman, wordlistFileStatus)) { CString message; - message.Format(IDS_STRING_SUBSTITUTION_ANALYSIS_MISSING_WORDLIST_GERMAN, m_editWordlistGerman); + message.Format(IDS_STRING_SUBSTITUTION_ANALYSIS_MISSING_WORDLIST_GERMAN, (LPCTSTR)m_editWordlistGerman); AfxMessageBox(message, MB_ICONWARNING); } if(!CFile::GetStatus(m_editWordlistEnglish, wordlistFileStatus)) { CString message; - message.Format(IDS_STRING_SUBSTITUTION_ANALYSIS_MISSING_WORDLIST_ENGLISH, m_editWordlistEnglish); + message.Format(IDS_STRING_SUBSTITUTION_ANALYSIS_MISSING_WORDLIST_ENGLISH, (LPCTSTR)m_editWordlistEnglish); AfxMessageBox(message, MB_ICONWARNING); } diff --git a/CrypTool/DlgPINAccess.cpp b/CrypTool/DlgPINAccess.cpp index 18c95f0e0..6c4646a70 100644 --- a/CrypTool/DlgPINAccess.cpp +++ b/CrypTool/DlgPINAccess.cpp @@ -103,7 +103,7 @@ BOOL CDlgPINAccess::OnInitDialog() m_ModusInfoBox = (CString) pc_str; LoadString(AfxGetInstanceHandle(),IDS_STRING_REQ_DEL_KEYDATA,pc_str,STR_LAENGE_STRING_TABLE); - sprintf(pc_str1, pc_str, UserKeyId); + sprintf(pc_str1, pc_str, (LPCTSTR)UserKeyId); m_InfoBox = (CString) pc_str1; } else @@ -114,7 +114,7 @@ BOOL CDlgPINAccess::OnInitDialog() m_ModusInfoBox = (CString) pc_str; LoadString(AfxGetInstanceHandle(),IDS_STRING_REQUEST_FOR_PIN,pc_str,STR_LAENGE_STRING_TABLE); - sprintf(pc_str1, pc_str, UserKeyId); + sprintf(pc_str1, pc_str, (LPCTSTR)UserKeyId); m_InfoBox = (CString) pc_str1; } diff --git a/CrypTool/DlgPermKey.cpp b/CrypTool/DlgPermKey.cpp index d19b1c64c..4fc59a8eb 100644 --- a/CrypTool/DlgPermKey.cpp +++ b/CrypTool/DlgPermKey.cpp @@ -92,7 +92,7 @@ void CDlgPermKey::OnBnClickedCopyKey() LoadString(AfxGetInstanceHandle(),IDS_CRYPT_PERMUTATION,pc_str,STR_LAENGE_STRING_TABLE); CString strKey; strKey.Format("%s %s%s%i,%i,%i,%i,%i,%i", - m_permKey1, PARAM_TOKEN, (m_dataType) ? TEXT_TOKEN : BINARY_TOKEN, inDir1, permDir1, outDir1, inDir2, permDir2, outDir2); + (LPCTSTR)m_permKey1, PARAM_TOKEN, (m_dataType) ? TEXT_TOKEN : BINARY_TOKEN, inDir1, permDir1, outDir1, inDir2, permDir2, outDir2); CopyKey ( pc_str, strKey ); // TODO: Add your control notification handler code here } diff --git a/CrypTool/DlgPrimeTest.cpp b/CrypTool/DlgPrimeTest.cpp index 43cdb7434..e8b420f91 100644 --- a/CrypTool/DlgPrimeTest.cpp +++ b/CrypTool/DlgPrimeTest.cpp @@ -136,7 +136,7 @@ BEGIN_MESSAGE_MAP(CDlgPrimeTest, CDialog) ON_BN_CLICKED(IDC_PRIMETEST_RADIO_SOLOVAY, &CDlgPrimeTest::OnBnClickedPrimetestRadio) ON_BN_CLICKED(IDC_PRIMETEST_RADIO_MILLERRABIN, &CDlgPrimeTest::OnBnClickedPrimetestRadio) ON_BN_CLICKED(IDC_PRIMETEST_RADIO_AKS, &CDlgPrimeTest::OnBnClickedPrimetestRadio) - ON_BN_CLICKED(IDC_BUTTON_JUMP_TO_FACTORIZATION, CDlgPrimeTest::OnBnClickedButtonJumpToFactorization) + ON_BN_CLICKED(IDC_BUTTON_JUMP_TO_FACTORIZATION, &CDlgPrimeTest::OnBnClickedButtonJumpToFactorization) END_MESSAGE_MAP() diff --git a/CrypTool/DlgPrimesGeneratorDemo.cpp b/CrypTool/DlgPrimesGeneratorDemo.cpp index 99b4e87eb..a218c5e07 100644 --- a/CrypTool/DlgPrimesGeneratorDemo.cpp +++ b/CrypTool/DlgPrimesGeneratorDemo.cpp @@ -365,7 +365,7 @@ void CDlgPrimesGeneratorDemo::OnButtonGenerate() // display value range in progress dialog char temp[1024]; LoadString(AfxGetInstanceHandle(), IDS_STRING_MULTIPLE_PRIME_NUMBERS_GENERATION_TEXT, pc_str, STR_LAENGE_STRING_TABLE); - sprintf(temp, pc_str, m_edit1, m_edit2); + sprintf(temp, pc_str, (LPCTSTR)m_edit1, (LPCTSTR)m_edit2); theApp.fs.setFormat(temp); // start the prime number generation thread AfxBeginThread(singleThreadGenerateMultiplePrimeNumbers, PVOID(this)); @@ -408,7 +408,7 @@ void CDlgPrimesGeneratorDemo::OnButtonGenerate() // either way, reset the abortion flag (otherwise we'll get weird errors) generationMultiplePrimeNumbersAborted = false; // build the notification message - sprintf(temp, pc_str, m_edit1, m_edit2, mapGeneratedPrimeNumbers.size()); + sprintf(temp, pc_str, (LPCTSTR)m_edit1, (LPCTSTR)m_edit2, mapGeneratedPrimeNumbers.size()); message.Append(temp); MessageBox(message, "CrypTool", MB_ICONINFORMATION); // write the separator to the registry @@ -447,11 +447,11 @@ void CDlgPrimesGeneratorDemo::OnButtonGenerate() char temp[1024]; if(m_radio4 == 0) { LoadString(AfxGetInstanceHandle(), IDS_STRING_PRIME_NUMBERS_GENERATION_TEXT2, pc_str, STR_LAENGE_STRING_TABLE); - sprintf(temp, pc_str, m_edit1, m_edit2, m_edit3, m_edit4); + sprintf(temp, pc_str, (LPCTSTR)m_edit1, (LPCTSTR)m_edit2, (LPCTSTR)m_edit3, (LPCTSTR)m_edit4); } else { LoadString(AfxGetInstanceHandle(), IDS_STRING_PRIME_NUMBERS_GENERATION_TEXT1, pc_str, STR_LAENGE_STRING_TABLE); - sprintf(temp, pc_str, m_edit1, m_edit2); + sprintf(temp, pc_str, (LPCTSTR)m_edit1, (LPCTSTR)m_edit2); } theApp.fs.setFormat(temp); // start the prime number generation thread diff --git a/CrypTool/DlgRSADemo.cpp b/CrypTool/DlgRSADemo.cpp index 36ca6b992..db9be553d 100644 --- a/CrypTool/DlgRSADemo.cpp +++ b/CrypTool/DlgRSADemo.cpp @@ -604,7 +604,7 @@ void CDlgRSADemo::InitPrivateRSAParameter() // an invalid parameter e, we present two options to the user: either going // with e=2^16+1 (the default) or computing e with regards to p and q CString message; - message.Format(IDS_STRING_RSATUT_WRONG_PUBLICKEY, m_edit_e, m_edit_phi_of_N); + message.Format(IDS_STRING_RSATUT_WRONG_PUBLICKEY, (LPCTSTR)m_edit_e, (LPCTSTR)m_edit_phi_of_N); if(AfxMessageBox(message, MB_ICONINFORMATION | MB_YESNO) == IDYES) { if(!calculateSmallestE(m_edit_e, m_edit_p, m_edit_q)) { CString message; @@ -1194,14 +1194,14 @@ void CDlgRSADemo::SetHeadLine(CString &mHeader, int IDS_STRING_ID, int base, int mHeader = line; } -void CDlgRSADemo::SetHeadLine(CString &mHeader, int IDS_STRING_ID, CString &Str, const char *str2) +void CDlgRSADemo::SetHeadLine(CString &mHeader, int IDS_STRING_ID, const CString &Str, const char *str2) { char line[IDS_STRINGLENGTH]; LoadString(AfxGetInstanceHandle(),IDS_STRING_ID,pc_str,STR_LAENGE_STRING_TABLE); if ( !str2 ) - sprintf( line, pc_str, Str ); + sprintf( line, pc_str, (LPCTSTR)Str ); else - sprintf( line, pc_str, Str, str2 ); + sprintf( line, pc_str, (LPCTSTR)Str, str2 ); mHeader = line; } diff --git a/CrypTool/DlgRSADemo.h b/CrypTool/DlgRSADemo.h index 85147d62f..293e5cf78 100644 --- a/CrypTool/DlgRSADemo.h +++ b/CrypTool/DlgRSADemo.h @@ -233,7 +233,7 @@ class CDlgRSADemo : public CDialog void HeadingEncryption(BOOL decryptText); void RequestForInput( BOOL clearInput = FALSE ); void SetHeadLine( CString &mHeader, int IDS_STRING_ID, int base = 0, int BlockLength = 0 ); - void SetHeadLine( CString &mHeader, int IDS_STRING_ID, CString &Str, const char *str2 = 0); + void SetHeadLine( CString &mHeader, int IDS_STRING_ID, const CString &Str, const char *str2 = 0); int GetBase(); bool KeyStatusPublicKeyOnly() { return (s_publicKeyOnly == (m_RSAKeyStatus & s_publicKeyOnly) ); } diff --git a/CrypTool/DlgRSAEncryption.cpp b/CrypTool/DlgRSAEncryption.cpp index 05cd95248..bed8cb8f6 100644 --- a/CrypTool/DlgRSAEncryption.cpp +++ b/CrypTool/DlgRSAEncryption.cpp @@ -115,7 +115,7 @@ BOOL CDlgRSAEncryption::OnInitDialog() m_listview.InsertColumn( 5, pc_str, LVCFMT_LEFT, colWidth-20 , 5); // Interne ID-Nr. CString tmpStr; - tmpStr.Format(IDS_TITLE_DLG_RSAENCRYPTION, oldTitle); + tmpStr.Format(IDS_TITLE_DLG_RSAENCRYPTION, (LPCTSTR)oldTitle); this->SetWindowText(tmpStr); diff --git a/CrypTool/DlgRandomGenerator.cpp b/CrypTool/DlgRandomGenerator.cpp index c2ae39596..c50abbb1e 100644 --- a/CrypTool/DlgRandomGenerator.cpp +++ b/CrypTool/DlgRandomGenerator.cpp @@ -300,21 +300,24 @@ void CDlgRandomGenerator::OnSelGenParam() case 1: { if (IDOK == m_pPara->DRPXN.DoModal() ) { - m_pPara->rnd_x2modN.setModul( m_pPara->DRPXN.GetModul() ); + CString strModul = m_pPara->DRPXN.GetModul(); + m_pPara->rnd_x2modN.setModul( strModul ); } } break; case 2: { if (IDOK == m_pPara->DRP_LCG.DoModal() ) { - m_pPara->DLCG.SetParameter(m_pPara->DRP_LCG.Get_a(), m_pPara->DRP_LCG.Get_b(), m_pPara->DRP_LCG.Get_N()); + CString strA = m_pPara->DRP_LCG.Get_a(), strB = m_pPara->DRP_LCG.Get_b(), strN = m_pPara->DRP_LCG.Get_N(); + m_pPara->DLCG.SetParameter(strA, strB, strN); } } break; case 3: { if (IDOK == m_pPara->DRP_ICG.DoModal() ) { - m_pPara->DICG.SetParameter(m_pPara->DRP_ICG.Get_a(), m_pPara->DRP_ICG.Get_b(), m_pPara->DRP_ICG.Get_N()); + CString strA = m_pPara->DRP_ICG.Get_a(), strB = m_pPara->DRP_ICG.Get_b(), strN = m_pPara->DRP_ICG.Get_N(); + m_pPara->DICG.SetParameter(strA, strB, strN); } break; diff --git a/CrypTool/DlgSecretSharingIntro.cpp b/CrypTool/DlgSecretSharingIntro.cpp index af44f30f3..433a61f2a 100644 --- a/CrypTool/DlgSecretSharingIntro.cpp +++ b/CrypTool/DlgSecretSharingIntro.cpp @@ -68,7 +68,7 @@ void CDlgSecretSharingIntro::OnOk() if ( CT_OPEN_REGISTRY_SETTINGS( KEY_WRITE, IDS_REGISTRY_SETTINGS, "SecretSharing" ) == ERROR_SUCCESS ) { - CT_WRITE_REGISTRY(unsigned long(!m_hide_intro), "ShowIntroShamir"); + CT_WRITE_REGISTRY((unsigned long)(!m_hide_intro), "ShowIntroShamir"); CT_CLOSE_REGISTRY(); } else diff --git a/CrypTool/DlgShowParameterKeyEC.cpp b/CrypTool/DlgShowParameterKeyEC.cpp index 46782a0e5..15d6aac87 100644 --- a/CrypTool/DlgShowParameterKeyEC.cpp +++ b/CrypTool/DlgShowParameterKeyEC.cpp @@ -97,7 +97,7 @@ BOOL CDlgShowParameterKeyEC::OnInitDialog() // m_dom_param_listview aufbauen UpdateData(TRUE); LoadString(AfxGetInstanceHandle(),IDS_STRING_EC_DOMAIN_PARAMETER,pc_str,STR_LAENGE_STRING_TABLE); - sprintf(pc_str1, pc_str, curveID); + sprintf(pc_str1, pc_str, (LPCTSTR)curveID); m_ec_dom_par_editbox = (CString) pc_str1; UpdateData(FALSE); diff --git a/CrypTool/DlgShowPrivateKeyEC.cpp b/CrypTool/DlgShowPrivateKeyEC.cpp index 8390eac81..bcfd506cd 100644 --- a/CrypTool/DlgShowPrivateKeyEC.cpp +++ b/CrypTool/DlgShowPrivateKeyEC.cpp @@ -257,7 +257,7 @@ BOOL CDlgShowPrivateKeyEC::OnInitDialog() // m_dom_param_listview aufbauen UpdateData(TRUE); LoadString(AfxGetInstanceHandle(),IDS_STRING_EC_DOMAIN_PARAMETER,pc_str,STR_LAENGE_STRING_TABLE); - sprintf(pc_str1, pc_str, curveID); + sprintf(pc_str1, pc_str, (LPCTSTR)curveID); m_ec_dom_par_editbox = (CString) pc_str1; UpdateData(FALSE); LoadString(AfxGetInstanceHandle(),IDS_STRING_PARAMETER,pc_str,STR_LAENGE_STRING_TABLE); diff --git a/CrypTool/DlgShowProgress.cpp b/CrypTool/DlgShowProgress.cpp index 44940e3ad..ad0734150 100644 --- a/CrypTool/DlgShowProgress.cpp +++ b/CrypTool/DlgShowProgress.cpp @@ -218,13 +218,13 @@ CString CDlgShowProgress::duration(double progress) const d.Format("%02d h", s/3600); } else if (rest < 3600.0*24.0*365.0) { - d.Format("%.1f %s",rest/(3600.0*24.0),m_days); + d.Format("%.1f %s",rest/(3600.0*24.0),(LPCTSTR)m_days); // flomar, 05/04/2010 // we want language-dependent separators (i.e. "," in DE, "." in EN); therefore we replace // all periods (".", default!) with the appropriate separator (IDS_FLOAT_NUMBER_SEPARATOR) d.Replace('.', m_floatNumberSeparator[0]); } else { - d.Format("%.2g %s",rest/(3600.0*24.0*365.0),m_years); + d.Format("%.2g %s",rest/(3600.0*24.0*365.0),(LPCTSTR)m_years); // flomar, 05/04/2010 // we want language-dependent separators (i.e. "," in DE, "." in EN); therefore we replace // all periods (".", default!) with the appropriate separator (IDS_FLOAT_NUMBER_SEPARATOR) @@ -249,7 +249,7 @@ void CDlgShowProgress::OnTimer(UINT nIDEvent) CString text; double progress = m_model->getProgress(); int percent = (int)(100.0*progress+0.5); - text.Format(m_format,percent,duration(progress)); + text.Format(m_format,percent,(LPCTSTR)duration(progress)); m_text_ctl.SetWindowText(text); m_Progress.SetPos(percent); } else { diff --git a/CrypTool/DlgShowPublicKeyEC.cpp b/CrypTool/DlgShowPublicKeyEC.cpp index 330ca4efc..888960ac4 100644 --- a/CrypTool/DlgShowPublicKeyEC.cpp +++ b/CrypTool/DlgShowPublicKeyEC.cpp @@ -251,7 +251,7 @@ BOOL CDlgShowPublicKeyEC::OnInitDialog() // m_dom_param_listview aufbauen UpdateData(TRUE); LoadString(AfxGetInstanceHandle(),IDS_STRING_EC_DOMAIN_PARAMETER,pc_str,STR_LAENGE_STRING_TABLE); - sprintf(pc_str1, pc_str, curveID); + sprintf(pc_str1, pc_str, (LPCTSTR)curveID); m_ec_dom_par_editbox = (CString) pc_str1; UpdateData(FALSE); LoadString(AfxGetInstanceHandle(),IDS_STRING_PARAMETER,pc_str,STR_LAENGE_STRING_TABLE); diff --git a/CrypTool/DlgSideChannelAttackVisualizationHE.cpp b/CrypTool/DlgSideChannelAttackVisualizationHE.cpp index e8723e89a..6436df5f8 100644 --- a/CrypTool/DlgSideChannelAttackVisualizationHE.cpp +++ b/CrypTool/DlgSideChannelAttackVisualizationHE.cpp @@ -1238,7 +1238,7 @@ void CDlgSideChannelAttackVisualizationHE::OnCheckDisablehelp() if ( CT_OPEN_REGISTRY_SETTINGS( KEY_WRITE, IDS_REGISTRY_SETTINGS ) == ERROR_SUCCESS ) { - CT_WRITE_REGISTRY(unsigned long(this->m_bShowInfoDialogues), "SCA_InfoDialogues"); + CT_WRITE_REGISTRY((unsigned long)(this->m_bShowInfoDialogues), "SCA_InfoDialogues"); CT_CLOSE_REGISTRY(); } else diff --git a/CrypTool/DlgSigAttModificDemo.cpp b/CrypTool/DlgSigAttModificDemo.cpp index 92c125192..17599ca76 100644 --- a/CrypTool/DlgSigAttModificDemo.cpp +++ b/CrypTool/DlgSigAttModificDemo.cpp @@ -210,7 +210,7 @@ void CDlgSigAttModificDemo::OnModify() NewDocument = theApp.OpenDocumentFileNoMRU(outfile); char title[1024]; - _snprintf(title, sizeof(title) - 1, "Modifikation von <%s>", m_Title); + _snprintf(title, sizeof(title) - 1, "Modifikation von <%s>", (LPCTSTR)m_Title); NewDocument->SetTitle(title); delete m_Document; diff --git a/CrypTool/DlgSignatureAttack.cpp b/CrypTool/DlgSignatureAttack.cpp index 05dbe7b76..82eb75eeb 100644 --- a/CrypTool/DlgSignatureAttack.cpp +++ b/CrypTool/DlgSignatureAttack.cpp @@ -258,9 +258,9 @@ void CDlgSignatureAttack::batchAttack() ifstr_Harmless.open(m_file_harmless, ios::in | ios::binary); ifstr_Dangerous.open(m_file_dangerous, ios::in | ios::binary); - _snprintf(FilePath, sizeof(FilePath) - 1, "%s\\%d%s", LogPath, StartTime, _SIG_ATT_HARMLESS); + _snprintf(FilePath, sizeof(FilePath) - 1, "%s\\%d%s", (LPCTSTR)LogPath, StartTime, _SIG_ATT_HARMLESS); ofstr_Harmless.open(FilePath, ios::out | ios::binary); - _snprintf(FilePath, sizeof(FilePath) - 1, "%s\\%d%s", LogPath, StartTime, _SIG_ATT_DANGEROUS); + _snprintf(FilePath, sizeof(FilePath) - 1, "%s\\%d%s", (LPCTSTR)LogPath, StartTime, _SIG_ATT_DANGEROUS); ofstr_Dangerous.open(FilePath, ios::out | ios::binary); if (!ifstr_Harmless || !ifstr_Dangerous || !ofstr_Harmless || !ofstr_Dangerous) { @@ -299,7 +299,7 @@ void CDlgSignatureAttack::batchAttack() delete []HarmlessText; delete []DangerousText; - _snprintf(FilePath, sizeof(FilePath) - 1, "%s\\%d%s", LogPath, StartTime, _SIG_ATT_HEADER); + _snprintf(FilePath, sizeof(FilePath) - 1, "%s\\%d%s", (LPCTSTR)LogPath, StartTime, _SIG_ATT_HEADER); SigAttTest = fopen(FilePath, "w+"); if (NULL == SigAttTest) { @@ -318,7 +318,7 @@ void CDlgSignatureAttack::batchAttack() "LogPath=%s\\\n", l_SignificantBitLengthMIN , l_SignificantBitLengthMAX, l_SignificantBitLengthJump, l_HashAlgorithmIDMIN, l_HashAlgorithmIDMAX, l_Attempts, - m_file_harmless, m_file_dangerous, LogPath); + (LPCTSTR)m_file_harmless, (LPCTSTR)m_file_dangerous, (LPCTSTR)LogPath); unsigned long u_SignatureAttackModificationMethod = 0; CT_OPEN_REGISTRY_SETTINGS( KEY_ALL_ACCESS, IDS_REGISTRY_SETTINGS, "SignatureAttack" ); @@ -326,8 +326,8 @@ void CDlgSignatureAttack::batchAttack() fprintf(SigAttTest, "\n\nInternal Information\n\nModificationMethod=%d\nHarmlessFile=%s\\%d%s\nHarmlessFileLength=%d\nDangerousFile=%s\\%d%s\nDangerousFileLength=%d", u_SignatureAttackModificationMethod, - LogPath, StartTime, _SIG_ATT_HARMLESS, HarmlessDocLength, - LogPath, StartTime, _SIG_ATT_DANGEROUS, DangerousDocLength); + (LPCTSTR)LogPath, StartTime, _SIG_ATT_HARMLESS, HarmlessDocLength, + (LPCTSTR)LogPath, StartTime, _SIG_ATT_DANGEROUS, DangerousDocLength); #ifdef _SIG_ATT_SPEED_INCREMENT fprintf(SigAttTest, "\nModifyOriginalDocumentBeforeRun=1"); @@ -355,7 +355,7 @@ void CDlgSignatureAttack::batchAttack() fprintf(SigAttTest, "\nComments=%s", tmpStr); fclose(SigAttTest); - _snprintf(FilePath, sizeof(FilePath) - 1, "%s\\%d%s", LogPath, StartTime, _SIG_ATT_LOG); + _snprintf(FilePath, sizeof(FilePath) - 1, "%s\\%d%s", (LPCTSTR)LogPath, StartTime, _SIG_ATT_LOG); SigAttTest = fopen(FilePath, "w+"); if (NULL == SigAttTest) { @@ -500,7 +500,7 @@ void CDlgSignatureAttack::OnCompute() hashhex[strlen - 1] = '\0'; _snprintf(doctitle, sizeof(doctitle) - 1, "%s%s, <%s>", - msg, OFSA.GetHashOp()->GetHashAlgorithmName(), hashhex); + (LPCTSTR)msg, OFSA.GetHashOp()->GetHashAlgorithmName(), hashhex); NewHarmlessDocument->SetTitle(doctitle); GetTmpName(outfile,"cry",".tmp"); @@ -519,7 +519,7 @@ void CDlgSignatureAttack::OnCompute() // Dokumenttitel für 2. Dokument erstellen: msg.Format(IDS_STRING_SIG_ATT_DOCUMENT_TITLE_DANGEROUS); _snprintf(doctitle, sizeof(doctitle) - 1, "%s%s, <%s>", - msg, OFSA.GetHashOp()->GetHashAlgorithmName(), hashhex); + (LPCTSTR)msg, OFSA.GetHashOp()->GetHashAlgorithmName(), hashhex); NewDangerousDocument->SetTitle(doctitle); HIDE_HOUR_GLASS diff --git a/CrypTool/DlgSignatureDemo.cpp b/CrypTool/DlgSignatureDemo.cpp index 7bc476f80..39e6c87a5 100644 --- a/CrypTool/DlgSignatureDemo.cpp +++ b/CrypTool/DlgSignatureDemo.cpp @@ -265,13 +265,13 @@ void CDlgSignatureDemo::OnInfoKey() m_DisplayContent.LoadString(IDS_CONTENT_KEY); sOutput.Format(IDS_BITLENGTH_MODN, m_Cert->GetBitLength()); m_DisplayInfo += sOutput; - sOutput.Format(IDS_MOD_N, sModN); + sOutput.Format(IDS_MOD_N, (LPCTSTR)sModN); m_DisplayInfo += sOutput; - sOutput.Format(IDS_PHI_N, sPhiN); + sOutput.Format(IDS_PHI_N, (LPCTSTR)sPhiN); m_DisplayInfo += sOutput; - sOutput.Format(IDS_KEY_PUBLIC, sKeyPublic); + sOutput.Format(IDS_KEY_PUBLIC, (LPCTSTR)sKeyPublic); m_DisplayInfo += sOutput; - sOutput.Format(IDS_KEY_PRIVATE, sKeyPrivate); + sOutput.Format(IDS_KEY_PRIVATE, (LPCTSTR)sKeyPrivate); m_DisplayInfo += sOutput; UpdateData(FALSE); } @@ -524,7 +524,7 @@ void CDlgSignatureDemo::OnInfoHash() UpdateData(TRUE); int srcSize = m_osHash.noctets; dataToHexDump(m_osHash.octets, m_osHash.noctets, m_DisplayInfo); /*FIXME*/ - m_DisplayContent.Format(IDS_STRING_HASH_VALUE_OF, m_Cert->GetHashAlg(), m_sFileName); + m_DisplayContent.Format(IDS_STRING_HASH_VALUE_OF, (LPCTSTR)m_Cert->GetHashAlg(), (LPCTSTR)m_sFileName); UpdateData(FALSE); } @@ -582,17 +582,17 @@ void CDlgSignatureDemo::OnInfoHashEnc() UpdateData(TRUE); m_DisplayInfo.Empty(); m_DisplayContent.LoadString(IDS_CONTENT_HASH_ENC); - Text.Format(IDS_PADDING, Padding); + Text.Format(IDS_PADDING, (LPCTSTR)Padding); m_DisplayInfo += Text; - Text.Format(IDS_DERCODE, Encoding); + Text.Format(IDS_DERCODE, (LPCTSTR)Encoding); m_DisplayInfo += Text; - Text.Format(IDS_HASHVALUE, Hash); + Text.Format(IDS_HASHVALUE, (LPCTSTR)Hash); m_DisplayInfo += Text+"\r\n"; - Text.Format(IDS_HASHDER, HashDER); + Text.Format(IDS_HASHDER, (LPCTSTR)HashDER); m_DisplayInfo += Text; Text.Format(IDS_BITLENGTH, m_osHashDER.noctets*8); m_DisplayInfo += Text+"\r\n"; - Text.Format(IDS_HASHENC, HashEnc); + Text.Format(IDS_HASHENC, (LPCTSTR)HashEnc); m_DisplayInfo += Text; Text.Format(IDS_BITLENGTH, m_osHashEnc.noctets*8); m_DisplayInfo += Text+"\r\n"; @@ -644,11 +644,11 @@ void CDlgSignatureDemo::OnInfoAlg() UpdateData(TRUE); m_DisplayInfo.Empty(); m_DisplayContent.LoadString(IDS_CONTENT_ALG); - Text.Format(IDS_NAME2, m_Cert->GetHashAlg(), ""); + Text.Format(IDS_NAME2, (LPCTSTR)m_Cert->GetHashAlg(), ""); m_DisplayInfo += Text; Text.Format(IDS_BITLENGTH, m_Cert->GetHashLength()); m_DisplayInfo += Text; - Text.Format(IDS_DERCODE, Encoding); + Text.Format(IDS_DERCODE, (LPCTSTR)Encoding); m_DisplayInfo += Text; UpdateData(FALSE); @@ -682,17 +682,17 @@ void CDlgSignatureDemo::OnOK() theApp.SecudeLib.aux_OctetString2file(&m_SignText, outfile,2); NewDoc = theApp.OpenDocumentFileNoMRU(outfile); remove(outfile); - m_sFileNameNew.Format(IDS_RSA_SIGNATURE_OF, m_Cert->GetHashAlg(), m_sFileName); + m_sFileNameNew.Format(IDS_RSA_SIGNATURE_OF, (LPCTSTR)m_Cert->GetHashAlg(), (LPCTSTR)m_sFileName); NewDoc->SetTitle(m_sFileNameNew); CString Text; CString Msg; Msg.LoadString(IDS_CONGRATULATIONS); - Text.Format(IDS_RSASGN_DOCNAME, m_sFileName); + Text.Format(IDS_RSASGN_DOCNAME, (LPCTSTR)m_sFileName); Msg += Text; - Text.Format(m_bPSEIsExtern? IDS_RSASGN_PSE_IMP: IDS_RSASGN_PSE, m_Cert->CreateUserKeyID()); + Text.Format(m_bPSEIsExtern? IDS_RSASGN_PSE_IMP: IDS_RSASGN_PSE, (LPCTSTR)m_Cert->CreateUserKeyID()); Msg += Text; - Text.Format(IDS_RSASGN_ALG, m_Cert->GetHashAlg()); + Text.Format(IDS_RSASGN_ALG, (LPCTSTR)m_Cert->GetHashAlg()); Msg += Text; AfxMessageBox(Msg, MB_OK | MB_ICONINFORMATION); } @@ -725,7 +725,7 @@ void CDlgSignatureDemo::OnInfoSign() if (!HexDumpMem(msgdata, destSize, reinterpret_cast(m_SignText.octets), srcSize, m_nCols)) return; UpdateData(TRUE); - m_DisplayContent.Format(IDS_CONTENT_SIGN, m_Cert->GetHashAlg(), m_sFileName); + m_DisplayContent.Format(IDS_CONTENT_SIGN, (LPCTSTR)m_Cert->GetHashAlg(), (LPCTSTR)m_sFileName); m_DisplayInfo = static_cast(msgdata); UpdateData(FALSE); } diff --git a/CrypTool/DlgSolitaire.cpp b/CrypTool/DlgSolitaire.cpp index 5bc3061ce..27ff1b9bb 100644 --- a/CrypTool/DlgSolitaire.cpp +++ b/CrypTool/DlgSolitaire.cpp @@ -270,7 +270,8 @@ void CDlgSolitaire::ChangeConfiguration() char str[256]; fin.read(str,255); fin.close(); - if ( deck.set_deck(CString(str)) ) + CString deckString(str); + if ( deck.set_deck(deckString) ) { InitialDeck = str; UpdateData(false); @@ -800,7 +801,8 @@ void CDlgSolitaire::OnPasteKey() LoadString(AfxGetInstanceHandle(),IDS_CRYPT_SOLITAIRE,pc_str,STR_LAENGE_STRING_TABLE); if(PasteKey(pc_str, buffer)) { - if ( deck.set_deck(CString(buffer)) ) + CString deckString(buffer); + if ( deck.set_deck(deckString) ) { InitialDeck = buffer; UpdateData(false); diff --git a/CrypTool/DlgStatisticsSignatureAttack.cpp b/CrypTool/DlgStatisticsSignatureAttack.cpp index bc6c843dc..45ded3060 100644 --- a/CrypTool/DlgStatisticsSignatureAttack.cpp +++ b/CrypTool/DlgStatisticsSignatureAttack.cpp @@ -171,15 +171,15 @@ void CDlgStatisticsSignatureAttack::PrintStatistics() HashingOperations HO(m_ResSigAtt->GetHashAlgorithmID()); msg.Format(IDS_SIGATT_STAT_HEADER, HO.GetHashAlgorithmName()); - strlen += _snprintf(doctext + strlen, sizeof(doctext) -1, "%s\n\n", msg); + strlen += _snprintf(doctext + strlen, sizeof(doctext) -1, "%s\n\n", (LPCTSTR)msg); msg.Format(IDS_SIGATT_STAT_ORIGINAL_FILE, m_ResSigAtt->getStrFilenameOriginal()); - strlen += _snprintf(doctext + strlen, sizeof(doctext) -1, "%s\n", msg); + strlen += _snprintf(doctext + strlen, sizeof(doctext) -1, "%s\n", (LPCTSTR)msg); msg.Format(IDS_SIGATT_STAT_FAKE_FILE, m_ResSigAtt->getStrFilenameFake()); - strlen += _snprintf(doctext + strlen, sizeof(doctext) -1, "%s\n", msg); + strlen += _snprintf(doctext + strlen, sizeof(doctext) -1, "%s\n", (LPCTSTR)msg); msg.Format(IDS_STRING_SIG_ATT_STA_EXPENSE); - strlen += _snprintf(doctext + strlen, sizeof(doctext) - 1, "\n%s\n\n", msg); + strlen += _snprintf(doctext + strlen, sizeof(doctext) - 1, "\n%s\n\n", (LPCTSTR)msg); msg.Format(IDS_STRING_SIG_ATT_STA_CALCTIME); @@ -187,23 +187,23 @@ void CDlgStatisticsSignatureAttack::PrintStatistics() double_fmt( ExpectedTC.GetSeconds(), strDuration, 2 ); Time.Format(IDS_STRING_SIG_ATT_STA_TIME, ExpectedTC.GetYears(), ExpectedTC.GetDays(), ExpectedTC.GetHours(), ExpectedTC.GetMinutes(), strDuration); - strlen += _snprintf(doctext + strlen, sizeof(doctext) - 1, "%s: %s\n", msg, Time); + strlen += _snprintf(doctext + strlen, sizeof(doctext) - 1, "%s: %s\n", (LPCTSTR)msg, (LPCTSTR)Time); msg.Format(IDS_STRING_SIG_ATT_STA_STEPS, itoa_fmt((__int64)m_ResSigAtt->GetExpectedSteps())); - strlen += _snprintf(doctext + strlen, sizeof(doctext) - 1, "%s\n\n\n", msg); + strlen += _snprintf(doctext + strlen, sizeof(doctext) - 1, "%s\n\n\n", (LPCTSTR)msg); msg.Format(IDS_STRING_SIG_ATT_STA_EFFEXPENSE); - strlen += _snprintf(doctext + strlen, sizeof(doctext) - 1, "%s\n\n", msg); + strlen += _snprintf(doctext + strlen, sizeof(doctext) - 1, "%s\n\n", (LPCTSTR)msg); msg.Format(IDS_STRING_SIG_ATT_STA_CALCTIME); double_fmt( EffectiveTC.GetSeconds(), strDuration, 2 ); Time.Format(IDS_STRING_SIG_ATT_STA_TIME, EffectiveTC.GetYears(), EffectiveTC.GetDays(), EffectiveTC.GetHours(), EffectiveTC.GetMinutes(), strDuration); - strlen += _snprintf(doctext + strlen, sizeof(doctext) - 1, "%s: %s\n", msg, Time); + strlen += _snprintf(doctext + strlen, sizeof(doctext) - 1, "%s: %s\n", (LPCTSTR)msg, (LPCTSTR)Time); msg.Format(IDS_STRING_SIG_ATT_STA_STEPS, itoa_fmt((__int64)m_ResSigAtt->GetTotalSteps())); - strlen += _snprintf(doctext + strlen, sizeof(doctext) - 1, "%s\n",msg); + strlen += _snprintf(doctext + strlen, sizeof(doctext) - 1, "%s\n",(LPCTSTR)msg); msg.Format(IDS_STRING_SIG_ATT_STA_PERFORMED, itoa_fmt((__int64)m_ResSigAtt->GetHashOperationsPerformed())); - strlen += _snprintf(doctext + strlen, sizeof(doctext) - 1, "%s\n\n", msg); + strlen += _snprintf(doctext + strlen, sizeof(doctext) - 1, "%s\n\n", (LPCTSTR)msg); msg.Format(IDS_STRING_SIG_ATT_STA_TITLE); - strlen += _snprintf(doctext + strlen, sizeof(doctext) - 1, "%s", msg); + strlen += _snprintf(doctext + strlen, sizeof(doctext) - 1, "%s", (LPCTSTR)msg); for (ii = 0; ii < m_ResSigAtt->GetRuns(); ii ++) { @@ -216,18 +216,18 @@ void CDlgStatisticsSignatureAttack::PrintStatistics() } msg.Format(IDS_STRING_SIG_ATT_STA_ADDEDBYTES); - strlen += _snprintf(doctext + strlen, sizeof(doctext) - 1, "\n\n\n%s\n\n", msg); + strlen += _snprintf(doctext + strlen, sizeof(doctext) - 1, "\n\n\n%s\n\n", (LPCTSTR)msg); msg.Format(IDS_STRING_SIG_ATT_STA_HARMLESS, m_ModifiedBytesHarmless); - strlen += _snprintf(doctext + strlen, sizeof(doctext) - 1, "%s\n", msg); + strlen += _snprintf(doctext + strlen, sizeof(doctext) - 1, "%s\n", (LPCTSTR)msg); msg.Format(IDS_STRING_SIG_ATT_STA_DANGEROUS, m_ModifiedBytesDangerous); - strlen += _snprintf(doctext + strlen, sizeof(doctext) - 1, "%s", msg); + strlen += _snprintf(doctext + strlen, sizeof(doctext) - 1, "%s", (LPCTSTR)msg); StatisticsFile.write(doctext, strlen); StatisticsFile.close(); Statistics = theApp.OpenDocumentFileNoMRU(outfile); msg.Format(IDS_STRING_SIG_ATT_STA_DOCTITLE); - _snprintf(doctitle, sizeof(doctitle) - 1, "%s", msg); + _snprintf(doctitle, sizeof(doctitle) - 1, "%s", (LPCTSTR)msg); Statistics->SetTitle(doctitle); HIDE_HOUR_GLASS } diff --git a/CrypTool/DlgVerifyECSignatureStepByStep.cpp b/CrypTool/DlgVerifyECSignatureStepByStep.cpp index c4c8905e5..0a33bcebf 100644 --- a/CrypTool/DlgVerifyECSignatureStepByStep.cpp +++ b/CrypTool/DlgVerifyECSignatureStepByStep.cpp @@ -715,7 +715,7 @@ int CDlgVerifyECSignatureStepByStep::UpdateDataDisplay() return -1; } LoadString(AfxGetInstanceHandle(),IDS_STRING_DOMAIN_PARAMETER_TO_USE,pc_str,STR_LAENGE_STRING_TABLE); - sprintf(pc_str1, pc_str, curveID); + sprintf(pc_str1, pc_str, (LPCTSTR)curveID); m_DataDisplay += (CString) pc_str1 + nl + nl; m_DataDisplay += ( sp2 + ((CString)"a = ") + DomParamAcString.a + nl ); m_DataDisplay += ( sp2 + ((CString)"b = ") + DomParamAcString.b + nl ); diff --git a/CrypTool/DlgVerifySignature.cpp b/CrypTool/DlgVerifySignature.cpp index 984a11634..a6ea2c6e3 100644 --- a/CrypTool/DlgVerifySignature.cpp +++ b/CrypTool/DlgVerifySignature.cpp @@ -908,7 +908,7 @@ void CDlgVerifySignature::OnButtonSearchKey() // Info: Schlüsselbezeichner signKey nicht in Liste vorhanden, // obwohl alle Schlüsseltypen durchsucht wurden LoadString(AfxGetInstanceHandle(),IDS_STRING_MSG_KEY_IDENTIFIER_NOT_FOUND,pc_str1,STR_LAENGE_STRING_TABLE); - sprintf(pc_str, pc_str1, signKey); + sprintf(pc_str, pc_str1, (LPCTSTR)signKey); LoadString(AfxGetInstanceHandle(),IDS_STRING_ERR_UNKNOWN_SIGNATURE,pc_str1,STR_LAENGE_STRING_TABLE); MessageBox(pc_str,pc_str1,MB_ICONWARNING|MB_OK); return; // no selection @@ -931,7 +931,7 @@ void CDlgVerifySignature::OnButtonSearchKey() // Info: Schlüsselbezeichner nicht in Liste vorhanden, obwohl alle Schlüsseltypen // durchsucht wurden LoadString(AfxGetInstanceHandle(),IDS_STRING_MSG_KEY_IDENTIFIER_NOT_FOUND,pc_str1,STR_LAENGE_STRING_TABLE); - sprintf(pc_str, pc_str1, signKey); + sprintf(pc_str, pc_str1, (LPCTSTR)signKey); LoadString(AfxGetInstanceHandle(),IDS_STRING_ERR_UNKNOWN_SIGNATURE,pc_str1,STR_LAENGE_STRING_TABLE); MessageBox(pc_str,pc_str1,MB_ICONWARNING|MB_OK); return; // no selection diff --git a/CrypTool/DlgVigenereAnalysisSchroedel.cpp b/CrypTool/DlgVigenereAnalysisSchroedel.cpp index b9ff9f60d..9d596393d 100644 --- a/CrypTool/DlgVigenereAnalysisSchroedel.cpp +++ b/CrypTool/DlgVigenereAnalysisSchroedel.cpp @@ -994,17 +994,17 @@ int VigenereAnalysisSchroedel::solveTrigram() { CString unitCharacters; unitCharacters.LoadStringA(IDS_STRING_VIGENERE_ANALYSIS_SCHROEDEL_UNIT_CHARACTERS); CString ciphertextLength; - ciphertextLength.Format("%s %d %s", ciphertextLengthTag, ciphertext.GetLength(), unitCharacters); + ciphertextLength.Format("%s %d %s", (LPCTSTR)ciphertextLengthTag, ciphertext.GetLength(), (LPCTSTR)unitCharacters); output(ciphertextLength, true); // dump the selected keyword language(s) CString selectedKeywordLanguagesTag; - selectedKeywordLanguagesTag.Format(IDS_STRING_VIGENERE_ANALYSIS_SCHROEDEL_SELECTED_KEYWORD_LANGUAGES, theDialog->getSelectedCiphertextLanguageAsString()); + selectedKeywordLanguagesTag.Format(IDS_STRING_VIGENERE_ANALYSIS_SCHROEDEL_SELECTED_KEYWORD_LANGUAGES, (LPCTSTR)theDialog->getSelectedCiphertextLanguageAsString()); output(selectedKeywordLanguagesTag, true); // dump the selected ciphertext language CString selectedCiphertextLanguageTag; - selectedCiphertextLanguageTag.Format(IDS_STRING_VIGENERE_ANALYSIS_SCHROEDEL_SELECTED_CIPHERTEXT_LANGUAGE, theDialog->getSelectedCiphertextLanguageAsString()); + selectedCiphertextLanguageTag.Format(IDS_STRING_VIGENERE_ANALYSIS_SCHROEDEL_SELECTED_CIPHERTEXT_LANGUAGE, (LPCTSTR)theDialog->getSelectedCiphertextLanguageAsString()); output(selectedCiphertextLanguageTag, true); output("", true); @@ -1526,7 +1526,7 @@ void CDlgVigenereAnalysisSchroedel::updateSelectedLanguages(const CString &_sele // if our language is empty, we go with the default column header (language assumption not displayed) if(selectedCiphertextLanguageAsString.IsEmpty()) columnHeaderCleartext.Format(IDS_STRING_VIGENERE_ANALYSIS_SCHROEDEL_COLUMNHEADERCLEARTEXT); // if our language is set, we add the language assumption - else columnHeaderCleartext.Format(IDS_STRING_VIGENERE_ANALYSIS_SCHROEDEL_COLUMNHEADERCLEARTEXT_WITHLANGUAGE, selectedCiphertextLanguageAsString); + else columnHeaderCleartext.Format(IDS_STRING_VIGENERE_ANALYSIS_SCHROEDEL_COLUMNHEADERCLEARTEXT_WITHLANGUAGE, (LPCTSTR)selectedCiphertextLanguageAsString); // delete old column controlListPossibleResults.DeleteColumn(1); // add new column diff --git a/CrypTool/DlgX2ModNRandomParamater.cpp b/CrypTool/DlgX2ModNRandomParamater.cpp index 3ea240af7..12ba551f8 100644 --- a/CrypTool/DlgX2ModNRandomParamater.cpp +++ b/CrypTool/DlgX2ModNRandomParamater.cpp @@ -64,7 +64,7 @@ END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // Behandlungsroutinen für Nachrichten CDlgX2ModNRandomParamater -void CDlgX2ModNRandomParamater::SetModul(CString &NStr) +void CDlgX2ModNRandomParamater::SetModul(const CString &NStr) { m_EditModul_N = NStr; } diff --git a/CrypTool/DlgX2ModNRandomParamater.h b/CrypTool/DlgX2ModNRandomParamater.h index 0d913fce8..b107adafe 100644 --- a/CrypTool/DlgX2ModNRandomParamater.h +++ b/CrypTool/DlgX2ModNRandomParamater.h @@ -35,7 +35,7 @@ class CDlgX2ModNRandomParamater : public CDialog // Konstruktion public: CString GetModul(void); - void SetModul(CString &NStr); + void SetModul(const CString &NStr); CDlgX2ModNRandomParamater(CWnd* pParent = NULL); // Standardkonstruktor // Dialogfelddaten diff --git a/CrypTool/HillAnalysis.cpp b/CrypTool/HillAnalysis.cpp index fb2d39b82..4b6a5ac41 100644 --- a/CrypTool/HillAnalysis.cpp +++ b/CrypTool/HillAnalysis.cpp @@ -67,7 +67,7 @@ int CHillAnalysis::analyze(CString &err_str) err_str.LoadString(IDS_PA_COULDNOTOPEN_CIPHERFILE); return -1; } - if ( 0 >= d_plain || 0 >= d_cipher ) + if ( 0 >= s_plain || 0 >= s_cipher ) { err_str.LoadString(IDS_STRING_ERR_INPUT_TEXT_LENGTH); return -1; diff --git a/CrypTool/IntegerArithmetic.cpp b/CrypTool/IntegerArithmetic.cpp index 34a40da7b..9c706d449 100644 --- a/CrypTool/IntegerArithmetic.cpp +++ b/CrypTool/IntegerArithmetic.cpp @@ -1607,13 +1607,13 @@ CTutorialFactorisation::~CTutorialFactorisation() } set_mip(mip); if(ww) - delete[mlf] ww; + delete[] ww; if(xx) - delete[mmm] xx; + delete[] xx; if(yy) - delete[mmm] yy; + delete[] yy; if(zz) - delete[mlf] zz; + delete[] zz; ww = xx = yy = zz = NULL; } diff --git a/CrypTool/MonoSubstCracker.cpp b/CrypTool/MonoSubstCracker.cpp index 891753f15..89d9a55aa 100644 --- a/CrypTool/MonoSubstCracker.cpp +++ b/CrypTool/MonoSubstCracker.cpp @@ -455,7 +455,7 @@ UINT AutoAnaSubst(PVOID p) AfxMessageBox(message, MB_ICONINFORMATION|MB_OK); return 0; } - fprintf(file, "%s", Dialog.getText()); + fprintf(file, "%s", (LPCTSTR)Dialog.getText()); fclose(file); // Start manual analysis if requested and possible diff --git a/CrypTool/PSEDemo.cpp b/CrypTool/PSEDemo.cpp index e2e48905a..1fffb22b3 100644 --- a/CrypTool/PSEDemo.cpp +++ b/CrypTool/PSEDemo.cpp @@ -180,7 +180,7 @@ BOOL CPSEDemo::CreatePSE() // PSE-File erzeugen und öffnen m_sUserKeyId = CreateUserKeyID(); - m_sPseName.Format(IDS_CREATE_PSE, PseVerzeichnis, m_sUserKeyId); + m_sPseName.Format(IDS_CREATE_PSE, PseVerzeichnis, (LPCTSTR)m_sUserKeyId); m_hPSE = theApp.SecudeLib.af_create(const_cast(LPCTSTR(m_sPseName)), NULL, const_cast(LPCTSTR(m_sPIN)), NULL, TRUE); if(!m_hPSE) { @@ -442,7 +442,7 @@ CString CPSEDemo::CreateUserKeyID(const CString& sName, const CString& sFirstNam FirstName.TrimRight(); KeyID.TrimLeft(); KeyID.TrimRight(); - sUserKeyId.Format(KeyID.IsEmpty()? IDS_CREATE_USER_KEY_ID1: IDS_CREATE_USER_KEY_ID2, Name, FirstName, TUTORIAL_ALG_NAME, GetBitLength(), lTime, sKeyID); + sUserKeyId.Format(KeyID.IsEmpty()? IDS_CREATE_USER_KEY_ID1: IDS_CREATE_USER_KEY_ID2, (LPCTSTR)Name, (LPCTSTR)FirstName, TUTORIAL_ALG_NAME, GetBitLength(), lTime, (LPCTSTR)sKeyID); return sUserKeyId; } @@ -455,7 +455,7 @@ CString CPSEDemo::CreateDisName(const CString& sName, const CString& sFirstName, Name.TrimRight(); FirstName.TrimLeft(); FirstName.TrimRight(); - sDisName.Format("CN=%s %s [%I64i], %s", FirstName, Name, lTime, DN_SUFFIX); + sDisName.Format("CN=%s %s [%I64i], %s", (LPCTSTR)FirstName, (LPCTSTR)Name, lTime, DN_SUFFIX); return sDisName; } @@ -678,7 +678,7 @@ BOOL CPSEDemo::AccessPSE_DLG() SetTime(DlgPSE.CreatTime); SetPIN(DlgPSE.m_PinCode); m_sUserKeyId = DlgPSE.UserKeyId; - m_sPseName.Format(IDS_CREATE_PSE, PseVerzeichnis, m_sUserKeyId); + m_sPseName.Format(IDS_CREATE_PSE, PseVerzeichnis, (LPCTSTR)m_sUserKeyId); return TestAccess() && AccessPSE(); } return FALSE; diff --git a/CrypTool/PictureExLoop_wx.cpp b/CrypTool/PictureExLoop_wx.cpp new file mode 100644 index 000000000..5b909ba3e --- /dev/null +++ b/CrypTool/PictureExLoop_wx.cpp @@ -0,0 +1,27 @@ +/************************************************************************** + + Copyright [2009] [CrypTool Team] + + This file is part of CrypTool. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +**************************************************************************/ + +// wxWidgets implementation of CPictureExLoop (PictureExLoop.cpp is the Windows implementation). + +#include "stdafx.h" +#include "PictureExLoop.h" + +#define PICTUREEX_WX_CLASS CPictureExLoop +#include "PictureEx_wx.cpp" diff --git a/CrypTool/PictureEx_wx.cpp b/CrypTool/PictureEx_wx.cpp new file mode 100644 index 000000000..9e570800b --- /dev/null +++ b/CrypTool/PictureEx_wx.cpp @@ -0,0 +1,636 @@ +/************************************************************************** + + Copyright [2009] [CrypTool Team] + + This file is part of CrypTool. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +**************************************************************************/ + +////////////////////////////////////////////////////////////////////// +// PictureEx_wx.cpp: wxWidgets implementation of CPictureEx for platforms +// without OLE (IPicture). PictureEx.cpp remains the Windows implementation. +// +// GIF frames are decoded with wxGIFDecoder, other formats with wxImage. +// The memory DCs of the Windows version are replaced by wxImage canvases +// (m_pCanvas, m_pDispCanvas); the animation is advanced by a window timer +// on the GUI thread instead of a worker thread. +// +// PictureExLoop_wx.cpp compiles this file once more for CPictureExLoop. +////////////////////////////////////////////////////////////////////// + +#ifndef PICTUREEX_WX_CLASS +#include "stdafx.h" +#include "PictureEx.h" +#define PICTUREEX_WX_CLASS CPictureEx +#endif + +#include +#include +#include +#include +#include + +namespace { + +const UINT_PTR ANIMATION_TIMER_ID = 1; + +void FillArea(wxImage &img, int x, int y, int w, int h, COLORREF clr) +{ + wxRect rect(x, y, w, h); + rect.Intersect(wxRect(0, 0, img.GetWidth(), img.GetHeight())); + if (!rect.IsEmpty()) + img.SetRGB(rect, GetRValue(clr), GetGValue(clr), GetBValue(clr)); +} + +// copies the rectangle (x, y, w, h) from src to dst (both of the same size) +void CopyArea(wxImage &dst, const wxImage &src, int x, int y, int w, int h) +{ + wxRect rect(x, y, w, h); + rect.Intersect(wxRect(0, 0, dst.GetWidth(), dst.GetHeight())); + rect.Intersect(wxRect(0, 0, src.GetWidth(), src.GetHeight())); + if (rect.IsEmpty()) + return; + for (int j = rect.y; j < rect.y + rect.height; j++) + memcpy(dst.GetData() + 3 * (j * dst.GetWidth() + rect.x), + src.GetData() + 3 * (j * src.GetWidth() + rect.x), 3 * rect.width); +} + +// draws src onto dst at (x, y); masked or transparent pixels of src leave dst unchanged +void Compose(wxImage &dst, const wxImage &src, int x, int y) +{ + const int dw = dst.GetWidth(), dh = dst.GetHeight(); + const int sw = src.GetWidth(), sh = src.GetHeight(); + const unsigned char *s = src.GetData(); + const unsigned char *alpha = src.HasAlpha() ? src.GetAlpha() : NULL; + const bool bMask = src.HasMask(); + const unsigned char mr = src.GetMaskRed(), mg = src.GetMaskGreen(), mb = src.GetMaskBlue(); + unsigned char *d = dst.GetData(); + + for (int j = 0; j < sh; j++) + { + const int dy = y + j; + if (dy < 0 || dy >= dh) + continue; + for (int i = 0; i < sw; i++) + { + const int dx = x + i; + if (dx < 0 || dx >= dw) + continue; + const unsigned char *sp = s + 3 * (j * sw + i); + unsigned char *dp = d + 3 * (dy * dw + dx); + if (bMask && sp[0] == mr && sp[1] == mg && sp[2] == mb) + continue; + const unsigned a = alpha ? alpha[j * sw + i] : 255; + for (int c = 0; c < 3; c++) + dp[c] = (unsigned char)((sp[c] * a + dp[c] * (255 - a)) / 255); + } + } +} + +UINT GifDisposal(wxAnimationDisposal disposal) +{ + switch (disposal) + { + case wxANIM_DONOTREMOVE: return 1; + case wxANIM_TOBACKGROUND: return 2; + case wxANIM_TOPREVIOUS: return 3; + default: return 0; + } +} + +} + +inline int PICTUREEX_WX_CLASS::TGIFLSDescriptor::GetPackedValue(enum LSDPackedValues Value) +{ + int nRet = (int)m_cPacked; + + switch (Value) + { + case LSD_PACKED_GLOBALCT: + nRet = nRet >> 7; + break; + + case LSD_PACKED_CRESOLUTION: + nRet = ((nRet & 0x70) >> 4) + 1; + break; + + case LSD_PACKED_SORT: + nRet = (nRet & 8) >> 3; + break; + + case LSD_PACKED_GLOBALCTSIZE: + nRet &= 7; + break; + }; + + return nRet; +} + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +PICTUREEX_WX_CLASS::PICTUREEX_WX_CLASS() +{ + m_pGIFLSDescriptor = NULL; + m_pGIFHeader = NULL; + m_pPicture = NULL; + m_pCanvas = NULL; + m_pDispCanvas = NULL; + m_pRawData = NULL; + m_hThread = NULL; + m_hBitmap = NULL; + m_hOldBitmap = NULL; + m_hMemDC = NULL; + m_hDispMemDC = NULL; + m_hDispMemBM = NULL; + m_hDispOldBM = NULL; + m_hExitEvent = NULL; + + m_bIsInitialized = FALSE; + m_bExitThread = FALSE; + m_bIsPlaying = FALSE; + m_bIsGIF = FALSE; + m_clrBackground = RGB(255,255,255); // white by default + m_nGlobalCTSize = 0; + m_nCurrOffset = 0; + m_nCurrFrame = 0; + m_nDataSize = 0; + m_PictureSize.cx = m_PictureSize.cy = 0; + SetRect(&m_PaintRect,0,0,0,0); +} + +PICTUREEX_WX_CLASS::~PICTUREEX_WX_CLASS() +{ + UnLoad(); +} + +BEGIN_MESSAGE_MAP(PICTUREEX_WX_CLASS, CStatic) + ON_WM_DESTROY() + ON_WM_PAINT() + ON_WM_TIMER() +END_MESSAGE_MAP() + +BOOL PICTUREEX_WX_CLASS::Load(HGLOBAL hGlobal, DWORD dwSize) +{ + UnLoad(); + + if (!(m_pRawData = reinterpret_cast (GlobalLock(hGlobal))) ) + { + TRACE(_T("Load: Error locking memory\n")); + return FALSE; + }; + + m_nDataSize = dwSize; + m_pGIFHeader = reinterpret_cast (m_pRawData); + wxMemoryInputStream stream(m_pRawData, dwSize); + + if (dwSize < sizeof(TGIFHeader) + sizeof(TGIFLSDescriptor) || + memcmp(&m_pGIFHeader->m_cSignature,"GIF",3) != 0) + { + // it's not a GIF: let wxImage decode it + if (!wxImage::FindHandler(wxBITMAP_TYPE_JPEG)) + wxInitAllImageHandlers(); + + wxImage image; + bool bLoaded = image.LoadFile(stream, wxBITMAP_TYPE_ANY); + m_pRawData = NULL; + m_pGIFHeader = NULL; + GlobalUnlock(hGlobal); + if (!bLoaded || !image.IsOk()) + return FALSE; + + m_pPicture = new wxImage(image); + m_PictureSize.cx = image.GetWidth(); + m_PictureSize.cy = image.GetHeight(); + } + else + { + // it's a GIF + m_bIsGIF = TRUE; + m_pGIFLSDescriptor = reinterpret_cast + (m_pRawData + sizeof(TGIFHeader)); + if (m_pGIFLSDescriptor->GetPackedValue(LSD_PACKED_GLOBALCT) == 1) + { + // calculate the globat color table size + m_nGlobalCTSize = static_cast + (3* (1 << (m_pGIFLSDescriptor->GetPackedValue(LSD_PACKED_GLOBALCTSIZE)+1))); + // get the background color if GCT is present + UINT nBkOffset = sizeof(TGIFHeader) + sizeof(TGIFLSDescriptor) + 3*m_pGIFLSDescriptor->m_cBkIndex; + if (nBkOffset + 3 <= dwSize) + { + unsigned char *pBkClr = m_pRawData + nBkOffset; + m_clrBackground = RGB(pBkClr[0],pBkClr[1],pBkClr[2]); + } + }; + + // store the picture's size + m_PictureSize.cx = m_pGIFLSDescriptor->m_wWidth; + m_PictureSize.cy = m_pGIFLSDescriptor->m_wHeight; + + wxGIFDecoder decoder; + wxGIFErrorCode err = decoder.LoadGIF(stream); + UINT nFrameCount = (err == wxGIF_OK || err == wxGIF_TRUNCATED) ? decoder.GetFrameCount() : 0; + + m_pRawData = NULL; + m_pGIFHeader = NULL; + m_pGIFLSDescriptor = NULL; + GlobalUnlock(hGlobal); + + if (nFrameCount == 0) // it's an empty GIF! + return FALSE; + + for (UINT i = 0; i < nFrameCount; i++) + { + wxImage image; + if (!decoder.ConvertToImage(i, &image) || !image.IsOk()) + continue; + + TFrame frame; + wxSize size = decoder.GetFrameSize(i); + wxPoint pos = decoder.GetFramePosition(i); + long nDelay = decoder.GetDelay(i); // milliseconds + frame.m_pPicture = new wxImage(image); + frame.m_frameSize.cx = size.GetWidth(); + frame.m_frameSize.cy = size.GetHeight(); + frame.m_frameOffset.cx = pos.x; + frame.m_frameOffset.cy = pos.y; + frame.m_nDelay = nDelay > 0 ? (UINT)(nDelay / 10) : 0; + frame.m_nDisposal = GifDisposal(decoder.GetDisposalMethod(i)); + m_arrFrames.push_back(frame); + } + + if (m_arrFrames.empty()) // couldn't load any frames + return FALSE; + + // a GIF with a single frame is not animated: Draw() renders m_arrFrames[0] like any other pic + }; // if (!IsGIF... + + return PrepareDC(m_PictureSize.cx,m_PictureSize.cy); +} + +void PICTUREEX_WX_CLASS::UnLoad() +{ + Stop(); + delete m_pPicture; + m_pPicture = NULL; + + std::vector::iterator it; + for (it=m_arrFrames.begin();it= m_arrFrames.size()) + { + // a single run has finished: start again with the first frame + m_nCurrFrame = 0; + FillArea(*m_pCanvas, 0, 0, m_PictureSize.cx, m_PictureSize.cy, m_clrBackground); + } + + // first, restore background (for stop/draw support) + const TFrame &frame = m_arrFrames[m_nCurrFrame]; + if (frame.m_nDisposal == 2) + FillArea(*m_pCanvas, frame.m_frameOffset.cx, frame.m_frameOffset.cy, + frame.m_frameSize.cx, frame.m_frameSize.cy, m_clrBackground); + else if (m_pDispCanvas && frame.m_nDisposal == 3) + { + CopyArea(*m_pCanvas, *m_pDispCanvas, frame.m_frameOffset.cx, frame.m_frameOffset.cy, + frame.m_frameSize.cx, frame.m_frameSize.cy); + delete m_pDispCanvas; + m_pDispCanvas = NULL; + } + + m_bIsPlaying = TRUE; + ThreadAnimation(); + return TRUE; + } + + if (m_arrFrames.size() == 1 && m_arrFrames[0].m_pPicture) + { + Compose(*m_pCanvas, *m_arrFrames[0].m_pPicture, m_arrFrames[0].m_frameOffset.cx, m_arrFrames[0].m_frameOffset.cy); + if (m_hWnd) + Invalidate(FALSE); + return TRUE; + }; + + if (m_pPicture) + { + if (m_pPicture->GetWidth() == m_PictureSize.cx && m_pPicture->GetHeight() == m_PictureSize.cy) + Compose(*m_pCanvas, *m_pPicture, 0, 0); + else + Compose(*m_pCanvas, m_pPicture->Scale(m_PictureSize.cx, m_PictureSize.cy, wxIMAGE_QUALITY_HIGH), 0, 0); + if (m_hWnd) + Invalidate(FALSE); + return TRUE; + }; + + return FALSE; +} + +// renders the current animation frame and schedules the next one +void PICTUREEX_WX_CLASS::ThreadAnimation() +{ + const TFrame &frame = m_arrFrames[m_nCurrFrame]; + UINT nDelay = 1; + + if (frame.m_pPicture) + { + // disposal method #3: store the background + if (frame.m_nDisposal == 3) + { + delete m_pDispCanvas; + m_pDispCanvas = new wxImage(m_pCanvas->Copy()); + }; + + Compose(*m_pCanvas, *frame.m_pPicture, frame.m_frameOffset.cx, frame.m_frameOffset.cy); + if (m_hWnd) + Invalidate(FALSE); + + // if the delay time is too short (like in old GIFs), wait for 100ms + nDelay = frame.m_nDelay < 5 ? 100 : 10*frame.m_nDelay; + }; + + if (m_hWnd) + SetTimer(ANIMATION_TIMER_ID, nDelay, NULL); + else + m_bIsPlaying = FALSE; +} + +void PICTUREEX_WX_CLASS::OnTimer(UINT_PTR nIDEvent) +{ + if (nIDEvent != ANIMATION_TIMER_ID) + { + CStatic::OnTimer(nIDEvent); + return; + } + + KillTimer(ANIMATION_TIMER_ID); + if (!m_bIsPlaying || m_nCurrFrame >= m_arrFrames.size()) + return; + + const TFrame &frame = m_arrFrames[m_nCurrFrame]; + if (frame.m_nDisposal == 2) + FillArea(*m_pCanvas, frame.m_frameOffset.cx, frame.m_frameOffset.cy, + frame.m_frameSize.cx, frame.m_frameSize.cy, m_clrBackground); + else if (m_pDispCanvas && frame.m_nDisposal == 3) + { + CopyArea(*m_pCanvas, *m_pDispCanvas, frame.m_frameOffset.cx, frame.m_frameOffset.cy, + frame.m_frameSize.cx, frame.m_frameSize.cy); + delete m_pDispCanvas; + m_pDispCanvas = NULL; + } + + m_nCurrFrame++; + if (m_nCurrFrame == m_arrFrames.size()) + { +// Animation nur ein einziges Mal abspielen? +#ifdef PLAY_ANIM_ONLY_ONCE + m_bIsPlaying = FALSE; + return; +#else + m_nCurrFrame = 0; + // init the screen for the first frame, + FillArea(*m_pCanvas, 0, 0, m_PictureSize.cx, m_PictureSize.cy, m_clrBackground); +#endif + }; + + ThreadAnimation(); +} + +SIZE PICTUREEX_WX_CLASS::GetSize() const +{ + return m_PictureSize; +} + +BOOL PICTUREEX_WX_CLASS::Load(LPCTSTR szFileName) +{ + ASSERT(szFileName); + + CFile file; + if (!file.Open(szFileName, CFile::modeRead | CFile::shareDenyWrite)) + { + TRACE(_T("Load (file): Error opening file %s\n"),szFileName); + return FALSE; + }; + + DWORD dwSize = (DWORD)file.GetLength(); + HGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE,dwSize); + if (!hGlobal) + { + TRACE(_T("Load (file): Error allocating memory\n")); + return FALSE; + }; + + char *pData = reinterpret_cast(GlobalLock(hGlobal)); + if (!pData) + { + TRACE(_T("Load (file): Error locking memory\n")); + GlobalFree(hGlobal); + return FALSE; + }; + + UINT nRead = 0; + TRY + { + nRead = file.Read(pData,dwSize); + } + CATCH(CFileException, e) + { + TRACE(_T("Load (file): An exception occured while reading the file %s\n"), szFileName); + e->Delete(); + } + END_CATCH + GlobalUnlock(hGlobal); + file.Close(); + + BOOL bRetValue = nRead == dwSize && Load(hGlobal,dwSize); + GlobalFree(hGlobal); + return bRetValue; +} + +BOOL PICTUREEX_WX_CLASS::Load(LPCTSTR szResourceName, LPCTSTR szResourceType) +{ + ASSERT(szResourceName); + ASSERT(szResourceType); + + HRSRC hPicture = FindResource(AfxGetResourceHandle(),szResourceName,szResourceType); + HGLOBAL hResData; + if (!hPicture || !(hResData = LoadResource(AfxGetResourceHandle(),hPicture))) + { + TRACE(_T("Load (resource): Error loading resource %s\n"),szResourceName); + return FALSE; + }; + DWORD dwSize = SizeofResource(AfxGetResourceHandle(),hPicture); + + HGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE,dwSize); + if (!hGlobal) + { + TRACE(_T("Load (resource): Error allocating memory\n")); + FreeResource(hResData); + return FALSE; + }; + + char *pDest = reinterpret_cast (GlobalLock(hGlobal)); + char *pSrc = reinterpret_cast (LockResource(hResData)); + if (!pSrc || !pDest) + { + TRACE(_T("Load (resource): Error locking memory\n")); + GlobalFree(hGlobal); + FreeResource(hResData); + return FALSE; + }; + CopyMemory(pDest,pSrc,dwSize); + FreeResource(hResData); + GlobalUnlock(hGlobal); + + BOOL bRetValue = Load(hGlobal,dwSize); + GlobalFree(hGlobal); + return bRetValue; +} + +void PICTUREEX_WX_CLASS::Stop() +{ + m_bIsPlaying = FALSE; + if (m_hWnd && ::IsWindow(m_hWnd)) + KillTimer(ANIMATION_TIMER_ID); + m_bExitThread = FALSE; +} + +BOOL PICTUREEX_WX_CLASS::IsGIF() const +{ + return m_bIsGIF; +} + +BOOL PICTUREEX_WX_CLASS::IsAnimatedGIF() const +{ + return (m_bIsGIF && (m_arrFrames.size() > 1)); +} + +BOOL PICTUREEX_WX_CLASS::IsPlaying() const +{ + return m_bIsPlaying; +} + +int PICTUREEX_WX_CLASS::GetFrameCount() const +{ + if (!IsAnimatedGIF()) + return 0; + + return (int)m_arrFrames.size(); +} + +COLORREF PICTUREEX_WX_CLASS::GetBkColor() const +{ + return m_clrBackground; +} + +void PICTUREEX_WX_CLASS::OnPaint() +{ + CPaintDC dc(this); // device context for painting + + wxDC *pDC = dc.GetWx(); + if (!m_pCanvas || !pDC) + return; + + LONG nPaintWidth = m_PaintRect.right-m_PaintRect.left; + + if (nPaintWidth > 0) + { + wxRect rect(m_PaintRect.left, m_PaintRect.top, nPaintWidth, m_PaintRect.bottom - m_PaintRect.top); + rect.Intersect(wxRect(0, 0, m_pCanvas->GetWidth(), m_pCanvas->GetHeight())); + if (!rect.IsEmpty()) + pDC->DrawBitmap(wxBitmap(m_pCanvas->GetSubImage(rect)), + rect.x - m_PaintRect.left, rect.y - m_PaintRect.top, false); + } + else + { + pDC->DrawBitmap(wxBitmap(*m_pCanvas), 0, 0, false); + }; +} + +BOOL PICTUREEX_WX_CLASS::PrepareDC(int nWidth, int nHeight) +{ + if (m_hWnd) + SetWindowPos(NULL,0,0,nWidth,nHeight,SWP_NOMOVE | SWP_NOZORDER); + + if (nWidth <= 0 || nHeight <= 0) + return FALSE; + + m_pCanvas = new wxImage(nWidth, nHeight, false); + + // fill the background + m_clrBackground = GetSysColor(COLOR_3DFACE); + FillArea(*m_pCanvas, 0, 0, nWidth, nHeight, m_clrBackground); + + m_bIsInitialized = TRUE; + return TRUE; +} + +void PICTUREEX_WX_CLASS::OnDestroy() +{ + Stop(); + CStatic::OnDestroy(); +} + +void PICTUREEX_WX_CLASS::SetBkColor(COLORREF clr) +{ + if (!m_bIsInitialized) return; + + m_clrBackground = clr; + FillArea(*m_pCanvas, 0, 0, m_PictureSize.cx, m_PictureSize.cy, clr); +} + +BOOL PICTUREEX_WX_CLASS::SetPaintRect(const RECT *lpRect) +{ + return CopyRect(&m_PaintRect, lpRect); +} + +BOOL PICTUREEX_WX_CLASS::GetPaintRect(RECT *lpRect) +{ + return CopyRect(lpRect, &m_PaintRect); +} diff --git a/CrypTool/PlayfairAnalysis.cpp b/CrypTool/PlayfairAnalysis.cpp index df615e0f8..944841acb 100644 --- a/CrypTool/PlayfairAnalysis.cpp +++ b/CrypTool/PlayfairAnalysis.cpp @@ -2370,7 +2370,7 @@ void playfair_liste::init( int msize ) playfair_liste::~playfair_liste(void) { - if(d) delete[n+2] d; + if(d) delete[] d; } playfair_liste::playfair_liste(int msize, int l) diff --git a/CrypTool/PlayfairStructures.cpp b/CrypTool/PlayfairStructures.cpp index 4c1706abc..dd6c41c2a 100644 --- a/CrypTool/PlayfairStructures.cpp +++ b/CrypTool/PlayfairStructures.cpp @@ -68,7 +68,7 @@ CPlayfairAlphabet::CPlayfairAlphabet(char lower_letter/* = A*/, char upper_lette CPlayfairLetter CPlayfairAlphabet::addLetter(char let) { CPlayfairLetter ret_let; - if ((int)&(ret_let = getLetter(let, true)) != NULL) + if (&(ret_let = getLetter(let, true)) != NULL) return ret_let; if (my_countgetInterceptedSessionKey(), temp); + OctetString interceptedSessionKey = _trudy->getInterceptedSessionKey(); + convertOctetStringToHexString( &interceptedSessionKey, temp); strcat(protocolString, temp); // *** ATTACK CYCLE *** diff --git a/CrypTool/SizingDialog.cpp b/CrypTool/SizingDialog.cpp index a0f2c00b0..806c15d98 100644 --- a/CrypTool/SizingDialog.cpp +++ b/CrypTool/SizingDialog.cpp @@ -168,7 +168,7 @@ BOOL CCtrlInfo::ExtractOptions(LPCTSTR lpszSection, else { CString strErr; - strErr.Format(_T("'%s' is not predefined keyword"), strWord); + strErr.Format(_T("'%s' is not predefined keyword"), (LPCTSTR)strWord); MessageBox(NULL, strErr, _T("There is something wrong in the expression describing control position"), MB_OK | MB_ICONEXCLAMATION); return FALSE; diff --git a/CrypTool/StereotypedAttack.cpp b/CrypTool/StereotypedAttack.cpp index 515299e58..35f0d4ee3 100644 --- a/CrypTool/StereotypedAttack.cpp +++ b/CrypTool/StereotypedAttack.cpp @@ -248,7 +248,7 @@ bool StereotypedAttack::FindRoots(ZZX f, vec_ZZ& r){ } // Getters and setters -void StereotypedAttack::setN(ZZ& N){ +void StereotypedAttack::setN(const ZZ& N){ this->N=N; updateX(); } @@ -257,7 +257,7 @@ ZZ& StereotypedAttack::getN(){ return N; } -void StereotypedAttack::setE(ZZ& e){ +void StereotypedAttack::setE(const ZZ& e){ this->e=e; updateX(); } @@ -266,7 +266,7 @@ ZZ& StereotypedAttack::getE(){ return e; } -void StereotypedAttack::setH(ZZ& h){ +void StereotypedAttack::setH(const ZZ& h){ this->h=h; updateX(); } @@ -275,15 +275,15 @@ ZZ& StereotypedAttack::getH(){ return h; } -void StereotypedAttack::setLeftText(ZZ& leftText){ +void StereotypedAttack::setLeftText(const ZZ& leftText){ this->leftText=leftText; } -void StereotypedAttack::setRightText(ZZ& rightText){ +void StereotypedAttack::setRightText(const ZZ& rightText){ this->rightText=rightText; } -void StereotypedAttack::setCiphertext(ZZ& ciphertext){ +void StereotypedAttack::setCiphertext(const ZZ& ciphertext){ this->ciphertext=ciphertext; } diff --git a/CrypTool/StereotypedAttack.h b/CrypTool/StereotypedAttack.h index 643236195..d697f3e9d 100644 --- a/CrypTool/StereotypedAttack.h +++ b/CrypTool/StereotypedAttack.h @@ -42,13 +42,13 @@ class StereotypedAttack{ // StereotypedAttack(); // get/set-methods - void setN(ZZ&); - void setE(ZZ&); - void setLeftText(ZZ&); - void setRightText(ZZ&); + void setN(const ZZ&); + void setE(const ZZ&); + void setLeftText(const ZZ&); + void setRightText(const ZZ&); void setUnknownLength(int); - void setCiphertext(ZZ&); - void setH(ZZ&); + void setCiphertext(const ZZ&); + void setH(const ZZ&); ZZ& getBound(); ZZ& getSolution(); diff --git a/CrypTool/automated_permanalysis.cpp b/CrypTool/automated_permanalysis.cpp index 719f937e4..2b73ac398 100644 --- a/CrypTool/automated_permanalysis.cpp +++ b/CrypTool/automated_permanalysis.cpp @@ -293,6 +293,6 @@ int automated_permanalysis::iterate_key_param() for (it_plain = row_dir; it_plain <= col_dir; it_plain++) if (check(it_plain, rangePlain)) analyse(permSize, it_plain, it_perm, it_cipher); - return (int)(keyList); + return keyList != 0; } diff --git a/CrypTool/base64error.cpp b/CrypTool/base64error.cpp index 367e32f9e..937121d63 100644 --- a/CrypTool/base64error.cpp +++ b/CrypTool/base64error.cpp @@ -40,15 +40,15 @@ void base64error(std::string text, char errchar,ssize_t errcode, ssize_t positio AfxMessageBox(result, MB_OK); break; case -3: - result.Format(IDS_BASE64_DECODE_MISSING_CHARS, chlb,text); + result.Format(IDS_BASE64_DECODE_MISSING_CHARS, chlb,text.c_str()); AfxMessageBox(result, MB_OK); break; case -4: - result.Format(IDS_BASE64_FILE_WRITE_ERROR,text); + result.Format(IDS_BASE64_FILE_WRITE_ERROR,text.c_str()); AfxMessageBox(result, MB_OK); break; case -5: - result.Format(IDS_BASE64_FILE_OPEN_ERROR,text); + result.Format(IDS_BASE64_FILE_OPEN_ERROR,text.c_str()); AfxMessageBox(result, MB_OK); break; } diff --git a/CrypTool/keyHillBase.cpp b/CrypTool/keyHillBase.cpp index 5d7dd5dbf..17db89d66 100644 --- a/CrypTool/keyHillBase.cpp +++ b/CrypTool/keyHillBase.cpp @@ -326,7 +326,7 @@ int CKeyHillBase::isInvertable() { CString msg, cs; cs.Format( "%dx%d", dim, dim ); - msg.Format( IDS_HILL_BAD_KEY_INV, cs, modul); + msg.Format( IDS_HILL_BAD_KEY_INV, (LPCTSTR)cs, modul); cs.LoadStringA( IDS_HILL_BAD_KEY ); currentDialog->MessageBoxA(msg, cs, MB_ICONWARNING|MB_OK); } @@ -517,7 +517,7 @@ int CKeyHillBase::strToKey( CString &cs, CString *alphabet ) else theApp.TextOptions.setAlphabet( cs.Mid( laenge, l ) ); laenge = cs.Find( HILLSTR_ALPHABETOFFSET ) + strlen(HILLSTR_ALPHABETOFFSET) +1; - HillOptions.m_offset = unsigned long(cs.GetAt(laenge) - '0'); + HillOptions.m_offset = (unsigned long)(cs.GetAt(laenge) - '0'); laenge = cs.Find(HILLSTR_MULTVARIANT); ASSERT(laenge > 0); laenge += strlen(HILLSTR_MULTVARIANT) +1; @@ -596,10 +596,10 @@ void CKeyHillBase::writeRegistry() { if ( CT_OPEN_REGISTRY_SETTINGS( KEY_WRITE, IDS_REGISTRY_SETTINGS, "Hill" ) == ERROR_SUCCESS ) { - CT_WRITE_REGISTRY( unsigned long(matType), "EditKeyChrMatrix" ); + CT_WRITE_REGISTRY( (unsigned long)(matType), "EditKeyChrMatrix" ); CT_WRITE_REGISTRY( dim, "MatrixDimension" ); - CT_WRITE_REGISTRY( unsigned long(verbose), "ShowExample" ); - CT_WRITE_REGISTRY( unsigned long(multType), "MultType" ); + CT_WRITE_REGISTRY( (unsigned long)(verbose), "ShowExample" ); + CT_WRITE_REGISTRY( (unsigned long)(multType), "MultType" ); CT_CLOSE_REGISTRY(); } HillOptions.writeRegistry(); diff --git a/CrypTool/passwordchecker.cpp b/CrypTool/passwordchecker.cpp index 8f7ca9ce0..272e54c52 100644 --- a/CrypTool/passwordchecker.cpp +++ b/CrypTool/passwordchecker.cpp @@ -777,7 +777,7 @@ char *checkPassword(char *password, char *path, int hidePassword, double *determ CString reconstructionDictionaryWords; // flomar, 01/24/2012: add integral separators (i.e. '1999' -> '1.999') CString stringDictSize = createStringNumberWithDigitGrouping(dict_size); - reconstructionDictionaryWords.Format(IDS_PQM_PASSWORD_RECONSTRUCTION_DICTIONARYWORDS, stringDictSize); + reconstructionDictionaryWords.Format(IDS_PQM_PASSWORD_RECONSTRUCTION_DICTIONARYWORDS, (LPCTSTR)stringDictSize); strcat(str_fnds, (LPCTSTR)(reconstructionDictionaryWords)); get_pwd_substrings( pwd_len, closure_matrix, DICT_WORDS, password, hidePassword, str_fnds, word_list, word_listSize); diff --git a/CrypTool/rsastereotypedmsgdlg.cpp b/CrypTool/rsastereotypedmsgdlg.cpp index f2220aaf0..45ac43a6e 100644 --- a/CrypTool/rsastereotypedmsgdlg.cpp +++ b/CrypTool/rsastereotypedmsgdlg.cpp @@ -798,7 +798,7 @@ void CRSAStereotypedMSGDlg::OnChangeEditplainfull() MaxChars = to_ZZ(floor(log(N+255)/log(256.0))); else MaxChars = 0; - chars.Format("%d (%s)",tmp.GetLength(),toString(MaxChars,10,0)); + chars.Format("%d (%s)",tmp.GetLength(),(LPCTSTR)toString(MaxChars,10,0)); SetDlgItemText(IDC_EDITPLAINLENGTH,chars); SetDlgItemText(IDC_EDITCIPHER,""); UpdateSliders(); diff --git a/CrypTool/uucodeerror.cpp b/CrypTool/uucodeerror.cpp index 3c99ad9de..b6cfd3c2b 100644 --- a/CrypTool/uucodeerror.cpp +++ b/CrypTool/uucodeerror.cpp @@ -41,11 +41,11 @@ void uucodeerror(std::string text, char errchar,ssize_t errcode, ssize_t positio AfxMessageBox(result, MB_OK); break; case -3: - result.Format(IDS_UU_DECODE_MISSING_CHAR, chlb,text); + result.Format(IDS_UU_DECODE_MISSING_CHAR, chlb,text.c_str()); AfxMessageBox(result, MB_OK); break; case -4: - result.Format(IDS_UU_FILE_WRITE_ERROR,text); + result.Format(IDS_UU_FILE_WRITE_ERROR,text.c_str()); AfxMessageBox(result, MB_OK); break; case -5: diff --git a/CrypTool/zip/zunzip.h b/CrypTool/zip/zunzip.h index 2ca975ad0..64635d1b2 100644 --- a/CrypTool/zip/zunzip.h +++ b/CrypTool/zip/zunzip.h @@ -221,7 +221,12 @@ extern "C" { typedef long longint; typedef unsigned short UWORD; #ifndef OS2 +#ifdef _WIN32 typedef unsigned long ULONG; +#else +#include +typedef uint32_t ULONG; /* 32 bits as on Windows (and as mfcwx defines it) */ +#endif #endif /*************************/ diff --git a/libVolRen/ArcBall.cpp b/libVolRen/ArcBall.cpp index 81acc2c9f..08914bc8f 100644 --- a/libVolRen/ArcBall.cpp +++ b/libVolRen/ArcBall.cpp @@ -36,13 +36,13 @@ CArcBall::~CArcBall() } -void CArcBall::place(CPosition &vec2Center, double dRadius) +void CArcBall::place(const CPosition &vec2Center, double dRadius) { m_vec2Center = vec2Center; m_dRadius = dRadius; } -void CArcBall::setMouse(CPosition &vec2Now) +void CArcBall::setMouse(const CPosition &vec2Now) { m_vec2Now = vec2Now; } diff --git a/libVolRen/ArcBall.h b/libVolRen/ArcBall.h index 6c49c43d4..2fbdedcf3 100644 --- a/libVolRen/ArcBall.h +++ b/libVolRen/ArcBall.h @@ -22,8 +22,8 @@ class CArcBall virtual ~CArcBall(); /* Public routines */ - void place(CPosition &vecPos, double dRadius); - void setMouse(CPosition &vecNow); + void place(const CPosition &vecPos, double dRadius); + void setMouse(const CPosition &vecNow); void startDragging(); void finishDragging(); CMatrix getRotatonMatrix(); diff --git a/libVolRen/EditorDialog.cpp b/libVolRen/EditorDialog.cpp index 022a966d2..d8f90cbd4 100644 --- a/libVolRen/EditorDialog.cpp +++ b/libVolRen/EditorDialog.cpp @@ -53,11 +53,13 @@ void CEditorDialog::Create() CDialog::ShowWindow(SW_SHOW); } +#ifdef _WIN32 BEGIN_EVENTSINK_MAP(CEditorDialog, CDialog) //{{AFX_EVENTSINK_MAP(CEditorDialog) ON_EVENT(CEditorDialog, IDC_GTTRAPEZEDITORCTRL1, 1 /* ColorTableChanged */, OnColorTableChanged, VTS_NONE) //}}AFX_EVENTSINK_MAP END_EVENTSINK_MAP() +#endif /*void CEditorDialog::OnTransferFunctionChanged(LPUNKNOWN colortable) { diff --git a/libVolRen/EditorDialog.h b/libVolRen/EditorDialog.h index 88aaf65b1..461f3614a 100644 --- a/libVolRen/EditorDialog.h +++ b/libVolRen/EditorDialog.h @@ -43,7 +43,9 @@ class CEditorDialog : public CDialog // Generated message map functions //{{AFX_MSG(CEditorDialog) afx_msg void OnColorTableChanged(); +#ifdef _WIN32 DECLARE_EVENTSINK_MAP() +#endif //}}AFX_MSG DECLARE_MESSAGE_MAP() private: diff --git a/libVolRen/Trapezeditor.cpp b/libVolRen/Trapezeditor.cpp index a6c979615..f91001e74 100644 --- a/libVolRen/Trapezeditor.cpp +++ b/libVolRen/Trapezeditor.cpp @@ -15,6 +15,7 @@ IMPLEMENT_DYNCREATE(CTrapezEditor, CWnd) ///////////////////////////////////////////////////////////////////////////// // CTrapezEditor properties +#ifdef _WIN32 BOOL CTrapezEditor::GetDisplayColorTable() { BOOL result; @@ -98,6 +99,23 @@ void CTrapezEditor::SetDisplayHistogram(BOOL propVal) { SetProperty(0x3, VT_BOOL, propVal); } +#else +// no ActiveX control behind the wrapper: properties read as 0, writes are ignored +BOOL CTrapezEditor::GetDisplayColorTable() { return FALSE; } +void CTrapezEditor::SetDisplayColorTable(BOOL) {} +LPUNKNOWN CTrapezEditor::GetColortable() { return NULL; } +void CTrapezEditor::SetColortable(LPUNKNOWN) {} +long CTrapezEditor::GetResolution() { return 0; } +void CTrapezEditor::SetResolution(long) {} +float CTrapezEditor::GetHistScale() { return 0.0f; } +void CTrapezEditor::SetHistScale(float) {} +LPUNKNOWN CTrapezEditor::GetHistogram() { return NULL; } +void CTrapezEditor::SetHistogram(LPUNKNOWN) {} +short CTrapezEditor::GetDrawstyle() { return 0; } +void CTrapezEditor::SetDrawstyle(short) {} +BOOL CTrapezEditor::GetDisplayHistogram() { return FALSE; } +void CTrapezEditor::SetDisplayHistogram(BOOL) {} +#endif ///////////////////////////////////////////////////////////////////////////// // CTrapezEditor operations diff --git a/libVolRen/Trapezeditor.h b/libVolRen/Trapezeditor.h index 8d46665c7..4893d9bbc 100644 --- a/libVolRen/Trapezeditor.h +++ b/libVolRen/Trapezeditor.h @@ -23,6 +23,7 @@ class CTrapezEditor : public CWnd = { 0x90393a34, 0x2f31, 0x48a4, { 0x93, 0x23, 0x8c, 0xec, 0x42, 0xc7, 0x39, 0x54 } }; return clsid; } +#ifdef _WIN32 virtual BOOL Create(LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, const RECT& rect, @@ -36,6 +37,14 @@ class CTrapezEditor : public CWnd BSTR bstrLicKey = NULL) { return CreateControl(GetClsid(), lpszWindowName, dwStyle, rect, pParentWnd, nID, pPersist, bStorage, bstrLicKey); } +#else + // ActiveX controls are not available: the transfer function editor cannot be created + virtual BOOL Create(LPCTSTR, LPCTSTR, DWORD, const RECT&, CWnd*, UINT, CCreateContext* = NULL) + { return FALSE; } + + BOOL Create(LPCTSTR, DWORD, const RECT&, CWnd*, UINT, CFile* = NULL, BOOL = FALSE, BSTR = NULL) + { return FALSE; } +#endif // Attributes public: From 0a111f9f2b908eba2f0a9111d6b9fdd8b3c85642 Mon Sep 17 00:00:00 2001 From: Kamil Marczak Date: Tue, 6 Oct 2026 17:40:34 +0200 Subject: [PATCH 15/46] feat(mfcwx): add partial runtime, GDI, control classes, Scintilla and help layers Work in progress from parallel efforts: kernel objects, files, threads and registry emulation; CDC on wxDC; MFC control wrapper classes; the Scintilla message proxy and the HTML Help replacement. Some parts are unfinished. --- port/mfcwx/CMakeLists.txt | 19 + port/mfcwx/include/afxdlgs.h | 5 + port/mfcwx/include/afxwin.h | 2 + port/mfcwx/include/htmlhelp.h | 10 + port/mfcwx/include/mfcwx/crtcompat.h | 8 +- port/mfcwx/include/mfcwx/extras.h | 29 +- port/mfcwx/include/process.h | 7 + port/mfcwx/src/afxcore.cpp | 669 ++++++++ port/mfcwx/src/afxfile.cpp | 990 +++++++++++ port/mfcwx/src/afxmt.cpp | 137 ++ port/mfcwx/src/codepage_tables.cpp | 8 + port/mfcwx/src/controls.cpp | 79 +- port/mfcwx/src/controls_internal.h | 42 + port/mfcwx/src/crt.cpp | 801 +++++++++ port/mfcwx/src/file.cpp | 876 ++++++++++ port/mfcwx/src/gdi.cpp | 1429 ++++++++++++++++ port/mfcwx/src/gdi_bits.cpp | 794 +++++++++ port/mfcwx/src/gdi_dc.cpp | 2331 ++++++++++++++++++++++++++ port/mfcwx/src/gdi_internal.h | 218 +++ port/mfcwx/src/gdi_text.cpp | 688 ++++++++ port/mfcwx/src/profile.cpp | 773 +++++++++ port/mfcwx/src/thread.cpp | 141 ++ port/mfcwx/src/win32.cpp | 1453 ++++++++++++++++ port/mfcwx/tests/runtime_stubs.cpp | 38 + port/mfcwx/tests/runtime_tests.cpp | 1046 ++++++++++++ 25 files changed, 12550 insertions(+), 43 deletions(-) create mode 100644 port/mfcwx/src/afxcore.cpp create mode 100644 port/mfcwx/src/afxfile.cpp create mode 100644 port/mfcwx/src/afxmt.cpp create mode 100644 port/mfcwx/src/controls_internal.h create mode 100644 port/mfcwx/src/crt.cpp create mode 100644 port/mfcwx/src/file.cpp create mode 100644 port/mfcwx/src/gdi.cpp create mode 100644 port/mfcwx/src/gdi_bits.cpp create mode 100644 port/mfcwx/src/gdi_dc.cpp create mode 100644 port/mfcwx/src/gdi_internal.h create mode 100644 port/mfcwx/src/gdi_text.cpp create mode 100644 port/mfcwx/src/profile.cpp create mode 100644 port/mfcwx/src/thread.cpp create mode 100644 port/mfcwx/src/win32.cpp create mode 100644 port/mfcwx/tests/runtime_stubs.cpp create mode 100644 port/mfcwx/tests/runtime_tests.cpp diff --git a/port/mfcwx/CMakeLists.txt b/port/mfcwx/CMakeLists.txt index 20ad75171..557c081fe 100644 --- a/port/mfcwx/CMakeLists.txt +++ b/port/mfcwx/CMakeLists.txt @@ -11,3 +11,22 @@ target_link_libraries(mfcwx PUBLIC ${wxWidgets_LIBRARIES} OpenGL::GL Threads::Th if(APPLE) target_link_libraries(mfcwx PUBLIC "-framework OpenGL") endif() + +option(MFCWX_TESTS "Build the mfcwx runtime tests" OFF) +if(MFCWX_TESTS) + # The runtime sources are compiled directly so the tests do not depend on the GUI parts; + # tests/runtime_stubs.cpp provides the few GUI symbols they reference. + set(MFCWX_RUNTIME_SOURCES + afxcore.cpp afxfile.cpp afxmt.cpp codepage.cpp codepage_tables.cpp crt.cpp cstring.cpp file.cpp kernel.cpp + profile.cpp thread.cpp win32.cpp) + list(TRANSFORM MFCWX_RUNTIME_SOURCES PREPEND ${CMAKE_CURRENT_SOURCE_DIR}/src/) + add_executable(mfcwx_runtime_tests tests/runtime_tests.cpp tests/runtime_stubs.cpp ${MFCWX_RUNTIME_SOURCES}) + target_include_directories(mfcwx_runtime_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include + ${CMAKE_CURRENT_SOURCE_DIR}/src) + target_include_directories(mfcwx_runtime_tests SYSTEM PRIVATE ${wxWidgets_INCLUDE_DIRS}) + target_compile_features(mfcwx_runtime_tests PRIVATE cxx_std_14) + target_compile_definitions(mfcwx_runtime_tests PRIVATE ${wxWidgets_DEFINITIONS} MFCWX=1) + target_compile_options(mfcwx_runtime_tests PRIVATE ${wxWidgets_CXX_FLAGS}) + target_link_libraries(mfcwx_runtime_tests PRIVATE ${wxWidgets_LIBRARIES} Threads::Threads ${CMAKE_DL_LIBS}) + add_test(NAME mfcwx_runtime_tests COMMAND mfcwx_runtime_tests) +endif() diff --git a/port/mfcwx/include/afxdlgs.h b/port/mfcwx/include/afxdlgs.h index 61a643c8f..f4c909a92 100644 --- a/port/mfcwx/include/afxdlgs.h +++ b/port/mfcwx/include/afxdlgs.h @@ -124,6 +124,11 @@ class CPrintDialog : public CCommonDialog { HDC GetPrinterDC() const { return nullptr; } HDC CreatePrinterDC() { return nullptr; } CString GetDeviceName() const { return CString(); } + BOOL PrintSelection() const; + BOOL PrintRange() const; + BOOL PrintAll() const; + int GetFromPage() const; + int GetToPage() const; }; #define FR_DOWN 0x00000001 diff --git a/port/mfcwx/include/afxwin.h b/port/mfcwx/include/afxwin.h index 305e0d603..b3adb9bb4 100644 --- a/port/mfcwx/include/afxwin.h +++ b/port/mfcwx/include/afxwin.h @@ -1074,6 +1074,7 @@ class CWinThread : public CCmdTarget { virtual BOOL OnIdle(LONG lCount); virtual BOOL IsIdleMessage(MSG*) { return TRUE; } virtual CWnd* GetMainWnd(); + virtual BOOL ProcessMessageFilter(int code, LPMSG lpMsg); operator HANDLE() const { return m_hThread; } CWnd* m_pMainWnd; @@ -1157,6 +1158,7 @@ class CWinApp : public CWinThread { virtual void ParseCommandLine(class CCommandLineInfo& rCmdInfo); virtual BOOL DoPromptFileName(CString& fileName, UINT nIDSTitle, DWORD lFlags, BOOL bOpenFileDialog, CDocTemplate* pTemplate); + virtual BOOL OnDDECommand(LPTSTR lpszCommand); void AddDocTemplate(CDocTemplate* pTemplate); POSITION GetFirstDocTemplatePosition() const; diff --git a/port/mfcwx/include/htmlhelp.h b/port/mfcwx/include/htmlhelp.h index 85c4f63d3..1aff78a33 100644 --- a/port/mfcwx/include/htmlhelp.h +++ b/port/mfcwx/include/htmlhelp.h @@ -1,5 +1,15 @@ #pragma once #include "mfcwx/win32.h" +typedef struct tagHH_AKLINK { + int cbStruct; + BOOL fReserved; + const char* pszKeywords; + const char* pszUrl; + const char* pszMsgText; + const char* pszMsgTitle; + const char* pszWindow; + BOOL fIndexOnFail; +} HH_AKLINK, *PHH_AKLINK; #ifdef __cplusplus HWND HtmlHelp(HWND hwndCaller, const char* pszFile, UINT uCommand, DWORD_PTR dwData); HWND HtmlHelpA(HWND hwndCaller, const char* pszFile, UINT uCommand, DWORD_PTR dwData); diff --git a/port/mfcwx/include/mfcwx/crtcompat.h b/port/mfcwx/include/mfcwx/crtcompat.h index 8f715ef5b..81337a5dd 100644 --- a/port/mfcwx/include/mfcwx/crtcompat.h +++ b/port/mfcwx/include/mfcwx/crtcompat.h @@ -31,6 +31,8 @@ extern "C" { #define _MAX_DIR 256 #define _MAX_FNAME 256 #define _MAX_EXT 256 +#define _TRUNCATE ((size_t)-1) +#define STRUNCATE 80 #define _O_RDONLY O_RDONLY #define _O_WRONLY O_WRONLY @@ -229,7 +231,7 @@ extern "C" { #define _tunlink mfcwx_unlink #define _taccess mfcwx_access #define _tmkdir _mkdir -#define _tgetcwd getcwd +#define _tgetcwd _getcwd #define _tchdir mfcwx_chdir #define _tsplitpath _splitpath #define _tmakepath _makepath @@ -277,6 +279,8 @@ uintptr_t mfcwx_beginthreadex(void* security, unsigned stack, unsigned (*fn)(voi unsigned* threadId); void mfcwx_endthread(unsigned code); void mfcwx_normalize_path(char* path); +/* getcwd returning the application path form ("\\dir\\sub"); see mfcwx::AppPath. */ +char* mfcwx_getcwd(char* buf, int size); int _mkdir(const char* path); @@ -291,7 +295,7 @@ static inline FILE* _fdopen(int fd, const char* mode) { return fdopen(fd, mode); static inline int _unlink(const char* name) { return mfcwx_unlink(name); } static inline int _rmdir(const char* name) { return mfcwx_rmdir(name); } static inline int _chdir(const char* name) { return mfcwx_chdir(name); } -static inline char* _getcwd(char* buf, int size) { return getcwd(buf, (size_t)size); } +static inline char* _getcwd(char* buf, int size) { return mfcwx_getcwd(buf, size); } static inline int _access(const char* name, int mode) { return mfcwx_access(name, mode); } static inline int _chmod(const char* name, int mode) { return mfcwx_chmod(name, mode); } static inline int _getpid(void) { return (int)getpid(); } diff --git a/port/mfcwx/include/mfcwx/extras.h b/port/mfcwx/include/mfcwx/extras.h index 15ba7dcd3..0bc660c4d 100644 --- a/port/mfcwx/include/mfcwx/extras.h +++ b/port/mfcwx/include/mfcwx/extras.h @@ -91,14 +91,14 @@ typedef struct tagLOGPALETTE { WORD palVersion, palNumEntries; PALETTEENTRY palP #define PC_NOCOLLAPSE 0x04 #define ListView_GetSubItemRect(hwnd, iItem, iSubItem, code, prc) \ - (BOOL)SendMessage((hwnd), LVM_GETSUBITEMRECT, (WPARAM)(int)(iItem), \ + (BOOL)::SendMessage((hwnd), LVM_GETSUBITEMRECT, (WPARAM)(int)(iItem), \ ((prc) ? ((((LPRECT)(prc))->top = (iSubItem)), (((LPRECT)(prc))->left = (code)), (LPARAM)(prc)) : (LPARAM)(LPRECT)NULL)) -#define ListView_GetTopIndex(hwnd) (int)SendMessage((hwnd), LVM_GETTOPINDEX, 0, 0) +#define ListView_GetTopIndex(hwnd) (int)::SendMessage((hwnd), LVM_GETTOPINDEX, 0, 0) #define ListView_GetItemText(hwndLV, i, iSubItem_, pszText_, cchTextMax_) \ { LVITEM _lvi; _lvi.iSubItem = (iSubItem_); _lvi.cchTextMax = (cchTextMax_); _lvi.pszText = (pszText_); \ - SendMessage((hwndLV), LVM_GETITEMTEXT, (WPARAM)(i), (LPARAM)&_lvi); } -#define ListView_SetExtendedListViewStyle(hwnd, style) SendMessage((hwnd), LVM_SETEXTENDEDLISTVIEWSTYLE, 0, (style)) -#define ListView_GetItemCount(hwnd) (int)SendMessage((hwnd), LVM_GETITEMCOUNT, 0, 0) + ::SendMessage((hwndLV), LVM_GETITEMTEXT, (WPARAM)(i), (LPARAM)&_lvi); } +#define ListView_SetExtendedListViewStyle(hwnd, style) ::SendMessage((hwnd), LVM_SETEXTENDEDLISTVIEWSTYLE, 0, (style)) +#define ListView_GetItemCount(hwnd) (int)::SendMessage((hwnd), LVM_GETITEMCOUNT, 0, 0) class CWaitCursor { public: @@ -141,3 +141,22 @@ BOOL wglMakeCurrent(HDC hdc, HGLRC hglrc); HGLRC wglGetCurrentContext(); HDC wglGetCurrentDC(); void* wglGetProcAddress(const char* name); + +// --------------------------------------------------------------------------------------------- +// Declarations required by the application sources (CrypTool, ChallengeResponse, libVolRen). + +#define _AFX_NO_DAO_SUPPORT // as in 64-bit MFC: no DAO database classes (afxdao.h) +#define WM_ICONERASEBKGND 0x0027 +#define MSGF_DIALOGBOX 0 +#define MSGF_MESSAGEBOX 1 +#define MSGF_MENU 2 +#define MSGF_SCROLLBAR 5 +#define MSGF_NEXTWINDOW 6 +typedef struct tagLOGPALETTE* PLOGPALETTE; +struct IUnknown; +typedef IUnknown* LPUNKNOWN; +DWORD SearchPath(const char* lpPath, const char* lpFileName, const char* lpExtension, DWORD nBufferLength, + char* lpBuffer, char** lpFilePart); +DWORD SearchPathA(const char* lpPath, const char* lpFileName, const char* lpExtension, DWORD nBufferLength, + char* lpBuffer, char** lpFilePart); +int GetMenuStringW(HMENU hMenu, UINT uIDItem, LPWSTR lpString, int cchMax, UINT flags); diff --git a/port/mfcwx/include/process.h b/port/mfcwx/include/process.h index fbffeb452..dd01f07f1 100644 --- a/port/mfcwx/include/process.h +++ b/port/mfcwx/include/process.h @@ -1,2 +1,9 @@ #pragma once #include "mfcwx/crtcompat.h" + +#define _P_WAIT 0 +#define _P_NOWAIT 1 +#define _P_OVERLAY 2 +#define _P_NOWAITO 3 +#define _P_DETACH 4 +intptr_t _spawnl(int mode, const char* cmdname, const char* arg0, ...); diff --git a/port/mfcwx/src/afxcore.cpp b/port/mfcwx/src/afxcore.cpp new file mode 100644 index 000000000..80f86ced7 --- /dev/null +++ b/port/mfcwx/src/afxcore.cpp @@ -0,0 +1,669 @@ +#include "afxwin.h" +#include "afxole.h" +#include "runtime.h" + +#include +#include + +using namespace mfcwx; + +// --------------------------------------------------------------------------------------------- +// Runtime classes + +namespace { + +CRuntimeClass* g_firstClass = nullptr; + +long long DaysFromCivil(long long y, unsigned m, unsigned d) { + y -= m <= 2; + const long long era = (y >= 0 ? y : y - 399) / 400; + const unsigned yoe = static_cast(y - era * 400); + const unsigned doy = (153 * (m > 2 ? m - 3 : m + 9) + 2) / 5 + d - 1; + const unsigned doe = yoe * 365 + yoe / 4 - yoe / 100 + doy; + return era * 146097 + static_cast(doe) - 719468; +} + +void CivilFromDays(long long z, int& y, int& m, int& d) { + z += 719468; + const long long era = (z >= 0 ? z : z - 146096) / 146097; + const unsigned doe = static_cast(z - era * 146097); + const unsigned yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365; + const long long yy = static_cast(yoe) + era * 400; + const unsigned doy = doe - (365 * yoe + yoe / 4 - yoe / 100); + const unsigned mp = (5 * doy + 2) / 153; + d = static_cast(doy - (153 * mp + 2) / 5 + 1); + m = static_cast(mp < 10 ? mp + 3 : mp - 9); + y = static_cast(yy + (m <= 2)); +} + +const long long kOleEpochDays = DaysFromCivil(1899, 12, 30); + +bool IsLeap(int y) { return (y % 4 == 0 && y % 100 != 0) || y % 400 == 0; } + +int DaysInMonth(int y, int m) { + static const int days[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + return m == 2 && IsLeap(y) ? 29 : days[m - 1]; +} + +std::string LocalizedPiece(const char* buf, size_t n) { + std::string piece(buf, n); + for (unsigned char c : piece) + if (c >= 0x80) { + std::string ansi = Utf8ToAnsi(piece.c_str()); + return ansi.empty() ? piece : ansi; + } + return piece; +} + +std::string Strftime(const char* fmt, const struct tm& tm) { + char buf[512]; + size_t n = strftime(buf, sizeof buf, fmt, &tm); + return LocalizedPiece(buf, n); +} + +// strftime with the Microsoft '#' flag (no leading zeros, long %c/%x forms). +CString FormatTm(const char* fmt, const struct tm& tm) { + std::string out; + for (const char* p = fmt ? fmt : ""; *p; ++p) { + if (*p != '%') { + out += *p; + continue; + } + if (!*++p) + break; + bool alt = false; + if (*p == '#') { + alt = true; + if (!*++p) + break; + } + char spec = *p; + if (spec == '%') { + out += '%'; + continue; + } + if (alt && (spec == 'x' || spec == 'c')) { + out += Strftime("%A, %B ", tm) + std::to_string(tm.tm_mday) + Strftime(", %Y", tm); + if (spec == 'c') + out += Strftime(" %H:%M:%S", tm); + continue; + } + char one[3] = {'%', spec, 0}; + std::string piece = Strftime(one, tm); + if (alt && strchr("dHIjmMSUwWy", spec)) { + size_t z = 0; + while (z + 1 < piece.size() && piece[z] == '0') + ++z; + piece.erase(0, z); + } + out += piece; + } + return CString(out.c_str(), static_cast(out.size())); +} + +bool OleFromParts(int y, int mo, int d, int h, int mi, int s, double& out) { + if (y < 100 || y > 9999 || mo < 1 || mo > 12 || d < 1 || d > DaysInMonth(y, mo) || h < 0 || h > 23 || mi < 0 || + mi > 59 || s < 0 || s > 59) + return false; + long long days = DaysFromCivil(y, static_cast(mo), static_cast(d)) - kOleEpochDays; + double frac = (h * 3600 + mi * 60 + s) / 86400.0; + out = days >= 0 ? static_cast(days) + frac : static_cast(days) - frac; + return true; +} + +void TmFromOle(double dt, struct tm& tm) { + long long dayPart = static_cast(std::trunc(dt)); + double frac = std::fabs(dt - static_cast(dayPart)); + long long secs = std::llround(frac * 86400.0); + if (secs >= 86400) { + secs -= 86400; + ++dayPart; + } + long long days = dayPart + kOleEpochDays; + int y, m, d; + CivilFromDays(days, y, m, d); + memset(&tm, 0, sizeof tm); + tm.tm_year = y - 1900; + tm.tm_mon = m - 1; + tm.tm_mday = d; + tm.tm_hour = static_cast(secs / 3600); + tm.tm_min = static_cast(secs / 60 % 60); + tm.tm_sec = static_cast(secs % 60); + tm.tm_wday = static_cast(((days + 4) % 7 + 7) % 7); + tm.tm_yday = static_cast(days - DaysFromCivil(y, 1, 1)); + tm.tm_isdst = -1; +} + +std::string FileNameOrUnnamed(const CString& name) { return name.IsEmpty() ? "an unnamed file" : name.GetString(); } + +BOOL CopyMessage(const std::string& text, char* buffer, UINT size, PUINT help) { + if (help) + *help = 0; + if (buffer && size) { + size_t n = std::min(text.size(), static_cast(size - 1)); + memcpy(buffer, text.data(), n); + buffer[n] = 0; + } + return TRUE; +} + +std::string Substitute(const char* tmpl, const std::string& name) { + std::string s = tmpl; + size_t pos = s.find("%1"); + if (pos != std::string::npos) + s.replace(pos, 2, name); + return s; +} + +} // namespace + +AFX_CLASSINIT::AFX_CLASSINIT(CRuntimeClass* pNewClass) { + pNewClass->m_pNextClass = g_firstClass; + g_firstClass = pNewClass; +} + +CObject* CRuntimeClass::CreateObject() { return m_pfnCreateObject ? m_pfnCreateObject() : nullptr; } + +BOOL CRuntimeClass::IsDerivedFrom(const CRuntimeClass* pBaseClass) const { + for (const CRuntimeClass* c = this; c; c = c->GetBaseClass()) + if (c == pBaseClass) + return TRUE; + return FALSE; +} + +CRuntimeClass* CRuntimeClass::FromName(const char* name) { + if (!name) + return nullptr; + for (CRuntimeClass* c = g_firstClass; c; c = c->m_pNextClass) + if (strcmp(c->m_lpszClassName, name) == 0) + return c; + return nullptr; +} + +CObject* CRuntimeClass::CreateObject(const char* name) { + CRuntimeClass* c = FromName(name); + return c ? c->CreateObject() : nullptr; +} + +CRuntimeClass CObject::classCObject = {"CObject", sizeof(CObject), 0xFFFF, nullptr, nullptr, nullptr}; +static AFX_CLASSINIT _init_CObject(&CObject::classCObject); + +CRuntimeClass* CObject::GetThisClass() { return &classCObject; } + +CRuntimeClass* CObject::GetRuntimeClass() const { return &classCObject; } + +BOOL CObject::IsKindOf(const CRuntimeClass* pClass) const { return GetRuntimeClass()->IsDerivedFrom(pClass); } + +BOOL CObject::IsSerializable() const { return GetRuntimeClass()->m_wSchema != 0xFFFF; } + +void CObject::Serialize(CArchive&) {} + +CDumpContext afxDump; + +void AfxAssertFailedLine(const char* file, int line, const char* expr) { + fprintf(stderr, "mfcwx: assertion failed: %s (%s:%d)\n", expr ? expr : "", file ? file : "?", line); +} + +void AfxDebugBreak() { fprintf(stderr, "mfcwx: AfxDebugBreak()\n"); } + +// --------------------------------------------------------------------------------------------- +// Exceptions + +IMPLEMENT_DYNAMIC(CException, CObject) +IMPLEMENT_DYNAMIC(CSimpleException, CException) +IMPLEMENT_DYNAMIC(CMemoryException, CSimpleException) +IMPLEMENT_DYNAMIC(CNotSupportedException, CSimpleException) +IMPLEMENT_DYNAMIC(CInvalidArgException, CSimpleException) +IMPLEMENT_DYNAMIC(CUserException, CSimpleException) +IMPLEMENT_DYNAMIC(CResourceException, CSimpleException) +IMPLEMENT_DYNAMIC(CArchiveException, CException) +IMPLEMENT_DYNAMIC(CFileException, CException) + +void CException::Delete() { + if (m_bAutoDelete > 0) + delete this; +} + +BOOL CException::GetErrorMessage(char* lpszError, UINT nMaxError, PUINT pnHelpContext) const { + const char* text = nullptr; + if (IsKindOf(RUNTIME_CLASS(CNotSupportedException))) + text = "An unsupported operation was attempted."; + else if (IsKindOf(RUNTIME_CLASS(CInvalidArgException))) + text = "Encountered an improper argument."; + else if (IsKindOf(RUNTIME_CLASS(CResourceException))) + text = "A required resource was unavailable."; + if (text) + return CopyMessage(text, lpszError, nMaxError, pnHelpContext); + if (pnHelpContext) + *pnHelpContext = 0; + if (lpszError && nMaxError) + lpszError[0] = 0; + return FALSE; +} + +int CException::ReportError(UINT nType, UINT nMessageID) { + char msg[1024]; + if (GetErrorMessage(msg, sizeof msg)) + return AfxMessageBox(msg, nType); + if (nMessageID) + return AfxMessageBox(nMessageID, nType); + return AfxMessageBox("No error message is available.", nType); +} + +BOOL CMemoryException::GetErrorMessage(char* lpszError, UINT nMaxError, PUINT pnHelpContext) const { + return CopyMessage("Out of memory.", lpszError, nMaxError, pnHelpContext); +} + +BOOL CArchiveException::GetErrorMessage(char* lpszError, UINT nMaxError, PUINT pnHelpContext) const { + static const char* const texts[] = { + "No error occurred.", + "An unknown error occurred while accessing %1.", + "An attempt was made to write to the reading %1.", + "An attempt was made to access %1 past its end.", + "An attempt was made to read from the writing %1.", + "%1 has a bad format.", + "%1 contained an unexpected object.", + "%1 contains an incorrect schema.", + "The archive buffer of %1 is full.", + }; + int cause = m_cause >= 0 && m_cause <= bufferFull ? m_cause : genericException; + return CopyMessage(Substitute(texts[cause], FileNameOrUnnamed(m_strFileName)), lpszError, nMaxError, pnHelpContext); +} + +BOOL CFileException::GetErrorMessage(char* lpszError, UINT nMaxError, PUINT pnHelpContext) const { + static const char* const texts[] = { + "No error occurred.", + "An unknown error occurred while accessing %1.", + "%1 was not found.", + "%1 contains an invalid path.", + "%1 could not be opened because there are too many open files.", + "Access to %1 was denied.", + "An invalid file handle was associated with %1.", + "%1 could not be removed because it is the current directory.", + "%1 could not be created because the directory is full.", + "Seek failed on %1", + "A hardware I/O error was reported while accessing %1.", + "A sharing violation occurred while accessing %1.", + "A locking violation occurred while accessing %1.", + "Disk full while accessing %1.", + "An attempt was made to access %1 past its end.", + }; + int cause = m_cause >= 0 && m_cause <= endOfFile ? m_cause : genericException; + return CopyMessage(Substitute(texts[cause], FileNameOrUnnamed(m_strFileName)), lpszError, nMaxError, pnHelpContext); +} + +int CFileException::OsErrorToException(LONG lOsError) { + switch (lOsError) { + case 0: + return none; + case 2: + case 6: + case 18: + return fileNotFound; + case 3: + case 15: + case 17: + case 123: + case 206: + return badPath; + case 4: + return tooManyOpenFiles; + case 5: + case 12: + case 19: + case 80: + case 183: + return accessDenied; + case 11: + return invalidFile; + case 16: + case 145: + return removeCurrentDir; + case 20: + case 21: + case 1117: + return hardIO; + case 32: + return sharingViolation; + case 33: + return lockViolation; + case 38: + return endOfFile; + case 39: + case 112: + return diskFull; + case 131: + case 132: + return badSeek; + default: + return genericException; + } +} + +int CFileException::ErrnoToException(int nErrno) { + switch (nErrno) { + case 0: + return none; + case EPERM: + case EACCES: + case EEXIST: + case EISDIR: + case EROFS: + return accessDenied; + case ENOENT: + return fileNotFound; + case EBADF: + return invalidFile; + case EMFILE: + case ENFILE: + return tooManyOpenFiles; + case ENOSPC: + return diskFull; + case ENOTDIR: + case ENAMETOOLONG: + case ELOOP: + return badPath; + case EIO: + return hardIO; + case ESPIPE: + return badSeek; + case EBUSY: + return sharingViolation; + default: + return genericException; + } +} + +void CFileException::ThrowOsError(LONG lOsError, const char* lpszFileName) { + if (lOsError != 0) + throw new CFileException(OsErrorToException(lOsError), lOsError, lpszFileName); +} + +void CFileException::ThrowErrno(int nErrno, const char* lpszFileName) { + if (nErrno != 0) + throw new CFileException(ErrnoToException(nErrno), static_cast(ErrnoToWin32(nErrno)), lpszFileName); +} + +void AfxThrowMemoryException() { throw new CMemoryException; } + +void AfxThrowNotSupportedException() { throw new CNotSupportedException; } + +void AfxThrowInvalidArgException() { throw new CInvalidArgException; } + +void AfxThrowUserException() { throw new CUserException; } + +void AfxThrowResourceException() { throw new CResourceException; } + +void AfxThrowArchiveException(int cause, const char* lpszArchiveName) { + throw new CArchiveException(cause, lpszArchiveName); +} + +void AfxThrowFileException(int cause, LONG lOsError, const char* lpszFileName) { + throw new CFileException(cause, lOsError, lpszFileName); +} + +void AfxAbort() { + fprintf(stderr, "mfcwx: AfxAbort()\n"); + abort(); +} + +// --------------------------------------------------------------------------------------------- +// Collections + +IMPLEMENT_SERIAL(CStringArray, CObject, 0) +IMPLEMENT_SERIAL(CPtrArray, CObject, 0) +IMPLEMENT_SERIAL(CObArray, CObject, 0) +IMPLEMENT_SERIAL(CDWordArray, CObject, 0) +IMPLEMENT_SERIAL(CUIntArray, CObject, 0) +IMPLEMENT_SERIAL(CWordArray, CObject, 0) +IMPLEMENT_SERIAL(CByteArray, CObject, 0) +IMPLEMENT_SERIAL(CStringList, CObject, 0) +IMPLEMENT_SERIAL(CPtrList, CObject, 0) +IMPLEMENT_SERIAL(CObList, CObject, 0) +IMPLEMENT_SERIAL(CMapStringToString, CObject, 0) +IMPLEMENT_SERIAL(CMapStringToPtr, CObject, 0) +IMPLEMENT_SERIAL(CMapStringToOb, CObject, 0) +IMPLEMENT_SERIAL(CMapPtrToPtr, CObject, 0) +IMPLEMENT_SERIAL(CMapPtrToWord, CObject, 0) +IMPLEMENT_SERIAL(CMapWordToPtr, CObject, 0) +IMPLEMENT_SERIAL(CMapWordToOb, CObject, 0) + +// --------------------------------------------------------------------------------------------- +// Time + +CString CTimeSpan::Format(const char* pFormat) const { + std::string out; + char buf[32]; + for (const char* p = pFormat ? pFormat : ""; *p; ++p) { + if (*p != '%' || !p[1]) { + out += *p; + continue; + } + switch (*++p) { + case 'D': + snprintf(buf, sizeof buf, "%lld", static_cast(GetDays())); + out += buf; + break; + case 'H': + snprintf(buf, sizeof buf, "%02ld", static_cast(GetHours())); + out += buf; + break; + case 'M': + snprintf(buf, sizeof buf, "%02ld", static_cast(GetMinutes())); + out += buf; + break; + case 'S': + snprintf(buf, sizeof buf, "%02ld", static_cast(GetSeconds())); + out += buf; + break; + default: + out += *p; + break; + } + } + return CString(out.c_str(), static_cast(out.size())); +} + +CTime::CTime(int nYear, int nMonth, int nDay, int nHour, int nMin, int nSec, int nDST) { + struct tm tm; + memset(&tm, 0, sizeof tm); + tm.tm_year = nYear - 1900; + tm.tm_mon = nMonth - 1; + tm.tm_mday = nDay; + tm.tm_hour = nHour; + tm.tm_min = nMin; + tm.tm_sec = nSec; + tm.tm_isdst = nDST; + m_time = mktime(&tm); +} + +CTime::CTime(const SYSTEMTIME& st, int nDST) : m_time(0) { + if (st.wYear >= 1900) + *this = CTime(st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, nDST); +} + +CTime::CTime(const FILETIME& ft, int) : m_time(FileTimeToUnix(ft)) {} + +CTime CTime::GetCurrentTime() { return CTime(time(nullptr)); } + +struct tm* CTime::GetLocalTm(struct tm* ptm) const { + static thread_local struct tm local; + if (!ptm) + ptm = &local; + return localtime_r(&m_time, ptm); +} + +struct tm* CTime::GetGmtTm(struct tm* ptm) const { + static thread_local struct tm gmt; + if (!ptm) + ptm = &gmt; + return gmtime_r(&m_time, ptm); +} + +BOOL CTime::GetAsSystemTime(SYSTEMTIME& st) const { + struct tm tm; + if (!GetLocalTm(&tm)) + return FALSE; + st.wYear = static_cast(tm.tm_year + 1900); + st.wMonth = static_cast(tm.tm_mon + 1); + st.wDayOfWeek = static_cast(tm.tm_wday); + st.wDay = static_cast(tm.tm_mday); + st.wHour = static_cast(tm.tm_hour); + st.wMinute = static_cast(tm.tm_min); + st.wSecond = static_cast(tm.tm_sec); + st.wMilliseconds = 0; + return TRUE; +} + +int CTime::GetYear() const { + struct tm tm; + return GetLocalTm(&tm) ? tm.tm_year + 1900 : 0; +} + +int CTime::GetMonth() const { + struct tm tm; + return GetLocalTm(&tm) ? tm.tm_mon + 1 : 0; +} + +int CTime::GetDay() const { + struct tm tm; + return GetLocalTm(&tm) ? tm.tm_mday : 0; +} + +int CTime::GetHour() const { + struct tm tm; + return GetLocalTm(&tm) ? tm.tm_hour : 0; +} + +int CTime::GetMinute() const { + struct tm tm; + return GetLocalTm(&tm) ? tm.tm_min : 0; +} + +int CTime::GetSecond() const { + struct tm tm; + return GetLocalTm(&tm) ? tm.tm_sec : 0; +} + +int CTime::GetDayOfWeek() const { + struct tm tm; + return GetLocalTm(&tm) ? tm.tm_wday + 1 : 0; +} + +CString CTime::Format(const char* pFormat) const { + struct tm tm; + if (!GetLocalTm(&tm)) + return CString(); + return FormatTm(pFormat, tm); +} + +CString CTime::Format(UINT nFormatID) const { + CString fmt; + fmt.LoadString(nFormatID); + return Format(fmt.GetString()); +} + +CString CTime::FormatGmt(const char* pFormat) const { + struct tm tm; + if (!GetGmtTm(&tm)) + return CString(); + return FormatTm(pFormat, tm); +} + +COleDateTime::COleDateTime(int nYear, int nMonth, int nDay, int nHour, int nMin, int nSec) : m_dt(0), m_status(valid) { + if (!OleFromParts(nYear, nMonth, nDay, nHour, nMin, nSec, m_dt)) { + m_dt = 0; + m_status = invalid; + } +} + +COleDateTime COleDateTime::GetCurrentTime() { + time_t now = time(nullptr); + struct tm tm; + localtime_r(&now, &tm); + return COleDateTime(tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); +} + +int COleDateTime::GetYear() const { + if (m_status != valid) + return -1; + struct tm tm; + TmFromOle(m_dt, tm); + return tm.tm_year + 1900; +} + +int COleDateTime::GetMonth() const { + if (m_status != valid) + return -1; + struct tm tm; + TmFromOle(m_dt, tm); + return tm.tm_mon + 1; +} + +int COleDateTime::GetDay() const { + if (m_status != valid) + return -1; + struct tm tm; + TmFromOle(m_dt, tm); + return tm.tm_mday; +} + +int COleDateTime::GetHour() const { + if (m_status != valid) + return -1; + struct tm tm; + TmFromOle(m_dt, tm); + return tm.tm_hour; +} + +int COleDateTime::GetMinute() const { + if (m_status != valid) + return -1; + struct tm tm; + TmFromOle(m_dt, tm); + return tm.tm_min; +} + +int COleDateTime::GetSecond() const { + if (m_status != valid) + return -1; + struct tm tm; + TmFromOle(m_dt, tm); + return tm.tm_sec; +} + +int COleDateTime::GetDayOfWeek() const { + if (m_status != valid) + return -1; + struct tm tm; + TmFromOle(m_dt, tm); + return tm.tm_wday + 1; +} + +CString COleDateTime::Format(const char* pFormat) const { + if (m_status == null) + return CString(); + if (m_status != valid) + return CString("Invalid DateTime"); + struct tm tm; + TmFromOle(m_dt, tm); + return FormatTm(pFormat, tm); +} + +CString COleDateTime::Format(DWORD dwFlags, LCID) const { + if (m_status == null) + return CString(); + if (m_status != valid) + return CString("Invalid DateTime"); + struct tm tm; + TmFromOle(m_dt, tm); + bool hasTime = tm.tm_hour || tm.tm_min || tm.tm_sec; + bool hasDate = static_cast(std::trunc(m_dt)) != 0; + bool timeOnly = (dwFlags & 0x2) != 0 || (!hasDate && hasTime); + bool dateOnly = (dwFlags & 0x4) != 0 || !hasTime; + if (timeOnly && !(dwFlags & 0x4)) + return FormatTm("%X", tm); + if (dateOnly) + return FormatTm("%x", tm); + return FormatTm("%x %X", tm); +} diff --git a/port/mfcwx/src/afxfile.cpp b/port/mfcwx/src/afxfile.cpp new file mode 100644 index 000000000..0b9e1a13e --- /dev/null +++ b/port/mfcwx/src/afxfile.cpp @@ -0,0 +1,990 @@ +#include "afx.h" +#include "runtime.h" + +#include +#include +#include + +#include +#include +#include +#include + +#undef fopen +#undef rename +#undef remove + +using namespace mfcwx; + +namespace { + +#ifdef __APPLE__ +inline time_t BirthSeconds(const struct stat& st) { return st.st_birthtimespec.tv_sec; } +#else +inline time_t BirthSeconds(const struct stat& st) { return st.st_ctim.tv_sec; } +#endif + +CString FileNamePart(const CString& path) { + const char* p = path.GetString(); + const char* last = p; + for (const char* q = p; *q; ++q) + if (*q == '\\' || *q == '/' || *q == ':') + last = q + 1; + return CString(last); +} + +CString StripExtension(const CString& name) { + int dot = name.ReverseFind('.'); + return dot > 0 ? name.Left(dot) : name; +} + +void FillStatus(const struct stat& st, const std::string& native, CFileStatus& rStatus) { + rStatus.m_ctime = BirthSeconds(st); + rStatus.m_atime = st.st_atime; + rStatus.m_mtime = st.st_mtime; + rStatus.m_size = static_cast(st.st_size); + BYTE attr = 0; + if (S_ISDIR(st.st_mode)) + attr |= CFile::directory; + else + attr |= CFile::archive; + if (access(native.c_str(), W_OK) != 0) + attr |= CFile::readOnly; + size_t slash = native.rfind('/'); + const char* base = native.c_str() + (slash == std::string::npos ? 0 : slash + 1); + if (base[0] == '.' && strcmp(base, ".") != 0 && strcmp(base, "..") != 0) + attr |= CFile::hidden; + rStatus.m_attribute = attr; +} + +void ThrowArchive(int cause, CFile* file) { + AfxThrowArchiveException(cause, file ? file->GetFilePath().GetString() : nullptr); +} + +constexpr WORD kNullTag = 0; +constexpr WORD kNewClassTag = 0xFFFF; +constexpr WORD kClassTag = 0x8000; +constexpr DWORD kBigClassTag = 0x80000000u; +constexpr WORD kBigObjectTag = 0x7FFF; + +} // namespace + +// --------------------------------------------------------------------------------------------- +// CFile + +IMPLEMENT_DYNAMIC(CFile, CObject) +IMPLEMENT_DYNAMIC(CStdioFile, CFile) +IMPLEMENT_DYNAMIC(CMemFile, CFile) +IMPLEMENT_DYNAMIC(CFileFind, CObject) + +const HANDLE CFile::hFileNull = INVALID_HANDLE_VALUE; + +CFile::CFile() : m_hFile(hFileNull), m_bCloseOnDelete(FALSE), m_fd(-1) {} + +CFile::CFile(HANDLE hFile) : m_hFile(hFile), m_bCloseOnDelete(FALSE), m_fd(FdFromHandle(hFile)) {} + +CFile::CFile(const char* lpszFileName, UINT nOpenFlags) : CFile() { + CFileException e; + if (!Open(lpszFileName, nOpenFlags, &e)) + AfxThrowFileException(e.m_cause, e.m_lOsError, e.m_strFileName); +} + +CFile::~CFile() { + if (m_hFile != hFileNull && m_bCloseOnDelete) + Abort(); +} + +BOOL CFile::Open(const char* lpszFileName, UINT nOpenFlags, CFileException* pError) { + if (m_hFile != hFileNull) + Abort(); + auto fail = [&](int err, int cause) { + if (pError) { + pError->m_cause = cause; + pError->m_lOsError = static_cast(ErrnoToWin32(err)); + pError->m_strFileName = lpszFileName; + } + return FALSE; + }; + if (!lpszFileName || !*lpszFileName) + return fail(ENOENT, CFileException::badPath); + int oflags = O_CLOEXEC; + switch (nOpenFlags & 3) { + case modeWrite: + oflags |= O_WRONLY; + break; + case modeReadWrite: + oflags |= O_RDWR; + break; + default: + oflags |= O_RDONLY; + break; + } + if (nOpenFlags & modeCreate) { + oflags |= O_CREAT; + if (!(nOpenFlags & modeNoTruncate)) + oflags |= O_TRUNC; + } + std::string path = FsPath(lpszFileName); + struct stat st; + if (stat(path.c_str(), &st) == 0 && S_ISDIR(st.st_mode)) + return fail(EACCES, CFileException::accessDenied); + int fd = open(path.c_str(), oflags, 0666); + if (fd < 0) { + int err = errno; + return fail(err, CFileException::ErrnoToException(err)); + } + m_strFileName = FullAppPath(lpszFileName).c_str(); + m_fd = fd; + m_hFile = HandleFromFd(fd, path, true); + m_bCloseOnDelete = TRUE; + return TRUE; +} + +void CFile::Close() { + if (m_hFile != hFileNull) + CloseHandle(m_hFile); + m_hFile = hFileNull; + m_fd = -1; + m_bCloseOnDelete = FALSE; + m_strFileName.Empty(); +} + +void CFile::Abort() { + if (m_hFile != hFileNull) + CloseHandle(m_hFile); + m_hFile = hFileNull; + m_fd = -1; + m_bCloseOnDelete = FALSE; + m_strFileName.Empty(); +} + +UINT CFile::Read(void* lpBuf, UINT nCount) { + if (nCount == 0) + return 0; + if (m_fd < 0) + AfxThrowFileException(CFileException::invalidFile, ERROR_INVALID_HANDLE, m_strFileName); + char* p = static_cast(lpBuf); + UINT total = 0; + while (total < nCount) { + ssize_t n = read(m_fd, p + total, nCount - total); + if (n < 0 && errno == EINTR) + continue; + if (n < 0) + CFileException::ThrowErrno(errno, m_strFileName); + if (n == 0) + break; + total += static_cast(n); + } + return total; +} + +void CFile::Write(const void* lpBuf, UINT nCount) { + if (nCount == 0) + return; + if (m_fd < 0) + AfxThrowFileException(CFileException::invalidFile, ERROR_INVALID_HANDLE, m_strFileName); + const char* p = static_cast(lpBuf); + UINT total = 0; + while (total < nCount) { + ssize_t n = write(m_fd, p + total, nCount - total); + if (n < 0 && errno == EINTR) + continue; + if (n <= 0) + CFileException::ThrowErrno(n < 0 ? errno : ENOSPC, m_strFileName); + total += static_cast(n); + } +} + +ULONGLONG CFile::Seek(LONGLONG lOff, UINT nFrom) { + int whence = nFrom == current ? SEEK_CUR : nFrom == end ? SEEK_END : SEEK_SET; + off_t pos = m_fd >= 0 ? lseek(m_fd, static_cast(lOff), whence) : -1; + if (pos < 0) + AfxThrowFileException(CFileException::badSeek, 131, m_strFileName); + return static_cast(pos); +} + +ULONGLONG CFile::GetLength() const { + struct stat st; + if (m_fd < 0 || fstat(m_fd, &st) != 0) + AfxThrowFileException(CFileException::invalidFile, ERROR_INVALID_HANDLE, m_strFileName); + return static_cast(st.st_size); +} + +void CFile::SetLength(ULONGLONG dwNewLen) { + if (m_fd < 0 || ftruncate(m_fd, static_cast(dwNewLen)) != 0) + CFileException::ThrowErrno(m_fd < 0 ? EBADF : errno, m_strFileName); +} + +ULONGLONG CFile::GetPosition() const { + off_t pos = m_fd >= 0 ? lseek(m_fd, 0, SEEK_CUR) : -1; + if (pos < 0) + AfxThrowFileException(CFileException::badSeek, 131, m_strFileName); + return static_cast(pos); +} + +void CFile::Flush() {} + +CString CFile::GetFileName() const { return FileNamePart(m_strFileName); } + +CString CFile::GetFileTitle() const { return StripExtension(GetFileName()); } + +BOOL CFile::GetStatus(CFileStatus& rStatus) const { + memset(&rStatus, 0, sizeof rStatus); + struct stat st; + if (m_fd < 0 || fstat(m_fd, &st) != 0) + return FALSE; + FillStatus(st, NativePath(m_strFileName), rStatus); + strncpy(rStatus.m_szFullName, m_strFileName.GetString(), sizeof rStatus.m_szFullName - 1); + return TRUE; +} + +BOOL CFile::GetStatus(const char* lpszFileName, CFileStatus& rStatus) { + memset(&rStatus, 0, sizeof rStatus); + if (!lpszFileName || !*lpszFileName) + return FALSE; + std::string path = FsPath(lpszFileName); + struct stat st; + if (stat(path.c_str(), &st) != 0) + return FALSE; + FillStatus(st, path, rStatus); + strncpy(rStatus.m_szFullName, FullAppPath(lpszFileName).c_str(), sizeof rStatus.m_szFullName - 1); + return TRUE; +} + +void CFile::SetStatus(const char* lpszFileName, const CFileStatus& status) { + std::string path = FsPath(lpszFileName); + struct stat st; + if (stat(path.c_str(), &st) != 0) + CFileException::ThrowErrno(errno, lpszFileName); + if (status.m_mtime != 0) { + struct timeval tv[2]; + tv[0].tv_sec = status.m_atime ? status.m_atime : status.m_mtime; + tv[0].tv_usec = 0; + tv[1].tv_sec = status.m_mtime; + tv[1].tv_usec = 0; + if (utimes(path.c_str(), tv) != 0) + CFileException::ThrowErrno(errno, lpszFileName); + } + mode_t mode = st.st_mode & 07777; + mode_t wanted = (status.m_attribute & readOnly) ? mode & ~static_cast(S_IWUSR | S_IWGRP | S_IWOTH) + : mode | S_IWUSR; + if (wanted != mode && chmod(path.c_str(), wanted) != 0) + CFileException::ThrowErrno(errno, lpszFileName); +} + +void CFile::Rename(const char* lpszOldName, const char* lpszNewName) { + if (rename(FsPath(lpszOldName).c_str(), FsPath(lpszNewName).c_str()) != 0) + CFileException::ThrowErrno(errno, lpszOldName); +} + +void CFile::Remove(const char* lpszFileName) { + std::string path = FsPath(lpszFileName); + struct stat st; + if (lstat(path.c_str(), &st) == 0 && S_ISDIR(st.st_mode)) + CFileException::ThrowErrno(EACCES, lpszFileName); + if (unlink(path.c_str()) != 0) + CFileException::ThrowErrno(errno, lpszFileName); +} + +CFile* CFile::Duplicate() const { + if (m_fd < 0) + return nullptr; + int fd = fcntl(m_fd, F_DUPFD_CLOEXEC, 0); + if (fd < 0) + CFileException::ThrowErrno(errno, m_strFileName); + auto* file = new CFile; + file->m_fd = fd; + file->m_hFile = HandleFromFd(fd, NativePath(m_strFileName), true); + file->m_bCloseOnDelete = m_bCloseOnDelete; + file->m_strFileName = m_strFileName; + return file; +} + +// --------------------------------------------------------------------------------------------- +// CStdioFile + +CStdioFile::CStdioFile() : m_pStream(nullptr), m_text(true) {} + +CStdioFile::CStdioFile(FILE* pOpenStream) : m_pStream(pOpenStream), m_text(true) { + if (pOpenStream) { + m_fd = fileno(pOpenStream); + m_hFile = HandleFromFd(m_fd, std::string(), false); + } +} + +CStdioFile::CStdioFile(const char* lpszFileName, UINT nOpenFlags) : CStdioFile() { + CFileException e; + if (!Open(lpszFileName, nOpenFlags, &e)) + AfxThrowFileException(e.m_cause, e.m_lOsError, e.m_strFileName); +} + +CStdioFile::~CStdioFile() { + if (m_pStream && m_bCloseOnDelete) { + Abort(); + } else if (m_hFile != hFileNull) { + CloseHandle(m_hFile); + m_hFile = hFileNull; + } +} + +BOOL CStdioFile::Open(const char* lpszFileName, UINT nOpenFlags, CFileException* pError) { + if (m_pStream) + Abort(); + m_text = !(nOpenFlags & typeBinary); + if (!CFile::Open(lpszFileName, nOpenFlags & ~(typeText | typeBinary), pError)) + return FALSE; + char mode[4]; + int n = 0; + if (nOpenFlags & modeCreate) + mode[n++] = (nOpenFlags & modeNoTruncate) ? 'a' : 'w'; + else if (nOpenFlags & modeWrite) + mode[n++] = 'a'; + else + mode[n++] = 'r'; + if ((mode[0] == 'r' && (nOpenFlags & modeReadWrite)) || (mode[0] != 'r' && !(nOpenFlags & modeWrite))) + mode[n++] = '+'; + mode[n] = 0; + // The stream takes its own descriptor; the kernel file object keeps a non-owning view of it. + int fd = fcntl(m_fd, F_DUPFD_CLOEXEC, 0); + FILE* stream = fd >= 0 ? fdopen(fd, mode) : nullptr; + if (!stream) { + int err = errno; + if (fd >= 0) + close(fd); + CString name = m_strFileName; + CFile::Abort(); + if (pError) { + pError->m_cause = CFileException::ErrnoToException(err); + pError->m_lOsError = static_cast(ErrnoToWin32(err)); + pError->m_strFileName = lpszFileName; + } + return FALSE; + } + std::string native = FsPath(lpszFileName); + CloseHandle(m_hFile); + m_pStream = stream; + m_fd = fd; + m_hFile = HandleFromFd(fd, native, false); + return TRUE; +} + +void CStdioFile::Close() { + int rc = 0; + if (m_pStream) + rc = fclose(m_pStream); + m_pStream = nullptr; + CString name = m_strFileName; + CFile::Abort(); + if (rc != 0) + AfxThrowFileException(CFileException::diskFull, static_cast(ErrnoToWin32(errno)), name); +} + +void CStdioFile::Abort() { + if (m_pStream) + fclose(m_pStream); + m_pStream = nullptr; + CFile::Abort(); +} + +UINT CStdioFile::Read(void* lpBuf, UINT nCount) { + if (nCount == 0) + return 0; + if (!m_pStream) + AfxThrowFileException(CFileException::invalidFile, ERROR_INVALID_HANDLE, m_strFileName); + size_t n = fread(lpBuf, 1, nCount, m_pStream); + if (n == 0 && ferror(m_pStream)) { + clearerr(m_pStream); + CFileException::ThrowErrno(errno, m_strFileName); + } + return static_cast(n); +} + +void CStdioFile::Write(const void* lpBuf, UINT nCount) { + if (!m_pStream) + AfxThrowFileException(CFileException::invalidFile, ERROR_INVALID_HANDLE, m_strFileName); + if (nCount && fwrite(lpBuf, 1, nCount, m_pStream) != nCount) + CFileException::ThrowErrno(errno ? errno : ENOSPC, m_strFileName); +} + +ULONGLONG CStdioFile::Seek(LONGLONG lOff, UINT nFrom) { + int whence = nFrom == current ? SEEK_CUR : nFrom == end ? SEEK_END : SEEK_SET; + if (!m_pStream || fseeko(m_pStream, static_cast(lOff), whence) != 0) + AfxThrowFileException(CFileException::badSeek, 131, m_strFileName); + return static_cast(ftello(m_pStream)); +} + +ULONGLONG CStdioFile::GetLength() const { + if (!m_pStream) + AfxThrowFileException(CFileException::invalidFile, ERROR_INVALID_HANDLE, m_strFileName); + off_t pos = ftello(m_pStream); + fseeko(m_pStream, 0, SEEK_END); + off_t len = ftello(m_pStream); + fseeko(m_pStream, pos, SEEK_SET); + return static_cast(len < 0 ? 0 : len); +} + +ULONGLONG CStdioFile::GetPosition() const { + off_t pos = m_pStream ? ftello(m_pStream) : -1; + if (pos < 0) + AfxThrowFileException(CFileException::badSeek, 131, m_strFileName); + return static_cast(pos); +} + +void CStdioFile::Flush() { + if (m_pStream && fflush(m_pStream) != 0) + AfxThrowFileException(CFileException::diskFull, static_cast(ErrnoToWin32(errno)), m_strFileName); +} + +void CStdioFile::WriteString(const char* lpsz) { + if (!m_pStream) + AfxThrowFileException(CFileException::invalidFile, ERROR_INVALID_HANDLE, m_strFileName); + if (lpsz && fputs(lpsz, m_pStream) == EOF) + AfxThrowFileException(CFileException::diskFull, static_cast(ErrnoToWin32(errno)), m_strFileName); +} + +char* CStdioFile::ReadString(char* lpsz, UINT nMax) { + if (!m_pStream || !lpsz || nMax == 0) + return nullptr; + char* r = fgets(lpsz, static_cast(nMax), m_pStream); + if (!r) { + if (ferror(m_pStream)) { + clearerr(m_pStream); + CFileException::ThrowErrno(errno, m_strFileName); + } + return nullptr; + } + size_t len = strlen(lpsz); + if (m_text && len >= 2 && lpsz[len - 2] == '\r' && lpsz[len - 1] == '\n') { + lpsz[len - 2] = '\n'; + lpsz[len - 1] = 0; + } + return lpsz; +} + +BOOL CStdioFile::ReadString(CString& rString) { + rString.Empty(); + if (!m_pStream) + return FALSE; + std::string line; + bool any = false; + int c; + while ((c = getc(m_pStream)) != EOF) { + any = true; + if (c == '\n') + break; + line += static_cast(c); + } + if (!any) { + if (ferror(m_pStream)) { + clearerr(m_pStream); + CFileException::ThrowErrno(errno, m_strFileName); + } + return FALSE; + } + if (m_text && !line.empty() && line.back() == '\r') + line.pop_back(); + rString = CString(line.data(), static_cast(line.size())); + return TRUE; +} + +// --------------------------------------------------------------------------------------------- +// CMemFile + +CMemFile::CMemFile(UINT nGrowBytes) + : m_lpBuffer(nullptr), m_nBufferSize(0), m_nFileSize(0), m_nPosition(0), m_nGrowBytes(nGrowBytes), + m_bAutoDelete(true) {} + +CMemFile::CMemFile(BYTE* lpBuffer, UINT nBufferSize, UINT nGrowBytes) : CMemFile(nGrowBytes) { + Attach(lpBuffer, nBufferSize, nGrowBytes); +} + +CMemFile::~CMemFile() { Close(); } + +void CMemFile::Attach(BYTE* lpBuffer, UINT nBufferSize, UINT nGrowBytes) { + Close(); + m_lpBuffer = lpBuffer; + m_nBufferSize = nBufferSize; + m_nFileSize = nGrowBytes == 0 ? nBufferSize : 0; + m_nGrowBytes = nGrowBytes; + m_nPosition = 0; + m_bAutoDelete = false; +} + +BYTE* CMemFile::Detach() { + BYTE* buffer = m_lpBuffer; + m_lpBuffer = nullptr; + m_nBufferSize = 0; + m_nFileSize = 0; + m_nPosition = 0; + return buffer; +} + +void CMemFile::Grow(size_t size) { + if (size <= m_nBufferSize) + return; + if (m_nGrowBytes == 0) + AfxThrowMemoryException(); + size_t n = m_nBufferSize; + while (n < size) + n += m_nGrowBytes; + auto* p = static_cast(realloc(m_lpBuffer, n)); + if (!p) + AfxThrowMemoryException(); + m_lpBuffer = p; + m_nBufferSize = n; +} + +void CMemFile::Close() { + if (m_lpBuffer && m_bAutoDelete) + free(m_lpBuffer); + m_lpBuffer = nullptr; + m_nBufferSize = 0; + m_nFileSize = 0; + m_nPosition = 0; +} + +UINT CMemFile::Read(void* lpBuf, UINT nCount) { + if (m_nPosition >= m_nFileSize || nCount == 0) + return 0; + size_t n = std::min(static_cast(nCount), m_nFileSize - m_nPosition); + memcpy(lpBuf, m_lpBuffer + m_nPosition, n); + m_nPosition += n; + return static_cast(n); +} + +void CMemFile::Write(const void* lpBuf, UINT nCount) { + if (nCount == 0) + return; + size_t endPos = m_nPosition + nCount; + if (endPos > m_nBufferSize) + Grow(endPos); + if (m_nPosition > m_nFileSize) + memset(m_lpBuffer + m_nFileSize, 0, m_nPosition - m_nFileSize); + memcpy(m_lpBuffer + m_nPosition, lpBuf, nCount); + m_nPosition = endPos; + if (m_nPosition > m_nFileSize) + m_nFileSize = m_nPosition; +} + +ULONGLONG CMemFile::Seek(LONGLONG lOff, UINT nFrom) { + LONGLONG base = nFrom == current ? static_cast(m_nPosition) + : nFrom == end ? static_cast(m_nFileSize) + : 0; + LONGLONG pos = base + lOff; + if (pos < 0) + AfxThrowFileException(CFileException::badSeek, 131, m_strFileName); + m_nPosition = static_cast(pos); + return static_cast(pos); +} + +void CMemFile::SetLength(ULONGLONG dwNewLen) { + size_t len = static_cast(dwNewLen); + if (len > m_nBufferSize) + Grow(len); + if (len > m_nFileSize) + memset(m_lpBuffer + m_nFileSize, 0, len - m_nFileSize); + if (len < m_nPosition) + m_nPosition = len; + m_nFileSize = len; +} + +// --------------------------------------------------------------------------------------------- +// CFileFind + +struct CFileFind::Impl { + CString root; + std::vector entries; + size_t next = 0; + long current = -1; + const WIN32_FIND_DATA* Current() const { return current >= 0 ? &entries[static_cast(current)] : nullptr; } +}; + +CFileFind::CFileFind() : m_impl(nullptr) {} + +CFileFind::~CFileFind() { Close(); } + +BOOL CFileFind::FindFile(const char* pstrName, DWORD) { + Close(); + const char* pattern = pstrName && *pstrName ? pstrName : "*.*"; + WIN32_FIND_DATA fd; + HANDLE h = ::FindFirstFileA(pattern, &fd); + if (h == INVALID_HANDLE_VALUE) + return FALSE; + m_impl = new Impl; + do + m_impl->entries.push_back(fd); + while (::FindNextFileA(h, &fd)); + ::FindClose(h); + std::string full = FullAppPath(pattern); + size_t sep = full.rfind('\\'); + m_impl->root = sep == std::string::npos ? CString("\\") : CString(full.substr(0, sep + 1).c_str()); + SetLastError(0); + return TRUE; +} + +BOOL CFileFind::FindNextFile() { + if (!m_impl || m_impl->next >= m_impl->entries.size()) + return FALSE; + m_impl->current = static_cast(m_impl->next++); + return m_impl->next < m_impl->entries.size(); +} + +void CFileFind::Close() { + delete m_impl; + m_impl = nullptr; +} + +ULONGLONG CFileFind::GetLength() const { + const WIN32_FIND_DATA* fd = m_impl ? m_impl->Current() : nullptr; + return fd ? (static_cast(fd->nFileSizeHigh) << 32) | fd->nFileSizeLow : 0; +} + +CString CFileFind::GetFileName() const { + const WIN32_FIND_DATA* fd = m_impl ? m_impl->Current() : nullptr; + return fd ? CString(fd->cFileName) : CString(); +} + +CString CFileFind::GetFilePath() const { + if (!m_impl || !m_impl->Current()) + return CString(); + CString path = m_impl->root; + if (!path.IsEmpty() && path[path.GetLength() - 1] != '\\' && path[path.GetLength() - 1] != '/') + path += '\\'; + return path + GetFileName(); +} + +CString CFileFind::GetFileTitle() const { return StripExtension(GetFileName()); } + +CString CFileFind::GetFileURL() const { + CString path = GetFilePath(); + if (path.IsEmpty()) + return path; + path.Replace('\\', '/'); + return CString("file://") + path; +} + +CString CFileFind::GetRoot() const { return m_impl ? m_impl->root : CString(); } + +BOOL CFileFind::IsDots() const { + const WIN32_FIND_DATA* fd = m_impl ? m_impl->Current() : nullptr; + return fd && (strcmp(fd->cFileName, ".") == 0 || strcmp(fd->cFileName, "..") == 0); +} + +BOOL CFileFind::IsDirectory() const { return MatchesMask(FILE_ATTRIBUTE_DIRECTORY); } + +BOOL CFileFind::IsHidden() const { return MatchesMask(FILE_ATTRIBUTE_HIDDEN); } + +BOOL CFileFind::MatchesMask(DWORD dwMask) const { + const WIN32_FIND_DATA* fd = m_impl ? m_impl->Current() : nullptr; + return fd && (fd->dwFileAttributes & dwMask) != 0; +} + +// --------------------------------------------------------------------------------------------- +// CArchive + +CArchive::CArchive(CFile* pFile, UINT nMode, int, void*) + : m_pDocument(nullptr), m_pFile(pFile), m_nMode(nMode), m_nObjectSchema(static_cast(-1)) {} + +CArchive::~CArchive() { + if (m_pFile && !(m_nMode & bNoFlushOnDelete)) + Close(); +} + +BOOL CArchive::IsBufferEmpty() const { + if (!IsLoading() || !m_pFile) + return TRUE; + return m_pFile->GetPosition() >= m_pFile->GetLength(); +} + +void CArchive::Close() { + if (m_pFile && IsStoring()) + m_pFile->Flush(); + m_pFile = nullptr; +} + +UINT CArchive::Read(void* lpBuf, UINT nMax) { + if (!m_pFile || nMax == 0) + return 0; + if (IsStoring()) + ThrowArchive(CArchiveException::writeOnly, m_pFile); + return m_pFile->Read(lpBuf, nMax); +} + +void CArchive::Write(const void* lpBuf, UINT nMax) { + if (!m_pFile || nMax == 0) + return; + if (IsLoading()) + ThrowArchive(CArchiveException::readOnly, m_pFile); + m_pFile->Write(lpBuf, nMax); +} + +namespace { + +template +T ReadValue(CArchive& ar, CFile* file) { + T v{}; + if (ar.Read(&v, sizeof v) != sizeof v) + ThrowArchive(CArchiveException::endOfFile, file); + return v; +} + +} // namespace + +CArchive& CArchive::operator<<(const CString& s) { + DWORD len = static_cast(s.GetLength()); + if (len < 255) { + *this << static_cast(len); + } else if (len < 0xFFFE) { + *this << static_cast(0xFF); + *this << static_cast(len); + } else { + *this << static_cast(0xFF); + *this << static_cast(0xFFFF); + *this << len; + } + Write(s.GetString(), len); + return *this; +} + +CArchive& CArchive::operator>>(CString& s) { + size_t charSize = 1; + // MFC length prefix: BYTE, 0xFF + WORD, 0xFF 0xFFFF + DWORD; 0xFF 0xFFFE marks a UTF-16 string. + auto readLength = [&]() -> ULONGLONG { + BYTE b = ReadValue(*this, m_pFile); + if (b < 0xFF) + return b; + WORD w = ReadValue(*this, m_pFile); + if (w == 0xFFFE) { + charSize = 2; + b = ReadValue(*this, m_pFile); + if (b < 0xFF) + return b; + w = ReadValue(*this, m_pFile); + } + if (w < 0xFFFF) + return w; + DWORD d = ReadValue(*this, m_pFile); + return d < 0xFFFFFFFFu ? d : ReadValue(*this, m_pFile); + }; + ULONGLONG len = readLength(); + if (len > 0x7FFFFFFFULL / charSize) + ThrowArchive(CArchiveException::badIndex, m_pFile); + std::string bytes(static_cast(len * charSize), '\0'); + if (!bytes.empty() && Read(&bytes[0], static_cast(bytes.size())) != bytes.size()) + ThrowArchive(CArchiveException::endOfFile, m_pFile); + if (charSize == 1) { + s = CString(bytes.data(), static_cast(bytes.size())); + return *this; + } + std::string ansi; + for (size_t i = 0; i + 1 < bytes.size(); i += 2) { + uint32_t cp = static_cast(bytes[i]) | (static_cast(bytes[i + 1]) << 8); + if (cp >= 0xD800 && cp < 0xDC00 && i + 3 < bytes.size()) { + uint32_t lo = static_cast(bytes[i + 2]) | (static_cast(bytes[i + 3]) << 8); + if (lo >= 0xDC00 && lo < 0xE000) { + cp = 0x10000 + ((cp - 0xD800) << 10) + (lo - 0xDC00); + i += 2; + } + } + char ch; + UnicodeToAnsi(static_cast(cp), ch); + ansi.push_back(ch); + } + s = CString(ansi.data(), static_cast(ansi.size())); + return *this; +} + +BOOL CArchive::ReadString(CString& rString) { + rString.Empty(); + std::string line; + bool any = false; + char ch; + while (Read(&ch, 1) == 1) { + any = true; + if (ch == '\n') + break; + line += ch; + } + if (!any) + return FALSE; + if (!line.empty() && line.back() == '\r') + line.pop_back(); + rString = CString(line.data(), static_cast(line.size())); + return TRUE; +} + +char* CArchive::ReadString(char* lpsz, UINT nMax) { + if (!lpsz) + return nullptr; + UINT nRead = 0; + char ch; + while (nRead < nMax) { + if (Read(&ch, 1) != 1) { + if (nRead == 0) + return nullptr; + break; + } + if (ch == '\n' || ch == '\r') { + if (ch == '\r') + Read(&ch, 1); + break; + } + lpsz[nRead++] = ch; + } + lpsz[nRead] = 0; + return lpsz; +} + +void CArchive::WriteString(const char* lpsz) { + if (lpsz) + Write(lpsz, static_cast(strlen(lpsz))); +} + +UINT CArchive::ReadCount() { + WORD w = ReadValue(*this, m_pFile); + if (w != 0xFFFF) + return w; + DWORD d = ReadValue(*this, m_pFile); + if (d != 0xFFFFFFFFu) + return d; + return static_cast(ReadValue(*this, m_pFile)); +} + +void CArchive::WriteCount(UINT dwCount) { + if (dwCount < 0xFFFF) { + *this << static_cast(dwCount); + } else { + *this << static_cast(0xFFFF); + *this << static_cast(dwCount); + } +} + +void CArchive::WriteClass(const CRuntimeClass* pClassRef) { + auto it = m_storeClasses.find(pClassRef); + if (it != m_storeClasses.end()) { + if (it->second < kBigObjectTag) { + *this << static_cast(kClassTag | it->second); + } else { + *this << kBigObjectTag; + *this << static_cast(kBigClassTag | it->second); + } + return; + } + *this << kNewClassTag; + WORD len = static_cast(strlen(pClassRef->m_lpszClassName)); + *this << static_cast(pClassRef->m_wSchema); + *this << len; + Write(pClassRef->m_lpszClassName, len); + DWORD index = static_cast(1 + m_storeClasses.size() + m_storeObjects.size()); + m_storeClasses[pClassRef] = index; +} + +void CArchive::WriteObject(const CObject* pOb) { + if (!pOb) { + *this << kNullTag; + return; + } + auto it = m_storeObjects.find(pOb); + if (it != m_storeObjects.end()) { + if (it->second < kBigObjectTag) { + *this << static_cast(it->second); + } else { + *this << kBigObjectTag; + *this << static_cast(it->second); + } + return; + } + WriteClass(pOb->GetRuntimeClass()); + DWORD index = static_cast(1 + m_storeClasses.size() + m_storeObjects.size()); + m_storeObjects[pOb] = index; + const_cast(pOb)->Serialize(*this); +} + +CRuntimeClass* CArchive::ReadClass(const CRuntimeClass* pClassRefRequested, UINT* pSchema, DWORD* pObTag) { + WORD wTag = ReadValue(*this, m_pFile); + DWORD obTag; + if (wTag == kBigObjectTag) + obTag = ReadValue(*this, m_pFile); + else + obTag = (static_cast(wTag & kClassTag) << 16) | (wTag & ~kClassTag); + if (!(obTag & kBigClassTag)) { + if (!pObTag) + ThrowArchive(CArchiveException::badIndex, m_pFile); + *pObTag = obTag; + return nullptr; + } + if (m_loadClasses.empty()) { + m_loadClasses.push_back(nullptr); + m_loadObjects.push_back(nullptr); + } + CRuntimeClass* pClass; + UINT schema; + if (wTag == kNewClassTag) { + WORD w = ReadValue(*this, m_pFile); + WORD len = ReadValue(*this, m_pFile); + char name[64]; + if (len >= sizeof name || Read(name, len) != len) + ThrowArchive(CArchiveException::badClass, m_pFile); + name[len] = 0; + pClass = CRuntimeClass::FromName(name); + if (!pClass) + ThrowArchive(CArchiveException::badClass, m_pFile); + schema = w; + if ((pClass->m_wSchema & ~VERSIONABLE_SCHEMA) != schema && !(pClass->m_wSchema & VERSIONABLE_SCHEMA)) + ThrowArchive(CArchiveException::badSchema, m_pFile); + m_loadClasses.push_back(pClass); + m_loadObjects.push_back(nullptr); + } else { + DWORD index = obTag & ~kBigClassTag; + if (index == 0 || index >= m_loadClasses.size() || !m_loadClasses[index]) + ThrowArchive(CArchiveException::badIndex, m_pFile); + pClass = const_cast(m_loadClasses[index]); + schema = pClass->m_wSchema & ~VERSIONABLE_SCHEMA; + } + if (pClassRefRequested && !pClass->IsDerivedFrom(pClassRefRequested)) + ThrowArchive(CArchiveException::badClass, m_pFile); + if (pSchema) + *pSchema = schema; + else + m_nObjectSchema = schema; + if (pObTag) + *pObTag = obTag; + return pClass; +} + +CObject* CArchive::ReadObject(const CRuntimeClass* pClassRefRequested) { + UINT schema = 0; + DWORD obTag = 0; + CRuntimeClass* pClass = ReadClass(pClassRefRequested, &schema, &obTag); + if (m_loadObjects.empty()) { + m_loadClasses.push_back(nullptr); + m_loadObjects.push_back(nullptr); + } + if (!pClass) { + if (obTag == 0) + return nullptr; + if (obTag >= m_loadObjects.size() || !m_loadObjects[obTag]) + ThrowArchive(CArchiveException::badIndex, m_pFile); + CObject* pOb = m_loadObjects[obTag]; + if (pClassRefRequested && !pOb->IsKindOf(pClassRefRequested)) + ThrowArchive(CArchiveException::badClass, m_pFile); + return pOb; + } + CObject* pOb = pClass->CreateObject(); + if (!pOb) + AfxThrowMemoryException(); + m_loadClasses.push_back(nullptr); + m_loadObjects.push_back(pOb); + UINT saved = m_nObjectSchema; + m_nObjectSchema = schema; + pOb->Serialize(*this); + m_nObjectSchema = saved; + return pOb; +} + +void CArchive::SerializeClass(const CRuntimeClass* pClassRef) { + if (IsStoring()) + WriteClass(pClassRef); + else + ReadClass(pClassRef, &m_nObjectSchema); +} diff --git a/port/mfcwx/src/afxmt.cpp b/port/mfcwx/src/afxmt.cpp new file mode 100644 index 000000000..95700d2ca --- /dev/null +++ b/port/mfcwx/src/afxmt.cpp @@ -0,0 +1,137 @@ +#include "afxmt.h" + +#include + +IMPLEMENT_DYNAMIC(CSyncObject, CObject) +IMPLEMENT_DYNAMIC(CCriticalSection, CSyncObject) +IMPLEMENT_DYNAMIC(CMutex, CSyncObject) +IMPLEMENT_DYNAMIC(CSemaphore, CSyncObject) +IMPLEMENT_DYNAMIC(CEvent, CSyncObject) + +CSyncObject::CSyncObject(const char*) : m_hObject(nullptr) {} + +CSyncObject::~CSyncObject() { + if (m_hObject) + CloseHandle(m_hObject); + m_hObject = nullptr; +} + +BOOL CSyncObject::Lock(DWORD dwTimeout) { + DWORD r = WaitForSingleObject(m_hObject, dwTimeout); + return r == WAIT_OBJECT_0 || r == WAIT_ABANDONED; +} + +CCriticalSection::CCriticalSection() : CSyncObject(nullptr) { InitializeCriticalSection(&m_sect); } + +CCriticalSection::~CCriticalSection() { DeleteCriticalSection(&m_sect); } + +BOOL CCriticalSection::Lock(DWORD) { + EnterCriticalSection(&m_sect); + return TRUE; +} + +BOOL CCriticalSection::Unlock() { + LeaveCriticalSection(&m_sect); + return TRUE; +} + +CMutex::CMutex(BOOL bInitiallyOwn, const char* lpszName, LPSECURITY_ATTRIBUTES lpsaAttribute) : CSyncObject(lpszName) { + m_hObject = CreateMutexA(lpsaAttribute, bInitiallyOwn, lpszName); + if (!m_hObject) + AfxThrowResourceException(); +} + +BOOL CMutex::Unlock() { return ReleaseMutex(m_hObject); } + +CSemaphore::CSemaphore(LONG lInitialCount, LONG lMaxCount, const char* pstrName, LPSECURITY_ATTRIBUTES lpsaAttributes) + : CSyncObject(pstrName) { + m_hObject = CreateSemaphoreA(lpsaAttributes, lInitialCount, lMaxCount, pstrName); + if (!m_hObject) + AfxThrowResourceException(); +} + +BOOL CSemaphore::Unlock() { return Unlock(1, nullptr); } + +BOOL CSemaphore::Unlock(LONG lCount, LPLONG lprevCount) { return ReleaseSemaphore(m_hObject, lCount, lprevCount); } + +CEvent::CEvent(BOOL bInitiallyOwn, BOOL bManualReset, const char* lpszName, LPSECURITY_ATTRIBUTES lpsaAttribute) + : CSyncObject(lpszName) { + m_hObject = CreateEventA(lpsaAttribute, bManualReset, bInitiallyOwn, lpszName); + if (!m_hObject) + AfxThrowResourceException(); +} + +BOOL CEvent::SetEvent() { return ::SetEvent(m_hObject); } + +BOOL CEvent::PulseEvent() { return ::PulseEvent(m_hObject); } + +BOOL CEvent::ResetEvent() { return ::ResetEvent(m_hObject); } + +CSingleLock::CSingleLock(CSyncObject* pObject, BOOL bInitialLock) : m_pObject(pObject), m_bAcquired(FALSE) { + if (bInitialLock) + Lock(); +} + +CSingleLock::~CSingleLock() { Unlock(); } + +BOOL CSingleLock::Lock(DWORD dwTimeOut) { + m_bAcquired = m_pObject->Lock(dwTimeOut); + return m_bAcquired; +} + +BOOL CSingleLock::Unlock() { + if (m_bAcquired) + m_bAcquired = !m_pObject->Unlock(); + return !m_bAcquired; +} + +BOOL CSingleLock::Unlock(LONG lCount, LPLONG lPrevCount) { + if (m_bAcquired) + m_bAcquired = !m_pObject->Unlock(lCount, lPrevCount); + return !m_bAcquired; +} + +CMultiLock::CMultiLock(CSyncObject* ppObjects[], DWORD dwCount, BOOL bInitialLock) + : m_objects(ppObjects, ppObjects + dwCount), m_locked(dwCount, false) { + if (bInitialLock) + Lock(); +} + +CMultiLock::~CMultiLock() { Unlock(); } + +DWORD CMultiLock::Lock(DWORD dwTimeOut, BOOL bWaitForAll, DWORD) { + std::vector handles; + std::vector owners; + for (size_t i = 0; i < m_objects.size(); ++i) { + if (m_objects[i]->m_hObject) { + handles.push_back(m_objects[i]->m_hObject); + owners.push_back(i); + } else if (bWaitForAll && !m_locked[i]) { + // Critical sections have no kernel handle; they are entered directly. + m_locked[i] = m_objects[i]->Lock(dwTimeOut) != FALSE; + } + } + if (handles.empty()) + return WAIT_OBJECT_0; + DWORD r = WaitForMultipleObjects(static_cast(handles.size()), handles.data(), bWaitForAll, dwTimeOut); + if (r < WAIT_OBJECT_0 + handles.size()) { + if (bWaitForAll) { + for (size_t owner : owners) + m_locked[owner] = true; + } else { + size_t owner = owners[r - WAIT_OBJECT_0]; + m_locked[owner] = true; + return static_cast(WAIT_OBJECT_0 + owner); + } + } + return r; +} + +BOOL CMultiLock::Unlock() { + for (size_t i = 0; i < m_objects.size(); ++i) + if (m_locked[i]) { + m_objects[i]->Unlock(); + m_locked[i] = false; + } + return TRUE; +} diff --git a/port/mfcwx/src/codepage_tables.cpp b/port/mfcwx/src/codepage_tables.cpp index 77293ded9..fd4a161a5 100644 --- a/port/mfcwx/src/codepage_tables.cpp +++ b/port/mfcwx/src/codepage_tables.cpp @@ -3,6 +3,14 @@ namespace mfcwx { +// Namespace-scope const arrays have internal linkage unless declared extern. +extern const uint16_t kCp1250High[128]; +extern const uint16_t kCp1251High[128]; +extern const uint16_t kCp1252High[128]; +extern const uint16_t kCp1253High[128]; +extern const uint16_t kCp1254High[128]; +extern const uint16_t kCp1257High[128]; + const uint16_t kCp1250High[128] = { 0x20AC, 0x0081, 0x201A, 0x0083, 0x201E, 0x2026, 0x2020, 0x2021, 0x0088, 0x2030, 0x0160, 0x2039, 0x015A, 0x0164, 0x017D, 0x0179, diff --git a/port/mfcwx/src/controls.cpp b/port/mfcwx/src/controls.cpp index c7c6677d5..68904ee21 100644 --- a/port/mfcwx/src/controls.cpp +++ b/port/mfcwx/src/controls.cpp @@ -1,5 +1,6 @@ -#include "windows_impl.h" +#include "controls_internal.h" +#include #include #include #include @@ -49,6 +50,9 @@ struct RangeExtra { int step = 10; int pos = 0; bool inverted = false; + int page = 0; + int selStart = 0; + int selEnd = 0; }; struct ButtonExtra { @@ -58,20 +62,6 @@ struct ButtonExtra { HICON icon = nullptr; }; -struct TabExtra { - std::vector> items; - int current = -1; -}; - -template -T& Extra(WindowState& st) { - if (!st.extra) { - st.extra = new T(); - st.deleteExtra = [&st] { delete static_cast(st.extra); st.extra = nullptr; }; - } - return *static_cast(st.extra); -} - wxString AnsiText(const char* s) { return ToWx(s ? s : ""); } bool IsMultilineEdit(const WindowState& st) { @@ -372,6 +362,33 @@ bool FilterEditChar(wxWindow* w, WindowState& st, UINT& ch, wxKeyEvent& e) { return false; } +// --------------------------------------------------------------------------------------------- +// Tab control geometry + +int TabHeaderHeight(wxWindow* w) { + WindowState* st = GetState(w); + if (st && Extra(*st).itemSize.y > 0) + return Extra(*st).itemSize.y; + return w->GetCharHeight() + 8; +} + +std::vector TabRects(wxWindow* w) { + std::vector rects; + WindowState* st = GetState(w); + if (!st) + return rects; + TabExtra& tx = Extra(*st); + int tabH = TabHeaderHeight(w); + int x = 2; + for (const auto& item : tx.items) { + int tw = tx.itemSize.x > 0 ? tx.itemSize.x + : w->GetTextExtent(wxStripMenuCodes(item.first, wxStrip_Mnemonics)).x + 16; + rects.emplace_back(x, 0, tw, tabH); + x += tw; + } + return rects; +} + // --------------------------------------------------------------------------------------------- // Owner-drawn buttons and statics @@ -564,17 +581,20 @@ void PaintTabs(wxWindow* w, wxPaintEvent&) { TabExtra& tx = Extra(*st); dc.SetFont(w->GetFont()); wxSize sz = w->GetClientSize(); - int tabH = dc.GetCharHeight() + 8; + int tabH = TabHeaderHeight(w); wxColour shadow = wxSystemSettings::GetColour(wxSYS_COLOUR_3DSHADOW); dc.SetPen(wxPen(shadow)); dc.SetBrush(*wxTRANSPARENT_BRUSH); dc.DrawRectangle(0, tabH - 1, sz.x, sz.y - tabH + 1); - int x = 2; - for (size_t i = 0; i < tx.items.size(); ++i) { + std::vector rects = TabRects(w); + for (size_t i = 0; i < rects.size() && i < tx.items.size(); ++i) { wxString label = wxStripMenuCodes(tx.items[i].first, wxStrip_Mnemonics); - int tw = dc.GetTextExtent(label).x + 16; bool sel = static_cast(i) == tx.current; - wxRect r(x, sel ? 0 : 2, tw, tabH - (sel ? 0 : 2)); + wxRect r = rects[i]; + if (!sel) { + r.y += 2; + r.height -= 2; + } dc.SetBrush(sel ? wxBrush(w->GetBackgroundColour()) : wxBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE))); dc.SetPen(wxPen(shadow)); dc.DrawRectangle(r); @@ -584,27 +604,14 @@ void PaintTabs(wxWindow* w, wxPaintEvent&) { } dc.SetTextForeground(w->GetForegroundColour()); dc.DrawLabel(label, r, wxALIGN_CENTER); - x += tw; } } int TabHitTest(wxWindow* w, const wxPoint& p) { - WindowState* st = GetState(w); - if (!st) - return -1; - TabExtra& tx = Extra(*st); - wxClientDC dc(w); - dc.SetFont(w->GetFont()); - int tabH = dc.GetCharHeight() + 8; - if (p.y > tabH) - return -1; - int x = 2; - for (size_t i = 0; i < tx.items.size(); ++i) { - int tw = dc.GetTextExtent(wxStripMenuCodes(tx.items[i].first, wxStrip_Mnemonics)).x + 16; - if (p.x >= x && p.x < x + tw) + std::vector rects = TabRects(w); + for (size_t i = 0; i < rects.size(); ++i) + if (rects[i].Contains(p)) return static_cast(i); - x += tw; - } return -1; } diff --git a/port/mfcwx/src/controls_internal.h b/port/mfcwx/src/controls_internal.h new file mode 100644 index 000000000..4941cb8f1 --- /dev/null +++ b/port/mfcwx/src/controls_internal.h @@ -0,0 +1,42 @@ +#pragma once + +// Control internals shared by controls.cpp, listctrl.cpp and ctrlclasses.cpp. + +#include "windows_impl.h" + +#include +#include +#include + +class wxTextCtrl; + +namespace mfcwx { + +// Kind-specific data of a control, kept in WindowState::extra. Each control kind uses one type. +template +T& Extra(WindowState& st) { + if (!st.extra) { + st.extra = new T(); + st.deleteExtra = [&st] { delete static_cast(st.extra); st.extra = nullptr; }; + } + return *static_cast(st.extra); +} + +struct TabExtra { + std::vector> items; + int current = -1; + wxSize itemSize; +}; + +// controls.cpp: tab header geometry in client coordinates. +int TabHeaderHeight(wxWindow* w); +std::vector TabRects(wxWindow* w); + +// listctrl.cpp +void BindListViewEvents(wxWindow* w); +LRESULT ListViewProc(wxWindow* w, WindowState& st, UINT msg, WPARAM wParam, LPARAM lParam, bool& handled); + +// ctrlclasses.cpp: inserts RTF (ANSI code page) at the selection of a rich edit control. +void InsertRtf(wxTextCtrl* t, bool multiline, const std::string& rtf); + +} // namespace mfcwx diff --git a/port/mfcwx/src/crt.cpp b/port/mfcwx/src/crt.cpp new file mode 100644 index 000000000..dda026c24 --- /dev/null +++ b/port/mfcwx/src/crt.cpp @@ -0,0 +1,801 @@ +#include "afx.h" +#include "runtime.h" + +#include +#include +#include +#include + +#include +#include +#include +#ifdef __APPLE__ +#include +#else +#include +#endif + +#undef fopen +#undef rename +#undef remove + +using namespace mfcwx; + +namespace { + +char* FormatUnsigned(unsigned long long v, char* buf, int radix, bool negative) { + if (!buf) + return buf; + if (radix < 2 || radix > 36) { + buf[0] = 0; + errno = EINVAL; + return buf; + } + char tmp[72]; + int n = 0; + do { + int d = static_cast(v % static_cast(radix)); + tmp[n++] = static_cast(d < 10 ? '0' + d : 'a' + d - 10); + v /= static_cast(radix); + } while (v); + char* p = buf; + if (negative) + *p++ = '-'; + while (n) + *p++ = tmp[--n]; + *p = 0; + return buf; +} + +char* FormatSigned(long long value, char* buf, int radix, unsigned long long unsignedValue) { + if (radix == 10 && value < 0) + return FormatUnsigned(0ULL - static_cast(value), buf, 10, true); + return FormatUnsigned(unsignedValue, buf, radix, false); +} + +// MSVC's long is 32 bits: negative values print as 32-bit two's complement in non-decimal radixes. +unsigned long long LongBits(long value) { + if (value < 0 && value >= INT32_MIN) + return static_cast(value); + return static_cast(value); +} + +int CopyChecked(const std::string& s, char* buf, size_t size) { + if (!buf || size == 0) + return EINVAL; + if (s.size() + 1 > size) { + buf[0] = 0; + return ERANGE; + } + memcpy(buf, s.c_str(), s.size() + 1); + return 0; +} + +thread_local char t_cvtBuffer[400]; + +int FilterFopenMode(const char* mode, char* out, size_t size) { + size_t o = 0; + for (const char* p = mode ? mode : "r"; *p && *p != ',' && o + 1 < size; ++p) + if (strchr("rwa+bx", *p)) + out[o++] = *p; + out[o] = 0; + return static_cast(o); +} + +void FillFindData(const WIN32_FIND_DATA& fd, struct _finddata_t* data) { + memset(data, 0, sizeof *data); + data->attrib = fd.dwFileAttributes & (_A_RDONLY | _A_HIDDEN | _A_SYSTEM | _A_SUBDIR | _A_ARCH); + data->time_create = FileTimeToUnix(fd.ftCreationTime); + data->time_access = FileTimeToUnix(fd.ftLastAccessTime); + data->time_write = FileTimeToUnix(fd.ftLastWriteTime); + data->size = (static_cast(fd.nFileSizeHigh) << 32) | fd.nFileSizeLow; + strncpy(data->name, fd.cFileName, sizeof data->name - 1); +} + +} // namespace + +int mfcwx_open(const char* name, int flags, ...) { + mode_t mode = 0666; + if (flags & O_CREAT) { + va_list ap; + va_start(ap, flags); + int requested = va_arg(ap, int); + va_end(ap); + if (requested & ~(S_IRUSR | S_IWUSR)) + mode = static_cast(requested); + else if (!(requested & S_IWUSR)) + mode = 0444; + } + return open(FsPath(name).c_str(), flags, mode); +} + +int mfcwx_unlink(const char* name) { return unlink(FsPath(name).c_str()); } + +int mfcwx_rmdir(const char* name) { return rmdir(FsPath(name).c_str()); } + +int mfcwx_chdir(const char* name) { return chdir(FsPath(name).c_str()); } + +int mfcwx_access(const char* name, int mode) { return access(FsPath(name).c_str(), mode & 6); } + +int mfcwx_chmod(const char* name, int mode) { + std::string path = FsPath(name); + struct stat st; + if (stat(path.c_str(), &st) != 0) + return -1; + mode_t m = st.st_mode & 07777; + if (mode & S_IWUSR) + m |= S_IWUSR; + else + m &= ~static_cast(S_IWUSR | S_IWGRP | S_IWOTH); + return chmod(path.c_str(), m); +} + +int mfcwx_rename(const char* from, const char* to) { return rename(FsPath(from).c_str(), FsPath(to).c_str()); } + +FILE* mfcwx_fopen(const char* name, const char* mode) { + if (!name) { + errno = EINVAL; + return nullptr; + } + char m[16]; + FilterFopenMode(mode, m, sizeof m); + return fopen(FsPath(name).c_str(), m); +} + +size_t mfcwx_msize(void* p) { +#ifdef __APPLE__ + return p ? malloc_size(p) : 0; +#else + return p ? malloc_usable_size(p) : 0; +#endif +} + +unsigned int mfcwx_rotl(unsigned int v, int s) { + s &= 31; + return s ? (v << s) | (v >> (32 - s)) : v; +} + +unsigned int mfcwx_rotr(unsigned int v, int s) { + s &= 31; + return s ? (v >> s) | (v << (32 - s)) : v; +} + +void mfcwx_normalize_path(char* path) { + if (!path) + return; + for (char* p = path; *p; ++p) + if (*p == '\\') + *p = '/'; + if (path[0] && path[1] == ':' && isalpha(static_cast(path[0]))) + memmove(path, path + 2, strlen(path + 2) + 1); +} + +char* mfcwx_getcwd(char* buf, int size) { + char native[PATH_MAX]; + if (!getcwd(native, sizeof native)) + return nullptr; + std::string app = AppPath(native); + if (!buf) { + size_t bytes = std::max(static_cast(size > 0 ? size : 0), app.size() + 1); + buf = static_cast(malloc(bytes)); + if (!buf) { + errno = ENOMEM; + return nullptr; + } + } else if (size <= 0 || app.size() + 1 > static_cast(size)) { + errno = ERANGE; + return nullptr; + } + memcpy(buf, app.c_str(), app.size() + 1); + return buf; +} + +int _mkdir(const char* path) { return mkdir(FsPath(path).c_str(), 0777); } + +char* _strupr(char* s) { + for (char* p = s; p && *p; ++p) + if (*p >= 'a' && *p <= 'z') + *p = static_cast(*p - 'a' + 'A'); + return s; +} + +char* _strlwr(char* s) { + for (char* p = s; p && *p; ++p) + if (*p >= 'A' && *p <= 'Z') + *p = static_cast(*p - 'A' + 'a'); + return s; +} + +char* _strrev(char* s) { + if (s) + std::reverse(s, s + strlen(s)); + return s; +} + +char* strupr(char* s) { return _strupr(s); } + +char* strlwr(char* s) { return _strlwr(s); } + +char* strrev(char* s) { return _strrev(s); } + +char* _strset(char* s, int c) { + for (char* p = s; p && *p; ++p) + *p = static_cast(c); + return s; +} + +char* _strnset(char* s, int c, size_t n) { + for (char* p = s; p && *p && n; ++p, --n) + *p = static_cast(c); + return s; +} + +char* _itoa(int value, char* buf, int radix) { return FormatSigned(value, buf, radix, static_cast(value)); } + +char* _ltoa(long value, char* buf, int radix) { return FormatSigned(value, buf, radix, LongBits(value)); } + +char* _ultoa(unsigned long value, char* buf, int radix) { return FormatUnsigned(value, buf, radix, false); } + +char* _i64toa(long long value, char* buf, int radix) { + return FormatSigned(value, buf, radix, static_cast(value)); +} + +char* _ui64toa(unsigned long long value, char* buf, int radix) { return FormatUnsigned(value, buf, radix, false); } + +char* itoa(int value, char* buf, int radix) { return _itoa(value, buf, radix); } + +char* ltoa(long value, char* buf, int radix) { return _ltoa(value, buf, radix); } + +char* ultoa(unsigned long value, char* buf, int radix) { return _ultoa(value, buf, radix); } + +char* _gcvt(double value, int digits, char* buf) { + if (!buf) + return buf; + if (digits < 1) + digits = 1; + char tmp[400]; + snprintf(tmp, sizeof tmp, "%.*g", digits, value); + std::string s = tmp; + if (std::isfinite(value) && s.find('.') == std::string::npos) { + size_t e = s.find('e'); + if (e == std::string::npos) + s += '.'; + else + s.insert(e, "."); + } + strcpy(buf, s.c_str()); + return buf; +} + +char* gcvt(double value, int digits, char* buf) { return _gcvt(value, digits, buf); } + +char* _ecvt(double value, int digits, int* dec, int* sign) { + char* out = t_cvtBuffer; + int dummyDec, dummySign; + if (!dec) + dec = &dummyDec; + if (!sign) + sign = &dummySign; + *sign = std::signbit(value) ? 1 : 0; + value = std::fabs(value); + if (digits > 340) + digits = 340; + if (!std::isfinite(value)) { + strcpy(out, std::isnan(value) ? "1#QNAN" : "1#INF"); + *dec = 1; + return out; + } + if (digits <= 0) { + out[0] = 0; + *dec = value == 0 ? 0 : static_cast(std::floor(std::log10(value))) + 1; + return out; + } + if (value == 0) { + memset(out, '0', static_cast(digits)); + out[digits] = 0; + *dec = 0; + return out; + } + char tmp[400]; + snprintf(tmp, sizeof tmp, "%.*e", digits - 1, value); + int o = 0; + const char* p = tmp; + for (; *p && *p != 'e'; ++p) + if (*p != '.') + out[o++] = *p; + out[o] = 0; + *dec = (*p == 'e' ? atoi(p + 1) : 0) + 1; + return out; +} + +char* _fcvt(double value, int digits, int* dec, int* sign) { + char* out = t_cvtBuffer; + int dummyDec, dummySign; + if (!dec) + dec = &dummyDec; + if (!sign) + sign = &dummySign; + *sign = std::signbit(value) ? 1 : 0; + value = std::fabs(value); + if (digits < 0) + digits = 0; + if (!std::isfinite(value)) { + strcpy(out, std::isnan(value) ? "1#QNAN" : "1#INF"); + *dec = 1; + return out; + } + char tmp[400]; + if (value >= 1e300) { + snprintf(tmp, sizeof tmp, "%.0f", value); + digits = 0; + } else { + snprintf(tmp, sizeof tmp, "%.*f", std::min(digits, 60), value); + } + std::string s = tmp; + size_t point = s.find('.'); + std::string intPart = s.substr(0, point); + std::string frac = point == std::string::npos ? std::string() : s.substr(point + 1); + std::string all = intPart + frac; + int d = static_cast(intPart.size()); + size_t lead = 0; + while (lead < all.size() && all[lead] == '0') + ++lead; + if (lead == all.size()) { + std::string zeros(static_cast(std::min(digits, 60)), '0'); + strcpy(out, zeros.c_str()); + *dec = 0; + return out; + } + all.erase(0, lead); + d -= static_cast(lead); + strncpy(out, all.c_str(), sizeof t_cvtBuffer - 1); + out[sizeof t_cvtBuffer - 1] = 0; + *dec = d; + return out; +} + +long _filelength(int fd) { + struct stat st; + return fstat(fd, &st) == 0 ? static_cast(st.st_size) : -1L; +} + +long filelength(int fd) { return _filelength(fd); } + +long long _filelengthi64(int fd) { + struct stat st; + return fstat(fd, &st) == 0 ? static_cast(st.st_size) : -1LL; +} + +int _chsize(int fd, long size) { return ftruncate(fd, size); } + +int _eof(int fd) { + struct stat st; + off_t pos = lseek(fd, 0, SEEK_CUR); + if (pos < 0 || fstat(fd, &st) != 0) + return -1; + return pos >= st.st_size ? 1 : 0; +} + +int _setmode(int fd, int) { return fcntl(fd, F_GETFL) == -1 ? -1 : 0x8000; } + +void _splitpath(const char* path, char* drive, char* dir, char* fname, char* ext) { + std::string p = path ? path : ""; + std::string d, rest; + if (p.size() >= 2 && p[1] == ':') { + d = p.substr(0, 2); + p.erase(0, 2); + } + size_t sep = p.find_last_of("\\/"); + std::string directory = sep == std::string::npos ? std::string() : p.substr(0, sep + 1); + rest = sep == std::string::npos ? p : p.substr(sep + 1); + size_t dot = rest.rfind('.'); + std::string name = dot == std::string::npos ? rest : rest.substr(0, dot); + std::string extension = dot == std::string::npos ? std::string() : rest.substr(dot); + auto put = [](char* out, const std::string& s, size_t max) { + if (!out) + return; + size_t n = std::min(s.size(), max - 1); + memcpy(out, s.data(), n); + out[n] = 0; + }; + put(drive, d, _MAX_DRIVE); + put(dir, directory, _MAX_DIR); + put(fname, name, _MAX_FNAME); + put(ext, extension, _MAX_EXT); +} + +void _makepath(char* path, const char* drive, const char* dir, const char* fname, const char* ext) { + if (!path) + return; + std::string out; + if (drive && *drive) { + out += drive[0]; + out += ':'; + } + if (dir && *dir) { + out += dir; + if (out.back() != '\\' && out.back() != '/') + out += '\\'; + } + if (fname) + out += fname; + if (ext && *ext) { + if (ext[0] != '.') + out += '.'; + out += ext; + } + size_t n = std::min(out.size(), static_cast(_MAX_PATH - 1)); + memcpy(path, out.data(), n); + path[n] = 0; +} + +char* _fullpath(char* abs, const char* rel, size_t size) { + std::string full = FullAppPath(rel && *rel ? rel : "."); + if (rel && *rel) { + char last = rel[strlen(rel) - 1]; + if ((last == '\\' || last == '/') && full.back() != '\\') + full += '\\'; + } + if (!abs) { + abs = static_cast(malloc(std::max(static_cast(_MAX_PATH), full.size() + 1))); + if (!abs) { + errno = ENOMEM; + return nullptr; + } + } else if (full.size() + 1 > size) { + errno = ERANGE; + return nullptr; + } + memcpy(abs, full.c_str(), full.size() + 1); + return abs; +} + +int _getdrive(void) { return 3; } + +int _chdrive(int drive) { return drive >= 1 && drive <= 26 ? 0 : -1; } + +char* _mktemp(char* templ) { + if (!templ) + return nullptr; + size_t len = strlen(templ); + if (len < 6 || strcmp(templ + len - 6, "XXXXXX") != 0) { + errno = EINVAL; + return nullptr; + } + char* x = templ + len - 6; + char digits[8]; + snprintf(digits, sizeof digits, "%05u", static_cast(getpid() % 100000)); + for (char c = 'a'; c <= 'z'; ++c) { + x[0] = c; + memcpy(x + 1, digits, 5); + if (access(FsPath(templ).c_str(), F_OK) != 0) + return templ; + } + memcpy(x, "XXXXXX", 6); + errno = EEXIST; + return nullptr; +} + +char* _tempnam(const char* dir, const char* prefix) { + std::string base; + struct stat st; + if (dir && *dir && stat(FsPath(dir).c_str(), &st) == 0 && S_ISDIR(st.st_mode)) + base = dir; + else + base = AppPath(TempDirectory().c_str()); + while (base.size() > 1 && (base.back() == '\\' || base.back() == '/')) + base.pop_back(); + static std::atomic counter{1}; + for (int attempt = 0; attempt < 100000; ++attempt) { + unsigned n = counter.fetch_add(1); + std::string name = base + "\\" + (prefix ? prefix : "") + std::to_string(n); + if (access(FsPath(name.c_str()).c_str(), F_OK) != 0) + return strdup(name.c_str()); + } + errno = EEXIST; + return nullptr; +} + +int _vsnprintf_s(char* buf, size_t size, size_t count, const char* fmt, va_list args) { + if (!buf || size == 0 || !fmt) { + errno = EINVAL; + return -1; + } + char translated[4096]; + fmt = AfxTranslateFormat(fmt, translated, sizeof translated); + va_list copy; + va_copy(copy, args); + int n = vsnprintf(nullptr, 0, fmt, copy); + va_end(copy); + if (n < 0) { + buf[0] = 0; + return -1; + } + size_t needed = static_cast(n); + if (count == static_cast(-1)) { + vsnprintf(buf, size, fmt, args); + return needed >= size ? -1 : n; + } + size_t limit = std::min(count, size - 1); + if (needed <= limit) { + vsnprintf(buf, size, fmt, args); + return n; + } + if (count < size) { + std::string tmp(needed + 1, '\0'); + vsnprintf(&tmp[0], tmp.size(), fmt, args); + memcpy(buf, tmp.data(), count); + buf[count] = 0; + return -1; + } + buf[0] = 0; + errno = ERANGE; + return -1; +} + +int vsprintf_s(char* buf, size_t size, const char* fmt, va_list args) { + if (!buf || size == 0 || !fmt) { + errno = EINVAL; + return -1; + } + char translated[4096]; + fmt = AfxTranslateFormat(fmt, translated, sizeof translated); + int n = vsnprintf(buf, size, fmt, args); + if (n < 0 || static_cast(n) >= size) { + buf[0] = 0; + errno = ERANGE; + return -1; + } + return n; +} + +int sprintf_s(char* buf, size_t size, const char* fmt, ...) { + va_list args; + va_start(args, fmt); + int n = vsprintf_s(buf, size, fmt, args); + va_end(args); + return n; +} + +int _snprintf_s(char* buf, size_t size, size_t count, const char* fmt, ...) { + va_list args; + va_start(args, fmt); + int n = _vsnprintf_s(buf, size, count, fmt, args); + va_end(args); + return n; +} + +int strcpy_s(char* dst, size_t size, const char* src) { + if (!dst || size == 0) + return EINVAL; + if (!src) { + dst[0] = 0; + return EINVAL; + } + return CopyChecked(src, dst, size); +} + +int strncpy_s(char* dst, size_t size, const char* src, size_t count) { + if (!dst || size == 0) + return EINVAL; + if (!src) { + dst[0] = 0; + return count ? EINVAL : 0; + } + size_t len = strlen(src); + if (count == static_cast(-1)) { + size_t n = std::min(len, size - 1); + memcpy(dst, src, n); + dst[n] = 0; + return n < len ? 80 : 0; + } + size_t n = std::min(len, count); + if (n >= size) { + dst[0] = 0; + return ERANGE; + } + memcpy(dst, src, n); + dst[n] = 0; + return 0; +} + +int strcat_s(char* dst, size_t size, const char* src) { + if (!dst || size == 0) + return EINVAL; + size_t len = strnlen(dst, size); + if (len == size || !src) { + dst[0] = 0; + return EINVAL; + } + if (len + strlen(src) >= size) { + dst[0] = 0; + return ERANGE; + } + strcpy(dst + len, src); + return 0; +} + +int strncat_s(char* dst, size_t size, const char* src, size_t count) { + if (!dst || size == 0) + return EINVAL; + size_t len = strnlen(dst, size); + if (len == size || (!src && count)) { + dst[0] = 0; + return EINVAL; + } + size_t add = src ? strlen(src) : 0; + if (count != static_cast(-1)) + add = std::min(add, count); + bool truncated = false; + if (len + add >= size) { + if (count != static_cast(-1)) { + dst[0] = 0; + return ERANGE; + } + add = size - 1 - len; + truncated = true; + } + memcpy(dst + len, src, add); + dst[len + add] = 0; + return truncated ? 80 : 0; +} + +char* strtok_s(char* s, const char* delim, char** ctx) { return strtok_r(s, delim, ctx); } + +int _strupr_s(char* s, size_t size) { + if (!s || strnlen(s, size) == size) + return EINVAL; + _strupr(s); + return 0; +} + +int _strlwr_s(char* s, size_t size) { + if (!s || strnlen(s, size) == size) + return EINVAL; + _strlwr(s); + return 0; +} + +int _itoa_s(int value, char* buf, size_t size, int radix) { + char tmp[72]; + _itoa(value, tmp, radix); + return CopyChecked(tmp, buf, size); +} + +int _ltoa_s(long value, char* buf, size_t size, int radix) { + char tmp[72]; + _ltoa(value, tmp, radix); + return CopyChecked(tmp, buf, size); +} + +int _i64toa_s(long long value, char* buf, size_t size, int radix) { + char tmp[72]; + _i64toa(value, tmp, radix); + return CopyChecked(tmp, buf, size); +} + +int fopen_s(FILE** f, const char* name, const char* mode) { + if (!f) + return EINVAL; + *f = mfcwx_fopen(name, mode); + return *f ? 0 : errno; +} + +int memcpy_s(void* dst, size_t dstSize, const void* src, size_t count) { + if (count == 0) + return 0; + if (!dst) + return EINVAL; + if (!src || count > dstSize) { + memset(dst, 0, dstSize); + return src ? ERANGE : EINVAL; + } + memcpy(dst, src, count); + return 0; +} + +int memmove_s(void* dst, size_t dstSize, const void* src, size_t count) { + if (count == 0) + return 0; + if (!dst || !src) + return EINVAL; + if (count > dstSize) + return ERANGE; + memmove(dst, src, count); + return 0; +} + +int localtime_s(struct tm* out, const time_t* t) { + if (!out || !t) + return EINVAL; + return localtime_r(t, out) ? 0 : EINVAL; +} + +int gmtime_s(struct tm* out, const time_t* t) { + if (!out || !t) + return EINVAL; + return gmtime_r(t, out) ? 0 : EINVAL; +} + +int asctime_s(char* buf, size_t size, const struct tm* t) { + if (!buf || !t) + return EINVAL; + char tmp[64]; + if (!asctime_r(t, tmp)) + return EINVAL; + return CopyChecked(tmp, buf, size); +} + +int ctime_s(char* buf, size_t size, const time_t* t) { + if (!buf || !t) + return EINVAL; + struct tm tm; + if (!localtime_r(t, &tm)) + return EINVAL; + return asctime_s(buf, size, &tm); +} + +int _dupenv_s(char** buf, size_t* len, const char* name) { + if (!buf || !name) + return EINVAL; + const char* v = getenv(name); + if (!v) { + *buf = nullptr; + if (len) + *len = 0; + return 0; + } + *buf = strdup(v); + if (len) + *len = strlen(v) + 1; + return *buf ? 0 : ENOMEM; +} + +char* _strdate(char* buf) { + time_t now = time(nullptr); + struct tm tm; + localtime_r(&now, &tm); + strftime(buf, 9, "%m/%d/%y", &tm); + return buf; +} + +char* _strtime(char* buf) { + time_t now = time(nullptr); + struct tm tm; + localtime_r(&now, &tm); + strftime(buf, 9, "%H:%M:%S", &tm); + return buf; +} + +void _tzset(void) { tzset(); } + +intptr_t _findfirst(const char* pattern, struct _finddata_t* data) { + WIN32_FIND_DATA fd; + HANDLE h = FindFirstFileA(pattern, &fd); + if (h == INVALID_HANDLE_VALUE) { + errno = ENOENT; + return -1; + } + if (data) + FillFindData(fd, data); + return reinterpret_cast(h); +} + +int _findnext(intptr_t handle, struct _finddata_t* data) { + WIN32_FIND_DATA fd; + if (!FindNextFileA(reinterpret_cast(handle), &fd)) { + errno = ENOENT; + return -1; + } + if (data) + FillFindData(fd, data); + return 0; +} + +int _findclose(intptr_t handle) { + if (!FindClose(reinterpret_cast(handle))) { + errno = ENOENT; + return -1; + } + return 0; +} diff --git a/port/mfcwx/src/file.cpp b/port/mfcwx/src/file.cpp new file mode 100644 index 000000000..540e48f23 --- /dev/null +++ b/port/mfcwx/src/file.cpp @@ -0,0 +1,876 @@ +#include "afx.h" +#include "runtime.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef __APPLE__ +#include +#endif + +#undef fopen +#undef rename +#undef remove + +namespace mfcwx { + +std::string GetDataDirectory(); + +namespace { + +#ifdef __APPLE__ +inline const struct timespec& MTime(const struct stat& st) { return st.st_mtimespec; } +inline const struct timespec& ATime(const struct stat& st) { return st.st_atimespec; } +inline const struct timespec& BirthTime(const struct stat& st) { return st.st_birthtimespec; } +#else +inline const struct timespec& MTime(const struct stat& st) { return st.st_mtim; } +inline const struct timespec& ATime(const struct stat& st) { return st.st_atim; } +inline const struct timespec& BirthTime(const struct stat& st) { return st.st_ctim; } +#endif + +std::string Join(const std::string& a, const std::string& b) { + if (a.empty()) + return b; + if (a.back() == '/') + return a + b; + return a + "/" + b; +} + +bool Exists(const std::string& p) { + struct stat st; + return lstat(p.c_str(), &st) == 0; +} + +std::vector SplitComponents(const std::string& p) { + std::vector parts; + size_t start = 0; + while (start <= p.size()) { + size_t slash = p.find('/', start); + std::string part = p.substr(start, slash == std::string::npos ? std::string::npos : slash - start); + if (!part.empty()) + parts.push_back(part); + if (slash == std::string::npos) + break; + start = slash + 1; + } + return parts; +} + +std::string ResolveCase(const std::string& native) { + bool absolute = native[0] == '/'; + std::vector parts = SplitComponents(native); + std::string out = absolute ? "/" : ""; + for (size_t i = 0; i < parts.size(); ++i) { + const std::string& part = parts[i]; + std::string candidate = Join(out, part); + if (part == "." || part == ".." || Exists(candidate)) { + out = candidate; + continue; + } + bool found = false; + if (DIR* d = opendir(out.empty() ? "." : out.c_str())) { + while (dirent* e = readdir(d)) { + if (strcasecmp(e->d_name, part.c_str()) == 0) { + out = Join(out, e->d_name); + found = true; + break; + } + } + closedir(d); + } + if (!found) { + for (size_t j = i; j < parts.size(); ++j) + out = Join(out, parts[j]); + break; + } + } + if (native.size() > 1 && native.back() == '/' && (out.empty() || out.back() != '/')) + out += '/'; + return out; +} + +std::string ResolveNative(const std::string& native) { + if (native.empty() || Exists(native)) + return native; + return ResolveCase(native); +} + +std::string CurrentDirectory() { + char buf[PATH_MAX]; + if (getcwd(buf, sizeof buf)) + return buf; + return "/"; +} + +std::string NormalizeNative(const std::string& native) { + std::string full = !native.empty() && native[0] == '/' ? native : Join(CurrentDirectory(), native); + std::vector out; + for (const std::string& part : SplitComponents(full)) { + if (part == ".") + continue; + if (part == "..") { + if (!out.empty()) + out.pop_back(); + continue; + } + out.push_back(part); + } + std::string result; + for (const std::string& part : out) + result += "/" + part; + return result.empty() ? "/" : result; +} + +DWORD CopyResult(const std::string& s, LPSTR buffer, DWORD size) { + if (!buffer || size <= s.size()) + return static_cast(s.size() + 1); + memcpy(buffer, s.c_str(), s.size() + 1); + return static_cast(s.size()); +} + +DWORD AttributesFromStat(const struct stat& st, const std::string& path, const char* name) { + DWORD attrs = 0; + if (S_ISDIR(st.st_mode)) + attrs |= FILE_ATTRIBUTE_DIRECTORY; + else + attrs |= FILE_ATTRIBUTE_ARCHIVE; + if (!(st.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH)) || access(path.c_str(), W_OK) != 0) + attrs |= FILE_ATTRIBUTE_READONLY; + if (name && name[0] == '.' && strcmp(name, ".") != 0 && strcmp(name, "..") != 0) + attrs |= FILE_ATTRIBUTE_HIDDEN; + return attrs ? attrs : FILE_ATTRIBUTE_NORMAL; +} + +bool MatchGlob(const char* name, const char* mask) { + while (*mask) { + if (*mask == '*') { + while (*mask == '*') + ++mask; + if (!*mask) + return true; + for (const char* p = name; *p; ++p) + if (MatchGlob(p, mask)) + return true; + return MatchGlob(name + strlen(name), mask); + } + if (!*name) + return false; + if (*mask != '?' && tolower(static_cast(*mask)) != tolower(static_cast(*name))) + return false; + ++mask; + ++name; + } + return *name == 0; +} + +bool MatchMask(const char* name, const std::string& mask) { + if (mask == "*" || mask == "*.*") + return true; + if (MatchGlob(name, mask.c_str())) + return true; + // DOS semantics: "name.*" also matches "name"; "name." matches names without extension. + if (mask.size() > 2 && mask.compare(mask.size() - 2, 2, ".*") == 0 && !strchr(name, '.')) + return MatchGlob(name, mask.substr(0, mask.size() - 2).c_str()); + if (mask.size() > 1 && mask.back() == '.' && mask != "." && mask != ".." && !strchr(name, '.')) + return MatchGlob(name, mask.substr(0, mask.size() - 1).c_str()); + return false; +} + +struct FindObj { + std::string dir; + std::vector names; + size_t index = 0; +}; + +std::mutex& FindLock() { + static std::mutex* m = new std::mutex; + return *m; +} + +std::set& Finds() { + static auto* s = new std::set; + return *s; +} + +void FillFindData(const FindObj& f, const std::string& name, LPWIN32_FIND_DATA data) { + memset(data, 0, sizeof *data); + std::string path = Join(f.dir, name); + struct stat st; + if (stat(path.c_str(), &st) == 0 || lstat(path.c_str(), &st) == 0) { + data->dwFileAttributes = AttributesFromStat(st, path, name.c_str()); + data->ftCreationTime = MakeFileTime(BirthTime(st).tv_sec, BirthTime(st).tv_nsec); + data->ftLastAccessTime = MakeFileTime(ATime(st).tv_sec, ATime(st).tv_nsec); + data->ftLastWriteTime = MakeFileTime(MTime(st).tv_sec, MTime(st).tv_nsec); + if (!S_ISDIR(st.st_mode)) { + data->nFileSizeHigh = static_cast(static_cast(st.st_size) >> 32); + data->nFileSizeLow = static_cast(st.st_size); + } + } else { + data->dwFileAttributes = FILE_ATTRIBUTE_NORMAL; + } + std::string ansi = Utf8ToAnsi(name.c_str()); + strncpy(data->cFileName, ansi.c_str(), sizeof data->cFileName - 1); +} + +bool CopyFileContents(const std::string& src, const std::string& dst) { + int in = open(src.c_str(), O_RDONLY | O_CLOEXEC); + if (in < 0) + return false; + struct stat st; + if (fstat(in, &st) != 0 || S_ISDIR(st.st_mode)) { + int err = S_ISDIR(st.st_mode) ? EACCES : errno; + close(in); + errno = err; + return false; + } + int out = open(dst.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, st.st_mode & 0777); + if (out < 0) { + int err = errno; + close(in); + errno = err; + return false; + } + char buf[65536]; + bool ok = true; + for (;;) { + ssize_t n = read(in, buf, sizeof buf); + if (n < 0 && errno == EINTR) + continue; + if (n <= 0) { + ok = n == 0; + break; + } + for (ssize_t done = 0; done < n;) { + ssize_t w = write(out, buf + done, static_cast(n - done)); + if (w < 0 && errno == EINTR) + continue; + if (w <= 0) { + ok = false; + break; + } + done += w; + } + if (!ok) + break; + } + int err = errno; + if (ok) { + struct timespec times[2] = {ATime(st), MTime(st)}; + futimens(out, times); + fchmod(out, st.st_mode & 07777); + } + close(in); + if (close(out) != 0 && ok) { + ok = false; + err = errno; + } + errno = err; + return ok; +} + +} // namespace + +std::string FsPath(const char* appPath) { return ResolveNative(NativePath(appPath)); } + +std::string AbsoluteNativePath(const char* appPath) { return NormalizeNative(NativePath(appPath)); } + +std::string FullAppPath(const char* appPath) { return AppPath(AbsoluteNativePath(appPath).c_str()); } + +std::string ExecutablePath() { + static const std::string path = []() { +#ifdef __APPLE__ + uint32_t size = 0; + _NSGetExecutablePath(nullptr, &size); + std::string buf(size + 1, '\0'); + if (_NSGetExecutablePath(&buf[0], &size) != 0) + return std::string(); + buf.resize(strlen(buf.c_str())); + char real[PATH_MAX]; + return realpath(buf.c_str(), real) ? std::string(real) : buf; +#else + char buf[PATH_MAX]; + ssize_t n = readlink("/proc/self/exe", buf, sizeof buf - 1); + if (n <= 0) + return std::string(); + buf[n] = 0; + return std::string(buf); +#endif + }(); + return path; +} + +std::string TempDirectory() { + static std::mutex* m = new std::mutex; + std::lock_guard lk(*m); + const char* env = getenv("TMPDIR"); + std::string base = env && *env ? env : "/tmp"; + while (base.size() > 1 && base.back() == '/') + base.pop_back(); + std::string dir = base + "/cryptool1-" + std::to_string(static_cast(getuid())); + struct stat st; + if (lstat(dir.c_str(), &st) != 0) { + mkdir(dir.c_str(), 0700); + if (lstat(dir.c_str(), &st) != 0) + return base; + } + if (!S_ISDIR(st.st_mode) || st.st_uid != getuid()) + return base; + return dir; +} + +std::string UserConfigDirectory() { + const char* home = getenv("HOME"); + std::string h; + if (home && *home) { + h = home; + } else if (struct passwd* pw = getpwuid(getuid())) { + h = pw->pw_dir; + } +#ifdef __APPLE__ + return h + "/Library/Preferences"; +#else + const char* xdg = getenv("XDG_CONFIG_HOME"); + if (xdg && *xdg == '/') + return xdg; + return h + "/.config"; +#endif +} + +DWORD ErrnoToWin32(int err) { + switch (err) { + case 0: + return ERROR_SUCCESS; + case ENOENT: + return ERROR_FILE_NOT_FOUND; + case ENOTDIR: + return ERROR_PATH_NOT_FOUND; + case EACCES: + case EPERM: + case EISDIR: + return ERROR_ACCESS_DENIED; + case EBADF: + return ERROR_INVALID_HANDLE; + case ENOMEM: + return ERROR_NOT_ENOUGH_MEMORY; + case EEXIST: + return ERROR_ALREADY_EXISTS; + case EMFILE: + case ENFILE: + return 4; + case ENOSPC: + return 112; + case EINVAL: + return 87; + case ENOTEMPTY: + return 145; + case EXDEV: + return 17; + case EROFS: + return 19; + case EBUSY: + case ETXTBSY: + return 32; + case ENAMETOOLONG: + return 206; + case ENOEXEC: + return 193; + case EIO: + return 1117; + case ESPIPE: + return 131; + default: + return 31; + } +} + +void SetLastErrorFromErrno(int err) { ::SetLastError(ErrnoToWin32(err)); } + +} // namespace mfcwx + +using namespace mfcwx; + +HANDLE CreateFileA(LPCSTR name, DWORD access, DWORD, LPSECURITY_ATTRIBUTES, DWORD disposition, DWORD flags, HANDLE) { + if (!name || !*name) { + SetLastError(ERROR_PATH_NOT_FOUND); + return INVALID_HANDLE_VALUE; + } + const DWORD readBits = 0x80000000u | 0x10000000u | FILE_READ_DATA; + const DWORD writeBits = 0x40000000u | 0x10000000u | FILE_WRITE_DATA | FILE_APPEND_DATA; + bool rd = (access & readBits) != 0; + bool wr = (access & writeBits) != 0; + int oflags = O_CLOEXEC | (rd && wr ? O_RDWR : wr ? O_WRONLY : O_RDONLY); + if ((access & FILE_APPEND_DATA) && !(access & (0x40000000u | 0x10000000u | FILE_WRITE_DATA))) + oflags |= O_APPEND; + switch (disposition) { + case CREATE_NEW: + oflags |= O_CREAT | O_EXCL; + break; + case CREATE_ALWAYS: + oflags |= O_CREAT | O_TRUNC; + break; + case OPEN_EXISTING: + break; + case OPEN_ALWAYS: + oflags |= O_CREAT; + break; + case TRUNCATE_EXISTING: + oflags |= O_TRUNC; + break; + default: + SetLastError(87); + return INVALID_HANDLE_VALUE; + } + std::string path = FsPath(name); + struct stat st; + bool existed = stat(path.c_str(), &st) == 0; + if (existed && S_ISDIR(st.st_mode)) { + SetLastError(ERROR_ACCESS_DENIED); + return INVALID_HANDLE_VALUE; + } + int fd = open(path.c_str(), oflags, (flags & FILE_ATTRIBUTE_READONLY) ? 0444 : 0666); + if (fd < 0) { + SetLastError(errno == EEXIST ? 80 : ErrnoToWin32(errno)); + return INVALID_HANDLE_VALUE; + } + if (flags & 0x04000000u) + unlink(path.c_str()); + HANDLE h = HandleFromFd(fd, path); + SetLastError(existed && (disposition == CREATE_ALWAYS || disposition == OPEN_ALWAYS) ? ERROR_ALREADY_EXISTS : 0); + return h; +} + +BOOL ReadFile(HANDLE f, LPVOID buffer, DWORD toRead, LPDWORD readCount, LPOVERLAPPED ov) { + if (readCount) + *readCount = 0; + int fd = FdFromHandle(f); + if (fd < 0) { + SetLastError(ERROR_INVALID_HANDLE); + return FALSE; + } + char* p = static_cast(buffer); + off_t offset = ov ? static_cast((static_cast(ov->OffsetHigh) << 32) | ov->Offset) : 0; + DWORD total = 0; + while (total < toRead) { + ssize_t n = ov ? pread(fd, p + total, toRead - total, offset + total) : read(fd, p + total, toRead - total); + if (n < 0 && errno == EINTR) + continue; + if (n < 0) { + SetLastErrorFromErrno(errno); + if (readCount) + *readCount = total; + return FALSE; + } + if (n == 0) + break; + total += static_cast(n); + struct stat st; + if (fstat(fd, &st) == 0 && !S_ISREG(st.st_mode)) + break; + } + if (readCount) + *readCount = total; + return TRUE; +} + +BOOL WriteFile(HANDLE f, LPCVOID buffer, DWORD toWrite, LPDWORD written, LPOVERLAPPED ov) { + if (written) + *written = 0; + int fd = FdFromHandle(f); + if (fd < 0) { + SetLastError(ERROR_INVALID_HANDLE); + return FALSE; + } + const char* p = static_cast(buffer); + off_t offset = ov ? static_cast((static_cast(ov->OffsetHigh) << 32) | ov->Offset) : 0; + DWORD total = 0; + while (total < toWrite) { + ssize_t n = ov ? pwrite(fd, p + total, toWrite - total, offset + total) : write(fd, p + total, toWrite - total); + if (n < 0 && errno == EINTR) + continue; + if (n <= 0) { + SetLastErrorFromErrno(n < 0 ? errno : ENOSPC); + if (written) + *written = total; + return FALSE; + } + total += static_cast(n); + } + if (written) + *written = total; + return TRUE; +} + +DWORD SetFilePointer(HANDLE f, LONG distance, PLONG distanceHigh, DWORD method) { + int fd = FdFromHandle(f); + if (fd < 0) { + SetLastError(ERROR_INVALID_HANDLE); + return INVALID_SET_FILE_POINTER; + } + LONGLONG off = distanceHigh + ? static_cast((static_cast(static_cast(*distanceHigh)) << 32) | + static_cast(distance)) + : distance; + int whence = method == FILE_CURRENT ? SEEK_CUR : method == FILE_END ? SEEK_END : SEEK_SET; + off_t pos = lseek(fd, static_cast(off), whence); + if (pos < 0) { + SetLastError(errno == EINVAL ? 131 : ErrnoToWin32(errno)); + return INVALID_SET_FILE_POINTER; + } + if (distanceHigh) + *distanceHigh = static_cast(static_cast(pos) >> 32); + SetLastError(0); + return static_cast(pos); +} + +BOOL SetEndOfFile(HANDLE f) { + int fd = FdFromHandle(f); + off_t pos = fd >= 0 ? lseek(fd, 0, SEEK_CUR) : -1; + if (pos < 0 || ftruncate(fd, pos) != 0) { + SetLastError(fd < 0 ? ERROR_INVALID_HANDLE : ErrnoToWin32(errno)); + return FALSE; + } + return TRUE; +} + +DWORD GetFileSize(HANDLE f, LPDWORD high) { + int fd = FdFromHandle(f); + struct stat st; + if (fd < 0 || fstat(fd, &st) != 0) { + SetLastError(fd < 0 ? ERROR_INVALID_HANDLE : ErrnoToWin32(errno)); + return INVALID_FILE_SIZE; + } + if (high) + *high = static_cast(static_cast(st.st_size) >> 32); + SetLastError(0); + return static_cast(st.st_size); +} + +BOOL GetFileSizeEx(HANDLE f, PLARGE_INTEGER size) { + int fd = FdFromHandle(f); + struct stat st; + if (fd < 0 || fstat(fd, &st) != 0) { + SetLastError(fd < 0 ? ERROR_INVALID_HANDLE : ErrnoToWin32(errno)); + return FALSE; + } + size->QuadPart = st.st_size; + return TRUE; +} + +BOOL FlushFileBuffers(HANDLE f) { + int fd = FdFromHandle(f); + if (fd < 0) { + SetLastError(ERROR_INVALID_HANDLE); + return FALSE; + } + fsync(fd); + return TRUE; +} + +BOOL GetFileTime(HANDLE f, LPFILETIME creation, LPFILETIME accessTime, LPFILETIME writeTime) { + int fd = FdFromHandle(f); + struct stat st; + if (fd < 0 || fstat(fd, &st) != 0) { + SetLastError(fd < 0 ? ERROR_INVALID_HANDLE : ErrnoToWin32(errno)); + return FALSE; + } + if (creation) + *creation = MakeFileTime(BirthTime(st).tv_sec, BirthTime(st).tv_nsec); + if (accessTime) + *accessTime = MakeFileTime(ATime(st).tv_sec, ATime(st).tv_nsec); + if (writeTime) + *writeTime = MakeFileTime(MTime(st).tv_sec, MTime(st).tv_nsec); + return TRUE; +} + +BOOL DeleteFileA(LPCSTR name) { + std::string path = FsPath(name); + struct stat st; + if (lstat(path.c_str(), &st) == 0 && S_ISDIR(st.st_mode)) { + SetLastError(ERROR_ACCESS_DENIED); + return FALSE; + } + if (unlink(path.c_str()) != 0) { + SetLastErrorFromErrno(errno); + return FALSE; + } + return TRUE; +} + +BOOL CopyFileA(LPCSTR from, LPCSTR to, BOOL failIfExists) { + std::string src = FsPath(from); + std::string dst = FsPath(to); + if (failIfExists && Exists(dst)) { + SetLastError(80); + return FALSE; + } + if (!CopyFileContents(src, dst)) { + SetLastErrorFromErrno(errno); + return FALSE; + } + return TRUE; +} + +BOOL MoveFileA(LPCSTR from, LPCSTR to) { return MoveFileExA(from, to, 0); } + +BOOL MoveFileExA(LPCSTR from, LPCSTR to, DWORD flags) { + std::string src = FsPath(from); + if (!to) + return DeleteFileA(from); + std::string dst = FsPath(to); + struct stat a, b; + if (lstat(src.c_str(), &a) != 0) { + SetLastErrorFromErrno(errno); + return FALSE; + } + if (!(flags & MOVEFILE_REPLACE_EXISTING) && lstat(dst.c_str(), &b) == 0 && + !(a.st_dev == b.st_dev && a.st_ino == b.st_ino)) { + SetLastError(ERROR_ALREADY_EXISTS); + return FALSE; + } + if (rename(src.c_str(), dst.c_str()) == 0) + return TRUE; + if (errno == EXDEV && (flags & MOVEFILE_COPY_ALLOWED) && !S_ISDIR(a.st_mode)) { + if (CopyFileContents(src, dst) && unlink(src.c_str()) == 0) + return TRUE; + } + SetLastErrorFromErrno(errno); + return FALSE; +} + +BOOL CreateDirectoryA(LPCSTR path, LPSECURITY_ATTRIBUTES) { + if (mkdir(FsPath(path).c_str(), 0777) != 0) { + SetLastError(errno == ENOENT ? ERROR_PATH_NOT_FOUND : ErrnoToWin32(errno)); + return FALSE; + } + return TRUE; +} + +BOOL RemoveDirectoryA(LPCSTR path) { + if (rmdir(FsPath(path).c_str()) != 0) { + SetLastErrorFromErrno(errno); + return FALSE; + } + return TRUE; +} + +BOOL SetCurrentDirectoryA(LPCSTR path) { + if (!path || chdir(FsPath(path).c_str()) != 0) { + SetLastError(path ? ErrnoToWin32(errno) : 87); + return FALSE; + } + return TRUE; +} + +DWORD GetCurrentDirectoryA(DWORD size, LPSTR buffer) { + return CopyResult(AppPath(CurrentDirectory().c_str()), buffer, size); +} + +DWORD GetTempPathA(DWORD size, LPSTR buffer) { + return CopyResult(AppPath(TempDirectory().c_str()) + "\\", buffer, size); +} + +UINT GetTempFileNameA(LPCSTR path, LPCSTR prefix, UINT unique, LPSTR buffer) { + std::string dir = path && *path ? path : "."; + while (dir.size() > 1 && (dir.back() == '\\' || dir.back() == '/')) + dir.pop_back(); + std::string pre = prefix ? std::string(prefix).substr(0, 3) : std::string(); + auto makeName = [&](UINT u) { + char hex[16]; + snprintf(hex, sizeof hex, "%X", u & 0xFFFF); + return dir + "\\" + pre + hex + ".tmp"; + }; + if (unique) { + std::string name = makeName(unique); + if (buffer) + strncpy(buffer, name.c_str(), MAX_PATH - 1), buffer[MAX_PATH - 1] = 0; + return unique & 0xFFFF; + } + UINT start = (static_cast(GetTickCount()) ^ static_cast(getpid()) * 2654435761u) & 0xFFFF; + for (UINT i = 0; i < 0x10000; ++i) { + UINT u = (start + i) & 0xFFFF; + if (!u) + continue; + std::string name = makeName(u); + int fd = open(FsPath(name.c_str()).c_str(), O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, 0666); + if (fd >= 0) { + close(fd); + if (buffer) + strncpy(buffer, name.c_str(), MAX_PATH - 1), buffer[MAX_PATH - 1] = 0; + return u; + } + if (errno != EEXIST) { + SetLastError(errno == ENOENT ? ERROR_PATH_NOT_FOUND : ErrnoToWin32(errno)); + return 0; + } + } + SetLastError(80); + return 0; +} + +DWORD GetFileAttributesA(LPCSTR name) { + std::string path = FsPath(name); + struct stat st; + if (stat(path.c_str(), &st) != 0) { + SetLastErrorFromErrno(errno); + return INVALID_FILE_ATTRIBUTES; + } + size_t slash = path.rfind('/'); + return AttributesFromStat(st, path, path.c_str() + (slash == std::string::npos ? 0 : slash + 1)); +} + +BOOL SetFileAttributesA(LPCSTR name, DWORD attrs) { + std::string path = FsPath(name); + struct stat st; + if (stat(path.c_str(), &st) != 0) { + SetLastErrorFromErrno(errno); + return FALSE; + } + mode_t mode = st.st_mode & 07777; + if (attrs & FILE_ATTRIBUTE_READONLY) + mode &= ~static_cast(S_IWUSR | S_IWGRP | S_IWOTH); + else + mode |= S_IWUSR; + if (mode != (st.st_mode & 07777) && chmod(path.c_str(), mode) != 0) { + SetLastErrorFromErrno(errno); + return FALSE; + } + return TRUE; +} + +DWORD GetFullPathNameA(LPCSTR name, DWORD size, LPSTR buffer, LPSTR* filePart) { + if (!name || !*name) { + SetLastError(87); + return 0; + } + std::string full = FullAppPath(name); + char last = name[strlen(name) - 1]; + if ((last == '\\' || last == '/') && full.back() != '\\') + full += '\\'; + DWORD r = CopyResult(full, buffer, size); + if (filePart) { + *filePart = nullptr; + if (buffer && r < size) { + char* slash = strrchr(buffer, '\\'); + if (slash && slash[1]) + *filePart = slash + 1; + } + } + return r; +} + +DWORD GetShortPathNameA(LPCSTR longPath, LPSTR shortPath, DWORD size) { + if (!longPath || !Exists(FsPath(longPath))) { + SetLastError(ERROR_FILE_NOT_FOUND); + return 0; + } + std::string s = longPath; + return CopyResult(s, shortPath, size); +} + +DWORD GetLongPathNameA(LPCSTR shortPath, LPSTR longPath, DWORD size) { return GetShortPathNameA(shortPath, longPath, size); } + +HANDLE FindFirstFileA(LPCSTR pattern, LPWIN32_FIND_DATA data) { + std::string native = NativePath(pattern); + size_t slash = native.rfind('/'); + std::string dir = slash == std::string::npos ? "." : (slash == 0 ? "/" : native.substr(0, slash)); + std::string mask = slash == std::string::npos ? native : native.substr(slash + 1); + if (mask.empty()) { + SetLastError(ERROR_FILE_NOT_FOUND); + return INVALID_HANDLE_VALUE; + } + dir = ResolveNative(dir); + DIR* d = opendir(dir.c_str()); + if (!d) { + SetLastError(errno == ENOENT || errno == ENOTDIR ? ERROR_PATH_NOT_FOUND : ErrnoToWin32(errno)); + return INVALID_HANDLE_VALUE; + } + auto* f = new FindObj; + f->dir = dir; + while (dirent* e = readdir(d)) + if (MatchMask(e->d_name, mask)) + f->names.push_back(e->d_name); + closedir(d); + if (f->names.empty()) { + delete f; + SetLastError(ERROR_FILE_NOT_FOUND); + return INVALID_HANDLE_VALUE; + } + std::sort(f->names.begin(), f->names.end(), + [](const std::string& a, const std::string& b) { return strcasecmp(a.c_str(), b.c_str()) < 0; }); + FillFindData(*f, f->names[0], data); + f->index = 1; + { + std::lock_guard lk(FindLock()); + Finds().insert(f); + } + SetLastError(0); + return f; +} + +BOOL FindNextFileA(HANDLE find, LPWIN32_FIND_DATA data) { + std::lock_guard lk(FindLock()); + auto* f = static_cast(find); + if (!Finds().count(f)) { + SetLastError(ERROR_INVALID_HANDLE); + return FALSE; + } + if (f->index >= f->names.size()) { + SetLastError(ERROR_NO_MORE_FILES); + return FALSE; + } + FillFindData(*f, f->names[f->index++], data); + return TRUE; +} + +BOOL FindClose(HANDLE find) { + auto* f = static_cast(find); + { + std::lock_guard lk(FindLock()); + if (!Finds().erase(f)) { + SetLastError(ERROR_INVALID_HANDLE); + return FALSE; + } + } + delete f; + return TRUE; +} + +UINT GetWindowsDirectoryA(LPSTR buffer, UINT size) { + return CopyResult(AppPath(GetDataDirectory().c_str()), buffer, size); +} + +UINT GetSystemDirectoryA(LPSTR buffer, UINT size) { return GetWindowsDirectoryA(buffer, size); } + +UINT GetDriveTypeA(LPCSTR root) { + if (root && *root && !Exists(FsPath(root))) + return 1; + return 3; +} + +BOOL GetDiskFreeSpaceExA(LPCSTR dir, PULARGE_INTEGER freeToCaller, PULARGE_INTEGER total, PULARGE_INTEGER totalFree) { + struct statvfs vfs; + std::string path = dir && *dir ? FsPath(dir) : std::string("."); + if (statvfs(path.c_str(), &vfs) != 0) { + SetLastErrorFromErrno(errno); + return FALSE; + } + ULONGLONG unit = vfs.f_frsize ? vfs.f_frsize : vfs.f_bsize; + if (freeToCaller) + freeToCaller->QuadPart = unit * vfs.f_bavail; + if (total) + total->QuadPart = unit * vfs.f_blocks; + if (totalFree) + totalFree->QuadPart = unit * vfs.f_bfree; + return TRUE; +} diff --git a/port/mfcwx/src/gdi.cpp b/port/mfcwx/src/gdi.cpp new file mode 100644 index 000000000..4ed041428 --- /dev/null +++ b/port/mfcwx/src/gdi.cpp @@ -0,0 +1,1429 @@ +#include "gdi_internal.h" + +#include +#include +#include +#include +#include +#include +#include + +IMPLEMENT_DYNCREATE(CGdiObject, CObject) +IMPLEMENT_DYNAMIC(CPen, CGdiObject) +IMPLEMENT_DYNAMIC(CBrush, CGdiObject) +IMPLEMENT_DYNAMIC(CFont, CGdiObject) +IMPLEMENT_DYNAMIC(CBitmap, CGdiObject) +IMPLEMENT_DYNAMIC(CRgn, CGdiObject) +IMPLEMENT_DYNAMIC(CPalette, CGdiObject) + +namespace mfcwx { + +void PurgeTemporaryDCs(); + +namespace { + +std::unordered_set& Objects() { + static auto* objects = new std::unordered_set; + return *objects; +} + +std::unordered_map& TempObjects() { + static auto* temps = new std::unordered_map; + return *temps; +} + +std::vector& DetachedTemps() { + static auto* detached = new std::vector; + return *detached; +} + +void DetachWrappers(GdiObjectImpl* impl) { + HGDIOBJ h = reinterpret_cast(impl); + if (impl->permanent) { + impl->permanent->m_hObject = nullptr; + impl->permanent = nullptr; + } + auto it = TempObjects().find(h); + if (it != TempObjects().end()) { + it->second->m_hObject = nullptr; + DetachedTemps().push_back(it->second); + TempObjects().erase(it); + } +} + +void FreeImpl(GdiObjectImpl* impl) { + DetachWrappers(impl); + Objects().erase(impl); + delete impl; +} + +double ScreenDpi() { +#ifdef __WXOSX__ + return 72.0; +#else + static double dpi = [] { + wxSize ppi = wxGetDisplayPPI(); + return ppi.y > 0 ? static_cast(ppi.y) : 96.0; + }(); + return dpi; +#endif +} + +double PointsToPixels(double pt) { return pt * ScreenDpi() / 72.0; } + +void SetFontPixels(wxFont& f, double px) { + if (px < 1) + px = 1; +#ifdef __WXOSX__ + f.SetFractionalPointSize(PixelsToPoints(px)); +#else + f.SetPixelSize(wxSize(0, static_cast(std::lround(px)))); +#endif +} + +std::string Lower(std::string s) { + for (char& c : s) + c = static_cast(tolower(static_cast(c))); + return s; +} + +enum class FaceBase { Gui, Mono, Serif, Named }; + +bool IsValidFace(const wxString& name) { + static std::map cache; + auto it = cache.find(name); + if (it != cache.end()) + return it->second; + bool ok = wxFontEnumerator::IsValidFacename(name); + cache[name] = ok; + return ok; +} + +FaceBase ResolveFace(const std::string& face, BYTE pitchAndFamily, wxString& named) { + static const char* const gui[] = {"ms sans serif", "microsoft sans serif", "ms shell dlg", "ms shell dlg 2", + "tahoma", "arial", "helvetica", "system", "segoe ui", "small fonts"}; + static const char* const mono[] = {"courier new", "courier", "fixedsys", "terminal", "lucida console", + "consolas", "lucida sans typewriter", "monospace"}; + static const char* const serif[] = {"times new roman", "times", "ms serif"}; + std::string l = Lower(face); + while (!l.empty() && l.back() == ' ') + l.pop_back(); + if (!l.empty()) { + for (const char* g : gui) + if (l == g) + return FaceBase::Gui; + for (const char* m : mono) + if (l == m) + return FaceBase::Mono; + for (const char* s : serif) + if (l == s) + return FaceBase::Serif; + named = ToWx(face.c_str()); + if (l == "symbol" || l == "wingdings" || l == "webdings" || IsValidFace(named)) + return FaceBase::Named; + } + int family = pitchAndFamily & 0xF0; + if ((pitchAndFamily & 3) == FIXED_PITCH || family == FF_MODERN) + return FaceBase::Mono; + if (family == FF_ROMAN && l.empty()) + return FaceBase::Serif; + return FaceBase::Gui; +} + +wxFont GuiFont() { + wxFont f = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); + if (!f.IsOk()) + f = *wxNORMAL_FONT; + return f; +} + +wxFont BaseFont(FaceBase base, const wxString& named) { + switch (base) { + case FaceBase::Mono: +#ifdef __WXOSX__ + return wxFont(wxFontInfo(12).FaceName("Menlo")); +#else + return wxFont(wxFontInfo(10).Family(wxFONTFAMILY_TELETYPE)); +#endif + case FaceBase::Serif: +#ifdef __WXOSX__ + if (IsValidFace("Times New Roman")) + return wxFont(wxFontInfo(12).FaceName("Times New Roman")); +#endif + return wxFont(wxFontInfo(12).Family(wxFONTFAMILY_ROMAN)); + case FaceBase::Named: + return wxFont(wxFontInfo(12).FaceName(named)); + case FaceBase::Gui: + break; + } + return GuiFont(); +} + +double GuiEmPixels() { + static double em = [] { + wxFont f = GuiFont(); +#ifdef __WXGTK__ + if (f.GetPixelSize().y > 0 && f.GetPointSize() <= 0) + return static_cast(f.GetPixelSize().y); +#endif + return PointsToPixels(f.GetFractionalPointSize()); + }(); + return em; +} + +std::string FaceOf(const LOGFONT& lf) { return std::string(lf.lfFaceName, strnlen(lf.lfFaceName, LF_FACESIZE)); } + +void SetFace(LOGFONT& lf, const char* face) { + memset(lf.lfFaceName, 0, sizeof lf.lfFaceName); + if (face) + strncpy(lf.lfFaceName, face, LF_FACESIZE - 1); +} + +LOGFONT LogFontFromWx(const wxFont& f, double em) { + LOGFONT lf; + memset(&lf, 0, sizeof lf); + lf.lfHeight = -static_cast(std::lround(em)); + lf.lfWeight = f.GetNumericWeight(); + lf.lfItalic = f.GetStyle() != wxFONTSTYLE_NORMAL; + lf.lfUnderline = f.GetUnderlined(); + lf.lfStrikeOut = f.GetStrikethrough(); + lf.lfCharSet = DEFAULT_CHARSET; + lf.lfQuality = DEFAULT_QUALITY; + bool fixed = f.IsFixedWidth(); + lf.lfPitchAndFamily = static_cast(fixed ? (FIXED_PITCH | FF_MODERN) : (VARIABLE_PITCH | FF_SWISS)); + wxString face = f.GetFaceName(); + if (face.empty() || face.StartsWith(".") || face == GuiFont().GetFaceName()) + SetFace(lf, fixed ? "Courier New" : "MS Shell Dlg"); + else + SetFace(lf, FromWx(face).c_str()); + return lf; +} + +GdiObjectImpl* NewImpl(GdiKind kind) { return new GdiObjectImpl(kind); } + +GdiObjectImpl* MakeStock(int index) { + auto brush = [](BYTE v, bool null) { + auto* b = NewImpl(GdiKind::Brush); + if (null) { + b->logBrush.lbStyle = BS_NULL; + b->brush = *wxTRANSPARENT_BRUSH; + } else { + b->logBrush.lbStyle = BS_SOLID; + b->logBrush.lbColor = RGB(v, v, v); + b->brush = wxBrush(wxColour(v, v, v)); + } + return b; + }; + auto pen = [](int style, BYTE v) { + auto* p = NewImpl(GdiKind::Pen); + p->penStyle = static_cast(style); + p->logPen.lopnStyle = static_cast(style); + p->logPen.lopnWidth.x = 0; + p->logPen.lopnColor = RGB(v, v, v); + return p; + }; + auto font = [](bool fixed, const char* face, int weight) { + auto* f = NewImpl(GdiKind::Font); + wxString named; + f->font = BaseFont(fixed ? FaceBase::Mono : FaceBase::Gui, named); + double em = GuiEmPixels(); + SetFontPixels(f->font, em); + f->emPx = em; + f->logFont = LogFontFromWx(f->font, em); + f->logFont.lfWeight = weight; + SetFace(f->logFont, face); + return f; + }; + switch (index) { + case WHITE_BRUSH: + return brush(255, false); + case LTGRAY_BRUSH: + return brush(192, false); + case GRAY_BRUSH: + return brush(128, false); + case DKGRAY_BRUSH: + return brush(64, false); + case BLACK_BRUSH: + return brush(0, false); + case NULL_BRUSH: + return brush(0, true); + case DC_BRUSH: + return brush(255, false); + case WHITE_PEN: + return pen(PS_SOLID, 255); + case BLACK_PEN: + case DC_PEN: + return pen(PS_SOLID, 0); + case NULL_PEN: + return pen(PS_NULL, 0); + case OEM_FIXED_FONT: + return font(true, "Terminal", FW_NORMAL); + case ANSI_FIXED_FONT: + return font(true, "Courier", FW_NORMAL); + case SYSTEM_FIXED_FONT: + return font(true, "Fixedsys", FW_NORMAL); + case ANSI_VAR_FONT: + return font(false, "MS Sans Serif", FW_NORMAL); + case SYSTEM_FONT: + return font(false, "System", FW_NORMAL); + case DEVICE_DEFAULT_FONT: + return font(false, "System", FW_NORMAL); + case DEFAULT_GUI_FONT: + return font(false, "MS Shell Dlg", FW_NORMAL); + case DEFAULT_PALETTE: + return NewImpl(GdiKind::Palette); + default: + return nullptr; + } +} + +constexpr int kStockCount = 21; +constexpr int kDefaultBitmapIndex = 20; + +GdiObjectImpl* Stock(int index) { + static GdiObjectImpl* stock[kStockCount] = {}; + if (index < 0 || index >= kStockCount) + return nullptr; + if (!stock[index]) { + GdiObjectImpl* impl = nullptr; + if (index == kDefaultBitmapIndex) { + impl = NewImpl(GdiKind::Bitmap); + impl->bitmap = wxBitmap(1, 1, 24); + impl->bitsPixel = 1; + } else { + impl = MakeStock(index); + } + if (!impl) + return nullptr; + impl->stock = true; + Objects().insert(impl); + stock[index] = impl; + } + return stock[index]; +} + +wxColour ClassicSysColor(int index) { + static const COLORREF table[] = { + RGB(212, 208, 200), RGB(58, 110, 165), RGB(10, 36, 106), RGB(128, 128, 128), RGB(212, 208, 200), + RGB(255, 255, 255), RGB(0, 0, 0), RGB(0, 0, 0), RGB(0, 0, 0), RGB(255, 255, 255), + RGB(212, 208, 200), RGB(212, 208, 200), RGB(128, 128, 128), RGB(10, 36, 106), RGB(255, 255, 255), + RGB(212, 208, 200), RGB(128, 128, 128), RGB(128, 128, 128), RGB(0, 0, 0), RGB(212, 208, 200), + RGB(255, 255, 255), RGB(64, 64, 64), RGB(212, 208, 200), RGB(0, 0, 0), RGB(255, 255, 225), + RGB(181, 181, 181), RGB(0, 0, 128), RGB(166, 202, 240), RGB(192, 192, 192), RGB(49, 106, 197), + RGB(236, 233, 216)}; + if (index < 0 || index >= static_cast(sizeof table / sizeof table[0])) + return *wxBLACK; + return ToWxColour(table[index]); +} + +bool WxSysColorIndex(int index, wxSystemColour& out) { + static const int map[] = {wxSYS_COLOUR_SCROLLBAR, + wxSYS_COLOUR_DESKTOP, + wxSYS_COLOUR_ACTIVECAPTION, + wxSYS_COLOUR_INACTIVECAPTION, + wxSYS_COLOUR_MENU, + wxSYS_COLOUR_WINDOW, + wxSYS_COLOUR_WINDOWFRAME, + wxSYS_COLOUR_MENUTEXT, + wxSYS_COLOUR_WINDOWTEXT, + wxSYS_COLOUR_CAPTIONTEXT, + wxSYS_COLOUR_ACTIVEBORDER, + wxSYS_COLOUR_INACTIVEBORDER, + wxSYS_COLOUR_APPWORKSPACE, + wxSYS_COLOUR_HIGHLIGHT, + wxSYS_COLOUR_HIGHLIGHTTEXT, + wxSYS_COLOUR_BTNFACE, + wxSYS_COLOUR_BTNSHADOW, + wxSYS_COLOUR_GRAYTEXT, + wxSYS_COLOUR_BTNTEXT, + wxSYS_COLOUR_INACTIVECAPTIONTEXT, + wxSYS_COLOUR_BTNHIGHLIGHT, + wxSYS_COLOUR_3DDKSHADOW, + wxSYS_COLOUR_3DLIGHT, + wxSYS_COLOUR_INFOTEXT, + wxSYS_COLOUR_INFOBK, + -1, + wxSYS_COLOUR_HOTLIGHT, + wxSYS_COLOUR_GRADIENTACTIVECAPTION, + wxSYS_COLOUR_GRADIENTINACTIVECAPTION, + wxSYS_COLOUR_MENUHILIGHT, + wxSYS_COLOUR_MENUBAR}; + if (index < 0 || index >= static_cast(sizeof map / sizeof map[0]) || map[index] < 0) + return false; + out = static_cast(map[index]); + return true; +} + +const wxDash* InternDashes(const std::vector& dashes) { + static auto* pool = new std::set>; + return pool->insert(dashes).first->data(); +} + +CGdiObject* NewWrapper(GdiKind kind) { + switch (kind) { + case GdiKind::Pen: + return new CPen; + case GdiKind::Brush: + return new CBrush; + case GdiKind::Font: + return new CFont; + case GdiKind::Bitmap: + return new CBitmap; + case GdiKind::Region: + return new CRgn; + case GdiKind::Palette: + return new CPalette; + } + return new CGdiObject; +} + +HGDIOBJ NewPen(int style, int width, COLORREF color, const LOGBRUSH* lb, int styleCount, const DWORD* userStyle) { + auto* impl = NewImpl(GdiKind::Pen); + impl->penStyle = static_cast(style); + impl->logPen.lopnStyle = static_cast(style); + impl->logPen.lopnWidth.x = width; + impl->logPen.lopnColor = lb ? lb->lbColor : color; + if ((style & PS_STYLE_MASK) == PS_USERSTYLE && userStyle) { + for (int i = 0; i < styleCount; ++i) + impl->dashes.push_back(static_cast(std::max(1, std::min(userStyle[i], 255)))); + } + return RegisterGdiObject(impl); +} + +wxBrushStyle HatchStyle(int index) { + switch (index) { + case HS_HORIZONTAL: + return wxBRUSHSTYLE_HORIZONTAL_HATCH; + case HS_VERTICAL: + return wxBRUSHSTYLE_VERTICAL_HATCH; + case HS_FDIAGONAL: + return wxBRUSHSTYLE_FDIAGONAL_HATCH; + case HS_BDIAGONAL: + return wxBRUSHSTYLE_BDIAGONAL_HATCH; + case HS_CROSS: + return wxBRUSHSTYLE_CROSS_HATCH; + default: + return wxBRUSHSTYLE_CROSSDIAG_HATCH; + } +} + +HGDIOBJ NewSolidBrush(COLORREF c) { + auto* impl = NewImpl(GdiKind::Brush); + impl->logBrush.lbStyle = BS_SOLID; + impl->logBrush.lbColor = c; + impl->brush = wxBrush(ToWxColour(c)); + return RegisterGdiObject(impl); +} + +HGDIOBJ NewHatchBrush(int index, COLORREF c) { + auto* impl = NewImpl(GdiKind::Brush); + impl->logBrush.lbStyle = BS_HATCHED; + impl->logBrush.lbColor = c; + impl->logBrush.lbHatch = static_cast(index); + impl->brush = wxBrush(ToWxColour(c), HatchStyle(index)); + return RegisterGdiObject(impl); +} + +HGDIOBJ NewPatternBrush(HBITMAP hbm) { + GdiObjectImpl* bmp = GdiImpl(hbm, GdiKind::Bitmap); + if (!bmp || !bmp->bitmap.IsOk()) + return nullptr; + SyncBitmap(bmp); + auto* impl = NewImpl(GdiKind::Brush); + impl->logBrush.lbStyle = BS_PATTERN; + impl->logBrush.lbHatch = reinterpret_cast(hbm); + wxBitmap copy = bmp->bitmap.GetSubBitmap(wxRect(0, 0, bmp->bitmap.GetLogicalWidth(), bmp->bitmap.GetLogicalHeight())); + impl->brush = wxBrush(copy.IsOk() ? copy : bmp->bitmap); + return RegisterGdiObject(impl); +} + +HGDIOBJ NewBrushIndirect(const LOGBRUSH* lb) { + if (!lb) + return nullptr; + switch (lb->lbStyle) { + case BS_SOLID: + return NewSolidBrush(lb->lbColor); + case BS_HATCHED: + return NewHatchBrush(static_cast(lb->lbHatch), lb->lbColor); + case BS_PATTERN: + return NewPatternBrush(reinterpret_cast(lb->lbHatch)); + case BS_NULL: { + auto* impl = NewImpl(GdiKind::Brush); + impl->logBrush.lbStyle = BS_NULL; + impl->brush = *wxTRANSPARENT_BRUSH; + return RegisterGdiObject(impl); + } + default: + return NewSolidBrush(lb->lbColor); + } +} + +HGDIOBJ NewFont(const LOGFONT* lf) { + if (!lf) + return nullptr; + auto* impl = NewImpl(GdiKind::Font); + impl->logFont = *lf; + impl->logFont.lfFaceName[LF_FACESIZE - 1] = 0; + impl->font = MakeFont(impl->logFont, &impl->emPx); + return RegisterGdiObject(impl); +} + +HGDIOBJ NewRegion(const wxRegion& r) { + auto* impl = NewImpl(GdiKind::Region); + impl->region = r; + return RegisterGdiObject(impl); +} + +wxRegion RectRegion(int x1, int y1, int x2, int y2) { + int l = std::min(x1, x2), r = std::max(x1, x2), t = std::min(y1, y2), b = std::max(y1, y2); + if (r <= l || b <= t) + return wxRegion(); + return wxRegion(l, t, r - l, b - t); +} + +wxRegion EllipseRegion(int x1, int y1, int x2, int y2) { + int l = std::min(x1, x2), r = std::max(x1, x2), t = std::min(y1, y2), b = std::max(y1, y2); + if (r <= l || b <= t) + return wxRegion(); + double cx = (l + r) / 2.0, cy = (t + b) / 2.0, rx = (r - l) / 2.0, ry = (b - t) / 2.0; + int n = std::max(16, static_cast(rx + ry)); + std::vector pts(n); + for (int i = 0; i < n; ++i) { + double a = 2 * M_PI * i / n; + pts[i] = wxPoint(static_cast(std::lround(cx + rx * cos(a))), static_cast(std::lround(cy + ry * sin(a)))); + } + return wxRegion(pts.size(), pts.data()); +} + +wxRegion RoundRectRegion(int x1, int y1, int x2, int y2, int w, int h) { + int l = std::min(x1, x2), r = std::max(x1, x2), t = std::min(y1, y2), b = std::max(y1, y2); + if (r <= l || b <= t) + return wxRegion(); + double rx = std::min(std::abs(w) / 2.0, (r - l) / 2.0), ry = std::min(std::abs(h) / 2.0, (b - t) / 2.0); + if (rx < 1 || ry < 1) + return wxRegion(l, t, r - l, b - t); + std::vector pts; + const double cxs[4] = {r - rx, l + rx, l + rx, r - rx}; + const double cys[4] = {t + ry, t + ry, b - ry, b - ry}; + int seg = std::max(4, static_cast((rx + ry) / 2)); + for (int q = 0; q < 4; ++q) { + for (int i = 0; i <= seg; ++i) { + double a = -M_PI / 2 * (q + static_cast(i) / seg); + pts.emplace_back(static_cast(std::lround(cxs[q] + rx * cos(a))), + static_cast(std::lround(cys[q] + ry * sin(a)))); + } + } + return wxRegion(pts.size(), pts.data()); +} + +int RegionType(const wxRegion& r) { + if (!r.IsOk() || r.IsEmpty()) + return NULLREGION; + int n = 0; + for (wxRegionIterator it(r); it && n < 2; ++it) + ++n; + return n <= 1 ? SIMPLEREGION : COMPLEXREGION; +} + +int GetObjectImpl(HGDIOBJ h, int c, void* pv) { + GdiLock lock; + int sys = SysColorBrushIndex(h); + if (sys >= 0) { + LOGBRUSH lb{BS_SOLID, SysColor(sys), 0}; + if (!pv) + return sizeof lb; + int n = std::min(c, sizeof lb); + memcpy(pv, &lb, static_cast(n)); + return n; + } + GdiObjectImpl* impl = GdiImpl(h); + if (!impl) + return 0; + const void* src = nullptr; + size_t size = 0; + BITMAP bm; + WORD entries = 0; + switch (impl->kind) { + case GdiKind::Pen: + src = &impl->logPen; + size = sizeof impl->logPen; + break; + case GdiKind::Brush: + src = &impl->logBrush; + size = sizeof impl->logBrush; + break; + case GdiKind::Font: + src = &impl->logFont; + size = sizeof impl->logFont; + break; + case GdiKind::Bitmap: { + memset(&bm, 0, sizeof bm); + if (impl->bitmap.IsOk()) { + bm.bmWidth = static_cast(std::lround(impl->bitmap.GetLogicalWidth())); + bm.bmHeight = static_cast(std::lround(impl->bitmap.GetLogicalHeight())); + } + bm.bmBitsPixel = static_cast(impl->bitsPixel); + bm.bmPlanes = 1; + bm.bmWidthBytes = ((bm.bmWidth * impl->bitsPixel + 15) / 16) * 2; + src = &bm; + size = sizeof bm; + break; + } + case GdiKind::Palette: + entries = 256; + src = &entries; + size = sizeof entries; + break; + case GdiKind::Region: + return 0; + } + if (!pv) + return static_cast(size); + int n = std::min(c, static_cast(size)); + if (n <= 0) + return 0; + memcpy(pv, src, static_cast(n)); + return n; +} + +} // namespace + +std::recursive_mutex& GdiMutex() { + static auto* m = new std::recursive_mutex; + return *m; +} + +double PixelsToPoints(double px) { return px * 72.0 / ScreenDpi(); } + +GdiObjectImpl* GdiImpl(HGDIOBJ h) { + if (!h || SysColorBrushIndex(h) >= 0) + return nullptr; + GdiLock lock; + auto* impl = reinterpret_cast(h); + return Objects().count(impl) ? impl : nullptr; +} + +GdiObjectImpl* GdiImpl(HGDIOBJ h, GdiKind kind) { + GdiObjectImpl* impl = GdiImpl(h); + return impl && impl->kind == kind ? impl : nullptr; +} + +HGDIOBJ RegisterGdiObject(GdiObjectImpl* impl) { + GdiLock lock; + Objects().insert(impl); + return reinterpret_cast(impl); +} + +void GdiAddRef(HGDIOBJ h) { + GdiLock lock; + if (GdiObjectImpl* impl = GdiImpl(h)) + if (!impl->stock) + ++impl->refs; +} + +void GdiRelease(HGDIOBJ h) { + GdiLock lock; + GdiObjectImpl* impl = GdiImpl(h); + if (!impl || impl->stock) + return; + if (--impl->refs <= 0) + FreeImpl(impl); +} + +int SysColorBrushIndex(HGDIOBJ h) { + auto v = reinterpret_cast(h); + return v >= 1 && v <= 31 ? static_cast(v) - 1 : -1; +} + +HGDIOBJ DefaultBitmapHandle() { return reinterpret_cast(Stock(kDefaultBitmapIndex)); } + +bool IsStockFont(HFONT h) { + GdiObjectImpl* impl = GdiImpl(h, GdiKind::Font); + return !impl || impl->stock; +} + +wxFont MakeFont(const LOGFONT& lfIn, double* emPx) { + LOGFONT lf = lfIn; + lf.lfFaceName[LF_FACESIZE - 1] = 0; + LONG height = lf.lfHeight; + if (height > 2000 || height < -2000) + height = 0; + wxString named; + FaceBase base = ResolveFace(FaceOf(lf), lf.lfPitchAndFamily, named); + wxFont f = BaseFont(base, named); + double em = height < 0 ? -static_cast(height) : (height > 0 ? height : GuiEmPixels()); + SetFontPixels(f, em); + LONG weight = lf.lfWeight; + if (weight > 0 && weight <= 1000) + f.SetNumericWeight(static_cast(weight)); + else + f.SetNumericWeight(wxFONTWEIGHT_NORMAL); + f.SetStyle(lf.lfItalic ? wxFONTSTYLE_ITALIC : wxFONTSTYLE_NORMAL); + f.SetUnderlined(lf.lfUnderline != 0); + f.SetStrikethrough(lf.lfStrikeOut != 0); + if (height > 0) { + GdiLock lock; + wxDC& dc = MeasureDC(); + dc.SetFont(f); + wxFontMetrics fm = dc.GetFontMetrics(); + int cell = fm.height - fm.externalLeading; + if (cell > 0) { + em = em * height / cell; + SetFontPixels(f, em); + } + } + if (emPx) + *emPx = em; + return f; +} + +wxPen DevicePen(HPEN h, double widthScale) { + GdiObjectImpl* impl = GdiImpl(h, GdiKind::Pen); + if (!impl) + return *wxBLACK_PEN; + int style = static_cast(impl->penStyle & PS_STYLE_MASK); + if (style == PS_NULL) + return *wxTRANSPARENT_PEN; + bool geometric = (impl->penStyle & PS_GEOMETRIC) != 0; + int w = impl->logPen.lopnWidth.x; + int dw = w <= 0 ? 1 : std::max(1, static_cast(std::lround(w * widthScale))); + if (!geometric && dw > 1 && style >= PS_DASH && style <= PS_DASHDOTDOT) + style = PS_SOLID; + wxPen pen(ToWxColour(impl->logPen.lopnColor), dw, wxPENSTYLE_SOLID); + static const wxDash dash[] = {18, 6}, dot[] = {3, 3}, dashDot[] = {9, 6, 3, 6}, dashDotDot[] = {9, 3, 3, 3, 3, 3}, + alternate[] = {1, 1}; + bool dashed = true; + switch (style) { + case PS_DASH: + pen.SetDashes(2, dash); + break; + case PS_DOT: + pen.SetDashes(2, dot); + break; + case PS_DASHDOT: + pen.SetDashes(4, dashDot); + break; + case PS_DASHDOTDOT: + pen.SetDashes(6, dashDotDot); + break; + case PS_ALTERNATE: + pen.SetDashes(2, alternate); + break; + case PS_USERSTYLE: + if (!impl->dashes.empty()) { + pen.SetDashes(static_cast(impl->dashes.size()), InternDashes(impl->dashes)); + break; + } + dashed = false; + break; + default: + dashed = false; + break; + } + if (dashed) + pen.SetStyle(wxPENSTYLE_USER_DASH); + if (geometric) { + DWORD cap = impl->penStyle & 0x00000F00; + pen.SetCap(cap == PS_ENDCAP_SQUARE ? wxCAP_PROJECTING : cap == PS_ENDCAP_FLAT ? wxCAP_BUTT : wxCAP_ROUND); + DWORD join = impl->penStyle & 0x0000F000; + pen.SetJoin(join == PS_JOIN_BEVEL ? wxJOIN_BEVEL : join == PS_JOIN_MITER ? wxJOIN_MITER : wxJOIN_ROUND); + } else if (dw == 1) { + pen.SetCap(dashed ? wxCAP_BUTT : wxCAP_PROJECTING); + pen.SetJoin(wxJOIN_MITER); + } + return pen; +} + +wxBrush DeviceBrush(HBRUSH h) { + int sys = SysColorBrushIndex(h); + if (sys >= 0) + return wxBrush(ToWxColour(SysColor(sys))); + GdiObjectImpl* impl = GdiImpl(h, GdiKind::Brush); + if (!impl) + return *wxTRANSPARENT_BRUSH; + if (impl->sysColor >= 0) { + COLORREF c = SysColor(impl->sysColor); + if (c != impl->logBrush.lbColor) { + impl->logBrush.lbColor = c; + impl->brush = wxBrush(ToWxColour(c)); + } + } + return impl->brush; +} + +wxFont DeviceFont(HFONT h, double scale) { + GdiObjectImpl* impl = GdiImpl(h, GdiKind::Font); + if (!impl) + impl = Stock(SYSTEM_FONT); + if (impl->stock || std::fabs(scale - 1.0) < 1e-6 || scale <= 0) + return impl->font; + if (!impl->scaledFont.IsOk() || std::fabs(impl->scaledFor - scale) > 1e-6) { + impl->scaledFont = impl->font; + SetFontPixels(impl->scaledFont, impl->emPx * scale); + impl->scaledFor = scale; + } + return impl->scaledFont; +} + +double FontEmPixels(HFONT h) { + GdiObjectImpl* impl = GdiImpl(h, GdiKind::Font); + if (!impl) + impl = Stock(SYSTEM_FONT); + return impl->emPx; +} + +bool BrushIsHatched(HBRUSH h) { + GdiObjectImpl* impl = GdiImpl(h, GdiKind::Brush); + return impl && impl->logBrush.lbStyle == BS_HATCHED; +} + +COLORREF BrushColor(HBRUSH h) { + int sys = SysColorBrushIndex(h); + if (sys >= 0) + return SysColor(sys); + GdiObjectImpl* impl = GdiImpl(h, GdiKind::Brush); + if (!impl) + return 0; + if (impl->sysColor >= 0) + return SysColor(impl->sysColor); + return impl->logBrush.lbColor; +} + +bool PenIsNull(HPEN h) { + GdiObjectImpl* impl = GdiImpl(h, GdiKind::Pen); + return !impl || (impl->penStyle & PS_STYLE_MASK) == PS_NULL; +} + +int PenStyle(HPEN h) { + GdiObjectImpl* impl = GdiImpl(h, GdiKind::Pen); + return impl ? static_cast(impl->penStyle & PS_STYLE_MASK) : PS_SOLID; +} + +int PenWidth(HPEN h) { + GdiObjectImpl* impl = GdiImpl(h, GdiKind::Pen); + return impl ? std::max(1, impl->logPen.lopnWidth.x) : 1; +} + +COLORREF PenColor(HPEN h) { + GdiObjectImpl* impl = GdiImpl(h, GdiKind::Pen); + return impl ? impl->logPen.lopnColor : 0; +} + +wxBitmap NewBitmap(int width, int height, double scale, const wxColour& fill) { + width = std::max(1, width); + height = std::max(1, height); + if (scale <= 0) + scale = 1; + int pw = std::max(1, static_cast(std::lround(width * scale))); + int ph = std::max(1, static_cast(std::lround(height * scale))); + wxImage img(pw, ph, false); + unsigned char* data = img.GetData(); + for (int i = 0; i < pw * ph; ++i) { + data[i * 3] = fill.Red(); + data[i * 3 + 1] = fill.Green(); + data[i * 3 + 2] = fill.Blue(); + } + return wxBitmap(img, -1, scale); +} + +HBITMAP NewBitmapHandle(const wxBitmap& bmp, int bitsPixel) { + if (!bmp.IsOk()) + return nullptr; + auto* impl = NewImpl(GdiKind::Bitmap); + impl->bitmap = bmp; + impl->bitsPixel = bitsPixel; + return RegisterGdiObject(impl); +} + +wxPen PenFromHandle(HPEN h) { + GdiLock lock; + return DevicePen(h, 1.0); +} + +wxBrush BrushFromHandle(HBRUSH h) { + GdiLock lock; + if (!h) + return wxNullBrush; + return DeviceBrush(h); +} + +wxFont FontFromHandle(HFONT h) { + GdiLock lock; + GdiObjectImpl* impl = GdiImpl(h, GdiKind::Font); + return impl ? impl->font : wxNullFont; +} + +wxBitmap* BitmapFromHandle(HBITMAP h) { + GdiLock lock; + GdiObjectImpl* impl = GdiImpl(h, GdiKind::Bitmap); + if (!impl || !impl->bitmap.IsOk()) + return nullptr; + SyncBitmap(impl); + return &impl->bitmap; +} + +HFONT DefaultGuiFont() { + GdiLock lock; + return reinterpret_cast(Stock(DEFAULT_GUI_FONT)); +} + +HBITMAP CreateBitmapHandle(const wxBitmap& bmp) { + GdiLock lock; + return NewBitmapHandle(bmp, bmp.IsOk() && bmp.GetDepth() == 1 ? 1 : 32); +} + +HFONT FontHandleFor(const wxFont& font) { + GdiLock lock; + if (!font.IsOk()) + return DefaultGuiFont(); + static auto* cache = new std::map; + wxString key = font.GetNativeFontInfoDesc(); + auto it = cache->find(key); + if (it != cache->end()) + return reinterpret_cast(it->second); + auto* impl = NewImpl(GdiKind::Font); + impl->font = font; +#ifdef __WXGTK__ + impl->emPx = font.GetPixelSize().y > 0 ? font.GetPixelSize().y : PointsToPixels(font.GetFractionalPointSize()); +#else + impl->emPx = PointsToPixels(font.GetFractionalPointSize()); +#endif + impl->logFont = LogFontFromWx(font, impl->emPx); + impl->stock = true; + Objects().insert(impl); + (*cache)[key] = impl; + return reinterpret_cast(impl); +} + +wxColour ToWxColour(COLORREF c) { + if (c == CLR_INVALID) + return wxTransparentColour; + if ((c & 0xFF000000) == 0x01000000) + return *wxBLACK; + return wxColour(GetRValue(c), GetGValue(c), GetBValue(c)); +} + +COLORREF FromWxColour(const wxColour& c) { + if (!c.IsOk()) + return 0; + return RGB(c.Red(), c.Green(), c.Blue()); +} + +COLORREF SysColor(int index) { + wxSystemColour sc; + if (WxSysColorIndex(index, sc)) { + wxColour c = wxSystemSettings::GetColour(sc); + if (c.IsOk() && c.IsSolid()) + return FromWxColour(c); + } + return FromWxColour(ClassicSysColor(index)); +} + +void PurgeTemporaryGdiWrappers() { + std::vector doomed; + { + GdiLock lock; + for (auto& kv : TempObjects()) { + kv.second->m_hObject = nullptr; + doomed.push_back(kv.second); + } + TempObjects().clear(); + doomed.insert(doomed.end(), DetachedTemps().begin(), DetachedTemps().end()); + DetachedTemps().clear(); + } + for (CGdiObject* o : doomed) + delete o; + PurgeTemporaryDCs(); +} + +} // namespace mfcwx + +using namespace mfcwx; + +// --------------------------------------------------------------------------------------------- +// CGdiObject and derived classes + +CGdiObject::CGdiObject() : m_hObject(nullptr) {} + +CGdiObject::~CGdiObject() { DeleteObject(); } + +CGdiObject* CGdiObject::FromHandle(HGDIOBJ hObject) { + if (!hObject) + return nullptr; + GdiLock lock; + GdiObjectImpl* impl = GdiImpl(hObject); + if (impl && impl->permanent) + return impl->permanent; + auto it = TempObjects().find(hObject); + if (it != TempObjects().end()) + return it->second; + CGdiObject* wrapper = nullptr; + if (impl) + wrapper = NewWrapper(impl->kind); + else if (SysColorBrushIndex(hObject) >= 0) + wrapper = new CBrush; + else + return nullptr; + wrapper->m_hObject = hObject; + TempObjects()[hObject] = wrapper; + return wrapper; +} + +BOOL CGdiObject::Attach(HGDIOBJ hObject) { + if (!hObject) + return FALSE; + GdiLock lock; + if (m_hObject && m_hObject != hObject) + DeleteObject(); + GdiObjectImpl* impl = GdiImpl(hObject); + if (!impl && SysColorBrushIndex(hObject) < 0) + return FALSE; + auto it = TempObjects().find(hObject); + if (it != TempObjects().end()) { + it->second->m_hObject = nullptr; + DetachedTemps().push_back(it->second); + TempObjects().erase(it); + } + if (impl) + impl->permanent = this; + m_hObject = hObject; + return TRUE; +} + +HGDIOBJ CGdiObject::Detach() { + if (AfxIsNullThis(this)) + return nullptr; + GdiLock lock; + HGDIOBJ h = m_hObject; + if (GdiObjectImpl* impl = GdiImpl(h)) + if (impl->permanent == this) + impl->permanent = nullptr; + m_hObject = nullptr; + return h; +} + +BOOL CGdiObject::DeleteObject() { + if (AfxIsNullThis(this) || !m_hObject) + return FALSE; + return ::DeleteObject(Detach()); +} + +int CGdiObject::GetObject(int nCount, LPVOID lpObject) const { return GetObjectImpl(m_hObject, nCount, lpObject); } + +BOOL CGdiObject::CreateStockObject(int nIndex) { return Attach(::GetStockObject(nIndex)); } + +CPen::CPen(int nPenStyle, int nWidth, COLORREF crColor) { CreatePen(nPenStyle, nWidth, crColor); } + +CPen::CPen(int nPenStyle, int nWidth, const LOGBRUSH* pLogBrush, int nStyleCount, const DWORD* lpStyle) { + CreatePen(nPenStyle, nWidth, pLogBrush, nStyleCount, lpStyle); +} + +BOOL CPen::CreatePen(int nPenStyle, int nWidth, COLORREF crColor) { + return Attach(::CreatePen(nPenStyle, nWidth, crColor)); +} + +BOOL CPen::CreatePen(int nPenStyle, int nWidth, const LOGBRUSH* pLogBrush, int nStyleCount, const DWORD* lpStyle) { + GdiLock lock; + return Attach(NewPen(nPenStyle, nWidth, 0, pLogBrush, nStyleCount, lpStyle)); +} + +BOOL CPen::CreatePenIndirect(LPLOGPEN lpLogPen) { + if (!lpLogPen) + return FALSE; + return Attach(::CreatePen(static_cast(lpLogPen->lopnStyle), lpLogPen->lopnWidth.x, lpLogPen->lopnColor)); +} + +int CPen::GetLogPen(LOGPEN* pLogPen) { return GetObject(sizeof(LOGPEN), pLogPen); } + +CBrush::CBrush(COLORREF crColor) { CreateSolidBrush(crColor); } + +CBrush::CBrush(int nIndex, COLORREF crColor) { CreateHatchBrush(nIndex, crColor); } + +CBrush::CBrush(CBitmap* pBitmap) { CreatePatternBrush(pBitmap); } + +BOOL CBrush::CreateSolidBrush(COLORREF crColor) { return Attach(::CreateSolidBrush(crColor)); } + +BOOL CBrush::CreateHatchBrush(int nIndex, COLORREF crColor) { return Attach(::CreateHatchBrush(nIndex, crColor)); } + +BOOL CBrush::CreatePatternBrush(CBitmap* pBitmap) { + return pBitmap ? Attach(::CreatePatternBrush(pBitmap->m_hObject)) : FALSE; +} + +BOOL CBrush::CreateSysColorBrush(int nIndex) { + GdiLock lock; + auto* impl = new GdiObjectImpl(GdiKind::Brush); + impl->logBrush.lbStyle = BS_SOLID; + impl->logBrush.lbColor = SysColor(nIndex); + impl->sysColor = nIndex; + impl->brush = wxBrush(ToWxColour(impl->logBrush.lbColor)); + return Attach(RegisterGdiObject(impl)); +} + +BOOL CBrush::CreateBrushIndirect(const LOGBRUSH* lpLogBrush) { + GdiLock lock; + return Attach(NewBrushIndirect(lpLogBrush)); +} + +int CBrush::GetLogBrush(LOGBRUSH* pLogBrush) { return GetObject(sizeof(LOGBRUSH), pLogBrush); } + +BOOL CFont::CreateFontIndirect(const LOGFONT* lpLogFont) { return Attach(::CreateFontIndirect(lpLogFont)); } + +BOOL CFont::CreateFont(int nHeight, int nWidth, int nEscapement, int nOrientation, int nWeight, BYTE bItalic, + BYTE bUnderline, BYTE cStrikeOut, BYTE nCharSet, BYTE nOutPrecision, BYTE nClipPrecision, + BYTE nQuality, BYTE nPitchAndFamily, const char* lpszFacename) { + return Attach(::CreateFont(nHeight, nWidth, nEscapement, nOrientation, nWeight, bItalic, bUnderline, cStrikeOut, + nCharSet, nOutPrecision, nClipPrecision, nQuality, nPitchAndFamily, lpszFacename)); +} + +BOOL CFont::CreatePointFont(int nPointSize, const char* lpszFaceName, CDC* pDC) { + LOGFONT lf; + memset(&lf, 0, sizeof lf); + lf.lfCharSet = DEFAULT_CHARSET; + lf.lfHeight = nPointSize; + SetFace(lf, lpszFaceName); + return CreatePointFontIndirect(&lf, pDC); +} + +BOOL CFont::CreatePointFontIndirect(const LOGFONT* lpLogFont, CDC* pDC) { + if (!lpLogFont) + return FALSE; + LOGFONT lf = *lpLogFont; + GdiLock lock; + DCState* s = pDC ? DCFromHandle(pDC->m_hAttribDC ? pDC->m_hAttribDC : pDC->m_hDC) : nullptr; + int dpi = s ? s->Dpi() : 96; + double py = static_cast(dpi) * lf.lfHeight / 720.0; + if (s) { + double x0 = 0, y0 = 0, x1 = 0, y1 = py; + s->ToLogical(x0, y0); + s->ToLogical(x1, y1); + py = y1 - y0; + } + lf.lfHeight = -static_cast(std::lround(std::fabs(py))); + return CreateFontIndirect(&lf); +} + +int CFont::GetLogFont(LOGFONT* pLogFont) { return GetObject(sizeof(LOGFONT), pLogFont); } + +BOOL CRgn::CreateRectRgn(int x1, int y1, int x2, int y2) { return Attach(::CreateRectRgn(x1, y1, x2, y2)); } + +BOOL CRgn::CreateRectRgnIndirect(LPCRECT lpRect) { + return lpRect ? CreateRectRgn(lpRect->left, lpRect->top, lpRect->right, lpRect->bottom) : FALSE; +} + +BOOL CRgn::CreateEllipticRgn(int x1, int y1, int x2, int y2) { return Attach(::CreateEllipticRgn(x1, y1, x2, y2)); } + +BOOL CRgn::CreateEllipticRgnIndirect(LPCRECT lpRect) { + return lpRect ? CreateEllipticRgn(lpRect->left, lpRect->top, lpRect->right, lpRect->bottom) : FALSE; +} + +BOOL CRgn::CreatePolygonRgn(LPPOINT lpPoints, int nCount, int nMode) { + if (!lpPoints || nCount < 3) + return FALSE; + std::vector pts; + for (int i = 0; i < nCount; ++i) + pts.emplace_back(lpPoints[i].x, lpPoints[i].y); + GdiLock lock; + return Attach(NewRegion(wxRegion(pts.size(), pts.data(), nMode == WINDING ? wxWINDING_RULE : wxODDEVEN_RULE))); +} + +BOOL CRgn::CreateRoundRectRgn(int x1, int y1, int x2, int y2, int x3, int y3) { + GdiLock lock; + return Attach(NewRegion(RoundRectRegion(x1, y1, x2, y2, x3, y3))); +} + +int CRgn::CombineRgn(CRgn* pRgn1, CRgn* pRgn2, int nCombineMode) { + GdiLock lock; + GdiObjectImpl* self = GdiImpl(m_hObject, GdiKind::Region); + GdiObjectImpl* a = pRgn1 ? GdiImpl(pRgn1->m_hObject, GdiKind::Region) : nullptr; + GdiObjectImpl* b = pRgn2 ? GdiImpl(pRgn2->m_hObject, GdiKind::Region) : nullptr; + if (!self || !a) + return ERROR; + wxRegion r = a->region; + wxRegion other = b ? b->region : wxRegion(); + bool aEmpty = !r.IsOk() || r.IsEmpty(); + bool bEmpty = !other.IsOk() || other.IsEmpty(); + switch (nCombineMode) { + case RGN_AND: + if (aEmpty || bEmpty) + r = wxRegion(); + else + r.Intersect(other); + break; + case RGN_OR: + if (aEmpty) + r = other; + else if (!bEmpty) + r.Union(other); + break; + case RGN_XOR: + if (aEmpty) + r = other; + else if (!bEmpty) + r.Xor(other); + break; + case RGN_DIFF: + if (!aEmpty && !bEmpty) + r.Subtract(other); + break; + case RGN_COPY: + break; + default: + return ERROR; + } + self->region = r; + return RegionType(self->region); +} + +int CRgn::CopyRgn(CRgn* pRgnSrc) { return CombineRgn(pRgnSrc, nullptr, RGN_COPY); } + +BOOL CRgn::PtInRegion(int x, int y) const { + GdiLock lock; + GdiObjectImpl* self = GdiImpl(m_hObject, GdiKind::Region); + return self && self->region.IsOk() && !self->region.IsEmpty() && self->region.Contains(x, y) == wxInRegion; +} + +BOOL CRgn::RectInRegion(LPCRECT lpRect) const { + GdiLock lock; + GdiObjectImpl* self = GdiImpl(m_hObject, GdiKind::Region); + if (!self || !lpRect || !self->region.IsOk() || self->region.IsEmpty()) + return FALSE; + wxRect r(lpRect->left, lpRect->top, lpRect->right - lpRect->left, lpRect->bottom - lpRect->top); + return r.width > 0 && r.height > 0 && self->region.Contains(r) != wxOutRegion; +} + +int CRgn::GetRgnBox(LPRECT lpRect) const { + GdiLock lock; + GdiObjectImpl* self = GdiImpl(m_hObject, GdiKind::Region); + if (!self || !lpRect) + return ERROR; + wxRect b = self->region.IsOk() ? self->region.GetBox() : wxRect(); + lpRect->left = b.x; + lpRect->top = b.y; + lpRect->right = b.x + b.width; + lpRect->bottom = b.y + b.height; + return RegionType(self->region); +} + +void CRgn::SetRectRgn(int x1, int y1, int x2, int y2) { + GdiLock lock; + if (GdiObjectImpl* self = GdiImpl(m_hObject, GdiKind::Region)) + self->region = RectRegion(x1, y1, x2, y2); +} + +int CRgn::OffsetRgn(int x, int y) { + GdiLock lock; + GdiObjectImpl* self = GdiImpl(m_hObject, GdiKind::Region); + if (!self) + return ERROR; + if (self->region.IsOk() && !self->region.IsEmpty()) + self->region.Offset(x, y); + return RegionType(self->region); +} + +// --------------------------------------------------------------------------------------------- +// Win32 object functions + +HGDIOBJ GetStockObject(int i) { + GdiLock lock; + if (i == kDefaultBitmapIndex || i == 9) + return nullptr; + return reinterpret_cast(Stock(i)); +} + +BOOL DeleteObject(HGDIOBJ ho) { + GdiLock lock; + GdiObjectImpl* impl = GdiImpl(ho); + if (!impl) + return SysColorBrushIndex(ho) >= 0; + if (impl->stock) + return TRUE; + if (impl->deleted) + return FALSE; + impl->deleted = true; + DetachWrappers(impl); + GdiRelease(ho); + return TRUE; +} + +int GetObject(HANDLE h, int c, LPVOID pv) { return GetObjectImpl(reinterpret_cast(h), c, pv); } + +int GetObjectA(HANDLE h, int c, LPVOID pv) { return GetObjectImpl(reinterpret_cast(h), c, pv); } + +HBRUSH CreateSolidBrush(COLORREF color) { + GdiLock lock; + return NewSolidBrush(color); +} + +HBRUSH CreateHatchBrush(int iHatch, COLORREF color) { + GdiLock lock; + return NewHatchBrush(iHatch, color); +} + +HBRUSH CreatePatternBrush(HBITMAP hbm) { + GdiLock lock; + return NewPatternBrush(hbm); +} + +HPEN CreatePen(int iStyle, int cWidth, COLORREF color) { + GdiLock lock; + return NewPen(iStyle, cWidth, color, nullptr, 0, nullptr); +} + +HFONT CreateFont(int cHeight, int cWidth, int cEscapement, int cOrientation, int cWeight, DWORD bItalic, + DWORD bUnderline, DWORD bStrikeOut, DWORD iCharSet, DWORD iOutPrecision, DWORD iClipPrecision, + DWORD iQuality, DWORD iPitchAndFamily, const char* pszFaceName) { + LOGFONT lf; + memset(&lf, 0, sizeof lf); + lf.lfHeight = cHeight; + lf.lfWidth = cWidth; + lf.lfEscapement = cEscapement; + lf.lfOrientation = cOrientation; + lf.lfWeight = cWeight; + lf.lfItalic = static_cast(bItalic != 0); + lf.lfUnderline = static_cast(bUnderline != 0); + lf.lfStrikeOut = static_cast(bStrikeOut != 0); + lf.lfCharSet = static_cast(iCharSet); + lf.lfOutPrecision = static_cast(iOutPrecision); + lf.lfClipPrecision = static_cast(iClipPrecision); + lf.lfQuality = static_cast(iQuality); + lf.lfPitchAndFamily = static_cast(iPitchAndFamily); + SetFace(lf, pszFaceName); + return CreateFontIndirect(&lf); +} + +HFONT CreateFontA(int cHeight, int cWidth, int cEscapement, int cOrientation, int cWeight, DWORD bItalic, + DWORD bUnderline, DWORD bStrikeOut, DWORD iCharSet, DWORD iOutPrecision, DWORD iClipPrecision, + DWORD iQuality, DWORD iPitchAndFamily, const char* pszFaceName) { + return CreateFont(cHeight, cWidth, cEscapement, cOrientation, cWeight, bItalic, bUnderline, bStrikeOut, iCharSet, + iOutPrecision, iClipPrecision, iQuality, iPitchAndFamily, pszFaceName); +} + +HFONT CreateFontIndirect(const LOGFONT* lplf) { + GdiLock lock; + return NewFont(lplf); +} + +HFONT CreateFontIndirectA(const LOGFONT* lplf) { return CreateFontIndirect(lplf); } + +HRGN CreateRectRgn(int x1, int y1, int x2, int y2) { + GdiLock lock; + return NewRegion(RectRegion(x1, y1, x2, y2)); +} + +HRGN CreateEllipticRgn(int x1, int y1, int x2, int y2) { + GdiLock lock; + return NewRegion(EllipseRegion(x1, y1, x2, y2)); +} + +DWORD GetSysColor(int nIndex) { return SysColor(nIndex); } + +HBRUSH GetSysColorBrush(int nIndex) { + GdiLock lock; + static std::map brushes; + GdiObjectImpl*& impl = brushes[nIndex]; + if (!impl) { + impl = new GdiObjectImpl(GdiKind::Brush); + impl->stock = true; + impl->sysColor = nIndex; + impl->logBrush.lbStyle = BS_SOLID; + impl->logBrush.lbColor = SysColor(nIndex); + impl->brush = wxBrush(ToWxColour(impl->logBrush.lbColor)); + Objects().insert(impl); + } + DeviceBrush(reinterpret_cast(impl)); + return reinterpret_cast(impl); +} + +// --------------------------------------------------------------------------------------------- +// Rectangles + +BOOL SetRect(LPRECT lprc, int xLeft, int yTop, int xRight, int yBottom) { + if (!lprc) + return FALSE; + lprc->left = xLeft; + lprc->top = yTop; + lprc->right = xRight; + lprc->bottom = yBottom; + return TRUE; +} + +BOOL SetRectEmpty(LPRECT lprc) { return SetRect(lprc, 0, 0, 0, 0); } + +BOOL CopyRect(LPRECT lprcDst, const RECT* lprcSrc) { + if (!lprcDst || !lprcSrc) + return FALSE; + *lprcDst = *lprcSrc; + return TRUE; +} + +BOOL InflateRect(LPRECT lprc, int dx, int dy) { + if (!lprc) + return FALSE; + lprc->left -= dx; + lprc->right += dx; + lprc->top -= dy; + lprc->bottom += dy; + return TRUE; +} + +BOOL OffsetRect(LPRECT lprc, int dx, int dy) { + if (!lprc) + return FALSE; + lprc->left += dx; + lprc->right += dx; + lprc->top += dy; + lprc->bottom += dy; + return TRUE; +} + +BOOL IsRectEmpty(const RECT* lprc) { return !lprc || lprc->left >= lprc->right || lprc->top >= lprc->bottom; } + +BOOL IntersectRect(LPRECT lprcDst, const RECT* lprcSrc1, const RECT* lprcSrc2) { + if (!lprcDst || !lprcSrc1 || !lprcSrc2) + return FALSE; + RECT r; + r.left = std::max(lprcSrc1->left, lprcSrc2->left); + r.top = std::max(lprcSrc1->top, lprcSrc2->top); + r.right = std::min(lprcSrc1->right, lprcSrc2->right); + r.bottom = std::min(lprcSrc1->bottom, lprcSrc2->bottom); + if (IsRectEmpty(&r)) { + SetRectEmpty(lprcDst); + return FALSE; + } + *lprcDst = r; + return TRUE; +} + +BOOL UnionRect(LPRECT lprcDst, const RECT* lprcSrc1, const RECT* lprcSrc2) { + if (!lprcDst || !lprcSrc1 || !lprcSrc2) + return FALSE; + bool e1 = IsRectEmpty(lprcSrc1), e2 = IsRectEmpty(lprcSrc2); + if (e1 && e2) { + SetRectEmpty(lprcDst); + return FALSE; + } + if (e1) { + *lprcDst = *lprcSrc2; + return TRUE; + } + if (e2) { + *lprcDst = *lprcSrc1; + return TRUE; + } + RECT r; + r.left = std::min(lprcSrc1->left, lprcSrc2->left); + r.top = std::min(lprcSrc1->top, lprcSrc2->top); + r.right = std::max(lprcSrc1->right, lprcSrc2->right); + r.bottom = std::max(lprcSrc1->bottom, lprcSrc2->bottom); + *lprcDst = r; + return TRUE; +} + +BOOL PtInRect(const RECT* lprc, POINT pt) { + return lprc && pt.x >= lprc->left && pt.x < lprc->right && pt.y >= lprc->top && pt.y < lprc->bottom; +} + +BOOL EqualRect(const RECT* lprc1, const RECT* lprc2) { + return lprc1 && lprc2 && lprc1->left == lprc2->left && lprc1->top == lprc2->top && + lprc1->right == lprc2->right && lprc1->bottom == lprc2->bottom; +} diff --git a/port/mfcwx/src/gdi_bits.cpp b/port/mfcwx/src/gdi_bits.cpp new file mode 100644 index 000000000..b4b227d4a --- /dev/null +++ b/port/mfcwx/src/gdi_bits.cpp @@ -0,0 +1,794 @@ +#include "gdi_internal.h" + +#include +#include +#include +#include + +namespace mfcwx { + +namespace { + +unsigned char Rop3Channel(unsigned rop3, unsigned char p, unsigned char s, unsigned char d) { + unsigned out = 0; + for (unsigned k = 0; k < 8; ++k) { + if (!((rop3 >> k) & 1)) + continue; + unsigned pm = (k & 4) ? p : static_cast(~p); + unsigned sm = (k & 2) ? s : static_cast(~s); + unsigned dm = (k & 1) ? d : static_cast(~d); + out |= pm & sm & dm & 0xFF; + } + return static_cast(out); +} + +bool RopUsesSource(DWORD rop) { + unsigned r = (rop >> 16) & 0xFF; + return ((r >> 2) & 0x33) != (r & 0x33); +} + +wxImage Opaque(wxImage img) { + if (img.HasAlpha()) + img.ClearAlpha(); + return img; +} + +wxRect PhysicalRect(const wxRect& r, double scale) { + return wxRect(static_cast(std::lround(r.x * scale)), static_cast(std::lround(r.y * scale)), + static_cast(std::lround(r.width * scale)), static_cast(std::lround(r.height * scale))); +} + +double TargetScale(DCState* s, wxDC* d) { + if (s->kind == DCKind::Memory) + return DCBitmapScale(s); + double scale = d ? d->GetContentScaleFactor() : 1.0; + return scale > 0 ? scale : 1.0; +} + +bool SourceImage(DCState* src, const wxRect& S, wxImage& out) { + if (!src || S.width <= 0 || S.height <= 0) + return false; + if (const wxImage* px = DCPixels(src)) { + double scale = DCBitmapScale(src); + wxRect P = PhysicalRect(S, scale); + wxRect clipped = P; + clipped.Intersect(wxRect(0, 0, px->GetWidth(), px->GetHeight())); + out = wxImage(std::max(1, P.width), std::max(1, P.height), true); + if (!clipped.IsEmpty()) + out.Paste(px->GetSubImage(clipped), clipped.x - P.x, clipped.y - P.y); + return true; + } + wxDC* d = src->Target(false); + if (!d) + return false; + wxBitmap b = d->GetAsBitmap(&S); + if (!b.IsOk()) + return false; + out = b.ConvertToImage(); + return out.IsOk(); +} + +void FitImage(wxImage& img, int w, int h, bool mirrorX, bool mirrorY, bool smooth) { + if (mirrorX) + img = img.Mirror(true); + if (mirrorY) + img = img.Mirror(false); + if (img.GetWidth() != w || img.GetHeight() != h) + img.Rescale(std::max(1, w), std::max(1, h), smooth ? wxIMAGE_QUALITY_BILINEAR : wxIMAGE_QUALITY_NEAREST); +} + +void Invert(wxImage& img) { + unsigned char* p = img.GetData(); + size_t n = static_cast(img.GetWidth()) * img.GetHeight() * 3; + for (size_t i = 0; i < n; ++i) + p[i] = static_cast(255 - p[i]); +} + +void DrawImageAt(wxDC* d, wxImage img, const wxRect& D, double scale) { + d->DrawBitmap(wxBitmap(Opaque(img), -1, scale), D.x, D.y, false); +} + +void DrawMasked(wxDC* d, wxImage img, const wxRect& D, double scale, unsigned char key) { + img = Opaque(img); + img.SetMaskColour(key, key, key); + d->DrawBitmap(wxBitmap(img, -1, scale), D.x, D.y, true); +} + +bool BlitImage(DCState* dst, wxDC* d, const wxRect& D, wxImage img, DWORD rop) { + double scale = TargetScale(dst, d); + switch (rop) { + case SRCCOPY: + DrawImageAt(d, img, D, scale); + return true; + case NOTSRCCOPY: + Invert(img); + DrawImageAt(d, img, D, scale); + return true; + default: + break; + } + if (dst->kind == DCKind::Memory) + return SoftwareRop(dst, D, &img, rop, BrushColor(dst->a.brush)); + switch (rop) { + case SRCAND: + DrawMasked(d, img, D, scale, 255); + return true; + case SRCPAINT: + case SRCINVERT: + DrawMasked(d, img, D, scale, 0); + return true; + case MERGEPAINT: + Invert(img); + DrawMasked(d, img, D, scale, 0); + return true; + case NOTSRCERASE: + Invert(img); + DrawImageAt(d, img, D, scale); + return true; + default: + DrawImageAt(d, img, D, scale); + return true; + } +} + +bool PatternOnly(DCState* dst, wxDC* d, const wxRect& D, DWORD rop) { + switch (rop) { + case BLACKNESS: + FillDeviceRectColor(d, D, *wxBLACK); + return true; + case WHITENESS: + FillDeviceRectColor(d, D, *wxWHITE); + return true; + case PATCOPY: + FillDeviceRect(dst, d, D, dst->a.brush); + return true; + default: + break; + } + if (rop == 0x00AA0029UL) + return true; + if (dst->kind == DCKind::Memory) + return SoftwareRop(dst, D, nullptr, rop, BrushColor(dst->a.brush)); + if (dst->SupportsLogicalOps(d)) { + wxRasterOperationMode mode = rop == DSTINVERT ? wxINVERT : wxXOR; + d->SetLogicalFunction(mode); + d->SetPen(*wxTRANSPARENT_PEN); + d->SetBrush(rop == DSTINVERT ? *wxBLACK_BRUSH : DeviceBrush(dst->a.brush)); + d->DrawRectangle(D); + d->SetLogicalFunction(wxCOPY); + return true; + } + return false; +} + +int MaskShift(DWORD mask) { + int s = 0; + if (!mask) + return 0; + while (!(mask & 1)) { + mask >>= 1; + ++s; + } + return s; +} + +unsigned char MaskValue(DWORD v, DWORD mask) { + if (!mask) + return 0; + int shift = MaskShift(mask); + DWORD m = mask >> shift; + DWORD x = (v & mask) >> shift; + return static_cast(m ? x * 255 / m : 0); +} + +int FloodImpl(DCState* s, int x, int y, COLORREF crColor, UINT type) { + const wxImage* px = DCPixels(s); + if (!px) + return FALSE; + double scale = DCBitmapScale(s); + wxPoint dp = s->ToDevice(x, y); + int sx = static_cast(std::lround(dp.x * scale)), sy = static_cast(std::lround(dp.y * scale)); + int w = px->GetWidth(), h = px->GetHeight(); + if (sx < 0 || sy < 0 || sx >= w || sy >= h) + return FALSE; + const unsigned char* data = px->GetData(); + auto colorAt = [&](int ix, int iy) { + const unsigned char* p = data + (static_cast(iy) * w + ix) * 3; + return RGB(p[0], p[1], p[2]); + }; + COLORREF target = crColor & 0xFFFFFF; + auto inside = [&](int ix, int iy) { + COLORREF c = colorAt(ix, iy); + return type == 1 ? c == target : c != target; + }; + if (!inside(sx, sy)) + return FALSE; + std::vector mask(static_cast(w) * h, 0); + std::vector stack{wxPoint(sx, sy)}; + int minX = sx, maxX = sx, minY = sy, maxY = sy; + while (!stack.empty()) { + wxPoint p = stack.back(); + stack.pop_back(); + if (p.x < 0 || p.y < 0 || p.x >= w || p.y >= h) + continue; + size_t i = static_cast(p.y) * w + p.x; + if (mask[i] || !inside(p.x, p.y)) + continue; + mask[i] = 1; + minX = std::min(minX, p.x); + maxX = std::max(maxX, p.x); + minY = std::min(minY, p.y); + maxY = std::max(maxY, p.y); + stack.emplace_back(p.x + 1, p.y); + stack.emplace_back(p.x - 1, p.y); + stack.emplace_back(p.x, p.y + 1); + stack.emplace_back(p.x, p.y - 1); + } + wxRect box(minX, minY, maxX - minX + 1, maxY - minY + 1); + wxImage sub = Opaque(px->GetSubImage(box)); + wxColour fill = DeviceBrush(s->a.brush).GetColour(); + unsigned char* out = sub.GetData(); + for (int iy = 0; iy < box.height; ++iy) + for (int ix = 0; ix < box.width; ++ix) + if (mask[static_cast(iy + box.y) * w + ix + box.x]) { + unsigned char* q = out + (static_cast(iy) * box.width + ix) * 3; + q[0] = fill.Red(); + q[1] = fill.Green(); + q[2] = fill.Blue(); + } + DrawScope ds(s); + if (!ds) + return FALSE; + ds.dc()->DestroyClippingRegion(); + s->ApplyClip(ds.dc()); + ds.dc()->DrawBitmap(wxBitmap(sub, -1, scale), static_cast(std::lround(box.x / scale)), + static_cast(std::lround(box.y / scale)), false); + return TRUE; +} + +GdiObjectImpl* BitmapImpl(HGDIOBJ h) { return GdiImpl(h, GdiKind::Bitmap); } + +} // namespace + +bool SoftwareRop(DCState* dst, const wxRect& r, const wxImage* src, DWORD rop, COLORREF pattern) { + const wxImage* px = DCPixels(dst); + if (!px) + return false; + double scale = DCBitmapScale(dst); + wxRect P = PhysicalRect(r, scale); + wxRect clipped = P; + clipped.Intersect(wxRect(0, 0, px->GetWidth(), px->GetHeight())); + if (clipped.IsEmpty()) + return true; + wxImage out = Opaque(px->GetSubImage(clipped)); + unsigned rop3 = (rop >> 16) & 0xFF; + unsigned char pr = GetRValue(pattern), pg = GetGValue(pattern), pb = GetBValue(pattern); + unsigned char* o = out.GetData(); + const unsigned char* sp = src ? src->GetData() : nullptr; + int sw = src ? src->GetWidth() : 0, sh = src ? src->GetHeight() : 0; + for (int y = 0; y < clipped.height; ++y) { + for (int x = 0; x < clipped.width; ++x) { + unsigned char* q = o + (static_cast(y) * clipped.width + x) * 3; + unsigned char s0 = 0, s1 = 0, s2 = 0; + int ix = x + clipped.x - P.x, iy = y + clipped.y - P.y; + if (sp && ix < sw && iy < sh) { + const unsigned char* t = sp + (static_cast(iy) * sw + ix) * 3; + s0 = t[0]; + s1 = t[1]; + s2 = t[2]; + } + q[0] = Rop3Channel(rop3, pr, s0, q[0]); + q[1] = Rop3Channel(rop3, pg, s1, q[1]); + q[2] = Rop3Channel(rop3, pb, s2, q[2]); + } + } + DrawScope ds(dst); + if (!ds) + return false; + ds.dc()->DrawBitmap(wxBitmap(out, -1, scale), static_cast(std::lround(clipped.x / scale)), + static_cast(std::lround(clipped.y / scale)), false); + return true; +} + +bool BlitImpl(DCState* dst, int x, int y, int w, int h, DCState* src, int xs, int ys, int ws, int hs, DWORD rop) { + if (!dst) + return false; + wxPoint p1 = dst->ToDevice(x, y), p2 = dst->ToDevice(x + w, y + h); + wxRect D(std::min(p1.x, p2.x), std::min(p1.y, p2.y), std::abs(p2.x - p1.x), std::abs(p2.y - p1.y)); + if (D.width <= 0 || D.height <= 0) + return true; + DrawScope ds(dst); + if (!ds) + return true; + wxDC* d = ds.dc(); + if (!RopUsesSource(rop)) + return PatternOnly(dst, d, D, rop); + if (!src) + return false; + wxPoint q1 = src->ToDevice(xs, ys), q2 = src->ToDevice(xs + ws, ys + hs); + wxRect S(std::min(q1.x, q2.x), std::min(q1.y, q2.y), std::abs(q2.x - q1.x), std::abs(q2.y - q1.y)); + if (S.width <= 0 || S.height <= 0) + return true; + bool mirrorX = (p2.x < p1.x) != (q2.x < q1.x); + bool mirrorY = (p2.y < p1.y) != (q2.y < q1.y); + if (rop == SRCCOPY && !mirrorX && !mirrorY && src->kind != DCKind::Memory) { + wxDC* sd = src->Target(false); + if (sd && d->StretchBlit(D.x, D.y, D.width, D.height, sd, S.x, S.y, S.width, S.height, wxCOPY)) + return true; + } + wxImage img; + if (!SourceImage(src, S, img)) + return false; + double scale = TargetScale(dst, d); + wxRect PD = PhysicalRect(D, scale); + bool smooth = dst->a.stretchMode == HALFTONE && (S.width != D.width || S.height != D.height); + FitImage(img, PD.width, PD.height, mirrorX, mirrorY, smooth); + return BlitImage(dst, d, D, img, rop); +} + +bool DrawImageImpl(DCState* dst, int x, int y, int w, int h, const wxImage& src, int xs, int ys, int ws, int hs, + DWORD rop) { + if (!dst || !src.IsOk() || ws == 0 || hs == 0) + return false; + wxPoint p1 = dst->ToDevice(x, y), p2 = dst->ToDevice(x + w, y + h); + wxRect D(std::min(p1.x, p2.x), std::min(p1.y, p2.y), std::abs(p2.x - p1.x), std::abs(p2.y - p1.y)); + if (D.width <= 0 || D.height <= 0) + return true; + DrawScope ds(dst); + if (!ds) + return true; + wxDC* d = ds.dc(); + if (!RopUsesSource(rop)) + return PatternOnly(dst, d, D, rop); + wxRect S(std::min(xs, xs + ws), std::min(ys, ys + hs), std::abs(ws), std::abs(hs)); + wxRect clipped = S; + clipped.Intersect(wxRect(0, 0, src.GetWidth(), src.GetHeight())); + wxImage img(S.width, S.height, true); + if (!clipped.IsEmpty()) + img.Paste(src.GetSubImage(clipped), clipped.x - S.x, clipped.y - S.y); + bool mirrorX = (p2.x < p1.x) != (ws < 0); + bool mirrorY = (p2.y < p1.y) != (hs < 0); + double scale = TargetScale(dst, d); + wxRect PD = PhysicalRect(D, scale); + bool smooth = dst->a.stretchMode == HALFTONE; + FitImage(img, PD.width, PD.height, mirrorX, mirrorY, smooth); + return BlitImage(dst, d, D, img, rop); +} + +bool DibToImage(const BITMAPINFO* bmi, const void* bits, UINT usage, wxImage& out, int startScan, int lines) { + if (!bmi || !bits) + return false; + const BITMAPINFOHEADER& hdr = bmi->bmiHeader; + int w = hdr.biWidth, h = std::abs(hdr.biHeight), bpp = hdr.biBitCount; + bool bottomUp = hdr.biHeight > 0; + if (w <= 0 || h <= 0 || (hdr.biCompression != BI_RGB && hdr.biCompression != 3)) + return false; + if (bpp != 1 && bpp != 4 && bpp != 8 && bpp != 16 && bpp != 24 && bpp != 32) + return false; + const unsigned char* base = reinterpret_cast(bmi) + hdr.biSize; + DWORD masks[3] = {0, 0, 0}; + if (hdr.biCompression == 3) { + memcpy(masks, base, sizeof masks); + } else if (bpp == 16) { + masks[0] = 0x7C00; + masks[1] = 0x03E0; + masks[2] = 0x001F; + } else if (bpp == 32) { + masks[0] = 0x00FF0000; + masks[1] = 0x0000FF00; + masks[2] = 0x000000FF; + } + const RGBQUAD* palette = reinterpret_cast(base); + int paletteSize = bpp <= 8 ? static_cast(hdr.biClrUsed ? hdr.biClrUsed : (1u << bpp)) : 0; + int stride = ((w * bpp + 31) / 32) * 4; + if (lines < 0) + lines = h; + out.Create(w, h, true); + unsigned char* data = out.GetData(); + const unsigned char* src = static_cast(bits); + for (int i = 0; i < lines; ++i) { + int dibRow = startScan + i; + if (dibRow < 0 || dibRow >= h) + continue; + int iy = bottomUp ? h - 1 - dibRow : dibRow; + const unsigned char* row = src + static_cast(i) * stride; + unsigned char* dstRow = data + static_cast(iy) * w * 3; + for (int x = 0; x < w; ++x) { + unsigned char r = 0, g = 0, b = 0; + if (bpp <= 8) { + int index = 0; + if (bpp == 1) + index = (row[x >> 3] >> (7 - (x & 7))) & 1; + else if (bpp == 4) + index = (row[x >> 1] >> ((x & 1) ? 0 : 4)) & 0xF; + else + index = row[x]; + if (usage == DIB_RGB_COLORS && index < paletteSize) { + r = palette[index].rgbRed; + g = palette[index].rgbGreen; + b = palette[index].rgbBlue; + } else { + r = g = b = static_cast(bpp == 1 ? index * 255 : bpp == 4 ? index * 17 : index); + } + } else if (bpp == 24) { + b = row[x * 3]; + g = row[x * 3 + 1]; + r = row[x * 3 + 2]; + } else { + DWORD v = bpp == 16 ? static_cast(row[x * 2] | (row[x * 2 + 1] << 8)) + : static_cast(row[x * 4] | (row[x * 4 + 1] << 8) | + (row[x * 4 + 2] << 16) | + (static_cast(row[x * 4 + 3]) << 24)); + r = MaskValue(v, masks[0]); + g = MaskValue(v, masks[1]); + b = MaskValue(v, masks[2]); + } + dstRow[x * 3] = r; + dstRow[x * 3 + 1] = g; + dstRow[x * 3 + 2] = b; + } + } + return true; +} + +bool DdbBitsToImage(int width, int height, int bpp, int stride, const void* bits, wxImage& out) { + if (width <= 0 || height <= 0) + return false; + out.Create(width, height, true); + if (!bits) + return true; + const unsigned char* src = static_cast(bits); + unsigned char* data = out.GetData(); + for (int y = 0; y < height; ++y) { + const unsigned char* row = src + static_cast(y) * stride; + unsigned char* d = data + static_cast(y) * width * 3; + for (int x = 0; x < width; ++x) { + unsigned char r = 0, g = 0, b = 0; + switch (bpp) { + case 1: + r = g = b = ((row[x >> 3] >> (7 - (x & 7))) & 1) ? 255 : 0; + break; + case 4: + r = g = b = static_cast(((row[x >> 1] >> ((x & 1) ? 0 : 4)) & 0xF) * 17); + break; + case 8: + r = g = b = row[x]; + break; + case 16: { + unsigned v = row[x * 2] | (row[x * 2 + 1] << 8); + r = static_cast(((v >> 10) & 31) * 255 / 31); + g = static_cast(((v >> 5) & 31) * 255 / 31); + b = static_cast((v & 31) * 255 / 31); + break; + } + case 24: + b = row[x * 3]; + g = row[x * 3 + 1]; + r = row[x * 3 + 2]; + break; + default: + b = row[x * 4]; + g = row[x * 4 + 1]; + r = row[x * 4 + 2]; + break; + } + d[x * 3] = r; + d[x * 3 + 1] = g; + d[x * 3 + 2] = b; + } + } + return true; +} + +bool ImageToDdbBits(const wxImage& img, int bpp, std::vector& out, int& stride) { + int w = img.GetWidth(), h = img.GetHeight(); + if (w <= 0 || h <= 0) + return false; + stride = ((w * bpp + 15) / 16) * 2; + out.assign(static_cast(stride) * h, 0); + const unsigned char* data = img.GetData(); + for (int y = 0; y < h; ++y) { + unsigned char* row = out.data() + static_cast(y) * stride; + const unsigned char* s = data + static_cast(y) * w * 3; + for (int x = 0; x < w; ++x) { + unsigned char r = s[x * 3], g = s[x * 3 + 1], b = s[x * 3 + 2]; + unsigned gray = (r * 30u + g * 59u + b * 11u) / 100u; + switch (bpp) { + case 1: + if (gray >= 128) + row[x >> 3] |= static_cast(0x80 >> (x & 7)); + break; + case 4: + row[x >> 1] |= static_cast((gray / 17) << ((x & 1) ? 0 : 4)); + break; + case 8: + row[x] = static_cast(gray); + break; + case 16: { + unsigned v = ((r >> 3) << 10) | ((g >> 3) << 5) | (b >> 3); + row[x * 2] = static_cast(v & 0xFF); + row[x * 2 + 1] = static_cast(v >> 8); + break; + } + case 24: + row[x * 3] = b; + row[x * 3 + 1] = g; + row[x * 3 + 2] = r; + break; + default: + row[x * 4] = b; + row[x * 4 + 1] = g; + row[x * 4 + 2] = r; + row[x * 4 + 3] = 0; + break; + } + } + } + return true; +} + +} // namespace mfcwx + +using namespace mfcwx; + +// --------------------------------------------------------------------------------------------- +// CBitmap + +BOOL CBitmap::LoadBitmap(const char* lpszResourceName) { + wxBitmap bmp = LoadBitmapResource(ResRef::From(lpszResourceName)); + if (!bmp.IsOk()) + return FALSE; + GdiLock lock; + return Attach(NewBitmapHandle(bmp, 32)); +} + +BOOL CBitmap::LoadBitmap(UINT nIDResource) { + wxBitmap bmp = LoadBitmapResource(ResRef::FromId(nIDResource)); + if (!bmp.IsOk()) + return FALSE; + GdiLock lock; + return Attach(NewBitmapHandle(bmp, 32)); +} + +BOOL CBitmap::LoadOEMBitmap(UINT) { return FALSE; } + +BOOL CBitmap::LoadMappedBitmap(UINT nIDBitmap, UINT, void*, int) { return LoadBitmap(nIDBitmap); } + +BOOL CBitmap::CreateBitmap(int nWidth, int nHeight, UINT nPlanes, UINT nBitcount, const void* lpBits) { + return Attach(::CreateBitmap(nWidth, nHeight, nPlanes, nBitcount, lpBits)); +} + +BOOL CBitmap::CreateBitmapIndirect(LPBITMAP lpBitmap) { + if (!lpBitmap) + return FALSE; + int bpp = std::max(1, lpBitmap->bmBitsPixel * std::max(1, lpBitmap->bmPlanes)); + wxImage img; + int stride = lpBitmap->bmWidthBytes ? lpBitmap->bmWidthBytes : ((lpBitmap->bmWidth * bpp + 15) / 16) * 2; + if (!DdbBitsToImage(lpBitmap->bmWidth, lpBitmap->bmHeight, bpp, stride, lpBitmap->bmBits, img)) + return FALSE; + GdiLock lock; + return Attach(NewBitmapHandle(wxBitmap(img), bpp == 1 ? 1 : bpp)); +} + +BOOL CBitmap::CreateCompatibleBitmap(CDC* pDC, int nWidth, int nHeight) { + return Attach(::CreateCompatibleBitmap(pDC ? pDC->m_hDC : nullptr, nWidth, nHeight)); +} + +BOOL CBitmap::CreateDiscardableBitmap(CDC* pDC, int nWidth, int nHeight) { + return CreateCompatibleBitmap(pDC, nWidth, nHeight); +} + +int CBitmap::GetBitmap(BITMAP* pBitMap) { return GetObject(sizeof(BITMAP), pBitMap); } + +DWORD CBitmap::SetBitmapBits(DWORD dwCount, const void* lpBits) { + GdiLock lock; + GdiObjectImpl* impl = BitmapImpl(m_hObject); + if (!impl || !impl->bitmap.IsOk() || !lpBits) + return 0; + SyncBitmap(impl); + double scale = impl->bitmap.GetScaleFactor(); + int w = static_cast(std::lround(impl->bitmap.GetLogicalWidth())); + int h = static_cast(std::lround(impl->bitmap.GetLogicalHeight())); + int stride = ((w * impl->bitsPixel + 15) / 16) * 2; + std::vector buf(static_cast(stride) * h, 0); + DWORD n = std::min(dwCount, static_cast(buf.size())); + if (n < buf.size()) { + std::vector current; + int st = 0; + wxImage img = impl->bitmap.ConvertToImage(); + if (scale != 1.0) + img.Rescale(w, h, wxIMAGE_QUALITY_NEAREST); + if (ImageToDdbBits(img, impl->bitsPixel, current, st) && current.size() == buf.size()) + buf = current; + } + memcpy(buf.data(), lpBits, n); + wxImage img; + if (!DdbBitsToImage(w, h, impl->bitsPixel, stride, buf.data(), img)) + return 0; + if (scale != 1.0) + img.Rescale(static_cast(std::lround(w * scale)), static_cast(std::lround(h * scale)), + wxIMAGE_QUALITY_NEAREST); + impl->bitmap = wxBitmap(img, -1, scale); + if (impl->selectedIn) + impl->selectedIn->pixels.reset(); + return n; +} + +DWORD CBitmap::GetBitmapBits(DWORD dwCount, LPVOID lpBits) const { + GdiLock lock; + GdiObjectImpl* impl = BitmapImpl(m_hObject); + if (!impl || !impl->bitmap.IsOk() || !lpBits) + return 0; + SyncBitmap(impl); + wxImage img = impl->bitmap.ConvertToImage(); + int w = static_cast(std::lround(impl->bitmap.GetLogicalWidth())); + int h = static_cast(std::lround(impl->bitmap.GetLogicalHeight())); + if (img.GetWidth() != w || img.GetHeight() != h) + img.Rescale(w, h, wxIMAGE_QUALITY_NEAREST); + std::vector buf; + int stride = 0; + if (!ImageToDdbBits(img, impl->bitsPixel, buf, stride)) + return 0; + DWORD n = std::min(dwCount, static_cast(buf.size())); + memcpy(lpBits, buf.data(), n); + return n; +} + +CSize CBitmap::SetBitmapDimension(int nWidth, int nHeight) { + GdiLock lock; + GdiObjectImpl* impl = BitmapImpl(m_hObject); + if (!impl) + return CSize(); + CSize old(impl->dimension); + impl->dimension = SIZE{nWidth, nHeight}; + return old; +} + +CSize CBitmap::GetBitmapDimension() const { + GdiLock lock; + GdiObjectImpl* impl = BitmapImpl(m_hObject); + return impl ? CSize(impl->dimension) : CSize(); +} + +// --------------------------------------------------------------------------------------------- +// CDC raster operations + +BOOL CDC::PatBlt(int x, int y, int nWidth, int nHeight, DWORD dwRop) { + GdiLock lock; + return BlitImpl(DCFromHandle(m_hDC), x, y, nWidth, nHeight, nullptr, 0, 0, 0, 0, dwRop); +} + +BOOL CDC::BitBlt(int x, int y, int nWidth, int nHeight, CDC* pSrcDC, int xSrc, int ySrc, DWORD dwRop) { + return ::BitBlt(m_hDC, x, y, nWidth, nHeight, pSrcDC ? pSrcDC->m_hDC : nullptr, xSrc, ySrc, dwRop); +} + +BOOL CDC::StretchBlt(int x, int y, int nWidth, int nHeight, CDC* pSrcDC, int xSrc, int ySrc, int nSrcWidth, + int nSrcHeight, DWORD dwRop) { + return ::StretchBlt(m_hDC, x, y, nWidth, nHeight, pSrcDC ? pSrcDC->m_hDC : nullptr, xSrc, ySrc, nSrcWidth, + nSrcHeight, dwRop); +} + +BOOL CDC::TransparentBlt(int xDest, int yDest, int nDestWidth, int nDestHeight, CDC* pSrcDC, int xSrc, int ySrc, + int nSrcWidth, int nSrcHeight, UINT clrTransparent) { + GdiLock lock; + DCState* dst = DCFromHandle(m_hDC); + DCState* src = pSrcDC ? DCFromHandle(pSrcDC->m_hDC) : nullptr; + if (!dst || !src) + return FALSE; + wxRect D = dst->DeviceRect(xDest, yDest, xDest + nDestWidth, yDest + nDestHeight); + wxRect S = src->DeviceRect(xSrc, ySrc, xSrc + nSrcWidth, ySrc + nSrcHeight); + if (D.width <= 0 || D.height <= 0) + return TRUE; + wxImage img; + if (!SourceImage(src, S, img)) + return FALSE; + DrawScope ds(dst); + if (!ds) + return TRUE; + double scale = TargetScale(dst, ds.dc()); + wxRect PD = PhysicalRect(D, scale); + FitImage(img, PD.width, PD.height, false, false, false); + img = Opaque(img); + img.SetMaskColour(GetRValue(clrTransparent), GetGValue(clrTransparent), GetBValue(clrTransparent)); + ds.dc()->DrawBitmap(wxBitmap(img, -1, scale), D.x, D.y, true); + return TRUE; +} + +COLORREF CDC::GetPixel(int x, int y) const { return ::GetPixel(m_hDC, x, y); } + +COLORREF CDC::SetPixel(int x, int y, COLORREF crColor) { return ::SetPixel(m_hDC, x, y, crColor); } + +BOOL CDC::FloodFill(int x, int y, COLORREF crColor) { return ExtFloodFill(x, y, crColor, 0); } + +BOOL CDC::ExtFloodFill(int x, int y, COLORREF crColor, UINT nFillType) { + GdiLock lock; + DCState* s = DCFromHandle(m_hDC); + return s ? FloodImpl(s, x, y, crColor, nFillType) : FALSE; +} + +// --------------------------------------------------------------------------------------------- +// Win32 bitmap functions + +HBITMAP CreateCompatibleBitmap(HDC hdc, int cx, int cy) { + GdiLock lock; + DCState* s = DCFromHandle(hdc); + double scale = s ? DCBitmapScale(s) : 1.0; + if (s && s->kind == DCKind::Window && s->window) + scale = s->window->GetContentScaleFactor(); + return NewBitmapHandle(NewBitmap(std::max(1, cx), std::max(1, cy), scale, *wxBLACK), 32); +} + +HBITMAP CreateBitmap(int nWidth, int nHeight, UINT nPlanes, UINT nBitCount, const void* lpBits) { + int bpp = static_cast(std::max(1, nBitCount) * std::max(1, nPlanes)); + wxImage img; + if (!DdbBitsToImage(std::max(1, nWidth), std::max(1, nHeight), bpp, ((std::max(1, nWidth) * bpp + 15) / 16) * 2, + lpBits, img)) + return nullptr; + GdiLock lock; + return NewBitmapHandle(wxBitmap(img), bpp); +} + +BOOL BitBlt(HDC hdc, int x, int y, int cx, int cy, HDC hdcSrc, int x1, int y1, DWORD rop) { + GdiLock lock; + return BlitImpl(DCFromHandle(hdc), x, y, cx, cy, DCFromHandle(hdcSrc), x1, y1, cx, cy, rop); +} + +BOOL StretchBlt(HDC hdcDest, int xDest, int yDest, int wDest, int hDest, HDC hdcSrc, int xSrc, int ySrc, int wSrc, + int hSrc, DWORD rop) { + GdiLock lock; + return BlitImpl(DCFromHandle(hdcDest), xDest, yDest, wDest, hDest, DCFromHandle(hdcSrc), xSrc, ySrc, wSrc, hSrc, + rop); +} + +COLORREF SetPixel(HDC hdc, int x, int y, COLORREF color) { + GdiLock lock; + DCState* s = DCFromHandle(hdc); + if (!s) + return CLR_INVALID; + DrawScope ds(s); + if (ds) + FillDeviceRectColor(ds.dc(), wxRect(s->ToDevice(x, y), wxSize(1, 1)), ToWxColour(color)); + return color & 0xFFFFFF; +} + +COLORREF GetPixel(HDC hdc, int x, int y) { + GdiLock lock; + DCState* s = DCFromHandle(hdc); + const wxImage* px = DCPixels(s); + if (!px) + return CLR_INVALID; + double scale = DCBitmapScale(s); + wxPoint p = s->ToDevice(x, y); + int ix = static_cast(std::floor(p.x * scale)), iy = static_cast(std::floor(p.y * scale)); + if (ix < 0 || iy < 0 || ix >= px->GetWidth() || iy >= px->GetHeight()) + return CLR_INVALID; + const unsigned char* d = px->GetData() + (static_cast(iy) * px->GetWidth() + ix) * 3; + return RGB(d[0], d[1], d[2]); +} + +int SetDIBitsToDevice(HDC hdc, int xDest, int yDest, DWORD w, DWORD h, int xSrc, int ySrc, UINT StartScan, + UINT cLines, const void* lpvBits, const BITMAPINFO* lpbmi, UINT ColorUse) { + wxImage img; + if (!DibToImage(lpbmi, lpvBits, ColorUse, img, static_cast(StartScan), static_cast(cLines))) + return 0; + int iw = static_cast(w), ih = static_cast(h); + int top = lpbmi->bmiHeader.biHeight > 0 ? img.GetHeight() - ySrc - ih : ySrc; + GdiLock lock; + DCState* s = DCFromHandle(hdc); + if (!s || !DrawImageImpl(s, xDest, yDest, iw, ih, img, xSrc, top, iw, ih, SRCCOPY)) + return 0; + return static_cast(cLines); +} + +int StretchDIBits(HDC hdc, int xDest, int yDest, int DestWidth, int DestHeight, int xSrc, int ySrc, int SrcWidth, + int SrcHeight, const void* lpBits, const BITMAPINFO* lpbmi, UINT iUsage, DWORD rop) { + wxImage img; + if (!DibToImage(lpbmi, lpBits, iUsage, img)) + return 0; + int ys = lpbmi->bmiHeader.biHeight > 0 ? img.GetHeight() - ySrc - SrcHeight : ySrc; + GdiLock lock; + DCState* s = DCFromHandle(hdc); + if (!s || !DrawImageImpl(s, xDest, yDest, DestWidth, DestHeight, img, xSrc, ys, SrcWidth, SrcHeight, rop)) + return 0; + return std::abs(SrcHeight); +} diff --git a/port/mfcwx/src/gdi_dc.cpp b/port/mfcwx/src/gdi_dc.cpp new file mode 100644 index 000000000..163849544 --- /dev/null +++ b/port/mfcwx/src/gdi_dc.cpp @@ -0,0 +1,2331 @@ +#include "gdi_internal.h" + +#include +#include +#include +#include +#include + +IMPLEMENT_DYNCREATE(CDC, CObject) +IMPLEMENT_DYNAMIC(CPaintDC, CDC) +IMPLEMENT_DYNAMIC(CClientDC, CDC) +IMPLEMENT_DYNAMIC(CWindowDC, CDC) + +namespace mfcwx { + +namespace { + +struct PaintSession { + wxDC* dc = nullptr; + std::unique_ptr own; + std::vector users; +}; + +struct Overlay { + wxBitmap bitmap; + std::unique_ptr dc; + std::vector users; +}; + +struct ClipInfo { + DCState* owner = nullptr; + bool clipped = false; +}; + +std::unordered_set& States() { + static auto* states = new std::unordered_set; + return *states; +} + +std::unordered_map& TempDCs() { + static auto* temps = new std::unordered_map; + return *temps; +} + +std::vector& DetachedTempDCs() { + static auto* detached = new std::vector; + return *detached; +} + +std::unordered_map& Sessions() { + static auto* sessions = new std::unordered_map; + return *sessions; +} + +std::unordered_map& Overlays() { + static auto* overlays = new std::unordered_map; + return *overlays; +} + +std::unordered_map& ClipOwners() { + static auto* owners = new std::unordered_map; + return *owners; +} + +std::unordered_set& HookedWindows() { + static auto* hooked = new std::unordered_set; + return *hooked; +} + +void Remove(std::vector& v, DCState* s) { v.erase(std::remove(v.begin(), v.end(), s), v.end()); } + +void ForgetDC(wxDC* d) { + if (d) + ClipOwners().erase(d); +} + +bool NativePainting(wxWindow* w) { +#if defined(__WXOSX__) + return w && w->MacGetCGContextRef() != nullptr; +#elif defined(__WXGTK3__) + return w && w->GTKPaintContext() != nullptr; +#else + (void)w; + return false; +#endif +} + +bool ClientDrawsDirectly() { +#if defined(__WXOSX__) + return false; +#elif defined(__WXGTK__) + static bool wayland = wxGetDisplayInfo().type == wxDisplayWayland; + return !wayland; +#else + return true; +#endif +} + +void OnWindowDestroyed(wxWindow* w); + +void HookDestroy(wxWindow* w) { + if (!w || !HookedWindows().insert(w).second) + return; + w->Bind(wxEVT_DESTROY, [w](wxWindowDestroyEvent& e) { + e.Skip(); + if (e.GetEventObject() == w) + OnWindowDestroyed(w); + }); +} + +void EndSession(wxWindow* w) { + auto it = Sessions().find(w); + if (it == Sessions().end()) + return; + PaintSession& ps = it->second; + for (DCState* u : ps.users) { + u->dc = nullptr; + u->target = WinTarget::None; + } + ForgetDC(ps.dc); + std::unique_ptr own = std::move(ps.own); + Sessions().erase(it); + own.reset(); +} + +void ReleaseOverlayDC(Overlay& ov) { + for (DCState* u : ov.users) { + u->dc = nullptr; + u->target = WinTarget::None; + } + ov.users.clear(); + if (ov.dc) { + ForgetDC(ov.dc.get()); + ov.dc->SelectObject(wxNullBitmap); + ov.dc.reset(); + } +} + +void ReleaseTarget(DCState* s) { + if (s->kind != DCKind::Window) + return; + if (s->dc) { + auto c = ClipOwners().find(s->dc); + if (c != ClipOwners().end() && c->second.owner == s) { + if (c->second.clipped && s->target != WinTarget::Client && s->target != WinTarget::OverlayPending) + s->dc->DestroyClippingRegion(); + ClipOwners().erase(c); + } + } + if (s->target == WinTarget::Paint && s->window) { + auto it = Sessions().find(s->window); + if (it != Sessions().end()) { + Remove(it->second.users, s); + if (it->second.own && it->second.users.empty()) + EndSession(s->window); + } + } else if (s->target == WinTarget::Overlay && s->window) { + auto it = Overlays().find(s->window); + if (it != Overlays().end()) { + Remove(it->second.users, s); + if (it->second.users.empty() && it->second.dc) { + ForgetDC(it->second.dc.get()); + it->second.dc->SelectObject(wxNullBitmap); + it->second.dc.reset(); + } + } + } else if (s->owned) { + ForgetDC(s->owned.get()); + s->owned.reset(); + } + s->dc = nullptr; + s->target = WinTarget::None; +} + +wxBitmap TransparentBitmap(wxSize size, double scale) { + int pw = std::max(1, static_cast(std::lround(size.x * scale))); + int ph = std::max(1, static_cast(std::lround(size.y * scale))); + wxImage img(pw, ph, true); + img.InitAlpha(); + memset(img.GetAlpha(), 0, static_cast(pw) * ph); + return wxBitmap(img, 32, scale); +} + +void AcquireWindowTarget(DCState* s) { + wxWindow* w = s->window; + auto it = Sessions().find(w); + if (it == Sessions().end() && NativePainting(w)) { + PaintSession ps; + ps.own.reset(new wxPaintDC(w)); + ps.dc = ps.own.get(); + it = Sessions().emplace(w, std::move(ps)).first; + HookDestroy(w); + } + if (it != Sessions().end()) { + s->dc = it->second.dc; + s->target = WinTarget::Paint; + it->second.users.push_back(s); + } else if (ClientDrawsDirectly()) { + if (s->wholeWindow) + s->owned.reset(new wxWindowDC(w)); + else + s->owned.reset(new wxClientDC(w)); + s->dc = s->owned.get(); + s->target = WinTarget::Client; + } else { + s->owned.reset(new wxClientDC(w)); + s->dc = s->owned.get(); + s->target = WinTarget::OverlayPending; + } + s->clipDirty = true; +} + +void SwitchToOverlay(DCState* s) { + wxWindow* w = s->window; + if (s->owned) { + ForgetDC(s->owned.get()); + s->owned.reset(); + } + s->dc = nullptr; + wxSize size = w->GetClientSize(); + size.x = std::max(1, size.x); + size.y = std::max(1, size.y); + double scale = w->GetContentScaleFactor(); + Overlay& ov = Overlays()[w]; + if (ov.bitmap.IsOk()) { + wxSize cur(static_cast(std::lround(ov.bitmap.GetLogicalWidth())), + static_cast(std::lround(ov.bitmap.GetLogicalHeight()))); + if (cur != size || std::fabs(ov.bitmap.GetScaleFactor() - scale) > 1e-6) { + ReleaseOverlayDC(ov); + ov.bitmap = wxBitmap(); + } + } + if (!ov.bitmap.IsOk()) + ov.bitmap = TransparentBitmap(size, scale); + if (!ov.dc) { + ov.dc.reset(new wxMemoryDC); + ov.dc->SelectObject(ov.bitmap); + } + ov.users.push_back(s); + s->dc = ov.dc.get(); + s->target = WinTarget::Overlay; + s->clipDirty = true; + HookDestroy(w); +} + +void OnWindowDestroyed(wxWindow* w) { + GdiLock lock; + for (DCState* s : States()) { + if (s->window != w) + continue; + if (s->kind == DCKind::Window) + ReleaseTarget(s); + s->window = nullptr; + } + EndSession(w); + auto it = Overlays().find(w); + if (it != Overlays().end()) { + ReleaseOverlayDC(it->second); + Overlays().erase(it); + } + HookedWindows().erase(w); +} + +double ScreenScale() { + wxWindow* top = wxTheApp ? wxTheApp->GetTopWindow() : nullptr; + return top ? top->GetContentScaleFactor() : 1.0; +} + +wxBitmap& CurrentBitmap(DCState* s) { + GdiObjectImpl* impl = s->SelectedBitmap(); + if (impl && impl->bitmap.IsOk()) + return impl->bitmap; + return s->defaultBitmap; +} + +void DeselectBitmap(DCState* s) { + if (s->kind != DCKind::Memory || s->bitmapOut || !s->dc) + return; + static_cast(s->dc)->SelectObject(wxNullBitmap); + s->bitmapOut = true; +} + +void ReselectBitmap(DCState* s) { + if (s->kind != DCKind::Memory || !s->dc) + return; + static_cast(s->dc)->SelectObject(CurrentBitmap(s)); + s->bitmapOut = false; + s->clipDirty = true; +} + +DCAttrs DefaultAttrs(bool memory) { + DCAttrs a; + a.pen = ::GetStockObject(BLACK_PEN); + a.brush = ::GetStockObject(WHITE_BRUSH); + a.font = ::GetStockObject(SYSTEM_FONT); + a.palette = ::GetStockObject(DEFAULT_PALETTE); + a.bitmap = memory ? DefaultBitmapHandle() : nullptr; + return a; +} + +void AddRefs(const DCAttrs& a) { + GdiAddRef(a.pen); + GdiAddRef(a.brush); + GdiAddRef(a.font); + GdiAddRef(a.bitmap); + GdiAddRef(a.palette); +} + +void ReleaseRefs(const DCAttrs& a) { + GdiRelease(a.pen); + GdiRelease(a.brush); + GdiRelease(a.font); + GdiRelease(a.bitmap); + GdiRelease(a.palette); +} + +DCState* NewState(DCKind kind) { + auto* s = new DCState; + s->kind = kind; + s->a = DefaultAttrs(kind == DCKind::Memory); + States().insert(s); + return s; +} + +DCState* NewWindowState(wxWindow* w, bool whole) { + if (!w) + return NewState(DCKind::Info); + DCState* s = NewState(DCKind::Window); + s->window = w; + s->wholeWindow = whole; + s->scale = w->GetContentScaleFactor(); + HookDestroy(w); + return s; +} + +DCState* NewMemoryState(DCState* src) { + DCState* s = NewState(DCKind::Memory); + if (src) + s->scale = src->kind == DCKind::Memory ? CurrentBitmap(src).GetScaleFactor() : src->scale; + else + s->scale = ScreenScale(); + if (s->scale <= 0) + s->scale = 1; + s->defaultBitmap = NewBitmap(1, 1, s->scale, *wxWHITE); + auto* mdc = new wxMemoryDC; + mdc->SelectObject(s->defaultBitmap); + s->owned.reset(mdc); + s->dc = mdc; + return s; +} + +void FreeState(DCState* s) { + if (!s || !States().count(s)) + return; + if (s->owner) { + s->owner->m_hDC = nullptr; + s->owner->m_hAttribDC = nullptr; + s->owner = nullptr; + } + auto it = TempDCs().find(ToHdc(s)); + if (it != TempDCs().end()) { + it->second->m_hDC = nullptr; + it->second->m_hAttribDC = nullptr; + DetachedTempDCs().push_back(it->second); + TempDCs().erase(it); + } + if (s->kind == DCKind::Window) { + ReleaseTarget(s); + } else if (s->kind == DCKind::Memory) { + if (GdiObjectImpl* b = s->SelectedBitmap()) + b->selectedIn = nullptr; + DeselectBitmap(s); + } else if (s->kind == DCKind::Borrowed && s->dc) { + auto c = ClipOwners().find(s->dc); + if (c != ClipOwners().end() && c->second.clipped) + s->dc->DestroyClippingRegion(); + } + if (s->sessionOwner && s->window) + EndSession(s->window); + for (auto c = ClipOwners().begin(); c != ClipOwners().end();) { + if (c->second.owner == s) + c = ClipOwners().erase(c); + else + ++c; + } + if (s->owned) { + ForgetDC(s->owned.get()); + s->owned.reset(); + } + ReleaseRefs(s->a); + for (const DCAttrs& saved : s->saved) + ReleaseRefs(saved); + States().erase(s); + delete s; +} + +int RegionType(const wxRegion& r) { + if (!r.IsOk() || r.IsEmpty()) + return NULLREGION; + int n = 0; + for (wxRegionIterator it(r); it && n < 2; ++it) + ++n; + return n <= 1 ? SIMPLEREGION : COMPLEXREGION; +} + +wxRasterOperationMode Rop2ToWx(int rop2) { + switch (rop2) { + case R2_BLACK: + return wxCLEAR; + case R2_NOTMERGEPEN: + return wxNOR; + case R2_MASKNOTPEN: + return wxAND_INVERT; + case R2_NOTCOPYPEN: + return wxSRC_INVERT; + case R2_MASKPENNOT: + return wxAND_REVERSE; + case R2_NOT: + return wxINVERT; + case R2_XORPEN: + return wxXOR; + case R2_NOTMASKPEN: + return wxNAND; + case R2_MASKPEN: + return wxAND; + case R2_NOTXORPEN: + return wxEQUIV; + case R2_NOP: + return wxNO_OP; + case R2_MERGENOTPEN: + return wxOR_INVERT; + case R2_MERGEPENNOT: + return wxOR_REVERSE; + case R2_MERGEPEN: + return wxOR; + case R2_WHITE: + return wxSET; + default: + return wxCOPY; + } +} + +wxColour Rop2Colour(int rop2, const wxColour& c) { + switch (rop2) { + case R2_BLACK: + return *wxBLACK; + case R2_WHITE: + return *wxWHITE; + case R2_NOTCOPYPEN: + return wxColour(255 - c.Red(), 255 - c.Green(), 255 - c.Blue()); + default: + return c; + } +} + +void SetRop(DCState* s, wxDC* d, int rop2) { + if (!s->SupportsLogicalOps(d)) + return; + wxRasterOperationMode m = Rop2ToWx(rop2); + if (d->GetLogicalFunction() != m) + d->SetLogicalFunction(m); +} + +HGDIOBJ SelectBitmapImpl(DCState* s, HGDIOBJ h, GdiObjectImpl* impl) { + if (s->kind != DCKind::Memory) + return nullptr; + if (!impl->stock && impl->selectedIn && impl->selectedIn != s) + return nullptr; + HGDIOBJ old = s->a.bitmap; + if (old == h) + return old; + if (GdiObjectImpl* cur = s->SelectedBitmap()) + cur->selectedIn = nullptr; + DeselectBitmap(s); + GdiAddRef(h); + s->a.bitmap = h; + if (!impl->stock) + impl->selectedIn = s; + ReselectBitmap(s); + s->pixels.reset(); + GdiRelease(old); + return old; +} + +int SelectClipImpl(DCState* s, const wxRegion* rgn) { + if (!rgn) { + s->a.hasClip = false; + s->a.clip = wxRegion(); + } else { + s->a.hasClip = true; + s->a.clip = *rgn; + } + s->clipDirty = true; + return s->a.hasClip ? RegionType(s->a.clip) : SIMPLEREGION; +} + +HGDIOBJ SelectImpl(DCState* s, HGDIOBJ h) { + if (!s || !h) + return nullptr; + HGDIOBJ old = nullptr; + if (SysColorBrushIndex(h) >= 0) { + old = s->a.brush; + s->a.brush = h; + GdiRelease(old); + return old; + } + GdiObjectImpl* impl = GdiImpl(h); + if (!impl) + return nullptr; + HGDIOBJ* slot = nullptr; + switch (impl->kind) { + case GdiKind::Pen: + slot = &s->a.pen; + break; + case GdiKind::Brush: + slot = &s->a.brush; + break; + case GdiKind::Font: + slot = &s->a.font; + break; + case GdiKind::Palette: + slot = &s->a.palette; + break; + case GdiKind::Bitmap: + return SelectBitmapImpl(s, h, impl); + case GdiKind::Region: + return reinterpret_cast(static_cast(SelectClipImpl(s, &impl->region))); + } + old = *slot; + if (old == h) + return old; + GdiAddRef(h); + *slot = h; + GdiRelease(old); + return old; +} + +void RestoreAttrs(DCState* s, const DCAttrs& saved) { + SelectImpl(s, saved.pen); + SelectImpl(s, saved.brush); + SelectImpl(s, saved.font); + SelectImpl(s, saved.palette); + if (s->kind == DCKind::Memory && saved.bitmap) + SelectImpl(s, saved.bitmap); + DCAttrs keep = s->a; + s->a = saved; + s->a.pen = keep.pen; + s->a.brush = keep.brush; + s->a.font = keep.font; + s->a.palette = keep.palette; + s->a.bitmap = keep.bitmap; + s->clipDirty = true; +} + +bool HasPen(DCState* s) { return !PenIsNull(s->a.pen) && s->a.rop2 != R2_NOP; } + +int DevicePenWidth(DCState* s) { + double sx, sy; + s->Scales(sx, sy); + return s->a.pen ? std::max(1, static_cast(std::lround(PenWidth(s->a.pen) * std::fabs(sx)))) : 1; +} + +void SetSolidBrush(wxDC* d, const wxColour& c) { + wxBrush b(c); + if (d->GetBrush() != b) + d->SetBrush(b); +} + +void SetNoPen(wxDC* d) { + if (d->GetPen().GetStyle() != wxPENSTYLE_TRANSPARENT) + d->SetPen(*wxTRANSPARENT_PEN); +} + +bool NeedsHatchBackground(DCState* s) { return s->a.bkMode == OPAQUE && BrushIsHatched(s->a.brush); } + +template +void DrawShape(DCState* s, wxDC* d, bool fill, F draw) { + if (fill && NeedsHatchBackground(s)) { + SetRop(s, d, R2_COPYPEN); + SetNoPen(d); + SetSolidBrush(d, ToWxColour(s->a.bkColor)); + draw(); + } + s->ApplyPen(d); + s->ApplyBrush(d, fill); + draw(); + SetRop(s, d, R2_COPYPEN); +} + +void DrawSegment(DCState* s, wxDC* d, wxPoint p1, wxPoint p2) { + if (p1 == p2) + return; + int width = DevicePenWidth(s); + bool solid = PenStyle(s->a.pen) == PS_SOLID || PenStyle(s->a.pen) == PS_INSIDEFRAME; + if (width <= 1 && solid && (p1.x == p2.x || p1.y == p2.y)) { + if (p1.x == p2.x) + p2.y += p2.y > p1.y ? -1 : 1; + else + p2.x += p2.x > p1.x ? -1 : 1; + if (p1 == p2) { + SetRop(s, d, s->a.rop2); + FillDeviceRectColor(d, wxRect(p1, wxSize(1, 1)), Rop2Colour(s->a.rop2, ToWxColour(PenColor(s->a.pen)))); + SetRop(s, d, R2_COPYPEN); + return; + } + } + s->ApplyPen(d); + d->DrawLine(p1, p2); + SetRop(s, d, R2_COPYPEN); +} + +void DrawPolyline(DCState* s, wxDC* d, const std::vector& pts) { + if (pts.size() < 2 || !HasPen(s)) + return; + bool solid = PenStyle(s->a.pen) == PS_SOLID || PenStyle(s->a.pen) == PS_INSIDEFRAME; + if (DevicePenWidth(s) <= 1 && solid) { + for (size_t i = 0; i + 1 < pts.size(); ++i) + DrawSegment(s, d, pts[i], pts[i + 1]); + return; + } + s->ApplyPen(d); + d->DrawLines(static_cast(pts.size()), pts.data()); + SetRop(s, d, R2_COPYPEN); +} + +std::vector DevicePoints(DCState* s, const POINT* p, int n) { + std::vector out; + out.reserve(static_cast(std::max(0, n))); + for (int i = 0; i < n; ++i) + out.push_back(s->ToDevice(p[i].x, p[i].y)); + return out; +} + +std::vector ArcPoints(const wxRect& r, wxPoint start, wxPoint end, bool full) { + double rx = (r.width - 1) / 2.0, ry = (r.height - 1) / 2.0; + double cx = r.x + rx, cy = r.y + ry; + if (rx <= 0 || ry <= 0) + return {}; + auto angle = [&](wxPoint p) { return std::atan2(-(p.y - cy) / ry, (p.x - cx) / rx); }; + double a0 = angle(start), a1 = angle(end); + if (full || a1 <= a0 + 1e-9) + a1 += 2 * M_PI; + int n = std::max(8, static_cast((rx + ry) * (a1 - a0) / 4)); + std::vector pts; + for (int i = 0; i <= n; ++i) { + double t = a0 + (a1 - a0) * i / n; + pts.emplace_back(static_cast(std::lround(cx + rx * std::cos(t))), + static_cast(std::lround(cy - ry * std::sin(t)))); + } + return pts; +} + +void FillRegionRects(DCState* s, wxDC* d, const wxRegion& logical, HBRUSH brush) { + if (!logical.IsOk() || logical.IsEmpty()) + return; + for (wxRegionIterator it(logical); it; ++it) { + wxRect r = it.GetRect(); + FillDeviceRect(s, d, s->DeviceRect(r.x, r.y, r.x + r.width, r.y + r.height), brush); + } +} + +void EdgeRect(wxDC* d, const wxRect& r, UINT flags, COLORREF lt, COLORREF rb) { + if (flags & BF_LEFT) + FillDeviceRectColor(d, wxRect(r.x, r.y, 1, r.height), ToWxColour(lt)); + if (flags & BF_TOP) + FillDeviceRectColor(d, wxRect(r.x, r.y, r.width, 1), ToWxColour(lt)); + if (flags & BF_RIGHT) + FillDeviceRectColor(d, wxRect(r.x + r.width - 1, r.y, 1, r.height), ToWxColour(rb)); + if (flags & BF_BOTTOM) + FillDeviceRectColor(d, wxRect(r.x, r.y + r.height - 1, r.width, 1), ToWxColour(rb)); +} + +wxRect ShrinkEdges(const wxRect& r, UINT flags) { + wxRect o = r; + if (flags & BF_LEFT) { + o.x += 1; + o.width -= 1; + } + if (flags & BF_TOP) { + o.y += 1; + o.height -= 1; + } + if (flags & BF_RIGHT) + o.width -= 1; + if (flags & BF_BOTTOM) + o.height -= 1; + return o; +} + +} // namespace + +// --------------------------------------------------------------------------------------------- +// DCState + +GdiObjectImpl* DCState::SelectedBitmap() const { + GdiObjectImpl* impl = GdiImpl(a.bitmap, GdiKind::Bitmap); + return impl && !impl->stock ? impl : nullptr; +} + +wxDC* DCState::Target(bool drawing) { + wxDC* d = nullptr; + switch (kind) { + case DCKind::Info: + return nullptr; + case DCKind::Borrowed: + d = dc; + break; + case DCKind::Memory: + if (bitmapOut) + ReselectBitmap(this); + d = dc; + break; + case DCKind::Window: + if (!window) + return nullptr; + if (target == WinTarget::Paint) { + auto it = Sessions().find(window); + if (it == Sessions().end()) + ReleaseTarget(this); + else if (it->second.own && !NativePainting(window)) + EndSession(window); + } + if (target == WinTarget::None) + AcquireWindowTarget(this); + if (drawing && target == WinTarget::OverlayPending) + SwitchToOverlay(this); + d = dc; + break; + } + if (d && drawing) { + auto it = ClipOwners().find(d); + bool mine = it != ClipOwners().end() && it->second.owner == this; + if (clipDirty || !mine) + ApplyClip(d); + } + return d; +} + +wxDC* DCState::Measure() { + wxDC* d = Target(false); + if (!d) + d = &MeasureDC(); + ApplyFont(d); + return d; +} + +void DCState::Finish() { + if (kind == DCKind::Memory) + pixels.reset(); + else if (kind == DCKind::Window && target == WinTarget::Overlay && window) + window->Refresh(false); +} + +void DCState::Scales(double& sx, double& sy) const { + if (a.mapMode == MM_TEXT) { + sx = sy = 1.0; + return; + } + sx = a.winExt.cx ? static_cast(a.vpExt.cx) / a.winExt.cx : 1.0; + sy = a.winExt.cy ? static_cast(a.vpExt.cy) / a.winExt.cy : 1.0; + if (sx == 0) + sx = 1; + if (sy == 0) + sy = 1; +} + +wxPoint DCState::ToDevice(double x, double y) const { + double sx, sy; + Scales(sx, sy); + return wxPoint(static_cast(std::lround((x - a.winOrg.x) * sx + a.vpOrg.x)), + static_cast(std::lround((y - a.winOrg.y) * sy + a.vpOrg.y))); +} + +void DCState::ToLogical(double& x, double& y) const { + double sx, sy; + Scales(sx, sy); + x = (x - a.vpOrg.x) / sx + a.winOrg.x; + y = (y - a.vpOrg.y) / sy + a.winOrg.y; +} + +wxRect DCState::DeviceRect(int l, int t, int r, int b) const { + wxPoint p1 = ToDevice(l, t), p2 = ToDevice(r, b); + return wxRect(std::min(p1.x, p2.x), std::min(p1.y, p2.y), std::abs(p2.x - p1.x), std::abs(p2.y - p1.y)); +} + +int DCState::LogicalWidth(int deviceCx) const { + double sx, sy; + Scales(sx, sy); + return static_cast(std::lround(deviceCx / std::fabs(sx))); +} + +int DCState::LogicalHeight(int deviceCy) const { + double sx, sy; + Scales(sx, sy); + return static_cast(std::lround(deviceCy / std::fabs(sy))); +} + +int DCState::DeviceWidth(int logicalCx) const { + double sx, sy; + Scales(sx, sy); + return static_cast(std::lround(logicalCx * std::fabs(sx))); +} + +int DCState::DeviceHeight(int logicalCy) const { + double sx, sy; + Scales(sx, sy); + return static_cast(std::lround(logicalCy * std::fabs(sy))); +} + +int DCState::Dpi() const { + if (owner && owner->m_bPrinting && dc) { + int ppi = dc->GetPPI().y; + if (ppi > 0) + return ppi; + } + return 96; +} + +bool DCState::SupportsLogicalOps(wxDC* d) const { return d && d->GetGraphicsContext() == nullptr; } + +void DCState::ApplyPen(wxDC* d) { + double sx, sy; + Scales(sx, sy); + wxPen p = DevicePen(a.pen, std::fabs(sx)); + if (a.rop2 == R2_NOP) { + p = *wxTRANSPARENT_PEN; + } else if (a.rop2 != R2_COPYPEN) { + if (SupportsLogicalOps(d)) + SetRop(this, d, a.rop2); + else if (p.GetStyle() != wxPENSTYLE_TRANSPARENT) + p.SetColour(Rop2Colour(a.rop2, p.GetColour())); + } + if (d->GetPen() != p) + d->SetPen(p); +} + +void DCState::ApplyBrush(wxDC* d, bool fill) { + wxBrush b = fill ? DeviceBrush(a.brush) : *wxTRANSPARENT_BRUSH; + if (fill && a.rop2 != R2_COPYPEN && !SupportsLogicalOps(d) && b.GetStyle() == wxBRUSHSTYLE_SOLID) + b = wxBrush(Rop2Colour(a.rop2, b.GetColour())); + if (d->GetBrush() != b) + d->SetBrush(b); +} + +void DCState::ApplyFont(wxDC* d) { + double sx, sy; + Scales(sx, sy); + wxFont f = DeviceFont(a.font, std::fabs(sy)); + if (f.IsOk() && d->GetFont() != f) + d->SetFont(f); + wxColour fg = ToWxColour(a.textColor); + if (d->GetTextForeground() != fg) + d->SetTextForeground(fg); + if (d->GetBackgroundMode() != wxBRUSHSTYLE_TRANSPARENT) + d->SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT); +} + +void DCState::ApplyClip(wxDC* d, const wxRect* extra) { + ClipInfo& info = ClipOwners()[d]; + wxRegion r; + bool any = false; + if (a.hasClip) { + r = a.clip; + any = true; + } + if (extra) { + if (any) { + if (r.IsOk() && !r.IsEmpty()) + r.Intersect(*extra); + } else { + r = wxRegion(*extra); + } + any = true; + } + if (!any) { + if (info.clipped) + d->DestroyClippingRegion(); + info.clipped = false; + } else { + d->DestroyClippingRegion(); + if (!r.IsOk() || r.IsEmpty()) { + d->SetClippingRegion(wxRect(-32000, -32000, 1, 1)); + } else if (RegionType(r) == SIMPLEREGION) { + d->SetClippingRegion(r.GetBox()); + } else { + wxRegion dev = r; + dev.Offset(d->LogicalToDeviceX(0), d->LogicalToDeviceY(0)); + d->SetDeviceClippingRegion(dev); + } + info.clipped = true; + } + info.owner = this; + if (!extra) + clipDirty = false; + else + clipDirty = true; +} + +DCState* DCFromHandle(HDC h) { + if (!h) + return nullptr; + GdiLock lock; + auto* s = reinterpret_cast(h); + return States().count(s) ? s : nullptr; +} + +wxDC& MeasureDC() { + static wxMemoryDC* dc = [] { + auto* d = new wxMemoryDC; + static wxBitmap bmp(1, 1, 24); + d->SelectObject(bmp); + return d; + }(); + return *dc; +} + +void SyncBitmap(GdiObjectImpl* bmp) { + if (!bmp || !bmp->selectedIn) + return; + DeselectBitmap(bmp->selectedIn); +} + +const wxImage* DCPixels(DCState* s) { + if (!s || s->kind != DCKind::Memory) + return nullptr; + if (!s->pixels) { + DeselectBitmap(s); + wxBitmap& bmp = CurrentBitmap(s); + s->pixels.reset(new wxImage(bmp.ConvertToImage())); + } + return s->pixels.get(); +} + +double DCBitmapScale(DCState* s) { + if (!s) + return 1.0; + if (s->kind == DCKind::Memory) { + double scale = CurrentBitmap(s).GetScaleFactor(); + return scale > 0 ? scale : 1.0; + } + if (s->dc) + return s->dc->GetContentScaleFactor(); + return s->scale; +} + +void FillDeviceRectColor(wxDC* dc, const wxRect& r, const wxColour& c) { + if (r.width <= 0 || r.height <= 0) + return; + SetNoPen(dc); + SetSolidBrush(dc, c); + dc->DrawRectangle(r); +} + +void FillDeviceRect(DCState* s, wxDC* dc, const wxRect& r, HBRUSH brush) { + if (r.width <= 0 || r.height <= 0) + return; + if (s && s->a.bkMode == OPAQUE && BrushIsHatched(brush)) + FillDeviceRectColor(dc, r, ToWxColour(s->a.bkColor)); + wxBrush b = DeviceBrush(brush); + if (!b.IsOk() || b.GetStyle() == wxBRUSHSTYLE_TRANSPARENT) + return; + SetNoPen(dc); + if (dc->GetBrush() != b) + dc->SetBrush(b); + dc->DrawRectangle(r); +} + +void PurgeTemporaryDCs() { + std::vector doomed; + { + GdiLock lock; + for (auto& kv : TempDCs()) { + kv.second->m_hDC = nullptr; + kv.second->m_hAttribDC = nullptr; + doomed.push_back(kv.second); + } + TempDCs().clear(); + doomed.insert(doomed.end(), DetachedTempDCs().begin(), DetachedTempDCs().end()); + DetachedTempDCs().clear(); + for (DCState* s : States()) + if (s->fromGetDC && s->kind == DCKind::Window && s->target != WinTarget::None) + ReleaseTarget(s); + } + for (CDC* d : doomed) + delete d; +} + +CDC* WrapDC(wxDC* dc, wxWindow* window) { + GdiLock lock; + DCState* s = NewState(DCKind::Borrowed); + s->dc = dc; + s->window = window; + s->scale = dc ? dc->GetContentScaleFactor() : 1.0; + if (window && dc && wxDynamicCast(dc, wxPaintDC) && !Sessions().count(window)) { + PaintSession ps; + ps.dc = dc; + Sessions().emplace(window, std::move(ps)); + s->sessionOwner = true; + HookDestroy(window); + } else if (window) { + HookDestroy(window); + } + auto* cdc = new CDC; + cdc->m_hDC = ToHdc(s); + cdc->m_hAttribDC = ToHdc(s); + s->owner = cdc; + return cdc; +} + +void UnwrapDC(CDC* cdc) { + if (!cdc) + return; + { + GdiLock lock; + FreeState(DCFromHandle(cdc->m_hDC)); + cdc->m_hDC = nullptr; + cdc->m_hAttribDC = nullptr; + } + delete cdc; +} + +void PaintClientDCOverlay(wxWindow* w, wxDC& dc) { + GdiLock lock; + auto s = Sessions().find(w); + if (s != Sessions().end() && s->second.own) + EndSession(w); + auto it = Overlays().find(w); + if (it == Overlays().end() || !it->second.bitmap.IsOk()) + return; + ReleaseOverlayDC(it->second); + ForgetDC(&dc); + dc.DestroyClippingRegion(); + dc.DrawBitmap(it->second.bitmap, 0, 0, true); +} + +bool HasClientDCOverlay(wxWindow* w) { + GdiLock lock; + return Overlays().count(w) != 0; +} + +void ClearClientDCOverlay(wxWindow* w, const wxRect* rect) { + GdiLock lock; + auto it = Overlays().find(w); + if (it == Overlays().end()) + return; + Overlay& ov = it->second; + ReleaseOverlayDC(ov); + if (!rect) { + Overlays().erase(it); + return; + } + double scale = ov.bitmap.GetScaleFactor(); + wxRect pr(static_cast(std::floor(rect->x * scale)), static_cast(std::floor(rect->y * scale)), + static_cast(std::ceil(rect->width * scale)), static_cast(std::ceil(rect->height * scale))); + pr.Intersect(wxRect(0, 0, ov.bitmap.GetWidth(), ov.bitmap.GetHeight())); + if (pr.IsEmpty()) + return; + wxImage img = ov.bitmap.ConvertToImage(); + if (!img.HasAlpha()) + img.InitAlpha(); + unsigned char* alpha = img.GetAlpha(); + unsigned char* data = img.GetData(); + for (int y = pr.y; y < pr.GetBottom() + 1; ++y) + for (int x = pr.x; x < pr.GetRight() + 1; ++x) { + size_t i = static_cast(y) * img.GetWidth() + x; + alpha[i] = 0; + data[i * 3] = data[i * 3 + 1] = data[i * 3 + 2] = 0; + } + ov.bitmap = wxBitmap(img, 32, scale); +} + +} // namespace mfcwx + +using namespace mfcwx; + +// --------------------------------------------------------------------------------------------- +// CDC + +CDC::CDC() : m_hDC(nullptr), m_hAttribDC(nullptr), m_bPrinting(FALSE) {} + +CDC::~CDC() { + if (m_hDC) + ::DeleteDC(Detach()); +} + +CDC* CDC::FromHandle(HDC hDC) { + GdiLock lock; + DCState* s = DCFromHandle(hDC); + if (!s) + return nullptr; + if (s->owner) + return s->owner; + auto it = TempDCs().find(hDC); + if (it != TempDCs().end()) + return it->second; + auto* cdc = new CDC; + cdc->m_hDC = hDC; + cdc->m_hAttribDC = hDC; + TempDCs()[hDC] = cdc; + return cdc; +} + +void CDC::DeleteTempMap() { PurgeTemporaryDCs(); } + +BOOL CDC::Attach(HDC hDC) { + GdiLock lock; + DCState* s = DCFromHandle(hDC); + if (!s) + return FALSE; + if (m_hDC && m_hDC != hDC) + ::DeleteDC(Detach()); + auto it = TempDCs().find(hDC); + if (it != TempDCs().end()) { + it->second->m_hDC = nullptr; + it->second->m_hAttribDC = nullptr; + DetachedTempDCs().push_back(it->second); + TempDCs().erase(it); + } + s->owner = this; + m_hDC = hDC; + m_hAttribDC = hDC; + return TRUE; +} + +HDC CDC::Detach() { + if (AfxIsNullThis(this)) + return nullptr; + GdiLock lock; + HDC h = m_hDC; + if (DCState* s = DCFromHandle(h)) + if (s->owner == this) + s->owner = nullptr; + m_hDC = nullptr; + m_hAttribDC = nullptr; + return h; +} + +BOOL CDC::CreateCompatibleDC(CDC* pDC) { return Attach(::CreateCompatibleDC(pDC ? pDC->m_hDC : nullptr)); } + +BOOL CDC::DeleteDC() { + if (AfxIsNullThis(this) || !m_hDC) + return FALSE; + return ::DeleteDC(Detach()); +} + +CWnd* CDC::GetWindow() const { + GdiLock lock; + DCState* s = DCFromHandle(m_hDC); + return s && s->window ? CWnd::FromHandle(ToHwnd(s->window)) : nullptr; +} + +wxDC* CDC::GetWx() const { + GdiLock lock; + DCState* s = DCFromHandle(m_hDC); + if (!s) + return nullptr; + wxDC* d = s->Target(true); + if (!d) + return nullptr; + s->ApplyPen(d); + s->ApplyBrush(d, true); + s->ApplyFont(d); + s->Finish(); + return d; +} + +int CDC::SaveDC() { return ::SaveDC(m_hDC); } + +BOOL CDC::RestoreDC(int nSavedDC) { return ::RestoreDC(m_hDC, nSavedDC); } + +CGdiObject* CDC::SelectStockObject(int nIndex) { + return CGdiObject::FromHandle(::SelectObject(m_hDC, ::GetStockObject(nIndex))); +} + +CPen* CDC::SelectObject(CPen* pPen) { + return static_cast(CGdiObject::FromHandle(::SelectObject(m_hDC, pPen ? pPen->m_hObject : nullptr))); +} + +CBrush* CDC::SelectObject(CBrush* pBrush) { + return static_cast(CGdiObject::FromHandle(::SelectObject(m_hDC, pBrush ? pBrush->m_hObject : nullptr))); +} + +CFont* CDC::SelectObject(CFont* pFont) { + return static_cast(CGdiObject::FromHandle(::SelectObject(m_hDC, pFont ? pFont->m_hObject : nullptr))); +} + +CBitmap* CDC::SelectObject(CBitmap* pBitmap) { + return static_cast( + CGdiObject::FromHandle(::SelectObject(m_hDC, pBitmap ? pBitmap->m_hObject : nullptr))); +} + +int CDC::SelectObject(CRgn* pRgn) { return ::SelectClipRgn(m_hDC, pRgn ? pRgn->m_hObject : nullptr); } + +CGdiObject* CDC::SelectObject(CGdiObject* pObject) { + if (!pObject) + return nullptr; + GdiLock lock; + GdiObjectImpl* impl = GdiImpl(pObject->m_hObject); + if (impl && impl->kind == GdiKind::Region) { + ::SelectClipRgn(m_hDC, pObject->m_hObject); + return nullptr; + } + return CGdiObject::FromHandle(::SelectObject(m_hDC, pObject->m_hObject)); +} + +HGDIOBJ CDC::SelectObject(HGDIOBJ hObject) { return ::SelectObject(m_hDC, hObject); } + +CPalette* CDC::SelectPalette(CPalette* pPalette, BOOL) { + return static_cast( + CGdiObject::FromHandle(::SelectObject(m_hDC, pPalette ? pPalette->m_hObject : nullptr))); +} + +CPen* CDC::GetCurrentPen() const { + GdiLock lock; + DCState* s = DCFromHandle(m_hDC); + return s ? static_cast(CGdiObject::FromHandle(s->a.pen)) : nullptr; +} + +CBrush* CDC::GetCurrentBrush() const { + GdiLock lock; + DCState* s = DCFromHandle(m_hDC); + return s ? static_cast(CGdiObject::FromHandle(s->a.brush)) : nullptr; +} + +CFont* CDC::GetCurrentFont() const { + GdiLock lock; + DCState* s = DCFromHandle(m_hDC); + return s ? static_cast(CGdiObject::FromHandle(s->a.font)) : nullptr; +} + +CBitmap* CDC::GetCurrentBitmap() const { + GdiLock lock; + DCState* s = DCFromHandle(m_hDC); + return s ? static_cast(CGdiObject::FromHandle(s->a.bitmap)) : nullptr; +} + +int CDC::GetDeviceCaps(int nIndex) const { return ::GetDeviceCaps(m_hDC, nIndex); } + +COLORREF CDC::GetBkColor() const { + GdiLock lock; + DCState* s = DCFromHandle(m_hDC); + return s ? s->a.bkColor : CLR_INVALID; +} + +COLORREF CDC::SetBkColor(COLORREF crColor) { return ::SetBkColor(m_hDC, crColor); } + +int CDC::GetBkMode() const { + GdiLock lock; + DCState* s = DCFromHandle(m_hDC); + return s ? s->a.bkMode : 0; +} + +int CDC::SetBkMode(int nBkMode) { return ::SetBkMode(m_hDC, nBkMode); } + +COLORREF CDC::GetTextColor() const { + GdiLock lock; + DCState* s = DCFromHandle(m_hDC); + return s ? s->a.textColor : CLR_INVALID; +} + +COLORREF CDC::SetTextColor(COLORREF crColor) { return ::SetTextColor(m_hDC, crColor); } + +int CDC::GetROP2() const { + GdiLock lock; + DCState* s = DCFromHandle(m_hDC); + return s ? s->a.rop2 : 0; +} + +int CDC::SetROP2(int nDrawMode) { + GdiLock lock; + DCState* s = DCFromHandle(m_hDC); + if (!s || nDrawMode < R2_BLACK || nDrawMode > R2_WHITE) + return 0; + int old = s->a.rop2; + s->a.rop2 = nDrawMode; + return old; +} + +int CDC::SetStretchBltMode(int nStretchMode) { + GdiLock lock; + DCState* s = DCFromHandle(m_hDC); + if (!s) + return 0; + int old = s->a.stretchMode; + s->a.stretchMode = nStretchMode; + return old; +} + +int CDC::GetStretchBltMode() const { + GdiLock lock; + DCState* s = DCFromHandle(m_hDC); + return s ? s->a.stretchMode : 0; +} + +UINT CDC::SetTextAlign(UINT nFlags) { + GdiLock lock; + DCState* s = DCFromHandle(m_hDC); + if (!s) + return static_cast(-1); + UINT old = s->a.textAlign; + s->a.textAlign = nFlags; + return old; +} + +UINT CDC::GetTextAlign() const { + GdiLock lock; + DCState* s = DCFromHandle(m_hDC); + return s ? s->a.textAlign : 0; +} + +int CDC::SetMapMode(int nMapMode) { + GdiLock lock; + DCState* s = DCFromHandle(m_hDC); + if (!s || nMapMode < MM_TEXT || nMapMode > MM_ANISOTROPIC) + return 0; + int old = s->a.mapMode; + int dpi = s->Dpi(); + auto fixed = [&](int units) { + s->a.winExt = SIZE{units, units}; + s->a.vpExt = SIZE{dpi, -dpi}; + }; + switch (nMapMode) { + case MM_TEXT: + s->a.winExt = SIZE{1, 1}; + s->a.vpExt = SIZE{1, 1}; + break; + case MM_LOMETRIC: + fixed(254); + break; + case MM_HIMETRIC: + fixed(2540); + break; + case MM_LOENGLISH: + fixed(100); + break; + case MM_HIENGLISH: + fixed(1000); + break; + case MM_TWIPS: + fixed(1440); + break; + default: + break; + } + s->a.mapMode = nMapMode; + return old; +} + +int CDC::GetMapMode() const { + GdiLock lock; + DCState* s = DCFromHandle(m_hDC); + return s ? s->a.mapMode : 0; +} + +CPoint CDC::SetViewportOrg(int x, int y) { + GdiLock lock; + DCState* s = DCFromHandle(m_hDC); + if (!s) + return CPoint(); + CPoint old(s->a.vpOrg); + s->a.vpOrg = POINT{x, y}; + return old; +} + +CPoint CDC::GetViewportOrg() const { + GdiLock lock; + DCState* s = DCFromHandle(m_hDC); + return s ? CPoint(s->a.vpOrg) : CPoint(); +} + +CPoint CDC::OffsetViewportOrg(int nWidth, int nHeight) { + CPoint old = GetViewportOrg(); + SetViewportOrg(old.x + nWidth, old.y + nHeight); + return old; +} + +namespace { + +void AdjustIsotropic(DCState* s) { + if (s->a.mapMode != MM_ISOTROPIC || !s->a.winExt.cx || !s->a.winExt.cy) + return; + double sx = static_cast(s->a.vpExt.cx) / s->a.winExt.cx; + double sy = static_cast(s->a.vpExt.cy) / s->a.winExt.cy; + double m = std::min(std::fabs(sx), std::fabs(sy)); + if (m <= 0) + return; + s->a.vpExt.cx = static_cast(std::lround(s->a.winExt.cx * std::copysign(m, sx))); + s->a.vpExt.cy = static_cast(std::lround(s->a.winExt.cy * std::copysign(m, sy))); +} + +} // namespace + +CSize CDC::SetViewportExt(int cx, int cy) { + GdiLock lock; + DCState* s = DCFromHandle(m_hDC); + if (!s) + return CSize(); + CSize old(s->a.vpExt); + if ((s->a.mapMode == MM_ISOTROPIC || s->a.mapMode == MM_ANISOTROPIC) && cx && cy) { + s->a.vpExt = SIZE{cx, cy}; + AdjustIsotropic(s); + } + return old; +} + +CSize CDC::GetViewportExt() const { + GdiLock lock; + DCState* s = DCFromHandle(m_hDC); + return s ? CSize(s->a.vpExt) : CSize(); +} + +CPoint CDC::SetWindowOrg(int x, int y) { + GdiLock lock; + DCState* s = DCFromHandle(m_hDC); + if (!s) + return CPoint(); + CPoint old(s->a.winOrg); + s->a.winOrg = POINT{x, y}; + return old; +} + +CPoint CDC::GetWindowOrg() const { + GdiLock lock; + DCState* s = DCFromHandle(m_hDC); + return s ? CPoint(s->a.winOrg) : CPoint(); +} + +CPoint CDC::OffsetWindowOrg(int nWidth, int nHeight) { + CPoint old = GetWindowOrg(); + SetWindowOrg(old.x + nWidth, old.y + nHeight); + return old; +} + +CSize CDC::SetWindowExt(int cx, int cy) { + GdiLock lock; + DCState* s = DCFromHandle(m_hDC); + if (!s) + return CSize(); + CSize old(s->a.winExt); + if ((s->a.mapMode == MM_ISOTROPIC || s->a.mapMode == MM_ANISOTROPIC) && cx && cy) { + s->a.winExt = SIZE{cx, cy}; + AdjustIsotropic(s); + } + return old; +} + +CSize CDC::GetWindowExt() const { + GdiLock lock; + DCState* s = DCFromHandle(m_hDC); + return s ? CSize(s->a.winExt) : CSize(1, 1); +} + +void CDC::DPtoLP(LPPOINT lpPoints, int nCount) const { + GdiLock lock; + DCState* s = DCFromHandle(m_hDC); + if (!s || !lpPoints) + return; + for (int i = 0; i < nCount; ++i) { + double x = lpPoints[i].x, y = lpPoints[i].y; + s->ToLogical(x, y); + lpPoints[i].x = static_cast(std::lround(x)); + lpPoints[i].y = static_cast(std::lround(y)); + } +} + +void CDC::DPtoLP(LPRECT lpRect) const { DPtoLP(reinterpret_cast(lpRect), 2); } + +void CDC::DPtoLP(LPSIZE lpSize) const { + GdiLock lock; + DCState* s = DCFromHandle(m_hDC); + if (!s || !lpSize) + return; + double sx, sy; + s->Scales(sx, sy); + lpSize->cx = static_cast(std::lround(lpSize->cx / std::fabs(sx))); + lpSize->cy = static_cast(std::lround(lpSize->cy / std::fabs(sy))); +} + +void CDC::LPtoDP(LPPOINT lpPoints, int nCount) const { + GdiLock lock; + DCState* s = DCFromHandle(m_hDC); + if (!s || !lpPoints) + return; + for (int i = 0; i < nCount; ++i) { + wxPoint p = s->ToDevice(lpPoints[i].x, lpPoints[i].y); + lpPoints[i].x = p.x; + lpPoints[i].y = p.y; + } +} + +void CDC::LPtoDP(LPRECT lpRect) const { LPtoDP(reinterpret_cast(lpRect), 2); } + +void CDC::LPtoDP(LPSIZE lpSize) const { + GdiLock lock; + DCState* s = DCFromHandle(m_hDC); + if (!s || !lpSize) + return; + double sx, sy; + s->Scales(sx, sy); + lpSize->cx = static_cast(std::lround(lpSize->cx * std::fabs(sx))); + lpSize->cy = static_cast(std::lround(lpSize->cy * std::fabs(sy))); +} + +int CDC::GetClipBox(LPRECT lpRect) const { + GdiLock lock; + DCState* s = DCFromHandle(m_hDC); + if (!s || !lpRect) + return ERROR; + wxRect box; + int type = SIMPLEREGION; + if (s->a.hasClip) { + type = RegionType(s->a.clip); + box = s->a.clip.IsOk() ? s->a.clip.GetBox() : wxRect(); + } else if (s->kind == DCKind::Window && s->window) { + box = wxRect(wxPoint(0, 0), s->window->GetClientSize()); + } else if (s->kind == DCKind::Memory) { + wxBitmap& bmp = CurrentBitmap(s); + box = wxRect(0, 0, static_cast(std::lround(bmp.GetLogicalWidth())), + static_cast(std::lround(bmp.GetLogicalHeight()))); + } else if (s->dc) { + box = wxRect(wxPoint(0, 0), s->dc->GetSize()); + } else if (s->window) { + box = wxRect(wxPoint(0, 0), s->window->GetClientSize()); + } + double x0 = box.x, y0 = box.y, x1 = box.x + box.width, y1 = box.y + box.height; + s->ToLogical(x0, y0); + s->ToLogical(x1, y1); + lpRect->left = static_cast(std::lround(std::min(x0, x1))); + lpRect->top = static_cast(std::lround(std::min(y0, y1))); + lpRect->right = static_cast(std::lround(std::max(x0, x1))); + lpRect->bottom = static_cast(std::lround(std::max(y0, y1))); + return type; +} + +int CDC::SelectClipRgn(CRgn* pRgn) { return ::SelectClipRgn(m_hDC, pRgn ? pRgn->m_hObject : nullptr); } + +int CDC::IntersectClipRect(int x1, int y1, int x2, int y2) { + GdiLock lock; + DCState* s = DCFromHandle(m_hDC); + if (!s) + return ERROR; + wxRect r = s->DeviceRect(x1, y1, x2, y2); + if (s->a.hasClip) { + if (s->a.clip.IsOk() && !s->a.clip.IsEmpty() && r.width > 0 && r.height > 0) + s->a.clip.Intersect(r); + else + s->a.clip = wxRegion(); + } else { + s->a.clip = r.width > 0 && r.height > 0 ? wxRegion(r) : wxRegion(); + s->a.hasClip = true; + } + s->clipDirty = true; + return RegionType(s->a.clip); +} + +int CDC::IntersectClipRect(LPCRECT lpRect) { + return lpRect ? IntersectClipRect(lpRect->left, lpRect->top, lpRect->right, lpRect->bottom) : ERROR; +} + +CPoint CDC::GetCurrentPosition() const { + GdiLock lock; + DCState* s = DCFromHandle(m_hDC); + return s ? CPoint(s->a.pos) : CPoint(); +} + +CPoint CDC::MoveTo(int x, int y) { + POINT old{0, 0}; + ::MoveToEx(m_hDC, x, y, &old); + return CPoint(old); +} + +BOOL CDC::LineTo(int x, int y) { return ::LineTo(m_hDC, x, y); } + +namespace { + +BOOL ArcLike(HDC hdc, int kind, int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4) { + GdiLock lock; + DCState* s = DCFromHandle(hdc); + if (!s) + return FALSE; + DrawScope ds(s); + if (!ds) + return TRUE; + wxDC* d = ds.dc(); + wxRect r = s->DeviceRect(x1, y1, x2, y2); + std::vector pts = ArcPoints(r, s->ToDevice(x3, y3), s->ToDevice(x4, y4), x3 == x4 && y3 == y4); + if (pts.size() < 2) + return TRUE; + if (kind == 0) { + DrawPolyline(s, d, pts); + return TRUE; + } + if (kind == 1) + pts.insert(pts.begin(), wxPoint(r.x + (r.width - 1) / 2, r.y + (r.height - 1) / 2)); + DrawShape(s, d, true, [&] { d->DrawPolygon(static_cast(pts.size()), pts.data()); }); + return TRUE; +} + +} // namespace + +BOOL CDC::Arc(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4) { + return ArcLike(m_hDC, 0, x1, y1, x2, y2, x3, y3, x4, y4); +} + +BOOL CDC::Arc(LPCRECT lpRect, POINT ptStart, POINT ptEnd) { + return Arc(lpRect->left, lpRect->top, lpRect->right, lpRect->bottom, ptStart.x, ptStart.y, ptEnd.x, ptEnd.y); +} + +BOOL CDC::Pie(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4) { + return ArcLike(m_hDC, 1, x1, y1, x2, y2, x3, y3, x4, y4); +} + +BOOL CDC::Pie(LPCRECT lpRect, POINT ptStart, POINT ptEnd) { + return Pie(lpRect->left, lpRect->top, lpRect->right, lpRect->bottom, ptStart.x, ptStart.y, ptEnd.x, ptEnd.y); +} + +BOOL CDC::Chord(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4) { + return ArcLike(m_hDC, 2, x1, y1, x2, y2, x3, y3, x4, y4); +} + +BOOL CDC::Polyline(const POINT* lpPoints, int nCount) { return ::Polyline(m_hDC, lpPoints, nCount); } + +BOOL CDC::PolyBezier(const POINT* lpPoints, int nCount) { + GdiLock lock; + DCState* s = DCFromHandle(m_hDC); + if (!s || !lpPoints || nCount < 4 || (nCount - 1) % 3) + return FALSE; + DrawScope ds(s); + if (!ds) + return TRUE; + std::vector pts; + double x0 = lpPoints[0].x, y0 = lpPoints[0].y; + pts.push_back(s->ToDevice(x0, y0)); + for (int i = 1; i + 2 < nCount; i += 3) { + const POINT &c1 = lpPoints[i], &c2 = lpPoints[i + 1], &e = lpPoints[i + 2]; + for (int k = 1; k <= 24; ++k) { + double t = k / 24.0, u = 1 - t; + double x = u * u * u * x0 + 3 * u * u * t * c1.x + 3 * u * t * t * c2.x + t * t * t * e.x; + double y = u * u * u * y0 + 3 * u * u * t * c1.y + 3 * u * t * t * c2.y + t * t * t * e.y; + pts.push_back(s->ToDevice(x, y)); + } + x0 = e.x; + y0 = e.y; + } + DrawPolyline(s, ds.dc(), pts); + return TRUE; +} + +BOOL CDC::PolylineTo(const POINT* lpPoints, int nCount) { + GdiLock lock; + DCState* s = DCFromHandle(m_hDC); + if (!s || !lpPoints || nCount < 1) + return FALSE; + std::vector all; + all.push_back(s->a.pos); + all.insert(all.end(), lpPoints, lpPoints + nCount); + BOOL r = ::Polyline(m_hDC, all.data(), static_cast(all.size())); + s->a.pos = lpPoints[nCount - 1]; + return r; +} + +void CDC::FillRect(LPCRECT lpRect, CBrush* pBrush) { ::FillRect(m_hDC, lpRect, pBrush ? pBrush->m_hObject : nullptr); } + +void CDC::FrameRect(LPCRECT lpRect, CBrush* pBrush) { + ::FrameRect(m_hDC, lpRect, pBrush ? pBrush->m_hObject : nullptr); +} + +void CDC::InvertRect(LPCRECT lpRect) { + if (lpRect) + PatBlt(lpRect->left, lpRect->top, lpRect->right - lpRect->left, lpRect->bottom - lpRect->top, DSTINVERT); +} + +BOOL CDC::DrawIcon(int x, int y, HICON hIcon) { return ::DrawIcon(m_hDC, x, y, hIcon); } + +BOOL CDC::DrawEdge(LPRECT lpRect, UINT nEdge, UINT nFlags) { return ::DrawEdge(m_hDC, lpRect, nEdge, nFlags); } + +BOOL CDC::DrawFrameControl(LPRECT lpRect, UINT nType, UINT nState) { + GdiLock lock; + DCState* s = DCFromHandle(m_hDC); + if (!s || !lpRect) + return FALSE; + RECT r = *lpRect; + if (nType == DFC_BUTTON && (nState & 0xFF) == DFCS_BUTTONRADIO) { + DrawScope ds(s); + if (!ds) + return TRUE; + wxDC* d = ds.dc(); + wxRect dr = s->DeviceRect(r.left, r.top, r.right, r.bottom); + int size = std::min(dr.width, dr.height); + wxRect box(dr.x, dr.y + (dr.height - size) / 2, size, size); + d->SetPen(wxPen(ToWxColour(SysColor(COLOR_BTNSHADOW)))); + d->SetBrush(wxBrush(ToWxColour(SysColor((nState & DFCS_INACTIVE) ? COLOR_BTNFACE : COLOR_WINDOW)))); + d->DrawEllipse(box.x, box.y, box.width - 1, box.height - 1); + if (nState & DFCS_CHECKED) { + int inner = std::max(2, size / 3); + d->SetPen(*wxTRANSPARENT_PEN); + d->SetBrush(wxBrush(ToWxColour(SysColor(COLOR_WINDOWTEXT)))); + d->DrawEllipse(box.x + (size - inner) / 2, box.y + (size - inner) / 2, inner, inner); + } + return TRUE; + } + if (nType == DFC_BUTTON && (nState & 0xFF) == DFCS_BUTTONCHECK) { + ::DrawEdge(m_hDC, &r, EDGE_SUNKEN, BF_RECT | BF_ADJUST); + HBRUSH bg = GetSysColorBrush((nState & DFCS_INACTIVE) ? COLOR_BTNFACE : COLOR_WINDOW); + ::FillRect(m_hDC, &r, bg); + if (nState & DFCS_CHECKED) { + DrawScope ds(s); + if (!ds) + return TRUE; + wxDC* d = ds.dc(); + wxRect dr = s->DeviceRect(r.left, r.top, r.right, r.bottom); + d->SetPen(wxPen(ToWxColour(SysColor(COLOR_WINDOWTEXT)), std::max(1, dr.width / 6))); + wxPoint pts[3] = {wxPoint(dr.x + dr.width / 5, dr.y + dr.height / 2), + wxPoint(dr.x + dr.width * 2 / 5, dr.y + dr.height * 3 / 4), + wxPoint(dr.x + dr.width * 4 / 5, dr.y + dr.height / 4)}; + d->DrawLines(3, pts); + } + return TRUE; + } + ::FillRect(m_hDC, &r, GetSysColorBrush(COLOR_BTNFACE)); + UINT edge = (nState & DFCS_PUSHED) ? EDGE_SUNKEN : EDGE_RAISED; + if (nState & DFCS_FLAT) + edge = (nState & DFCS_PUSHED) ? BDR_SUNKENOUTER : BDR_RAISEDINNER; + return ::DrawEdge(m_hDC, &r, edge, BF_RECT); +} + +void CDC::DrawFocusRect(LPCRECT lpRect) { ::DrawFocusRect(m_hDC, lpRect); } + +BOOL CDC::Ellipse(int x1, int y1, int x2, int y2) { return ::Ellipse(m_hDC, x1, y1, x2, y2); } + +BOOL CDC::Polygon(const POINT* lpPoints, int nCount) { return ::Polygon(m_hDC, lpPoints, nCount); } + +BOOL CDC::PolyPolygon(const POINT* lpPoints, const INT* lpPolyCounts, int nCount) { + GdiLock lock; + DCState* s = DCFromHandle(m_hDC); + if (!s || !lpPoints || !lpPolyCounts || nCount <= 0) + return FALSE; + DrawScope ds(s); + if (!ds) + return TRUE; + int total = 0; + std::vector counts; + for (int i = 0; i < nCount; ++i) { + counts.push_back(lpPolyCounts[i]); + total += lpPolyCounts[i]; + } + std::vector pts = DevicePoints(s, lpPoints, total); + wxDC* d = ds.dc(); + wxPolygonFillMode mode = s->a.polyFillMode == WINDING ? wxWINDING_RULE : wxODDEVEN_RULE; + DrawShape(s, d, true, [&] { d->DrawPolyPolygon(nCount, counts.data(), pts.data(), 0, 0, mode); }); + return TRUE; +} + +BOOL CDC::Rectangle(int x1, int y1, int x2, int y2) { return ::Rectangle(m_hDC, x1, y1, x2, y2); } + +BOOL CDC::RoundRect(int x1, int y1, int x2, int y2, int x3, int y3) { + GdiLock lock; + DCState* s = DCFromHandle(m_hDC); + if (!s) + return FALSE; + DrawScope ds(s); + if (!ds) + return TRUE; + wxDC* d = ds.dc(); + wxRect r = s->DeviceRect(x1, y1, x2, y2); + if (r.width <= 1 || r.height <= 1) + return TRUE; + double radius = (s->DeviceWidth(std::abs(x3)) + s->DeviceHeight(std::abs(y3))) / 4.0; + radius = std::min(radius, std::min(r.width, r.height) / 2.0); + bool pen = HasPen(s); + int w = r.width - (pen ? 0 : 1), h = r.height - (pen ? 0 : 1); + DrawShape(s, d, true, [&] { d->DrawRoundedRectangle(r.x, r.y, w, h, radius); }); + return TRUE; +} + +BOOL CDC::RoundRect(LPCRECT lpRect, POINT point) { + return RoundRect(lpRect->left, lpRect->top, lpRect->right, lpRect->bottom, point.x, point.y); +} + +void CDC::FillSolidRect(LPCRECT lpRect, COLORREF clr) { + if (!lpRect) + return; + GdiLock lock; + DCState* s = DCFromHandle(m_hDC); + if (!s) + return; + s->a.bkColor = clr; + DrawScope ds(s); + if (!ds) + return; + FillDeviceRectColor(ds.dc(), s->DeviceRect(lpRect->left, lpRect->top, lpRect->right, lpRect->bottom), + ToWxColour(clr)); +} + +void CDC::FillSolidRect(int x, int y, int cx, int cy, COLORREF clr) { + RECT r{x, y, x + cx, y + cy}; + FillSolidRect(&r, clr); +} + +void CDC::Draw3dRect(LPCRECT lpRect, COLORREF clrTopLeft, COLORREF clrBottomRight) { + Draw3dRect(lpRect->left, lpRect->top, lpRect->right - lpRect->left, lpRect->bottom - lpRect->top, clrTopLeft, + clrBottomRight); +} + +void CDC::Draw3dRect(int x, int y, int cx, int cy, COLORREF clrTopLeft, COLORREF clrBottomRight) { + FillSolidRect(x, y, cx - 1, 1, clrTopLeft); + FillSolidRect(x, y, 1, cy - 1, clrTopLeft); + FillSolidRect(x + cx, y, -1, cy, clrBottomRight); + FillSolidRect(x, y + cy, cx, -1, clrBottomRight); +} + +BOOL CDC::FillRgn(CRgn* pRgn, CBrush* pBrush) { + GdiLock lock; + DCState* s = DCFromHandle(m_hDC); + GdiObjectImpl* rgn = pRgn ? GdiImpl(pRgn->m_hObject, GdiKind::Region) : nullptr; + if (!s || !rgn || !pBrush) + return FALSE; + DrawScope ds(s); + if (ds) + FillRegionRects(s, ds.dc(), rgn->region, pBrush->m_hObject); + return TRUE; +} + +BOOL CDC::FrameRgn(CRgn* pRgn, CBrush* pBrush, int nWidth, int nHeight) { + GdiLock lock; + DCState* s = DCFromHandle(m_hDC); + GdiObjectImpl* rgn = pRgn ? GdiImpl(pRgn->m_hObject, GdiKind::Region) : nullptr; + if (!s || !rgn || !pBrush || !rgn->region.IsOk() || rgn->region.IsEmpty()) + return FALSE; + wxRegion inner = rgn->region; + const int offs[4][2] = {{nWidth, 0}, {-nWidth, 0}, {0, nHeight}, {0, -nHeight}}; + for (const auto& o : offs) { + wxRegion moved = rgn->region; + moved.Offset(o[0], o[1]); + inner.Intersect(moved); + } + wxRegion frame = rgn->region; + if (inner.IsOk() && !inner.IsEmpty()) + frame.Subtract(inner); + DrawScope ds(s); + if (ds) + FillRegionRects(s, ds.dc(), frame, pBrush->m_hObject); + return TRUE; +} + +BOOL CDC::PaintRgn(CRgn* pRgn) { + GdiLock lock; + DCState* s = DCFromHandle(m_hDC); + GdiObjectImpl* rgn = pRgn ? GdiImpl(pRgn->m_hObject, GdiKind::Region) : nullptr; + if (!s || !rgn) + return FALSE; + DrawScope ds(s); + if (ds) + FillRegionRects(s, ds.dc(), rgn->region, s->a.brush); + return TRUE; +} + +// --------------------------------------------------------------------------------------------- +// Window DCs + +CPaintDC::CPaintDC(CWnd* pWnd) : m_hWnd(pWnd ? pWnd->m_hWnd : nullptr) { + memset(&m_ps, 0, sizeof m_ps); + Attach(::BeginPaint(m_hWnd, &m_ps)); +} + +CPaintDC::~CPaintDC() { + if (m_hDC) + ::EndPaint(m_hWnd, &m_ps); + Detach(); +} + +CClientDC::CClientDC(CWnd* pWnd) : m_hWnd(pWnd ? pWnd->m_hWnd : nullptr) { Attach(::GetDC(m_hWnd)); } + +CClientDC::~CClientDC() { + HDC h = Detach(); + if (h) + ::ReleaseDC(m_hWnd, h); +} + +CWindowDC::CWindowDC(CWnd* pWnd) : m_hWnd(pWnd ? pWnd->m_hWnd : nullptr) { Attach(::GetWindowDC(m_hWnd)); } + +CWindowDC::~CWindowDC() { + HDC h = Detach(); + if (h) + ::ReleaseDC(m_hWnd, h); +} + +CDC* CWnd::GetDC() { return CDC::FromHandle(::GetDC(m_hWnd)); } + +CDC* CWnd::GetWindowDC() { return CDC::FromHandle(::GetWindowDC(m_hWnd)); } + +int CWnd::ReleaseDC(CDC* pDC) { return pDC ? ::ReleaseDC(m_hWnd, pDC->m_hDC) : 0; } + +CDC* CWnd::BeginPaint(LPPAINTSTRUCT lpPaint) { return CDC::FromHandle(::BeginPaint(m_hWnd, lpPaint)); } + +void CWnd::EndPaint(LPPAINTSTRUCT lpPaint) { ::EndPaint(m_hWnd, lpPaint); } + +HDC GetDC(HWND hWnd) { + GdiLock lock; + DCState* s = NewWindowState(ToWx(hWnd), false); + s->fromGetDC = true; + return ToHdc(s); +} + +HDC GetWindowDC(HWND hWnd) { + GdiLock lock; + DCState* s = NewWindowState(ToWx(hWnd), true); + s->fromGetDC = true; + return ToHdc(s); +} + +int ReleaseDC(HWND, HDC hDC) { + GdiLock lock; + DCState* s = DCFromHandle(hDC); + if (!s) + return 0; + FreeState(s); + return 1; +} + +HDC BeginPaint(HWND hWnd, LPPAINTSTRUCT lpPaint) { + GdiLock lock; + wxWindow* w = ToWx(hWnd); + if (!w) + return nullptr; + DCState* s = NewWindowState(w, false); + s->paintDC = true; + if (lpPaint) { + memset(lpPaint, 0, sizeof *lpPaint); + lpPaint->hdc = ToHdc(s); + wxRect box; + if (Sessions().count(w) || NativePainting(w)) + box = w->GetUpdateRegion().GetBox(); + if (box.IsEmpty()) + box = wxRect(wxPoint(0, 0), w->GetClientSize()); + lpPaint->rcPaint.left = box.x; + lpPaint->rcPaint.top = box.y; + lpPaint->rcPaint.right = box.x + box.width; + lpPaint->rcPaint.bottom = box.y + box.height; + } + return ToHdc(s); +} + +BOOL EndPaint(HWND, const PAINTSTRUCT* lpPaint) { + if (!lpPaint) + return FALSE; + GdiLock lock; + FreeState(DCFromHandle(lpPaint->hdc)); + return TRUE; +} + +// --------------------------------------------------------------------------------------------- +// Win32 DC functions + +HDC CreateCompatibleDC(HDC hdc) { + GdiLock lock; + return ToHdc(NewMemoryState(DCFromHandle(hdc))); +} + +BOOL DeleteDC(HDC hdc) { + GdiLock lock; + DCState* s = DCFromHandle(hdc); + if (!s) + return FALSE; + FreeState(s); + return TRUE; +} + +HGDIOBJ SelectObject(HDC hdc, HGDIOBJ h) { + GdiLock lock; + return SelectImpl(DCFromHandle(hdc), h); +} + +int SelectClipRgn(HDC hdc, HRGN hrgn) { + GdiLock lock; + DCState* s = DCFromHandle(hdc); + if (!s) + return ERROR; + GdiObjectImpl* rgn = GdiImpl(hrgn, GdiKind::Region); + return SelectClipImpl(s, rgn ? &rgn->region : nullptr); +} + +COLORREF SetTextColor(HDC hdc, COLORREF color) { + GdiLock lock; + DCState* s = DCFromHandle(hdc); + if (!s) + return CLR_INVALID; + COLORREF old = s->a.textColor; + s->a.textColor = color; + return old; +} + +COLORREF SetBkColor(HDC hdc, COLORREF color) { + GdiLock lock; + DCState* s = DCFromHandle(hdc); + if (!s) + return CLR_INVALID; + COLORREF old = s->a.bkColor; + s->a.bkColor = color; + return old; +} + +int SetBkMode(HDC hdc, int mode) { + GdiLock lock; + DCState* s = DCFromHandle(hdc); + if (!s || (mode != TRANSPARENT && mode != OPAQUE)) + return 0; + int old = s->a.bkMode; + s->a.bkMode = mode; + return old; +} + +int GetDeviceCaps(HDC hdc, int index) { + GdiLock lock; + DCState* s = DCFromHandle(hdc); + int dpi = s ? s->Dpi() : 96; + bool printing = s && s->owner && s->owner->m_bPrinting && s->dc; + wxSize res = printing ? s->dc->GetSize() : wxGetDisplaySize(); + switch (index) { + case 2: + return printing ? 2 : 1; + case HORZSIZE: + return static_cast(std::lround(res.x * 25.4 / dpi)); + case VERTSIZE: + return static_cast(std::lround(res.y * 25.4 / dpi)); + case HORZRES: + case 118: + return res.x; + case VERTRES: + case 117: + return res.y; + case BITSPIXEL: + return 32; + case PLANES: + return 1; + case NUMCOLORS: + return -1; + case 28: + case 30: + case 32: + case 34: + return 0xFF; + case 36: + return 1; + case RASTERCAPS: + return 0x2A89; + case 40: + case 42: + return 36; + case 44: + return 51; + case LOGPIXELSX: + case LOGPIXELSY: + return dpi; + case 108: + return 24; + case 110: + return printing ? res.x : 0; + case 111: + return printing ? res.y : 0; + case 116: + return 60; + default: + return 0; + } +} + +BOOL MoveToEx(HDC hdc, int x, int y, LPPOINT lppt) { + GdiLock lock; + DCState* s = DCFromHandle(hdc); + if (!s) + return FALSE; + if (lppt) + *lppt = s->a.pos; + s->a.pos = POINT{x, y}; + return TRUE; +} + +BOOL LineTo(HDC hdc, int x, int y) { + GdiLock lock; + DCState* s = DCFromHandle(hdc); + if (!s) + return FALSE; + if (HasPen(s)) { + DrawScope ds(s); + if (ds) + DrawSegment(s, ds.dc(), s->ToDevice(s->a.pos.x, s->a.pos.y), s->ToDevice(x, y)); + } + s->a.pos = POINT{x, y}; + return TRUE; +} + +BOOL Rectangle(HDC hdc, int left, int top, int right, int bottom) { + GdiLock lock; + DCState* s = DCFromHandle(hdc); + if (!s) + return FALSE; + DrawScope ds(s); + if (!ds) + return TRUE; + wxDC* d = ds.dc(); + wxRect r = s->DeviceRect(left, top, right, bottom); + if (!HasPen(s)) { + FillDeviceRect(s, d, wxRect(r.x, r.y, r.width - 1, r.height - 1), s->a.brush); + return TRUE; + } + if (r.width <= 0 || r.height <= 0) + return TRUE; + int pw = DevicePenWidth(s); + if (PenStyle(s->a.pen) == PS_INSIDEFRAME && pw > 1) + r.Deflate(pw / 2); + if (r.width <= 0 || r.height <= 0) + return TRUE; + DrawShape(s, d, true, [&] { d->DrawRectangle(r); }); + return TRUE; +} + +BOOL Ellipse(HDC hdc, int left, int top, int right, int bottom) { + GdiLock lock; + DCState* s = DCFromHandle(hdc); + if (!s) + return FALSE; + DrawScope ds(s); + if (!ds) + return TRUE; + wxDC* d = ds.dc(); + wxRect r = s->DeviceRect(left, top, right, bottom); + if (r.width <= 1 || r.height <= 1) + return TRUE; + int pw = HasPen(s) ? DevicePenWidth(s) : 0; + if (pw > 1 && PenStyle(s->a.pen) == PS_INSIDEFRAME) + r.Deflate(pw / 2); + DrawShape(s, d, true, [&] { d->DrawEllipse(r.x, r.y, r.width - 1, r.height - 1); }); + return TRUE; +} + +BOOL Polygon(HDC hdc, const POINT* apt, int cpt) { + GdiLock lock; + DCState* s = DCFromHandle(hdc); + if (!s || !apt || cpt < 2) + return FALSE; + DrawScope ds(s); + if (!ds) + return TRUE; + wxDC* d = ds.dc(); + std::vector pts = DevicePoints(s, apt, cpt); + wxPolygonFillMode mode = s->a.polyFillMode == WINDING ? wxWINDING_RULE : wxODDEVEN_RULE; + DrawShape(s, d, true, [&] { d->DrawPolygon(static_cast(pts.size()), pts.data(), 0, 0, mode); }); + return TRUE; +} + +BOOL Polyline(HDC hdc, const POINT* apt, int cpt) { + GdiLock lock; + DCState* s = DCFromHandle(hdc); + if (!s || !apt || cpt < 2) + return FALSE; + DrawScope ds(s); + if (ds) + DrawPolyline(s, ds.dc(), DevicePoints(s, apt, cpt)); + return TRUE; +} + +int FillRect(HDC hDC, const RECT* lprc, HBRUSH hbr) { + GdiLock lock; + DCState* s = DCFromHandle(hDC); + if (!s || !lprc) + return 0; + DrawScope ds(s); + if (ds) + FillDeviceRect(s, ds.dc(), s->DeviceRect(lprc->left, lprc->top, lprc->right, lprc->bottom), hbr); + return 1; +} + +int FrameRect(HDC hDC, const RECT* lprc, HBRUSH hbr) { + GdiLock lock; + DCState* s = DCFromHandle(hDC); + if (!s || !lprc) + return 0; + DrawScope ds(s); + if (!ds) + return 1; + wxDC* d = ds.dc(); + wxRect r = s->DeviceRect(lprc->left, lprc->top, lprc->right, lprc->bottom); + if (r.width <= 0 || r.height <= 0) + return 1; + int tx = std::max(1, s->DeviceWidth(1)), ty = std::max(1, s->DeviceHeight(1)); + FillDeviceRect(s, d, wxRect(r.x, r.y, r.width, ty), hbr); + FillDeviceRect(s, d, wxRect(r.x, r.y + r.height - ty, r.width, ty), hbr); + FillDeviceRect(s, d, wxRect(r.x, r.y + ty, tx, r.height - 2 * ty), hbr); + FillDeviceRect(s, d, wxRect(r.x + r.width - tx, r.y + ty, tx, r.height - 2 * ty), hbr); + return 1; +} + +BOOL DrawEdge(HDC hdc, LPRECT qrc, UINT edge, UINT grfFlags) { + GdiLock lock; + DCState* s = DCFromHandle(hdc); + if (!s || !qrc) + return FALSE; + UINT sides = grfFlags & BF_RECT; + UINT outer = edge & (BDR_RAISEDOUTER | BDR_SUNKENOUTER); + UINT inner = edge & (BDR_RAISEDINNER | BDR_SUNKENINNER); + bool soft = (grfFlags & BF_SOFT) != 0; + auto colors = [&](UINT bdr, COLORREF& lt, COLORREF& rb) { + if (grfFlags & BF_MONO) { + lt = rb = SysColor(COLOR_WINDOWFRAME); + } else if (grfFlags & BF_FLAT) { + lt = rb = SysColor((bdr & (BDR_RAISEDOUTER | BDR_SUNKENOUTER)) ? COLOR_BTNSHADOW : COLOR_BTNFACE); + } else if (bdr & BDR_RAISEDOUTER) { + lt = SysColor(soft ? COLOR_3DHILIGHT : COLOR_3DLIGHT); + rb = SysColor(COLOR_3DDKSHADOW); + } else if (bdr & BDR_SUNKENOUTER) { + lt = SysColor(soft ? COLOR_3DDKSHADOW : COLOR_3DSHADOW); + rb = SysColor(COLOR_3DHILIGHT); + } else if (bdr & BDR_RAISEDINNER) { + lt = SysColor(soft ? COLOR_3DLIGHT : COLOR_3DHILIGHT); + rb = SysColor(COLOR_3DSHADOW); + } else { + lt = SysColor(soft ? COLOR_3DSHADOW : COLOR_3DDKSHADOW); + rb = SysColor(COLOR_3DLIGHT); + } + }; + wxRect r = s->DeviceRect(qrc->left, qrc->top, qrc->right, qrc->bottom); + { + DrawScope ds(s); + if (ds) { + wxDC* d = ds.dc(); + wxRect cur = r; + for (UINT bdr : {outer, inner}) { + if (!bdr) + continue; + COLORREF lt, rb; + colors(bdr, lt, rb); + EdgeRect(d, cur, sides, lt, rb); + cur = ShrinkEdges(cur, sides); + } + if (grfFlags & BF_MIDDLE) + FillDeviceRectColor(d, cur, + ToWxColour(SysColor((grfFlags & BF_MONO) ? COLOR_WINDOW : COLOR_BTNFACE))); + } + } + if (grfFlags & BF_ADJUST) { + int n = (outer ? 1 : 0) + (inner ? 1 : 0); + int lx = s->LogicalWidth(n), ly = s->LogicalHeight(n); + if (sides & BF_LEFT) + qrc->left += lx; + if (sides & BF_TOP) + qrc->top += ly; + if (sides & BF_RIGHT) + qrc->right -= lx; + if (sides & BF_BOTTOM) + qrc->bottom -= ly; + } + return TRUE; +} + +BOOL DrawFocusRect(HDC hDC, const RECT* lprc) { + GdiLock lock; + DCState* s = DCFromHandle(hDC); + if (!s || !lprc) + return FALSE; + DrawScope ds(s); + if (!ds) + return TRUE; + wxDC* d = ds.dc(); + wxRect r = s->DeviceRect(lprc->left, lprc->top, lprc->right, lprc->bottom); + if (r.width <= 0 || r.height <= 0) + return TRUE; + if (s->SupportsLogicalOps(d)) { + d->SetLogicalFunction(wxINVERT); + static const wxDash dots[] = {1, 1}; + wxPen p(*wxBLACK, 1, wxPENSTYLE_USER_DASH); + p.SetDashes(2, dots); + d->SetPen(p); + d->SetBrush(*wxTRANSPARENT_BRUSH); + d->DrawRectangle(r); + d->SetLogicalFunction(wxCOPY); + return TRUE; + } + wxColour c(96, 96, 96); + SetNoPen(d); + SetSolidBrush(d, c); + for (int x = r.x; x < r.GetRight() + 1; x += 2) { + d->DrawRectangle(x, r.y, 1, 1); + d->DrawRectangle(x, r.GetBottom(), 1, 1); + } + for (int y = r.y; y < r.GetBottom() + 1; y += 2) { + d->DrawRectangle(r.x, y, 1, 1); + d->DrawRectangle(r.GetRight(), y, 1, 1); + } + return TRUE; +} + +int SaveDC(HDC hdc) { + GdiLock lock; + DCState* s = DCFromHandle(hdc); + if (!s) + return 0; + s->saved.push_back(s->a); + AddRefs(s->a); + return static_cast(s->saved.size()); +} + +BOOL RestoreDC(HDC hdc, int nSavedDC) { + GdiLock lock; + DCState* s = DCFromHandle(hdc); + if (!s || s->saved.empty()) + return FALSE; + int depth = static_cast(s->saved.size()); + int index = nSavedDC < 0 ? depth + nSavedDC : nSavedDC - 1; + if (index < 0 || index >= depth) + return FALSE; + DCAttrs target = s->saved[static_cast(index)]; + RestoreAttrs(s, target); + for (int i = depth - 1; i >= index; --i) { + ReleaseRefs(s->saved[static_cast(i)]); + s->saved.pop_back(); + } + return TRUE; +} + +BOOL DrawIcon(HDC hDC, int X, int Y, HICON hIcon) { + GdiLock lock; + DCState* s = DCFromHandle(hDC); + wxIcon* icon = IconFromHandle(hIcon); + if (!s || !icon || !icon->IsOk()) + return FALSE; + DrawScope ds(s); + if (ds) + ds.dc()->DrawIcon(*icon, s->ToDevice(X, Y)); + return TRUE; +} diff --git a/port/mfcwx/src/gdi_internal.h b/port/mfcwx/src/gdi_internal.h new file mode 100644 index 000000000..e18a63be8 --- /dev/null +++ b/port/mfcwx/src/gdi_internal.h @@ -0,0 +1,218 @@ +#pragma once + +// Shared implementation details of the GDI emulation (gdi*.cpp). + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "internal.h" + +#include +#include +#include +#include + +namespace mfcwx { + +std::recursive_mutex& GdiMutex(); + +class GdiLock { +public: + GdiLock() { GdiMutex().lock(); } + ~GdiLock() { GdiMutex().unlock(); } + GdiLock(const GdiLock&) = delete; + GdiLock& operator=(const GdiLock&) = delete; +}; + +enum class GdiKind { Pen, Brush, Font, Bitmap, Region, Palette }; + +struct DCState; + +struct GdiObjectImpl { + explicit GdiObjectImpl(GdiKind k) : kind(k) {} + GdiKind kind; + int refs = 1; + bool stock = false; + bool deleted = false; + CGdiObject* permanent = nullptr; + + LOGPEN logPen{}; + DWORD penStyle = 0; + std::vector dashes; + + LOGBRUSH logBrush{}; + int sysColor = -1; + wxBrush brush; + + LOGFONT logFont{}; + wxFont font; + double emPx = 0; + double scaledFor = 1.0; + wxFont scaledFont; + + wxBitmap bitmap; + int bitsPixel = 32; + SIZE dimension{0, 0}; + DCState* selectedIn = nullptr; + + wxRegion region; +}; + +// Handles (gdi.cpp) +GdiObjectImpl* GdiImpl(HGDIOBJ h); +GdiObjectImpl* GdiImpl(HGDIOBJ h, GdiKind kind); +HGDIOBJ RegisterGdiObject(GdiObjectImpl* impl); +void GdiAddRef(HGDIOBJ h); +void GdiRelease(HGDIOBJ h); +// (HBRUSH)(COLOR_xxx + 1) values accepted by FillRect and window classes; -1 otherwise. +int SysColorBrushIndex(HGDIOBJ h); +HGDIOBJ DefaultBitmapHandle(); +bool IsStockFont(HFONT h); + +wxPen DevicePen(HPEN h, double widthScale); +wxBrush DeviceBrush(HBRUSH h); +wxFont DeviceFont(HFONT h, double scale); +double FontEmPixels(HFONT h); +bool BrushIsHatched(HBRUSH h); +COLORREF BrushColor(HBRUSH h); +bool PenIsNull(HPEN h); +int PenStyle(HPEN h); +int PenWidth(HPEN h); +COLORREF PenColor(HPEN h); + +wxFont MakeFont(const LOGFONT& lf, double* emPx); +double PixelsToPoints(double px); +wxBitmap NewBitmap(int width, int height, double scale, const wxColour& fill); +HBITMAP NewBitmapHandle(const wxBitmap& bmp, int bitsPixel); + +// DCs (gdi_dc.cpp) +enum class DCKind { Info, Borrowed, Window, Memory }; +enum class WinTarget { None, Paint, Client, OverlayPending, Overlay }; + +struct DCAttrs { + HPEN pen = nullptr; + HBRUSH brush = nullptr; + HFONT font = nullptr; + HBITMAP bitmap = nullptr; + HPALETTE palette = nullptr; + COLORREF textColor = 0; + COLORREF bkColor = 0xFFFFFF; + int bkMode = OPAQUE; + int rop2 = R2_COPYPEN; + int stretchMode = BLACKONWHITE; + int polyFillMode = ALTERNATE; + UINT textAlign = 0; + int mapMode = MM_TEXT; + POINT winOrg{0, 0}; + POINT vpOrg{0, 0}; + SIZE winExt{1, 1}; + SIZE vpExt{1, 1}; + POINT pos{0, 0}; + bool hasClip = false; + wxRegion clip; +}; + +struct DCState { + DCKind kind = DCKind::Info; + DCAttrs a; + std::vector saved; + wxDC* dc = nullptr; + std::unique_ptr owned; + wxWindow* window = nullptr; + CDC* owner = nullptr; + WinTarget target = WinTarget::None; + bool fromGetDC = false; + bool paintDC = false; + bool sessionOwner = false; + bool wholeWindow = false; + bool clipDirty = true; + bool bitmapOut = false; + double scale = 1.0; + wxBitmap defaultBitmap; + std::unique_ptr pixels; + + wxDC* Target(bool drawing); + wxDC* Measure(); + void Finish(); + + void Scales(double& sx, double& sy) const; + wxPoint ToDevice(double x, double y) const; + void ToLogical(double& x, double& y) const; + wxRect DeviceRect(int l, int t, int r, int b) const; + int LogicalWidth(int deviceCx) const; + int LogicalHeight(int deviceCy) const; + int DeviceWidth(int logicalCx) const; + int DeviceHeight(int logicalCy) const; + int Dpi() const; + + void ApplyPen(wxDC* d); + void ApplyBrush(wxDC* d, bool fill); + void ApplyFont(wxDC* d); + void ApplyClip(wxDC* d, const wxRect* extra = nullptr); + bool SupportsLogicalOps(wxDC* d) const; + GdiObjectImpl* SelectedBitmap() const; +}; + +DCState* DCFromHandle(HDC h); +inline HDC ToHdc(DCState* s) { return reinterpret_cast(s); } +wxDC& MeasureDC(); +void SyncBitmap(GdiObjectImpl* bmp); +// Pixels of the bitmap selected into a memory DC (physical resolution); null for other DCs. +const wxImage* DCPixels(DCState* s); +// Scale factor of the bitmap a memory DC draws into (the DC's content scale otherwise). +double DCBitmapScale(DCState* s); + +class DrawScope { +public: + explicit DrawScope(DCState* s) : m_s(s), m_dc(s ? s->Target(true) : nullptr) {} + ~DrawScope() { + if (m_dc) + m_s->Finish(); + } + DrawScope(const DrawScope&) = delete; + DrawScope& operator=(const DrawScope&) = delete; + wxDC* dc() const { return m_dc; } + explicit operator bool() const { return m_dc != nullptr; } + +private: + DCState* m_s; + wxDC* m_dc; +}; + +// Fills a device rectangle with a brush, honouring hatch backgrounds and R2 modes. +void FillDeviceRect(DCState* s, wxDC* dc, const wxRect& r, HBRUSH brush); +void FillDeviceRectColor(wxDC* dc, const wxRect& r, const wxColour& c); + +// Text (gdi_text.cpp) +struct DeviceMetrics { + int height = 0; + int ascent = 0; + int descent = 0; + int internalLeading = 0; + int externalLeading = 0; + int aveWidth = 0; + int maxWidth = 0; +}; +DeviceMetrics GetDeviceMetrics(DCState* s, wxDC* dc); +int DeviceTextWidth(wxDC* dc, const wxString& text); + +// Bits (gdi_bits.cpp) +bool BlitImpl(DCState* dst, int x, int y, int w, int h, DCState* src, int xs, int ys, int ws, int hs, DWORD rop); +bool DrawImageImpl(DCState* dst, int x, int y, int w, int h, const wxImage& img, int xs, int ys, int ws, int hs, + DWORD rop); +bool DibToImage(const BITMAPINFO* bmi, const void* bits, UINT usage, wxImage& out, int startScan = 0, + int lines = -1); +bool DdbBitsToImage(int width, int height, int bpp, int stride, const void* bits, wxImage& out); +bool ImageToDdbBits(const wxImage& img, int bpp, std::vector& out, int& stride); +// Applies a raster operation to a device rectangle of a memory DC in software. +bool SoftwareRop(DCState* dst, const wxRect& r, const wxImage* src, DWORD rop, COLORREF pattern); + +} // namespace mfcwx diff --git a/port/mfcwx/src/gdi_text.cpp b/port/mfcwx/src/gdi_text.cpp new file mode 100644 index 000000000..b59c392dd --- /dev/null +++ b/port/mfcwx/src/gdi_text.cpp @@ -0,0 +1,688 @@ +#include "gdi_internal.h" + +#include +#include +#include + +namespace mfcwx { + +namespace { + +struct Run { + wxString text; + int x; + size_t start; +}; + +struct Line { + wxString text; + int mnemonic = -1; +}; + +wxString StripLineBreaks(const wxString& s) { + wxString out; + out.reserve(s.length()); + for (wxUniChar c : s) + if (c != '\r' && c != '\n') + out += c; + return out; +} + +wxString ProcessPrefix(const wxString& s, int& mnemonic) { + wxString out; + out.reserve(s.length()); + mnemonic = -1; + for (size_t i = 0; i < s.length(); ++i) { + wxUniChar c = s[i]; + if (c == '&') { + if (i + 1 < s.length() && s[i + 1] == '&') { + out += '&'; + ++i; + } else if (i + 1 < s.length()) { + if (mnemonic < 0) + mnemonic = static_cast(out.length()); + } + continue; + } + out += c; + } + return out; +} + +std::vector LayoutRuns(wxDC* dc, const wxString& line, int tabWidth, int* width) { + std::vector runs; + int x = 0; + size_t start = 0; + for (size_t i = 0; i <= line.length(); ++i) { + if (i < line.length() && line[i] != '\t') + continue; + wxString seg = line.Mid(start, i - start); + if (!seg.empty()) { + runs.push_back(Run{seg, x, start}); + x += DeviceTextWidth(dc, seg); + } + if (i < line.length()) { + if (tabWidth > 0) + x = (x / tabWidth + 1) * tabWidth; + else + x += DeviceTextWidth(dc, " "); + } + start = i + 1; + } + if (width) + *width = x; + return runs; +} + +int LineWidth(wxDC* dc, const wxString& line, int tabWidth) { + if (line.Find('\t') == wxNOT_FOUND) + return DeviceTextWidth(dc, line); + int w = 0; + LayoutRuns(dc, line, tabWidth, &w); + return w; +} + +std::vector> WrapParagraph(wxDC* dc, const wxString& p, int maxWidth, int tabWidth) { + std::vector> ranges; + size_t len = p.length(), i = 0; + if (len == 0) { + ranges.emplace_back(0, 0); + return ranges; + } + while (i < len) { + size_t lineStart = i, end = i, j = i; + while (j < len) { + size_t k = j; + while (k < len && p[k] == ' ') + ++k; + while (k < len && p[k] != ' ') + ++k; + int w = LineWidth(dc, p.Mid(lineStart, k - lineStart), tabWidth); + if (w > maxWidth && end > lineStart) + break; + end = k; + j = k; + if (w > maxWidth) + break; + } + if (end == lineStart) + end = std::min(len, lineStart + 1); + ranges.emplace_back(lineStart, end); + i = end; + while (i < len && p[i] == ' ') + ++i; + } + return ranges; +} + +wxString Ellipsize(wxDC* dc, const wxString& line, int maxWidth, UINT fmt) { + const wxString dots = "..."; + if (fmt & DT_PATH_ELLIPSIS) { + int sep = std::max(line.Find('\\', true), line.Find('/', true)); + if (sep > 0) { + wxString head = line.Left(static_cast(sep)), tail = line.Mid(static_cast(sep)); + if (DeviceTextWidth(dc, dots + tail) <= maxWidth) { + size_t lo = 0, hi = head.length(); + while (lo < hi) { + size_t mid = (lo + hi + 1) / 2; + if (DeviceTextWidth(dc, head.Left(mid) + dots + tail) <= maxWidth) + lo = mid; + else + hi = mid - 1; + } + return head.Left(lo) + dots + tail; + } + } + } + size_t lo = 0, hi = line.length(); + while (lo < hi) { + size_t mid = (lo + hi + 1) / 2; + if (DeviceTextWidth(dc, line.Left(mid) + dots) <= maxWidth) + lo = mid; + else + hi = mid - 1; + } + return line.Left(lo) + dots; +} + +void DrawRuns(wxDC* d, const std::vector& runs, int x, int y) { + for (const Run& r : runs) + d->DrawText(r.text, x + r.x, y); +} + +void FillTextBackground(DCState* s, wxDC* d, const wxRect& r) { + if (s->a.bkMode == OPAQUE && r.width > 0 && r.height > 0) { + FillDeviceRectColor(d, r, ToWxColour(s->a.bkColor)); + s->ApplyFont(d); + } +} + +int AlignLeft(UINT align, int refX, int width) { + if ((align & TA_CENTER) == TA_CENTER) + return refX - width / 2; + if (align & TA_RIGHT) + return refX - width; + return refX; +} + +int AlignTop(UINT align, int refY, const DeviceMetrics& m) { + if ((align & TA_BASELINE) == TA_BASELINE) + return refY - m.ascent; + if (align & TA_BOTTOM) + return refY - m.height; + return refY; +} + +void UpdateCurrentPosition(DCState* s, int deviceWidth) { + UINT align = s->a.textAlign; + if (!(align & TA_UPDATECP) || (align & TA_CENTER) == TA_CENTER) + return; + double sx, sy; + s->Scales(sx, sy); + int lw = s->LogicalWidth(deviceWidth); + int dir = sx < 0 ? -1 : 1; + s->a.pos.x += (align & TA_RIGHT) ? -dir * lw : dir * lw; +} + +int Escapement(DCState* s) { + GdiObjectImpl* f = GdiImpl(s->a.font, GdiKind::Font); + if (!f) + return 0; + LONG e = f->logFont.lfEscapement; + return e > -3600 && e < 3600 ? static_cast(e) : 0; +} + +BOOL TextOutImpl(DCState* s, int x, int y, const char* str, int n) { + if (!s || !str) + return FALSE; + if (n < 0) + n = static_cast(strlen(str)); + wxString text = StripLineBreaks(ToWx(str, n)); + DrawScope ds(s); + wxDC* d = ds ? ds.dc() : s->Measure(); + s->ApplyFont(d); + int width = DeviceTextWidth(d, text); + DeviceMetrics m = GetDeviceMetrics(s, d); + POINT ref = (s->a.textAlign & TA_UPDATECP) ? s->a.pos : POINT{x, y}; + wxPoint dp = s->ToDevice(ref.x, ref.y); + int left = AlignLeft(s->a.textAlign, dp.x, width); + int top = AlignTop(s->a.textAlign, dp.y, m); + if (ds) { + int esc = Escapement(s); + if (esc) { + d->DrawRotatedText(text, dp.x, dp.y, esc / 10.0); + } else { + FillTextBackground(s, d, wxRect(left, top, width, m.height)); + d->DrawText(text, left, top); + } + } + UpdateCurrentPosition(s, width); + return TRUE; +} + +int NextTabStop(int x, int origin, int nTabPositions, const int* stops, int defaultTab) { + int rel = x - origin; + if (nTabPositions <= 0 || !stops) { + int t = std::max(1, defaultTab); + return origin + (rel / t + 1) * t; + } + if (nTabPositions == 1) { + int t = std::max(1, std::abs(stops[0])); + return origin + (rel / t + 1) * t; + } + for (int i = 0; i < nTabPositions; ++i) + if (std::abs(stops[i]) > rel) + return origin + std::abs(stops[i]); + return x; +} + +CSize TabbedImpl(DCState* s, int x, int y, const char* str, int n, int nTabPositions, const int* stops, + int nTabOrigin, bool draw) { + if (!s || !str) + return CSize(); + if (n < 0) + n = static_cast(strlen(str)); + wxString text = StripLineBreaks(ToWx(str, n)); + DrawScope scope(draw ? s : nullptr); + wxDC* d = scope ? scope.dc() : s->Measure(); + s->ApplyFont(d); + DeviceMetrics m = GetDeviceMetrics(s, d); + int defaultTab = s->LogicalWidth(8 * m.aveWidth); + POINT ref = (s->a.textAlign & TA_UPDATECP) ? s->a.pos : POINT{x, y}; + int lx = ref.x; + size_t start = 0; + for (size_t i = 0; i <= text.length(); ++i) { + if (i < text.length() && text[i] != '\t') + continue; + wxString seg = text.Mid(start, i - start); + int w = DeviceTextWidth(d, seg); + if (scope && !seg.empty()) { + wxPoint p = s->ToDevice(lx, ref.y); + int top = AlignTop(s->a.textAlign, p.y, m); + FillTextBackground(s, d, wxRect(p.x, top, w, m.height)); + d->DrawText(seg, p.x, top); + } + lx += s->LogicalWidth(w); + if (i < text.length()) + lx = NextTabStop(lx, nTabOrigin, nTabPositions, stops, defaultTab); + start = i + 1; + } + return CSize(lx - ref.x, s->LogicalHeight(m.height)); +} + +int DrawTextImpl(DCState* s, const char* str, int n, LPRECT rc, UINT fmt) { + if (!s || !rc || !str) + return 0; + if (n < 0) + n = static_cast(strlen(str)); + int tabChars = 8; + if (fmt & DT_TABSTOP) { + tabChars = static_cast((fmt >> 8) & 0xFF); + if (!tabChars) + tabChars = 8; + fmt &= ~0xFF00u; + } + bool calc = (fmt & DT_CALCRECT) != 0; + bool single = (fmt & DT_SINGLELINE) != 0; + DrawScope scope(calc ? nullptr : s); + wxDC* d = scope ? scope.dc() : s->Measure(); + s->ApplyFont(d); + DeviceMetrics m = GetDeviceMetrics(s, d); + int lineH = m.height + ((fmt & DT_EXTERNALLEADING) ? m.externalLeading : 0); + int tabW = (fmt & DT_EXPANDTABS) ? tabChars * m.aveWidth : 0; + wxRect R = s->DeviceRect(rc->left, rc->top, rc->right, rc->bottom); + + wxString text = ToWx(str, n); + std::vector paragraphs; + if (single) { + paragraphs.push_back(StripLineBreaks(text)); + } else { + wxString cur; + for (size_t i = 0; i < text.length(); ++i) { + wxUniChar c = text[i]; + if (c == '\r' || c == '\n') { + paragraphs.push_back(cur); + cur.clear(); + if (c == '\r' && i + 1 < text.length() && text[i + 1] == '\n') + ++i; + continue; + } + cur += c; + } + paragraphs.push_back(cur); + } + + std::vector lines; + for (const wxString& para : paragraphs) { + int mnemonic = -1; + wxString p = (fmt & DT_NOPREFIX) ? para : ProcessPrefix(para, mnemonic); + if ((fmt & DT_WORDBREAK) && !single) { + for (const auto& range : WrapParagraph(d, p, R.width, tabW)) { + Line l; + l.text = p.Mid(range.first, range.second - range.first); + if (mnemonic >= static_cast(range.first) && mnemonic < static_cast(range.second)) + l.mnemonic = mnemonic - static_cast(range.first); + lines.push_back(l); + } + } else { + Line l; + l.text = p; + l.mnemonic = mnemonic; + lines.push_back(l); + } + } + + std::vector widths; + int maxW = 0; + for (const Line& l : lines) { + widths.push_back(LineWidth(d, l.text, tabW)); + maxW = std::max(maxW, widths.back()); + } + int total = lineH * static_cast(lines.size()); + double sx, sy; + s->Scales(sx, sy); + if (calc) { + rc->right = rc->left + (sx < 0 ? -1 : 1) * s->LogicalWidth(maxW); + rc->bottom = rc->top + (sy < 0 ? -1 : 1) * s->LogicalHeight(total); + return s->LogicalHeight(total); + } + + int y0 = R.y; + if (single && (fmt & DT_VCENTER)) + y0 = R.y + (R.height - lineH) / 2; + else if (single && (fmt & DT_BOTTOM)) + y0 = R.y + R.height - lineH; + + if (fmt & (DT_END_ELLIPSIS | DT_PATH_ELLIPSIS | DT_WORD_ELLIPSIS)) { + for (size_t i = 0; i < lines.size(); ++i) { + if (widths[i] > R.width) { + lines[i].text = Ellipsize(d, lines[i].text, R.width, fmt); + lines[i].mnemonic = -1; + widths[i] = LineWidth(d, lines[i].text, tabW); + } + } + } + + if (scope) { + bool clip = false; + std::vector xs; + for (size_t i = 0; i < lines.size(); ++i) { + int x = R.x; + if (fmt & DT_CENTER) + x = R.x + (R.width - widths[i]) / 2; + else if (fmt & DT_RIGHT) + x = R.x + R.width - widths[i]; + xs.push_back(x); + int y = y0 + lineH * static_cast(i); + if (x < R.x || x + widths[i] > R.x + R.width || y < R.y || y + lineH > R.y + R.height) + clip = true; + } + if (fmt & DT_NOCLIP) + clip = false; + if (clip) + s->ApplyClip(d, &R); + for (size_t i = 0; i < lines.size(); ++i) { + int y = y0 + lineH * static_cast(i); + FillTextBackground(s, d, wxRect(xs[i], y, widths[i], lineH)); + int w = 0; + std::vector runs = LayoutRuns(d, lines[i].text, tabW, &w); + DrawRuns(d, runs, xs[i], y); + int mn = lines[i].mnemonic; + if (mn >= 0 && mn < static_cast(lines[i].text.length())) { + for (const Run& r : runs) { + if (static_cast(mn) < r.start || static_cast(mn) >= r.start + r.text.length()) + continue; + size_t col = static_cast(mn) - r.start; + int ux = xs[i] + r.x + DeviceTextWidth(d, r.text.Left(col)); + int uw = DeviceTextWidth(d, r.text.Mid(col, 1)); + FillDeviceRectColor(d, wxRect(ux, y + m.ascent + 1, uw, 1), ToWxColour(s->a.textColor)); + s->ApplyFont(d); + } + } + } + if (clip) + s->ApplyClip(d); + } + if (single && (fmt & (DT_VCENTER | DT_BOTTOM))) + return s->LogicalHeight(y0 - R.y + total); + return s->LogicalHeight(total); +} + +DCState* LockedState(HDC h) { return DCFromHandle(h); } + +} // namespace + +DeviceMetrics GetDeviceMetrics(DCState* s, wxDC* dc) { + DeviceMetrics m; + wxFontMetrics fm = dc->GetFontMetrics(); + m.externalLeading = std::max(0, fm.externalLeading); + m.descent = fm.descent; + m.height = fm.height - m.externalLeading; + m.ascent = m.height - m.descent; + double sx, sy; + s->Scales(sx, sy); + double em = FontEmPixels(s->a.font) * (IsStockFont(s->a.font) ? 1.0 : std::fabs(sy)); + m.internalLeading = std::max(0, m.height - static_cast(std::lround(em))); + m.aveWidth = fm.averageWidth > 0 ? fm.averageWidth : DeviceTextWidth(dc, "x"); + if (dc->GetFont().IsFixedWidth()) + m.maxWidth = m.aveWidth; + else + m.maxWidth = std::max(DeviceTextWidth(dc, "W"), DeviceTextWidth(dc, "M")); + return m; +} + +int DeviceTextWidth(wxDC* dc, const wxString& text) { + if (text.empty()) + return 0; + wxCoord w = 0, h = 0; + dc->GetTextExtent(text, &w, &h); + return w; +} + +} // namespace mfcwx + +using namespace mfcwx; + +BOOL CDC::TextOut(int x, int y, const char* lpszString, int nCount) { return ::TextOut(m_hDC, x, y, lpszString, nCount); } + +BOOL CDC::TextOut(int x, int y, const CString& str) { + return ::TextOut(m_hDC, x, y, str.GetString(), str.GetLength()); +} + +BOOL CDC::ExtTextOut(int x, int y, UINT nOptions, LPCRECT lpRect, const char* lpszString, UINT nCount, + LPINT lpDxWidths) { + GdiLock lock; + DCState* s = LockedState(m_hDC); + if (!s) + return FALSE; + const char* str = lpszString ? lpszString : ""; + int n = lpszString ? static_cast(nCount) : 0; + DrawScope ds(s); + wxDC* d = ds ? ds.dc() : s->Measure(); + s->ApplyFont(d); + wxRect R; + if (lpRect) + R = s->DeviceRect(lpRect->left, lpRect->top, lpRect->right, lpRect->bottom); + if (ds && lpRect && (nOptions & ETO_OPAQUE)) { + FillDeviceRectColor(d, R, ToWxColour(s->a.bkColor)); + s->ApplyFont(d); + } + wxString text = StripLineBreaks(ToWx(str, n)); + DeviceMetrics m = GetDeviceMetrics(s, d); + std::vector offsets; + int width = 0; + if (lpDxWidths && !text.empty()) { + int acc = 0; + for (size_t i = 0; i < text.length() && i < static_cast(n); ++i) { + offsets.push_back(s->DeviceWidth(acc)); + acc += lpDxWidths[i]; + } + width = s->DeviceWidth(acc); + } else { + width = DeviceTextWidth(d, text); + } + POINT ref = (s->a.textAlign & TA_UPDATECP) ? s->a.pos : POINT{x, y}; + wxPoint dp = s->ToDevice(ref.x, ref.y); + int left = AlignLeft(s->a.textAlign, dp.x, width); + int top = AlignTop(s->a.textAlign, dp.y, m); + if (ds) { + bool clip = lpRect && (nOptions & ETO_CLIPPED); + if (clip) + s->ApplyClip(d, &R); + if (!(lpRect && (nOptions & ETO_OPAQUE))) + FillTextBackground(s, d, wxRect(left, top, width, m.height)); + if (!offsets.empty()) { + for (size_t i = 0; i < offsets.size(); ++i) + d->DrawText(text.Mid(i, 1), left + offsets[i], top); + } else { + d->DrawText(text, left, top); + } + if (clip) + s->ApplyClip(d); + } + UpdateCurrentPosition(s, width); + return TRUE; +} + +BOOL CDC::ExtTextOut(int x, int y, UINT nOptions, LPCRECT lpRect, const CString& str, LPINT lpDxWidths) { + return ExtTextOut(x, y, nOptions, lpRect, str.GetString(), static_cast(str.GetLength()), lpDxWidths); +} + +CSize CDC::TabbedTextOut(int x, int y, const char* lpszString, int nCount, int nTabPositions, + LPINT lpnTabStopPositions, int nTabOrigin) { + GdiLock lock; + DCState* s = LockedState(m_hDC); + CSize size = TabbedImpl(s, x, y, lpszString, nCount, nTabPositions, lpnTabStopPositions, nTabOrigin, true); + if (s && (s->a.textAlign & TA_UPDATECP)) + s->a.pos.x += size.cx; + return size; +} + +int CDC::DrawText(const char* lpszString, int nCount, LPRECT lpRect, UINT nFormat) { + return ::DrawText(m_hDC, lpszString, nCount, lpRect, nFormat); +} + +int CDC::DrawText(const CString& str, LPRECT lpRect, UINT nFormat) { + return ::DrawText(m_hDC, str.GetString(), str.GetLength(), lpRect, nFormat); +} + +CSize CDC::GetTextExtent(const char* lpszString, int nCount) const { + SIZE sz{0, 0}; + ::GetTextExtentPoint32(m_hDC, lpszString, nCount, &sz); + return CSize(sz); +} + +CSize CDC::GetTextExtent(const CString& str) const { return GetTextExtent(str.GetString(), str.GetLength()); } + +CSize CDC::GetTabbedTextExtent(const char* lpszString, int nCount, int nTabPositions, LPINT lpnTabStopPositions) const { + GdiLock lock; + return TabbedImpl(LockedState(m_hDC), 0, 0, lpszString, nCount, nTabPositions, lpnTabStopPositions, 0, false); +} + +BOOL CDC::GetTextExtentExPoint(const char* lpszString, int nCount, int nMaxExtent, LPINT lpnFit, LPINT alpDx, + LPSIZE lpSize) const { + GdiLock lock; + DCState* s = LockedState(m_hDC); + if (!s || !lpszString) + return FALSE; + if (nCount < 0) + nCount = static_cast(strlen(lpszString)); + wxDC* d = s->Measure(); + wxString text = ToWx(lpszString, nCount); + wxArrayInt widths; + if (!text.empty()) + d->GetPartialTextExtents(text, widths); + int fit = 0; + for (size_t i = 0; i < widths.size(); ++i) { + int lw = s->LogicalWidth(widths[i]); + if (lw <= nMaxExtent || !lpnFit) + fit = static_cast(i) + 1; + if (alpDx && (static_cast(i) < fit || !lpnFit)) + alpDx[i] = lw; + } + if (lpnFit) + *lpnFit = fit; + if (lpSize) { + DeviceMetrics m = GetDeviceMetrics(s, d); + lpSize->cx = widths.empty() ? 0 : s->LogicalWidth(widths.back()); + lpSize->cy = s->LogicalHeight(m.height); + } + return TRUE; +} + +BOOL CDC::GetTextMetrics(LPTEXTMETRIC lpMetrics) const { return ::GetTextMetrics(m_hDC, lpMetrics); } + +int CDC::GetTextFace(int nCount, char* lpszFacename) const { + GdiLock lock; + DCState* s = LockedState(m_hDC); + if (!s) + return 0; + LOGFONT lf; + memset(&lf, 0, sizeof lf); + ::GetObject(s->a.font, sizeof lf, &lf); + int len = static_cast(strnlen(lf.lfFaceName, LF_FACESIZE)); + if (!lpszFacename) + return len + 1; + if (nCount <= 0) + return 0; + int n = std::min(len, nCount - 1); + memcpy(lpszFacename, lf.lfFaceName, static_cast(n)); + lpszFacename[n] = 0; + return n; +} + +int CDC::GetTextFace(CString& rString) const { + char buf[LF_FACESIZE + 1]; + int n = GetTextFace(sizeof buf, buf); + rString = buf; + return n; +} + +BOOL CDC::GetCharWidth(UINT nFirstChar, UINT nLastChar, LPINT lpBuffer) const { + GdiLock lock; + DCState* s = LockedState(m_hDC); + if (!s || !lpBuffer || nLastChar < nFirstChar) + return FALSE; + wxDC* d = s->Measure(); + for (UINT c = nFirstChar; c <= nLastChar; ++c) { + char ch = static_cast(c & 0xFF); + lpBuffer[c - nFirstChar] = s->LogicalWidth(DeviceTextWidth(d, ToWx(&ch, 1))); + } + return TRUE; +} + +BOOL TextOut(HDC hdc, int x, int y, const char* lpString, int c) { + GdiLock lock; + return TextOutImpl(LockedState(hdc), x, y, lpString, c); +} + +BOOL TextOutA(HDC hdc, int x, int y, const char* lpString, int c) { return TextOut(hdc, x, y, lpString, c); } + +int DrawText(HDC hdc, const char* lpchText, int cchText, LPRECT lprc, UINT format) { + GdiLock lock; + return DrawTextImpl(LockedState(hdc), lpchText, cchText, lprc, format); +} + +int DrawTextA(HDC hdc, const char* lpchText, int cchText, LPRECT lprc, UINT format) { + return DrawText(hdc, lpchText, cchText, lprc, format); +} + +BOOL GetTextExtentPoint32(HDC hdc, const char* lpString, int c, LPSIZE psizl) { + GdiLock lock; + DCState* s = LockedState(hdc); + if (!s || !psizl) + return FALSE; + if (!lpString) + c = 0; + if (c < 0) + c = static_cast(strlen(lpString)); + wxDC* d = s->Measure(); + DeviceMetrics m = GetDeviceMetrics(s, d); + psizl->cx = c ? s->LogicalWidth(DeviceTextWidth(d, StripLineBreaks(ToWx(lpString, c)))) : 0; + psizl->cy = s->LogicalHeight(m.height); + return TRUE; +} + +BOOL GetTextExtentPoint32A(HDC hdc, const char* lpString, int c, LPSIZE psizl) { + return GetTextExtentPoint32(hdc, lpString, c, psizl); +} + +BOOL GetTextMetrics(HDC hdc, LPTEXTMETRIC lptm) { + GdiLock lock; + DCState* s = LockedState(hdc); + if (!s || !lptm) + return FALSE; + wxDC* d = s->Measure(); + DeviceMetrics m = GetDeviceMetrics(s, d); + LOGFONT lf; + memset(&lf, 0, sizeof lf); + ::GetObject(s->a.font, sizeof lf, &lf); + memset(lptm, 0, sizeof *lptm); + lptm->tmHeight = s->LogicalHeight(m.height); + lptm->tmAscent = s->LogicalHeight(m.ascent); + lptm->tmDescent = lptm->tmHeight - lptm->tmAscent; + lptm->tmInternalLeading = s->LogicalHeight(m.internalLeading); + lptm->tmExternalLeading = s->LogicalHeight(m.externalLeading); + lptm->tmAveCharWidth = s->LogicalWidth(m.aveWidth); + lptm->tmMaxCharWidth = s->LogicalWidth(m.maxWidth); + lptm->tmWeight = lf.lfWeight ? lf.lfWeight : FW_NORMAL; + lptm->tmOverhang = 0; + lptm->tmDigitizedAspectX = 96; + lptm->tmDigitizedAspectY = 96; + lptm->tmFirstChar = 0x20; + lptm->tmLastChar = 0xFF; + lptm->tmDefaultChar = 0x1F; + lptm->tmBreakChar = 0x20; + lptm->tmItalic = lf.lfItalic ? 1 : 0; + lptm->tmUnderlined = lf.lfUnderline ? 1 : 0; + lptm->tmStruckOut = lf.lfStrikeOut ? 1 : 0; + bool fixed = d->GetFont().IsFixedWidth(); + BYTE family = static_cast(lf.lfPitchAndFamily & 0xF0); + if (!family) + family = static_cast(fixed ? FF_MODERN : FF_SWISS); + lptm->tmPitchAndFamily = static_cast(family | (fixed ? 0 : 0x01) | 0x02 | 0x04); + lptm->tmCharSet = lf.lfCharSet; + return TRUE; +} + +BOOL GetTextMetricsA(HDC hdc, LPTEXTMETRIC lptm) { return GetTextMetrics(hdc, lptm); } diff --git a/port/mfcwx/src/profile.cpp b/port/mfcwx/src/profile.cpp new file mode 100644 index 000000000..97338ee89 --- /dev/null +++ b/port/mfcwx/src/profile.cpp @@ -0,0 +1,773 @@ +#include +#include +#include + +#include "afxwin.h" +#include "bridge.h" +#include "runtime.h" + +#include +#include +#include +#include +#include + +#include +#include + +#undef fopen +#undef rename +#undef remove + +namespace mfcwx { +namespace { + +// --------------------------------------------------------------------------------------------- +// INI files + +struct IniFile { + std::vector lines; + std::string eol = "\r\n"; +}; + +std::mutex& IniLock() { + static std::mutex* m = new std::mutex; + return *m; +} + +std::string Trim(const std::string& s) { + size_t b = s.find_first_not_of(" \t"); + if (b == std::string::npos) + return std::string(); + size_t e = s.find_last_not_of(" \t"); + return s.substr(b, e - b + 1); +} + +void MakeDirectories(const std::string& dir) { + for (size_t pos = 1; pos != std::string::npos;) { + pos = dir.find('/', pos + 1); + mkdir(dir.substr(0, pos).c_str(), 0755); + } +} + +std::string IniPath(LPCSTR file) { + std::string f = file ? file : "win.ini"; + if (f.find_first_of("\\/") == std::string::npos) { + std::string dir = UserConfigDirectory(); + MakeDirectories(dir); + return dir + "/" + NativePath(f.c_str()); + } + return FsPath(f.c_str()); +} + +bool LoadIni(const std::string& path, IniFile& ini) { + FILE* f = ::fopen(path.c_str(), "rb"); + if (!f) + return false; + std::string data; + char buf[8192]; + size_t n; + while ((n = fread(buf, 1, sizeof buf, f)) > 0) + data.append(buf, n); + fclose(f); + if (data.size() >= 3 && static_cast(data[0]) == 0xEF && static_cast(data[1]) == 0xBB && + static_cast(data[2]) == 0xBF) + data.erase(0, 3); + size_t firstNl = data.find('\n'); + ini.eol = firstNl != std::string::npos && firstNl > 0 && data[firstNl - 1] == '\r' ? "\r\n" + : firstNl != std::string::npos ? "\n" + : "\r\n"; + size_t start = 0; + while (start < data.size()) { + size_t nl = data.find('\n', start); + std::string line = data.substr(start, nl == std::string::npos ? std::string::npos : nl - start); + if (!line.empty() && line.back() == '\r') + line.pop_back(); + ini.lines.push_back(line); + if (nl == std::string::npos) + break; + start = nl + 1; + } + return true; +} + +bool SaveIni(const std::string& path, const IniFile& ini) { + FILE* f = ::fopen(path.c_str(), "wb"); + if (!f) { + SetLastErrorFromErrno(errno); + return false; + } + for (const std::string& line : ini.lines) { + fwrite(line.data(), 1, line.size(), f); + fwrite(ini.eol.data(), 1, ini.eol.size(), f); + } + return fclose(f) == 0; +} + +bool SectionName(const std::string& line, std::string& name) { + std::string t = Trim(line); + if (t.empty() || t[0] != '[') + return false; + size_t close = t.find(']'); + name = Trim(t.substr(1, close == std::string::npos ? std::string::npos : close - 1)); + return true; +} + +bool KeyValue(const std::string& line, std::string& key, std::string& value) { + std::string t = Trim(line); + if (t.empty() || t[0] == ';') + return false; + size_t eq = t.find('='); + key = Trim(t.substr(0, eq)); + value = eq == std::string::npos ? std::string() : Trim(t.substr(eq + 1)); + return true; +} + +// Returns the index of the section header line and sets end to the first line after the section. +long FindSection(const IniFile& ini, const char* app, size_t& end) { + std::string name; + for (size_t i = 0; i < ini.lines.size(); ++i) { + if (SectionName(ini.lines[i], name) && strcasecmp(name.c_str(), app) == 0) { + end = i + 1; + std::string other; + while (end < ini.lines.size() && !SectionName(ini.lines[end], other)) + ++end; + return static_cast(i); + } + } + return -1; +} + +DWORD CopyList(const std::vector& items, LPSTR ret, DWORD size) { + if (!ret || size == 0) + return 0; + if (size == 1) { + ret[0] = 0; + return 0; + } + DWORD o = 0; + for (const std::string& item : items) { + if (o + item.size() + 1 > size - 1) { + size_t room = size - 1 - o; + if (room > 0) { + memcpy(ret + o, item.data(), room - 1); + ret[o + room - 1] = 0; + } + ret[size - 2] = 0; + ret[size - 1] = 0; + return size - 2; + } + memcpy(ret + o, item.c_str(), item.size() + 1); + o += static_cast(item.size() + 1); + } + ret[o] = 0; + return o; +} + +DWORD CopyString(const std::string& s, LPSTR ret, DWORD size) { + if (!ret || size == 0) + return 0; + size_t n = std::min(s.size(), static_cast(size - 1)); + memcpy(ret, s.data(), n); + ret[n] = 0; + return static_cast(n); +} + +// --------------------------------------------------------------------------------------------- +// Registry + +constexpr unsigned kKeyMagic = 0x5245474Bu; + +struct RegKey { + unsigned magic; + std::string path; +}; + +std::mutex& RegLock() { + static std::mutex* m = new std::mutex; + return *m; +} + +std::set& RegKeys() { + static auto* s = new std::set; + return *s; +} + +std::string RegistryFile() { + const char* env = getenv("MFCWX_REGISTRY_FILE"); + if (env && *env) + return env; +#ifdef __APPLE__ + return UserConfigDirectory() + "/CrypTool1.ini"; +#else + return UserConfigDirectory() + "/cryptool1.ini"; +#endif +} + +wxFileConfig* Config() { + static wxFileConfig* config = nullptr; + if (!config) { + std::string path = RegistryFile(); + size_t slash = path.rfind('/'); + if (slash != std::string::npos && slash > 0) + MakeDirectories(path.substr(0, slash)); + wxLogNull noLog; + config = new wxFileConfig(wxEmptyString, wxEmptyString, wxString::FromUTF8(path.c_str()), wxEmptyString, + wxCONFIG_USE_LOCAL_FILE); + config->SetExpandEnvVars(false); + config->SetRecordDefaults(false); + } + return config; +} + +void Flush() { + wxLogNull noLog; + Config()->Flush(); +} + +std::string Escape(const std::string& s) { + std::string out; + for (char c : s) { + if (c == '%') + out += "%25"; + else if (c == '/') + out += "%2F"; + else + out += c; + } + return out; +} + +std::string Unescape(const std::string& s) { + std::string out; + for (size_t i = 0; i < s.size(); ++i) { + if (s[i] == '%' && i + 2 < s.size()) { + if (s.compare(i, 3, "%25") == 0) { + out += '%'; + i += 2; + continue; + } + if (s.compare(i, 3, "%2F") == 0) { + out += '/'; + i += 2; + continue; + } + } + out += s[i]; + } + return out; +} + +const char* RootName(HKEY key) { + switch (reinterpret_cast(key)) { + case 0x80000000u: + return "HKEY_CLASSES_ROOT"; + case 0x80000001u: + return "HKEY_CURRENT_USER"; + case 0x80000002u: + return "HKEY_LOCAL_MACHINE"; + case 0x80000003u: + return "HKEY_USERS"; + case 0x80000005u: + return "HKEY_CURRENT_CONFIG"; + default: + return nullptr; + } +} + +// Must be called with RegLock held. Returns false for an invalid handle. +bool BasePath(HKEY key, std::string& path) { + if (const char* root = RootName(key)) { + path = std::string("/") + root; + return true; + } + auto* k = reinterpret_cast(key); + if (!RegKeys().count(k)) + return false; + path = k->path; + return true; +} + +std::string Append(const std::string& base, LPCSTR sub) { + std::string path = base; + if (!sub) + return path; + std::string s = sub; + size_t start = 0; + while (start <= s.size()) { + size_t sep = s.find('\\', start); + std::string part = s.substr(start, sep == std::string::npos ? std::string::npos : sep - start); + if (!part.empty()) + path += "/" + Escape(part); + if (sep == std::string::npos) + break; + start = sep + 1; + } + return path; +} + +bool IsRootPath(const std::string& path) { return std::count(path.begin(), path.end(), '/') == 1; } + +wxString WxPath(const std::string& path) { return ToWx(path.c_str()); } + +std::string ValueEntry(const std::string& keyPath, LPCSTR name) { + return keyPath + "/" + (name && *name ? Escape(name) : std::string("@")); +} + +HKEY NewKey(const std::string& path) { + auto* k = new RegKey{kKeyMagic, path}; + RegKeys().insert(k); + return reinterpret_cast(k); +} + +std::string ToHex(const BYTE* data, size_t n) { + static const char digits[] = "0123456789abcdef"; + std::string out; + for (size_t i = 0; i < n; ++i) { + out += digits[data[i] >> 4]; + out += digits[data[i] & 15]; + } + return out; +} + +std::vector FromHex(const std::string& s) { + std::vector out; + auto val = [](char c) { return c >= 'a' ? c - 'a' + 10 : c >= 'A' ? c - 'A' + 10 : c - '0'; }; + for (size_t i = 0; i + 1 < s.size(); i += 2) + out.push_back(static_cast(val(s[i]) * 16 + val(s[i + 1]))); + return out; +} + +void Decode(const wxString& raw, DWORD& type, std::vector& bytes) { + std::string utf8(raw.utf8_str()); + auto text = [&](size_t prefix) { + std::string ansi = FromWx(raw.Mid(prefix)); + bytes.assign(ansi.begin(), ansi.end()); + bytes.push_back(0); + }; + if (utf8.compare(0, 3, "sz:") == 0) { + type = REG_SZ; + text(3); + } else if (utf8.compare(0, 7, "expand:") == 0) { + type = REG_EXPAND_SZ; + text(7); + } else if (utf8.compare(0, 6, "dword:") == 0) { + type = REG_DWORD; + DWORD v = static_cast(strtoul(utf8.c_str() + 6, nullptr, 16)); + bytes.resize(4); + memcpy(bytes.data(), &v, 4); + } else if (utf8.compare(0, 4, "hex:") == 0) { + type = REG_BINARY; + bytes = FromHex(utf8.substr(4)); + } else if (utf8.size() > 1 && utf8[0] == 't' && utf8.find(':') != std::string::npos) { + type = static_cast(strtoul(utf8.c_str() + 1, nullptr, 10)); + bytes = FromHex(utf8.substr(utf8.find(':') + 1)); + } else { + type = REG_SZ; + text(0); + } +} + +wxString Encode(DWORD type, const BYTE* data, DWORD size) { + if (type == REG_SZ || type == REG_EXPAND_SZ) { + const char* s = reinterpret_cast(data); + size_t n = s ? strnlen(s, size) : 0; + return wxString(type == REG_SZ ? "sz:" : "expand:") + ToWx(s ? s : "", static_cast(n)); + } + if (type == REG_DWORD) { + DWORD v = 0; + memcpy(&v, data, 4); + return wxString::Format("dword:%08x", static_cast(v)); + } + std::string hex = data ? ToHex(data, size) : std::string(); + if (type == REG_BINARY) + return wxString("hex:") + wxString::FromUTF8(hex.c_str()); + return wxString::Format("t%u:", static_cast(type)) + wxString::FromUTF8(hex.c_str()); +} + +} // namespace +} // namespace mfcwx + +using namespace mfcwx; + +DWORD GetPrivateProfileStringA(LPCSTR app, LPCSTR key, LPCSTR def, LPSTR ret, DWORD size, LPCSTR file) { + std::lock_guard lk(IniLock()); + IniFile ini; + LoadIni(IniPath(file), ini); + if (!app) { + std::vector sections; + std::string name; + for (const std::string& line : ini.lines) + if (SectionName(line, name)) + sections.push_back(name); + return CopyList(sections, ret, size); + } + size_t end = 0; + long start = FindSection(ini, app, end); + if (!key) { + std::vector keys; + std::string k, v; + for (size_t i = start < 0 ? end : static_cast(start) + 1; start >= 0 && i < end; ++i) + if (KeyValue(ini.lines[i], k, v)) + keys.push_back(k); + return CopyList(keys, ret, size); + } + if (start >= 0) { + std::string k, v; + for (size_t i = static_cast(start) + 1; i < end; ++i) { + if (KeyValue(ini.lines[i], k, v) && strcasecmp(k.c_str(), key) == 0) { + if (v.size() >= 2 && (v[0] == '"' || v[0] == '\'') && v.back() == v[0]) + v = v.substr(1, v.size() - 2); + return CopyString(v, ret, size); + } + } + } + std::string d = def ? def : ""; + while (!d.empty() && d.back() == ' ') + d.pop_back(); + return CopyString(d, ret, size); +} + +UINT GetPrivateProfileIntA(LPCSTR app, LPCSTR key, INT def, LPCSTR file) { + char buf[64]; + GetPrivateProfileStringA(app, key, "", buf, sizeof buf, file); + if (!buf[0]) + return static_cast(def); + return static_cast(strtol(buf, nullptr, 10)); +} + +DWORD GetPrivateProfileSectionA(LPCSTR app, LPSTR ret, DWORD size, LPCSTR file) { + std::lock_guard lk(IniLock()); + IniFile ini; + LoadIni(IniPath(file), ini); + std::vector items; + size_t end = 0; + long start = app ? FindSection(ini, app, end) : -1; + for (size_t i = start < 0 ? end : static_cast(start) + 1; start >= 0 && i < end; ++i) { + std::string t = Trim(ini.lines[i]); + if (!t.empty() && t[0] != ';') + items.push_back(t); + } + return CopyList(items, ret, size); +} + +BOOL WritePrivateProfileStringA(LPCSTR app, LPCSTR key, LPCSTR value, LPCSTR file) { + if (!app) { + if (!key && !value) + return TRUE; + SetLastError(87); + return FALSE; + } + std::lock_guard lk(IniLock()); + std::string path = IniPath(file); + IniFile ini; + LoadIni(path, ini); + size_t end = 0; + long start = FindSection(ini, app, end); + if (!key) { + if (start < 0) + return TRUE; + ini.lines.erase(ini.lines.begin() + start, ini.lines.begin() + static_cast(end)); + return SaveIni(path, ini) ? TRUE : FALSE; + } + std::string entry = std::string(key) + "=" + (value ? value : ""); + if (start < 0) { + if (!value) + return TRUE; + ini.lines.push_back(std::string("[") + app + "]"); + ini.lines.push_back(entry); + return SaveIni(path, ini) ? TRUE : FALSE; + } + size_t insertAt = static_cast(start) + 1; + std::string k, v; + for (size_t i = static_cast(start) + 1; i < end; ++i) { + if (!Trim(ini.lines[i]).empty()) + insertAt = i + 1; + if (KeyValue(ini.lines[i], k, v) && strcasecmp(k.c_str(), key) == 0) { + if (value) + ini.lines[i] = entry; + else + ini.lines.erase(ini.lines.begin() + static_cast(i)); + return SaveIni(path, ini) ? TRUE : FALSE; + } + } + if (!value) + return TRUE; + ini.lines.insert(ini.lines.begin() + static_cast(insertAt), entry); + return SaveIni(path, ini) ? TRUE : FALSE; +} + +LONG RegOpenKeyExA(HKEY key, LPCSTR sub, DWORD, REGSAM, PHKEY result) { + if (!result) + return 87; + std::lock_guard lk(RegLock()); + std::string base; + if (!BasePath(key, base)) + return ERROR_INVALID_HANDLE; + std::string path = Append(base, sub); + if (!IsRootPath(path) && !Config()->HasGroup(WxPath(path))) + return ERROR_FILE_NOT_FOUND; + *result = NewKey(path); + return ERROR_SUCCESS; +} + +LONG RegOpenKeyA(HKEY key, LPCSTR sub, PHKEY result) { return RegOpenKeyExA(key, sub, 0, KEY_ALL_ACCESS, result); } + +LONG RegCreateKeyExA(HKEY key, LPCSTR sub, DWORD, LPSTR, DWORD, REGSAM, LPSECURITY_ATTRIBUTES, PHKEY result, + LPDWORD disposition) { + if (!result) + return 87; + std::lock_guard lk(RegLock()); + std::string base; + if (!BasePath(key, base)) + return ERROR_INVALID_HANDLE; + std::string path = Append(base, sub); + bool existed = IsRootPath(path) || Config()->HasGroup(WxPath(path)); + if (!existed) { + wxString old = Config()->GetPath(); + Config()->SetPath(WxPath(path)); + Config()->SetPath(old.empty() ? wxString("/") : old); + Flush(); + } + if (disposition) + *disposition = existed ? REG_OPENED_EXISTING_KEY : REG_CREATED_NEW_KEY; + *result = NewKey(path); + return ERROR_SUCCESS; +} + +LONG RegCreateKeyA(HKEY key, LPCSTR sub, PHKEY result) { + return RegCreateKeyExA(key, sub, 0, nullptr, 0, KEY_ALL_ACCESS, nullptr, result, nullptr); +} + +LONG RegCloseKey(HKEY key) { + if (RootName(key)) + return ERROR_SUCCESS; + std::lock_guard lk(RegLock()); + auto* k = reinterpret_cast(key); + if (!RegKeys().erase(k)) + return ERROR_INVALID_HANDLE; + k->magic = 0; + delete k; + return ERROR_SUCCESS; +} + +LONG RegQueryValueExA(HKEY key, LPCSTR name, LPDWORD, LPDWORD type, LPBYTE data, LPDWORD size) { + std::lock_guard lk(RegLock()); + std::string base; + if (!BasePath(key, base)) + return ERROR_INVALID_HANDLE; + wxString entry = WxPath(ValueEntry(base, name)); + if (!Config()->HasEntry(entry)) + return ERROR_FILE_NOT_FOUND; + wxString raw; + Config()->Read(entry, &raw); + DWORD t; + std::vector bytes; + Decode(raw, t, bytes); + if (type) + *type = t; + if (!size) + return data ? 87 : ERROR_SUCCESS; + DWORD needed = static_cast(bytes.size()); + if (!data) { + *size = needed; + return ERROR_SUCCESS; + } + if (*size < needed) { + *size = needed; + return ERROR_MORE_DATA; + } + if (needed) + memcpy(data, bytes.data(), needed); + *size = needed; + return ERROR_SUCCESS; +} + +LONG RegSetValueExA(HKEY key, LPCSTR name, DWORD, DWORD type, const BYTE* data, DWORD size) { + if (type == REG_DWORD && (!data || size < 4)) + return 87; + if (!data && size) + return 87; + std::lock_guard lk(RegLock()); + std::string base; + if (!BasePath(key, base)) + return ERROR_INVALID_HANDLE; + wxLogNull noLog; + if (!Config()->Write(WxPath(ValueEntry(base, name)), Encode(type, data, size))) + return ERROR_ACCESS_DENIED; + Config()->SetPath("/"); + Flush(); + return ERROR_SUCCESS; +} + +LONG RegDeleteValueA(HKEY key, LPCSTR name) { + std::lock_guard lk(RegLock()); + std::string base; + if (!BasePath(key, base)) + return ERROR_INVALID_HANDLE; + wxString entry = WxPath(ValueEntry(base, name)); + if (!Config()->HasEntry(entry)) + return ERROR_FILE_NOT_FOUND; + Config()->DeleteEntry(entry, false); + Config()->SetPath("/"); + Flush(); + return ERROR_SUCCESS; +} + +LONG RegDeleteKeyA(HKEY key, LPCSTR sub) { + if (!sub || !*sub) + return 87; + std::lock_guard lk(RegLock()); + std::string base; + if (!BasePath(key, base)) + return ERROR_INVALID_HANDLE; + std::string path = Append(base, sub); + if (IsRootPath(path) || !Config()->HasGroup(WxPath(path))) + return ERROR_FILE_NOT_FOUND; + Config()->DeleteGroup(WxPath(path)); + Config()->SetPath("/"); + Flush(); + return ERROR_SUCCESS; +} + +LONG RegEnumKeyExA(HKEY key, DWORD index, LPSTR name, LPDWORD nameSize, LPDWORD, LPSTR cls, LPDWORD clsSize, + PFILETIME lastWrite) { + if (!name || !nameSize) + return 87; + std::vector names; + { + std::lock_guard lk(RegLock()); + std::string base; + if (!BasePath(key, base)) + return ERROR_INVALID_HANDLE; + wxFileConfig* config = Config(); + if (!config->HasGroup(WxPath(base))) + return 259; + wxString old = config->GetPath(); + config->SetPath(WxPath(base)); + wxString group; + long cookie; + for (bool more = config->GetFirstGroup(group, cookie); more; more = config->GetNextGroup(group, cookie)) + names.push_back(Unescape(FromWx(group))); + config->SetPath(old.empty() ? wxString("/") : old); + } + std::sort(names.begin(), names.end(), + [](const std::string& a, const std::string& b) { return strcasecmp(a.c_str(), b.c_str()) < 0; }); + if (index >= names.size()) + return 259; + const std::string& n = names[index]; + if (*nameSize <= n.size()) { + *nameSize = static_cast(n.size() + 1); + return ERROR_MORE_DATA; + } + memcpy(name, n.c_str(), n.size() + 1); + *nameSize = static_cast(n.size()); + if (cls && clsSize && *clsSize) + cls[0] = 0; + if (clsSize) + *clsSize = 0; + if (lastWrite) + GetSystemTimeAsFileTime(lastWrite); + return ERROR_SUCCESS; +} + +LONG CRegKey::Create(HKEY hKeyParent, const char* lpszKeyName, LPSTR lpszClass, DWORD dwOptions, REGSAM samDesired, + LPSECURITY_ATTRIBUTES lpSecAttr, LPDWORD lpdwDisposition) { + HKEY hKey = nullptr; + LONG r = RegCreateKeyExA(hKeyParent, lpszKeyName, 0, lpszClass, dwOptions, samDesired, lpSecAttr, &hKey, + lpdwDisposition); + if (r == ERROR_SUCCESS) { + Close(); + m_hKey = hKey; + } + return r; +} + +LONG CRegKey::Open(HKEY hKeyParent, const char* lpszKeyName, REGSAM samDesired) { + HKEY hKey = nullptr; + LONG r = RegOpenKeyExA(hKeyParent, lpszKeyName, 0, samDesired, &hKey); + if (r == ERROR_SUCCESS) { + Close(); + m_hKey = hKey; + } + return r; +} + +LONG CRegKey::Close() { + LONG r = ERROR_SUCCESS; + if (m_hKey) { + r = RegCloseKey(m_hKey); + m_hKey = nullptr; + } + return r; +} + +LONG CRegKey::QueryDWORDValue(const char* pszValueName, DWORD& dwValue) { + DWORD type = 0; + DWORD size = sizeof(DWORD); + DWORD value = 0; + LONG r = RegQueryValueExA(m_hKey, pszValueName, nullptr, &type, reinterpret_cast(&value), &size); + if (r != ERROR_SUCCESS) + return r; + if (type != REG_DWORD) + return 13; + dwValue = value; + return ERROR_SUCCESS; +} + +LONG CRegKey::QueryStringValue(const char* pszValueName, LPSTR pszValue, ULONG* pnChars) { + if (!pnChars) + return 87; + DWORD type = 0; + DWORD size = *pnChars; + LONG r = RegQueryValueExA(m_hKey, pszValueName, nullptr, &type, reinterpret_cast(pszValue), &size); + if (r != ERROR_SUCCESS && r != ERROR_MORE_DATA) + return r; + if (type != REG_SZ && type != REG_EXPAND_SZ) + return 13; + *pnChars = size; + return r; +} + +LONG CRegKey::QueryValue(LPSTR szValue, const char* lpszValueName, DWORD* pdwCount) { + if (!pdwCount) + return 87; + DWORD type = 0; + LONG r = RegQueryValueExA(m_hKey, lpszValueName, nullptr, &type, reinterpret_cast(szValue), pdwCount); + if (r == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ && type != 7) + return 13; + return r; +} + +LONG CRegKey::QueryBinaryValue(const char* pszValueName, void* pValue, ULONG* pnBytes) { + if (!pnBytes) + return 87; + DWORD type = 0; + DWORD size = *pnBytes; + LONG r = RegQueryValueExA(m_hKey, pszValueName, nullptr, &type, static_cast(pValue), &size); + if (r != ERROR_SUCCESS && r != ERROR_MORE_DATA) + return r; + if (type != REG_BINARY) + return 13; + *pnBytes = size; + return r; +} + +LONG CRegKey::SetDWORDValue(const char* pszValueName, DWORD dwValue) { + return RegSetValueExA(m_hKey, pszValueName, 0, REG_DWORD, reinterpret_cast(&dwValue), sizeof dwValue); +} + +LONG CRegKey::SetStringValue(const char* pszValueName, const char* pszValue, DWORD dwType) { + if (!pszValue) + return 87; + return RegSetValueExA(m_hKey, pszValueName, 0, dwType, reinterpret_cast(pszValue), + static_cast(strlen(pszValue) + 1)); +} + +LONG CRegKey::SetBinaryValue(const char* pszValueName, const void* pValue, ULONG nBytes) { + return RegSetValueExA(m_hKey, pszValueName, 0, REG_BINARY, static_cast(pValue), nBytes); +} + +LONG CRegKey::DeleteValue(const char* lpszValue) { return RegDeleteValueA(m_hKey, lpszValue); } + +LONG CRegKey::DeleteSubKey(const char* lpszSubKey) { return RegDeleteKeyA(m_hKey, lpszSubKey); } diff --git a/port/mfcwx/src/thread.cpp b/port/mfcwx/src/thread.cpp new file mode 100644 index 000000000..f1db24d1d --- /dev/null +++ b/port/mfcwx/src/thread.cpp @@ -0,0 +1,141 @@ +#include "afxwin.h" +#include "runtime.h" + +IMPLEMENT_DYNAMIC(CWinThread, CCmdTarget) + +namespace { + +thread_local CWinThread* t_currentThread = nullptr; + +DWORD RunThread(CWinThread* pThread) { + t_currentThread = pThread; + // Like MFC, a secondary thread sees the application's main window as its own. + CWnd* inherited = nullptr; + if (!pThread->m_pMainWnd) { + CWinApp* app = AfxGetApp(); + if (app && app != pThread && app->m_pMainWnd) + inherited = pThread->m_pMainWnd = app->m_pMainWnd; + } + DWORD code = 0; + bool deleteThread = true; + try { + if (pThread->m_pfnThreadProc) + code = pThread->m_pfnThreadProc(pThread->m_pThreadParams); + else if (!pThread->InitInstance()) + code = static_cast(pThread->ExitInstance()); + else + code = static_cast(pThread->Run()); + } catch (mfcwx::ThreadExit& e) { + code = e.code; + deleteThread = e.deleteThread; + } + t_currentThread = nullptr; + if (deleteThread && pThread->m_bAutoDelete) { + delete pThread; + } else if (inherited && pThread->m_pMainWnd == inherited) { + pThread->m_pMainWnd = nullptr; + } + return code; +} + +CWinThread* StartWinThread(CWinThread* pThread, int nPriority, UINT nStackSize, DWORD dwCreateFlags, + LPSECURITY_ATTRIBUTES lpSecurityAttrs) { + if (!pThread->CreateThread(dwCreateFlags | CREATE_SUSPENDED, nStackSize, lpSecurityAttrs)) { + pThread->m_bAutoDelete = FALSE; + delete pThread; + return nullptr; + } + pThread->SetThreadPriority(nPriority); + if (!(dwCreateFlags & CREATE_SUSPENDED)) + pThread->ResumeThread(); + return pThread; +} + +} // namespace + +CWinThread::CWinThread() + : m_pMainWnd(nullptr), m_pActiveWnd(nullptr), m_bAutoDelete(TRUE), m_hThread(nullptr), m_nThreadID(0), + m_pfnThreadProc(nullptr), m_pThreadParams(nullptr) { + memset(&m_msgCur, 0, sizeof m_msgCur); +} + +CWinThread::CWinThread(AFX_THREADPROC pfnThreadProc, LPVOID pParam) : CWinThread() { + m_pfnThreadProc = pfnThreadProc; + m_pThreadParams = pParam; +} + +CWinThread::~CWinThread() { + if (m_hThread) + CloseHandle(m_hThread); + m_hThread = nullptr; + if (t_currentThread == this) + t_currentThread = nullptr; +} + +BOOL CWinThread::CreateThread(DWORD dwCreateFlags, UINT nStackSize, LPSECURITY_ATTRIBUTES) { + if (m_hThread) + return FALSE; + CWinThread* self = this; + DWORD id = 0; + // Start suspended so the members are set before an auto-deleting thread can finish. + HANDLE h = mfcwx::StartThread([self]() { return RunThread(self); }, CREATE_SUSPENDED, nStackSize, &id); + if (!h) + return FALSE; + m_hThread = h; + m_nThreadID = id; + if (!(dwCreateFlags & CREATE_SUSPENDED)) + ::ResumeThread(h); + return TRUE; +} + +int CWinThread::GetThreadPriority() { return ::GetThreadPriority(m_hThread); } + +BOOL CWinThread::SetThreadPriority(int nPriority) { return ::SetThreadPriority(m_hThread, nPriority); } + +DWORD CWinThread::SuspendThread() { return ::SuspendThread(m_hThread); } + +DWORD CWinThread::ResumeThread() { return ::ResumeThread(m_hThread); } + +BOOL CWinThread::PostThreadMessage(UINT, WPARAM, LPARAM) { return FALSE; } + +BOOL CWinThread::InitInstance() { return FALSE; } + +int CWinThread::ExitInstance() { return static_cast(m_msgCur.wParam); } + +int CWinThread::Run() { return ExitInstance(); } + +BOOL CWinThread::PreTranslateMessage(MSG*) { return FALSE; } + +BOOL CWinThread::PumpMessage() { return FALSE; } + +BOOL CWinThread::OnIdle(LONG) { return FALSE; } + +CWnd* CWinThread::GetMainWnd() { return m_pActiveWnd ? m_pActiveWnd : m_pMainWnd; } + +BOOL CWinThread::ProcessMessageFilter(int, LPMSG) { return FALSE; } + +CWinThread* AFXAPI AfxBeginThread(AFX_THREADPROC pfnThreadProc, LPVOID pParam, int nPriority, UINT nStackSize, + DWORD dwCreateFlags, LPSECURITY_ATTRIBUTES lpSecurityAttrs) { + return StartWinThread(new CWinThread(pfnThreadProc, pParam), nPriority, nStackSize, dwCreateFlags, + lpSecurityAttrs); +} + +CWinThread* AFXAPI AfxBeginThread(CRuntimeClass* pThreadClass, int nPriority, UINT nStackSize, DWORD dwCreateFlags, + LPSECURITY_ATTRIBUTES lpSecurityAttrs) { + CObject* object = pThreadClass ? pThreadClass->CreateObject() : nullptr; + if (!object) + AfxThrowMemoryException(); + return StartWinThread(static_cast(object), nPriority, nStackSize, dwCreateFlags, lpSecurityAttrs); +} + +void AFXAPI AfxEndThread(UINT nExitCode, BOOL bDelete) { + if (mfcwx::IsProcessMainThread()) + exit(static_cast(nExitCode)); + throw mfcwx::ThreadExit{nExitCode, bDelete != FALSE}; +} + +CWinThread* AFXAPI AfxGetThread() { + if (t_currentThread) + return t_currentThread; + return mfcwx::IsProcessMainThread() ? AfxGetApp() : nullptr; +} diff --git a/port/mfcwx/src/win32.cpp b/port/mfcwx/src/win32.cpp new file mode 100644 index 000000000..1a9b0b2a5 --- /dev/null +++ b/port/mfcwx/src/win32.cpp @@ -0,0 +1,1453 @@ +#include +#include +#include +#include + +#include "afx.h" +#include "globalmem.h" +#include "runtime.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#ifdef __APPLE__ +#include +#include +#include +#endif + +#undef fopen +#undef rename +#undef remove + +extern char** environ; + +namespace mfcwx { + +extern const uint16_t kCp1250High[128]; +extern const uint16_t kCp1251High[128]; +extern const uint16_t kCp1252High[128]; +extern const uint16_t kCp1253High[128]; +extern const uint16_t kCp1254High[128]; +extern const uint16_t kCp1257High[128]; + +namespace { + +wchar_t UniUpper(wchar_t c) { + if (c < 0x80) + return c >= 'a' && c <= 'z' ? c - 0x20 : c; + if (c >= 0xE0 && c <= 0xFE && c != 0xF7) + return c - 0x20; + if (c == 0xFF) + return 0x178; + if (c == 0x131) + return 'I'; + if ((c >= 0x100 && c <= 0x137) || (c >= 0x14A && c <= 0x177)) + return (c & 1) ? c - 1 : c; + if ((c >= 0x139 && c <= 0x148) || (c >= 0x179 && c <= 0x17E)) + return (c & 1) ? c : c - 1; + if (c == 0x192) + return 0x191; + if ((c >= 0x3B1 && c <= 0x3CB && c != 0x3C2)) + return c - 0x20; + if (c == 0x3C2) + return 0x3A3; + if (c == 0x3AC) + return 0x386; + if (c >= 0x3AD && c <= 0x3AF) + return c - 0x25; + if (c == 0x3CC) + return 0x38C; + if (c == 0x3CD || c == 0x3CE) + return c - 0x3F; + if (c >= 0x430 && c <= 0x44F) + return c - 0x20; + if (c >= 0x450 && c <= 0x45F) + return c - 0x50; + if (c == 0x491) + return 0x490; + return c; +} + +wchar_t UniLower(wchar_t c) { + if (c < 0x80) + return c >= 'A' && c <= 'Z' ? c + 0x20 : c; + if (c >= 0xC0 && c <= 0xDE && c != 0xD7) + return c + 0x20; + if (c == 0x178) + return 0xFF; + if (c == 0x130) + return 'i'; + if ((c >= 0x100 && c <= 0x137) || (c >= 0x14A && c <= 0x177)) + return (c & 1) ? c : c + 1; + if ((c >= 0x139 && c <= 0x148) || (c >= 0x179 && c <= 0x17E)) + return (c & 1) ? c + 1 : c; + if (c == 0x191) + return 0x192; + if (c >= 0x391 && c <= 0x3AB && c != 0x3A2) + return c + 0x20; + if (c == 0x386) + return 0x3AC; + if (c >= 0x388 && c <= 0x38A) + return c + 0x25; + if (c == 0x38C) + return 0x3CC; + if (c == 0x38E || c == 0x38F) + return c + 0x3F; + if (c >= 0x410 && c <= 0x42F) + return c + 0x20; + if (c >= 0x400 && c <= 0x40F) + return c + 0x50; + if (c == 0x490) + return 0x491; + return c; +} + +bool UniIsCaselessLetter(wchar_t c) { + return c == 0xAA || c == 0xB5 || c == 0xBA || c == 0xDF || c == 0x138 || c == 0x149 || c == 0x17F || + c == 0x390 || c == 0x3B0; +} + +const uint16_t* CodePageTable(UINT cp) { + switch (cp) { + case 1250: + return kCp1250High; + case 1251: + return kCp1251High; + case 1252: + return kCp1252High; + case 1253: + return kCp1253High; + case 1254: + return kCp1254High; + case 1257: + return kCp1257High; + default: + return nullptr; + } +} + +UINT EffectiveCodePage(UINT cp) { + if (cp == CP_ACP || cp == CP_OEMCP || cp == 2 || cp == 3) + return static_cast(GetAnsiCodePage()); + return cp; +} + +bool DecodeUtf8(const unsigned char* s, size_t n, std::wstring& out, bool strict) { + for (size_t i = 0; i < n;) { + unsigned char c = s[i]; + uint32_t cp; + size_t len; + if (c < 0x80) { + cp = c; + len = 1; + } else if ((c & 0xE0) == 0xC0) { + cp = c & 0x1F; + len = 2; + } else if ((c & 0xF0) == 0xE0) { + cp = c & 0x0F; + len = 3; + } else if ((c & 0xF8) == 0xF0) { + cp = c & 0x07; + len = 4; + } else { + if (strict) + return false; + out.push_back(0xFFFD); + ++i; + continue; + } + bool ok = i + len <= n; + for (size_t k = 1; ok && k < len; ++k) { + if ((s[i + k] & 0xC0) != 0x80) + ok = false; + else + cp = (cp << 6) | (s[i + k] & 0x3F); + } + if (ok && ((len == 2 && cp < 0x80) || (len == 3 && cp < 0x800) || (len == 4 && cp < 0x10000) || cp > 0x10FFFF || + (cp >= 0xD800 && cp <= 0xDFFF))) + ok = false; + if (!ok) { + if (strict) + return false; + out.push_back(0xFFFD); + ++i; + continue; + } + out.push_back(static_cast(cp)); + i += len; + } + return true; +} + +void EncodeUtf8(uint32_t cp, std::string& out) { + if (cp >= 0xD800 && cp <= 0xDFFF) + cp = 0xFFFD; + if (cp < 0x80) { + out.push_back(static_cast(cp)); + } else if (cp < 0x800) { + out.push_back(static_cast(0xC0 | (cp >> 6))); + out.push_back(static_cast(0x80 | (cp & 0x3F))); + } else if (cp < 0x10000) { + out.push_back(static_cast(0xE0 | (cp >> 12))); + out.push_back(static_cast(0x80 | ((cp >> 6) & 0x3F))); + out.push_back(static_cast(0x80 | (cp & 0x3F))); + } else if (cp <= 0x10FFFF) { + out.push_back(static_cast(0xF0 | (cp >> 18))); + out.push_back(static_cast(0x80 | ((cp >> 12) & 0x3F))); + out.push_back(static_cast(0x80 | ((cp >> 6) & 0x3F))); + out.push_back(static_cast(0x80 | (cp & 0x3F))); + } else { + EncodeUtf8(0xFFFD, out); + } +} + +wxString CodePageCharsetName(UINT cp) { return wxString::Format("windows-%u", cp); } + +bool MultiByteToUnicode(UINT cp, const char* src, size_t n, std::wstring& out, bool strict) { + cp = EffectiveCodePage(cp); + if (cp == CP_UTF8) + return DecodeUtf8(reinterpret_cast(src), n, out, strict); + if (cp == static_cast(GetAnsiCodePage())) { + for (size_t i = 0; i < n; ++i) + out.push_back(AnsiToUnicode(static_cast(src[i]))); + return true; + } + if (const uint16_t* table = CodePageTable(cp)) { + for (size_t i = 0; i < n; ++i) { + unsigned char c = static_cast(src[i]); + out.push_back(c < 0x80 ? c : static_cast(table[c - 0x80])); + } + return true; + } + if (cp == 28591 || cp == 20127) { + for (size_t i = 0; i < n; ++i) { + unsigned char c = static_cast(src[i]); + out.push_back(cp == 20127 && c >= 0x80 ? L'?' : c); + } + return true; + } + wxCSConv conv(CodePageCharsetName(cp)); + if (!conv.IsOk()) + return false; + std::string tmp(src, n); + wxWCharBuffer wide = conv.cMB2WC(tmp.c_str(), n, nullptr); + if (!wide.data()) + return false; + out.append(wide.data()); + return true; +} + +bool UnicodeToMultiByte(UINT cp, const wchar_t* src, size_t n, std::string& out, char defChar, bool& usedDef) { + cp = EffectiveCodePage(cp); + if (cp == CP_UTF8) { + for (size_t i = 0; i < n; ++i) + EncodeUtf8(static_cast(src[i]), out); + return true; + } + const uint16_t* table = CodePageTable(cp); + bool current = cp == static_cast(GetAnsiCodePage()); + if (!table && !current && cp != 28591 && cp != 20127) { + wxCSConv conv(CodePageCharsetName(cp)); + if (!conv.IsOk()) + return false; + for (size_t i = 0; i < n; ++i) { + wchar_t one[2] = {src[i], 0}; + wxCharBuffer mb = conv.cWC2MB(one, 1, nullptr); + if (mb.data() && mb.length() > 0) + out.append(mb.data(), mb.length()); + else { + out.push_back(defChar); + usedDef = true; + } + } + return true; + } + for (size_t i = 0; i < n; ++i) { + wchar_t w = src[i]; + if (static_cast(w) < 0x80) { + out.push_back(static_cast(w)); + continue; + } + char ch = 0; + bool ok = false; + if (current) { + ok = UnicodeToAnsi(w, ch); + } else if (table) { + for (int k = 0; k < 128 && !ok; ++k) + if (table[k] == static_cast(w)) { + ch = static_cast(0x80 + k); + ok = true; + } + } else if (cp == 28591 && static_cast(w) <= 0xFF) { + ch = static_cast(w); + ok = true; + } + if (!ok) { + ch = defChar; + usedDef = true; + } + out.push_back(ch); + } + return true; +} + +const char* SystemMessage(DWORD code) { + switch (code) { + case 0: + return "The operation completed successfully."; + case 1: + return "Incorrect function."; + case 2: + return "The system cannot find the file specified."; + case 3: + return "The system cannot find the path specified."; + case 4: + return "The system cannot open the file."; + case 5: + return "Access is denied."; + case 6: + return "The handle is invalid."; + case 8: + return "Not enough storage is available to process this command."; + case 11: + return "An attempt was made to load a program with an incorrect format."; + case 17: + return "The system cannot move the file to a different disk drive."; + case 18: + return "There are no more files."; + case 19: + return "The media is write protected."; + case 31: + return "A device attached to the system is not functioning."; + case 32: + return "The process cannot access the file because it is being used by another process."; + case 50: + return "The request is not supported."; + case 80: + return "The file exists."; + case 87: + return "The parameter is incorrect."; + case 112: + return "There is not enough space on the disk."; + case 122: + return "The data area passed to a system call is too small."; + case 126: + return "The specified module could not be found."; + case 127: + return "The specified procedure could not be found."; + case 131: + return "An attempt was made to move the file pointer before the beginning of the file."; + case 145: + return "The directory is not empty."; + case 183: + return "Cannot create a file when that file already exists."; + case 193: + return "%1 is not a valid Win32 application."; + case 203: + return "The system could not find the environment option that was entered."; + case 206: + return "The filename or extension is too long."; + case 234: + return "More data is available."; + case 259: + return "No more data is available."; + case 288: + return "Attempt to release mutex not owned by caller."; + case 298: + return "Too many posts were made to a semaphore."; + case 1117: + return "The request could not be performed because of an I/O device error."; + case 1223: + return "The operation was canceled by the user."; + default: + return nullptr; + } +} + +std::string ExpandInserts(const char* tmpl, bool argArray, va_list* args) { + std::string typeSpec[100]; + int maxArg = 0; + for (const char* p = tmpl; *p; ++p) { + if (*p == '%' && p[1] >= '1' && p[1] <= '9') { + int n = p[1] - '0'; + const char* q = p + 2; + if (*q >= '0' && *q <= '9') + n = n * 10 + (*q++ - '0'); + if (*q == '!') { + const char* e = strchr(q + 1, '!'); + if (e) + typeSpec[n] = std::string(q + 1, e); + } + maxArg = std::max(maxArg, n); + } else if (*p == '%' && p[1]) { + ++p; + } + } + const char* strArgs[100] = {}; + long long intArgs[100] = {}; + va_list ap; + if (args && !argArray) + va_copy(ap, *args); + for (int i = 1; i <= maxArg && args; ++i) { + bool isString = typeSpec[i].empty() || typeSpec[i].back() == 's'; + bool isWide = typeSpec[i].find("I64") != std::string::npos || typeSpec[i].find("ll") != std::string::npos; + if (argArray) { + DWORD_PTR v = reinterpret_cast(args)[i - 1]; + if (isString) + strArgs[i] = reinterpret_cast(v); + else + intArgs[i] = static_cast(v); + } else if (isString) { + strArgs[i] = va_arg(ap, const char*); + } else if (isWide) { + intArgs[i] = va_arg(ap, long long); + } else { + intArgs[i] = va_arg(ap, int); + } + } + if (args && !argArray) + va_end(ap); + std::string out; + for (const char* p = tmpl; *p; ++p) { + if (*p != '%') { + out += *p; + continue; + } + char c = p[1]; + if (c >= '1' && c <= '9') { + int n = c - '0'; + const char* q = p + 2; + if (*q >= '0' && *q <= '9') + n = n * 10 + (*q++ - '0'); + if (*q == '!') { + const char* e = strchr(q + 1, '!'); + if (e) + q = e + 1; + } + if (typeSpec[n].empty() || typeSpec[n].back() == 's') { + out += strArgs[n] ? strArgs[n] : ""; + } else { + char fmt[64]; + char buf[128]; + std::string spec = typeSpec[n]; + size_t i64 = spec.find("I64"); + if (i64 != std::string::npos) + spec.replace(i64, 3, "ll"); + snprintf(fmt, sizeof fmt, "%%%s", spec.c_str()); + if (spec.find("ll") != std::string::npos) + snprintf(buf, sizeof buf, fmt, intArgs[n]); + else + snprintf(buf, sizeof buf, fmt, static_cast(intArgs[n])); + out += buf; + } + p = q - 1; + } else if (c == 'n') { + out += "\r\n"; + ++p; + } else if (c == '0') { + break; + } else if (c) { + out += c; + ++p; + } + } + return out; +} + +struct Module { + void* dl; + std::string path; + int refs; +}; + +std::mutex& ModuleLock() { + static std::mutex* m = new std::mutex; + return *m; +} + +std::vector& Modules() { + static auto* v = new std::vector; + return *v; +} + +char g_mainModuleTag; + +Module* FindModuleLocked(HMODULE h) { + for (Module* m : Modules()) + if (reinterpret_cast(m) == h) + return m; + return nullptr; +} + +std::mutex& CommandLineLock() { + static std::mutex* m = new std::mutex; + return *m; +} + +std::string& CommandLineStore() { + static auto* s = new std::string; + return *s; +} + +std::string QuoteArg(const std::string& a) { + if (!a.empty() && a.find_first_of(" \t\"") == std::string::npos) + return a; + std::string out = "\""; + for (char c : a) { + if (c == '"') + out += '\\'; + out += c; + } + return out + "\""; +} + +std::vector ProcessArguments() { + std::vector args; +#ifdef __APPLE__ + int argc = *_NSGetArgc(); + char** argv = *_NSGetArgv(); + for (int i = 0; i < argc; ++i) + args.push_back(argv[i] ? argv[i] : ""); +#else + if (FILE* f = ::fopen("/proc/self/cmdline", "rb")) { + std::string cur; + int c; + while ((c = fgetc(f)) != EOF) { + if (c == 0) { + args.push_back(cur); + cur.clear(); + } else { + cur += static_cast(c); + } + } + if (!cur.empty()) + args.push_back(cur); + fclose(f); + } +#endif + return args; +} + +std::string BuildCommandLine() { + std::vector args = ProcessArguments(); + std::string exe = ExecutablePath(); + std::string line = QuoteArg(AppPath(exe.empty() ? (args.empty() ? "" : args[0].c_str()) : exe.c_str())); + for (size_t i = 1; i < args.size(); ++i) { + struct stat st; + const std::string& a = args[i]; + std::string conv = !a.empty() && a[0] == '/' && stat(a.c_str(), &st) == 0 ? AppPath(a.c_str()) : Utf8ToAnsi(a.c_str()); + line += " " + QuoteArg(conv); + } + return line; +} + +const char* FindEnv(const char* name) { + if (!name) + return nullptr; + if (const char* v = getenv(name)) + return v; + size_t len = strlen(name); + for (char** e = environ; e && *e; ++e) + if (strncasecmp(*e, name, len) == 0 && (*e)[len] == '=') + return *e + len + 1; + return nullptr; +} + +WORD LanguageFromCode(const std::string& code) { + static const struct { + const char* code; + WORD primary; + } kLanguages[] = { + {"ar", 0x01}, {"bg", 0x02}, {"ca", 0x03}, {"zh", 0x04}, {"cs", 0x05}, {"da", 0x06}, {"de", 0x07}, + {"el", 0x08}, {"en", 0x09}, {"es", 0x0a}, {"fi", 0x0b}, {"fr", 0x0c}, {"he", 0x0d}, {"hu", 0x0e}, + {"is", 0x0f}, {"it", 0x10}, {"ja", 0x11}, {"ko", 0x12}, {"nl", 0x13}, {"nb", 0x14}, {"no", 0x14}, + {"pl", 0x15}, {"pt", 0x16}, {"ro", 0x18}, {"ru", 0x19}, {"hr", 0x1a}, {"sr", 0x1a}, {"sk", 0x1b}, + {"sq", 0x1c}, {"sv", 0x1d}, {"th", 0x1e}, {"tr", 0x1f}, {"uk", 0x22}, {"sl", 0x24}, {"et", 0x25}, + {"lv", 0x26}, {"lt", 0x27}, + }; + std::string primary = code.substr(0, code.find_first_of("_-")); + for (const auto& l : kLanguages) + if (primary == l.code) { + WORD sub = SUBLANG_DEFAULT; + std::string region = code.size() > primary.size() + 1 ? code.substr(primary.size() + 1, 2) : ""; + if (primary == "en" && region == "GB") + sub = SUBLANG_ENGLISH_UK; + else if (primary == "de" && region == "CH") + sub = SUBLANG_GERMAN_SWISS; + else if (primary == "sr" && code.find("Latn") != std::string::npos) + sub = SUBLANG_SERBIAN_LATIN; + return static_cast(MAKELANGID(l.primary, sub)); + } + return static_cast(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US)); +} + +WORD SystemLangId() { + static const WORD id = []() { + int lang = wxLocale::GetSystemLanguage(); + if (lang == wxLANGUAGE_UNKNOWN || lang == wxLANGUAGE_DEFAULT) + return static_cast(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US)); + wxString name = wxLocale::GetLanguageCanonicalName(lang); + return LanguageFromCode(std::string(name.utf8_str())); + }(); + return id; +} + +thread_local LCID t_threadLocale = 0; +UINT g_errorMode = 0; + +} // namespace + +char AnsiToUpper(char ch) { + wchar_t u = UniUpper(AnsiToUnicode(static_cast(ch))); + char out; + return UnicodeToAnsi(u, out) ? out : ch; +} + +char AnsiToLower(char ch) { + wchar_t u = UniLower(AnsiToUnicode(static_cast(ch))); + char out; + return UnicodeToAnsi(u, out) ? out : ch; +} + +bool AnsiIsAlpha(char ch) { + wchar_t u = AnsiToUnicode(static_cast(ch)); + return UniUpper(u) != u || UniLower(u) != u || UniIsCaselessLetter(u); +} + +bool AnsiIsUpper(char ch) { + wchar_t u = AnsiToUnicode(static_cast(ch)); + return UniLower(u) != u; +} + +bool AnsiIsLower(char ch) { + wchar_t u = AnsiToUnicode(static_cast(ch)); + return UniUpper(u) != u || (UniIsCaselessLetter(u) && u != 0xAA && u != 0xBA); +} + +HGLOBAL GlobalFromData(const void* data, size_t size, UINT flags) { + HGLOBAL h = ::GlobalAlloc(flags | GMEM_ZEROINIT, size); + if (h && data && size) + memcpy(h, data, size); + return h; +} + +bool GlobalIsValid(HGLOBAL h) { return GlobalHeaderOf(h) != nullptr; } + +void SetCommandLineOverride(const char* commandLine) { + std::lock_guard lk(CommandLineLock()); + CommandLineStore() = commandLine ? commandLine : ""; +} + +} // namespace mfcwx + +using namespace mfcwx; + +DWORD GetTickCount(void) { return static_cast(GetTickCount64()); } + +ULONGLONG GetTickCount64(void) { + return static_cast( + std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count()); +} + +BOOL QueryPerformanceCounter(LARGE_INTEGER* c) { + if (!c) + return FALSE; + c->QuadPart = std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count() / 100; + return TRUE; +} + +BOOL QueryPerformanceFrequency(LARGE_INTEGER* f) { + if (!f) + return FALSE; + f->QuadPart = 10000000; + return TRUE; +} + +static void FillSystemTime(const struct tm& tm, long nsec, LPSYSTEMTIME st) { + st->wYear = static_cast(tm.tm_year + 1900); + st->wMonth = static_cast(tm.tm_mon + 1); + st->wDayOfWeek = static_cast(tm.tm_wday); + st->wDay = static_cast(tm.tm_mday); + st->wHour = static_cast(tm.tm_hour); + st->wMinute = static_cast(tm.tm_min); + st->wSecond = static_cast(tm.tm_sec > 59 ? 59 : tm.tm_sec); + st->wMilliseconds = static_cast(nsec / 1000000); +} + +void GetSystemTime(LPSYSTEMTIME st) { + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + struct tm tm; + gmtime_r(&ts.tv_sec, &tm); + FillSystemTime(tm, ts.tv_nsec, st); +} + +void GetLocalTime(LPSYSTEMTIME st) { + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + struct tm tm; + localtime_r(&ts.tv_sec, &tm); + FillSystemTime(tm, ts.tv_nsec, st); +} + +void GetSystemTimeAsFileTime(LPFILETIME ft) { + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + *ft = MakeFileTime(ts.tv_sec, ts.tv_nsec); +} + +BOOL SystemTimeToFileTime(const SYSTEMTIME* st, LPFILETIME ft) { + if (!st || !ft || st->wMonth < 1 || st->wMonth > 12 || st->wDay < 1 || st->wDay > 31 || st->wHour > 23 || + st->wMinute > 59 || st->wSecond > 59 || st->wMilliseconds > 999 || st->wYear < 1601 || st->wYear > 30827) { + SetLastError(87); + return FALSE; + } + struct tm tm; + memset(&tm, 0, sizeof tm); + tm.tm_year = st->wYear - 1900; + tm.tm_mon = st->wMonth - 1; + tm.tm_mday = st->wDay; + tm.tm_hour = st->wHour; + tm.tm_min = st->wMinute; + tm.tm_sec = st->wSecond; + time_t t = timegm(&tm); + *ft = MakeFileTime(t, static_cast(st->wMilliseconds) * 1000000L); + return TRUE; +} + +BOOL FileTimeToSystemTime(const FILETIME* ft, LPSYSTEMTIME st) { + if (!ft || !st) + return FALSE; + LONGLONG ticks = FileTimeTicks(*ft) - kFileTimeUnixEpoch; + LONGLONG sec = ticks >= 0 ? ticks / 10000000LL : -((-ticks + 9999999LL) / 10000000LL); + LONGLONG rem = ticks - sec * 10000000LL; + time_t t = static_cast(sec); + struct tm tm; + if (!gmtime_r(&t, &tm)) + return FALSE; + FillSystemTime(tm, static_cast(rem * 100), st); + return TRUE; +} + +BOOL FileTimeToLocalFileTime(const FILETIME* ft, LPFILETIME lft) { + if (!ft || !lft) + return FALSE; + time_t t = FileTimeToUnix(*ft); + struct tm tm; + localtime_r(&t, &tm); + ULONGLONG v = static_cast(FileTimeTicks(*ft) + static_cast(tm.tm_gmtoff) * 10000000LL); + lft->dwLowDateTime = static_cast(v); + lft->dwHighDateTime = static_cast(v >> 32); + return TRUE; +} + +void GetSystemInfo(LPSYSTEM_INFO si) { + memset(si, 0, sizeof *si); +#if defined(__x86_64__) + si->wProcessorArchitecture = 9; + si->dwProcessorType = 8664; +#elif defined(__aarch64__) + si->wProcessorArchitecture = 12; +#elif defined(__i386__) + si->wProcessorArchitecture = 0; + si->dwProcessorType = 586; +#endif + long page = sysconf(_SC_PAGESIZE); + si->dwPageSize = page > 0 ? static_cast(page) : 4096; + si->lpMinimumApplicationAddress = reinterpret_cast(0x10000); + si->lpMaximumApplicationAddress = reinterpret_cast(static_cast(0x7FFFFFFEFFFFULL)); + unsigned n = std::thread::hardware_concurrency(); + si->dwNumberOfProcessors = n ? n : 1; + si->dwActiveProcessorMask = si->dwNumberOfProcessors >= 64 ? ~static_cast(0) + : (static_cast(1) << si->dwNumberOfProcessors) - 1; + si->dwAllocationGranularity = 65536; +} + +BOOL GetVersionEx(LPOSVERSIONINFO vi) { + if (!vi) + return FALSE; + vi->dwMajorVersion = 5; + vi->dwMinorVersion = 1; + vi->dwBuildNumber = 2600; + vi->dwPlatformId = 2; + strcpy(vi->szCSDVersion, "Service Pack 3"); + return TRUE; +} + +DWORD GetVersion(void) { return 5u | (1u << 8) | (2600u << 16); } + +void GlobalMemoryStatus(LPMEMORYSTATUS ms) { + ULONGLONG total = 0, avail = 0; +#ifdef __APPLE__ + uint64_t mem = 0; + size_t len = sizeof mem; + if (sysctlbyname("hw.memsize", &mem, &len, nullptr, 0) == 0) + total = mem; + vm_statistics64_data_t vm; + mach_msg_type_number_t count = HOST_VM_INFO64_COUNT; + if (host_statistics64(mach_host_self(), HOST_VM_INFO64, reinterpret_cast(&vm), &count) == KERN_SUCCESS) + avail = (static_cast(vm.free_count) + vm.inactive_count) * static_cast(sysconf(_SC_PAGESIZE)); +#else + long pages = sysconf(_SC_PHYS_PAGES); + long availPages = sysconf(_SC_AVPHYS_PAGES); + long page = sysconf(_SC_PAGESIZE); + if (pages > 0 && page > 0) + total = static_cast(pages) * static_cast(page); + if (availPages > 0 && page > 0) + avail = static_cast(availPages) * static_cast(page); +#endif + if (!total) + total = 4ULL << 30; + if (!avail || avail > total) + avail = total / 2; + memset(ms, 0, sizeof *ms); + ms->dwLength = sizeof *ms; + ms->dwMemoryLoad = static_cast(100 - avail * 100 / total); + ms->dwTotalPhys = static_cast(total); + ms->dwAvailPhys = static_cast(avail); + ms->dwTotalPageFile = static_cast(total * 2); + ms->dwAvailPageFile = static_cast(avail * 2); + ms->dwTotalVirtual = static_cast(0x7FFFFFFEFFFFULL); + ms->dwAvailVirtual = static_cast(0x7FFFFFFEFFFFULL); +} + +DWORD FormatMessageA(DWORD flags, LPCVOID source, DWORD msgId, DWORD, LPSTR buffer, DWORD size, va_list* args) { + std::string tmpl; + if (flags & FORMAT_MESSAGE_FROM_STRING) { + tmpl = source ? static_cast(source) : ""; + } else { + const char* known = SystemMessage(msgId); + if (known) { + tmpl = known; + } else { + const char* text = strerror(static_cast(msgId)); + char buf[64]; + if (!text || strncmp(text, "Unknown error", 13) == 0) { + snprintf(buf, sizeof buf, "Unknown error 0x%08X.", static_cast(msgId)); + text = buf; + } + tmpl = text; + if (!tmpl.empty() && tmpl.back() != '.') + tmpl += '.'; + } + tmpl += "\r\n"; + } + std::string text = (flags & FORMAT_MESSAGE_IGNORE_INSERTS) ? tmpl : ExpandInserts(tmpl.c_str(), (flags & 0x2000) != 0, args); + if (flags & FORMAT_MESSAGE_ALLOCATE_BUFFER) { + if (!buffer) + return 0; + size_t bytes = std::max(text.size() + 1, size); + auto* out = static_cast(LocalAlloc(GMEM_ZEROINIT, bytes)); + if (!out) { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return 0; + } + memcpy(out, text.c_str(), text.size() + 1); + *reinterpret_cast(buffer) = out; + return static_cast(text.size()); + } + if (!buffer || size < text.size() + 1) { + SetLastError(122); + return 0; + } + memcpy(buffer, text.c_str(), text.size() + 1); + return static_cast(text.size()); +} + +HLOCAL LocalAlloc(UINT flags, SIZE_T bytes) { return GlobalAlloc(flags, bytes); } + +HLOCAL LocalFree(HLOCAL mem) { return GlobalFree(mem); } + +HGLOBAL GlobalAlloc(UINT flags, SIZE_T bytes) { + size_t total = sizeof(GlobalHeader) + bytes; + void* block = (flags & GMEM_ZEROINIT) ? calloc(1, total) : malloc(total); + if (!block) { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return nullptr; + } + auto* header = static_cast(block); + header->magic = kGlobalMagic; + header->flags = flags; + header->size = bytes; + header->lockCount = 0; + header->reserved = 0; + return header + 1; +} + +HGLOBAL GlobalFree(HGLOBAL mem) { + if (!mem) + return nullptr; + GlobalHeader* header = GlobalHeaderOf(mem); + if (!header) { + SetLastError(ERROR_INVALID_HANDLE); + return mem; + } + header->magic = 0; + free(header); + return nullptr; +} + +LPVOID GlobalLock(HGLOBAL mem) { + GlobalHeader* header = GlobalHeaderOf(mem); + if (!header) { + SetLastError(ERROR_INVALID_HANDLE); + return nullptr; + } + ++header->lockCount; + return mem; +} + +BOOL GlobalUnlock(HGLOBAL mem) { + GlobalHeader* header = GlobalHeaderOf(mem); + if (!header) { + SetLastError(ERROR_INVALID_HANDLE); + return FALSE; + } + if (header->lockCount > 0) + --header->lockCount; + SetLastError(0); + return header->lockCount > 0; +} + +SIZE_T GlobalSize(HGLOBAL mem) { + GlobalHeader* header = GlobalHeaderOf(mem); + if (!header) { + SetLastError(ERROR_INVALID_HANDLE); + return 0; + } + return header->size; +} + +HGLOBAL GlobalReAlloc(HGLOBAL mem, SIZE_T bytes, UINT flags) { + GlobalHeader* header = GlobalHeaderOf(mem); + if (!header) { + SetLastError(ERROR_INVALID_HANDLE); + return nullptr; + } + if (flags & 0x80) { + header->flags = (header->flags & ~GMEM_MOVEABLE) | (flags & GMEM_MOVEABLE); + return mem; + } + size_t old = header->size; + auto* fresh = static_cast(realloc(header, sizeof(GlobalHeader) + bytes)); + if (!fresh) { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return nullptr; + } + if ((flags & GMEM_ZEROINIT) && bytes > old) + memset(reinterpret_cast(fresh + 1) + old, 0, bytes - old); + fresh->size = bytes; + return fresh + 1; +} + +DWORD GetEnvironmentVariableA(LPCSTR name, LPSTR buffer, DWORD size) { + const char* v = FindEnv(name); + if (!v) { + SetLastError(203); + return 0; + } + std::string ansi = Utf8ToAnsi(v); + if (!buffer || size <= ansi.size()) + return static_cast(ansi.size() + 1); + memcpy(buffer, ansi.c_str(), ansi.size() + 1); + return static_cast(ansi.size()); +} + +BOOL SetEnvironmentVariableA(LPCSTR name, LPCSTR value) { + if (!name || !*name || strchr(name, '=')) { + SetLastError(87); + return FALSE; + } + int rc = value ? setenv(name, AnsiToUtf8(value).c_str(), 1) : unsetenv(name); + if (rc != 0) { + SetLastErrorFromErrno(errno); + return FALSE; + } + return TRUE; +} + +DWORD ExpandEnvironmentStringsA(LPCSTR src, LPSTR dst, DWORD size) { + std::string out; + for (const char* p = src ? src : ""; *p;) { + if (*p == '%') { + const char* end = strchr(p + 1, '%'); + if (end && end > p + 1) { + std::string name(p + 1, end); + if (const char* v = FindEnv(name.c_str())) { + out += Utf8ToAnsi(v); + p = end + 1; + continue; + } + } + } + out += *p++; + } + if (dst && size > out.size()) + memcpy(dst, out.c_str(), out.size() + 1); + return static_cast(out.size() + 1); +} + +LPSTR GetCommandLineA(void) { + std::lock_guard lk(CommandLineLock()); + std::string& line = CommandLineStore(); + if (line.empty()) + line = BuildCommandLine(); + return &line[0]; +} + +UINT SetErrorMode(UINT mode) { + UINT previous = g_errorMode; + g_errorMode = mode; + return previous; +} + +void OutputDebugStringA(LPCSTR s) { + if (s) + fputs(s, stderr); +} + +void DebugBreak(void) { fprintf(stderr, "mfcwx: DebugBreak()\n"); } + +BOOL Beep(DWORD, DWORD) { + wxBell(); + return TRUE; +} + +BOOL MessageBeep(UINT) { + wxBell(); + return TRUE; +} + +int MulDiv(int number, int numerator, int denominator) { + if (denominator == 0) + return -1; + long long product = static_cast(number) * numerator; + long long half = (denominator < 0 ? -static_cast(denominator) : denominator) / 2; + bool negative = (product < 0) != (denominator < 0); + long long a = product < 0 ? -product : product; + long long d = denominator < 0 ? -static_cast(denominator) : denominator; + long long q = (a + half) / d; + if (q > INT_MAX) + return -1; + return static_cast(negative ? -q : q); +} + +BOOL IsBadReadPtr(const void* p, UINT_PTR cb) { return !p && cb ? TRUE : FALSE; } + +BOOL IsBadWritePtr(LPVOID p, UINT_PTR cb) { return !p && cb ? TRUE : FALSE; } + +DWORD GetUserDefaultLangID(void) { return SystemLangId(); } + +LCID GetUserDefaultLCID(void) { return MAKELCID(SystemLangId(), 0); } + +LCID GetSystemDefaultLCID(void) { return MAKELCID(SystemLangId(), 0); } + +LCID GetThreadLocale(void) { return t_threadLocale ? t_threadLocale : GetUserDefaultLCID(); } + +BOOL SetThreadLocale(LCID lcid) { + t_threadLocale = lcid; + return TRUE; +} + +UINT GetACP(void) { return static_cast(GetAnsiCodePage()); } + +UINT GetOEMCP(void) { + switch (GetAnsiCodePage()) { + case 1250: + return 852; + case 1251: + return 866; + case 1253: + return 737; + case 1254: + return 857; + case 1257: + return 775; + default: + return 850; + } +} + +int MultiByteToWideChar(UINT cp, DWORD flags, LPCSTR src, int srcLen, LPWSTR dst, int dstLen) { + if (!src || srcLen == 0 || dstLen < 0 || (dstLen > 0 && !dst)) { + SetLastError(87); + return 0; + } + size_t n = srcLen < 0 ? strlen(src) + 1 : static_cast(srcLen); + std::wstring wide; + if (!MultiByteToUnicode(cp, src, n, wide, (flags & 0x8) != 0)) { + SetLastError(EffectiveCodePage(cp) == CP_UTF8 ? 1113 : 87); + return 0; + } + if (dstLen == 0) + return static_cast(wide.size()); + if (wide.size() > static_cast(dstLen)) { + SetLastError(122); + return 0; + } + std::copy(wide.begin(), wide.end(), dst); + return static_cast(wide.size()); +} + +int WideCharToMultiByte(UINT cp, DWORD, LPCWSTR src, int srcLen, LPSTR dst, int dstLen, LPCSTR defChar, LPBOOL usedDef) { + if (!src || srcLen == 0 || dstLen < 0 || (dstLen > 0 && !dst)) { + SetLastError(87); + return 0; + } + size_t n = srcLen < 0 ? wcslen(src) + 1 : static_cast(srcLen); + std::string out; + bool used = false; + if (!UnicodeToMultiByte(cp, src, n, out, defChar ? *defChar : '?', used)) { + SetLastError(87); + return 0; + } + if (usedDef) + *usedDef = used ? TRUE : FALSE; + if (dstLen == 0) + return static_cast(out.size()); + if (out.size() > static_cast(dstLen)) { + SetLastError(122); + return 0; + } + memcpy(dst, out.data(), out.size()); + return static_cast(out.size()); +} + +BOOL GetUserNameA(LPSTR buffer, LPDWORD size) { + std::string name; + if (struct passwd* pw = getpwuid(getuid())) + name = pw->pw_name; + if (name.empty()) { + const char* env = getenv("USER"); + name = env ? env : "user"; + } + name = Utf8ToAnsi(name.c_str()); + if (!size) + return FALSE; + if (!buffer || *size < name.size() + 1) { + *size = static_cast(name.size() + 1); + SetLastError(122); + return FALSE; + } + memcpy(buffer, name.c_str(), name.size() + 1); + *size = static_cast(name.size() + 1); + return TRUE; +} + +BOOL GetComputerNameA(LPSTR buffer, LPDWORD size) { + char host[256] = ""; + gethostname(host, sizeof host - 1); + std::string name(host); + name = name.substr(0, name.find('.')); + if (name.empty()) + name = "localhost"; + if (!size) + return FALSE; + if (!buffer || *size < name.size() + 1) { + *size = static_cast(name.size() + 1); + SetLastError(111); + return FALSE; + } + memcpy(buffer, name.c_str(), name.size() + 1); + *size = static_cast(name.size()); + return TRUE; +} + +HMODULE LoadLibraryA(LPCSTR name) { return LoadLibraryExA(name, nullptr, 0); } + +HMODULE LoadLibraryExA(LPCSTR name, HANDLE, DWORD) { + if (!name || !*name) { + SetLastError(87); + return nullptr; + } + std::string path = FsPath(name); + void* dl = dlopen(path.c_str(), RTLD_NOW | RTLD_LOCAL); + if (!dl) { + SetLastError(126); + return nullptr; + } + std::lock_guard lk(ModuleLock()); + for (Module* m : Modules()) + if (m->dl == dl) { + ++m->refs; + dlclose(dl); + return reinterpret_cast(m); + } + auto* m = new Module{dl, path, 1}; + Modules().push_back(m); + return reinterpret_cast(m); +} + +BOOL FreeLibrary(HMODULE mod) { + std::lock_guard lk(ModuleLock()); + Module* m = FindModuleLocked(mod); + if (!m) { + SetLastError(ERROR_INVALID_HANDLE); + return FALSE; + } + if (--m->refs == 0) { + dlclose(m->dl); + Modules().erase(std::find(Modules().begin(), Modules().end(), m)); + delete m; + } + return TRUE; +} + +FARPROC GetProcAddress(HMODULE mod, LPCSTR name) { + if (!name || IS_INTRESOURCE(name)) { + SetLastError(127); + return nullptr; + } + void* dl; + { + std::lock_guard lk(ModuleLock()); + Module* m = FindModuleLocked(mod); + dl = m ? m->dl : RTLD_DEFAULT; + } + void* sym = dlsym(dl, name); + if (!sym) { + SetLastError(127); + return nullptr; + } + return reinterpret_cast(sym); +} + +HMODULE GetModuleHandleA(LPCSTR name) { + if (!name) + return reinterpret_cast(&g_mainModuleTag); + std::string wanted = name; + size_t slash = wanted.find_last_of("\\/"); + if (slash != std::string::npos) + wanted = wanted.substr(slash + 1); + std::lock_guard lk(ModuleLock()); + for (Module* m : Modules()) { + size_t s = m->path.rfind('/'); + std::string base = m->path.substr(s == std::string::npos ? 0 : s + 1); + if (strcasecmp(base.c_str(), wanted.c_str()) == 0) + return reinterpret_cast(m); + } + std::string exe = ExecutablePath(); + size_t s = exe.rfind('/'); + std::string exeBase = exe.substr(s == std::string::npos ? 0 : s + 1); + if (strcasecmp(exeBase.c_str(), wanted.c_str()) == 0 || + strcasecmp((exeBase + ".exe").c_str(), wanted.c_str()) == 0) + return reinterpret_cast(&g_mainModuleTag); + SetLastError(126); + return nullptr; +} + +DWORD GetModuleFileNameA(HMODULE mod, LPSTR buffer, DWORD size) { + std::string path; + { + std::lock_guard lk(ModuleLock()); + if (Module* m = FindModuleLocked(mod)) + path = m->path; + } + if (path.empty()) + path = ExecutablePath(); + std::string app = AppPath(path.c_str()); + if (!buffer || size == 0) { + SetLastError(122); + return 0; + } + if (app.size() + 1 > size) { + memcpy(buffer, app.c_str(), size - 1); + buffer[size - 1] = 0; + SetLastError(122); + return size; + } + memcpy(buffer, app.c_str(), app.size() + 1); + SetLastError(0); + return static_cast(app.size()); +} + +HINSTANCE ShellExecuteA(HWND, LPCSTR op, LPCSTR file, LPCSTR params, LPCSTR dir, int) { + auto result = [](int v) { return reinterpret_cast(static_cast(v)); }; + if (!file || !*file) + return result(2); + std::string verb = op ? op : ""; + std::transform(verb.begin(), verb.end(), verb.begin(), [](char c) { return static_cast(tolower(c)); }); + if (!verb.empty() && verb != "open" && verb != "explore" && verb != "edit") + return result(31); + std::string f = file; + bool url = f.find("://") != std::string::npos || strncasecmp(f.c_str(), "mailto:", 7) == 0 || + strncasecmp(f.c_str(), "www.", 4) == 0; + if (url) { + std::string target = strncasecmp(f.c_str(), "www.", 4) == 0 ? "http://" + f : f; + return wxLaunchDefaultBrowser(wxString::FromUTF8(AnsiToUtf8(target.c_str()).c_str())) ? result(42) : result(31); + } + bool absolute = f[0] == '\\' || f[0] == '/' || (f.size() > 1 && f[1] == ':'); + bool hasSeparator = f.find_first_of("\\/") != std::string::npos; + std::string native; + struct stat st; + if (!absolute && dir && *dir) { + std::string inDir = FsPath((std::string(dir) + "\\" + f).c_str()); + if (stat(inDir.c_str(), &st) == 0) + native = inDir; + } + if (native.empty()) { + std::string direct = FsPath(f.c_str()); + if (stat(direct.c_str(), &st) == 0) + native = direct; + } + if (native.empty() && f.size() > 4 && strcasecmp(f.c_str() + f.size() - 4, ".exe") == 0) { + std::string stripped = FsPath(f.substr(0, f.size() - 4).c_str()); + if (stat(stripped.c_str(), &st) == 0) + native = stripped; + } + bool runProgram = false; + if (native.empty()) { + if (hasSeparator) + return result(2); + runProgram = true; + } else if (S_ISREG(st.st_mode) && access(native.c_str(), X_OK) == 0) { + size_t slash = native.rfind('/'); + size_t dot = native.rfind('.'); + std::string ext = dot != std::string::npos && (slash == std::string::npos || dot > slash) ? native.substr(dot) : ""; + std::transform(ext.begin(), ext.end(), ext.begin(), [](char c) { return static_cast(tolower(c)); }); + runProgram = ext.empty() || ext == ".sh" || ext == ".bin" || ext == ".run" || ext == ".command" || + ext == ".py" || ext == ".pl"; + } + if (runProgram) { + std::string cmd = "\"" + (native.empty() ? f : AppPath(native.c_str())) + "\""; + if (params && *params) + cmd += std::string(" ") + params; + STARTUPINFO si; + memset(&si, 0, sizeof si); + si.cb = sizeof si; + PROCESS_INFORMATION pi; + if (!CreateProcessA(nullptr, &cmd[0], nullptr, nullptr, FALSE, 0, nullptr, dir, &si, &pi)) + return result(GetLastError() == ERROR_PATH_NOT_FOUND ? 3 : 2); + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + return result(42); + } + return wxLaunchDefaultApplication(wxString::FromUTF8(native.c_str())) ? result(42) : result(31); +} + +int lstrlenA(LPCSTR s) { return s ? static_cast(strlen(s)) : 0; } + +LPSTR lstrcpyA(LPSTR d, LPCSTR s) { + if (!d) + return nullptr; + return strcpy(d, s ? s : ""); +} + +LPSTR lstrcpynA(LPSTR d, LPCSTR s, int n) { + if (!d || n <= 0) + return d; + int i = 0; + for (; s && i < n - 1 && s[i]; ++i) + d[i] = s[i]; + d[i] = 0; + return d; +} + +LPSTR lstrcatA(LPSTR d, LPCSTR s) { + if (!d) + return nullptr; + return strcat(d, s ? s : ""); +} + +static int CompareAnsi(LPCSTR a, LPCSTR b, bool caseSensitive) { + a = a ? a : ""; + b = b ? b : ""; + int tie = 0; + for (;; ++a, ++b) { + unsigned char la = static_cast(AnsiToLower(*a)); + unsigned char lb = static_cast(AnsiToLower(*b)); + wchar_t ua = AnsiToUnicode(la); + wchar_t ub = AnsiToUnicode(lb); + if (ua != ub) + return ua < ub ? -1 : 1; + if (!*a) + break; + if (caseSensitive && !tie && *a != *b) + tie = AnsiIsLower(*a) ? -1 : 1; + } + return tie; +} + +int lstrcmpA(LPCSTR a, LPCSTR b) { return CompareAnsi(a, b, true); } + +int lstrcmpiA(LPCSTR a, LPCSTR b) { return CompareAnsi(a, b, false); } + +int wvsprintfA(LPSTR buf, LPCSTR fmt, va_list args) { + char translated[1024]; + fmt = AfxTranslateFormat(fmt, translated, sizeof translated); + int n = vsnprintf(buf, 1025, fmt ? fmt : "", args); + return n > 1024 ? 1024 : n; +} + +int wsprintfA(LPSTR buf, LPCSTR fmt, ...) { + va_list args; + va_start(args, fmt); + int n = wvsprintfA(buf, fmt, args); + va_end(args); + return n; +} + +LPSTR CharUpperA(LPSTR s) { + if (IS_INTRESOURCE(s)) + return reinterpret_cast(static_cast(static_cast( + AnsiToUpper(static_cast(reinterpret_cast(s)))))); + for (char* p = s; *p; ++p) + *p = AnsiToUpper(*p); + return s; +} + +LPSTR CharLowerA(LPSTR s) { + if (IS_INTRESOURCE(s)) + return reinterpret_cast(static_cast(static_cast( + AnsiToLower(static_cast(reinterpret_cast(s)))))); + for (char* p = s; *p; ++p) + *p = AnsiToLower(*p); + return s; +} + +DWORD CharUpperBuffA(LPSTR s, DWORD len) { + for (DWORD i = 0; s && i < len; ++i) + s[i] = AnsiToUpper(s[i]); + return len; +} + +DWORD CharLowerBuffA(LPSTR s, DWORD len) { + for (DWORD i = 0; s && i < len; ++i) + s[i] = AnsiToLower(s[i]); + return len; +} + +LPSTR CharNextA(LPCSTR s) { return const_cast(s && *s ? s + 1 : s); } + +LPSTR CharPrevA(LPCSTR start, LPCSTR s) { return const_cast(s > start ? s - 1 : start); } + +BOOL IsCharAlphaA(CHAR c) { return AnsiIsAlpha(c) ? TRUE : FALSE; } + +BOOL IsCharAlphaNumericA(CHAR c) { return (AnsiIsAlpha(c) || (c >= '0' && c <= '9')) ? TRUE : FALSE; } + +BOOL IsCharUpperA(CHAR c) { return AnsiIsUpper(c) ? TRUE : FALSE; } + +BOOL IsCharLowerA(CHAR c) { return AnsiIsLower(c) ? TRUE : FALSE; } + +BOOL IsDBCSLeadByte(BYTE) { return FALSE; } + +BOOL OemToCharA(LPCSTR src, LPSTR dst) { + if (!src || !dst) + return FALSE; + if (src != dst) + memmove(dst, src, strlen(src) + 1); + return TRUE; +} + +BOOL CharToOemA(LPCSTR src, LPSTR dst) { return OemToCharA(src, dst); } diff --git a/port/mfcwx/tests/runtime_stubs.cpp b/port/mfcwx/tests/runtime_stubs.cpp new file mode 100644 index 000000000..34cfe52a0 --- /dev/null +++ b/port/mfcwx/tests/runtime_stubs.cpp @@ -0,0 +1,38 @@ +// Stand-ins for GUI-side symbols so the runtime tests link without the window/app parts. + +#include "afxwin.h" + +#include +#include + +CRuntimeClass CCmdTarget::classCCmdTarget = {"CCmdTarget", sizeof(CCmdTarget), 0xFFFF, nullptr, + &CObject::GetThisClass, nullptr}; +static AFX_CLASSINIT _init_CCmdTarget(&CCmdTarget::classCCmdTarget); +CRuntimeClass* CCmdTarget::GetThisClass() { return &CCmdTarget::classCCmdTarget; } +CRuntimeClass* CCmdTarget::GetRuntimeClass() const { return &CCmdTarget::classCCmdTarget; } +BOOL CCmdTarget::OnCmdMsg(UINT, int, void*, AFX_CMDHANDLERINFO*) { return FALSE; } +const AFX_MSGMAP* CCmdTarget::GetThisMessageMap() { return nullptr; } +const AFX_MSGMAP* CCmdTarget::GetMessageMap() const { return nullptr; } + +CWinApp* AFXAPI AfxGetApp() { return nullptr; } + +int AFXAPI AfxMessageBox(const char* lpszText, UINT, UINT) { + fprintf(stderr, "AfxMessageBox: %s\n", lpszText ? lpszText : ""); + return IDOK; +} + +int AFXAPI AfxMessageBox(UINT nIDPrompt, UINT, UINT) { + fprintf(stderr, "AfxMessageBox: #%u\n", nIDPrompt); + return IDOK; +} + +namespace mfcwx { + +bool LoadResourceString(UINT, std::string&) { return false; } + +std::string GetDataDirectory() { + char buf[4096]; + return getcwd(buf, sizeof buf) ? std::string(buf) : std::string("/"); +} + +} // namespace mfcwx diff --git a/port/mfcwx/tests/runtime_tests.cpp b/port/mfcwx/tests/runtime_tests.cpp new file mode 100644 index 000000000..48ee76509 --- /dev/null +++ b/port/mfcwx/tests/runtime_tests.cpp @@ -0,0 +1,1046 @@ +// Tests of the non-GUI runtime of mfcwx (paths, files, archives, profiles, registry, threads, +// processes, CRT and Win32 helpers). + +#include + +#include "afxmt.h" +#include "afxole.h" +#include "afxwin.h" + +#include +#include +#include + +#include +#include +#include + +namespace { + +int g_checks = 0; +int g_failures = 0; + +#define CHECK(cond) \ + do { \ + ++g_checks; \ + if (!(cond)) { \ + ++g_failures; \ + fprintf(stderr, "FAILED %s:%d: %s\n", __FILE__, __LINE__, #cond); \ + } \ + } while (0) + +#define CHECK_EQ_STR(a, b) \ + do { \ + ++g_checks; \ + std::string va_ = (a), vb_ = (b); \ + if (va_ != vb_) { \ + ++g_failures; \ + fprintf(stderr, "FAILED %s:%d: %s == %s (\"%s\" vs \"%s\")\n", __FILE__, __LINE__, #a, #b, \ + va_.c_str(), vb_.c_str()); \ + } \ + } while (0) + +std::string g_root; // native scratch directory +std::string g_appRoot; // the same in application form + +std::string Native(const std::string& name) { return g_root + "/" + name; } +std::string App(const std::string& name) { return g_appRoot + "\\" + name; } + +void WriteRaw(const std::string& native, const std::string& data) { + FILE* f = ::mfcwx_fopen(native.c_str(), "wb"); + if (f) { + fwrite(data.data(), 1, data.size(), f); + fclose(f); + } +} + +std::string ReadRaw(const std::string& native) { + std::string data; + FILE* f = ::mfcwx_fopen(native.c_str(), "rb"); + if (!f) + return data; + char buf[4096]; + size_t n; + while ((n = fread(buf, 1, sizeof buf, f)) > 0) + data.append(buf, n); + fclose(f); + return data; +} + +void TestPaths() { + CHECK_EQ_STR(mfcwx::NativePath("C:\\dir\\sub\\file.txt"), "/dir/sub/file.txt"); + CHECK_EQ_STR(mfcwx::NativePath("rel\\file.txt"), "rel/file.txt"); + CHECK_EQ_STR(mfcwx::AppPath("/Users/x/a.txt"), "\\Users\\x\\a.txt"); + mfcwx::SetAnsiCodePage(1250); + std::string utf8 = "/tmp/za\xC5\xBC\xC3\xB3\xC5\x82\xC4\x87"; + std::string app = mfcwx::AppPath(utf8.c_str()); + CHECK_EQ_STR(app, "\\tmp\\za\xBF\xF3\xB3\xE6"); + CHECK_EQ_STR(mfcwx::NativePath(app.c_str()), utf8); + mfcwx::SetAnsiCodePage(1252); + + char drive[_MAX_DRIVE], dir[_MAX_DIR], fname[_MAX_FNAME], ext[_MAX_EXT]; + _splitpath("C:\\dir\\sub\\file.tar.gz", drive, dir, fname, ext); + CHECK_EQ_STR(drive, "C:"); + CHECK_EQ_STR(dir, "\\dir\\sub\\"); + CHECK_EQ_STR(fname, "file.tar"); + CHECK_EQ_STR(ext, ".gz"); + _splitpath("\\Users\\x\\noext", drive, dir, fname, ext); + CHECK_EQ_STR(drive, ""); + CHECK_EQ_STR(dir, "\\Users\\x\\"); + CHECK_EQ_STR(fname, "noext"); + CHECK_EQ_STR(ext, ""); + _splitpath("plain.txt", drive, dir, fname, ext); + CHECK_EQ_STR(dir, ""); + CHECK_EQ_STR(fname, "plain"); + char path[_MAX_PATH]; + _makepath(path, "", "\\Users\\x", "name", "txt"); + CHECK_EQ_STR(path, "\\Users\\x\\name.txt"); + + char cwd[1024]; + CHECK(_getcwd(cwd, sizeof cwd) != nullptr); + CHECK(cwd[0] == '\\'); + CHECK(strchr(cwd, '/') == nullptr); + DWORD n = GetCurrentDirectory(sizeof cwd, cwd); + CHECK(n == strlen(cwd)); + CHECK(GetCurrentDirectory(2, cwd) > 2); + + char full[1024]; + char* filePart = nullptr; + GetFullPathName("a\\..\\b\\c.txt", sizeof full, full, &filePart); + char expected[1024]; + _getcwd(expected, sizeof expected); + CHECK_EQ_STR(full, std::string(expected) + "\\b\\c.txt"); + CHECK(filePart && strcmp(filePart, "c.txt") == 0); + char* fp = _fullpath(nullptr, "x\\y", 0); + CHECK_EQ_STR(fp, std::string(expected) + "\\x\\y"); + free(fp); + + char temp[1024]; + DWORD t = GetTempPath(sizeof temp, temp); + CHECK(t > 0 && temp[t - 1] == '\\'); + struct stat st; + CHECK(stat(mfcwx::NativePath(temp).c_str(), &st) == 0 && S_ISDIR(st.st_mode)); + + mkdir(Native("CaseDir").c_str(), 0755); + WriteRaw(Native("CaseDir/MixedCase.TXT"), "x"); + CHECK(GetFileAttributes(App("casedir\\mixedcase.txt").c_str()) != INVALID_FILE_ATTRIBUTES); + CHECK(_access(App("CASEDIR\\MIXEDCASE.txt").c_str(), 0) == 0); + CHECK((GetFileAttributes(App("CaseDir").c_str()) & FILE_ATTRIBUTE_DIRECTORY) != 0); + + char exe[1024]; + DWORD len = GetModuleFileName(nullptr, exe, sizeof exe); + CHECK(len > 0 && exe[0] == '\\'); + CHECK(strstr(exe, "mfcwx_runtime_tests") != nullptr); + CHECK(strncmp(GetCommandLine(), "\\", 1) == 0 || GetCommandLine()[0] == '"'); +} + +void TestFiles() { + std::string name = App("data.bin"); + { + CFile f; + CFileException e; + CHECK(f.Open(name.c_str(), CFile::modeCreate | CFile::modeWrite, &e)); + const char data[] = "0123456789"; + f.Write(data, 10); + CHECK(f.GetLength() == 10); + CHECK(f.GetFileName() == "data.bin"); + CHECK(f.GetFileTitle() == "data"); + CHECK(f.GetFilePath() == name.c_str()); + f.Close(); + } + { + CFile f(name.c_str(), CFile::modeRead); + char buf[16] = {}; + f.Seek(3, CFile::begin); + CHECK(f.Read(buf, 4) == 4); + CHECK_EQ_STR(buf, "3456"); + CHECK(f.GetPosition() == 7); + CHECK(f.Read(buf, 16) == 3); + CFileStatus status; + CHECK(f.GetStatus(status)); + CHECK(status.m_size == 10); + } + { + CFile f(name.c_str(), CFile::modeReadWrite | CFile::modeCreate | CFile::modeNoTruncate); + CHECK(f.GetLength() == 10); + f.SeekToEnd(); + f.Write("AB", 2); + f.SetLength(11); + } + CHECK_EQ_STR(ReadRaw(Native("data.bin")), "0123456789A"); + + CFile missing; + CFileException e; + CHECK(!missing.Open(App("missing.bin").c_str(), CFile::modeRead, &e)); + CHECK(e.m_cause == CFileException::fileNotFound); + bool thrown = false; + try { + CFile f(App("missing.bin").c_str(), CFile::modeRead); + } catch (CFileException* ex) { + thrown = ex->m_cause == CFileException::fileNotFound; + char msg[256]; + ex->GetErrorMessage(msg, sizeof msg); + CHECK(strstr(msg, "was not found") != nullptr); + ex->Delete(); + } + CHECK(thrown); + + CFileStatus st; + CHECK(CFile::GetStatus(name.c_str(), st)); + CHECK(st.m_size == 11); + CFile::Rename(name.c_str(), App("renamed.bin").c_str()); + CHECK(!CFile::GetStatus(name.c_str(), st)); + CFile::Remove(App("renamed.bin").c_str()); + CHECK(!CFile::GetStatus(App("renamed.bin").c_str(), st)); + thrown = false; + try { + CFile::Remove(App("renamed.bin").c_str()); + } catch (CFileException* ex) { + thrown = true; + ex->Delete(); + } + CHECK(thrown); + + WriteRaw(Native("crlf.txt"), "first line\r\nsecond\r\n\r\nlast"); + { + CStdioFile f; + CHECK(f.Open(App("crlf.txt").c_str(), CFile::modeRead)); + CString line; + CHECK(f.ReadString(line) && line == "first line"); + CHECK(f.ReadString(line) && line == "second"); + CHECK(f.ReadString(line) && line.IsEmpty()); + CHECK(f.ReadString(line) && line == "last"); + CHECK(!f.ReadString(line)); + f.SeekToBegin(); + char buf[64]; + CHECK(f.ReadString(buf, sizeof buf) != nullptr); + CHECK_EQ_STR(buf, "first line\n"); + } + { + CStdioFile f(App("out.txt").c_str(), CFile::modeCreate | CFile::modeWrite | CFile::typeText); + f.WriteString("alpha\n"); + f.WriteString("beta"); + } + CHECK_EQ_STR(ReadRaw(Native("out.txt")), "alpha\nbeta"); + + CMemFile mem; + mem.Write("hello", 5); + mem.Write(" world", 6); + CHECK(mem.GetLength() == 11); + mem.SeekToBegin(); + char buf[16] = {}; + CHECK(mem.Read(buf, 5) == 5); + CHECK_EQ_STR(buf, "hello"); + mem.SetLength(3); + CHECK(mem.GetLength() == 3 && mem.GetPosition() == 3); + + HANDLE h = CreateFile(App("api.bin").c_str(), GENERIC_WRITE | GENERIC_READ, 0, nullptr, CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, nullptr); + CHECK(h != INVALID_HANDLE_VALUE); + DWORD written = 0; + CHECK(WriteFile(h, "abcdef", 6, &written, nullptr) && written == 6); + CHECK(SetFilePointer(h, 2, nullptr, FILE_BEGIN) == 2); + char rb[8] = {}; + DWORD read = 0; + CHECK(ReadFile(h, rb, 3, &read, nullptr) && read == 3); + CHECK_EQ_STR(rb, "cde"); + CHECK(GetFileSize(h, nullptr) == 6); + FILETIME ftWrite; + CHECK(GetFileTime(h, nullptr, nullptr, &ftWrite)); + CTime written_at(ftWrite); + CHECK(llabs(static_cast(written_at.GetTime() - time(nullptr))) < 60); + CHECK(CloseHandle(h)); + CHECK(!CloseHandle(h)); + CHECK(CreateFile(App("api.bin").c_str(), GENERIC_READ, 0, nullptr, CREATE_NEW, 0, nullptr) == INVALID_HANDLE_VALUE); + CHECK(GetLastError() == 80); + + CHECK(CopyFile(App("api.bin").c_str(), App("copy.bin").c_str(), TRUE)); + CHECK(!CopyFile(App("api.bin").c_str(), App("copy.bin").c_str(), TRUE)); + CHECK(!MoveFile(App("api.bin").c_str(), App("copy.bin").c_str())); + CHECK(MoveFileEx(App("api.bin").c_str(), App("copy.bin").c_str(), MOVEFILE_REPLACE_EXISTING)); + CHECK(GetFileAttributes(App("api.bin").c_str()) == INVALID_FILE_ATTRIBUTES); + CHECK(GetLastError() == ERROR_FILE_NOT_FOUND); + CHECK(SetFileAttributes(App("copy.bin").c_str(), FILE_ATTRIBUTE_READONLY)); + CHECK(GetFileAttributes(App("copy.bin").c_str()) & FILE_ATTRIBUTE_READONLY); + CHECK(SetFileAttributes(App("copy.bin").c_str(), FILE_ATTRIBUTE_NORMAL)); + CHECK(DeleteFile(App("copy.bin").c_str())); + CHECK(CreateDirectory(App("newdir").c_str(), nullptr)); + CHECK(!CreateDirectory(App("newdir").c_str(), nullptr) && GetLastError() == ERROR_ALREADY_EXISTS); + CHECK(RemoveDirectory(App("newdir").c_str())); + + char tmpName[MAX_PATH]; + UINT u = GetTempFileName(g_appRoot.c_str(), "tst", 0, tmpName); + CHECK(u != 0); + CHECK(strncmp(tmpName, g_appRoot.c_str(), g_appRoot.size()) == 0); + CHECK(GetFileAttributes(tmpName) != INVALID_FILE_ATTRIBUTES); + DeleteFile(tmpName); + + FILE* f = fopen(App("fopen.txt").c_str(), "wt"); + CHECK(f != nullptr); + if (f) { + fputs("text", f); + fclose(f); + } + CHECK(rename(App("fopen.txt").c_str(), App("fopen2.txt").c_str()) == 0); + CHECK(remove(App("fopen2.txt").c_str()) == 0); +} + +void TestFind() { + mkdir(Native("find").c_str(), 0755); + WriteRaw(Native("find/a.txt"), "1"); + WriteRaw(Native("find/b.TXT"), "22"); + WriteRaw(Native("find/c.dat"), "333"); + WriteRaw(Native("find/noext"), "4"); + struct _finddata_t fd; + intptr_t h = _findfirst(App("find\\*.txt").c_str(), &fd); + CHECK(h != -1); + std::vector names; + if (h != -1) { + do + names.push_back(fd.name); + while (_findnext(h, &fd) == 0); + CHECK(_findclose(h) == 0); + } + CHECK(names.size() == 2); + CHECK(names.size() == 2 && names[0] == "a.txt" && names[1] == "b.TXT"); + CHECK(_findfirst(App("find\\*.none").c_str(), &fd) == -1); + + WIN32_FIND_DATA wfd; + HANDLE fh = FindFirstFile(App("find\\*.*").c_str(), &wfd); + CHECK(fh != INVALID_HANDLE_VALUE); + int count = 0; + bool dots = false; + if (fh != INVALID_HANDLE_VALUE) { + do { + ++count; + if (strcmp(wfd.cFileName, ".") == 0) + dots = (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; + if (strcmp(wfd.cFileName, "c.dat") == 0) + CHECK(wfd.nFileSizeLow == 3); + } while (FindNextFile(fh, &wfd)); + CHECK(GetLastError() == ERROR_NO_MORE_FILES); + CHECK(FindClose(fh)); + } + CHECK(count == 6); + CHECK(dots); + fh = FindFirstFile(App("find\\*.").c_str(), &wfd); + CHECK(fh != INVALID_HANDLE_VALUE && strcmp(wfd.cFileName, "noext") == 0); + if (fh != INVALID_HANDLE_VALUE) + FindClose(fh); + + CFileFind finder; + int files = 0; + BOOL more = finder.FindFile(App("find\\*.dat").c_str()); + while (more) { + more = finder.FindNextFile(); + ++files; + CHECK(finder.GetFileName() == "c.dat"); + CHECK(finder.GetFilePath() == App("find\\c.dat").c_str()); + CHECK(finder.GetRoot() == App("find\\").c_str()); + CHECK(finder.GetLength() == 3); + CHECK(!finder.IsDirectory() && !finder.IsDots()); + } + CHECK(files == 1); +} + +void TestArchive() { + CString shortStr = "abc"; + CString longStr(static_cast('x'), 300); + CString hugeStr(static_cast('y'), 70000); + { + CFile f(App("archive.bin").c_str(), CFile::modeCreate | CFile::modeWrite); + CArchive ar(&f, CArchive::store); + ar << shortStr << longStr << hugeStr; + ar << static_cast(7) << static_cast(0x1234) << static_cast(-5) << static_cast(0xDEADBEEF); + ar << 3.25 << static_cast(-1234567890123LL) << (int)42; + ar.WriteCount(5); + ar.WriteCount(70000); + CStringArray arr; + arr.Add("one"); + arr.Add("two"); + arr.Serialize(ar); + CStringArray* obj = new CStringArray; + obj->Add("serialized"); + ar << obj; + ar << obj; + ar.WriteObject(nullptr); + ar.WriteString("text line\r\n"); + ar.Close(); + delete obj; + } + std::string raw = ReadRaw(Native("archive.bin")); + CHECK(raw.size() > 8 && raw.substr(0, 4) == "\x03" "abc"); + CHECK(static_cast(raw[4]) == 0xFF && static_cast(raw[5]) == 0x2C && + static_cast(raw[6]) == 0x01); + { + CFile f(App("archive.bin").c_str(), CFile::modeRead); + CArchive ar(&f, CArchive::load); + CString a, b, c; + ar >> a >> b >> c; + CHECK(a == shortStr); + CHECK(b == longStr); + CHECK(c == hugeStr); + BYTE by; + WORD w; + LONG l; + DWORD dw; + double d; + LONGLONG ll; + int i; + ar >> by >> w >> l >> dw >> d >> ll >> i; + CHECK(by == 7 && w == 0x1234 && l == -5 && dw == 0xDEADBEEF && d == 3.25 && ll == -1234567890123LL && i == 42); + CHECK(ar.ReadCount() == 5); + CHECK(ar.ReadCount() == 70000); + CStringArray arr; + arr.Serialize(ar); + CHECK(arr.GetSize() == 2 && arr[0] == "one" && arr[1] == "two"); + CStringArray* first = nullptr; + CStringArray* second = nullptr; + ar >> first >> second; + CHECK(first != nullptr && first == second); + CHECK(first && first->GetSize() == 1 && first->GetAt(0) == "serialized"); + CHECK(first && first->IsKindOf(RUNTIME_CLASS(CStringArray))); + CObject* none = ar.ReadObject(nullptr); + CHECK(none == nullptr); + CString line; + CHECK(ar.ReadString(line) && line == "text line"); + CHECK(!ar.ReadString(line)); + bool eof = false; + try { + CString more; + ar >> more; + } catch (CArchiveException* e) { + eof = e->m_cause == CArchiveException::endOfFile; + e->Delete(); + } + CHECK(eof); + delete first; + } + WriteRaw(Native("unicode.bin"), std::string("\xFF\xFE\xFF\x02h\0i\0", 8)); + { + CFile f(App("unicode.bin").c_str(), CFile::modeRead); + CArchive ar(&f, CArchive::load); + CString s; + ar >> s; + CHECK(s == "hi"); + } + CHECK(CRuntimeClass::FromName("CStringArray") == RUNTIME_CLASS(CStringArray)); + CObject* created = CRuntimeClass::CreateObject("CDWordArray"); + CHECK(created && created->IsKindOf(RUNTIME_CLASS(CDWordArray)) && created->IsSerializable()); + delete created; +} + +void TestIni() { + std::string ini = App("test.ini"); + CHECK(WritePrivateProfileString("General", "Name", "Value one", ini.c_str())); + CHECK(WritePrivateProfileString("General", "Count", "42", ini.c_str())); + CHECK(WritePrivateProfileString("Other", "Key", "\"quoted\"", ini.c_str())); + CHECK(WritePrivateProfileString("general", "name", "Value two", ini.c_str())); + char buf[256]; + CHECK(GetPrivateProfileString("GENERAL", "NAME", "def", buf, sizeof buf, ini.c_str()) == 9); + CHECK_EQ_STR(buf, "Value two"); + CHECK(GetPrivateProfileInt("General", "Count", 0, ini.c_str()) == 42); + CHECK(GetPrivateProfileInt("General", "Missing", 7, ini.c_str()) == 7); + GetPrivateProfileString("Other", "Key", "", buf, sizeof buf, ini.c_str()); + CHECK_EQ_STR(buf, "quoted"); + GetPrivateProfileString("Nope", "Key", "fallback ", buf, sizeof buf, ini.c_str()); + CHECK_EQ_STR(buf, "fallback"); + DWORD n = GetPrivateProfileString(nullptr, nullptr, "", buf, sizeof buf, ini.c_str()); + CHECK(n == 14 && memcmp(buf, "General\0Other\0\0", 15) == 0); + n = GetPrivateProfileString("General", nullptr, "", buf, sizeof buf, ini.c_str()); + CHECK(n == 11 && memcmp(buf, "Name\0Count\0\0", 12) == 0); + n = GetPrivateProfileString("General", "Name", "", buf, 4, ini.c_str()); + CHECK(n == 3); + CHECK_EQ_STR(buf, "Val"); + n = GetPrivateProfileSection("General", buf, sizeof buf, ini.c_str()); + CHECK(n > 0 && strcmp(buf, "Name=Value two") == 0); + CHECK(WritePrivateProfileString("General", "Count", nullptr, ini.c_str())); + CHECK(GetPrivateProfileInt("General", "Count", -1, ini.c_str()) == static_cast(-1)); + CHECK(WritePrivateProfileString("Other", nullptr, nullptr, ini.c_str())); + n = GetPrivateProfileString(nullptr, nullptr, "", buf, sizeof buf, ini.c_str()); + CHECK(n == 8 && memcmp(buf, "General\0\0", 9) == 0); + std::string text = ReadRaw(Native("test.ini")); + CHECK(text == "[General]\r\nName=Value two\r\n"); +} + +void TestRegistry() { + CRegKey key; + CHECK(key.Open(HKEY_CURRENT_USER, "Software\\MfcwxTest\\Missing") == ERROR_FILE_NOT_FOUND); + DWORD disposition = 0; + CHECK(key.Create(HKEY_CURRENT_USER, "Software\\MfcwxTest\\Settings", nullptr, REG_OPTION_NON_VOLATILE, + KEY_ALL_ACCESS, nullptr, &disposition) == ERROR_SUCCESS); + CHECK(disposition == REG_CREATED_NEW_KEY); + CHECK(key.SetDWORDValue("Number", 1234) == ERROR_SUCCESS); + CHECK(key.SetValue(77u, "Other") == ERROR_SUCCESS); + CHECK(key.SetStringValue("Text", "hello w\xF6rld") == ERROR_SUCCESS); + CHECK(key.SetValue("default value") == ERROR_SUCCESS); + BYTE bin[4] = {1, 2, 0xFE, 0}; + CHECK(key.SetBinaryValue("Blob", bin, 4) == ERROR_SUCCESS); + CHECK(key.Close() == ERROR_SUCCESS); + + HKEY sub; + CHECK(RegCreateKeyEx(HKEY_CURRENT_USER, "Software\\MfcwxTest\\Empty", 0, nullptr, 0, KEY_ALL_ACCESS, nullptr, &sub, + &disposition) == ERROR_SUCCESS); + CHECK(RegCloseKey(sub) == ERROR_SUCCESS); + CHECK(RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\MfcwxTest\\Empty", 0, KEY_READ, &sub) == ERROR_SUCCESS); + RegCloseKey(sub); + + CHECK(key.Open(HKEY_CURRENT_USER, "SOFTWARE\\mfcwxtest\\settings", KEY_READ) == ERROR_SUCCESS); + DWORD dw = 0; + CHECK(key.QueryDWORDValue("number", dw) == ERROR_SUCCESS && dw == 1234); + unsigned long other = 0; + CHECK(key.QueryValue(dw, "Other") == ERROR_SUCCESS && dw == 77); + (void)other; + char text[64]; + DWORD count = sizeof text; + CHECK(key.QueryValue(text, "Text", &count) == ERROR_SUCCESS); + CHECK_EQ_STR(text, "hello w\xF6rld"); + CHECK(count == 12); + ULONG chars = 4; + CHECK(key.QueryStringValue("Text", text, &chars) == ERROR_MORE_DATA && chars == 12); + CHECK(key.QueryDWORDValue("Text", dw) == 13); + count = sizeof text; + CHECK(key.QueryValue(text, nullptr, &count) == ERROR_SUCCESS); + CHECK_EQ_STR(text, "default value"); + BYTE out[8] = {}; + ULONG bytes = sizeof out; + CHECK(key.QueryBinaryValue("Blob", out, &bytes) == ERROR_SUCCESS && bytes == 4 && memcmp(out, bin, 4) == 0); + DWORD type = 0, size = 0; + CHECK(RegQueryValueEx(key, "Text", nullptr, &type, nullptr, &size) == ERROR_SUCCESS && type == REG_SZ && size == 12); + CHECK(key.DeleteValue("Other") == ERROR_SUCCESS); + CHECK(key.QueryDWORDValue("Other", dw) == ERROR_FILE_NOT_FOUND); + key.Close(); + + HKEY parent; + CHECK(RegOpenKey(HKEY_CURRENT_USER, "Software\\MfcwxTest", &parent) == ERROR_SUCCESS); + char name[64]; + std::vector subkeys; + for (DWORD i = 0;; ++i) { + DWORD nameSize = sizeof name; + if (RegEnumKeyEx(parent, i, name, &nameSize, nullptr, nullptr, nullptr, nullptr) != ERROR_SUCCESS) + break; + subkeys.push_back(name); + } + CHECK(subkeys.size() == 2 && subkeys[0] == "Empty" && subkeys[1] == "Settings"); + CHECK(RegDeleteKey(parent, "Empty") == ERROR_SUCCESS); + CHECK(RegOpenKeyEx(parent, "Empty", 0, KEY_READ, &sub) == ERROR_FILE_NOT_FOUND); + RegCloseKey(parent); + + std::string file = ReadRaw(getenv("MFCWX_REGISTRY_FILE")); + CHECK(file.find("Number=dword:000004d2") != std::string::npos); + CHECK(file.find("HKEY_CURRENT_USER/Software/MfcwxTest/Settings") != std::string::npos); +} + +std::atomic g_counter{0}; + +UINT CountingProc(LPVOID param) { + int n = *static_cast(param); + for (int i = 0; i < n; ++i) + ++g_counter; + CHECK(AfxGetThread() != nullptr); + return 17; +} + +struct LockData { + CCriticalSection* cs; + long value; +}; + +UINT LockedIncrement(LPVOID param) { + auto* d = static_cast(param); + for (int i = 0; i < 10000; ++i) { + CSingleLock lock(d->cs, TRUE); + long v = d->value; + d->value = v + 1; + } + return 0; +} + +UINT EndEarly(LPVOID) { + AfxEndThread(99, FALSE); + return 1; +} + +UINT WaitForEvent(LPVOID param) { + auto* ev = static_cast(param); + return ::WaitForSingleObject(*ev, 5000) == WAIT_OBJECT_0 ? 5 : 6; +} + +std::atomic g_ticks{0}; +std::atomic g_stop{false}; + +DWORD Ticker(LPVOID) { + while (!g_stop) { + ++g_ticks; + Sleep(1); + } + return 3; +} + +DWORD ExitEarly(LPVOID) { + ExitThread(21); + return 0; +} + +unsigned BeginEx(void* arg) { + ++*static_cast*>(arg); + return 8; +} + +void TestThreads() { + int iterations = 1000; + CWinThread* t = AfxBeginThread(CountingProc, &iterations, THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED); + CHECK(t != nullptr); + t->m_bAutoDelete = FALSE; + Sleep(20); + CHECK(g_counter == 0); + DWORD code = 0; + CHECK(GetExitCodeThread(t->m_hThread, &code) && code == STILL_ACTIVE); + t->ResumeThread(); + CHECK(WaitForSingleObject(t->m_hThread, 5000) == WAIT_OBJECT_0); + CHECK(g_counter == 1000); + CHECK(GetExitCodeThread(t->m_hThread, &code) && code == 17); + delete t; + + t = AfxBeginThread(CountingProc, &iterations); + HANDLE stale = t->m_hThread; + CHECK(WaitForSingleObject(stale, 5000) == WAIT_OBJECT_0); + Sleep(20); + CHECK(WaitForSingleObject(stale, 0) == WAIT_OBJECT_0); + CHECK(g_counter == 2000); + + t = AfxBeginThread(EndEarly, nullptr, THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED); + t->m_bAutoDelete = FALSE; + t->ResumeThread(); + CHECK(WaitForSingleObject(*t, 5000) == WAIT_OBJECT_0); + CHECK(GetExitCodeThread(*t, &code) && code == 99); + delete t; + + CCriticalSection cs; + LockData data{&cs, 0}; + std::vector workers; + std::vector handles; + for (int i = 0; i < 4; ++i) { + CWinThread* w = AfxBeginThread(LockedIncrement, &data, THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED); + w->m_bAutoDelete = FALSE; + workers.push_back(w); + handles.push_back(w->m_hThread); + } + for (CWinThread* w : workers) + w->ResumeThread(); + CHECK(WaitForMultipleObjects(4, handles.data(), TRUE, 10000) == WAIT_OBJECT_0); + CHECK(data.value == 40000); + for (CWinThread* w : workers) + delete w; + + CEvent manual(FALSE, TRUE); + CHECK(WaitForSingleObject(manual, 10) == WAIT_TIMEOUT); + manual.SetEvent(); + CHECK(WaitForSingleObject(manual, 0) == WAIT_OBJECT_0); + CHECK(WaitForSingleObject(manual, 0) == WAIT_OBJECT_0); + manual.ResetEvent(); + CHECK(WaitForSingleObject(manual, 0) == WAIT_TIMEOUT); + CEvent autoEvent; + autoEvent.SetEvent(); + CHECK(WaitForSingleObject(autoEvent, 0) == WAIT_OBJECT_0); + CHECK(WaitForSingleObject(autoEvent, 0) == WAIT_TIMEOUT); + CEvent go(FALSE, TRUE); + t = AfxBeginThread(WaitForEvent, &go, THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED); + t->m_bAutoDelete = FALSE; + t->ResumeThread(); + Sleep(20); + CHECK(WaitForSingleObject(*t, 0) == WAIT_TIMEOUT); + go.SetEvent(); + CHECK(WaitForSingleObject(*t, 5000) == WAIT_OBJECT_0); + CHECK(GetExitCodeThread(*t, &code) && code == 5); + delete t; + + HANDLE ev1 = CreateEvent(nullptr, FALSE, FALSE, nullptr); + HANDLE ev2 = CreateEvent(nullptr, FALSE, TRUE, nullptr); + HANDLE pair[2] = {ev1, ev2}; + CHECK(WaitForMultipleObjects(2, pair, FALSE, 0) == WAIT_OBJECT_0 + 1); + CHECK(WaitForMultipleObjects(2, pair, FALSE, 10) == WAIT_TIMEOUT); + SetEvent(ev1); + SetEvent(ev2); + CHECK(WaitForMultipleObjects(2, pair, TRUE, 0) == WAIT_OBJECT_0); + CHECK(WaitForSingleObject(ev1, 0) == WAIT_TIMEOUT); + HANDLE named = CreateEvent(nullptr, TRUE, FALSE, "mfcwx-test-event"); + HANDLE again = CreateEvent(nullptr, TRUE, FALSE, "mfcwx-test-event"); + CHECK(again == named && GetLastError() == ERROR_ALREADY_EXISTS); + CloseHandle(again); + CloseHandle(named); + CloseHandle(ev1); + CloseHandle(ev2); + CHECK(WaitForSingleObject(ev1, 0) == WAIT_FAILED); + + CMutex mutex; + CSingleLock ml(&mutex); + CHECK(ml.Lock(0) && ml.IsLocked()); + CHECK(mutex.Lock(0)); + mutex.Unlock(); + ml.Unlock(); + CSemaphore sem(1, 2); + CHECK(sem.Lock(0)); + CHECK(!sem.Lock(0)); + LONG prev = -1; + CHECK(sem.Unlock(2, &prev) && prev == 0); + CHECK(!sem.Unlock(1)); + CSyncObject* objs[2] = {&manual, &sem}; + CMultiLock multi(objs, 2); + manual.SetEvent(); + CHECK(multi.Lock(100, TRUE) == WAIT_OBJECT_0); + multi.Unlock(); + + DWORD tid = 0; + HANDLE ticker = CreateThread(nullptr, 0, Ticker, nullptr, CREATE_SUSPENDED, &tid); + CHECK(ticker != nullptr && tid != 0 && tid != GetCurrentThreadId()); + Sleep(20); + CHECK(g_ticks == 0); + CHECK(ResumeThread(ticker) == 1); + Sleep(30); + CHECK(g_ticks > 0); + CHECK(SuspendThread(ticker) == 0); + Sleep(20); + int frozen = g_ticks; + Sleep(40); + CHECK(g_ticks == frozen); + CHECK(ResumeThread(ticker) == 1); + Sleep(30); + CHECK(g_ticks > frozen); + g_stop = true; + CHECK(WaitForSingleObject(ticker, 5000) == WAIT_OBJECT_0); + CHECK(GetExitCodeThread(ticker, &code) && code == 3); + CHECK(!TerminateThread(ticker, 0)); + CloseHandle(ticker); + + HANDLE early = CreateThread(nullptr, 0, ExitEarly, nullptr, 0, nullptr); + CHECK(WaitForSingleObject(early, 5000) == WAIT_OBJECT_0); + CHECK(GetExitCodeThread(early, &code) && code == 21); + CloseHandle(early); + + std::atomic hits{0}; + unsigned exTid = 0; + HANDLE ex = reinterpret_cast(_beginthreadex(nullptr, 0, BeginEx, &hits, 0, &exTid)); + CHECK(WaitForSingleObject(ex, 5000) == WAIT_OBJECT_0 && hits == 1); + CHECK(GetExitCodeThread(ex, &code) && code == 8); + CloseHandle(ex); + + HANDLE never = CreateThread(nullptr, 0, Ticker, nullptr, CREATE_SUSPENDED, nullptr); + CHECK(TerminateThread(never, 4)); + CHECK(WaitForSingleObject(never, 0) == WAIT_OBJECT_0); + CloseHandle(never); + + LONG v = 5; + CHECK(InterlockedIncrement(&v) == 6 && InterlockedDecrement(&v) == 5); + CHECK(InterlockedExchange(&v, 9) == 5 && v == 9); + CHECK(InterlockedCompareExchange(&v, 1, 9) == 9 && v == 1); + CHECK(InterlockedExchangeAdd(&v, 4) == 1 && v == 5); + CRITICAL_SECTION raw; + InitializeCriticalSection(&raw); + EnterCriticalSection(&raw); + CHECK(TryEnterCriticalSection(&raw)); + LeaveCriticalSection(&raw); + LeaveCriticalSection(&raw); + DeleteCriticalSection(&raw); + CHECK(AfxGetThread() == nullptr); +} + +void TestProcesses() { + std::string out = App("echo.txt"); + SECURITY_ATTRIBUTES sa = {sizeof sa, nullptr, TRUE}; + HANDLE file = CreateFile(out.c_str(), FILE_WRITE_DATA, FILE_SHARE_READ, &sa, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); + CHECK(file != INVALID_HANDLE_VALUE); + STARTUPINFO si; + memset(&si, 0, sizeof si); + si.cb = sizeof si; + si.dwFlags = STARTF_USESTDHANDLES; + si.hStdOutput = file; + si.hStdError = file; + PROCESS_INFORMATION pi; + char cmd[] = "/bin/echo hello \"big world\""; + CHECK(CreateProcess(nullptr, cmd, nullptr, nullptr, TRUE, CREATE_NO_WINDOW, nullptr, nullptr, &si, &pi)); + CHECK(WaitForSingleObject(pi.hProcess, 5000) == WAIT_OBJECT_0); + DWORD code = 1234; + CHECK(GetExitCodeProcess(pi.hProcess, &code) && code == 0); + CHECK(WaitForSingleObject(pi.hThread, 0) == WAIT_OBJECT_0); + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + CloseHandle(file); + CHECK_EQ_STR(ReadRaw(Native("echo.txt")), "hello big world\n"); + + char sh[] = "sh -c \"exit 3\""; + CHECK(CreateProcess(nullptr, sh, nullptr, nullptr, FALSE, 0, nullptr, g_appRoot.c_str(), nullptr, &pi)); + CHECK(WaitForSingleObject(pi.hProcess, 5000) == WAIT_OBJECT_0); + CHECK(GetExitCodeProcess(pi.hProcess, &code) && code == 3); + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + + char sleeper[] = "sleep 5"; + CHECK(CreateProcess(nullptr, sleeper, nullptr, nullptr, FALSE, 0, nullptr, nullptr, nullptr, &pi)); + CHECK(WaitForSingleObject(pi.hProcess, 50) == WAIT_TIMEOUT); + CHECK(GetExitCodeProcess(pi.hProcess, &code) && code == STILL_ACTIVE); + CHECK(TerminateProcess(pi.hProcess, 1)); + CHECK(WaitForSingleObject(pi.hProcess, 5000) == WAIT_OBJECT_0); + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + + char missing[] = "definitely-not-a-program-xyz.exe --flag"; + CHECK(!CreateProcess(nullptr, missing, nullptr, nullptr, FALSE, 0, nullptr, nullptr, nullptr, &pi)); + CHECK(GetLastError() == ERROR_FILE_NOT_FOUND); + CHECK(WinExec("/usr/bin/true", SW_HIDE) > 31); + CHECK(reinterpret_cast(ShellExecute(nullptr, "open", "/no/such/file.xyz", nullptr, nullptr, SW_SHOW)) <= 32); +} + +void TestCrt() { + char buf[80]; + CHECK_EQ_STR(_itoa(-255, buf, 10), "-255"); + CHECK_EQ_STR(_itoa(-1, buf, 16), "ffffffff"); + CHECK_EQ_STR(_itoa(255, buf, 2), "11111111"); + CHECK_EQ_STR(_itoa(35, buf, 36), "z"); + CHECK_EQ_STR(itoa(0, buf, 10), "0"); + CHECK_EQ_STR(_ltoa(-1L, buf, 16), "ffffffff"); + CHECK_EQ_STR(_ltoa(-123456L, buf, 10), "-123456"); + CHECK_EQ_STR(_ultoa(4294967295UL, buf, 10), "4294967295"); + CHECK_EQ_STR(_i64toa(-1LL, buf, 16), "ffffffffffffffff"); + CHECK_EQ_STR(_i64toa(LLONG_MIN, buf, 10), "-9223372036854775808"); + CHECK_EQ_STR(_ui64toa(18446744073709551615ULL, buf, 10), "18446744073709551615"); + CHECK(_itoa_s(12345, buf, 4, 10) == ERANGE && buf[0] == 0); + CHECK(_itoa_s(123, buf, 4, 10) == 0 && strcmp(buf, "123") == 0); + CHECK_EQ_STR(_gcvt(1.0, 5, buf), "1."); + CHECK_EQ_STR(_gcvt(3.14159, 3, buf), "3.14"); + int dec = 0, sign = 0; + CHECK_EQ_STR(_ecvt(3.1415926535, 5, &dec, &sign), "31416"); + CHECK(dec == 1 && sign == 0); + CHECK_EQ_STR(_fcvt(-3.1415926535, 7, &dec, &sign), "31415927"); + CHECK(dec == 1 && sign == 1); + CHECK_EQ_STR(_fcvt(0.012, 4, &dec, &sign), "120"); + CHECK(dec == -1); + char s[] = "Hello"; + CHECK_EQ_STR(_strupr(s), "HELLO"); + CHECK_EQ_STR(_strlwr(s), "hello"); + CHECK_EQ_STR(_strrev(s), "olleh"); + CHECK_EQ_STR(_strnset(s, '*', 2), "**leh"); + CHECK(sprintf_s(buf, 4, "%s", "toolong") == -1 && buf[0] == 0); + CHECK(sprintf_s(buf, sizeof buf, "%I64d", 1234567890123LL) == 13 && strcmp(buf, "1234567890123") == 0); + CHECK(_snprintf_s(buf, sizeof buf, _TRUNCATE, "%s", "abc") == 3); + CHECK(_snprintf_s(buf, 4, static_cast(-1), "%s", "abcdef") == -1 && strcmp(buf, "abc") == 0); + CHECK(strcpy_s(buf, 3, "abc") == ERANGE && buf[0] == 0); + CHECK(strcpy_s(buf, sizeof buf, "abc") == 0 && strcat_s(buf, sizeof buf, "def") == 0 && strcmp(buf, "abcdef") == 0); + CHECK(strncpy_s(buf, sizeof buf, "abcdef", 3) == 0 && strcmp(buf, "abc") == 0); + CHECK(mfcwx_rotl(0x80000001u, 1) == 3u && mfcwx_rotr(3u, 1) == 0x80000001u); + time_t now = time(nullptr); + struct tm tm; + CHECK(localtime_s(&tm, &now) == 0); + char* env = nullptr; + size_t envLen = 0; + CHECK(_dupenv_s(&env, &envLen, "HOME") == 0 && env && envLen == strlen(env) + 1); + free(env); + char templ[MAX_PATH]; + snprintf(templ, sizeof templ, "%s\\tmpXXXXXX", g_appRoot.c_str()); + CHECK(_mktemp(templ) != nullptr && strstr(templ, "XXXXXX") == nullptr); + char* tn = _tempnam(g_appRoot.c_str(), "pse"); + CHECK(tn && strncmp(tn, (g_appRoot + "\\pse").c_str(), g_appRoot.size() + 4) == 0); + free(tn); + CHECK(_mkdir(App("crtdir").c_str()) == 0); + CHECK(_rmdir(App("crtdir").c_str()) == 0); + int fd = _open(App("open.bin").c_str(), _O_CREAT | _O_WRONLY | _O_BINARY, _S_IREAD | _S_IWRITE); + CHECK(fd >= 0); + CHECK(_write(fd, "12345", 5) == 5); + CHECK(_filelength(fd) == 5); + CHECK(_chsize(fd, 2) == 0 && _filelength(fd) == 2); + _close(fd); +} + +void TestWin32() { + mfcwx::SetAnsiCodePage(1250); + const char polish[] = "\xB9\xE6\x9C\xBF\xA3"; + wchar_t wide[16]; + int n = MultiByteToWideChar(1250, 0, polish, -1, nullptr, 0); + CHECK(n == 6); + CHECK(MultiByteToWideChar(1250, 0, polish, -1, wide, 16) == 6); + CHECK(wide[0] == 0x105 && wide[1] == 0x107 && wide[2] == 0x15B && wide[3] == 0x17C && wide[4] == 0x141 && wide[5] == 0); + CHECK(MultiByteToWideChar(CP_ACP, 0, polish, 2, wide, 16) == 2 && wide[0] == 0x105); + char back[16]; + BOOL usedDefault = TRUE; + CHECK(WideCharToMultiByte(1250, 0, wide, 2, back, sizeof back, nullptr, &usedDefault) == 2); + CHECK(memcmp(back, polish, 2) == 0 && !usedDefault); + CHECK(WideCharToMultiByte(1252, 0, wide, 1, back, sizeof back, nullptr, &usedDefault) == 1 && back[0] == '?' && + usedDefault); + CHECK(WideCharToMultiByte(CP_UTF8, 0, wide, 1, back, sizeof back, nullptr, nullptr) == 2); + CHECK(memcmp(back, "\xC4\x85", 2) == 0); + CHECK(MultiByteToWideChar(CP_UTF8, 0, "\xF0\x9F\x98\x80", 4, wide, 16) == 1 && wide[0] == 0x1F600); + CHECK(MultiByteToWideChar(1250, 0, polish, 5, wide, 2) == 0 && GetLastError() == 122); + + char upper[] = "\xB9\xE6\x9F" "abc"; + CharUpper(upper); + CHECK_EQ_STR(upper, "\xA5\xC6\x8F" "ABC"); + CharLowerBuff(upper, 2); + CHECK_EQ_STR(upper, "\xB9\xE6\x8F" "ABC"); + CHECK(IsCharAlpha('\xB9') && IsCharLower('\xB9') && IsCharUpper('\xA5') && !IsCharAlpha('1')); + CHECK(IsCharAlphaNumeric('7') && !IsCharAlpha('\xA7')); + CHECK(GetACP() == 1250); + mfcwx::SetAnsiCodePage(1252); + CHECK(lstrcmpi("\xC4pfel", "\xE4PFEL") == 0); + CHECK(lstrcmp("a", "B") < 0 && lstrcmp("a", "A") < 0 && lstrcmp("abc", "abc") == 0); + char buf[256]; + CHECK(wsprintf(buf, "%d-%s-%I64u", 5, "x", 99ULL) == 6 && strcmp(buf, "5-x-99") == 0); + lstrcpyn(buf, "abcdef", 4); + CHECK_EQ_STR(buf, "abc"); + + LPSTR msg = nullptr; + DWORD len = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + nullptr, ERROR_FILE_NOT_FOUND, 0, reinterpret_cast(&msg), 0, nullptr); + CHECK(len > 0 && msg && strstr(msg, "cannot find the file") != nullptr); + CHECK(LocalFree(msg) == nullptr); + DWORD_PTR args[2] = {reinterpret_cast("file.txt"), 7}; + len = FormatMessage(FORMAT_MESSAGE_FROM_STRING | 0x2000, "Open %1 failed (%2!d!)%n", 0, 0, buf, sizeof buf, + reinterpret_cast(args)); + CHECK_EQ_STR(buf, "Open file.txt failed (7)\r\n"); + + HGLOBAL g = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, 16); + CHECK(g && GlobalSize(g) == 16); + char* p = static_cast(GlobalLock(g)); + CHECK(p && p[15] == 0); + strcpy(p, "clip"); + GlobalUnlock(g); + g = GlobalReAlloc(g, 4096, GMEM_MOVEABLE); + CHECK(g && GlobalSize(g) == 4096 && strcmp(static_cast(GlobalLock(g)), "clip") == 0); + GlobalUnlock(g); + CHECK(GlobalFree(g) == nullptr); + char* fixed = static_cast(GlobalAlloc(GPTR, 8)); + CHECK(fixed && fixed[7] == 0 && GlobalLock(fixed) == fixed); + GlobalFree(fixed); + + OSVERSIONINFO vi; + vi.dwOSVersionInfoSize = sizeof vi; + CHECK(GetVersionEx(&vi) && vi.dwMajorVersion == 5 && vi.dwMinorVersion == 1); + SYSTEM_INFO si; + GetSystemInfo(&si); + CHECK(si.dwNumberOfProcessors >= 1 && si.dwPageSize >= 4096); + MEMORYSTATUS ms; + GlobalMemoryStatus(&ms); + CHECK(ms.dwTotalPhys > 0 && ms.dwAvailPhys <= ms.dwTotalPhys); + LARGE_INTEGER f, c1, c2; + CHECK(QueryPerformanceFrequency(&f) && f.QuadPart == 10000000); + QueryPerformanceCounter(&c1); + DWORD t0 = GetTickCount(); + Sleep(20); + QueryPerformanceCounter(&c2); + CHECK(c2.QuadPart - c1.QuadPart >= 150000); + CHECK(GetTickCount() - t0 >= 15); + SYSTEMTIME st; + GetSystemTime(&st); + FILETIME ft, ft2; + CHECK(SystemTimeToFileTime(&st, &ft)); + SYSTEMTIME st2; + CHECK(FileTimeToSystemTime(&ft, &st2)); + CHECK(st2.wYear == st.wYear && st2.wSecond == st.wSecond && st2.wMilliseconds == st.wMilliseconds); + st.wYear = 1601; + st.wMonth = 1; + st.wDay = 1; + st.wHour = st.wMinute = st.wSecond = st.wMilliseconds = 0; + CHECK(SystemTimeToFileTime(&st, &ft2) && ft2.dwLowDateTime == 0 && ft2.dwHighDateTime == 0); + CHECK(MulDiv(10, 3, 4) == 8 && MulDiv(-10, 3, 4) == -8 && MulDiv(1, 1, 0) == -1); + WORD lang = static_cast(GetUserDefaultLangID()); + CHECK(PRIMARYLANGID(lang) != 0); + + CHECK(SetEnvironmentVariable("MFCWX_TEST_VAR", "abc")); + CHECK(GetEnvironmentVariable("MFCWX_TEST_VAR", buf, sizeof buf) == 3 && strcmp(buf, "abc") == 0); + CHECK(ExpandEnvironmentStrings("x%MFCWX_TEST_VAR%y%UNDEFINED_MFCWX%", buf, sizeof buf) == 23); + CHECK_EQ_STR(buf, "xabcy%UNDEFINED_MFCWX%"); + CHECK(SetEnvironmentVariable("MFCWX_TEST_VAR", nullptr)); + CHECK(GetEnvironmentVariable("MFCWX_TEST_VAR", buf, sizeof buf) == 0); + DWORD size = sizeof buf; + CHECK(GetUserName(buf, &size) && size == strlen(buf) + 1); + size = sizeof buf; + CHECK(GetComputerName(buf, &size) && size == strlen(buf)); + CHECK(LoadLibrary("SciLexer.dll") == nullptr && GetLastError() == 126); + CHECK(GetModuleHandle(nullptr) != nullptr); + + CTime ct(2024, 3, 5, 7, 8, 9); + CHECK(ct.GetYear() == 2024 && ct.GetMonth() == 3 && ct.GetDay() == 5 && ct.GetHour() == 7); + CHECK(ct.Format("%#d.%#m.%Y %H:%M:%S") == "5.3.2024 07:08:09"); + CHECK(ct.Format("%#x") == "Tuesday, March 5, 2024"); + CHECK(ct.GetDayOfWeek() == 3); + CHECK((ct + CTimeSpan(1, 2, 3, 4)).Format("%d %H:%M:%S") == "06 09:11:13"); + CHECK(CTimeSpan(1, 2, 3, 4).Format("%D:%H:%M:%S") == "1:02:03:04"); + COleDateTime od(2024, 2, 29, 12, 30, 15); + CHECK(od.m_status == COleDateTime::valid); + CHECK(od.GetYear() == 2024 && od.GetMonth() == 2 && od.GetDay() == 29 && od.GetHour() == 12 && + od.GetMinute() == 30 && od.GetSecond() == 15); + CHECK(od.GetDayOfWeek() == 5); + CHECK(static_cast(od.m_dt) == 45351); + CHECK(od.Format("%Y-%m-%d") == "2024-02-29"); + COleDateTime bad(2023, 2, 29, 0, 0, 0); + CHECK(bad.m_status == COleDateTime::invalid); + CHECK(COleDateTime::GetCurrentTime().GetYear() >= 2024); +} + +void TestExceptions() { + bool caught = false; + try { + AfxThrowArchiveException(CArchiveException::badSchema, "doc.ct"); + } catch (CException* e) { + char msg[256]; + caught = e->IsKindOf(RUNTIME_CLASS(CArchiveException)) && e->GetErrorMessage(msg, sizeof msg) && + strcmp(msg, "doc.ct contains an incorrect schema.") == 0; + e->Delete(); + } + CHECK(caught); + caught = false; + try { + AfxThrowMemoryException(); + } catch (CMemoryException* e) { + caught = true; + e->Delete(); + } + CHECK(caught); + CHECK(CFileException::ErrnoToException(ENOENT) == CFileException::fileNotFound); + CHECK(CFileException::OsErrorToException(5) == CFileException::accessDenied); +} + +} // namespace + +int main(int argc, char** argv) { + wxInitializer initializer(argc, argv); + if (!initializer.IsOk()) { + fprintf(stderr, "wxWidgets initialization failed\n"); + return 2; + } + const char* tmp = getenv("TMPDIR"); + std::string base = tmp && *tmp ? tmp : "/tmp"; + while (base.size() > 1 && base.back() == '/') + base.pop_back(); + std::string templ = base + "/mfcwx-tests-XXXXXX"; + std::vector buf(templ.begin(), templ.end()); + buf.push_back(0); + if (!mkdtemp(buf.data())) { + perror("mkdtemp"); + return 2; + } + g_root = buf.data(); + char real[PATH_MAX]; + if (realpath(g_root.c_str(), real)) + g_root = real; + g_appRoot = mfcwx::AppPath(g_root.c_str()); + std::string registry = g_root + "/registry.ini"; + setenv("MFCWX_REGISTRY_FILE", registry.c_str(), 1); + + TestPaths(); + TestFiles(); + TestFind(); + TestArchive(); + TestIni(); + TestRegistry(); + TestThreads(); + TestProcesses(); + TestCrt(); + TestWin32(); + TestExceptions(); + + std::string cleanup = "rm -rf '" + g_root + "'"; + if (g_failures == 0 && system(cleanup.c_str()) != 0) + fprintf(stderr, "cleanup of %s failed\n", g_root.c_str()); + printf("%d checks, %d failures\n", g_checks, g_failures); + return g_failures ? 1 : 0; +} From cf6465e28b128802f67ea42234951f140e633d30 Mon Sep 17 00:00:00 2001 From: Kamil Marczak Date: Tue, 6 Oct 2026 17:40:34 +0200 Subject: [PATCH 16/46] docs(port): update porting status and next steps --- port/PORTING_STATUS.md | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/port/PORTING_STATUS.md b/port/PORTING_STATUS.md index 0d10aa6ed..69cdfefdc 100644 --- a/port/PORTING_STATUS.md +++ b/port/PORTING_STATUS.md @@ -44,7 +44,13 @@ Build: `cmake -S . -B build -G Ninja && ninja -C build -k 0 CrypTool` - Cross-thread `SendMessage` is marshalled to the main thread like on Windows. ## Remaining work (in order) -1. mfcwx implementation still missing: non-GUI runtime (files, archives, threads, registry/INI, CRT compat), GDI (CDC on wxDC), `DefaultWindowProc` + Win32 HWND functions (winapi.cpp), dialogs + DDX (dialog.cpp), menus, CWinApp/main loop/help, doc/view + MDI frames, Scintilla proxy (ANSI↔UTF-8 positions), WGL on wxGLCanvas. -2. Finish SECUDE replacement and its tests; recreate sample PSE keys. -3. Remaining ~680 compile errors in app sources (MSVC-isms: CString through varargs, temporaries bound to non-const refs, PictureEx/IPicture, OpenGL/libVolRen NV extensions, ActiveX editor). -4. Link, run, visually tune dialog units/fonts, then packaging (.app/DMG, AppImage/.deb). +1. Finish the partially written mfcwx parts: non-GUI runtime (`kernel.cpp` and files/archives/CRT), + GDI (`gdi.cpp`), control classes (`ctrlclasses.cpp`), Scintilla proxy (`scintilla.cpp`), help + (`help.cpp`), WGL on wxGLCanvas. +2. Fix the remaining compile errors in the app sources (see the port-status badge). +3. Link, run, visually tune dialog units and fonts. +4. Packaging (.app/DMG, AppImage/.deb). + +Done so far: resource compiler, third-party libraries, SECUDE replacement (10/10 tests), +message dispatch and controls, dialogs and DDX, menus, document/view with tabbed MDI, +CWinApp and startup glue. From 6d4730bbaa1169009a38f14ce1a05e5bb492d434 Mon Sep 17 00:00:00 2001 From: Kamil Marczak Date: Tue, 6 Oct 2026 23:18:29 +0200 Subject: [PATCH 17/46] feat(mfcwx): emulate WGL with a wxGLCanvas covering the window --- port/mfcwx/src/gl.cpp | 194 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 194 insertions(+) create mode 100644 port/mfcwx/src/gl.cpp diff --git a/port/mfcwx/src/gl.cpp b/port/mfcwx/src/gl.cpp new file mode 100644 index 000000000..6bc70e07b --- /dev/null +++ b/port/mfcwx/src/gl.cpp @@ -0,0 +1,194 @@ +#include "windows_impl.h" + +#include + +#include +#include + +namespace mfcwx { + +namespace { + +// A GL canvas covering the client area of a window that asked for an OpenGL pixel format. Paint, +// mouse and keyboard input are forwarded to the window, which renders through the WGL calls. +class GLSurface : public wxGLCanvas { +public: + GLSurface(wxWindow* owner, const int* attribs) + : wxGLCanvas(owner, wxID_ANY, attribs, wxPoint(0, 0), owner->GetClientSize(), wxFULL_REPAINT_ON_RESIZE | wxWANTS_CHARS), + m_owner(owner) { + Bind(wxEVT_PAINT, [this](wxPaintEvent&) { + wxPaintDC dc(this); + if (IsManagedWindow(m_owner)) + DispatchMessageTo(ToHwnd(m_owner), WM_PAINT, 0, 0); + }); + Bind(wxEVT_ERASE_BACKGROUND, [](wxEraseEvent&) {}); + owner->Bind(wxEVT_SIZE, [this](wxSizeEvent& e) { + SetSize(m_owner->GetClientSize()); + e.Skip(); + }); + auto forwardMouse = [this](wxMouseEvent& e, UINT msg) { + if (IsManagedWindow(m_owner)) + DispatchMessageTo(ToHwnd(m_owner), msg, MouseFlags(e), MAKELPARAM(e.GetX(), e.GetY())); + e.Skip(); + }; + Bind(wxEVT_LEFT_DOWN, [forwardMouse](wxMouseEvent& e) { forwardMouse(e, WM_LBUTTONDOWN); }); + Bind(wxEVT_LEFT_UP, [forwardMouse](wxMouseEvent& e) { forwardMouse(e, WM_LBUTTONUP); }); + Bind(wxEVT_LEFT_DCLICK, [forwardMouse](wxMouseEvent& e) { forwardMouse(e, WM_LBUTTONDBLCLK); }); + Bind(wxEVT_RIGHT_DOWN, [forwardMouse](wxMouseEvent& e) { forwardMouse(e, WM_RBUTTONDOWN); }); + Bind(wxEVT_RIGHT_UP, [forwardMouse](wxMouseEvent& e) { forwardMouse(e, WM_RBUTTONUP); }); + Bind(wxEVT_MIDDLE_DOWN, [forwardMouse](wxMouseEvent& e) { forwardMouse(e, WM_MBUTTONDOWN); }); + Bind(wxEVT_MIDDLE_UP, [forwardMouse](wxMouseEvent& e) { forwardMouse(e, WM_MBUTTONUP); }); + Bind(wxEVT_MOTION, [forwardMouse](wxMouseEvent& e) { forwardMouse(e, WM_MOUSEMOVE); }); + Bind(wxEVT_MOUSEWHEEL, [this](wxMouseEvent& e) { + int delta = e.GetWheelRotation() * WHEEL_DELTA / std::max(1, e.GetWheelDelta()); + wxPoint screen = ClientToScreen(e.GetPosition()); + if (IsManagedWindow(m_owner)) + DispatchMessageTo(ToHwnd(m_owner), WM_MOUSEWHEEL, + MAKEWPARAM(MouseFlags(e), static_cast(static_cast(delta))), + MAKELPARAM(screen.x, screen.y)); + }); + auto forwardKey = [this](wxKeyEvent& e, UINT msg) { + UINT vk = VirtualKeyFromWx(e.GetKeyCode()); + if (vk && IsManagedWindow(m_owner)) + DispatchMessageTo(ToHwnd(m_owner), msg, vk, KeyLParam(e, msg == WM_KEYUP)); + e.Skip(); + }; + Bind(wxEVT_KEY_DOWN, [forwardKey](wxKeyEvent& e) { forwardKey(e, WM_KEYDOWN); }); + Bind(wxEVT_KEY_UP, [forwardKey](wxKeyEvent& e) { forwardKey(e, WM_KEYUP); }); + } + +private: + wxWindow* m_owner; +}; + +struct GLContextHandle { + wxGLContext* context = nullptr; + GLSurface* surface = nullptr; +}; + +std::map& Surfaces() { + static std::map s; + return s; +} + +HGLRC g_current = nullptr; +HDC g_currentDC = nullptr; + +wxWindow* WindowOfDC(HDC hdc) { + CDC* dc = hdc ? CDC::FromHandle(hdc) : nullptr; + CWnd* wnd = dc ? dc->GetWindow() : nullptr; + return wnd && wnd->m_hWnd ? wnd->GetWx() : nullptr; +} + +GLSurface* SurfaceFor(HDC hdc) { + wxWindow* w = WindowOfDC(hdc); + if (!w) + return nullptr; + auto it = Surfaces().find(w); + return it == Surfaces().end() ? nullptr : it->second; +} + +} // namespace + +} // namespace mfcwx + +using namespace mfcwx; + +int ChoosePixelFormat(HDC, const PIXELFORMATDESCRIPTOR*) { return 1; } + +BOOL SetPixelFormat(HDC hdc, int, const PIXELFORMATDESCRIPTOR* ppfd) { + return OnMain([&]() -> BOOL { + wxWindow* w = WindowOfDC(hdc); + if (!w) + return FALSE; + if (Surfaces().count(w)) + return TRUE; + int depth = ppfd && ppfd->cDepthBits ? ppfd->cDepthBits : 24; + std::vector attribs = {WX_GL_RGBA, WX_GL_DEPTH_SIZE, depth}; + if (!ppfd || (ppfd->dwFlags & PFD_DOUBLEBUFFER)) + attribs.push_back(WX_GL_DOUBLEBUFFER); + attribs.push_back(0); + auto* surface = new GLSurface(w, attribs.data()); + Surfaces()[w] = surface; + w->Bind(wxEVT_DESTROY, [w](wxWindowDestroyEvent& e) { + if (e.GetWindow() == w) + Surfaces().erase(w); + e.Skip(); + }); + return TRUE; + }); +} + +int GetPixelFormat(HDC hdc) { return SurfaceFor(hdc) ? 1 : 0; } + +int DescribePixelFormat(HDC, int, UINT nBytes, LPPIXELFORMATDESCRIPTOR ppfd) { + if (ppfd && nBytes >= sizeof(PIXELFORMATDESCRIPTOR)) { + memset(ppfd, 0, sizeof *ppfd); + ppfd->nSize = sizeof *ppfd; + ppfd->nVersion = 1; + ppfd->dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; + ppfd->iPixelType = PFD_TYPE_RGBA; + ppfd->cColorBits = 32; + ppfd->cRedBits = ppfd->cGreenBits = ppfd->cBlueBits = ppfd->cAlphaBits = 8; + ppfd->cDepthBits = 24; + ppfd->iLayerType = PFD_MAIN_PLANE; + } + return 1; +} + +BOOL SwapBuffers(HDC hdc) { + return OnMain([&]() -> BOOL { + GLSurface* s = SurfaceFor(hdc); + if (!s && g_current) + s = reinterpret_cast(g_current)->surface; + return s ? s->SwapBuffers() : FALSE; + }); +} + +HGLRC wglCreateContext(HDC hdc) { + return OnMain([&]() -> HGLRC { + GLSurface* s = SurfaceFor(hdc); + if (!s) + return nullptr; + auto* h = new GLContextHandle; + h->surface = s; + h->context = new wxGLContext(s); + return reinterpret_cast(h); + }); +} + +BOOL wglDeleteContext(HGLRC hglrc) { + if (!hglrc) + return FALSE; + OnMain([&] { + auto* h = reinterpret_cast(hglrc); + if (g_current == hglrc) { + g_current = nullptr; + g_currentDC = nullptr; + } + delete h->context; + delete h; + }); + return TRUE; +} + +BOOL wglMakeCurrent(HDC hdc, HGLRC hglrc) { + return OnMain([&]() -> BOOL { + g_current = hglrc; + g_currentDC = hdc; + if (!hglrc) + return TRUE; + auto* h = reinterpret_cast(hglrc); + GLSurface* s = SurfaceFor(hdc); + if (!s) + s = h->surface; + if (!s || !s->IsShownOnScreen()) + return TRUE; + return s->SetCurrent(*h->context) ? TRUE : FALSE; + }); +} + +HGLRC wglGetCurrentContext() { return g_current; } +HDC wglGetCurrentDC() { return g_currentDC; } + +void* wglGetProcAddress(const char* name) { return dlsym(RTLD_DEFAULT, name); } From 99c18da0dba7fa53b6bee7e3fb684f3b7335b6e6 Mon Sep 17 00:00:00 2001 From: Kamil Marczak Date: Tue, 6 Oct 2026 23:19:05 +0200 Subject: [PATCH 18/46] fix(mfcwx): add CWinApp::OnDDECommand --- port/mfcwx/src/app.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/port/mfcwx/src/app.cpp b/port/mfcwx/src/app.cpp index 197115764..f8af4865d 100644 --- a/port/mfcwx/src/app.cpp +++ b/port/mfcwx/src/app.cpp @@ -661,3 +661,5 @@ CWaitCursor::~CWaitCursor() { if (CWinApp* app = AfxGetApp()) app->DoWaitCursor(-1); } + +BOOL CWinApp::OnDDECommand(LPTSTR) { return FALSE; } From dc3248f24d2726f7fe74ce7b06ff54529b3ad022 Mon Sep 17 00:00:00 2001 From: Kamil Marczak Date: Tue, 6 Oct 2026 23:20:19 +0200 Subject: [PATCH 19/46] build(app): add macOS bundle, Linux install layout and CPack packages The app is built as CrypTool.app on macOS (Homebrew dylibs are copied in by fixup_bundle) and installs to bin/ and share/CrypTool on Linux with a desktop entry and icons. CPack produces a DMG on macOS and tar.gz/deb on Linux. --- port/app/CMakeLists.txt | 64 ++++++++++++++++++++++++++++++++ port/app/Info.plist.in | 43 +++++++++++++++++++++ port/app/cryptool.desktop | 9 +++++ port/app/icons/CrypTool.icns | Bin 0 -> 266713 bytes port/app/icons/cryptool-256.png | Bin 0 -> 23887 bytes port/app/icons/cryptool-48.png | Bin 0 -> 1528 bytes 6 files changed, 116 insertions(+) create mode 100644 port/app/Info.plist.in create mode 100644 port/app/cryptool.desktop create mode 100644 port/app/icons/CrypTool.icns create mode 100644 port/app/icons/cryptool-256.png create mode 100644 port/app/icons/cryptool-48.png diff --git a/port/app/CMakeLists.txt b/port/app/CMakeLists.txt index 98fdfdbe2..ecf4aff66 100644 --- a/port/app/CMakeLists.txt +++ b/port/app/CMakeLists.txt @@ -51,3 +51,67 @@ add_custom_target(cryptool_data ALL COMMENT "Assembling the CrypTool data directory" VERBATIM) add_dependencies(CrypTool cryptool_data) + +# --------------------------------------------------------------------------------------------- +# Installation and packages + +include(GNUInstallDirs) +if(APPLE) + set(MACOSX_BUNDLE_ICON ${CMAKE_CURRENT_SOURCE_DIR}/icons/CrypTool.icns) + set_source_files_properties(${MACOSX_BUNDLE_ICON} PROPERTIES MACOSX_PACKAGE_LOCATION Resources) + target_sources(CrypTool PRIVATE ${MACOSX_BUNDLE_ICON}) + set_target_properties(CrypTool PROPERTIES + MACOSX_BUNDLE TRUE + MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist.in) + set(CRYPTOOL_DATA_DEST CrypTool.app/Contents/Resources) + install(TARGETS CrypTool BUNDLE DESTINATION .) +else() + set(CRYPTOOL_DATA_DEST ${CMAKE_INSTALL_DATADIR}/CrypTool) + install(TARGETS CrypTool RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + install(FILES cryptool.desktop DESTINATION ${CMAKE_INSTALL_DATADIR}/applications) + install(FILES icons/cryptool-256.png DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/256x256/apps RENAME cryptool.png) + install(FILES icons/cryptool-48.png DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/48x48/apps RENAME cryptool.png) +endif() + +mfcwx_install_rc_files(CrypTool DESTINATION ${CRYPTOOL_DATA_DEST}) +foreach(help hlp_en hlp_de) + install(DIRECTORY ${CT_SRC}/${help} DESTINATION ${CRYPTOOL_DATA_DEST} + PATTERN "*.vcproj" EXCLUDE PATTERN "*.vcxproj*" EXCLUDE PATTERN "*.hhp" EXCLUDE) +endforeach() +file(GLOB CT_TEMPLATE_ENTRIES RELATIVE ${REPO_ROOT}/setup/template ${REPO_ROOT}/setup/template/*) +foreach(entry IN LISTS CT_TEMPLATE_ENTRIES) + string(TOLOWER "${entry}" lower) + if(lower MATCHES "\\.(dll|manifest)$" OR lower STREQUAL "pse") + continue() + endif() + if(IS_DIRECTORY ${REPO_ROOT}/setup/template/${entry}) + install(DIRECTORY ${REPO_ROOT}/setup/template/${entry} DESTINATION ${CRYPTOOL_DATA_DEST}) + else() + install(FILES ${REPO_ROOT}/setup/template/${entry} DESTINATION ${CRYPTOOL_DATA_DEST}) + endif() +endforeach() +install(DIRECTORY ${CRYPTOOL_DEV_DATA_DIR}/PSE DESTINATION ${CRYPTOOL_DATA_DEST} OPTIONAL) + +if(APPLE) + # Copies the Homebrew dylibs (wxWidgets, OpenSSL, GMP) into the bundle so it runs on other Macs. + install(CODE " + include(BundleUtilities) + fixup_bundle(\"\${CMAKE_INSTALL_PREFIX}/CrypTool.app\" \"\" \"\") + ") +endif() + +set(CPACK_PACKAGE_NAME CrypTool) +set(CPACK_PACKAGE_VENDOR "CrypTool project") +set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "CrypTool 1 - learn and try out cryptography and cryptanalysis") +set(CPACK_PACKAGE_VERSION ${PROJECT_VERSION}) +set(CPACK_RESOURCE_FILE_LICENSE ${REPO_ROOT}/COPYING) +if(APPLE) + set(CPACK_GENERATOR DragNDrop) + set(CPACK_DMG_VOLUME_NAME "CrypTool ${PROJECT_VERSION}") +else() + set(CPACK_GENERATOR TGZ DEB) + set(CPACK_DEBIAN_PACKAGE_MAINTAINER "CrypTool project") + set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON) + set(CPACK_DEBIAN_PACKAGE_SECTION education) +endif() +include(CPack) diff --git a/port/app/Info.plist.in b/port/app/Info.plist.in new file mode 100644 index 000000000..e36da4ed2 --- /dev/null +++ b/port/app/Info.plist.in @@ -0,0 +1,43 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + CrypTool + CFBundleIconFile + CrypTool.icns + CFBundleIdentifier + org.cryptool.ct1 + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + CrypTool + CFBundleDisplayName + CrypTool 1 + CFBundlePackageType + APPL + CFBundleShortVersionString + ${PROJECT_VERSION} + CFBundleVersion + ${PROJECT_VERSION} + LSMinimumSystemVersion + 11.0 + NSHighResolutionCapable + + NSPrincipalClass + NSApplication + CFBundleDocumentTypes + + + CFBundleTypeRole + Editor + LSItemContentTypes + + public.data + + + + + diff --git a/port/app/cryptool.desktop b/port/app/cryptool.desktop new file mode 100644 index 000000000..54d857734 --- /dev/null +++ b/port/app/cryptool.desktop @@ -0,0 +1,9 @@ +[Desktop Entry] +Type=Application +Name=CrypTool 1 +GenericName=Cryptography learning tool +Comment=Learn and try out cryptography and cryptanalysis +Exec=CrypTool %F +Icon=cryptool +Terminal=false +Categories=Education;Security;Utility; diff --git a/port/app/icons/CrypTool.icns b/port/app/icons/CrypTool.icns new file mode 100644 index 0000000000000000000000000000000000000000..351f358fe490701ba3825f01e7d6e7f36fb3c85e GIT binary patch literal 266713 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�HV-CieEeK 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 eTM@eDBJ?l0leb9=E~#Sx0000 Date: Tue, 6 Oct 2026 23:22:03 +0200 Subject: [PATCH 20/46] feat(tools): add a dialog gallery that snapshots every dialog template Also provides the MS CRT globals __argc/__argv. --- CMakeLists.txt | 5 ++ port/mfcwx/include/mfcwx/crtcompat.h | 2 + port/mfcwx/src/app.cpp | 12 ++++ port/tools/gallery/CMakeLists.txt | 11 ++++ port/tools/gallery/gallery.cpp | 88 ++++++++++++++++++++++++++++ port/tools/gallery/snapshot_gtk.cpp | 21 +++++++ port/tools/gallery/snapshot_mac.mm | 22 +++++++ port/tools/gallery/stdafx_gallery.h | 4 ++ 8 files changed, 165 insertions(+) create mode 100644 port/tools/gallery/CMakeLists.txt create mode 100644 port/tools/gallery/gallery.cpp create mode 100644 port/tools/gallery/snapshot_gtk.cpp create mode 100644 port/tools/gallery/snapshot_mac.mm create mode 100644 port/tools/gallery/stdafx_gallery.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 19e038876..bee6c0c91 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,3 +34,8 @@ if(TARGET secude_compat) endif() add_subdirectory(port/mfcwx) add_subdirectory(port/app) + +option(CRYPTOOL_DIALOG_GALLERY "Build the dialog snapshot tool" OFF) +if(CRYPTOOL_DIALOG_GALLERY) + add_subdirectory(port/tools/gallery) +endif() diff --git a/port/mfcwx/include/mfcwx/crtcompat.h b/port/mfcwx/include/mfcwx/crtcompat.h index 81337a5dd..c6c970e43 100644 --- a/port/mfcwx/include/mfcwx/crtcompat.h +++ b/port/mfcwx/include/mfcwx/crtcompat.h @@ -261,6 +261,8 @@ extern "C" { #define _tmain main #define _tWinMain WinMain +extern int __argc; +extern char** __argv; int mfcwx_open(const char* name, int flags, ...); int mfcwx_memicmp(const void* a, const void* b, size_t n); int mfcwx_unlink(const char* name); diff --git a/port/mfcwx/src/app.cpp b/port/mfcwx/src/app.cpp index f8af4865d..d8dbe4fa9 100644 --- a/port/mfcwx/src/app.cpp +++ b/port/mfcwx/src/app.cpp @@ -9,6 +9,9 @@ #include #include +int __argc = 0; +char** __argv = nullptr; + namespace mfcwx { CWinApp*& AppInstance() { @@ -92,6 +95,15 @@ bool MfcWxApp::OnInit() { if (!app) return false; PrepareEnvironment(argc, argv); + static std::vector ansiArgs; + static std::vector argPointers; + for (int i = 0; i < argc; ++i) + ansiArgs.push_back(FromWx(wxString(argv[i]))); + for (std::string& a : ansiArgs) + argPointers.push_back(&a[0]); + argPointers.push_back(nullptr); + __argc = argc; + __argv = argPointers.data(); static std::string cmdLine = ArgsToCommandLine(argc, argv); app->m_lpCmdLine = &cmdLine[0]; app->m_nCmdShow = SW_SHOWNORMAL; diff --git a/port/tools/gallery/CMakeLists.txt b/port/tools/gallery/CMakeLists.txt new file mode 100644 index 000000000..2fb0b7ef5 --- /dev/null +++ b/port/tools/gallery/CMakeLists.txt @@ -0,0 +1,11 @@ +include(${REPO_ROOT}/port/rc/rc2cpp.cmake) + +add_executable(dialog_gallery gallery.cpp $,snapshot_mac.mm,snapshot_gtk.cpp>) +mfcwx_add_rc(dialog_gallery RC ${REPO_ROOT}/CrypTool/CrypTool.rc MODULE CrypTool HEADERS ${REPO_ROOT}/CrypTool/resource.h + OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/rc) +target_link_libraries(dialog_gallery PRIVATE mfcwx) +target_compile_definitions(dialog_gallery PRIVATE CRYPTOOL_DEV_DATA_DIR="${CMAKE_BINARY_DIR}/data") +if(APPLE) + enable_language(OBJCXX) + target_link_libraries(dialog_gallery PRIVATE "-framework Cocoa") +endif() diff --git a/port/tools/gallery/gallery.cpp b/port/tools/gallery/gallery.cpp new file mode 100644 index 000000000..d8847f0c3 --- /dev/null +++ b/port/tools/gallery/gallery.cpp @@ -0,0 +1,88 @@ +// Opens every dialog template of CrypTool.rc in a language and saves a PNG snapshot of each. +// dialog_gallery [--lang de] [--out dir] [--only 101,102] + +#include "stdafx_gallery.h" + +#include "mfcwx/app.h" +#include "mfcwx/rcdata.h" + +#include +#include + +extern const mfcwx::rc::Module g_rcModule_CrypTool; +bool SnapshotWindow(HWND hWnd, const std::string& pngPath); + +namespace { + +struct RegisterModule { + RegisterModule() { + mfcwx::RegisterResourceModule(&g_rcModule_CrypTool); + mfcwx::SetDefaultDataDirectory(CRYPTOOL_DEV_DATA_DIR); + } +} registerModule; + +class GalleryDialog : public CDialog { +public: + explicit GalleryDialog(UINT id) : CDialog(id) {} + BOOL OnInitDialog() override { return TRUE; } +}; + +class GalleryApp : public CWinApp { +public: + BOOL InitInstance() override; +}; + +GalleryApp theApp; + +BOOL GalleryApp::InitInstance() { + std::string lang = "en"; + std::string out = "gallery"; + std::set only; + for (int i = 1; i + 1 < __argc; ++i) { + std::string a = __argv[i]; + if (a == "--lang") + lang = __argv[++i]; + else if (a == "--out") + out = __argv[++i]; + else if (a == "--only") { + std::string list = __argv[++i]; + size_t pos = 0; + while (pos < list.size()) { + size_t comma = list.find(',', pos); + only.insert(atoi(list.substr(pos, comma - pos).c_str())); + pos = comma == std::string::npos ? list.size() : comma + 1; + } + } + } + mfcwx::SetResourceLanguage(lang.c_str()); + _mkdir(out.c_str()); + const mfcwx::rc::Language* language = nullptr; + for (int i = 0; i < g_rcModule_CrypTool.languageCount; ++i) + if (lang == g_rcModule_CrypTool.languages[i]->code) + language = g_rcModule_CrypTool.languages[i]; + if (!language) + return FALSE; + int saved = 0; + for (int i = 0; i < language->dialogCount; ++i) { + const mfcwx::rc::Dialog& d = language->dialogs[i]; + if (!d.id || (!only.empty() && !only.count(d.id))) + continue; + GalleryDialog dlg(static_cast(d.id)); + if (!dlg.Create(static_cast(d.id), nullptr)) + continue; + dlg.ShowWindow(SW_SHOW); + dlg.UpdateWindow(); + MSG msg; + for (int k = 0; k < 5; ++k) + PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE); + char path[1024]; + snprintf(path, sizeof path, "%s/%s_%05d_%s.png", out.c_str(), lang.c_str(), d.id, d.name ? d.name : ""); + if (SnapshotWindow(dlg.m_hWnd, path)) + ++saved; + dlg.DestroyWindow(); + } + printf("saved %d dialog snapshots to %s\n", saved, out.c_str()); + return FALSE; +} + +} // namespace diff --git a/port/tools/gallery/snapshot_gtk.cpp b/port/tools/gallery/snapshot_gtk.cpp new file mode 100644 index 000000000..b9657868c --- /dev/null +++ b/port/tools/gallery/snapshot_gtk.cpp @@ -0,0 +1,21 @@ +#include + +#include +#include +#include + +#include "afxwin.h" + +bool SnapshotWindow(HWND hWnd, const std::string& pngPath) { + CWnd* wnd = CWnd::FromHandle(hWnd); + if (!wnd) + return false; + wxWindow* w = wnd->GetWx(); + wxClientDC src(w); + wxSize size = w->GetClientSize(); + wxBitmap bmp(size); + wxMemoryDC dst(bmp); + dst.Blit(0, 0, size.x, size.y, &src, 0, 0); + dst.SelectObject(wxNullBitmap); + return bmp.SaveFile(wxString::FromUTF8(pngPath.c_str()), wxBITMAP_TYPE_PNG); +} diff --git a/port/tools/gallery/snapshot_mac.mm b/port/tools/gallery/snapshot_mac.mm new file mode 100644 index 000000000..5ca865e45 --- /dev/null +++ b/port/tools/gallery/snapshot_mac.mm @@ -0,0 +1,22 @@ +#include + +#import + +#include "afxwin.h" + +bool SnapshotWindow(HWND hWnd, const std::string& pngPath) { + CWnd* wnd = CWnd::FromHandle(hWnd); + if (!wnd) + return false; + NSView* view = (NSView*)wnd->GetWx()->GetHandle(); + NSView* content = [[view window] contentView]; + if (!content) + content = view; + [content displayIfNeeded]; + NSBitmapImageRep* rep = [content bitmapImageRepForCachingDisplayInRect:content.bounds]; + if (!rep) + return false; + [content cacheDisplayInRect:content.bounds toBitmapImageRep:rep]; + NSData* png = [rep representationUsingType:NSBitmapImageFileTypePNG properties:@{}]; + return [png writeToFile:[NSString stringWithUTF8String:pngPath.c_str()] atomically:YES]; +} diff --git a/port/tools/gallery/stdafx_gallery.h b/port/tools/gallery/stdafx_gallery.h new file mode 100644 index 000000000..3cc65b958 --- /dev/null +++ b/port/tools/gallery/stdafx_gallery.h @@ -0,0 +1,4 @@ +#pragma once +#include "afxwin.h" +#include "afxext.h" +#include "afxcmn.h" From 3574e5cfb066ec31a3964494de902d0b7bbb1e37 Mon Sep 17 00:00:00 2001 From: Kamil Marczak Date: Tue, 6 Oct 2026 23:26:19 +0200 Subject: [PATCH 21/46] feat(mfcwx): complete the non-GUI runtime Kernel objects and waits, threads and processes, files and directories, INI files and registry emulation, CRT extensions, CObject/exceptions, CFile/CArchive, sync classes and CWinThread, with 384 passing runtime checks on macOS and Linux. --- port/mfcwx/CMakeLists.txt | 24 + port/mfcwx/include/mfcwx/winconst.h | 10 + port/mfcwx/src/crt.cpp | 49 ++ port/mfcwx/src/file.cpp | 69 ++ port/mfcwx/src/profile.cpp | 6 +- port/mfcwx/src/runtime.h | 3 + port/mfcwx/src/win32.cpp | 72 ++- port/mfcwx/tests/gdi_test_stubs.cpp | 53 ++ port/mfcwx/tests/gdi_tests.cpp | 913 +++++++++++++++++++++++++++ port/mfcwx/tests/runtime_tests.cpp | 26 +- port/mfcwx/tests/scintilla_tests.cpp | 674 ++++++++++++++++++++ 11 files changed, 1884 insertions(+), 15 deletions(-) create mode 100644 port/mfcwx/tests/gdi_test_stubs.cpp create mode 100644 port/mfcwx/tests/gdi_tests.cpp create mode 100644 port/mfcwx/tests/scintilla_tests.cpp diff --git a/port/mfcwx/CMakeLists.txt b/port/mfcwx/CMakeLists.txt index 557c081fe..7b2849fb2 100644 --- a/port/mfcwx/CMakeLists.txt +++ b/port/mfcwx/CMakeLists.txt @@ -29,4 +29,28 @@ if(MFCWX_TESTS) target_compile_options(mfcwx_runtime_tests PRIVATE ${wxWidgets_CXX_FLAGS}) target_link_libraries(mfcwx_runtime_tests PRIVATE ${wxWidgets_LIBRARIES} Threads::Threads ${CMAKE_DL_LIBS}) add_test(NAME mfcwx_runtime_tests COMMAND mfcwx_runtime_tests) + + # GDI tests: render through CDC into bitmaps, check pixels and write PNGs for inspection. + set(MFCWX_GDI_SOURCES gdi.cpp gdi_bits.cpp gdi_dc.cpp gdi_text.cpp codepage.cpp codepage_tables.cpp cstring.cpp) + list(TRANSFORM MFCWX_GDI_SOURCES PREPEND ${CMAKE_CURRENT_SOURCE_DIR}/src/) + add_executable(mfcwx_gdi_tests tests/gdi_tests.cpp tests/gdi_test_stubs.cpp ${MFCWX_GDI_SOURCES}) + target_include_directories(mfcwx_gdi_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include ${CMAKE_CURRENT_SOURCE_DIR}/src) + target_include_directories(mfcwx_gdi_tests SYSTEM PRIVATE ${wxWidgets_INCLUDE_DIRS}) + target_compile_features(mfcwx_gdi_tests PRIVATE cxx_std_14) + target_compile_definitions(mfcwx_gdi_tests PRIVATE ${wxWidgets_DEFINITIONS} MFCWX=1) + target_compile_options(mfcwx_gdi_tests PRIVATE ${wxWidgets_CXX_FLAGS}) + target_link_libraries(mfcwx_gdi_tests PRIVATE ${wxWidgets_LIBRARIES} Threads::Threads ${CMAKE_DL_LIBS}) + add_test(NAME mfcwx_gdi_tests COMMAND mfcwx_gdi_tests ${CMAKE_CURRENT_BINARY_DIR}/gdi_test_output) + + # Scintilla proxy; tests/scintilla_tests.cpp records the notifications it sends. + add_executable(mfcwx_scintilla_tests tests/scintilla_tests.cpp src/scintilla.cpp src/codepage.cpp + src/codepage_tables.cpp) + target_include_directories(mfcwx_scintilla_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include + ${CMAKE_CURRENT_SOURCE_DIR}/src) + target_include_directories(mfcwx_scintilla_tests SYSTEM PRIVATE ${wxWidgets_INCLUDE_DIRS}) + target_compile_features(mfcwx_scintilla_tests PRIVATE cxx_std_14) + target_compile_definitions(mfcwx_scintilla_tests PRIVATE ${wxWidgets_DEFINITIONS} MFCWX=1) + target_compile_options(mfcwx_scintilla_tests PRIVATE ${wxWidgets_CXX_FLAGS}) + target_link_libraries(mfcwx_scintilla_tests PRIVATE ${wxWidgets_LIBRARIES} Threads::Threads) + add_test(NAME mfcwx_scintilla_tests COMMAND mfcwx_scintilla_tests) endif() diff --git a/port/mfcwx/include/mfcwx/winconst.h b/port/mfcwx/include/mfcwx/winconst.h index a20702aa8..c3a9b4e82 100644 --- a/port/mfcwx/include/mfcwx/winconst.h +++ b/port/mfcwx/include/mfcwx/winconst.h @@ -886,6 +886,9 @@ #define SBM_GETPOS 0x00E1 #define SBM_SETRANGE 0x00E2 #define SBM_GETRANGE 0x00E3 +#define SBM_SETRANGEREDRAW 0x00E6 +#define SBM_SETSCROLLINFO 0x00E9 +#define SBM_GETSCROLLINFO 0x00EA /* trackbar */ #define TBM_GETPOS (WM_USER) @@ -1750,6 +1753,13 @@ #define GMEM_SHARE 0x2000 #define GHND (GMEM_MOVEABLE | GMEM_ZEROINIT) #define GPTR (GMEM_FIXED | GMEM_ZEROINIT) +#define GMEM_NODISCARD 0x0020 +#define GMEM_MODIFY 0x0080 +#define LMEM_FIXED 0x0000 +#define LMEM_MOVEABLE 0x0002 +#define LMEM_ZEROINIT 0x0040 +#define LPTR (LMEM_FIXED | LMEM_ZEROINIT) +#define LHND (LMEM_MOVEABLE | LMEM_ZEROINIT) /* help */ #define HELP_CONTEXT 0x0001L diff --git a/port/mfcwx/src/crt.cpp b/port/mfcwx/src/crt.cpp index dda026c24..e9353cd5f 100644 --- a/port/mfcwx/src/crt.cpp +++ b/port/mfcwx/src/crt.cpp @@ -1,4 +1,5 @@ #include "afx.h" +#include "process.h" #include "runtime.h" #include @@ -799,3 +800,51 @@ int _findclose(intptr_t handle) { } return 0; } + +int mfcwx_memicmp(const void* a, const void* b, size_t n) { + const unsigned char* p = static_cast(a); + const unsigned char* q = static_cast(b); + for (size_t i = 0; i < n; ++i) { + int d = tolower(p[i]) - tolower(q[i]); + if (d) + return d; + } + return 0; +} + +intptr_t _spawnl(int mode, const char* cmdname, const char* arg0, ...) { + if (!cmdname) { + errno = EINVAL; + return -1; + } + // Like the Microsoft CRT, the arguments are joined with spaces into one command line. + std::string cmd = arg0 ? arg0 : cmdname; + va_list ap; + va_start(ap, arg0); + if (arg0) + for (const char* a = va_arg(ap, const char*); a; a = va_arg(ap, const char*)) + cmd += std::string(" ") + a; + va_end(ap); + STARTUPINFO si; + memset(&si, 0, sizeof si); + si.cb = sizeof si; + PROCESS_INFORMATION pi; + if (!CreateProcessA(cmdname, &cmd[0], nullptr, nullptr, TRUE, 0, nullptr, nullptr, &si, &pi)) { + errno = GetLastError() == ERROR_FILE_NOT_FOUND ? ENOENT : ENOEXEC; + return -1; + } + CloseHandle(pi.hThread); + if (mode == _P_WAIT) { + DWORD code = 0; + WaitForSingleObject(pi.hProcess, INFINITE); + GetExitCodeProcess(pi.hProcess, &code); + CloseHandle(pi.hProcess); + return static_cast(code); + } + if (mode == _P_OVERLAY) + exit(0); + if (mode == _P_NOWAIT || mode == _P_NOWAITO) + return reinterpret_cast(pi.hProcess); + CloseHandle(pi.hProcess); + return 0; +} diff --git a/port/mfcwx/src/file.cpp b/port/mfcwx/src/file.cpp index 540e48f23..84bf7d854 100644 --- a/port/mfcwx/src/file.cpp +++ b/port/mfcwx/src/file.cpp @@ -874,3 +874,72 @@ BOOL GetDiskFreeSpaceExA(LPCSTR dir, PULARGE_INTEGER freeToCaller, PULARGE_INTEG totalFree->QuadPart = unit * vfs.f_bfree; return TRUE; } + +DWORD SearchPathA(const char* lpPath, const char* lpFileName, const char* lpExtension, DWORD nBufferLength, + char* lpBuffer, char** lpFilePart) { + if (!lpFileName || !*lpFileName) { + SetLastError(87); + return 0; + } + std::string name = lpFileName; + size_t slash = name.find_last_of("\\/"); + if (lpExtension && *lpExtension && name.find('.', slash == std::string::npos ? 0 : slash) == std::string::npos) + name += lpExtension; + std::vector dirs; + bool absolute = name[0] == '\\' || name[0] == '/' || (name.size() > 1 && name[1] == ':'); + if (absolute) { + dirs.push_back(std::string()); + } else if (lpPath && *lpPath) { + std::string list = lpPath; + for (size_t start = 0; start <= list.size();) { + size_t sep = list.find(';', start); + std::string dir = list.substr(start, sep == std::string::npos ? std::string::npos : sep - start); + if (!dir.empty()) + dirs.push_back(dir); + if (sep == std::string::npos) + break; + start = sep + 1; + } + } else { + std::string exe = ExecutablePath(); + size_t s = exe.rfind('/'); + if (s != std::string::npos) + dirs.push_back(AppPath(exe.substr(0, s).c_str())); + dirs.push_back(AppPath(CurrentDirectory().c_str())); + dirs.push_back(AppPath(GetDataDirectory().c_str())); + const char* env = getenv("PATH"); + std::string list = env ? env : ""; + for (size_t start = 0; start <= list.size();) { + size_t sep = list.find(':', start); + std::string dir = list.substr(start, sep == std::string::npos ? std::string::npos : sep - start); + if (!dir.empty()) + dirs.push_back(AppPath(dir.c_str())); + if (sep == std::string::npos) + break; + start = sep + 1; + } + } + for (const std::string& dir : dirs) { + std::string candidate = dir.empty() ? name : dir + "\\" + name; + struct stat st; + if (stat(FsPath(candidate.c_str()).c_str(), &st) != 0 || S_ISDIR(st.st_mode)) + continue; + std::string full = FullAppPath(candidate.c_str()); + DWORD r = CopyResult(full, lpBuffer, nBufferLength); + if (lpFilePart) { + *lpFilePart = nullptr; + if (lpBuffer && r < nBufferLength) { + char* sep = strrchr(lpBuffer, '\\'); + *lpFilePart = sep ? sep + 1 : lpBuffer; + } + } + return r; + } + SetLastError(ERROR_FILE_NOT_FOUND); + return 0; +} + +DWORD SearchPath(const char* lpPath, const char* lpFileName, const char* lpExtension, DWORD nBufferLength, + char* lpBuffer, char** lpFilePart) { + return SearchPathA(lpPath, lpFileName, lpExtension, nBufferLength, lpBuffer, lpFilePart); +} diff --git a/port/mfcwx/src/profile.cpp b/port/mfcwx/src/profile.cpp index 97338ee89..4eefcc7ed 100644 --- a/port/mfcwx/src/profile.cpp +++ b/port/mfcwx/src/profile.cpp @@ -485,7 +485,7 @@ BOOL WritePrivateProfileStringA(LPCSTR app, LPCSTR key, LPCSTR value, LPCSTR fil insertAt = i + 1; if (KeyValue(ini.lines[i], k, v) && strcasecmp(k.c_str(), key) == 0) { if (value) - ini.lines[i] = entry; + ini.lines[i] = k + "=" + value; else ini.lines.erase(ini.lines.begin() + static_cast(i)); return SaveIni(path, ini) ? TRUE : FALSE; @@ -707,10 +707,12 @@ LONG CRegKey::QueryDWORDValue(const char* pszValueName, DWORD& dwValue) { DWORD size = sizeof(DWORD); DWORD value = 0; LONG r = RegQueryValueExA(m_hKey, pszValueName, nullptr, &type, reinterpret_cast(&value), &size); - if (r != ERROR_SUCCESS) + if (r != ERROR_SUCCESS && r != ERROR_MORE_DATA) return r; if (type != REG_DWORD) return 13; + if (r != ERROR_SUCCESS) + return r; dwValue = value; return ERROR_SUCCESS; } diff --git a/port/mfcwx/src/runtime.h b/port/mfcwx/src/runtime.h index f3b70bf0a..930094624 100644 --- a/port/mfcwx/src/runtime.h +++ b/port/mfcwx/src/runtime.h @@ -33,6 +33,9 @@ std::string AbsoluteNativePath(const char* appPath); std::string FullAppPath(const char* appPath); std::string ExecutablePath(); +// LoadLibrary/GetModuleHandle succeed for this DLL base name (case-insensitive) with a pseudo module +// whose GetProcAddress returns NULL; used for libraries built into the toolkit (Scintilla). +void RegisterBuiltinModule(const char* baseName); std::string TempDirectory(); std::string UserConfigDirectory(); void SetCommandLineOverride(const char* commandLine); diff --git a/port/mfcwx/src/win32.cpp b/port/mfcwx/src/win32.cpp index 1a9b0b2a5..5554694ad 100644 --- a/port/mfcwx/src/win32.cpp +++ b/port/mfcwx/src/win32.cpp @@ -483,6 +483,33 @@ std::vector& Modules() { char g_mainModuleTag; +std::vector& BuiltinNames() { + static auto* v = new std::vector{"scilexer.dll", "scintilla.dll"}; + return *v; +} + +std::string BaseName(const char* path) { + std::string s = path ? path : ""; + size_t slash = s.find_last_of("\\/"); + return slash == std::string::npos ? s : s.substr(slash + 1); +} + +// Must be called with ModuleLock held. +Module* BuiltinModuleLocked(const char* name) { + std::string base = BaseName(name); + bool known = false; + for (const std::string& b : BuiltinNames()) + known = known || strcasecmp(b.c_str(), base.c_str()) == 0; + if (!known) + return nullptr; + for (Module* m : Modules()) + if (!m->dl && strcasecmp(m->path.c_str(), base.c_str()) == 0) + return m; + auto* m = new Module{nullptr, base, 0}; + Modules().push_back(m); + return m; +} + Module* FindModuleLocked(HMODULE h) { for (Module* m : Modules()) if (reinterpret_cast(m) == h) @@ -644,6 +671,16 @@ HGLOBAL GlobalFromData(const void* data, size_t size, UINT flags) { bool GlobalIsValid(HGLOBAL h) { return GlobalHeaderOf(h) != nullptr; } +void RegisterBuiltinModule(const char* baseName) { + if (!baseName || !*baseName) + return; + std::lock_guard lk(ModuleLock()); + for (const std::string& b : BuiltinNames()) + if (strcasecmp(b.c_str(), baseName) == 0) + return; + BuiltinNames().push_back(baseName); +} + void SetCommandLineOverride(const char* commandLine) { std::lock_guard lk(CommandLineLock()); CommandLineStore() = commandLine ? commandLine : ""; @@ -713,15 +750,12 @@ BOOL SystemTimeToFileTime(const SYSTEMTIME* st, LPFILETIME ft) { SetLastError(87); return FALSE; } - struct tm tm; - memset(&tm, 0, sizeof tm); - tm.tm_year = st->wYear - 1900; - tm.tm_mon = st->wMonth - 1; - tm.tm_mday = st->wDay; - tm.tm_hour = st->wHour; - tm.tm_min = st->wMinute; - tm.tm_sec = st->wSecond; - time_t t = timegm(&tm); + long long y = st->wYear - (st->wMonth <= 2); + long long era = (y >= 0 ? y : y - 399) / 400; + long long yoe = y - era * 400; + long long doy = (153 * (st->wMonth > 2 ? st->wMonth - 3 : st->wMonth + 9) + 2) / 5 + st->wDay - 1; + long long days = era * 146097 + yoe * 365 + yoe / 4 - yoe / 100 + doy - 719468; + time_t t = static_cast(days * 86400 + st->wHour * 3600 + st->wMinute * 60 + st->wSecond); *ft = MakeFileTime(t, static_cast(st->wMilliseconds) * 1000000L); return TRUE; } @@ -1171,6 +1205,13 @@ HMODULE LoadLibraryExA(LPCSTR name, HANDLE, DWORD) { SetLastError(87); return nullptr; } + { + std::lock_guard lk(ModuleLock()); + if (Module* m = BuiltinModuleLocked(name)) { + ++m->refs; + return reinterpret_cast(m); + } + } std::string path = FsPath(name); void* dl = dlopen(path.c_str(), RTLD_NOW | RTLD_LOCAL); if (!dl) { @@ -1196,6 +1237,11 @@ BOOL FreeLibrary(HMODULE mod) { SetLastError(ERROR_INVALID_HANDLE); return FALSE; } + if (!m->dl) { + if (m->refs > 0) + --m->refs; + return TRUE; + } if (--m->refs == 0) { dlclose(m->dl); Modules().erase(std::find(Modules().begin(), Modules().end(), m)); @@ -1213,6 +1259,10 @@ FARPROC GetProcAddress(HMODULE mod, LPCSTR name) { { std::lock_guard lk(ModuleLock()); Module* m = FindModuleLocked(mod); + if (m && !m->dl) { + SetLastError(127); + return nullptr; + } dl = m ? m->dl : RTLD_DEFAULT; } void* sym = dlsym(dl, name); @@ -1231,6 +1281,8 @@ HMODULE GetModuleHandleA(LPCSTR name) { if (slash != std::string::npos) wanted = wanted.substr(slash + 1); std::lock_guard lk(ModuleLock()); + if (Module* m = BuiltinModuleLocked(name)) + return reinterpret_cast(m); for (Module* m : Modules()) { size_t s = m->path.rfind('/'); std::string base = m->path.substr(s == std::string::npos ? 0 : s + 1); @@ -1252,7 +1304,7 @@ DWORD GetModuleFileNameA(HMODULE mod, LPSTR buffer, DWORD size) { { std::lock_guard lk(ModuleLock()); if (Module* m = FindModuleLocked(mod)) - path = m->path; + path = m->dl ? m->path : std::string(); } if (path.empty()) path = ExecutablePath(); diff --git a/port/mfcwx/tests/gdi_test_stubs.cpp b/port/mfcwx/tests/gdi_test_stubs.cpp new file mode 100644 index 000000000..7387415bd --- /dev/null +++ b/port/mfcwx/tests/gdi_test_stubs.cpp @@ -0,0 +1,53 @@ +// Stand-ins for symbols of other mfcwx parts so the GDI tests link with the GDI sources only. + +#include "internal.h" + +#include + +AFX_CLASSINIT::AFX_CLASSINIT(CRuntimeClass*) {} + +CRuntimeClass CObject::classCObject = {"CObject", sizeof(CObject), 0xFFFF, nullptr, nullptr, nullptr}; +CRuntimeClass* CObject::GetThisClass() { return &classCObject; } +CRuntimeClass* CObject::GetRuntimeClass() const { return &classCObject; } +void CObject::Serialize(CArchive&) {} + +BOOL CRuntimeClass::IsDerivedFrom(const CRuntimeClass* pBaseClass) const { + for (const CRuntimeClass* c = this; c; c = c->GetBaseClass()) + if (c == pBaseClass) + return TRUE; + return FALSE; +} + +BOOL CObject::IsKindOf(const CRuntimeClass* pClass) const { return GetRuntimeClass()->IsDerivedFrom(pClass); } + +void AfxAssertFailedLine(const char* file, int line, const char* expr) { + fprintf(stderr, "ASSERT failed: %s:%d %s\n", file, line, expr ? expr : ""); +} + +void AfxDebugBreak() {} + +CWnd* CWnd::FromHandle(HWND) { return nullptr; } + +namespace mfcwx { + +bool LoadResourceString(UINT, std::string&) { return false; } + +ResRef ResRef::From(const char* lpszName) { + ResRef r; + if (IS_INTRESOURCE(lpszName)) + r.id = static_cast(reinterpret_cast(lpszName)); + else if (lpszName) + r.name = lpszName; + return r; +} + +wxBitmap LoadBitmapResource(const ResRef& ref) { + if (ref.id != 1 && ref.name != "TEST") + return wxNullBitmap; + wxImage img(4, 2, true); + img.SetRGB(0, 0, 255, 0, 0); + img.SetRGB(3, 1, 0, 0, 255); + return wxBitmap(img); +} + +} // namespace mfcwx diff --git a/port/mfcwx/tests/gdi_tests.cpp b/port/mfcwx/tests/gdi_tests.cpp new file mode 100644 index 000000000..a8e97402e --- /dev/null +++ b/port/mfcwx/tests/gdi_tests.cpp @@ -0,0 +1,913 @@ +// Visual and pixel tests of the GDI emulation. Renders through CDC into bitmaps, checks key +// pixels and writes PNGs (argv[1] or ./gdi_test_output) for inspection. +// Pass --no-window to skip the tests that create a frame (e.g. on a headless CI machine). + +#include "gdi_internal.h" + +#include + +#include +#include +#include + +using namespace mfcwx; + +namespace { + +int g_checks = 0; +int g_failures = 0; +wxString g_out = "gdi_test_output"; +bool g_windowTests = true; + +#define CHECK(cond, ...) \ + do { \ + ++g_checks; \ + if (!(cond)) { \ + ++g_failures; \ + printf("FAIL %s:%d: %s -- ", __FILE__, __LINE__, #cond); \ + printf(__VA_ARGS__); \ + printf("\n"); \ + } \ + } while (0) + +const COLORREF kWhite = RGB(255, 255, 255); +const COLORREF kBlack = RGB(0, 0, 0); +const COLORREF kRed = RGB(255, 0, 0); +const COLORREF kGreen = RGB(0, 255, 0); +const COLORREF kBlue = RGB(0, 0, 255); +const COLORREF kYellow = RGB(255, 255, 0); + +bool Near(COLORREF a, COLORREF b, int tol = 10) { + return std::abs(GetRValue(a) - GetRValue(b)) <= tol && std::abs(GetGValue(a) - GetGValue(b)) <= tol && + std::abs(GetBValue(a) - GetBValue(b)) <= tol; +} + +bool Dark(COLORREF c) { return GetRValue(c) + GetGValue(c) + GetBValue(c) < 384; } + +struct Box { + int left = 1 << 30, top = 1 << 30, right = -1, bottom = -1; + bool Empty() const { return right < 0; } +}; + +struct Canvas { + CDC dc; + CBitmap bmp; + CBitmap* old = nullptr; + int w, h; + Canvas(int width, int height, CDC* compat = nullptr) : w(width), h(height) { + dc.CreateCompatibleDC(compat); + bmp.CreateCompatibleBitmap(compat ? compat : &dc, w, h); + old = dc.SelectObject(&bmp); + dc.FillSolidRect(0, 0, w, h, kWhite); + } + ~Canvas() { + if (old) + dc.SelectObject(old); + } + COLORREF Px(int x, int y) { return dc.GetPixel(x, y); } + Box DarkBox(int l, int t, int r, int b) { + Box box; + for (int y = t; y < b; ++y) + for (int x = l; x < r; ++x) + if (Dark(Px(x, y))) { + box.left = std::min(box.left, x); + box.top = std::min(box.top, y); + box.right = std::max(box.right, x); + box.bottom = std::max(box.bottom, y); + } + return box; + } + void Save(const char* name) { + wxBitmap* b = BitmapFromHandle(bmp); + if (b) + b->SaveFile(g_out + "/" + name + ".png", wxBITMAP_TYPE_PNG); + } +}; + +void TestShapes() { + Canvas c(240, 160); + CPen pen(PS_SOLID, 1, kBlack); + CBrush red(kRed); + CPen* oldPen = c.dc.SelectObject(&pen); + CBrush* oldBrush = c.dc.SelectObject(&red); + CHECK(oldPen && oldBrush, "SelectObject returns the default objects"); + + c.dc.Rectangle(10, 10, 20, 20); + CHECK(Near(c.Px(10, 10), kBlack, 2) && c.Px(15, 10) == kBlack && Near(c.Px(19, 19), kBlack, 2) && + c.Px(10, 15) == kBlack && c.Px(19, 15) == kBlack && c.Px(15, 19) == kBlack, + "rectangle outline covers [left,right-1] (%06x %06x)", c.Px(10, 10), c.Px(19, 19)); + CHECK(c.Px(20, 15) == kWhite && c.Px(15, 20) == kWhite, "rectangle excludes right/bottom (%06x %06x)", + c.Px(20, 15), c.Px(15, 20)); + CHECK(c.Px(15, 15) == kRed && c.Px(11, 11) == kRed, "rectangle fill (%06x)", c.Px(15, 15)); + + CBrush blue(kBlue); + RECT fr{30, 10, 40, 20}; + c.dc.FillRect(&fr, &blue); + CHECK(c.Px(30, 10) == kBlue && c.Px(39, 19) == kBlue, "FillRect covers [left,right) (%06x)", c.Px(39, 19)); + CHECK(c.Px(40, 15) == kWhite && c.Px(35, 20) == kWhite && c.Px(29, 15) == kWhite, "FillRect excludes right/bottom"); + + CBrush green(kGreen); + c.dc.SelectStockObject(NULL_PEN); + c.dc.SelectObject(&green); + c.dc.Rectangle(50, 10, 60, 20); + CHECK(c.Px(50, 10) == kGreen && c.Px(58, 18) == kGreen, "null-pen rectangle fill (%06x)", c.Px(58, 18)); + CHECK(c.Px(59, 15) == kWhite && c.Px(55, 19) == kWhite, "null-pen rectangle is one pixel smaller (%06x)", + c.Px(59, 15)); + c.dc.SelectObject(&pen); + + c.dc.MoveTo(10, 30); + c.dc.LineTo(20, 30); + CHECK(c.Px(10, 30) == kBlack && c.Px(19, 30) == kBlack, "LineTo draws the start (%06x %06x)", c.Px(10, 30), + c.Px(19, 30)); + CHECK(c.Px(20, 30) == kWhite && c.Px(15, 29) == kWhite && c.Px(15, 31) == kWhite, "LineTo excludes the end (%06x)", + c.Px(20, 30)); + CHECK(c.dc.GetCurrentPosition() == CPoint(20, 30), "LineTo updates the position"); + c.dc.MoveTo(25, 25); + c.dc.LineTo(25, 35); + CHECK(c.Px(25, 25) == kBlack && c.Px(25, 34) == kBlack && c.Px(25, 35) == kWhite, "vertical LineTo"); + + POINT frame[] = {{30, 25}, {30, 40}, {45, 40}, {45, 25}, {30, 25}}; + c.dc.Polyline(frame, 5); + CHECK(c.Px(30, 25) == kBlack && c.Px(30, 40) == kBlack && c.Px(45, 40) == kBlack && c.Px(45, 25) == kBlack, + "polyline corners are covered"); + + CBrush yellow(kYellow); + c.dc.SelectObject(&yellow); + c.dc.Ellipse(70, 10, 90, 30); + CHECK(c.Px(80, 20) == kYellow, "ellipse fill (%06x)", c.Px(80, 20)); + CHECK(Dark(c.Px(70, 20)) && Dark(c.Px(89, 20)) && Dark(c.Px(80, 10)) && Dark(c.Px(80, 29)), + "ellipse touches [left,right-1] (%06x %06x)", c.Px(70, 20), c.Px(89, 20)); + CHECK(c.Px(90, 20) == kWhite && c.Px(69, 20) == kWhite && c.Px(80, 30) == kWhite, "ellipse stays in its box (%06x)", + c.Px(90, 20)); + + CPen dash(PS_DASH, 1, kBlack); + c.dc.SelectObject(&dash); + c.dc.MoveTo(10, 50); + c.dc.LineTo(110, 50); + int on = 0, off = 0; + for (int x = 10; x < 110; ++x) + (Dark(c.Px(x, 50)) ? on : off)++; + CHECK(on > 40 && off > 10, "dashed line has dashes and gaps (%d/%d)", on, off); + CPen dot(PS_DOT, 1, kBlack); + c.dc.SelectObject(&dot); + c.dc.MoveTo(10, 55); + c.dc.LineTo(110, 55); + on = off = 0; + for (int x = 10; x < 110; ++x) + (Dark(c.Px(x, 55)) ? on : off)++; + CHECK(on > 20 && off > 20, "dotted line (%d/%d)", on, off); + + CPen wide(PS_SOLID, 5, RGB(0, 0, 128)); + c.dc.SelectObject(&wide); + c.dc.MoveTo(10, 65); + c.dc.LineTo(110, 65); + CHECK(Dark(c.Px(50, 63)) && Dark(c.Px(50, 67)) && !Dark(c.Px(50, 69)), "wide pen width"); + + c.dc.SelectObject(&pen); + c.dc.SelectObject(&red); + POINT tri[] = {{130, 10}, {170, 10}, {150, 40}}; + c.dc.Polygon(tri, 3); + CHECK(c.Px(150, 20) == kRed && c.Px(132, 35) == kWhite, "polygon fill"); + + CBrush hatch(HS_DIAGCROSS, kBlue); + c.dc.SetBkColor(kYellow); + c.dc.SetBkMode(OPAQUE); + RECT hr{130, 50, 170, 80}; + c.dc.FillRect(&hr, &hatch); + int yellowCount = 0, blueCount = 0; + for (int y = 50; y < 80; ++y) + for (int x = 130; x < 170; ++x) { + if (Near(c.Px(x, y), kYellow, 40)) + ++yellowCount; + if (Near(c.Px(x, y), kBlue, 90)) + ++blueCount; + } + CHECK(yellowCount > 200 && blueCount > 50, "opaque hatch uses the bk colour (%d yellow %d blue)", yellowCount, + blueCount); + c.dc.SetBkMode(TRANSPARENT); + RECT hr2{175, 50, 215, 80}; + c.dc.FillRect(&hr2, &hatch); + int whiteCount = 0; + for (int y = 50; y < 80; ++y) + for (int x = 175; x < 215; ++x) + if (c.Px(x, y) == kWhite) + ++whiteCount; + CHECK(whiteCount > 200, "transparent hatch keeps the background (%d)", whiteCount); + + c.dc.SetPixel(5, 5, RGB(12, 34, 56)); + CHECK(c.Px(5, 5) == RGB(12, 34, 56), "SetPixel/GetPixel (%06x)", c.Px(5, 5)); + CHECK(c.dc.GetPixel(-1, 5) == CLR_INVALID, "GetPixel outside"); + + int saved = c.dc.SaveDC(); + CRgn clip; + clip.CreateRectRgn(100, 90, 120, 110); + c.dc.SelectClipRgn(&clip); + c.dc.FillSolidRect(90, 85, 40, 30, kRed); + CHECK(c.Px(105, 95) == kRed && c.Px(95, 95) == kWhite && c.Px(125, 95) == kWhite && c.Px(105, 110) == kWhite, + "clip region limits drawing"); + CRect box; + c.dc.GetClipBox(&box); + CHECK(box == CRect(100, 90, 120, 110), "GetClipBox (%d,%d,%d,%d)", box.left, box.top, box.right, box.bottom); + c.dc.IntersectClipRect(110, 80, 140, 100); + c.dc.FillSolidRect(90, 85, 40, 30, kGreen); + CHECK(c.Px(115, 95) == kGreen && c.Px(105, 95) == kRed && c.Px(115, 105) == kRed, "IntersectClipRect"); + c.dc.SelectObject(&yellow); + CHECK(c.dc.RestoreDC(saved), "RestoreDC"); + CHECK(c.dc.GetCurrentBrush()->m_hObject == red.m_hObject, "RestoreDC restores the brush"); + c.dc.FillSolidRect(90, 120, 40, 10, kBlue); + CHECK(c.Px(92, 125) == kBlue, "RestoreDC removes the clip region"); + + CRgn er; + er.CreateEllipticRgn(140, 100, 180, 140); + c.dc.FillRgn(&er, &blue); + CHECK(c.Px(160, 120) == kBlue && c.Px(141, 101) == kWhite, "elliptic region fill"); + CBrush black(kBlack); + c.dc.FrameRgn(&er, &black, 1, 1); + + RECT pie{190, 100, 230, 140}; + c.dc.Pie(&pie, CPoint(230, 120), CPoint(210, 100)); + CHECK(c.Px(220, 110) == kRed && c.Px(200, 130) != kRed, "pie quadrant"); + c.dc.RoundRect(190, 10, 230, 40, 10, 10); + CHECK(c.Px(210, 25) == kRed && c.Px(190, 10) == kWhite, "round rect corners"); + + c.dc.SelectObject(oldPen); + c.dc.SelectObject(oldBrush); + c.Save("shapes"); +} + +void TestText() { + Canvas c(320, 260); + c.dc.SetBkMode(TRANSPARENT); + TEXTMETRIC tm; + CHECK(c.dc.GetTextMetrics(&tm), "GetTextMetrics"); + CHECK(tm.tmHeight > 8 && tm.tmAscent + tm.tmDescent == tm.tmHeight && tm.tmAveCharWidth > 2, + "metrics h=%d a=%d d=%d ave=%d", tm.tmHeight, tm.tmAscent, tm.tmDescent, tm.tmAveCharWidth); + CSize hello = c.dc.GetTextExtent("Hello"); + CSize hello2 = c.dc.GetTextExtent("HelloHello"); + CHECK(hello.cy == tm.tmHeight && hello.cx > 10, "extent %dx%d", hello.cx, hello.cy); + CHECK(std::abs(hello2.cx - 2 * hello.cx) <= 2, "extent additivity %d vs %d", hello2.cx, hello.cx); + CHECK(c.dc.GetTextExtent("", 0).cx == 0, "empty extent"); + + c.dc.SetTextAlign(TA_LEFT | TA_BASELINE); + c.dc.TextOut(10, 40, "HHH", 3); + Box b = c.DarkBox(0, 0, 100, 60); + CHECK(!b.Empty() && b.bottom == 39, "baseline: glyph bottom at y-1 (%d)", b.bottom); + CHECK(b.left >= 10 && b.left <= 12, "TA_LEFT starts at x (%d)", b.left); + + c.dc.SetTextAlign(TA_LEFT | TA_TOP); + c.dc.TextOut(110, 10, "HHH", 3); + b = c.DarkBox(100, 0, 200, 60); + CHECK(!b.Empty() && b.top >= 10 && b.top <= 10 + tm.tmInternalLeading + 4, "TA_TOP (%d)", b.top); + int capTop = b.top - 10; + + c.dc.SetTextAlign(TA_RIGHT | TA_TOP); + c.dc.TextOut(300, 10, "HHH", 3); + b = c.DarkBox(200, 0, 320, 60); + CHECK(!b.Empty() && b.right <= 299 && b.right >= 295, "TA_RIGHT ends at x (%d)", b.right); + + c.dc.SetTextAlign(TA_CENTER | TA_BOTTOM); + c.dc.TextOut(160, 90, "HHHH", 4); + b = c.DarkBox(100, 60, 220, 100); + CHECK(!b.Empty() && std::abs((b.left + b.right) / 2 - 160) <= 2, "TA_CENTER (%d..%d)", b.left, b.right); + CHECK(b.bottom < 90 && b.bottom >= 90 - tm.tmDescent - 2, "TA_BOTTOM (%d)", b.bottom); + + c.dc.SetTextAlign(TA_LEFT | TA_TOP); + c.dc.SetBkMode(OPAQUE); + c.dc.SetBkColor(kYellow); + c.dc.TextOut(10, 60, "Opaque", 6); + CSize op = c.dc.GetTextExtent("Opaque"); + CHECK(Near(c.Px(10, 60), kYellow) && Near(c.Px(10 + op.cx - 1, 60 + tm.tmHeight - 1), kYellow), + "opaque text background box"); + CHECK(c.Px(10, 60 + tm.tmHeight) == kWhite && c.Px(10 + op.cx, 60) == kWhite, "opaque box size"); + c.dc.SetBkMode(TRANSPARENT); + + c.dc.MoveTo(10, 100); + c.dc.SetTextAlign(TA_UPDATECP); + c.dc.TextOut(0, 0, "ab", 2); + CHECK(c.dc.GetCurrentPosition().x == 10 + c.dc.GetTextExtent("ab").cx, "TA_UPDATECP advances"); + c.dc.SetTextAlign(TA_LEFT | TA_TOP); + + CRect r(0, 0, 0, 0); + int hgt = c.dc.DrawText("Hello World", -1, &r, DT_SINGLELINE | DT_CALCRECT); + CHECK(hgt == tm.tmHeight && r.Height() == tm.tmHeight && r.Width() == c.dc.GetTextExtent("Hello World").cx, + "DT_CALCRECT single line (%d, %dx%d)", hgt, r.Width(), r.Height()); + r.SetRect(0, 0, 60, 0); + hgt = c.dc.DrawText("aaa bbb ccc ddd eee", -1, &r, DT_WORDBREAK | DT_CALCRECT); + CHECK(hgt >= 2 * tm.tmHeight && hgt % tm.tmHeight == 0 && r.right <= 60, "DT_WORDBREAK wraps (%d, right %d)", hgt, + r.right); + r.SetRect(0, 0, 200, 0); + hgt = c.dc.DrawText("a\nb\r\nc", -1, &r, DT_CALCRECT); + CHECK(hgt == 3 * tm.tmHeight, "line breaks (%d)", hgt); + r.SetRect(0, 0, 0, 0); + c.dc.DrawText("&File", -1, &r, DT_SINGLELINE | DT_CALCRECT); + CHECK(r.Width() == c.dc.GetTextExtent("File").cx, "prefix removed (%d)", r.Width()); + r.SetRect(0, 0, 0, 0); + c.dc.DrawText("&File", -1, &r, DT_SINGLELINE | DT_CALCRECT | DT_NOPREFIX); + CHECK(r.Width() == c.dc.GetTextExtent("&File").cx, "DT_NOPREFIX keeps '&'"); + r.SetRect(0, 0, 0, 0); + c.dc.DrawText("a\tb", -1, &r, DT_SINGLELINE | DT_CALCRECT | DT_EXPANDTABS); + CHECK(std::abs(r.Width() - (8 * tm.tmAveCharWidth + c.dc.GetTextExtent("b").cx)) <= 2, "DT_EXPANDTABS (%d)", + r.Width()); + + CRect cr(10, 120, 190, 160); + c.dc.FrameRect(&cr, CBrush::FromHandle(::GetStockObject(GRAY_BRUSH))); + int ret = c.dc.DrawText("Centered", -1, &cr, DT_CENTER | DT_VCENTER | DT_SINGLELINE); + b = c.DarkBox(11, 121, 189, 159); + CHECK(!b.Empty() && std::abs((b.left + b.right) / 2 - 100) <= 2, "DT_CENTER (%d..%d)", b.left, b.right); + CHECK(std::abs((b.top + b.bottom) / 2 - 140) <= 4, "DT_VCENTER (%d..%d)", b.top, b.bottom); + CHECK(ret == (40 - tm.tmHeight) / 2 + tm.tmHeight, "DT_VCENTER return value (%d)", ret); + + CRect rr(200, 120, 300, 140); + c.dc.DrawText("Right", -1, &rr, DT_RIGHT | DT_SINGLELINE); + b = c.DarkBox(200, 120, 320, 140); + CHECK(!b.Empty() && b.right >= 296 && b.right <= 299, "DT_RIGHT (%d)", b.right); + + CRect er(10, 170, 70, 190); + c.dc.DrawText("abcdefghijklmnopqrstuvwxyz", -1, &er, DT_SINGLELINE | DT_END_ELLIPSIS); + b = c.DarkBox(0, 170, 320, 190); + CHECK(!b.Empty() && b.right < 70, "DT_END_ELLIPSIS fits (%d)", b.right); + CRect clipR(80, 170, 120, 190); + c.dc.DrawText("abcdefghijklmnopqrstuvwxyz", -1, &clipR, DT_SINGLELINE); + b = c.DarkBox(75, 170, 320, 190); + CHECK(!b.Empty() && b.right < 120, "DrawText clips to the rectangle (%d)", b.right); + CRect noclip(130, 170, 150, 190); + c.dc.DrawText("abcdefghij", -1, &noclip, DT_SINGLELINE | DT_NOCLIP); + b = c.DarkBox(125, 170, 320, 190); + CHECK(b.right > 150, "DT_NOCLIP draws outside (%d)", b.right); + + CRect pr(10, 200, 100, 220); + c.dc.DrawText("&Underline", -1, &pr, DT_SINGLELINE); + int under = 0; + for (int x = 10; x < 10 + c.dc.GetTextExtent("U").cx; ++x) + if (Dark(c.Px(x, 200 + tm.tmAscent + 1))) + ++under; + CHECK(under >= 3, "mnemonic underline (%d)", under); + + CRect wr(120, 200, 200, 260); + c.dc.DrawText("word wrapped text in a box", -1, &wr, DT_WORDBREAK | DT_CENTER); + b = c.DarkBox(115, 200, 320, 260); + CHECK(b.right < 200 && b.bottom > 200 + tm.tmHeight, "word wrap drawing (%d,%d)", b.right, b.bottom); + + c.Save("text"); + (void)capTop; +} + +void TestFonts() { + Canvas c(420, 260); + c.dc.SetBkMode(TRANSPARENT); + CFont arial; + arial.CreateFont(-20, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, ANSI_CHARSET, OUT_DEFAULT_PRECIS, + CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_SWISS, "Arial"); + CFont* old = c.dc.SelectObject(&arial); + TEXTMETRIC tm; + c.dc.GetTextMetrics(&tm); + CHECK(std::abs(tm.tmHeight - tm.tmInternalLeading - 20) <= 1, "negative lfHeight is the em height (%d-%d)", + tm.tmHeight, tm.tmInternalLeading); + c.dc.TextOut(10, 10, "Arial -20: The quick brown fox", 30); + + CFont cell; + cell.CreateFont(30, 0, 0, 0, FW_BOLD, TRUE, FALSE, FALSE, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, + DEFAULT_QUALITY, DEFAULT_PITCH | FF_ROMAN, "Times New Roman"); + c.dc.SelectObject(&cell); + c.dc.GetTextMetrics(&tm); + CHECK(std::abs(tm.tmHeight - 30) <= 1, "positive lfHeight is the cell height (%d)", tm.tmHeight); + CHECK(tm.tmWeight == FW_BOLD && tm.tmItalic, "weight/italic in metrics"); + c.dc.TextOut(10, 40, "Times 30 bold italic", 20); + + CFont courier; + courier.CreatePointFont(100, "Courier New"); + LOGFONT lf; + courier.GetLogFont(&lf); + CHECK(lf.lfHeight == -13, "CreatePointFont(100) -> lfHeight -13 (%d)", lf.lfHeight); + c.dc.SelectObject(&courier); + CHECK(c.dc.GetTextExtent("iiii").cx == c.dc.GetTextExtent("WWWW").cx, "Courier New maps to a monospace font"); + c.dc.GetTextMetrics(&tm); + CHECK((tm.tmPitchAndFamily & 1) == 0, "TMPF_FIXED_PITCH clear for fixed fonts (%x)", tm.tmPitchAndFamily); + c.dc.TextOut(10, 80, "Courier New 10pt: 0123456789 ABCDEF", 35); + CString face; + c.dc.GetTextFace(face); + CHECK(face == "Courier New", "GetTextFace (%s)", face.GetString()); + + CFont fixedStock; + fixedStock.CreateStockObject(SYSTEM_FIXED_FONT); + LOGFONT slf; + fixedStock.GetLogFont(&slf); + CHECK((slf.lfPitchAndFamily & 3) == FIXED_PITCH, "SYSTEM_FIXED_FONT is fixed pitch"); + CHECK(FontFromHandle(fixedStock).IsFixedWidth(), "SYSTEM_FIXED_FONT wx font is fixed"); + c.dc.SelectObject(&fixedStock); + c.dc.TextOut(10, 100, "SYSTEM_FIXED_FONT", 17); + + LOGFONT gui; + ::GetObject(DefaultGuiFont(), sizeof gui, &gui); + CHECK(strcmp(gui.lfFaceName, "MS Shell Dlg") == 0 && gui.lfHeight < 0, "DEFAULT_GUI_FONT LOGFONT (%s %d)", + gui.lfFaceName, gui.lfHeight); + gui.lfWeight = FW_BOLD; + CFont bold; + bold.CreateFontIndirect(&gui); + CHECK(FontFromHandle(bold).GetWeight() == wxFONTWEIGHT_BOLD, "bold copy of the GUI font"); + CHECK(FontFromHandle(bold).GetFaceName() == FontFromHandle(DefaultGuiFont()).GetFaceName(), + "GUI font face round-trips (%s)", (const char*)FontFromHandle(bold).GetFaceName().utf8_str()); + c.dc.SelectObject(&bold); + c.dc.TextOut(10, 120, "Bold GUI font", 13); + + LOGFONT garbage; + memset(&garbage, 0xCC, sizeof garbage); + garbage.lfWeight = FW_BOLD; + CFont g; + CHECK(g.CreateFontIndirect(&garbage) && FontFromHandle(g).IsOk(), "uninitialized LOGFONT is tolerated"); + + CFont underline; + underline.CreateFont(-16, 0, 0, 0, FW_NORMAL, FALSE, TRUE, TRUE, 0, 0, 0, 0, 0, "Tahoma"); + c.dc.SelectObject(&underline); + c.dc.TextOut(10, 140, "Tahoma underline strikeout", 26); + + CFont unknown; + unknown.CreateFont(15, 0, 0, 0, FW_BOLD, FALSE, FALSE, 0, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, + DEFAULT_QUALITY, DEFAULT_PITCH | FF_SWISS, "ArialBold"); + c.dc.SelectObject(&unknown); + c.dc.TextOut(10, 165, "ArialBold (unknown face) 15", 27); + + CFont fixedNoFace; + fixedNoFace.CreateFont(0, 0, 0, 0, 0, 0, 0, 0, DEFAULT_CHARSET, OUT_RASTER_PRECIS, 0, DEFAULT_QUALITY, + FIXED_PITCH | FF_DONTCARE, nullptr); + CHECK(FontFromHandle(fixedNoFace).IsFixedWidth(), "FIXED_PITCH without a face is monospace"); + c.dc.SelectObject(&fixedNoFace); + c.dc.TextOut(10, 185, "FIXED_PITCH no face", 19); + + c.dc.SetTextColor(RGB(200, 0, 0)); + c.dc.SelectObject(&arial); + c.dc.TextOut(10, 205, "\xC4\xD6\xDC \xE4\xF6\xFC \xDF \x80 (cp1252)", -1); + c.dc.SelectObject(old); + c.Save("fonts"); +} + +void TestBlit() { + Canvas src(40, 40); + src.dc.FillSolidRect(0, 0, 20, 40, kRed); + src.dc.FillSolidRect(20, 0, 20, 40, kBlue); + Canvas d(260, 140); + + d.dc.BitBlt(10, 10, 40, 40, &src.dc, 0, 0, SRCCOPY); + CHECK(d.Px(15, 15) == kRed && d.Px(45, 15) == kBlue && d.Px(50, 15) == kWhite && d.Px(10, 50) == kWhite, + "SRCCOPY (%06x %06x)", d.Px(15, 15), d.Px(45, 15)); + d.dc.BitBlt(60, 10, 40, 40, &src.dc, 0, 0, NOTSRCCOPY); + CHECK(d.Px(65, 15) == RGB(0, 255, 255) && d.Px(95, 15) == kYellow, "NOTSRCCOPY (%06x)", d.Px(65, 15)); + + const COLORREF gray = RGB(128, 128, 128); + d.dc.FillSolidRect(110, 10, 40, 40, gray); + d.dc.BitBlt(110, 10, 40, 40, &src.dc, 0, 0, SRCAND); + CHECK(d.Px(115, 15) == RGB(128, 0, 0) && d.Px(145, 15) == RGB(0, 0, 128), "SRCAND (%06x)", d.Px(115, 15)); + d.dc.FillSolidRect(160, 10, 40, 40, gray); + d.dc.BitBlt(160, 10, 40, 40, &src.dc, 0, 0, SRCPAINT); + CHECK(d.Px(165, 15) == RGB(255, 128, 128), "SRCPAINT (%06x)", d.Px(165, 15)); + d.dc.FillSolidRect(210, 10, 40, 40, gray); + d.dc.BitBlt(210, 10, 40, 40, &src.dc, 0, 0, SRCINVERT); + CHECK(d.Px(215, 15) == RGB(127, 128, 128), "SRCINVERT (%06x)", d.Px(215, 15)); + + CBrush green(kGreen); + d.dc.SelectObject(&green); + d.dc.PatBlt(10, 60, 20, 20, PATCOPY); + d.dc.PatBlt(35, 60, 20, 20, BLACKNESS); + d.dc.FillSolidRect(60, 60, 20, 20, kRed); + d.dc.PatBlt(60, 60, 10, 20, DSTINVERT); + CHECK(d.Px(15, 65) == kGreen && d.Px(40, 65) == kBlack && d.Px(62, 65) == RGB(0, 255, 255) && + d.Px(75, 65) == kRed, + "PatBlt PATCOPY/BLACKNESS/DSTINVERT"); + d.dc.InvertRect(CRect(70, 60, 80, 70)); + CHECK(d.Px(75, 65) == RGB(0, 255, 255), "InvertRect"); + + d.dc.StretchBlt(90, 60, 80, 20, &src.dc, 0, 0, 40, 10, SRCCOPY); + CHECK(d.Px(92, 70) == kRed && d.Px(168, 70) == kBlue && d.Px(130, 79) != kWhite && d.Px(130, 80) == kWhite, + "StretchBlt"); + d.dc.StretchBlt(250, 90, -60, 20, &src.dc, 0, 0, 40, 40, SRCCOPY); + CHECK(d.Px(195, 95) == kBlue && d.Px(245, 95) == kRed, "StretchBlt mirrored (%06x %06x)", d.Px(195, 95), + d.Px(245, 95)); + + Canvas mask(40, 40); + mask.dc.FillSolidRect(10, 10, 20, 20, kRed); + d.dc.FillSolidRect(10, 90, 40, 40, kYellow); + d.dc.TransparentBlt(10, 90, 40, 40, &mask.dc, 0, 0, 40, 40, kWhite); + CHECK(d.Px(12, 92) == kYellow && d.Px(25, 105) == kRed, "TransparentBlt"); + + unsigned char dib[] = {255, 0, 0, 0, 255, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 0}; + BITMAPINFO bmi; + memset(&bmi, 0, sizeof bmi); + bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bmi.bmiHeader.biWidth = 2; + bmi.bmiHeader.biHeight = 2; + bmi.bmiHeader.biPlanes = 1; + bmi.bmiHeader.biBitCount = 24; + ::StretchDIBits(d.dc, 60, 90, 2, 2, 0, 0, 2, 2, dib, &bmi, DIB_RGB_COLORS, SRCCOPY); + CHECK(d.Px(60, 90) == kRed && d.Px(61, 90) == kWhite && d.Px(60, 91) == kBlue && d.Px(61, 91) == kGreen, + "bottom-up DIB orientation (%06x %06x %06x %06x)", d.Px(60, 90), d.Px(61, 90), d.Px(60, 91), d.Px(61, 91)); + ::StretchDIBits(d.dc, 70, 90, 20, 20, 0, 0, 2, 2, dib, &bmi, DIB_RGB_COLORS, SRCCOPY); + CHECK(d.Px(75, 95) == kRed && d.Px(85, 105) == kGreen, "StretchDIBits scaled"); + ::SetDIBitsToDevice(d.dc, 95, 90, 2, 2, 0, 0, 0, 2, dib, &bmi, DIB_RGB_COLORS); + CHECK(d.Px(95, 90) == kRed && d.Px(96, 91) == kGreen, "SetDIBitsToDevice"); + d.Save("blit"); +} + +void TestBitmaps() { + const unsigned char bits[] = {0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 0, 255, 255, 255, 0, + 1, 2, 3, 0, 4, 5, 6, 0, 7, 8, 9, 0, 10, 11, 12, 0}; + CBitmap b; + CHECK(b.CreateBitmap(4, 2, 1, 32, bits), "CreateBitmap 32bpp"); + BITMAP bm; + b.GetBitmap(&bm); + CHECK(bm.bmWidth == 4 && bm.bmHeight == 2 && bm.bmBitsPixel == 32 && bm.bmWidthBytes == 16 && bm.bmPlanes == 1, + "GetBitmap %dx%d %dbpp %d", bm.bmWidth, bm.bmHeight, bm.bmBitsPixel, bm.bmWidthBytes); + unsigned char back[32]; + CHECK(b.GetBitmapBits(sizeof back, back) == 32 && memcmp(back, bits, 32) == 0, "GetBitmapBits round trip"); + CDC dc; + dc.CreateCompatibleDC(nullptr); + CBitmap* old = dc.SelectObject(&b); + CHECK(dc.GetPixel(0, 0) == kRed && dc.GetPixel(1, 0) == kGreen && dc.GetPixel(3, 0) == kWhite && + dc.GetPixel(0, 1) == RGB(3, 2, 1), + "pixels of a 32bpp DDB (%06x)", dc.GetPixel(0, 0)); + dc.SetPixel(2, 1, kYellow); + unsigned char after[32]; + b.GetBitmapBits(sizeof after, after); + CHECK(after[24] == 0 && after[25] == 255 && after[26] == 255, "GetBitmapBits sees drawing in a selected bitmap"); + unsigned char set[32]; + memset(set, 0x40, sizeof set); + b.SetBitmapBits(sizeof set, set); + CHECK(dc.GetPixel(1, 1) == RGB(0x40, 0x40, 0x40), "SetBitmapBits on a selected bitmap (%06x)", dc.GetPixel(1, 1)); + dc.SelectObject(old); + + const unsigned char mono[] = {0xAA, 0x55}; + CBitmap m; + m.CreateBitmap(16, 1, 1, 1, mono); + m.GetBitmap(&bm); + CHECK(bm.bmBitsPixel == 1 && bm.bmWidthBytes == 2, "monochrome GetBitmap"); + old = dc.SelectObject(&m); + CHECK(dc.GetPixel(0, 0) == kWhite && dc.GetPixel(1, 0) == kBlack && dc.GetPixel(9, 0) == kWhite, + "monochrome bits (1 = white)"); + dc.SelectObject(old); + + CBitmap res; + CHECK(res.LoadBitmap(1), "LoadBitmap through LoadBitmapResource"); + res.GetBitmap(&bm); + CHECK(bm.bmWidth == 4 && bm.bmHeight == 2, "loaded bitmap size"); + old = dc.SelectObject(&res); + CHECK(dc.GetPixel(0, 0) == kRed && dc.GetPixel(3, 1) == kBlue, "loaded bitmap pixels"); + dc.SelectObject(old); + CHECK(!CBitmap().LoadBitmap(2), "missing bitmap"); + + CBitmap other; + other.CreateCompatibleBitmap(&dc, 2, 2); + CDC dc2; + dc2.CreateCompatibleDC(nullptr); + dc.SelectObject(&other); + CHECK(dc2.SelectObject(&other) == nullptr, "a bitmap can be selected into one DC only"); + + HBITMAP h = CreateBitmapHandle(wxBitmap(3, 5, 24)); + CHECK(GetObject(h, sizeof bm, &bm) && bm.bmWidth == 3 && bm.bmHeight == 5, "CreateBitmapHandle"); + CHECK(DeleteObject(h) && !DeleteObject(h), "DeleteObject once"); + CBrush pattern(&res); + CHECK(pattern.m_hObject && BrushFromHandle(pattern).GetStyle() == wxBRUSHSTYLE_STIPPLE, "pattern brush"); +} + +void TestMapping() { + Canvas c(200, 200); + c.dc.SetMapMode(MM_ANISOTROPIC); + c.dc.SetWindowExt(1280, 1280); + c.dc.SetViewportExt(200, -200); + c.dc.SetViewportOrg(0, 200); + CPoint p[2] = {CPoint(0, 0), CPoint(1280, 1280)}; + c.dc.LPtoDP(p, 2); + CHECK(p[0] == CPoint(0, 200) && p[1] == CPoint(200, 0), "LPtoDP (%d,%d) (%d,%d)", p[0].x, p[0].y, p[1].x, p[1].y); + CPoint q(100, 100); + c.dc.DPtoLP(&q); + CHECK(q == CPoint(640, 640), "DPtoLP (%d,%d)", q.x, q.y); + CPen pen(PS_SOLID, 1, kBlack); + CBrush red(kRed); + c.dc.SelectObject(&pen); + c.dc.SelectObject(&red); + c.dc.Rectangle(0, 0, 640, 640); + CSize ext = c.dc.GetTextExtent("Plot"); + CSize dev = ext; + c.dc.LPtoDP(&dev); + CHECK(ext.cx > dev.cx * 5, "text extent in logical units (%d vs %d)", ext.cx, dev.cx); + TEXTMETRIC tm; + c.dc.GetTextMetrics(&tm); + CHECK(tm.tmHeight > 40, "metrics in logical units (%d)", tm.tmHeight); + c.dc.SetBkMode(TRANSPARENT); + c.dc.SetTextAlign(TA_CENTER | TA_BASELINE); + c.dc.TextOut(640, 1100, "Headline", 8); + CPen wide(PS_SOLID, 13, kBlue); + c.dc.SelectObject(&wide); + c.dc.MoveTo(700, 300); + c.dc.LineTo(1200, 300); + c.dc.SelectObject(&pen); + c.dc.SetMapMode(MM_TEXT); + c.dc.SetViewportOrg(0, 0); + CHECK(c.Px(50, 150) == kRed && c.Px(150, 50) == kWhite, "y-up rectangle placement"); + CHECK(c.Px(0, 150) == kBlack && c.Px(99, 150) == kBlack && c.Px(100, 150) == kWhite, "y-up rectangle edges"); + Box b = c.DarkBox(0, 0, 200, 60); + CHECK(!b.Empty() && b.bottom == 27 && std::abs((b.left + b.right) / 2 - 100) <= 2, + "text at a mapped point (%d %d..%d)", b.bottom, b.left, b.right); + int thick = 0; + for (int y = 145; y < 160; ++y) + if (Near(c.Px(150, y), kBlue, 60)) + ++thick; + CHECK(thick == 2, "pen width scales with the mapping (%d)", thick); + c.Save("mapping"); + + CDC m; + m.CreateCompatibleDC(nullptr); + m.SetMapMode(MM_LOMETRIC); + CPoint lm(254, -254); + m.LPtoDP(&lm); + CHECK(lm == CPoint(96, 96), "MM_LOMETRIC (%d,%d)", lm.x, lm.y); + m.SetMapMode(MM_ISOTROPIC); + m.SetWindowExt(100, 100); + m.SetViewportExt(200, 100); + CHECK(m.GetViewportExt() == CSize(100, 100), "MM_ISOTROPIC keeps the aspect ratio"); + m.SetMapMode(MM_TEXT); + m.SetWindowOrg(-10, -10); + CPoint o(0, 0); + m.LPtoDP(&o); + CHECK(o == CPoint(10, 10), "window origin in MM_TEXT"); + CHECK(m.GetDeviceCaps(LOGPIXELSY) == 96, "LOGPIXELSY"); +} + +void TestRegionsAndHandles() { + CRgn a, b, c; + a.CreateRectRgn(0, 0, 10, 10); + b.CreateRectRgn(5, 5, 15, 15); + c.CreateRectRgn(0, 0, 0, 0); + CHECK(c.CombineRgn(&a, &b, RGN_OR) == COMPLEXREGION, "RGN_OR"); + CHECK(c.PtInRegion(12, 12) && !c.PtInRegion(12, 2), "PtInRegion"); + CRect box; + c.GetRgnBox(&box); + CHECK(box == CRect(0, 0, 15, 15), "GetRgnBox"); + CHECK(c.CombineRgn(&a, &b, RGN_AND) == SIMPLEREGION, "RGN_AND"); + c.GetRgnBox(&box); + CHECK(box == CRect(5, 5, 10, 10), "intersection box"); + CHECK(c.CombineRgn(&a, &b, RGN_DIFF) == COMPLEXREGION && !c.PtInRegion(7, 7), "RGN_DIFF"); + c.OffsetRgn(100, 0); + CHECK(c.PtInRegion(101, 1), "OffsetRgn"); + + CGdiObject* t1 = CGdiObject::FromHandle(GetStockObject(BLACK_PEN)); + CGdiObject* t2 = CGdiObject::FromHandle(GetStockObject(BLACK_PEN)); + CHECK(t1 && t1 == t2 && t1->IsKindOf(RUNTIME_CLASS(CPen)), "temporary wrappers are reused and typed"); + CPen perm(PS_SOLID, 1, kRed); + CHECK(CGdiObject::FromHandle(perm.m_hObject) == &perm, "FromHandle returns the permanent object"); + PurgeTemporaryGdiWrappers(); + CHECK(DeleteObject(GetStockObject(WHITE_BRUSH)), "deleting a stock object succeeds"); + + { + CBitmap bmp; + bmp.CreateCompatibleBitmap(nullptr, 10, 10); + CDC memdc; + memdc.CreateCompatibleDC(nullptr); + memdc.SelectObject(&bmp); + ::DeleteDC(memdc); + ::DeleteObject(bmp); + CHECK(memdc.m_hDC == nullptr && bmp.m_hObject == nullptr, "Win32 deletes clear the MFC wrappers"); + } + + Canvas cv(40, 20); + { + CPen temp(PS_SOLID, 1, kRed); + cv.dc.SelectObject(&temp); + } + cv.dc.MoveTo(0, 5); + cv.dc.LineTo(30, 5); + CHECK(cv.Px(10, 5) == kRed, "a pen destroyed while selected stays usable"); + cv.dc.SelectStockObject(BLACK_PEN); + + HDC raw = ::CreateCompatibleDC(nullptr); + CDC* tdc = CDC::FromHandle(raw); + CHECK(tdc && tdc->m_hDC == raw && CDC::FromHandle(raw) == tdc, "temporary CDC"); + ::DeleteDC(raw); + CHECK(tdc->m_hDC == nullptr, "DeleteDC detaches the temporary CDC"); + CDC::DeleteTempMap(); + + CHECK(GetObject(GetStockObject(BLACK_PEN), 0, nullptr) == sizeof(LOGPEN), "GetObject size query"); + LOGBRUSH lb; + CHECK(GetObject(reinterpret_cast(COLOR_WINDOW + 1), sizeof lb, &lb) == sizeof lb && + lb.lbColor == GetSysColor(COLOR_WINDOW), + "system colour brush handle"); +} + +void TestWrapAndColors() { + wxBitmap target(120, 60, 24); + { + wxMemoryDC mdc(target); + mdc.SetBackground(*wxWHITE_BRUSH); + mdc.Clear(); + CDC* cdc = WrapDC(&mdc, nullptr); + cdc->SetTextColor(0xFFFFFFFF); + CHECK(cdc->GetTextColor() == 0xFFFFFFFF, "CTLCOLOR sentinel text colour is preserved"); + cdc->SetBkMode(TRANSPARENT); + CHECK(cdc->GetBkMode() == TRANSPARENT, "bk mode"); + CHECK(CDC::FromHandle(cdc->m_hDC) == cdc, "FromHandle of a wrapped DC"); + CPen pen(PS_SOLID, 1, kBlack); + CBrush br(kRed); + cdc->SelectObject(&pen); + cdc->SelectObject(&br); + cdc->Rectangle(10, 10, 20, 20); + RECT fr{30, 10, 40, 20}; + ::FillRect(cdc->m_hDC, &fr, reinterpret_cast(COLOR_WINDOW + 1)); + RECT er{50, 10, 70, 30}; + cdc->DrawEdge(&er, EDGE_RAISED, BF_RECT | BF_ADJUST); + CHECK(er.left == 52 && er.top == 12 && er.right == 68 && er.bottom == 28, "BF_ADJUST"); + cdc->Draw3dRect(80, 10, 20, 20, kBlue, kGreen); + UnwrapDC(cdc); + } + wxImage img = target.ConvertToImage(); + auto px = [&](int x, int y) { return RGB(img.GetRed(x, y), img.GetGreen(x, y), img.GetBlue(x, y)); }; + CHECK(Near(px(10, 10), kBlack, 2) && Near(px(19, 19), kBlack, 2) && px(20, 20) == kWhite && px(15, 15) == kRed, + "rectangle on a borrowed wx DC"); + CHECK(Near(px(35, 15), GetSysColor(COLOR_WINDOW), 2), "FillRect with (HBRUSH)(COLOR_WINDOW+1)"); + CHECK(Near(px(50, 10), GetSysColor(COLOR_3DLIGHT), 2) && Near(px(69, 29), GetSysColor(COLOR_3DDKSHADOW), 2) && + Near(px(51, 11), GetSysColor(COLOR_3DHILIGHT), 2) && Near(px(68, 28), GetSysColor(COLOR_3DSHADOW), 2), + "DrawEdge colours"); + CHECK(px(80, 10) == kBlue && px(99, 29) == kGreen && px(81, 11) == kWhite, "Draw3dRect"); + target.SaveFile(g_out + "/wrapped.png", wxBITMAP_TYPE_PNG); + + CHECK(BrushFromHandle(GetStockObject(NULL_BRUSH)).GetStyle() == wxBRUSHSTYLE_TRANSPARENT, "NULL_BRUSH"); + CHECK(!BrushFromHandle(nullptr).IsOk(), "null brush handle"); + CHECK(BrushFromHandle(GetSysColorBrush(COLOR_BTNFACE)).GetColour() == ToWxColour(GetSysColor(COLOR_BTNFACE)), + "GetSysColorBrush"); + CHECK(FromWxColour(ToWxColour(RGB(1, 2, 3))) == RGB(1, 2, 3), "colour round trip"); + CHECK(PenFromHandle(GetStockObject(NULL_PEN)).GetStyle() == wxPENSTYLE_TRANSPARENT, "NULL_PEN"); + HFONT fh = FontHandleFor(*wxNORMAL_FONT); + CHECK(fh == FontHandleFor(*wxNORMAL_FONT) && FontFromHandle(fh).IsOk(), "FontHandleFor caches"); +} + +void TestHiDpi() { + wxBitmap hb; + hb.CreateWithDIPSize(wxSize(120, 40), 2.0, 24); + wxMemoryDC hm(hb); + hm.SetBackground(*wxWHITE_BRUSH); + hm.Clear(); + CDC* wrapped = WrapDC(&hm, nullptr); + { + CDC mem; + mem.CreateCompatibleDC(wrapped); + CBitmap cb; + cb.CreateCompatibleBitmap(&mem, 120, 40); + CHECK(BitmapFromHandle(cb)->GetScaleFactor() == 2.0, "compatible bitmap inherits the scale factor"); + BITMAP bm; + cb.GetBitmap(&bm); + CHECK(bm.bmWidth == 120 && bm.bmHeight == 40, "BITMAP reports logical size"); + CBitmap* old = mem.SelectObject(&cb); + mem.FillSolidRect(0, 0, 120, 40, RGB(230, 240, 255)); + mem.FillSolidRect(100, 10, 10, 10, kRed); + CHECK(mem.GetPixel(105, 15) == kRed && mem.GetPixel(110, 15) == RGB(230, 240, 255), + "GetPixel in logical coordinates on a 2x bitmap"); + mem.SetBkMode(TRANSPARENT); + mem.TextOut(4, 10, "HiDPI text", 10); + wrapped->BitBlt(0, 0, 120, 40, &mem, 0, 0, SRCCOPY); + mem.SelectObject(old); + } + UnwrapDC(wrapped); + hm.SelectObject(wxNullBitmap); + CHECK(hb.GetWidth() == 240, "physical size %d", hb.GetWidth()); + wxImage img = hb.ConvertToImage(); + CHECK(img.GetRed(210, 30) == 255 && img.GetGreen(210, 30) == 0, "blit of a 2x bitmap to a 2x DC"); + hb.SaveFile(g_out + "/hidpi.png", wxBITMAP_TYPE_PNG); +} + +bool WaitFor(const std::function& done) { + for (int i = 0; i < 200 && !done(); ++i) { + wxYield(); + wxMilliSleep(10); + } + return done(); +} + +void TestWindows() { + auto* frame = new wxFrame(nullptr, wxID_ANY, "mfcwx gdi test", wxPoint(50, 50), wxSize(320, 240)); + auto* panel = new wxPanel(frame, wxID_ANY, wxPoint(0, 0), wxSize(300, 200)); + HWND hwnd = ToHwnd(panel); + + HDC h = ::GetDC(hwnd); + CDC* cdc = CDC::FromHandle(h); + CHECK(cdc && cdc->GetTextExtent("Measure").cx > 10, "measuring through GetDC outside paint"); + TEXTMETRIC tm; + CHECK(cdc->GetTextMetrics(&tm) && tm.tmHeight > 8, "metrics through GetDC"); + cdc->FillSolidRect(10, 10, 30, 20, kRed); + ::ReleaseDC(hwnd, h); +#ifdef __WXOSX__ + CHECK(HasClientDCOverlay(panel), "client DC drawing goes to the overlay on macOS"); +#endif + if (HasClientDCOverlay(panel)) { + wxBitmap out(panel->GetClientSize().x, panel->GetClientSize().y, 24); + { + wxMemoryDC md(out); + md.SetBackground(*wxWHITE_BRUSH); + md.Clear(); + PaintClientDCOverlay(panel, md); + } + wxImage img = out.ConvertToImage(); + CHECK(img.GetRed(15, 15) == 255 && img.GetGreen(15, 15) == 0 && img.GetRed(45, 15) == 255 && + img.GetGreen(45, 15) == 255, + "overlay content"); + ClearClientDCOverlay(panel); + CHECK(!HasClientDCOverlay(panel), "overlay cleared"); + } + + HDC leaked = ::GetDC(hwnd); + bool bridged = false, borrowed = false, ownSession = false, painted = false; + int pass = 0; + panel->Bind(wxEVT_PAINT, [&](wxPaintEvent&) { + ++pass; + if (pass == 1) { + wxPaintDC pdc(panel); + CDC* w = WrapDC(&pdc, panel); + PAINTSTRUCT ps; + HDC p = ::BeginPaint(hwnd, &ps); + HDC g = ::GetDC(hwnd); + bridged = DCFromHandle(p)->Target(true) == &pdc; + borrowed = DCFromHandle(g)->Target(true) == &pdc; + CDC::FromHandle(g)->FillSolidRect(0, 0, 20, 20, kBlue); + ::EndPaint(hwnd, &ps); + HDC inPaintLeak = ::GetDC(hwnd); + CDC::FromHandle(inPaintLeak)->FillSolidRect(20, 0, 20, 20, kGreen); + PaintClientDCOverlay(panel, pdc); + UnwrapDC(w); + CHECK(DCFromHandle(inPaintLeak)->dc == nullptr, "leaked paint DC detached at UnwrapDC"); + ::ReleaseDC(hwnd, g); + } else { + PAINTSTRUCT ps; + HDC p = ::BeginPaint(hwnd, &ps); + HDC g = ::GetDC(hwnd); + wxDC* pd = DCFromHandle(p)->Target(true); + ownSession = pd && DCFromHandle(g)->Target(true) == pd; + CDC::FromHandle(g)->FillSolidRect(0, 0, 20, 20, kRed); + ::ReleaseDC(hwnd, g); + ::EndPaint(hwnd, &ps); + painted = true; + } + }); + { + frame->Show(); + panel->Refresh(); + bool got = WaitFor([&] { return pass >= 1; }); + if (got) { + CHECK(bridged, "CPaintDC draws through the bridge's wxPaintDC"); + CHECK(borrowed, "GetDC inside paint borrows the paint DC"); + panel->Refresh(); + WaitFor([&] { return painted; }); + CHECK(painted && ownSession, "BeginPaint without bridge shares one wxPaintDC with GetDC"); + } else { + printf("SKIP paint tests: no paint event was delivered\n"); + } + } + frame->Destroy(); + wxTheApp->ProcessIdle(); + WaitFor([] { return false; }); + CDC* l = CDC::FromHandle(leaked); + CHECK(l && l->GetTextExtent("after destroy").cx > 10, "a GetDC handle survives its window"); + l->FillSolidRect(0, 0, 10, 10, kRed); + ::ReleaseDC(nullptr, leaked); + PurgeTemporaryGdiWrappers(); +} + +class TestApp : public wxApp { +public: + bool OnInit() override { + for (int i = 1; i < argc; ++i) { + wxString a = argv[i]; + if (a == "--no-window") + g_windowTests = false; + else + g_out = a; + } + wxInitAllImageHandlers(); + wxFileName::Mkdir(g_out, wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL); + CallAfter([this] { + TestShapes(); + TestText(); + TestFonts(); + TestBlit(); + TestBitmaps(); + TestMapping(); + TestRegionsAndHandles(); + TestWrapAndColors(); + TestHiDpi(); + if (g_windowTests) + TestWindows(); + PurgeTemporaryGdiWrappers(); + printf("%d checks, %d failures; images in %s\n", g_checks, g_failures, (const char*)g_out.utf8_str()); + ExitMainLoop(); + }); + return true; + } + int OnRun() override { + wxApp::OnRun(); + return g_failures ? 1 : 0; + } +}; + +} // namespace + +wxIMPLEMENT_APP_NO_MAIN(TestApp); + +int main(int argc, char** argv) { + int rc = wxEntry(argc, argv); + return g_failures ? 1 : rc; +} diff --git a/port/mfcwx/tests/runtime_tests.cpp b/port/mfcwx/tests/runtime_tests.cpp index 48ee76509..8cf9b7774 100644 --- a/port/mfcwx/tests/runtime_tests.cpp +++ b/port/mfcwx/tests/runtime_tests.cpp @@ -6,6 +6,8 @@ #include "afxmt.h" #include "afxole.h" #include "afxwin.h" +#include "process.h" +#include "runtime.h" #include #include @@ -324,9 +326,15 @@ void TestFind() { CHECK(count == 6); CHECK(dots); fh = FindFirstFile(App("find\\*.").c_str(), &wfd); - CHECK(fh != INVALID_HANDLE_VALUE && strcmp(wfd.cFileName, "noext") == 0); - if (fh != INVALID_HANDLE_VALUE) + CHECK(fh != INVALID_HANDLE_VALUE); + std::vector noExt; + if (fh != INVALID_HANDLE_VALUE) { + do + noExt.push_back(wfd.cFileName); + while (FindNextFile(fh, &wfd)); FindClose(fh); + } + CHECK(noExt.size() == 3 && noExt[0] == "." && noExt[1] == ".." && noExt[2] == "noext"); CFileFind finder; int files = 0; @@ -788,6 +796,12 @@ void TestProcesses() { CHECK(!CreateProcess(nullptr, missing, nullptr, nullptr, FALSE, 0, nullptr, nullptr, nullptr, &pi)); CHECK(GetLastError() == ERROR_FILE_NOT_FOUND); CHECK(WinExec("/usr/bin/true", SW_HIDE) > 31); + CHECK(_spawnl(_P_WAIT, "/bin/sh", "sh", "-c", "\"exit 4\"", nullptr) == 4); + char found[MAX_PATH]; + char* part = nullptr; + CHECK(SearchPath(nullptr, "sh", nullptr, sizeof found, found, &part) > 0 && part && strcmp(part, "sh") == 0); + CHECK(found[0] == '\\'); + CHECK(SearchPath(nullptr, "AESTool.exe", nullptr, sizeof found, found, &part) == 0); CHECK(reinterpret_cast(ShellExecute(nullptr, "open", "/no/such/file.xyz", nullptr, nullptr, SW_SHOW)) <= 32); } @@ -954,7 +968,13 @@ void TestWin32() { CHECK(GetUserName(buf, &size) && size == strlen(buf) + 1); size = sizeof buf; CHECK(GetComputerName(buf, &size) && size == strlen(buf)); - CHECK(LoadLibrary("SciLexer.dll") == nullptr && GetLastError() == 126); + CHECK(LoadLibrary("NoSuchLib.dll") == nullptr && GetLastError() == 126); + HMODULE sci = LoadLibrary("SciLexer.DLL"); + CHECK(sci != nullptr && GetModuleHandle("scilexer.dll") == sci); + CHECK(GetProcAddress(sci, "Scintilla_DirectFunction") == nullptr); + CHECK(FreeLibrary(sci)); + mfcwx::RegisterBuiltinModule("extra.dll"); + CHECK(LoadLibraryEx("C:\\x\\EXTRA.dll", nullptr, 0) != nullptr); CHECK(GetModuleHandle(nullptr) != nullptr); CTime ct(2024, 3, 5, 7, 8, 9); diff --git a/port/mfcwx/tests/scintilla_tests.cpp b/port/mfcwx/tests/scintilla_tests.cpp new file mode 100644 index 000000000..1ca64d377 --- /dev/null +++ b/port/mfcwx/tests/scintilla_tests.cpp @@ -0,0 +1,674 @@ +// Tests of the Scintilla proxy (src/scintilla.cpp): ANSI text and positions on top of a UTF-8 +// wxStyledTextCtrl. Built from scintilla.cpp and the code page sources only; the few window +// functions it calls are replaced by recording stubs below. + +#include "windows_impl.h" + +#include + +#include "../../../scintilla/include/Scintilla.h" +#include "../../../scintilla/include/SciLexer.h" + +#include +#include +#include +#include +#include +#include +#include + +namespace { + +struct Note { + unsigned code = 0; + int position = 0; + int length = 0; + int modificationType = 0; + int ch = 0; + std::string text; +}; + +std::vector g_notes; +std::vector g_commands; +std::function g_onNotify; + +int g_failures = 0; +int g_checks = 0; + +#define CHECK(cond) \ + do { \ + ++g_checks; \ + if (!(cond)) { \ + ++g_failures; \ + fprintf(stderr, "%s:%d: CHECK failed: %s\n", __FILE__, __LINE__, #cond); \ + } \ + } while (0) + +#define CHECK_EQ(a, b) \ + do { \ + ++g_checks; \ + long long va_ = static_cast(a); \ + long long vb_ = static_cast(b); \ + if (va_ != vb_) { \ + ++g_failures; \ + fprintf(stderr, "%s:%d: CHECK_EQ failed: %s (%lld) != %s (%lld)\n", __FILE__, __LINE__, #a, \ + va_, #b, vb_); \ + } \ + } while (0) + +#define CHECK_STR(a, b) \ + do { \ + ++g_checks; \ + std::string sa_(a); \ + std::string sb_(b); \ + if (sa_ != sb_) { \ + ++g_failures; \ + fprintf(stderr, "%s:%d: CHECK_STR failed: %s (\"%s\") != %s (\"%s\")\n", __FILE__, __LINE__, #a, \ + sa_.c_str(), #b, sb_.c_str()); \ + } \ + } while (0) + +wxStyledTextCtrl* g_stc = nullptr; + +LRESULT Send(UINT msg, WPARAM wp = 0, LPARAM lp = 0) { + bool handled = false; + LRESULT r = mfcwx::ScintillaWindowProc(g_stc, msg, wp, lp, handled); + if (!handled) + fprintf(stderr, "message %u not handled\n", msg); + return r; +} + +LRESULT Send(UINT msg, WPARAM wp, const char* text) { return Send(msg, wp, reinterpret_cast(text)); } +LRESULT Send(UINT msg, WPARAM wp, const void* p) { return Send(msg, wp, reinterpret_cast(p)); } +WPARAM W(int v) { return static_cast(static_cast(v)); } + +std::string GetAll() { + LRESULT len = Send(SCI_GETLENGTH); + std::vector buf(static_cast(len) + 1, 'x'); + Send(SCI_GETTEXT, static_cast(len + 1), buf.data()); + return std::string(buf.data(), static_cast(len)); +} + +std::string Range(int a, int b) { + std::vector buf(static_cast((b < 0 ? Send(SCI_GETLENGTH) : b) - a) + 1, 'x'); + TextRange tr; + tr.chrg.cpMin = a; + tr.chrg.cpMax = b; + tr.lpstrText = buf.data(); + LRESULT n = Send(SCI_GETTEXTRANGE, 0, &tr); + return std::string(buf.data(), static_cast(n)); +} + +void SetBytes(const std::string& s) { + Send(SCI_CLEARALL); + Send(SCI_ADDTEXT, s.size(), s.data()); +} + +void TestCp1252RoundTrip() { + mfcwx::SetAnsiCodePage(1252); + const char* t = "Gr\xFC\xDF" "e \x80\n\x89 caf\xE9"; + Send(SCI_SETTEXT, 0, t); + CHECK_EQ(Send(SCI_GETLENGTH), strlen(t)); + CHECK_EQ(Send(SCI_GETTEXTLENGTH), strlen(t)); + CHECK_EQ(Send(WM_GETTEXTLENGTH), strlen(t)); + CHECK(g_stc->GetLength() > static_cast(strlen(t))); + CHECK(g_stc->GetText() == wxString::FromUTF8("Gr\xC3\xBC\xC3\x9F" "e \xE2\x82\xAC\n\xE2\x80\xB0 caf\xC3\xA9")); + CHECK_STR(GetAll(), t); + + char buf[64]; + memset(buf, 'x', sizeof buf); + CHECK_EQ(Send(SCI_GETTEXT, 5, buf), 4); + CHECK_STR(buf, "Gr\xFC\xDF"); + memset(buf, 'x', sizeof buf); + CHECK_EQ(Send(WM_GETTEXT, 5, buf), 4); + CHECK_STR(buf, "Gr\xFC\xDF"); + + CHECK_EQ(Send(SCI_GETCHARAT, 2), static_cast('\xFC')); + CHECK_EQ(Send(SCI_GETCHARAT, 1), 'r'); + CHECK_STR(Range(3, 7), "\xDF" "e \x80"); + CHECK_STR(Range(9, -1), " caf\xE9"); + + CHECK_EQ(Send(SCI_LINEFROMPOSITION, 8), 1); + CHECK_EQ(Send(SCI_LINEFROMPOSITION, 6), 0); + CHECK_EQ(Send(SCI_POSITIONFROMLINE, 1), 8); + CHECK_EQ(Send(SCI_GETLINEENDPOSITION, 0), 7); + CHECK_EQ(Send(SCI_LINELENGTH, 0), 8); + CHECK_EQ(Send(SCI_GETCOLUMN, 11), 3); + CHECK_EQ(Send(SCI_POSITIONAFTER, 2), 3); + CHECK_EQ(Send(SCI_POSITIONBEFORE, 7), 6); + + Send(SCI_SETSEL, 2, 7); + CHECK_EQ(Send(SCI_GETSELECTIONSTART), 2); + CHECK_EQ(Send(SCI_GETSELECTIONEND), 7); + CHECK_EQ(Send(SCI_GETCURRENTPOS), 7); + CHECK_EQ(Send(SCI_GETANCHOR), 2); + CHECK_EQ(Send(SCI_GETSELTEXT, 0, static_cast(0)), 6); + memset(buf, 'x', sizeof buf); + CHECK_EQ(Send(SCI_GETSELTEXT, 0, buf), 6); + CHECK_STR(buf, "\xFC\xDF" "e \x80"); + Send(SCI_SETSEL, W(-1), W(-1)); + CHECK_EQ(Send(SCI_GETCURRENTPOS), strlen(t)); + Send(SCI_GOTOPOS, 10); + CHECK_EQ(Send(SCI_GETCURRENTPOS), 10); + CHECK_EQ(g_stc->GetCurrentPos(), 16); + + char line[64]; + memset(line, 'x', sizeof line); + CHECK_EQ(Send(SCI_GETLINE, 0, line), 8); + CHECK_STR(std::string(line, 8), "Gr\xFC\xDF" "e \x80\n"); + memset(line, 'x', sizeof line); + CHECK_EQ(Send(SCI_GETCURLINE, sizeof line, line), 2); + CHECK_STR(line, "\x89 caf\xE9"); + CHECK_EQ(Send(SCI_GETCURLINE, 0, static_cast(0)), 7); + + const char* p = reinterpret_cast(Send(SCI_GETCHARACTERPOINTER)); + CHECK_STR(p, t); + + Send(SCI_INSERTTEXT, 3, "\xE9"); + CHECK_STR(GetAll(), "Gr\xFC\xE9\xDF" "e \x80\n\x89 caf\xE9"); + Send(SCI_INSERTTEXT, W(-1), "\xA7"); + CHECK_EQ(Send(SCI_GETCHARAT, 11), static_cast('\xA7')); +} + +void TestAllBytes() { + mfcwx::SetAnsiCodePage(1252); + std::string all; + for (int i = 0; i < 256; ++i) + all.push_back(static_cast(i)); + SetBytes(all); + CHECK_EQ(Send(SCI_GETLENGTH), 256); + CHECK(Range(0, -1) == all); + CHECK(GetAll() == all); + const char* p = reinterpret_cast(Send(SCI_GETCHARACTERPOINTER)); + CHECK(memcmp(p, all.data(), 256) == 0); + for (int i = 0; i < 256; i += 17) + CHECK_EQ(Send(SCI_GETCHARAT, W(i)), static_cast(i)); + + std::vector cells(256 * 2 + 2, 'x'); + TextRange tr; + tr.chrg.cpMin = 0; + tr.chrg.cpMax = 256; + tr.lpstrText = cells.data(); + CHECK_EQ(Send(SCI_GETSTYLEDTEXT, 0, &tr), 512); + bool same = true; + for (int i = 0; i < 256; ++i) + same = same && cells[i * 2] == static_cast(i); + CHECK(same); + CHECK(cells[512] == 0 && cells[513] == 0); +} + +void TestCp1250Positions() { + mfcwx::SetAnsiCodePage(1250); + const char* t = "Za\xBF\xF3\xB3\xE6 g\xEA\x9Cl\xB9 ja\x9F\xF1\r\nZA\xAF\xD3\xA3\xC6 koniec"; + size_t len = strlen(t); + Send(SCI_SETTEXT, 0, t); + CHECK_STR(GetAll(), t); + CHECK(g_stc->GetText() == wxString::FromUTF8("Za\xC5\xBC\xC3\xB3\xC5\x82\xC4\x87 g\xC4\x99\xC5\x9Bl\xC4\x85 " + "ja\xC5\xBA\xC5\x84\r\nZA\xC5\xBB\xC3\x93\xC5\x81\xC4\x86 koniec")); + CHECK_EQ(Send(SCI_GETLENGTH), len); + + TextToFind ft; + ft.chrg.cpMin = 0; + ft.chrg.cpMax = static_cast(len); + ft.lpstrText = const_cast("g\xEA\x9Cl\xB9"); + CHECK_EQ(Send(SCI_FINDTEXT, SCFIND_MATCHCASE, &ft), 7); + CHECK_EQ(ft.chrgText.cpMin, 7); + CHECK_EQ(ft.chrgText.cpMax, 12); + CHECK_EQ(ft.chrg.cpMax, static_cast(len)); + + ft.chrg.cpMin = static_cast(len); + ft.chrg.cpMax = 0; + ft.lpstrText = const_cast("ja\x9F\xF1"); + CHECK_EQ(Send(SCI_FINDTEXT, SCFIND_MATCHCASE, &ft), 13); + CHECK_EQ(ft.chrgText.cpMax, 17); + + ft.chrg.cpMin = 0; + ft.chrg.cpMax = static_cast(len); + ft.lpstrText = const_cast("\xAF\xD3\xA3\xC6"); + CHECK_EQ(Send(SCI_FINDTEXT, SCFIND_MATCHCASE, &ft), 21); + // Scintilla 1.77 folds only ASCII when searching case-insensitively; so does wx's Scintilla. + CHECK_EQ(Send(SCI_FINDTEXT, 0, &ft), 21); + ft.lpstrText = const_cast("KONIEC"); + CHECK_EQ(Send(SCI_FINDTEXT, 0, &ft), 26); + CHECK_EQ(ft.chrgText.cpMax, 32); + + ft.lpstrText = const_cast("nothere"); + CHECK_EQ(Send(SCI_FINDTEXT, 0, &ft), -1); + + CHECK_EQ(Send(SCI_LINEFROMPOSITION, 21), 1); + CHECK_EQ(Send(SCI_POSITIONFROMLINE, 1), 19); + CHECK_EQ(Send(SCI_GETLINEENDPOSITION, 0), 17); + Send(SCI_SETSEL, 21, 25); + char buf[64]; + Send(SCI_GETSELTEXT, 0, buf); + CHECK_STR(buf, "\xAF\xD3\xA3\xC6"); + CHECK_STR(Range(2, 6), "\xBF\xF3\xB3\xE6"); + + // CScintillaWnd::SearchForward / ReplaceSearchedText + Send(SCI_SETSEL, 0, static_cast(0)); + Send(SCI_SETSEARCHFLAGS, SCFIND_MATCHCASE); + Send(SCI_SETTARGETSTART, 0); + Send(SCI_SETTARGETEND, W(static_cast(len))); + CHECK_EQ(Send(SCI_SEARCHINTARGET, 2, "\xB3\xE6"), 4); + CHECK_EQ(Send(SCI_GETTARGETSTART), 4); + CHECK_EQ(Send(SCI_GETTARGETEND), 6); + CHECK_EQ(Send(SCI_REPLACETARGET, W(-1), "\xA3\xC6!"), 3); + CHECK_STR(GetAll(), "Za\xBF\xF3\xA3\xC6! g\xEA\x9Cl\xB9 ja\x9F\xF1\r\nZA\xAF\xD3\xA3\xC6 koniec"); + CHECK_EQ(Send(SCI_GETLENGTH), len + 1); + + // CScintillaWnd::ReplaceAll + Send(SCI_SETTEXT, 0, "\xB9\xB9 a \xB9"); + const char* find = "\xB9"; + const char* repl = "\xA5\xA5"; + int count = 0; + long lEnd = static_cast(Send(SCI_GETTEXTLENGTH)); + Send(SCI_SETTARGETSTART, 0); + Send(SCI_SETTARGETEND, static_cast(lEnd)); + long lPos = static_cast(Send(SCI_SEARCHINTARGET, strlen(find), find)); + while (lPos < lEnd && lPos >= 0) { + long lLen = static_cast(Send(SCI_REPLACETARGET, strlen(repl), repl)); + lEnd = static_cast(Send(SCI_GETTEXTLENGTH)); + Send(SCI_SETTARGETSTART, static_cast(lPos + lLen)); + Send(SCI_SETTARGETEND, static_cast(lEnd)); + lPos = static_cast(Send(SCI_SEARCHINTARGET, strlen(find), find)); + ++count; + } + CHECK_EQ(count, 3); + CHECK_STR(GetAll(), "\xA5\xA5\xA5\xA5 a \xA5\xA5"); + + TEXTRANGE tr; + char out[16]; + tr.chrg.cpMin = 1; + tr.chrg.cpMax = 5; + tr.lpstrText = out; + bool handled = false; + CHECK_EQ(mfcwx::ScintillaWindowProc(g_stc, EM_GETTEXTRANGE, 0, reinterpret_cast(&tr), handled), 4); + CHECK(handled); + CHECK_STR(out, "\xA5\xA5\xA5 "); + int s0 = -1; + int s1 = -1; + Send(SCI_SETSEL, 2, 4); + mfcwx::ScintillaWindowProc(g_stc, EM_GETSEL, reinterpret_cast(&s0), reinterpret_cast(&s1), handled); + CHECK_EQ(s0, 2); + CHECK_EQ(s1, 4); + mfcwx::ScintillaWindowProc(g_stc, EM_SETSEL, 0, -1, handled); + CHECK_EQ(Send(SCI_GETSELECTIONEND), 9); + CHECK(Send(SCI_CANUNDO) != 0); + mfcwx::ScintillaWindowProc(g_stc, EM_EMPTYUNDOBUFFER, 0, 0, handled); + CHECK(handled); + CHECK_EQ(Send(SCI_CANUNDO), 0); + mfcwx::SetAnsiCodePage(1252); +} + +void TestDirectFunction() { + mfcwx::SetAnsiCodePage(1252); + auto fn = reinterpret_cast(Send(SCI_GETDIRECTFUNCTION)); + sptr_t ptr = static_cast(Send(SCI_GETDIRECTPOINTER)); + CHECK(fn != nullptr); + CHECK(ptr == reinterpret_cast(g_stc)); + fn(ptr, SCI_SETTEXT, 0, reinterpret_cast("\xE9t\xE9 \x80")); + CHECK_EQ(fn(ptr, SCI_GETLENGTH, 0, 0), 5); + CHECK_EQ(fn(ptr, SCI_GETCHARAT, 2, 0), static_cast('\xE9')); + fn(ptr, SCI_GOTOPOS, 4, 0); + CHECK_EQ(fn(ptr, SCI_GETCURRENTPOS, 0, 0), 4); + CHECK_EQ(g_stc->GetCurrentPos(), 6); + CHECK_STR(GetAll(), "\xE9t\xE9 \x80"); +} + +void TestCrypToolLexer() { + mfcwx::SetAnsiCodePage(1252); + Send(SCI_SETMODEVENTMASK, SC_MODEVENTMASKALL); + Send(SCI_SETTEXT, 0, "A\xE4\xC4" "b \xD6x"); + Send(SCI_STYLESETFORE, 2, RGB(192, 192, 192)); + Send(SCI_SETPROPERTY, reinterpret_cast("cryptool.nonalphabetstyle"), "2"); + Send(SCI_SETPROPERTY, reinterpret_cast("cryptool.alphabet"), "ABCDEFGHIJKLMNOPQRSTUVWXYZ\xC4\xD6\xDC"); + Send(SCI_SETSTYLEBITS, 5); + Send(SCI_SETLEXERLANGUAGE, 0, "CrypTool"); + CHECK(Send(SCI_GETLEXER) != SCLEX_NULL); + CHECK_EQ(g_stc->GetLexer(), SCLEX_CONTAINER); + char prop[64]; + CHECK_EQ(Send(SCI_GETPROPERTY, reinterpret_cast("cryptool.alphabet"), prop), 29); + CHECK_STR(prop, "ABCDEFGHIJKLMNOPQRSTUVWXYZ\xC4\xD6\xDC"); + Send(SCI_CLEARDOCUMENTSTYLE); + g_notes.clear(); + Send(SCI_COLOURISE, 0, 1); + CHECK(Send(SCI_GETENDSTYLED) >= 1); + Send(SCI_COLOURISE, 0, W(-1)); + CHECK_EQ(Send(SCI_GETENDSTYLED), 7); + const int expected[] = {0, 2, 0, 2, 2, 0, 2}; + for (int i = 0; i < 7; ++i) + CHECK_EQ(Send(SCI_GETSTYLEAT, W(i)), expected[i]); + CHECK_EQ(g_stc->GetStyleAt(2), 2); + bool styleNote = false; + for (const Note& n : g_notes) + if (n.code == SCN_MODIFIED && (n.modificationType & SC_MOD_CHANGESTYLE) && n.position == 1 && n.length == 6) + styleNote = true; + CHECK(styleNote); + bool styleNeededForwarded = false; + for (const Note& n : g_notes) + styleNeededForwarded = styleNeededForwarded || n.code == SCN_STYLENEEDED; + CHECK(!styleNeededForwarded); + + Send(SCI_SETPROPERTY, reinterpret_cast("cryptool.alphabet"), "abcdefghijklmnopqrstuvwxyz\xE4"); + Send(SCI_CLEARDOCUMENTSTYLE); + Send(SCI_COLOURISE, 0, W(-1)); + const int expected2[] = {2, 0, 2, 0, 2, 2, 0}; + for (int i = 0; i < 7; ++i) + CHECK_EQ(Send(SCI_GETSTYLEAT, W(i)), expected2[i]); + + Send(SCI_SETLEXER, SCLEX_NULL); + CHECK_EQ(Send(SCI_GETLEXER), SCLEX_NULL); + Send(SCI_CLEARDOCUMENTSTYLE); + CHECK_EQ(Send(SCI_GETSTYLEAT, 1), 0); + CHECK_EQ(Send(SCI_LOADLEXERLIBRARY, 0, "LexCrypTool"), 0); +} + +void TestContainerStyling() { + mfcwx::SetAnsiCodePage(1252); + Send(SCI_SETLEXER, SCLEX_CONTAINER); + Send(SCI_SETTEXT, 0, "\xE4\xF6\xFC" "abc"); + Send(SCI_CLEARDOCUMENTSTYLE); + Send(SCI_STARTSTYLING, 1, 0x1f); + Send(SCI_SETSTYLING, 2, 5); + CHECK_EQ(Send(SCI_GETSTYLEAT, 0), 0); + CHECK_EQ(Send(SCI_GETSTYLEAT, 1), 5); + CHECK_EQ(Send(SCI_GETSTYLEAT, 2), 5); + CHECK_EQ(Send(SCI_GETSTYLEAT, 3), 0); + CHECK_EQ(Send(SCI_GETENDSTYLED), 3); + Send(SCI_SETSTYLINGEX, 2, "\x07\x08"); + CHECK_EQ(Send(SCI_GETSTYLEAT, 3), 7); + CHECK_EQ(Send(SCI_GETSTYLEAT, 4), 8); + CHECK_EQ(Send(SCI_GETENDSTYLED), 5); + Send(SCI_STARTSTYLING, 0, 0x1f); + Send(SCI_SETSTYLINGEX, 3, "\x01\x02\x03"); + CHECK_EQ(g_stc->GetStyleAt(1), 1); + CHECK_EQ(g_stc->GetStyleAt(3), 2); + CHECK_EQ(g_stc->GetStyleAt(5), 3); + CHECK_EQ(Send(SCI_GETENDSTYLED), 3); + + std::vector cells(6 * 2 + 2); + TextRange tr; + tr.chrg.cpMin = 0; + tr.chrg.cpMax = 6; + tr.lpstrText = cells.data(); + CHECK_EQ(Send(SCI_GETSTYLEDTEXT, 0, &tr), 12); + CHECK(cells[0] == '\xE4' && cells[1] == 1 && cells[2] == '\xF6' && cells[3] == 2 && cells[6] == 'a' && cells[7] == 7); + + Send(SCI_CLEARALL); + const char styled[] = {'\xC4', 4, 'x', 6}; + Send(SCI_ADDSTYLEDTEXT, 4, styled); + CHECK_STR(GetAll(), "\xC4x"); + CHECK_EQ(Send(SCI_GETSTYLEAT, 0), 4); + CHECK_EQ(Send(SCI_GETSTYLEAT, 1), 6); + + Send(SCI_SETTEXT, 0, "\xE4\xF6\xFC" "abc"); + Send(SCI_CLEARDOCUMENTSTYLE); + g_notes.clear(); + Send(SCI_COLOURISE, 0, 4); + bool found = false; + for (const Note& n : g_notes) + if (n.code == SCN_STYLENEEDED && n.position == 4) + found = true; + CHECK(found); + Send(SCI_SETLEXER, SCLEX_NULL); +} + +void TestNotifications() { + mfcwx::SetAnsiCodePage(1252); + Send(SCI_SETMODEVENTMASK, SC_MOD_INSERTTEXT | SC_MOD_DELETETEXT); + CHECK_EQ(Send(SCI_GETMODEVENTMASK), SC_MOD_INSERTTEXT | SC_MOD_DELETETEXT); + Send(SCI_SETTEXT, 0, "ab"); + g_notes.clear(); + g_commands.clear(); + std::vector seen; + g_onNotify = [&seen](const SCNotification& scn) { + if (scn.nmhdr.code == SCN_MODIFIED) + seen.push_back(GetAll()); + }; + Send(SCI_INSERTTEXT, 1, "\xFC\xFC"); + CHECK_EQ(g_notes.size(), 1); + if (!g_notes.empty()) { + CHECK_EQ(g_notes[0].code, SCN_MODIFIED); + CHECK(g_notes[0].modificationType & SC_MOD_INSERTTEXT); + CHECK_EQ(g_notes[0].position, 1); + CHECK_EQ(g_notes[0].length, 2); + CHECK_STR(g_notes[0].text, "\xFC\xFC"); + } + CHECK_EQ(g_commands.size(), 1); + if (!g_commands.empty()) + CHECK_EQ(g_commands[0], SCEN_CHANGE); + CHECK_EQ(seen.size(), 1); + if (!seen.empty()) + CHECK_STR(seen[0], "a\xFC\xFC" "b"); + + g_notes.clear(); + Send(SCI_SETSEL, 1, 3); + Send(SCI_CLEAR); + CHECK_EQ(g_notes.size(), 1); + if (!g_notes.empty()) { + CHECK(g_notes[0].modificationType & SC_MOD_DELETETEXT); + CHECK_EQ(g_notes[0].position, 1); + CHECK_EQ(g_notes[0].length, 2); + CHECK_STR(g_notes[0].text, "\xFC\xFC"); + } + g_onNotify = nullptr; + + Send(SCI_SETSAVEPOINT); + g_notes.clear(); + Send(SCI_ADDTEXT, 1, "\xE9"); + bool left = false; + for (const Note& n : g_notes) + left = left || n.code == SCN_SAVEPOINTLEFT; + CHECK(left); + g_notes.clear(); + Send(SCI_UNDO); + bool reached = false; + for (const Note& n : g_notes) + reached = reached || n.code == SCN_SAVEPOINTREACHED; + CHECK(reached); + + Send(SCI_SETMODEVENTMASK, SC_MODEVENTMASKALL); + g_notes.clear(); + Send(SCI_SETTEXT, 0, "\xE4x"); + bool before = false; + for (const Note& n : g_notes) + before = before || (n.modificationType & SC_MOD_BEFOREINSERT); + CHECK(before); + Send(SCI_SETMODEVENTMASK, SC_MOD_INSERTTEXT | SC_MOD_DELETETEXT); +} + +void TestSanitizedInput() { + mfcwx::SetAnsiCodePage(1252); + g_stc->SetText(wxString::FromUTF8("x\xE4\xB8\xAD" "y\xC5\x82\xC3\xA9")); + CHECK_STR(GetAll(), "x?y?\xE9"); + CHECK(g_stc->GetText() == wxString::FromUTF8("x?y?\xC3\xA9")); + g_stc->InsertText(0, wxString::FromUTF8("\xE2\x82\xAC")); + CHECK_STR(GetAll(), "\x80x?y?\xE9"); + CHECK_EQ(Send(SCI_GETLENGTH), 6); +} + +void TestMisc() { + mfcwx::SetAnsiCodePage(1252); + Send(SCI_SETWRAPMODE, SC_WRAP_WORD); + CHECK_EQ(Send(SCI_GETWRAPMODE), SC_WRAP_WORD); + CHECK_EQ(g_stc->GetWrapMode(), wxSTC_WRAP_WHITESPACE); + Send(SCI_SETWRAPMODE, SC_WRAP_CHAR); + CHECK_EQ(Send(SCI_GETWRAPMODE), SC_WRAP_CHAR); + Send(SCI_SETWRAPMODE, SC_WRAP_NONE); + + Send(SCI_SETCODEPAGE, 0); + CHECK_EQ(Send(SCI_GETCODEPAGE), 0); + CHECK_EQ(g_stc->GetCodePage(), wxSTC_CP_UTF8); + + Send(SCI_SETTEXT, 0, "\xE4\xF6\xFC"); + struct { + void* hdc; + void* hdcTarget; + int rc[4]; + int rcPage[4]; + CharacterRange chrg; + } fr = {nullptr, nullptr, {0, 0, 100, 100}, {0, 0, 100, 100}, {0, 3}}; + CHECK_EQ(Send(SCI_FORMATRANGE, 1, &fr), 3); + CHECK(Send(SCI_TEXTWIDTH, STYLE_LINENUMBER, "9") > 0); + Send(SCI_STYLESETFONT, STYLE_DEFAULT, "Courier"); + char font[64]; + CHECK_EQ(Send(SCI_STYLEGETFONT, STYLE_DEFAULT, font), 7); + CHECK_STR(font, "Courier"); + + Send(SCI_EMPTYUNDOBUFFER); + CHECK_EQ(Send(SCI_CANUNDO), 0); + Send(SCI_SETSEL, 0, 3); + Send(SCI_COPY); + Send(SCI_SETUNDOCOLLECTION, 0); + Send(SCI_SETUNDOCOLLECTION, 1); + CHECK_EQ(Send(SCI_BRACEMATCH, W(-1)), -1); + Send(SCI_SETTEXT, 0, "(\xE4)"); + CHECK_EQ(Send(SCI_BRACEMATCH, 0), 2); + CHECK_EQ(Send(SCI_BRACEMATCH, 2), 0); + Send(SCI_BRACEHIGHLIGHT, 0, 2); +} + +// Random edits against a model string, compared through the ANSI interface. +void TestLargeDocument() { + mfcwx::SetAnsiCodePage(1250); + std::mt19937 rng(12345); + auto randomText = [&rng](size_t n) { + std::string s; + s.reserve(n); + for (size_t i = 0; i < n; ++i) { + unsigned r = rng() % 100; + if (r < 60) + s.push_back(static_cast('a' + rng() % 26)); + else if (r < 65) + s.append("\r\n"); + else + s.push_back(static_cast(0x80 + rng() % 128)); + } + return s; + }; + std::string model = randomText(300000); + auto t0 = std::chrono::steady_clock::now(); + SetBytes(model); + std::string copy = GetAll(); + long long sum = 0; + for (int i = 0; i < 20000; ++i) { + int p = static_cast(rng() % (model.size() + 1)); + Send(SCI_GOTOPOS, W(p)); + sum += Send(SCI_GETCURRENTPOS) + Send(SCI_LINEFROMPOSITION, W(p)); + } + for (int i = 0; i < 2000; ++i) { + int p = static_cast(rng() % model.size()); + Send(SCI_INSERTTEXT, W(p), "\xE4"); + Send(SCI_GOTOPOS, W(p + 1)); + sum += Send(SCI_GETCURRENTPOS); + Send(SCI_SETSEL, W(p), static_cast(p + 1)); + Send(SCI_CLEAR); + } + double proxyMs = std::chrono::duration(std::chrono::steady_clock::now() - t0).count(); + printf("large document: load + 20000 lookups + 2000 edits through the proxy: %.1f ms (%lld)\n", proxyMs, sum); + CHECK(copy == model); + CHECK(GetAll() == model); + bool ok = Send(SCI_GETLENGTH) == static_cast(model.size()); + for (int i = 0; i < 2000 && ok; ++i) { + int p = static_cast(rng() % (model.size() + 1)); + Send(SCI_GOTOPOS, W(p)); + ok = Send(SCI_GETCURRENTPOS) == p && g_stc->GetCurrentPos() == g_stc->PositionRelative(0, p); + if (!ok) + fprintf(stderr, "position %d mismatch\n", p); + if (p < static_cast(model.size())) + ok = ok && Send(SCI_GETCHARAT, W(p)) == static_cast(model[static_cast(p)]); + } + CHECK(ok); + for (int i = 0; i < 300 && ok; ++i) { + int op = static_cast(rng() % 3); + int p = static_cast(rng() % (model.size() + 1)); + if (op == 0) { + std::string ins = randomText(1 + rng() % 20); + Send(SCI_INSERTTEXT, W(p), ins.c_str()); + model.insert(static_cast(p), ins); + } else if (op == 1 && p < static_cast(model.size())) { + int n = std::min(static_cast(1 + rng() % 30), static_cast(model.size()) - p); + Send(SCI_SETSEL, W(p), static_cast(p + n)); + Send(SCI_CLEAR); + model.erase(static_cast(p), static_cast(n)); + } else { + int n = std::min(static_cast(rng() % 50), static_cast(model.size()) - p); + ok = Range(p, p + n) == model.substr(static_cast(p), static_cast(n)); + } + ok = ok && Send(SCI_GETLENGTH) == static_cast(model.size()); + int q = static_cast(rng() % (model.size() + 1)); + ok = ok && Send(SCI_LINEFROMPOSITION, W(q)) == g_stc->LineFromPosition(g_stc->PositionRelative(0, q)); + } + CHECK(ok); + CHECK(GetAll() == model); + double ms = std::chrono::duration(std::chrono::steady_clock::now() - t0).count(); + printf("large document: %zu chars, total with brute-force verification %.1f ms\n", model.size(), ms); + mfcwx::SetAnsiCodePage(1252); +} + +int RunTests() { + wxFrame* frame = new wxFrame(nullptr, wxID_ANY, "scintilla tests"); + g_stc = new wxStyledTextCtrl(frame, wxID_ANY); + mfcwx::BindScintillaEvents(g_stc); + TestCp1252RoundTrip(); + TestAllBytes(); + TestCp1250Positions(); + TestDirectFunction(); + TestCrypToolLexer(); + TestContainerStyling(); + TestNotifications(); + TestSanitizedInput(); + TestMisc(); + TestLargeDocument(); + frame->Destroy(); + printf("%d checks, %d failures\n", g_checks, g_failures); + return g_failures ? 1 : 0; +} + +int g_rc = 1; + +class TestApp : public wxApp { +public: + bool OnInit() override { + g_rc = RunTests(); + return false; + } +}; + +} // namespace + +namespace mfcwx { + +void NotifyParent(wxWindow*, UINT code) { g_commands.push_back(code); } + +LRESULT NotifyParentNM(wxWindow* control, NMHDR* hdr) { + hdr->hwndFrom = ToHwnd(control); + const SCNotification& scn = *reinterpret_cast(hdr); + Note n; + n.code = scn.nmhdr.code; + n.position = scn.position; + n.length = scn.length; + n.modificationType = scn.modificationType; + n.ch = scn.ch; + if (scn.text) + n.text.assign(scn.text, static_cast(scn.length)); + g_notes.push_back(n); + if (g_onNotify) + g_onNotify(scn); + return 0; +} + +bool IsMainThread() { return true; } +void RunOnMainThread(const std::function& fn) { fn(); } + +} // namespace mfcwx + +CDC* CDC::FromHandle(HDC) { return nullptr; } +wxDC* CDC::GetWx() const { return nullptr; } + +wxIMPLEMENT_APP_NO_MAIN(TestApp); + +int main(int argc, char** argv) { + wxEntry(argc, argv); + return g_rc; +} From abbae8bdc41d7ce8bcca43ec12102f835015b3ef Mon Sep 17 00:00:00 2001 From: Kamil Marczak Date: Tue, 6 Oct 2026 23:29:53 +0200 Subject: [PATCH 22/46] fix(mfcwx): finish the GDI layer Shares one paint DC per window during a paint, routes client-DC drawing outside a paint through a per-window overlay on macOS, and fixes text metrics; the GDI test passes 162 checks. --- port/mfcwx/src/gdi_dc.cpp | 16 +++++++++++++++- port/mfcwx/src/gdi_text.cpp | 13 +++++++++++++ port/mfcwx/tests/gdi_tests.cpp | 4 ++-- 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/port/mfcwx/src/gdi_dc.cpp b/port/mfcwx/src/gdi_dc.cpp index 163849544..4fa9f06c3 100644 --- a/port/mfcwx/src/gdi_dc.cpp +++ b/port/mfcwx/src/gdi_dc.cpp @@ -2015,7 +2015,21 @@ int GetDeviceCaps(HDC hdc, int index) { DCState* s = DCFromHandle(hdc); int dpi = s ? s->Dpi() : 96; bool printing = s && s->owner && s->owner->m_bPrinting && s->dc; - wxSize res = printing ? s->dc->GetSize() : wxGetDisplaySize(); + wxSize res; + switch (index) { + case HORZSIZE: + case VERTSIZE: + case HORZRES: + case VERTRES: + case 110: + case 111: + case 117: + case 118: + res = printing ? s->dc->GetSize() : wxGetDisplaySize(); + break; + default: + break; + } switch (index) { case 2: return printing ? 2 : 1; diff --git a/port/mfcwx/src/gdi_text.cpp b/port/mfcwx/src/gdi_text.cpp index b59c392dd..2a0818207 100644 --- a/port/mfcwx/src/gdi_text.cpp +++ b/port/mfcwx/src/gdi_text.cpp @@ -1,5 +1,9 @@ #include "gdi_internal.h" +#ifdef __WXOSX__ +#include +#endif + #include #include #include @@ -419,6 +423,15 @@ DeviceMetrics GetDeviceMetrics(DCState* s, wxDC* dc) { m.descent = fm.descent; m.height = fm.height - m.externalLeading; m.ascent = m.height - m.descent; +#ifdef __WXOSX__ + // Core Graphics puts the baseline at ceil(top + ascent); wx's integer metrics round differently. + if (CTFontRef ct = dc->GetFont().OSXGetCTFont()) { + m.ascent = static_cast(std::ceil(CTFontGetAscent(ct) - 1e-3)); + m.descent = static_cast(std::ceil(CTFontGetDescent(ct) - 1e-3)); + m.externalLeading = static_cast(std::lround(CTFontGetLeading(ct))); + m.height = m.ascent + m.descent; + } +#endif double sx, sy; s->Scales(sx, sy); double em = FontEmPixels(s->a.font) * (IsStockFont(s->a.font) ? 1.0 : std::fabs(sy)); diff --git a/port/mfcwx/tests/gdi_tests.cpp b/port/mfcwx/tests/gdi_tests.cpp index a8e97402e..7754b096e 100644 --- a/port/mfcwx/tests/gdi_tests.cpp +++ b/port/mfcwx/tests/gdi_tests.cpp @@ -867,6 +867,8 @@ void TestWindows() { PurgeTemporaryGdiWrappers(); } +} // namespace + class TestApp : public wxApp { public: bool OnInit() override { @@ -903,8 +905,6 @@ class TestApp : public wxApp { } }; -} // namespace - wxIMPLEMENT_APP_NO_MAIN(TestApp); int main(int argc, char** argv) { From fb35e4596c1670a9736dc8e557d76605040c7a49 Mon Sep 17 00:00:00 2001 From: Kamil Marczak Date: Tue, 6 Oct 2026 23:31:56 +0200 Subject: [PATCH 23/46] feat(mfcwx): connect the GDI layer to window painting and invalidation Wraps the paint DC for WM_PAINT and draws the client-DC overlay, clears the overlay on invalidation and resize, purges temporary GDI wrappers on idle and answers WM_GETFONT with a shared font handle. --- port/mfcwx/include/mfcwx/winapi_gui.h | 2 +- port/mfcwx/src/app.cpp | 1 + port/mfcwx/src/winapi.cpp | 4 +++- port/mfcwx/src/wnd.cpp | 9 +++++++++ 4 files changed, 14 insertions(+), 2 deletions(-) diff --git a/port/mfcwx/include/mfcwx/winapi_gui.h b/port/mfcwx/include/mfcwx/winapi_gui.h index bff9d3913..346d48081 100644 --- a/port/mfcwx/include/mfcwx/winapi_gui.h +++ b/port/mfcwx/include/mfcwx/winapi_gui.h @@ -1,6 +1,6 @@ #pragma once -// Win32 user/gdi functions operating on HWND/HDC handles (HWND is a wxWindow*, HDC a CDC*). +// Win32 user/gdi functions operating on HWND/HDC handles (HWND is a wxWindow*, HDC an opaque handle to an internal DC state). LRESULT SendMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam); LRESULT SendMessageA(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam); diff --git a/port/mfcwx/src/app.cpp b/port/mfcwx/src/app.cpp index d8dbe4fa9..507803af2 100644 --- a/port/mfcwx/src/app.cpp +++ b/port/mfcwx/src/app.cpp @@ -122,6 +122,7 @@ int MfcWxApp::OnExit() { void MfcWxApp::OnIdle(wxIdleEvent& e) { e.Skip(); + PurgeTemporaryGdiWrappers(); CWinApp* app = AppInstance(); if (app && app->OnIdle(m_idleCount++)) e.RequestMore(); diff --git a/port/mfcwx/src/winapi.cpp b/port/mfcwx/src/winapi.cpp index e3ccf3e79..1d9c35763 100644 --- a/port/mfcwx/src/winapi.cpp +++ b/port/mfcwx/src/winapi.cpp @@ -145,7 +145,7 @@ LRESULT DefaultWindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { return 0; } case WM_GETFONT: - return reinterpret_cast(st->font ? st->font : DefaultGuiFont()); + return reinterpret_cast(st->font ? st->font : FontHandleFor(w->GetFont())); case WM_SETREDRAW: if (wParam) { if (w->IsFrozen()) @@ -827,8 +827,10 @@ BOOL InvalidateRect(HWND hWnd, const RECT* lpRect, BOOL bErase) { wxWindow* w = ToWx(hWnd); if (lpRect) { wxRect r(lpRect->left, lpRect->top, lpRect->right - lpRect->left, lpRect->bottom - lpRect->top); + ClearClientDCOverlay(w, &r); w->RefreshRect(r, bErase != FALSE); } else { + ClearClientDCOverlay(w); w->Refresh(bErase != FALSE); } if (!w->IsTopLevel()) diff --git a/port/mfcwx/src/wnd.cpp b/port/mfcwx/src/wnd.cpp index c9ca7e5bd..cbaf3156e 100644 --- a/port/mfcwx/src/wnd.cpp +++ b/port/mfcwx/src/wnd.cpp @@ -429,11 +429,19 @@ void OnPaint(wxPaintEvent& e) { return; } if (!HasMessageHandler(pWnd, WM_PAINT)) { + if (HasClientDCOverlay(w)) { + wxPaintDC dc(w); + PaintClientDCOverlay(w, dc); + } e.Skip(); return; } bool def = false; + wxPaintDC dc(w); + CDC* c = WrapDC(&dc, w); SendFromEvent(w, WM_PAINT, 0, 0, e, &def); + PaintClientDCOverlay(w, dc); + UnwrapDC(c); if (def) e.Skip(); } @@ -456,6 +464,7 @@ void OnEraseBackground(wxEraseEvent& e) { void OnSize(wxSizeEvent& e) { wxWindow* w = static_cast(e.GetEventObject()); e.Skip(); + ClearClientDCOverlay(w); CWnd* pWnd = PermanentWnd(w); if (!HasMessageHandler(pWnd, WM_SIZE)) return; From 234de4771dbb14abb0124b8c577f964c8b8a3c3d Mon Sep 17 00:00:00 2001 From: Kamil Marczak Date: Tue, 6 Oct 2026 23:33:21 +0200 Subject: [PATCH 24/46] feat(mfcwx): implement the MFC control wrapper classes Adds CStatic/CButton/CEdit/CListBox/CComboBox/range control members, CBitmapButton, CCheckListBox, CListCtrl on wxListCtrl with ListView messages and notifications, CImageList, CTabCtrl, CToolTipCtrl and an RTF reader for EM_STREAMIN. All custom control painting follows the paint-bridge contract. --- port/mfcwx/include/afxwin.h | 4 + port/mfcwx/src/controls.cpp | 242 +++-- port/mfcwx/src/ctrlclasses.cpp | 1526 ++++++++++++++++++++++++++++++++ port/mfcwx/src/listctrl.cpp | 1173 ++++++++++++++++++++++++ 4 files changed, 2867 insertions(+), 78 deletions(-) create mode 100644 port/mfcwx/src/ctrlclasses.cpp create mode 100644 port/mfcwx/src/listctrl.cpp diff --git a/port/mfcwx/include/afxwin.h b/port/mfcwx/include/afxwin.h index b3adb9bb4..9a0acaaca 100644 --- a/port/mfcwx/include/afxwin.h +++ b/port/mfcwx/include/afxwin.h @@ -764,6 +764,7 @@ class CStatic : public CWnd { HCURSOR SetCursor(HCURSOR hCursor); HCURSOR GetCursor(); virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct); + BOOL OnChildNotify(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult) override; }; class CButton : public CWnd { @@ -784,6 +785,7 @@ class CButton : public CWnd { HCURSOR SetCursor(HCURSOR hCursor); HCURSOR GetCursor(); virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct); + BOOL OnChildNotify(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult) override; }; class CEdit : public CWnd { @@ -878,6 +880,8 @@ class CListBox : public CWnd { UINT ItemFromPoint(CPoint pt, BOOL& bOutside) const; }; +#define CLBN_CHKCHANGE 40 + class CCheckListBox : public CListBox { DECLARE_DYNAMIC(CCheckListBox) public: diff --git a/port/mfcwx/src/controls.cpp b/port/mfcwx/src/controls.cpp index 68904ee21..5396758e0 100644 --- a/port/mfcwx/src/controls.cpp +++ b/port/mfcwx/src/controls.cpp @@ -394,7 +394,19 @@ std::vector TabRects(wxWindow* w) { namespace { -void SendDrawItem(wxWindow* w, WindowState& st, wxDC& dc, UINT ctlType) { +using PaintFn = void (*)(wxWindow*, wxDC&, CDC*); + +std::function PaintBridge(wxWindow* w, PaintFn fn) { + return [w, fn](wxPaintEvent&) { + wxPaintDC dc(w); + CDC* cdc = WrapDC(&dc, w); + fn(w, dc, cdc); + PaintClientDCOverlay(w, dc); + UnwrapDC(cdc); + }; +} + +void SendDrawItem(wxWindow* w, WindowState& st, CDC* cdc, UINT ctlType) { ButtonExtra& bx = Extra(st); DRAWITEMSTRUCT dis; memset(&dis, 0, sizeof dis); @@ -409,7 +421,6 @@ void SendDrawItem(wxWindow* w, WindowState& st, wxDC& dc, UINT ctlType) { if (w->HasFocus()) dis.itemState |= ODS_FOCUS; dis.hwndItem = ToHwnd(w); - CDC* cdc = WrapDC(&dc, w); dis.hDC = cdc->m_hDC; wxSize sz = w->GetClientSize(); dis.rcItem.right = sz.x; @@ -417,19 +428,16 @@ void SendDrawItem(wxWindow* w, WindowState& st, wxDC& dc, UINT ctlType) { wxWindow* parent = LogicalParent(w); if (parent && IsManagedWindow(parent)) DispatchMessageTo(ToHwnd(parent), WM_DRAWITEM, static_cast(st.winId), reinterpret_cast(&dis)); - UnwrapDC(cdc); } -void PaintOwnerDrawButton(wxWindow* w, wxPaintEvent&) { - wxPaintDC dc(w); +void PaintOwnerDrawButton(wxWindow* w, wxDC&, CDC* cdc) { WindowState* st = GetState(w); if (!st) return; - SendDrawItem(w, *st, dc, ODT_BUTTON); + SendDrawItem(w, *st, cdc, ODT_BUTTON); } -void PaintStaticFrame(wxWindow* w, wxPaintEvent&) { - wxPaintDC dc(w); +void PaintStaticFrame(wxWindow* w, wxDC& dc, CDC*) { WindowState* st = GetState(w); if (!st) return; @@ -479,13 +487,12 @@ void PaintStaticFrame(wxWindow* w, wxPaintEvent&) { } } -void PaintGenericStatic(wxWindow* w, wxPaintEvent&) { - wxPaintDC dc(w); +void PaintGenericStatic(wxWindow* w, wxDC& dc, CDC* cdc) { WindowState* st = GetState(w); if (!st) return; if ((st->style & SS_TYPEMASK) == SS_OWNERDRAW) { - SendDrawItem(w, *st, dc, ODT_STATIC); + SendDrawItem(w, *st, cdc, ODT_STATIC); return; } wxString text = st->text; @@ -573,8 +580,7 @@ void BindStaticNotify(wxWindow* w) { // --------------------------------------------------------------------------------------------- // Tab control (header only, as in Windows) -void PaintTabs(wxWindow* w, wxPaintEvent&) { - wxPaintDC dc(w); +void PaintTabs(wxWindow* w, wxDC& dc, CDC*) { WindowState* st = GetState(w); if (!st) return; @@ -731,6 +737,8 @@ void ForwardScroll(wxScrollEvent& e) { if (st->kind == ControlKind::Slider) { RangeExtra& r = Extra(*st); r.pos = pos; + } else if (st->kind == ControlKind::ScrollBar) { + pos += Extra(*st).minValue; } DispatchMessageTo(ToHwnd(parent), msg, MAKEWPARAM(code, static_cast(pos)), reinterpret_cast(ToHwnd(w))); if (e.GetEventType() == wxEVT_SCROLL_CHANGED || e.GetEventType() == wxEVT_SCROLL_THUMBRELEASE) @@ -738,32 +746,6 @@ void ForwardScroll(wxScrollEvent& e) { reinterpret_cast(ToHwnd(w))); } -// --------------------------------------------------------------------------------------------- -// List view notifications - -void ListNotify(wxListEvent& e, UINT code, bool selected) { - wxWindow* w = static_cast(e.GetEventObject()); - NMLISTVIEW nm; - memset(&nm, 0, sizeof nm); - nm.hdr.code = code; - nm.iItem = static_cast(e.GetIndex()); - nm.iSubItem = e.GetColumn() < 0 ? 0 : e.GetColumn(); - if (code == LVN_ITEMCHANGED) { - nm.uChanged = LVIF_STATE; - nm.uNewState = selected ? (LVIS_SELECTED | LVIS_FOCUSED) : 0; - nm.uOldState = selected ? 0 : LVIS_SELECTED; - } - if (code == LVN_COLUMNCLICK) - nm.iItem = -1; - wxPoint p = e.GetPoint(); - nm.ptAction.x = p.x; - nm.ptAction.y = p.y; - if (auto* lc = wxDynamicCast(w, wxListCtrl)) - if (nm.iItem >= 0) - nm.lParam = static_cast(lc->GetItemData(nm.iItem)); - NotifyParentNM(w, &nm.hdr); -} - } // namespace // --------------------------------------------------------------------------------------------- @@ -806,11 +788,11 @@ void BindControlEvents(wxWindow* w, WindowState& st) { }); break; case ControlKind::OwnerDrawButton: - st.customPaint = [w](wxPaintEvent& e) { PaintOwnerDrawButton(w, e); }; + st.customPaint = PaintBridge(w, &PaintOwnerDrawButton); BindOwnerDrawButton(w); break; case ControlKind::StaticFrame: - st.customPaint = [w](wxPaintEvent& e) { PaintStaticFrame(w, e); }; + st.customPaint = PaintBridge(w, &PaintStaticFrame); BindStaticNotify(w); break; case ControlKind::Static: @@ -894,38 +876,10 @@ void BindControlEvents(wxWindow* w, WindowState& st) { }); break; case ControlKind::ListView: - w->Bind(wxEVT_LIST_ITEM_SELECTED, [](wxListEvent& e) { ListNotify(e, LVN_ITEMCHANGED, true); }); - w->Bind(wxEVT_LIST_ITEM_DESELECTED, [](wxListEvent& e) { ListNotify(e, LVN_ITEMCHANGED, false); }); - w->Bind(wxEVT_LIST_ITEM_ACTIVATED, [](wxListEvent& e) { ListNotify(e, NM_DBLCLK, true); }); - w->Bind(wxEVT_LIST_ITEM_RIGHT_CLICK, [](wxListEvent& e) { ListNotify(e, NM_RCLICK, true); }); - w->Bind(wxEVT_LIST_COL_CLICK, [](wxListEvent& e) { ListNotify(e, LVN_COLUMNCLICK, false); }); - w->Bind(wxEVT_LIST_ITEM_CHECKED, [](wxListEvent& e) { ListNotify(e, LVN_ITEMCHANGED, true); }); - w->Bind(wxEVT_LIST_ITEM_UNCHECKED, [](wxListEvent& e) { ListNotify(e, LVN_ITEMCHANGED, true); }); - w->Bind(wxEVT_LIST_KEY_DOWN, [w](wxListEvent& e) { - NMLVKEYDOWN nm; - memset(&nm, 0, sizeof nm); - nm.hdr.code = LVN_KEYDOWN; - nm.wVKey = static_cast(VirtualKeyFromWx(e.GetKeyCode())); - NotifyParentNM(w, &nm.hdr); - e.Skip(); - }); - w->Bind(wxEVT_LEFT_UP, [w](wxMouseEvent& e) { - NMITEMACTIVATE nm; - memset(&nm, 0, sizeof nm); - nm.hdr.code = NM_CLICK; - int flags = 0; - long sub = 0; - if (auto* lc = wxDynamicCast(w, wxListCtrl)) - nm.iItem = static_cast(lc->HitTest(e.GetPosition(), flags, &sub)); - nm.iSubItem = static_cast(sub); - nm.ptAction.x = e.GetX(); - nm.ptAction.y = e.GetY(); - NotifyParentNM(w, &nm.hdr); - e.Skip(); - }); + BindListViewEvents(w); break; case ControlKind::Tab: - st.customPaint = [w](wxPaintEvent& e) { PaintTabs(w, e); }; + st.customPaint = PaintBridge(w, &PaintTabs); BindTabs(w); break; case ControlKind::Scintilla: @@ -1067,7 +1021,7 @@ wxWindow* CreateControl(wxWindow* parent, const char* cls, const char* text, int } else if (type == SS_OWNERDRAW || type == SS_USERITEM) { w = new GenericWindow(parent, id, pos, size, wxBORDER_NONE); kind = ControlKind::Generic; - EnsureState(w).customPaint = [w](wxPaintEvent& e) { PaintGenericStatic(w, e); }; + EnsureState(w).customPaint = PaintBridge(w, &PaintGenericStatic); } else { long f = wxST_NO_AUTORESIZE | BorderFlags(style, exStyle, false); if (style & SS_SUNKEN) @@ -1236,10 +1190,34 @@ wxWindow* CreateWindowForClass(CWnd* pWnd, CREATESTRUCT& cs) { return w; } +namespace { + +wxWindow* ReplaceWithCheckListBox(wxListBox* lb, WindowState& st) { + auto* c = new wxCheckListBox(lb->GetParent(), lb->GetId(), lb->GetPosition(), lb->GetSize(), 0, nullptr, + lb->GetWindowStyleFlag()); + c->MoveAfterInTabOrder(lb); + c->SetFont(lb->GetFont()); + for (unsigned i = 0; i < lb->GetCount(); ++i) + c->Append(lb->GetString(i), lb->HasClientUntypedData() ? lb->GetClientData(i) : nullptr); + WindowState& cs = EnsureState(c); + HookWindow(c, ControlKind::ListBox, st.winId, st.style, st.exStyle); + cs.font = st.font; + c->Bind(wxEVT_CHECKLISTBOX, [c](wxCommandEvent&) { NotifyParent(c, CLBN_CHKCHANGE); }); + c->Show(lb->IsShown()); + c->Enable(lb->IsThisEnabled()); + lb->Destroy(); + return c; +} + +} // namespace + wxWindow* PrepareForSubclass(wxWindow* w, CWnd* pWnd) { WindowState* st = GetState(w); if (!st || !pWnd) return w; + if (st->kind == ControlKind::ListBox && pWnd->IsKindOf(RUNTIME_CLASS(CCheckListBox)) && + !wxDynamicCast(w, wxCheckListBox) && wxDynamicCast(w, wxListBox)) + return ReplaceWithCheckListBox(static_cast(w), *st); bool paints = HasMessageHandler(pWnd, WM_PAINT) || HasMessageHandler(pWnd, WM_ERASEBKGND); bool nativeStatic = st->kind == ControlKind::Static || st->kind == ControlKind::StaticBitmap; bool ownerDrawStatic = st->kind == ControlKind::Static && (st->style & SS_TYPEMASK) == SS_OWNERDRAW; @@ -1257,7 +1235,7 @@ wxWindow* PrepareForSubclass(wxWindow* w, CWnd* pWnd) { gs.text = st->text; gs.font = st->font; if (!HasMessageHandler(pWnd, WM_PAINT)) - gs.customPaint = [g](wxPaintEvent& e) { PaintGenericStatic(g, e); }; + gs.customPaint = PaintBridge(g, &PaintGenericStatic); g->Show(w->IsShown()); g->Enable(w->IsThisEnabled()); w->Destroy(); @@ -1269,6 +1247,13 @@ wxWindow* PrepareForSubclass(wxWindow* w, CWnd* pWnd) { namespace { +int FirstVisibleLine(wxTextCtrl* t) { + wxTextCoord col = 0, row = 0; + if (t->HitTest(wxPoint(2, 2), &col, &row) != wxTE_HT_UNKNOWN && row > 0) + return static_cast(row); + return 0; +} + LRESULT EditProc(wxTextCtrl* t, WindowState& st, UINT msg, WPARAM wParam, LPARAM lParam, bool& handled) { handled = true; EditExtra& ex = Extra(st); @@ -1400,12 +1385,29 @@ LRESULT EditProc(wxTextCtrl* t, WindowState& st, UINT msg, WPARAM wParam, LPARAM case EM_GETPASSWORDCHAR: return static_cast(ex.passwordChar); case EM_GETFIRSTVISIBLELINE: - return 0; + return FirstVisibleLine(t); case EM_SCROLLCARET: t->ShowPosition(t->GetInsertionPoint()); return TRUE; - case EM_LINESCROLL: + case EM_LINESCROLL: { + int lines = static_cast(lParam); + if (lines && IsMultilineEdit(st)) { + int count = std::max(1, t->GetNumberOfLines()); + int target = std::max(0, std::min(count - 1, FirstVisibleLine(t) + lines)); + t->ShowPosition(t->GetLastPosition()); + t->ShowPosition(t->XYToPosition(0, target)); + } return TRUE; + } + case EM_CANPASTE: + return t->CanPaste(); + case EM_SELECTIONTYPE: { + long from, to; + t->GetSelection(&from, &to); + return from == to ? 0 : 1; + } + case EM_GETTEXTLENGTHEX: + return static_cast(FromWx(FromEditText(st, t->GetValue())).size()); case EM_SETTABSTOPS: case EM_SETMARGINS: case EM_SETRECT: @@ -1428,7 +1430,18 @@ LRESULT EditProc(wxTextCtrl* t, WindowState& st, UINT msg, WPARAM wParam, LPARAM return 0; } case EM_POSFROMCHAR: { - wxPoint p(0, 0); + bool rich = st.kind == ControlKind::RichEdit && wParam > 0xFFFF; + long pos = WinToWxPos(t, st, static_cast(rich ? lParam : static_cast(wParam))); + wxPoint p = t->PositionToCoords(pos); + if (p == wxDefaultPosition) + p = wxPoint(0, 0); + if (rich) { + if (auto* pt = reinterpret_cast(wParam)) { + pt->x = p.x; + pt->y = p.y; + } + return 0; + } return MAKELRESULT(p.x, p.y); } case EM_CHARFROMPOS: { @@ -1509,6 +1522,12 @@ LRESULT EditProc(wxTextCtrl* t, WindowState& st, UINT msg, WPARAM wParam, LPARAM break; data.append(reinterpret_cast(buf), static_cast(got)); } + if (wParam & SF_RTF) { + if (!(wParam & SFF_SELECTION)) + t->Clear(); + InsertRtf(t, IsMultilineEdit(st), data); + return static_cast(data.size()); + } wxString v = ToEditText(st, ToWx(data.data(), static_cast(data.size()))); if (wParam & SFF_SELECTION) t->WriteText(v); @@ -1770,6 +1789,18 @@ LRESULT ComboProc(wxWindow* w, WindowState& st, UINT msg, WPARAM wParam, LPARAM return TRUE; case CB_GETDROPPEDSTATE: return FALSE; + case CB_GETDROPPEDCONTROLRECT: { + auto* r = reinterpret_cast(lParam); + if (r) { + wxRect sr = w->GetScreenRect(); + int visible = std::max(1, std::min(ItemCount(items), 30)); + r->left = sr.x; + r->top = sr.y; + r->right = sr.x + sr.width; + r->bottom = sr.y + sr.height + visible * (w->GetCharHeight() + 2) + 2; + } + return CB_OKAY; + } case CB_SETITEMHEIGHT: case CB_SETEXTENDEDUI: case CB_SETDROPPEDWIDTH: @@ -1892,10 +1923,11 @@ LRESULT ListBoxProc(wxListBox* lb, WindowState& st, UINT msg, WPARAM wParam, LPA case LB_SELITEMRANGEEX: { int first = LOWORD(lParam); int last = HIWORD(lParam); - bool select = msg == LB_SELITEMRANGE ? wParam != 0 : first <= last; + bool select = wParam != 0; if (msg == LB_SELITEMRANGEEX) { first = static_cast(wParam); last = static_cast(lParam); + select = first <= last; if (first > last) std::swap(first, last); } @@ -2245,10 +2277,22 @@ LRESULT RangeProc(wxWindow* w, WindowState& st, UINT msg, WPARAM wParam, LPARAM case TBM_GETNUMTICS: return 2; case TBM_SETSEL: + r.selStart = static_cast(LOWORD(lParam)); + r.selEnd = static_cast(HIWORD(lParam)); + return 0; case TBM_SETSELSTART: + r.selStart = static_cast(lParam); + return 0; case TBM_SETSELEND: + r.selEnd = static_cast(lParam); + return 0; case TBM_CLEARSEL: + r.selStart = r.selEnd = 0; return 0; + case TBM_GETSELSTART: + return r.selStart; + case TBM_GETSELEND: + return r.selEnd; default: break; } @@ -2314,12 +2358,52 @@ LRESULT RangeProc(wxWindow* w, WindowState& st, UINT msg, WPARAM wParam, LPARAM return sb->GetThumbPosition() + r.minValue; return 0; case SBM_SETRANGE: + case SBM_SETRANGEREDRAW: if (auto* sb = wxDynamicCast(w, wxScrollBar)) { r.minValue = static_cast(wParam); r.maxValue = static_cast(lParam); - sb->SetScrollbar(sb->GetThumbPosition(), 1, std::max(1, r.maxValue - r.minValue + 1), std::max(1, (r.maxValue - r.minValue) / 10)); + int thumb = std::max(1, r.page); + int pageSize = r.page > 0 ? r.page : std::max(1, (r.maxValue - r.minValue) / 10); + sb->SetScrollbar(sb->GetThumbPosition(), thumb, std::max(thumb, r.maxValue - r.minValue + 1), pageSize); } return 0; + case SBM_SETSCROLLINFO: { + auto* si = reinterpret_cast(lParam); + auto* sb = wxDynamicCast(w, wxScrollBar); + if (!si || !sb) + return 0; + int pos = sb->GetThumbPosition() + r.minValue; + if (si->fMask & SIF_RANGE) { + r.minValue = si->nMin; + r.maxValue = si->nMax; + } + if (si->fMask & SIF_PAGE) + r.page = static_cast(si->nPage); + if (si->fMask & SIF_POS) + pos = si->nPos; + int thumb = std::max(1, r.page); + int range = std::max(thumb, r.maxValue - r.minValue + 1); + pos = std::max(0, std::min(pos - r.minValue, range - thumb)); + sb->SetScrollbar(pos, thumb, range, r.page > 0 ? r.page : std::max(1, range / 10), wParam != 0); + return pos + r.minValue; + } + case SBM_GETSCROLLINFO: { + auto* si = reinterpret_cast(lParam); + auto* sb = wxDynamicCast(w, wxScrollBar); + if (!si || !sb) + return FALSE; + if (si->fMask & SIF_RANGE) { + si->nMin = r.minValue; + si->nMax = r.maxValue; + } + if (si->fMask & SIF_PAGE) + si->nPage = static_cast(std::max(0, r.page)); + if (si->fMask & SIF_POS) + si->nPos = sb->GetThumbPosition() + r.minValue; + if (si->fMask & SIF_TRACKPOS) + si->nTrackPos = sb->GetThumbPosition() + r.minValue; + return TRUE; + } case SBM_GETRANGE: if (wParam) *reinterpret_cast(wParam) = r.minValue; @@ -2384,6 +2468,8 @@ LRESULT ControlWindowProc(wxWindow* w, WindowState& st, UINT msg, WPARAM wParam, return RangeProc(w, st, msg, wParam, lParam, handled); case ControlKind::Tab: return TabProc(w, st, msg, wParam, lParam, handled); + case ControlKind::ListView: + return ListViewProc(w, st, msg, wParam, lParam, handled); case ControlKind::Scintilla: if (msg >= 2000 || msg == WM_GETTEXT || msg == WM_SETTEXT || msg == WM_GETTEXTLENGTH) return ScintillaWindowProc(w, msg, wParam, lParam, handled); diff --git a/port/mfcwx/src/ctrlclasses.cpp b/port/mfcwx/src/ctrlclasses.cpp new file mode 100644 index 000000000..e4f8d531e --- /dev/null +++ b/port/mfcwx/src/ctrlclasses.cpp @@ -0,0 +1,1526 @@ +#include "controls_internal.h" + +#include "afxcmn.h" +#include "afxext.h" + +#include +#include +#include + +#include +#include +#include +#include + +namespace mfcwx { + +namespace { + +struct RtfFormat { + bool bold = false; + bool italic = false; + bool underline = false; + int color = 0; + int font = -1; + int halfPoints = 0; + bool skip = false; + bool colorTable = false; + bool fontTable = false; +}; + +class RtfWriter { +public: + RtfWriter(wxTextCtrl* t, bool multiline) : m_t(t), m_multiline(multiline), m_default(t->GetDefaultStyle()) {} + ~RtfWriter() { m_t->SetDefaultStyle(m_default); } + + void Parse(const std::string& rtf) { + std::vector stack; + RtfFormat cur; + size_t i = 0; + while (i < rtf.size()) { + char c = rtf[i]; + if (c == '{') { + stack.push_back(cur); + ++i; + } else if (c == '}') { + Flush(); + if (cur.fontTable && m_fontId >= 0) + EndFont(); + if (stack.empty()) + break; + cur = stack.back(); + stack.pop_back(); + ++i; + } else if (c == '\\') { + i = Control(rtf, i + 1, cur); + } else if (c == '\r' || c == '\n') { + ++i; + } else { + Text(cur, c); + ++i; + } + } + Flush(); + } + +private: + size_t Control(const std::string& rtf, size_t i, RtfFormat& cur) { + if (i >= rtf.size()) + return i; + char c = rtf[i]; + if (!isalpha(static_cast(c))) { + switch (c) { + case '\'': { + if (i + 2 < rtf.size()) { + std::string hex = rtf.substr(i + 1, 2); + Text(cur, static_cast(strtol(hex.c_str(), nullptr, 16))); + } + return i + 3; + } + case '*': + cur.skip = true; + return i + 1; + case '~': + Text(cur, static_cast(0xA0)); + return i + 1; + case '_': + Text(cur, '-'); + return i + 1; + case '\r': + case '\n': + Text(cur, '\n'); + return i + 1; + default: + Text(cur, c); + return i + 1; + } + } + size_t start = i; + while (i < rtf.size() && isalpha(static_cast(rtf[i]))) + ++i; + std::string word = rtf.substr(start, i - start); + bool hasParam = false; + long param = 0; + size_t numStart = i; + if (i < rtf.size() && (rtf[i] == '-' || isdigit(static_cast(rtf[i])))) { + ++i; + while (i < rtf.size() && isdigit(static_cast(rtf[i]))) + ++i; + param = strtol(rtf.substr(numStart, i - numStart).c_str(), nullptr, 10); + hasParam = true; + } + if (i < rtf.size() && rtf[i] == ' ') + ++i; + Word(word, hasParam, param, cur); + return i; + } + + void Word(const std::string& w, bool hasParam, long param, RtfFormat& cur) { + bool on = !hasParam || param != 0; + if (cur.colorTable) { + int* component = w == "red" ? &m_r : w == "green" ? &m_g : w == "blue" ? &m_b : nullptr; + if (component) { + *component = static_cast(param); + m_hasColor = true; + } + return; + } + if (cur.fontTable) { + if (w == "f") { + m_fontId = static_cast(param); + m_fontName.clear(); + } + return; + } + if (w == "par" || w == "line") { + Text(cur, '\n'); + } else if (w == "tab") { + Text(cur, '\t'); + } else if (w == "colortbl") { + Flush(); + cur.colorTable = true; + } else if (w == "fonttbl") { + Flush(); + cur.fontTable = true; + } else if (w == "stylesheet" || w == "info" || w == "pict" || w == "header" || w == "footer" || + w == "object" || w == "listtable" || w == "generator") { + Flush(); + cur.skip = true; + } else if (w == "b") { + Flush(); + cur.bold = on; + } else if (w == "i") { + Flush(); + cur.italic = on; + } else if (w == "ul") { + Flush(); + cur.underline = on; + } else if (w == "ulnone") { + Flush(); + cur.underline = false; + } else if (w == "cf") { + Flush(); + cur.color = static_cast(param); + } else if (w == "f") { + Flush(); + cur.font = static_cast(param); + } else if (w == "fs") { + Flush(); + cur.halfPoints = static_cast(param); + } else if (w == "plain") { + Flush(); + cur.bold = cur.italic = cur.underline = false; + cur.color = 0; + cur.halfPoints = 0; + } + } + + void Text(RtfFormat& cur, char c) { + if (cur.skip) + return; + if (cur.colorTable) { + if (c == ';') { + m_colors.push_back(m_hasColor ? wxColour(m_r, m_g, m_b) : wxColour()); + m_r = m_g = m_b = 0; + m_hasColor = false; + } + return; + } + if (cur.fontTable) { + if (c == ';') + EndFont(); + else if (m_fontId >= 0) + m_fontName += c; + return; + } + if (c == '\n' && !m_multiline) + c = ' '; + if (!m_run.empty() && !SameFormat(cur)) + Flush(); + if (m_run.empty()) + m_runFormat = cur; + m_run += c; + } + + void EndFont() { + if (m_fontId >= 0) + m_fonts[m_fontId] = ToWx(m_fontName.c_str()); + m_fontId = -1; + m_fontName.clear(); + } + + bool SameFormat(const RtfFormat& f) const { + return f.bold == m_runFormat.bold && f.italic == m_runFormat.italic && f.underline == m_runFormat.underline && + f.color == m_runFormat.color && f.font == m_runFormat.font && f.halfPoints == m_runFormat.halfPoints; + } + + void Flush() { + if (m_run.empty()) + return; + const RtfFormat& f = m_runFormat; + wxTextAttr attr = m_default; + if (f.color > 0 && f.color < static_cast(m_colors.size()) && m_colors[f.color].IsOk()) + attr.SetTextColour(m_colors[f.color]); + else + attr.SetTextColour(m_default.HasTextColour() ? m_default.GetTextColour() : m_t->GetForegroundColour()); + attr.SetFontWeight(f.bold ? wxFONTWEIGHT_BOLD : wxFONTWEIGHT_NORMAL); + attr.SetFontStyle(f.italic ? wxFONTSTYLE_ITALIC : wxFONTSTYLE_NORMAL); + attr.SetFontUnderlined(f.underline); + if (f.halfPoints > 0) + attr.SetFontPointSize(std::max(1, f.halfPoints / 2)); + auto face = m_fonts.find(f.font); + if (face != m_fonts.end() && !face->second.empty()) + attr.SetFontFaceName(face->second); + m_t->SetDefaultStyle(attr); + m_t->WriteText(ToWx(m_run.data(), static_cast(m_run.size()))); + m_run.clear(); + } + + wxTextCtrl* m_t; + bool m_multiline; + wxTextAttr m_default; + std::string m_run; + RtfFormat m_runFormat; + std::vector m_colors; + std::map m_fonts; + int m_fontId = -1; + std::string m_fontName; + int m_r = 0; + int m_g = 0; + int m_b = 0; + bool m_hasColor = false; +}; + +} // namespace + +void InsertRtf(wxTextCtrl* t, bool multiline, const std::string& rtf) { + if (!t) + return; + long from, to; + t->GetSelection(&from, &to); + if (from != to) + t->Remove(from, to); + t->SetInsertionPoint(from); + RtfWriter writer(t, multiline); + writer.Parse(rtf); +} + +} // namespace mfcwx + +using namespace mfcwx; + +namespace { + +LRESULT Send(const CWnd* w, UINT msg, WPARAM wParam = 0, LPARAM lParam = 0) { + return ::SendMessage(w->m_hWnd, msg, wParam, lParam); +} + +int CopyText(const wxString& s, char* buf, int cch) { + if (!buf || cch <= 0) + return 0; + std::string a = FromWx(s); + size_t n = std::min(a.size(), static_cast(cch - 1)); + memcpy(buf, a.data(), n); + buf[n] = 0; + return static_cast(n); +} + +TabExtra* TabsOf(const CWnd* w) { + WindowState* st = w->m_hWnd ? GetState(w->GetWx()) : nullptr; + return st && st->kind == ControlKind::Tab ? &Extra(*st) : nullptr; +} + +struct ToolTipExtra { + struct Tool { + HWND hwnd; + UINT_PTR id; + wxString text; + }; + std::vector tools; + bool active = true; + int maxWidth = -1; +}; + +ToolTipExtra* TipsOf(const CWnd* w) { + WindowState* st = w->m_hWnd ? GetState(w->GetWx()) : nullptr; + return st && st->kind == ControlKind::Generic && !st->customPaint ? &Extra(*st) : nullptr; +} + +void ApplyTool(const ToolTipExtra::Tool& tool, bool active) { + wxWindow* w = ToWx(tool.hwnd); + if (!IsManagedWindow(w)) + return; + if (active && !tool.text.empty()) + w->SetToolTip(tool.text); + else + w->UnsetToolTip(); +} + +wxString ResourceText(UINT id) { + std::string s; + return LoadResourceString(id, s) ? ToWx(s.c_str()) : wxString(); +} + +wxString CallbackToolText(HWND hwndTool, HWND tooltip, UINT_PTR id) { + wxWindow* parent = LogicalParent(ToWx(hwndTool)); + if (!parent) + return wxString(); + NMTTDISPINFO di; + memset(&di, 0, sizeof di); + di.hdr.hwndFrom = tooltip; + di.hdr.idFrom = id ? id : reinterpret_cast(hwndTool); + di.hdr.code = TTN_NEEDTEXT; + di.uFlags = id ? 0 : TTF_IDISHWND; + DispatchMessageTo(ToHwnd(parent), WM_NOTIFY, di.hdr.idFrom, reinterpret_cast(&di)); + if (di.lpszText && IS_INTRESOURCE(di.lpszText)) + return ResourceText(static_cast(reinterpret_cast(di.lpszText))); + if (di.lpszText && di.lpszText != LPSTR_TEXTCALLBACK) + return ToWx(di.lpszText); + return ToWx(di.szText); +} + +} // namespace + +IMPLEMENT_DYNAMIC(CStatic, CWnd) +IMPLEMENT_DYNAMIC(CButton, CWnd) +IMPLEMENT_DYNAMIC(CEdit, CWnd) +IMPLEMENT_DYNAMIC(CListBox, CWnd) +IMPLEMENT_DYNAMIC(CCheckListBox, CListBox) +IMPLEMENT_DYNAMIC(CComboBox, CWnd) +IMPLEMENT_DYNAMIC(CScrollBar, CWnd) +IMPLEMENT_DYNAMIC(CProgressCtrl, CWnd) +IMPLEMENT_DYNAMIC(CSliderCtrl, CWnd) +IMPLEMENT_DYNAMIC(CSpinButtonCtrl, CWnd) +IMPLEMENT_DYNAMIC(CTreeCtrl, CWnd) +IMPLEMENT_DYNAMIC(CTabCtrl, CWnd) +IMPLEMENT_DYNAMIC(CToolTipCtrl, CWnd) +IMPLEMENT_DYNAMIC(CRichEditCtrl, CWnd) +IMPLEMENT_DYNAMIC(CBitmapButton, CButton) + +// --------------------------------------------------------------------------------------------- +// CStatic + +BOOL CStatic::Create(const char* lpszText, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID) { + return CWnd::Create("Static", lpszText, dwStyle, rect, pParentWnd, nID); +} + +HICON CStatic::SetIcon(HICON hIcon) { + return reinterpret_cast(SendMessage(STM_SETICON, reinterpret_cast(hIcon), 0)); +} + +HICON CStatic::GetIcon() const { return reinterpret_cast(Send(this, STM_GETICON)); } + +HBITMAP CStatic::SetBitmap(HBITMAP hBitmap) { + return reinterpret_cast(SendMessage(STM_SETIMAGE, IMAGE_BITMAP, reinterpret_cast(hBitmap))); +} + +HBITMAP CStatic::GetBitmap() const { return reinterpret_cast(Send(this, STM_GETIMAGE, IMAGE_BITMAP)); } + +HCURSOR CStatic::SetCursor(HCURSOR hCursor) { + return reinterpret_cast(SendMessage(STM_SETIMAGE, IMAGE_CURSOR, reinterpret_cast(hCursor))); +} + +HCURSOR CStatic::GetCursor() { return reinterpret_cast(SendMessage(STM_GETIMAGE, IMAGE_CURSOR)); } + +void CStatic::DrawItem(LPDRAWITEMSTRUCT) {} + +BOOL CStatic::OnChildNotify(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult) { + if (CWnd::OnChildNotify(message, wParam, lParam, pResult)) + return TRUE; + if (message != WM_DRAWITEM) + return FALSE; + DrawItem(reinterpret_cast(lParam)); + return TRUE; +} + +// --------------------------------------------------------------------------------------------- +// CButton + +BOOL CButton::Create(const char* lpszCaption, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID) { + return CWnd::Create("Button", lpszCaption, dwStyle, rect, pParentWnd, nID); +} + +UINT CButton::GetState() const { return static_cast(Send(this, BM_GETSTATE)); } +void CButton::SetState(BOOL bHighlight) { SendMessage(BM_SETSTATE, static_cast(bHighlight)); } +int CButton::GetCheck() const { return static_cast(Send(this, BM_GETCHECK)); } +void CButton::SetCheck(int nCheck) { SendMessage(BM_SETCHECK, static_cast(nCheck)); } +UINT CButton::GetButtonStyle() const { return static_cast(GetStyle() & 0xFFFF); } + +void CButton::SetButtonStyle(UINT nStyle, BOOL bRedraw) { + SendMessage(BM_SETSTYLE, static_cast(nStyle), static_cast(bRedraw)); +} + +HICON CButton::SetIcon(HICON hIcon) { + return reinterpret_cast(SendMessage(BM_SETIMAGE, IMAGE_ICON, reinterpret_cast(hIcon))); +} + +HICON CButton::GetIcon() const { return reinterpret_cast(Send(this, BM_GETIMAGE, IMAGE_ICON)); } + +HBITMAP CButton::SetBitmap(HBITMAP hBitmap) { + return reinterpret_cast(SendMessage(BM_SETIMAGE, IMAGE_BITMAP, reinterpret_cast(hBitmap))); +} + +HBITMAP CButton::GetBitmap() const { return reinterpret_cast(Send(this, BM_GETIMAGE, IMAGE_BITMAP)); } + +HCURSOR CButton::SetCursor(HCURSOR hCursor) { + return reinterpret_cast(SendMessage(BM_SETIMAGE, IMAGE_CURSOR, reinterpret_cast(hCursor))); +} + +HCURSOR CButton::GetCursor() { return reinterpret_cast(SendMessage(BM_GETIMAGE, IMAGE_CURSOR)); } + +void CButton::DrawItem(LPDRAWITEMSTRUCT) {} + +BOOL CButton::OnChildNotify(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult) { + if (CWnd::OnChildNotify(message, wParam, lParam, pResult)) + return TRUE; + if (message != WM_DRAWITEM) + return FALSE; + DrawItem(reinterpret_cast(lParam)); + return TRUE; +} + +// --------------------------------------------------------------------------------------------- +// CEdit + +BOOL CEdit::Create(DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID) { + return CWnd::Create("Edit", nullptr, dwStyle, rect, pParentWnd, nID); +} + +BOOL CEdit::CanUndo() const { return static_cast(Send(this, EM_CANUNDO)); } +int CEdit::GetLineCount() const { return static_cast(Send(this, EM_GETLINECOUNT)); } +BOOL CEdit::GetModify() const { return static_cast(Send(this, EM_GETMODIFY)); } +void CEdit::SetModify(BOOL bModified) { SendMessage(EM_SETMODIFY, static_cast(bModified)); } +void CEdit::GetRect(LPRECT lpRect) const { Send(this, EM_GETRECT, 0, reinterpret_cast(lpRect)); } +DWORD CEdit::GetSel() const { return static_cast(Send(this, EM_GETSEL)); } + +void CEdit::GetSel(int& nStartChar, int& nEndChar) const { + DWORD start = 0; + DWORD end = 0; + Send(this, EM_GETSEL, reinterpret_cast(&start), reinterpret_cast(&end)); + nStartChar = static_cast(start); + nEndChar = static_cast(end); +} + +int CEdit::GetLine(int nIndex, char* lpszBuffer) const { + return static_cast(Send(this, EM_GETLINE, static_cast(nIndex), reinterpret_cast(lpszBuffer))); +} + +int CEdit::GetLine(int nIndex, char* lpszBuffer, int nMaxLength) const { + if (!lpszBuffer || nMaxLength < static_cast(sizeof(WORD))) + return 0; + *reinterpret_cast(lpszBuffer) = static_cast(nMaxLength); + return GetLine(nIndex, lpszBuffer); +} + +void CEdit::EmptyUndoBuffer() { SendMessage(EM_EMPTYUNDOBUFFER); } +void CEdit::LimitText(int nChars) { SendMessage(EM_LIMITTEXT, static_cast(nChars)); } +int CEdit::LineFromChar(int nIndex) const { return static_cast(Send(this, EM_LINEFROMCHAR, static_cast(nIndex))); } +int CEdit::LineIndex(int nLine) const { return static_cast(Send(this, EM_LINEINDEX, static_cast(nLine))); } +int CEdit::LineLength(int nLine) const { return static_cast(Send(this, EM_LINELENGTH, static_cast(nLine))); } + +void CEdit::LineScroll(int nLines, int nChars) { + SendMessage(EM_LINESCROLL, static_cast(nChars), static_cast(nLines)); +} + +void CEdit::ReplaceSel(const char* lpszNewText, BOOL bCanUndo) { + SendMessage(EM_REPLACESEL, static_cast(bCanUndo), reinterpret_cast(lpszNewText ? lpszNewText : "")); +} + +void CEdit::SetPasswordChar(char ch) { SendMessage(EM_SETPASSWORDCHAR, static_cast(ch)); } + +void CEdit::SetSel(DWORD dwSelection, BOOL bNoScroll) { + SendMessage(EM_SETSEL, LOWORD(dwSelection), HIWORD(dwSelection)); + if (!bNoScroll) + SendMessage(EM_SCROLLCARET); +} + +void CEdit::SetSel(int nStartChar, int nEndChar, BOOL bNoScroll) { + SendMessage(EM_SETSEL, static_cast(nStartChar), static_cast(nEndChar)); + if (!bNoScroll) + SendMessage(EM_SCROLLCARET); +} + +BOOL CEdit::SetTabStops(int nTabStops, LPINT rgTabStops) { + return static_cast(SendMessage(EM_SETTABSTOPS, static_cast(nTabStops), reinterpret_cast(rgTabStops))); +} + +BOOL CEdit::Undo() { return static_cast(SendMessage(EM_UNDO)); } +void CEdit::Clear() { SendMessage(WM_CLEAR); } +void CEdit::Copy() { SendMessage(WM_COPY); } +void CEdit::Cut() { SendMessage(WM_CUT); } +void CEdit::Paste() { SendMessage(WM_PASTE); } +BOOL CEdit::SetReadOnly(BOOL bReadOnly) { return static_cast(SendMessage(EM_SETREADONLY, static_cast(bReadOnly))); } +int CEdit::GetFirstVisibleLine() const { return static_cast(Send(this, EM_GETFIRSTVISIBLELINE)); } +char CEdit::GetPasswordChar() const { return static_cast(Send(this, EM_GETPASSWORDCHAR)); } +void CEdit::SetLimitText(UINT nMax) { SendMessage(EM_SETLIMITTEXT, static_cast(nMax)); } +UINT CEdit::GetLimitText() const { return static_cast(Send(this, EM_GETLIMITTEXT)); } + +CPoint CEdit::PosFromChar(UINT nChar) const { + LRESULT r = Send(this, EM_POSFROMCHAR, static_cast(nChar)); + return CPoint(static_cast(LOWORD(r)), static_cast(HIWORD(r))); +} + +int CEdit::CharFromPos(CPoint pt) const { + return static_cast(Send(this, EM_CHARFROMPOS, 0, MAKELPARAM(pt.x, pt.y))); +} + +// --------------------------------------------------------------------------------------------- +// CListBox + +BOOL CListBox::Create(DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID) { + return CWnd::Create("ListBox", nullptr, dwStyle, rect, pParentWnd, nID); +} + +int CListBox::GetCount() const { return static_cast(Send(this, LB_GETCOUNT)); } +int CListBox::GetTopIndex() const { return static_cast(Send(this, LB_GETTOPINDEX)); } +int CListBox::SetTopIndex(int nIndex) { return static_cast(SendMessage(LB_SETTOPINDEX, static_cast(nIndex))); } +DWORD_PTR CListBox::GetItemData(int nIndex) const { return static_cast(Send(this, LB_GETITEMDATA, static_cast(nIndex))); } + +int CListBox::SetItemData(int nIndex, DWORD_PTR dwItemData) { + return static_cast(SendMessage(LB_SETITEMDATA, static_cast(nIndex), static_cast(dwItemData))); +} + +int CListBox::GetItemRect(int nIndex, LPRECT lpRect) const { + return static_cast(Send(this, LB_GETITEMRECT, static_cast(nIndex), reinterpret_cast(lpRect))); +} + +int CListBox::GetSel(int nIndex) const { return static_cast(Send(this, LB_GETSEL, static_cast(nIndex))); } + +int CListBox::GetText(int nIndex, char* lpszBuffer) const { + return static_cast(Send(this, LB_GETTEXT, static_cast(nIndex), reinterpret_cast(lpszBuffer))); +} + +void CListBox::GetText(int nIndex, CString& rString) const { + int len = GetTextLen(nIndex); + if (len == LB_ERR) { + rString.Empty(); + return; + } + GetText(nIndex, rString.GetBufferSetLength(len)); + rString.ReleaseBuffer(); +} + +int CListBox::GetTextLen(int nIndex) const { return static_cast(Send(this, LB_GETTEXTLEN, static_cast(nIndex))); } + +int CListBox::FindStringExact(int nIndexStart, const char* lpszFind) const { + return static_cast(Send(this, LB_FINDSTRINGEXACT, static_cast(nIndexStart), reinterpret_cast(lpszFind))); +} + +int CListBox::GetCaretIndex() const { return static_cast(Send(this, LB_GETCARETINDEX)); } + +int CListBox::SetCaretIndex(int nIndex, BOOL bScroll) { + return static_cast(SendMessage(LB_SETCARETINDEX, static_cast(nIndex), MAKELONG(bScroll, 0))); +} + +int CListBox::GetCurSel() const { return static_cast(Send(this, LB_GETCURSEL)); } +int CListBox::SetCurSel(int nSelect) { return static_cast(SendMessage(LB_SETCURSEL, static_cast(nSelect))); } + +int CListBox::SetSel(int nIndex, BOOL bSelect) { + return static_cast(SendMessage(LB_SETSEL, static_cast(bSelect), static_cast(nIndex))); +} + +int CListBox::GetSelCount() const { return static_cast(Send(this, LB_GETSELCOUNT)); } + +int CListBox::GetSelItems(int nMaxItems, LPINT rgIndex) const { + return static_cast(Send(this, LB_GETSELITEMS, static_cast(nMaxItems), reinterpret_cast(rgIndex))); +} + +int CListBox::SelItemRange(BOOL bSelect, int nFirstItem, int nLastItem) { + if (bSelect) + return static_cast(SendMessage(LB_SELITEMRANGEEX, static_cast(nFirstItem), static_cast(nLastItem))); + return static_cast(SendMessage(LB_SELITEMRANGEEX, static_cast(nLastItem), static_cast(nFirstItem))); +} + +int CListBox::AddString(const char* lpszItem) { + return static_cast(SendMessage(LB_ADDSTRING, 0, reinterpret_cast(lpszItem ? lpszItem : ""))); +} + +int CListBox::DeleteString(UINT nIndex) { return static_cast(SendMessage(LB_DELETESTRING, nIndex)); } + +int CListBox::InsertString(int nIndex, const char* lpszItem) { + return static_cast(SendMessage(LB_INSERTSTRING, static_cast(nIndex), reinterpret_cast(lpszItem ? lpszItem : ""))); +} + +void CListBox::ResetContent() { SendMessage(LB_RESETCONTENT); } + +int CListBox::FindString(int nStartAfter, const char* lpszItem) const { + return static_cast(Send(this, LB_FINDSTRING, static_cast(nStartAfter), reinterpret_cast(lpszItem))); +} + +int CListBox::SelectString(int nStartAfter, const char* lpszItem) { + return static_cast(SendMessage(LB_SELECTSTRING, static_cast(nStartAfter), reinterpret_cast(lpszItem))); +} + +UINT CListBox::ItemFromPoint(CPoint pt, BOOL& bOutside) const { + return OnMain([&]() -> UINT { + bOutside = TRUE; + auto* lb = m_hWnd ? wxDynamicCast(GetWx(), wxListBox) : nullptr; + if (!lb || lb->GetCount() == 0) + return 0; + int hit = lb->HitTest(wxPoint(pt.x, pt.y)); + if (hit != wxNOT_FOUND) { + bOutside = FALSE; + return static_cast(hit); + } + return pt.y < 0 ? 0 : lb->GetCount() - 1; + }); +} + +// --------------------------------------------------------------------------------------------- +// CCheckListBox + +void CCheckListBox::SetCheck(int nIndex, int nCheck) { + OnMain([&] { + auto* c = m_hWnd ? wxDynamicCast(GetWx(), wxCheckListBox) : nullptr; + if (c && nIndex >= 0 && nIndex < static_cast(c->GetCount())) + c->Check(static_cast(nIndex), nCheck != 0); + }); +} + +int CCheckListBox::GetCheck(int nIndex) { + return OnMain([&]() -> int { + auto* c = m_hWnd ? wxDynamicCast(GetWx(), wxCheckListBox) : nullptr; + if (!c || nIndex < 0 || nIndex >= static_cast(c->GetCount())) + return 0; + return c->IsChecked(static_cast(nIndex)) ? 1 : 0; + }); +} + +void CCheckListBox::Enable(int, BOOL) {} + +BOOL CCheckListBox::IsEnabled(int) { return TRUE; } + +// --------------------------------------------------------------------------------------------- +// CComboBox + +BOOL CComboBox::Create(DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID) { + return CWnd::Create("ComboBox", nullptr, dwStyle, rect, pParentWnd, nID); +} + +int CComboBox::GetCount() const { return static_cast(Send(this, CB_GETCOUNT)); } +int CComboBox::GetCurSel() const { return static_cast(Send(this, CB_GETCURSEL)); } +int CComboBox::SetCurSel(int nSelect) { return static_cast(SendMessage(CB_SETCURSEL, static_cast(nSelect))); } +DWORD CComboBox::GetEditSel() const { return static_cast(Send(this, CB_GETEDITSEL)); } +BOOL CComboBox::LimitText(int nMaxChars) { return static_cast(SendMessage(CB_LIMITTEXT, static_cast(nMaxChars))); } + +BOOL CComboBox::SetEditSel(int nStartChar, int nEndChar) { + return static_cast(SendMessage(CB_SETEDITSEL, 0, MAKELONG(nStartChar, nEndChar))); +} + +DWORD_PTR CComboBox::GetItemData(int nIndex) const { + return static_cast(Send(this, CB_GETITEMDATA, static_cast(nIndex))); +} + +int CComboBox::SetItemData(int nIndex, DWORD_PTR dwItemData) { + return static_cast(SendMessage(CB_SETITEMDATA, static_cast(nIndex), static_cast(dwItemData))); +} + +int CComboBox::GetLBText(int nIndex, char* lpszText) const { + return static_cast(Send(this, CB_GETLBTEXT, static_cast(nIndex), reinterpret_cast(lpszText))); +} + +void CComboBox::GetLBText(int nIndex, CString& rString) const { + int len = GetLBTextLen(nIndex); + if (len == CB_ERR) { + rString.Empty(); + return; + } + GetLBText(nIndex, rString.GetBufferSetLength(len)); + rString.ReleaseBuffer(); +} + +int CComboBox::GetLBTextLen(int nIndex) const { return static_cast(Send(this, CB_GETLBTEXTLEN, static_cast(nIndex))); } + +int CComboBox::FindStringExact(int nIndexStart, const char* lpszFind) const { + return static_cast(Send(this, CB_FINDSTRINGEXACT, static_cast(nIndexStart), reinterpret_cast(lpszFind))); +} + +void CComboBox::GetDroppedControlRect(LPRECT lprect) const { + Send(this, CB_GETDROPPEDCONTROLRECT, 0, reinterpret_cast(lprect)); +} + +void CComboBox::ShowDropDown(BOOL bShowIt) { SendMessage(CB_SHOWDROPDOWN, static_cast(bShowIt)); } + +int CComboBox::AddString(const char* lpszString) { + return static_cast(SendMessage(CB_ADDSTRING, 0, reinterpret_cast(lpszString ? lpszString : ""))); +} + +int CComboBox::DeleteString(UINT nIndex) { return static_cast(SendMessage(CB_DELETESTRING, nIndex)); } + +int CComboBox::InsertString(int nIndex, const char* lpszString) { + return static_cast(SendMessage(CB_INSERTSTRING, static_cast(nIndex), reinterpret_cast(lpszString ? lpszString : ""))); +} + +void CComboBox::ResetContent() { SendMessage(CB_RESETCONTENT); } + +int CComboBox::FindString(int nStartAfter, const char* lpszString) const { + return static_cast(Send(this, CB_FINDSTRING, static_cast(nStartAfter), reinterpret_cast(lpszString))); +} + +int CComboBox::SelectString(int nStartAfter, const char* lpszString) { + return static_cast(SendMessage(CB_SELECTSTRING, static_cast(nStartAfter), reinterpret_cast(lpszString))); +} + +void CComboBox::Clear() { SendMessage(WM_CLEAR); } +void CComboBox::Copy() { SendMessage(WM_COPY); } +void CComboBox::Cut() { SendMessage(WM_CUT); } +void CComboBox::Paste() { SendMessage(WM_PASTE); } + +// --------------------------------------------------------------------------------------------- +// CScrollBar + +BOOL CScrollBar::Create(DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID) { + return CWnd::Create("ScrollBar", nullptr, dwStyle, rect, pParentWnd, nID); +} + +int CScrollBar::GetScrollPos() const { return static_cast(Send(this, SBM_GETPOS)); } + +int CScrollBar::SetScrollPos(int nPos, BOOL bRedraw) { + return static_cast(SendMessage(SBM_SETPOS, static_cast(nPos), static_cast(bRedraw))); +} + +void CScrollBar::GetScrollRange(LPINT lpMinPos, LPINT lpMaxPos) const { + Send(this, SBM_GETRANGE, reinterpret_cast(lpMinPos), reinterpret_cast(lpMaxPos)); +} + +void CScrollBar::SetScrollRange(int nMinPos, int nMaxPos, BOOL bRedraw) { + SendMessage(bRedraw ? SBM_SETRANGEREDRAW : SBM_SETRANGE, static_cast(nMinPos), static_cast(nMaxPos)); +} + +void CScrollBar::ShowScrollBar(BOOL bShow) { ShowWindow(bShow ? SW_SHOW : SW_HIDE); } + +BOOL CScrollBar::EnableScrollBar(UINT nArrowFlags) { + EnableWindow((nArrowFlags & ESB_DISABLE_BOTH) != ESB_DISABLE_BOTH); + return TRUE; +} + +BOOL CScrollBar::SetScrollInfo(LPSCROLLINFO lpScrollInfo, BOOL bRedraw) { + if (!lpScrollInfo) + return FALSE; + lpScrollInfo->cbSize = sizeof(SCROLLINFO); + SendMessage(SBM_SETSCROLLINFO, static_cast(bRedraw), reinterpret_cast(lpScrollInfo)); + return TRUE; +} + +BOOL CScrollBar::GetScrollInfo(LPSCROLLINFO lpScrollInfo, UINT nMask) { + if (!lpScrollInfo) + return FALSE; + lpScrollInfo->cbSize = sizeof(SCROLLINFO); + lpScrollInfo->fMask = nMask; + return static_cast(SendMessage(SBM_GETSCROLLINFO, 0, reinterpret_cast(lpScrollInfo))); +} + +int CScrollBar::GetScrollLimit() { + int nMin = 0; + int nMax = 0; + GetScrollRange(&nMin, &nMax); + SCROLLINFO info; + memset(&info, 0, sizeof info); + if (GetScrollInfo(&info, SIF_PAGE)) + nMax -= std::max(static_cast(info.nPage) - 1, 0); + return nMax; +} + +// --------------------------------------------------------------------------------------------- +// CProgressCtrl + +BOOL CProgressCtrl::Create(DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID) { + return CWnd::Create("msctls_progress32", nullptr, dwStyle, rect, pParentWnd, nID); +} + +void CProgressCtrl::SetRange(short nLower, short nUpper) { SendMessage(PBM_SETRANGE, 0, MAKELPARAM(nLower, nUpper)); } + +void CProgressCtrl::SetRange32(int nLower, int nUpper) { + SendMessage(PBM_SETRANGE32, static_cast(nLower), static_cast(nUpper)); +} + +void CProgressCtrl::GetRange(int& nLower, int& nUpper) const { + int range[2] = {0, 0}; + Send(this, PBM_GETRANGE, FALSE, reinterpret_cast(range)); + nLower = range[0]; + nUpper = range[1]; +} + +int CProgressCtrl::GetPos() const { return static_cast(Send(this, PBM_GETPOS)); } +int CProgressCtrl::SetPos(int nPos) { return static_cast(SendMessage(PBM_SETPOS, static_cast(nPos))); } +int CProgressCtrl::OffsetPos(int nPos) { return static_cast(SendMessage(PBM_DELTAPOS, static_cast(nPos))); } +int CProgressCtrl::SetStep(int nStep) { return static_cast(SendMessage(PBM_SETSTEP, static_cast(nStep))); } +int CProgressCtrl::StepIt() { return static_cast(SendMessage(PBM_STEPIT)); } +COLORREF CProgressCtrl::SetBkColor(COLORREF clrNew) { return static_cast(SendMessage(PBM_SETBKCOLOR, 0, static_cast(clrNew))); } +COLORREF CProgressCtrl::SetBarColor(COLORREF clrBar) { return static_cast(SendMessage(PBM_SETBARCOLOR, 0, static_cast(clrBar))); } + +BOOL CProgressCtrl::SetMarquee(BOOL fMarqueeMode, int nInterval) { + return static_cast(SendMessage(PBM_SETMARQUEE, static_cast(fMarqueeMode), static_cast(nInterval))); +} + +// --------------------------------------------------------------------------------------------- +// CSliderCtrl + +BOOL CSliderCtrl::Create(DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID) { + return CWnd::Create("msctls_trackbar32", nullptr, dwStyle, rect, pParentWnd, nID); +} + +int CSliderCtrl::GetLineSize() const { return static_cast(Send(this, TBM_GETLINESIZE)); } +int CSliderCtrl::SetLineSize(int nSize) { return static_cast(SendMessage(TBM_SETLINESIZE, 0, static_cast(nSize))); } +int CSliderCtrl::GetPageSize() const { return static_cast(Send(this, TBM_GETPAGESIZE)); } +int CSliderCtrl::SetPageSize(int nSize) { return static_cast(SendMessage(TBM_SETPAGESIZE, 0, static_cast(nSize))); } +int CSliderCtrl::GetRangeMax() const { return static_cast(Send(this, TBM_GETRANGEMAX)); } +int CSliderCtrl::GetRangeMin() const { return static_cast(Send(this, TBM_GETRANGEMIN)); } + +void CSliderCtrl::GetRange(int& nMin, int& nMax) const { + nMin = GetRangeMin(); + nMax = GetRangeMax(); +} + +void CSliderCtrl::SetRangeMin(int nMin, BOOL bRedraw) { + SendMessage(TBM_SETRANGEMIN, static_cast(bRedraw), static_cast(nMin)); +} + +void CSliderCtrl::SetRangeMax(int nMax, BOOL bRedraw) { + SendMessage(TBM_SETRANGEMAX, static_cast(bRedraw), static_cast(nMax)); +} + +void CSliderCtrl::SetRange(int nMin, int nMax, BOOL bRedraw) { + SendMessage(TBM_SETRANGEMIN, FALSE, static_cast(nMin)); + SendMessage(TBM_SETRANGEMAX, static_cast(bRedraw), static_cast(nMax)); +} + +void CSliderCtrl::GetSelection(int& nMin, int& nMax) const { + nMin = static_cast(Send(this, TBM_GETSELSTART)); + nMax = static_cast(Send(this, TBM_GETSELEND)); +} + +void CSliderCtrl::SetSelection(int nMin, int nMax) { + SendMessage(TBM_SETSELSTART, FALSE, static_cast(nMin)); + SendMessage(TBM_SETSELEND, TRUE, static_cast(nMax)); +} + +void CSliderCtrl::GetChannelRect(LPRECT lprc) const { + OnMain([&] { + if (!lprc || !m_hWnd) + return; + wxSize sz = GetWx()->GetClientSize(); + bool vertical = (GetStyle() & TBS_VERT) != 0; + if (vertical) { + lprc->left = sz.x / 2 - 2; + lprc->right = sz.x / 2 + 2; + lprc->top = 8; + lprc->bottom = std::max(8, sz.y - 8); + } else { + lprc->left = 8; + lprc->right = std::max(8, sz.x - 8); + lprc->top = sz.y / 2 - 2; + lprc->bottom = sz.y / 2 + 2; + } + }); +} + +void CSliderCtrl::GetThumbRect(LPRECT lprc) const { + if (!lprc) + return; + RECT channel; + GetChannelRect(&channel); + int lo = GetRangeMin(); + int hi = GetRangeMax(); + int pos = GetPos(); + double f = hi > lo ? static_cast(pos - lo) / (hi - lo) : 0.0; + if (GetStyle() & TBS_VERT) { + int y = channel.top + static_cast(f * (channel.bottom - channel.top)); + lprc->left = channel.left - 8; + lprc->right = channel.right + 8; + lprc->top = y - 5; + lprc->bottom = y + 5; + } else { + int x = channel.left + static_cast(f * (channel.right - channel.left)); + lprc->left = x - 5; + lprc->right = x + 5; + lprc->top = channel.top - 8; + lprc->bottom = channel.bottom + 8; + } +} + +int CSliderCtrl::GetPos() const { return static_cast(Send(this, TBM_GETPOS)); } +void CSliderCtrl::SetPos(int nPos) { SendMessage(TBM_SETPOS, TRUE, static_cast(nPos)); } +UINT CSliderCtrl::GetNumTics() const { return static_cast(Send(this, TBM_GETNUMTICS)); } +int CSliderCtrl::GetTic(int nTic) const { return static_cast(Send(this, TBM_GETTIC, static_cast(nTic))); } +BOOL CSliderCtrl::SetTic(int nTic) { return static_cast(SendMessage(TBM_SETTIC, 0, static_cast(nTic))); } +void CSliderCtrl::SetTicFreq(int nFreq) { SendMessage(TBM_SETTICFREQ, static_cast(nFreq)); } +void CSliderCtrl::ClearSel(BOOL bRedraw) { SendMessage(TBM_CLEARSEL, static_cast(bRedraw)); } +void CSliderCtrl::ClearTics(BOOL bRedraw) { SendMessage(TBM_CLEARTICS, static_cast(bRedraw)); } + +// --------------------------------------------------------------------------------------------- +// CSpinButtonCtrl + +BOOL CSpinButtonCtrl::Create(DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID) { + return CWnd::Create("msctls_updown32", nullptr, dwStyle, rect, pParentWnd, nID); +} + +BOOL CSpinButtonCtrl::SetAccel(int nAccel, UDACCEL* pAccel) { + return static_cast(SendMessage(UDM_SETACCEL, static_cast(nAccel), reinterpret_cast(pAccel))); +} + +UINT CSpinButtonCtrl::GetAccel(int nAccel, UDACCEL* pAccel) const { + return static_cast(Send(this, UDM_GETACCEL, static_cast(nAccel), reinterpret_cast(pAccel))); +} + +int CSpinButtonCtrl::SetBase(int nBase) { return static_cast(SendMessage(UDM_SETBASE, static_cast(nBase))); } +UINT CSpinButtonCtrl::GetBase() const { return static_cast(Send(this, UDM_GETBASE)); } + +CWnd* CSpinButtonCtrl::SetBuddy(CWnd* pWndBuddy) { + return CWnd::FromHandle(reinterpret_cast( + SendMessage(UDM_SETBUDDY, reinterpret_cast(pWndBuddy ? pWndBuddy->m_hWnd : nullptr)))); +} + +CWnd* CSpinButtonCtrl::GetBuddy() const { return CWnd::FromHandle(reinterpret_cast(Send(this, UDM_GETBUDDY))); } +int CSpinButtonCtrl::SetPos(int nPos) { return static_cast(SendMessage(UDM_SETPOS32, 0, static_cast(nPos))); } +int CSpinButtonCtrl::GetPos() const { return static_cast(Send(this, UDM_GETPOS)); } +int CSpinButtonCtrl::GetPos32(BOOL* lpbError) const { return static_cast(Send(this, UDM_GETPOS32, 0, reinterpret_cast(lpbError))); } +void CSpinButtonCtrl::SetRange(short nLower, short nUpper) { SendMessage(UDM_SETRANGE, 0, MAKELPARAM(nUpper, nLower)); } + +void CSpinButtonCtrl::SetRange32(int nLower, int nUpper) { + SendMessage(UDM_SETRANGE32, static_cast(nLower), static_cast(nUpper)); +} + +DWORD CSpinButtonCtrl::GetRange() const { return static_cast(Send(this, UDM_GETRANGE)); } + +void CSpinButtonCtrl::GetRange(int& lower, int& upper) const { + Send(this, UDM_GETRANGE32, reinterpret_cast(&lower), reinterpret_cast(&upper)); +} + +// --------------------------------------------------------------------------------------------- +// CTabCtrl + +BOOL CTabCtrl::Create(DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID) { + return CWnd::Create("SysTabControl32", nullptr, dwStyle, rect, pParentWnd, nID); +} + +int CTabCtrl::GetItemCount() const { + return OnMain([&]() -> int { + TabExtra* tx = TabsOf(this); + return tx ? static_cast(tx->items.size()) : 0; + }); +} + +BOOL CTabCtrl::GetItem(int nItem, TCITEM* pTabCtrlItem) const { + return OnMain([&]() -> BOOL { + TabExtra* tx = TabsOf(this); + if (!tx || !pTabCtrlItem || nItem < 0 || nItem >= static_cast(tx->items.size())) + return FALSE; + const auto& item = tx->items[static_cast(nItem)]; + if (pTabCtrlItem->mask & TCIF_TEXT) + CopyText(item.first, pTabCtrlItem->pszText, pTabCtrlItem->cchTextMax); + if (pTabCtrlItem->mask & TCIF_PARAM) + pTabCtrlItem->lParam = item.second; + if (pTabCtrlItem->mask & TCIF_IMAGE) + pTabCtrlItem->iImage = -1; + if (pTabCtrlItem->mask & TCIF_STATE) + pTabCtrlItem->dwState = 0; + return TRUE; + }); +} + +BOOL CTabCtrl::SetItem(int nItem, TCITEM* pTabCtrlItem) { + return OnMain([&]() -> BOOL { + TabExtra* tx = TabsOf(this); + if (!tx || !pTabCtrlItem || nItem < 0 || nItem >= static_cast(tx->items.size())) + return FALSE; + auto& item = tx->items[static_cast(nItem)]; + if ((pTabCtrlItem->mask & TCIF_TEXT) && pTabCtrlItem->pszText) + item.first = ToWx(pTabCtrlItem->pszText); + if (pTabCtrlItem->mask & TCIF_PARAM) + item.second = pTabCtrlItem->lParam; + GetWx()->Refresh(); + return TRUE; + }); +} + +BOOL CTabCtrl::GetItemRect(int nItem, LPRECT lpRect) const { + return OnMain([&]() -> BOOL { + if (!TabsOf(this) || !lpRect) + return FALSE; + std::vector rects = TabRects(GetWx()); + if (nItem < 0 || nItem >= static_cast(rects.size())) + return FALSE; + const wxRect& r = rects[static_cast(nItem)]; + lpRect->left = r.x; + lpRect->top = r.y; + lpRect->right = r.x + r.width; + lpRect->bottom = r.y + r.height; + return TRUE; + }); +} + +int CTabCtrl::GetCurSel() const { + return OnMain([&]() -> int { + TabExtra* tx = TabsOf(this); + return tx ? tx->current : -1; + }); +} + +int CTabCtrl::SetCurSel(int nItem) { + return OnMain([&]() -> int { + TabExtra* tx = TabsOf(this); + if (!tx || nItem < 0 || nItem >= static_cast(tx->items.size())) + return -1; + int old = tx->current; + tx->current = nItem; + GetWx()->Refresh(); + return old; + }); +} + +void CTabCtrl::SetCurFocus(int nItem) { + OnMain([&] { + TabExtra* tx = TabsOf(this); + if (!tx || nItem < 0 || nItem >= static_cast(tx->items.size()) || nItem == tx->current) + return; + wxWindow* w = GetWx(); + NMHDR hdr; + hdr.code = TCN_SELCHANGING; + if (NotifyParentNM(w, &hdr)) + return; + if (!(tx = TabsOf(this))) + return; + tx->current = nItem; + w->Refresh(); + NMHDR changed; + changed.code = TCN_SELCHANGE; + NotifyParentNM(w, &changed); + }); +} + +CSize CTabCtrl::SetItemSize(CSize size) { + return OnMain([&]() -> CSize { + TabExtra* tx = TabsOf(this); + if (!tx) + return CSize(0, 0); + wxSize old = tx->itemSize; + if (GetStyle() & TCS_FIXEDWIDTH) + tx->itemSize.x = size.cx; + tx->itemSize.y = size.cy; + GetWx()->Refresh(); + return CSize(old.x, old.y); + }); +} + +LONG CTabCtrl::InsertItem(int nItem, TCITEM* pTabCtrlItem) { + return OnMain([&]() -> LONG { + TabExtra* tx = TabsOf(this); + if (!tx || !pTabCtrlItem || nItem < 0) + return -1; + int at = std::min(nItem, static_cast(tx->items.size())); + wxString text = (pTabCtrlItem->mask & TCIF_TEXT) && pTabCtrlItem->pszText ? ToWx(pTabCtrlItem->pszText) : wxString(); + LPARAM param = (pTabCtrlItem->mask & TCIF_PARAM) ? pTabCtrlItem->lParam : 0; + tx->items.insert(tx->items.begin() + at, std::make_pair(text, param)); + if (tx->current >= at) + ++tx->current; + if (tx->current < 0) + tx->current = 0; + GetWx()->Refresh(); + return at; + }); +} + +LONG CTabCtrl::InsertItem(int nItem, const char* lpszItem) { + TCITEM item; + memset(&item, 0, sizeof item); + item.mask = TCIF_TEXT; + item.pszText = const_cast(lpszItem); + return InsertItem(nItem, &item); +} + +LONG CTabCtrl::InsertItem(int nItem, const char* lpszItem, int nImage) { + TCITEM item; + memset(&item, 0, sizeof item); + item.mask = TCIF_TEXT | TCIF_IMAGE; + item.pszText = const_cast(lpszItem); + item.iImage = nImage; + return InsertItem(nItem, &item); +} + +BOOL CTabCtrl::DeleteItem(int nItem) { + return OnMain([&]() -> BOOL { + TabExtra* tx = TabsOf(this); + if (!tx || nItem < 0 || nItem >= static_cast(tx->items.size())) + return FALSE; + tx->items.erase(tx->items.begin() + nItem); + if (tx->current == nItem) + tx->current = -1; + else if (tx->current > nItem) + --tx->current; + GetWx()->Refresh(); + return TRUE; + }); +} + +BOOL CTabCtrl::DeleteAllItems() { + return OnMain([&]() -> BOOL { + TabExtra* tx = TabsOf(this); + if (!tx) + return FALSE; + tx->items.clear(); + tx->current = -1; + GetWx()->Refresh(); + return TRUE; + }); +} + +void CTabCtrl::AdjustRect(BOOL bLarger, LPRECT lpRect) { + if (!lpRect) + return; + int header = OnMain([&]() -> int { return m_hWnd ? TabHeaderHeight(GetWx()) : 0; }); + int d = bLarger ? -1 : 1; + lpRect->left += 2 * d; + lpRect->top += (header + 2) * d; + lpRect->right -= 2 * d; + lpRect->bottom -= 2 * d; +} + +// --------------------------------------------------------------------------------------------- +// CToolTipCtrl + +BOOL CToolTipCtrl::Create(CWnd* pParentWnd, DWORD dwStyle) { + if (!pParentWnd || !pParentWnd->m_hWnd) + return FALSE; + RECT r = {0, 0, 0, 0}; + return CWnd::CreateEx(0, "tooltips_class32", nullptr, WS_CHILD | (dwStyle & ~(WS_VISIBLE | WS_POPUP | WS_TABSTOP)), + r, pParentWnd, 0); +} + +BOOL CToolTipCtrl::AddTool(CWnd* pWnd, UINT nIDText, LPCRECT lpRectTool, UINT_PTR nIDTool) { + std::string text; + if (!LoadResourceString(nIDText, text)) + return FALSE; + return AddTool(pWnd, text.c_str(), lpRectTool, nIDTool); +} + +BOOL CToolTipCtrl::AddTool(CWnd* pWnd, const char* lpszText, LPCRECT, UINT_PTR nIDTool) { + if (!pWnd || !pWnd->m_hWnd) + return FALSE; + HWND tool = pWnd->m_hWnd; + return OnMain([&]() -> BOOL { + ToolTipExtra* tips = TipsOf(this); + if (!tips) + return FALSE; + wxString text = lpszText == LPSTR_TEXTCALLBACK ? CallbackToolText(tool, m_hWnd, nIDTool) + : (lpszText ? ToWx(lpszText) : wxString()); + auto it = std::find_if(tips->tools.begin(), tips->tools.end(), + [&](const ToolTipExtra::Tool& t) { return t.hwnd == tool && t.id == nIDTool; }); + if (it == tips->tools.end()) + it = tips->tools.insert(tips->tools.end(), ToolTipExtra::Tool{tool, nIDTool, text}); + else + it->text = text; + ApplyTool(*it, tips->active); + return TRUE; + }); +} + +void CToolTipCtrl::DelTool(CWnd* pWnd, UINT_PTR nIDTool) { + if (!pWnd) + return; + HWND tool = pWnd->m_hWnd; + OnMain([&] { + ToolTipExtra* tips = TipsOf(this); + if (!tips) + return; + auto it = std::find_if(tips->tools.begin(), tips->tools.end(), + [&](const ToolTipExtra::Tool& t) { return t.hwnd == tool && t.id == nIDTool; }); + if (it == tips->tools.end()) + return; + ApplyTool(ToolTipExtra::Tool{it->hwnd, it->id, wxString()}, false); + tips->tools.erase(it); + }); +} + +void CToolTipCtrl::UpdateTipText(const char* lpszText, CWnd* pWnd, UINT_PTR nIDTool) { + if (!pWnd) + return; + HWND tool = pWnd->m_hWnd; + OnMain([&] { + ToolTipExtra* tips = TipsOf(this); + if (!tips) + return; + auto it = std::find_if(tips->tools.begin(), tips->tools.end(), + [&](const ToolTipExtra::Tool& t) { return t.hwnd == tool && t.id == nIDTool; }); + if (it == tips->tools.end()) + it = std::find_if(tips->tools.begin(), tips->tools.end(), + [&](const ToolTipExtra::Tool& t) { return t.hwnd == tool; }); + if (it == tips->tools.end()) + return; + it->text = lpszText == LPSTR_TEXTCALLBACK ? CallbackToolText(tool, m_hWnd, nIDTool) + : (lpszText ? ToWx(lpszText) : wxString()); + ApplyTool(*it, tips->active); + }); +} + +void CToolTipCtrl::UpdateTipText(UINT nIDText, CWnd* pWnd, UINT_PTR nIDTool) { + std::string text; + LoadResourceString(nIDText, text); + UpdateTipText(text.c_str(), pWnd, nIDTool); +} + +void CToolTipCtrl::RelayEvent(LPMSG) {} + +void CToolTipCtrl::Activate(BOOL bActivate) { + OnMain([&] { + ToolTipExtra* tips = TipsOf(this); + if (!tips || tips->active == (bActivate != FALSE)) + return; + tips->active = bActivate != FALSE; + for (const auto& t : tips->tools) + ApplyTool(t, tips->active); + }); +} + +void CToolTipCtrl::SetDelayTime(UINT nDelay) { SetDelayTime(nDelay, TTDT_AUTOMATIC); } + +void CToolTipCtrl::SetDelayTime(DWORD dwDuration, int iTime) { + OnMain([&] { + long ms = static_cast(dwDuration); + switch (iTime) { + case TTDT_AUTOPOP: + wxToolTip::SetAutoPop(ms); + break; + case TTDT_RESHOW: + wxToolTip::SetReshow(ms); + break; + default: + wxToolTip::SetDelay(ms); + break; + } + }); +} + +int CToolTipCtrl::SetMaxTipWidth(int iWidth) { + return OnMain([&]() -> int { + ToolTipExtra* tips = TipsOf(this); + if (!tips) + return -1; + int old = tips->maxWidth; + tips->maxWidth = iWidth; + return old; + }); +} + +int CToolTipCtrl::GetToolCount() const { + return OnMain([&]() -> int { + ToolTipExtra* tips = TipsOf(this); + return tips ? static_cast(tips->tools.size()) : 0; + }); +} + +// --------------------------------------------------------------------------------------------- +// CRichEditCtrl + +BOOL CRichEditCtrl::Create(DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID) { + return CWnd::Create("RichEdit20A", nullptr, dwStyle, rect, pParentWnd, nID); +} + +BOOL CRichEditCtrl::CanUndo() const { return static_cast(Send(this, EM_CANUNDO)); } +BOOL CRichEditCtrl::CanRedo() const { return static_cast(Send(this, EM_CANREDO)); } +int CRichEditCtrl::GetLineCount() const { return static_cast(Send(this, EM_GETLINECOUNT)); } +BOOL CRichEditCtrl::GetModify() const { return static_cast(Send(this, EM_GETMODIFY)); } +void CRichEditCtrl::SetModify(BOOL bModified) { SendMessage(EM_SETMODIFY, static_cast(bModified)); } +void CRichEditCtrl::GetRect(LPRECT lpRect) const { Send(this, EM_GETRECT, 0, reinterpret_cast(lpRect)); } + +CPoint CRichEditCtrl::GetCharPos(long lChar) const { + POINT pt = {0, 0}; + Send(this, EM_POSFROMCHAR, reinterpret_cast(&pt), static_cast(lChar)); + return CPoint(pt.x, pt.y); +} + +int CRichEditCtrl::GetLine(int nIndex, char* lpszBuffer) const { + return static_cast(Send(this, EM_GETLINE, static_cast(nIndex), reinterpret_cast(lpszBuffer))); +} + +int CRichEditCtrl::GetLine(int nIndex, char* lpszBuffer, int nMaxLength) const { + if (!lpszBuffer || nMaxLength < static_cast(sizeof(WORD))) + return 0; + *reinterpret_cast(lpszBuffer) = static_cast(nMaxLength); + return GetLine(nIndex, lpszBuffer); +} + +BOOL CRichEditCtrl::CanPaste(UINT nFormat) const { return static_cast(Send(this, EM_CANPASTE, nFormat)); } + +void CRichEditCtrl::GetSel(long& nStartChar, long& nEndChar) const { + CHARRANGE cr = {0, 0}; + Send(this, EM_EXGETSEL, 0, reinterpret_cast(&cr)); + nStartChar = cr.cpMin; + nEndChar = cr.cpMax; +} + +void CRichEditCtrl::GetSel(CHARRANGE& cr) const { Send(this, EM_EXGETSEL, 0, reinterpret_cast(&cr)); } +void CRichEditCtrl::LimitText(long nChars) { SendMessage(EM_EXLIMITTEXT, 0, static_cast(nChars)); } +long CRichEditCtrl::LineFromChar(long nIndex) const { return static_cast(Send(this, EM_EXLINEFROMCHAR, 0, static_cast(nIndex))); } + +void CRichEditCtrl::SetSel(long nStartChar, long nEndChar) { + CHARRANGE cr = {static_cast(nStartChar), static_cast(nEndChar)}; + SendMessage(EM_EXSETSEL, 0, reinterpret_cast(&cr)); +} + +void CRichEditCtrl::SetSel(CHARRANGE& cr) { SendMessage(EM_EXSETSEL, 0, reinterpret_cast(&cr)); } + +DWORD CRichEditCtrl::GetDefaultCharFormat(CHARFORMAT& cf) const { + cf.cbSize = sizeof(CHARFORMAT); + return static_cast(Send(this, EM_GETCHARFORMAT, 0, reinterpret_cast(&cf))); +} + +DWORD CRichEditCtrl::GetSelectionCharFormat(CHARFORMAT& cf) const { + cf.cbSize = sizeof(CHARFORMAT); + return static_cast(Send(this, EM_GETCHARFORMAT, SCF_SELECTION, reinterpret_cast(&cf))); +} + +long CRichEditCtrl::GetLimitText() const { return static_cast(Send(this, EM_GETLIMITTEXT)); } + +DWORD CRichEditCtrl::GetParaFormat(PARAFORMAT& pf) const { + pf.cbSize = sizeof(PARAFORMAT); + return static_cast(Send(this, EM_GETPARAFORMAT, 0, reinterpret_cast(&pf))); +} + +long CRichEditCtrl::GetSelText(char* lpBuf) const { + return static_cast(Send(this, EM_GETSELTEXT, 0, reinterpret_cast(lpBuf))); +} + +CString CRichEditCtrl::GetSelText() const { + CHARRANGE cr = {0, 0}; + Send(this, EM_EXGETSEL, 0, reinterpret_cast(&cr)); + int len = std::max(0, static_cast(cr.cpMax - cr.cpMin)); + CString s; + char* buf = s.GetBuffer(len + 1); + buf[0] = 0; + long n = static_cast(Send(this, EM_GETSELTEXT, 0, reinterpret_cast(buf))); + s.ReleaseBuffer(static_cast(std::min(std::max(n, 0), len))); + return s; +} + +WORD CRichEditCtrl::GetSelectionType() const { return static_cast(Send(this, EM_SELECTIONTYPE)); } + +COLORREF CRichEditCtrl::SetBackgroundColor(BOOL bSysColor, COLORREF cr) { + return static_cast(SendMessage(EM_SETBKGNDCOLOR, static_cast(bSysColor), static_cast(cr))); +} + +BOOL CRichEditCtrl::SetDefaultCharFormat(CHARFORMAT& cf) { + cf.cbSize = sizeof(CHARFORMAT); + return static_cast(SendMessage(EM_SETCHARFORMAT, 0, reinterpret_cast(&cf))); +} + +BOOL CRichEditCtrl::SetSelectionCharFormat(CHARFORMAT& cf) { + cf.cbSize = sizeof(CHARFORMAT); + return static_cast(SendMessage(EM_SETCHARFORMAT, SCF_SELECTION, reinterpret_cast(&cf))); +} + +BOOL CRichEditCtrl::SetWordCharFormat(CHARFORMAT& cf) { + cf.cbSize = sizeof(CHARFORMAT); + return static_cast(SendMessage(EM_SETCHARFORMAT, SCF_SELECTION | SCF_WORD, reinterpret_cast(&cf))); +} + +BOOL CRichEditCtrl::SetParaFormat(PARAFORMAT& pf) { + pf.cbSize = sizeof(PARAFORMAT); + return static_cast(SendMessage(EM_SETPARAFORMAT, 0, reinterpret_cast(&pf))); +} + +BOOL CRichEditCtrl::SetReadOnly(BOOL bReadOnly) { + return static_cast(SendMessage(EM_SETREADONLY, static_cast(bReadOnly))); +} + +long CRichEditCtrl::GetTextLength() const { return static_cast(Send(this, WM_GETTEXTLENGTH)); } +void CRichEditCtrl::EmptyUndoBuffer() { SendMessage(EM_EMPTYUNDOBUFFER); } +int CRichEditCtrl::LineIndex(int nLine) const { return static_cast(Send(this, EM_LINEINDEX, static_cast(nLine))); } +int CRichEditCtrl::LineLength(int nLine) const { return static_cast(Send(this, EM_LINELENGTH, static_cast(nLine))); } + +void CRichEditCtrl::LineScroll(int nLines, int nChars) { + SendMessage(EM_LINESCROLL, static_cast(nChars), static_cast(nLines)); +} + +void CRichEditCtrl::ReplaceSel(const char* lpszNewText, BOOL bCanUndo) { + SendMessage(EM_REPLACESEL, static_cast(bCanUndo), reinterpret_cast(lpszNewText ? lpszNewText : "")); +} + +long CRichEditCtrl::StreamIn(int nFormat, EDITSTREAM& es) { + return static_cast(SendMessage(EM_STREAMIN, static_cast(nFormat), reinterpret_cast(&es))); +} + +long CRichEditCtrl::StreamOut(int nFormat, EDITSTREAM& es) { + return static_cast(SendMessage(EM_STREAMOUT, static_cast(nFormat), reinterpret_cast(&es))); +} + +long CRichEditCtrl::FindText(DWORD dwFlags, FINDTEXTEX* pFindText) const { + return static_cast(Send(this, EM_FINDTEXTEX, dwFlags, reinterpret_cast(pFindText))); +} + +BOOL CRichEditCtrl::Undo() { return static_cast(SendMessage(EM_UNDO)); } +BOOL CRichEditCtrl::Redo() { return static_cast(SendMessage(EM_REDO)); } +void CRichEditCtrl::Clear() { SendMessage(WM_CLEAR); } +void CRichEditCtrl::Copy() { SendMessage(WM_COPY); } +void CRichEditCtrl::Cut() { SendMessage(WM_CUT); } +void CRichEditCtrl::Paste() { SendMessage(WM_PASTE); } +int CRichEditCtrl::GetFirstVisibleLine() const { return static_cast(Send(this, EM_GETFIRSTVISIBLELINE)); } + +// --------------------------------------------------------------------------------------------- +// CBitmapButton + +BOOL CBitmapButton::LoadBitmaps(const char* lpszBitmapResource, const char* lpszBitmapResourceSel, + const char* lpszBitmapResourceFocus, const char* lpszBitmapResourceDisabled) { + m_bitmap.DeleteObject(); + m_bitmapSel.DeleteObject(); + m_bitmapFocus.DeleteObject(); + m_bitmapDisabled.DeleteObject(); + if (!lpszBitmapResource || !m_bitmap.LoadBitmap(lpszBitmapResource)) + return FALSE; + BOOL all = TRUE; + if (lpszBitmapResourceSel && !m_bitmapSel.LoadBitmap(lpszBitmapResourceSel)) + all = FALSE; + if (lpszBitmapResourceFocus && !m_bitmapFocus.LoadBitmap(lpszBitmapResourceFocus)) + all = FALSE; + if (lpszBitmapResourceDisabled && !m_bitmapDisabled.LoadBitmap(lpszBitmapResourceDisabled)) + all = FALSE; + if (m_hWnd) + Invalidate(); + return all; +} + +BOOL CBitmapButton::LoadBitmaps(UINT nIDBitmapResource, UINT nIDBitmapResourceSel, UINT nIDBitmapResourceFocus, + UINT nIDBitmapResourceDisabled) { + m_bitmap.DeleteObject(); + m_bitmapSel.DeleteObject(); + m_bitmapFocus.DeleteObject(); + m_bitmapDisabled.DeleteObject(); + if (!nIDBitmapResource || !m_bitmap.LoadBitmap(nIDBitmapResource)) + return FALSE; + BOOL all = TRUE; + if (nIDBitmapResourceSel && !m_bitmapSel.LoadBitmap(nIDBitmapResourceSel)) + all = FALSE; + if (nIDBitmapResourceFocus && !m_bitmapFocus.LoadBitmap(nIDBitmapResourceFocus)) + all = FALSE; + if (nIDBitmapResourceDisabled && !m_bitmapDisabled.LoadBitmap(nIDBitmapResourceDisabled)) + all = FALSE; + if (m_hWnd) + Invalidate(); + return all; +} + +BOOL CBitmapButton::AutoLoad(UINT nID, CWnd* pParent) { + if (!SubclassDlgItem(nID, pParent)) + return FALSE; + CString name; + GetWindowText(name); + LoadBitmaps(name + "U", name + "D", name + "F", name + "X"); + if (!m_bitmap.m_hObject) + return FALSE; + SizeToContent(); + return TRUE; +} + +void CBitmapButton::SizeToContent() { + BITMAP bm; + memset(&bm, 0, sizeof bm); + if (!m_bitmap.m_hObject || m_bitmap.GetObject(sizeof bm, &bm) <= 0) + return; + SetWindowPos(nullptr, -1, -1, bm.bmWidth, bm.bmHeight, SWP_NOMOVE | SWP_NOZORDER | SWP_NOREDRAW | SWP_NOACTIVATE); +} + +void CBitmapButton::DrawItem(LPDRAWITEMSTRUCT lpDIS) { + if (!lpDIS) + return; + CDC* pDC = CDC::FromHandle(lpDIS->hDC); + if (!pDC) + return; + CRect rect(lpDIS->rcItem.left, lpDIS->rcItem.top, lpDIS->rcItem.right, lpDIS->rcItem.bottom); + if (!m_bitmap.m_hObject) { + wxDC* dc = pDC->GetWx(); + if (!dc || !m_hWnd) + return; + wxRect r(rect.left, rect.top, rect.Width(), rect.Height()); + int flags = 0; + if (lpDIS->itemState & ODS_SELECTED) + flags |= wxCONTROL_PRESSED; + if (lpDIS->itemState & ODS_DISABLED) + flags |= wxCONTROL_DISABLED; + if (lpDIS->itemState & ODS_FOCUS) + flags |= wxCONTROL_FOCUSED; + wxRendererNative::Get().DrawPushButton(GetWx(), *dc, r, flags); + CString text; + GetWindowText(text); + dc->SetFont(GetWx()->GetFont()); + dc->SetTextForeground(wxSystemSettings::GetColour((lpDIS->itemState & ODS_DISABLED) ? wxSYS_COLOUR_GRAYTEXT + : wxSYS_COLOUR_BTNTEXT)); + dc->DrawLabel(wxStripMenuCodes(ToWx(text), wxStrip_Mnemonics), r, wxALIGN_CENTER); + return; + } + CBitmap* pBitmap = &m_bitmap; + UINT state = lpDIS->itemState; + if ((state & ODS_SELECTED) && m_bitmapSel.m_hObject) + pBitmap = &m_bitmapSel; + else if ((state & ODS_FOCUS) && m_bitmapFocus.m_hObject) + pBitmap = &m_bitmapFocus; + else if ((state & ODS_DISABLED) && m_bitmapDisabled.m_hObject) + pBitmap = &m_bitmapDisabled; + CDC memDC; + if (!memDC.CreateCompatibleDC(pDC)) + return; + CBitmap* pOld = memDC.SelectObject(pBitmap); + if (!pOld) + return; + pDC->BitBlt(rect.left, rect.top, rect.Width(), rect.Height(), &memDC, 0, 0, SRCCOPY); + memDC.SelectObject(pOld); +} diff --git a/port/mfcwx/src/listctrl.cpp b/port/mfcwx/src/listctrl.cpp new file mode 100644 index 000000000..1cec63ca6 --- /dev/null +++ b/port/mfcwx/src/listctrl.cpp @@ -0,0 +1,1173 @@ +#include "controls_internal.h" + +#include "afxcmn.h" + +#include +#include +#include + +#include + +namespace mfcwx { + +namespace { + +struct ListViewExtra { + DWORD exStyle = 0; + int selectionMark = -1; + COLORREF textBkColor = CLR_DEFAULT; + CImageList* imageLists[3] = {nullptr, nullptr, nullptr}; + bool suppressActivate = false; +}; + +ListViewExtra& ListExtra(wxListCtrl* lc) { return Extra(EnsureState(lc)); } + +UINT StateImage(int index) { return static_cast(index) << 12; } + +wxWindow* ListMainWindow(wxListCtrl* lc) { +#if !defined(__WXMSW__) && !defined(__WXQT__) + for (wxWindow* c : lc->GetChildren()) + if (static_cast(c) == static_cast(lc->m_mainWin)) + return c; +#endif + return lc; +} + +wxPoint ToListPoint(wxListCtrl* lc, wxWindow* from, const wxPoint& p) { + return from == lc ? p : lc->ScreenToClient(from->ClientToScreen(p)); +} + +wxString ItemText(const char* p) { + if (!p || p == LPSTR_TEXTCALLBACK) + return wxString(); + return ToWx(p); +} + +int CopyText(const wxString& s, char* buf, int cch) { + if (!buf || cch <= 0) + return 0; + std::string a = FromWx(s); + size_t n = std::min(a.size(), static_cast(cch - 1)); + memcpy(buf, a.data(), n); + buf[n] = 0; + return static_cast(n); +} + +bool ValidItem(wxListCtrl* lc, long item) { return item >= 0 && item < lc->GetItemCount(); } + +bool ValidColumn(wxListCtrl* lc, int col) { return col >= 0 && (col == 0 || col < lc->GetColumnCount()); } + +UINT ItemState(wxListCtrl* lc, long item, UINT mask) { + long wxMask = 0; + if (mask & LVIS_SELECTED) + wxMask |= wxLIST_STATE_SELECTED; + if (mask & LVIS_FOCUSED) + wxMask |= wxLIST_STATE_FOCUSED; + long s = wxMask ? lc->GetItemState(item, wxMask) : 0; + UINT state = 0; + if (s & wxLIST_STATE_SELECTED) + state |= LVIS_SELECTED; + if (s & wxLIST_STATE_FOCUSED) + state |= LVIS_FOCUSED; + if ((mask & LVIS_STATEIMAGEMASK) && lc->HasCheckBoxes()) + state |= StateImage(lc->IsItemChecked(item) ? 2 : 1); + return state & mask; +} + +void SetItemStateOne(wxListCtrl* lc, long item, UINT state, UINT mask) { + long wxMask = 0; + long wxState = 0; + if (mask & LVIS_SELECTED) { + wxMask |= wxLIST_STATE_SELECTED; + if (state & LVIS_SELECTED) + wxState |= wxLIST_STATE_SELECTED; + } + if (mask & LVIS_FOCUSED) { + wxMask |= wxLIST_STATE_FOCUSED; + if (state & LVIS_FOCUSED) + wxState |= wxLIST_STATE_FOCUSED; + } + if (wxMask) + lc->SetItemState(item, wxState, wxMask); + if ((mask & LVIS_STATEIMAGEMASK) && lc->HasCheckBoxes()) + lc->CheckItem(item, ((state & LVIS_STATEIMAGEMASK) >> 12) == 2); +} + +bool SetItemStates(wxListCtrl* lc, long item, UINT state, UINT mask) { + if (item == -1) { + for (long i = 0; i < lc->GetItemCount(); ++i) + SetItemStateOne(lc, i, state, mask); + return true; + } + if (!ValidItem(lc, item)) + return false; + SetItemStateOne(lc, item, state, mask); + return true; +} + +wxListColumnFormat AlignFromFormat(int fmt) { + switch (fmt & LVCFMT_JUSTIFYMASK) { + case LVCFMT_RIGHT: return wxLIST_FORMAT_RIGHT; + case LVCFMT_CENTER: return wxLIST_FORMAT_CENTRE; + default: return wxLIST_FORMAT_LEFT; + } +} + +int FormatFromAlign(wxListColumnFormat align) { + switch (align) { + case wxLIST_FORMAT_RIGHT: return LVCFMT_RIGHT; + case wxLIST_FORMAT_CENTRE: return LVCFMT_CENTER; + default: return LVCFMT_LEFT; + } +} + +int RectCode(int code) { + switch (code) { + case LVIR_ICON: return wxLIST_RECT_ICON; + case LVIR_LABEL: return wxLIST_RECT_LABEL; + default: return wxLIST_RECT_BOUNDS; + } +} + +bool ItemRect(wxListCtrl* lc, long item, int subItem, int code, RECT* out) { + if (!out || !ValidItem(lc, item)) + return false; + wxRect r; + bool ok = subItem < 0 ? lc->GetItemRect(item, r, RectCode(code)) + : subItem < lc->GetColumnCount() && lc->GetSubItemRect(item, subItem, r, RectCode(code)); + if (!ok) + return false; + out->left = r.x; + out->top = r.y; + out->right = r.x + r.width; + out->bottom = r.y + r.height; + return true; +} + +struct ListHit { + long item = -1; + int subItem = -1; + UINT flags = LVHT_NOWHERE; +}; + +ListHit HitTestList(wxListCtrl* lc, const wxPoint& pt) { + ListHit hit; + long count = lc->GetItemCount(); + long first = 0; + long last = count - 1; + bool report = lc->InReportView(); + if (report) { + first = std::max(0L, lc->GetTopItem()); + last = std::min(count - 1, first + lc->GetCountPerPage() + 1); + } + for (long i = first; i <= last; ++i) { + wxRect r; + if (!lc->GetItemRect(i, r, wxLIST_RECT_BOUNDS) || !r.Contains(pt)) + continue; + hit.item = i; + hit.subItem = 0; + hit.flags = LVHT_ONITEMLABEL; + if (report) { + for (int c = 0; c < lc->GetColumnCount(); ++c) { + wxRect sr; + if (lc->GetSubItemRect(i, c, sr) && pt.x >= sr.x && pt.x < sr.x + sr.width) { + hit.subItem = c; + break; + } + } + } + wxRect icon; + if (lc->HasCheckBoxes() && hit.subItem == 0 && + pt.x < r.x + wxRendererNative::Get().GetCheckBoxSize(lc).x + 4) + hit.flags = LVHT_ONITEMSTATEICON; + else if (lc->GetItemRect(i, icon, wxLIST_RECT_ICON) && icon.width > 0 && icon.Contains(pt)) + hit.flags = LVHT_ONITEMICON; + return hit; + } + return hit; +} + +void ApplyExtendedStyle(wxListCtrl* lc, DWORD oldStyle, DWORD newStyle) { + if ((oldStyle ^ newStyle) & LVS_EX_CHECKBOXES) + lc->EnableCheckBoxes((newStyle & LVS_EX_CHECKBOXES) != 0); + if ((oldStyle ^ newStyle) & LVS_EX_GRIDLINES) { + bool on = (newStyle & LVS_EX_GRIDLINES) != 0; + lc->SetSingleStyle(wxLC_HRULES, on); + lc->SetSingleStyle(wxLC_VRULES, on); + lc->Refresh(); + } +} + +LRESULT ListNotify(wxListCtrl* lc, UINT code, long item, int subItem, UINT newState = 0, UINT oldState = 0, + UINT changed = 0, const wxPoint& pt = wxPoint(0, 0)) { + NMITEMACTIVATE nm; + memset(&nm, 0, sizeof nm); + nm.hdr.code = code; + nm.iItem = static_cast(item); + nm.iSubItem = subItem; + nm.uNewState = newState; + nm.uOldState = oldState; + nm.uChanged = changed; + nm.ptAction.x = pt.x; + nm.ptAction.y = pt.y; + if (ValidItem(lc, item)) + nm.lParam = static_cast(lc->GetItemData(item)); + return NotifyParentNM(lc, &nm.hdr); +} + +void MouseNotify(wxListCtrl* lc, wxWindow* from, UINT code, const wxMouseEvent& e) { + wxPoint pt = ToListPoint(lc, from, e.GetPosition()); + ListHit hit = HitTestList(lc, pt); + ListNotify(lc, code, hit.item, hit.item >= 0 ? hit.subItem : -1, 0, 0, 0, pt); +} + +} // namespace + +void BindListViewEvents(wxWindow* w) { + auto* lc = wxDynamicCast(w, wxListCtrl); + if (!lc) + return; + lc->Bind(wxEVT_LIST_ITEM_SELECTED, [lc](wxListEvent& e) { + if (WindowState* st = GetState(lc)) + Extra(*st).selectionMark = static_cast(e.GetIndex()); + ListNotify(lc, LVN_ITEMCHANGED, e.GetIndex(), 0, LVIS_SELECTED | LVIS_FOCUSED, 0, LVIF_STATE); + }); + lc->Bind(wxEVT_LIST_ITEM_DESELECTED, + [lc](wxListEvent& e) { ListNotify(lc, LVN_ITEMCHANGED, e.GetIndex(), 0, 0, LVIS_SELECTED, LVIF_STATE); }); + lc->Bind(wxEVT_LIST_ITEM_CHECKED, [lc](wxListEvent& e) { + ListNotify(lc, LVN_ITEMCHANGED, e.GetIndex(), 0, StateImage(2), StateImage(1), LVIF_STATE); + }); + lc->Bind(wxEVT_LIST_ITEM_UNCHECKED, [lc](wxListEvent& e) { + ListNotify(lc, LVN_ITEMCHANGED, e.GetIndex(), 0, StateImage(1), StateImage(2), LVIF_STATE); + }); + lc->Bind(wxEVT_LIST_COL_CLICK, + [lc](wxListEvent& e) { ListNotify(lc, LVN_COLUMNCLICK, -1, std::max(0, e.GetColumn())); }); + lc->Bind(wxEVT_LIST_KEY_DOWN, [lc](wxListEvent& e) { + NMLVKEYDOWN nm; + memset(&nm, 0, sizeof nm); + nm.hdr.code = LVN_KEYDOWN; + nm.wVKey = static_cast(VirtualKeyFromWx(e.GetKeyCode())); + NotifyParentNM(lc, &nm.hdr); + e.Skip(); + }); + lc->Bind(wxEVT_LIST_ITEM_ACTIVATED, [lc](wxListEvent&) { + WindowState* st = GetState(lc); + if (!st) + return; + ListViewExtra& ex = Extra(*st); + if (ex.suppressActivate) { + ex.suppressActivate = false; + return; + } + NMHDR hdr; + hdr.code = NM_RETURN; + NotifyParentNM(lc, &hdr); + }); + + wxWindow* main = ListMainWindow(lc); + main->Bind(wxEVT_LEFT_UP, [lc, main](wxMouseEvent& e) { + MouseNotify(lc, main, NM_CLICK, e); + e.Skip(); + }); + main->Bind(wxEVT_LEFT_DCLICK, [lc, main](wxMouseEvent& e) { + if (WindowState* st = GetState(lc)) { + Extra(*st).suppressActivate = true; + lc->CallAfter([lc] { + if (WindowState* s = GetState(lc)) + Extra(*s).suppressActivate = false; + }); + } + MouseNotify(lc, main, NM_DBLCLK, e); + e.Skip(); + }); + main->Bind(wxEVT_RIGHT_UP, [lc, main](wxMouseEvent& e) { + MouseNotify(lc, main, NM_RCLICK, e); + e.Skip(); + }); + if (main != lc) { + main->Bind(wxEVT_SET_FOCUS, [lc](wxFocusEvent& e) { + if (WindowState* st = GetState(lc)) + NotifyFocusChange(lc, *st, true); + e.Skip(); + }); + main->Bind(wxEVT_KILL_FOCUS, [lc](wxFocusEvent& e) { + if (WindowState* st = GetState(lc)) + NotifyFocusChange(lc, *st, false); + e.Skip(); + }); + } +} + +LRESULT ListViewProc(wxWindow* w, WindowState& st, UINT msg, WPARAM wParam, LPARAM lParam, bool& handled) { + handled = true; + auto* lc = wxDynamicCast(w, wxListCtrl); + if (!lc) { + handled = false; + return 0; + } + ListViewExtra& ex = Extra(st); + switch (msg) { + case LVM_GETITEMCOUNT: + return lc->GetItemCount(); + case LVM_GETTOPINDEX: + return lc->InReportView() || lc->HasFlag(wxLC_LIST) ? std::max(0L, lc->GetTopItem()) : 0; + case LVM_GETITEMTEXT: { + auto* item = reinterpret_cast(lParam); + if (!item) + return 0; + long index = static_cast(wParam); + wxString text; + if (ValidItem(lc, index) && ValidColumn(lc, item->iSubItem)) + text = lc->GetItemText(index, item->iSubItem); + return CopyText(text, item->pszText, item->cchTextMax); + } + case LVM_GETITEMRECT: { + auto* r = reinterpret_cast(lParam); + return r && ItemRect(lc, static_cast(wParam), -1, r->left, r); + } + case LVM_GETSUBITEMRECT: { + auto* r = reinterpret_cast(lParam); + if (!r) + return FALSE; + int sub = r->top; + int code = r->left; + if (sub == 0 && code == LVIR_BOUNDS) + sub = -1; + return ItemRect(lc, static_cast(wParam), sub, code, r); + } + case LVM_SUBITEMHITTEST: { + auto* info = reinterpret_cast(lParam); + if (!info) + return -1; + ListHit hit = HitTestList(lc, wxPoint(info->pt.x, info->pt.y)); + info->flags = hit.flags; + info->iItem = static_cast(hit.item); + info->iSubItem = hit.item >= 0 ? hit.subItem : -1; + return hit.item; + } + case LVM_SETEXTENDEDLISTVIEWSTYLE: { + DWORD mask = wParam ? static_cast(wParam) : 0xFFFFFFFFu; + DWORD old = ex.exStyle; + ex.exStyle = (old & ~mask) | (static_cast(lParam) & mask); + ApplyExtendedStyle(lc, old, ex.exStyle); + return static_cast(old); + } + case LVM_GETEXTENDEDLISTVIEWSTYLE: + return static_cast(ex.exStyle); + case WM_GETDLGCODE: + return DLGC_WANTARROWS | DLGC_WANTCHARS; + default: + handled = false; + return 0; + } +} + +} // namespace mfcwx + +using namespace mfcwx; + +namespace { + +wxListCtrl* ListOf(const CWnd* w) { return w->m_hWnd ? wxDynamicCast(w->GetWx(), wxListCtrl) : nullptr; } + +wxImageList* WxImageList(const CImageList* il) { + return il ? reinterpret_cast(il->m_hImageList) : nullptr; +} + +struct SortContext { + int (*compare)(LPARAM, LPARAM, LPARAM); + LPARAM data; +}; + +int wxCALLBACK SortTrampoline(wxIntPtr item1, wxIntPtr item2, wxIntPtr sortData) { + auto* ctx = reinterpret_cast(sortData); + return ctx->compare(static_cast(item1), static_cast(item2), ctx->data); +} + +int AddBitmap(wxImageList* il, const wxBitmap& bmp, COLORREF crMask) { + if (!il || !bmp.IsOk()) + return -1; + int cx = 0; + int cy = 0; + il->GetSize(0, cx, cy); + if (cx <= 0 || cy <= 0) + return -1; + int first = -1; + int count = std::max(1, bmp.GetWidth() / cx); + for (int i = 0; i < count; ++i) { + wxBitmap part = bmp.GetWidth() > cx ? bmp.GetSubBitmap(wxRect(i * cx, 0, cx, std::min(cy, bmp.GetHeight()))) + : bmp; + if (part.GetWidth() != cx || part.GetHeight() != cy) + part = wxBitmap(part.ConvertToImage().Size(wxSize(cx, cy), wxPoint(0, 0))); + int index = crMask == CLR_NONE ? il->Add(part) : il->Add(part, ToWxColour(crMask)); + if (first < 0) + first = index; + } + return first; +} + +} // namespace + +// --------------------------------------------------------------------------------------------- +// CImageList + +IMPLEMENT_DYNCREATE(CImageList, CObject) + +CImageList::CImageList() : m_hImageList(nullptr) {} + +CImageList::~CImageList() { DeleteImageList(); } + +BOOL CImageList::Create(int cx, int cy, UINT nFlags, int nInitial, int) { + return OnMain([&]() -> BOOL { + DeleteImageList(); + m_hImageList = reinterpret_cast(new wxImageList(cx, cy, (nFlags & ILC_MASK) != 0, std::max(1, nInitial))); + return TRUE; + }); +} + +BOOL CImageList::Create(UINT nBitmapID, int cx, int, COLORREF crMask) { + return OnMain([&]() -> BOOL { + wxBitmap bmp = LoadBitmapResource(ResRef::FromId(nBitmapID)); + if (!bmp.IsOk() || cx <= 0) + return FALSE; + DeleteImageList(); + auto* il = new wxImageList(cx, bmp.GetHeight(), crMask != CLR_NONE, std::max(1, bmp.GetWidth() / cx)); + m_hImageList = reinterpret_cast(il); + AddBitmap(il, bmp, crMask); + return TRUE; + }); +} + +BOOL CImageList::DeleteImageList() { + if (!m_hImageList) + return FALSE; + wxImageList* il = WxImageList(this); + m_hImageList = nullptr; + OnMain([&] { delete il; }); + return TRUE; +} + +int CImageList::Add(CBitmap* pbmImage, CBitmap* pbmMask) { + return OnMain([&]() -> int { + wxBitmap* bmp = pbmImage ? BitmapFromHandle(static_cast(pbmImage->m_hObject)) : nullptr; + if (!bmp || !bmp->IsOk()) + return -1; + wxBitmap* mask = pbmMask ? BitmapFromHandle(static_cast(pbmMask->m_hObject)) : nullptr; + if (!mask || !mask->IsOk()) + return AddBitmap(WxImageList(this), *bmp, CLR_NONE); + wxImage img = bmp->ConvertToImage(); + wxImage m = mask->ConvertToImage(); + if (!img.HasAlpha()) + img.InitAlpha(); + int w = std::min(img.GetWidth(), m.GetWidth()); + int h = std::min(img.GetHeight(), m.GetHeight()); + for (int y = 0; y < h; ++y) + for (int x = 0; x < w; ++x) + if (m.GetRed(x, y) > 127) + img.SetAlpha(x, y, 0); + return AddBitmap(WxImageList(this), wxBitmap(img), CLR_NONE); + }); +} + +int CImageList::Add(CBitmap* pbmImage, COLORREF crMask) { + return OnMain([&]() -> int { + wxBitmap* bmp = pbmImage ? BitmapFromHandle(static_cast(pbmImage->m_hObject)) : nullptr; + return bmp ? AddBitmap(WxImageList(this), *bmp, crMask) : -1; + }); +} + +int CImageList::Add(HICON hIcon) { + return OnMain([&]() -> int { + auto* icon = reinterpret_cast(hIcon); + wxImageList* il = WxImageList(this); + if (!il || !icon || !icon->IsOk()) + return -1; + return il->Add(*icon); + }); +} + +int CImageList::GetImageCount() const { + return OnMain([&]() -> int { + wxImageList* il = WxImageList(this); + return il ? il->GetImageCount() : 0; + }); +} + +BOOL CImageList::Draw(CDC* pDC, int nImage, POINT pt, UINT nStyle) { + return OnMain([&]() -> BOOL { + wxImageList* il = WxImageList(this); + wxDC* dc = pDC ? pDC->GetWx() : nullptr; + if (!il || !dc || nImage < 0 || nImage >= il->GetImageCount()) + return FALSE; + int flags = (nStyle & ILD_TRANSPARENT) ? wxIMAGELIST_DRAW_TRANSPARENT : wxIMAGELIST_DRAW_NORMAL; + return il->Draw(nImage, *dc, pt.x, pt.y, flags, true); + }); +} + +// --------------------------------------------------------------------------------------------- +// CHeaderCtrl + +IMPLEMENT_DYNAMIC(CHeaderCtrl, CWnd) + +int CHeaderCtrl::GetItemCount() const { + return OnMain([&]() -> int { + wxListCtrl* lc = ListOf(this); + return lc ? lc->GetColumnCount() : 0; + }); +} + +// --------------------------------------------------------------------------------------------- +// CListCtrl + +IMPLEMENT_DYNAMIC(CListCtrl, CWnd) + +BOOL CListCtrl::Create(DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID) { + return CWnd::Create("SysListView32", nullptr, dwStyle, rect, pParentWnd, nID); +} + +COLORREF CListCtrl::GetBkColor() const { + return OnMain([&]() -> COLORREF { + wxListCtrl* lc = ListOf(this); + return lc ? FromWxColour(lc->GetBackgroundColour()) : CLR_NONE; + }); +} + +BOOL CListCtrl::SetBkColor(COLORREF cr) { + return OnMain([&]() -> BOOL { + wxListCtrl* lc = ListOf(this); + if (!lc) + return FALSE; + if (cr == CLR_NONE && lc->GetParent()) + lc->SetBackgroundColour(lc->GetParent()->GetBackgroundColour()); + else if (cr != CLR_NONE) + lc->SetBackgroundColour(ToWxColour(cr)); + lc->Refresh(); + return TRUE; + }); +} + +COLORREF CListCtrl::GetTextColor() const { + return OnMain([&]() -> COLORREF { + wxListCtrl* lc = ListOf(this); + return lc ? FromWxColour(lc->GetTextColour()) : 0; + }); +} + +BOOL CListCtrl::SetTextColor(COLORREF cr) { + return OnMain([&]() -> BOOL { + wxListCtrl* lc = ListOf(this); + if (!lc) + return FALSE; + lc->SetTextColour(ToWxColour(cr)); + lc->Refresh(); + return TRUE; + }); +} + +COLORREF CListCtrl::GetTextBkColor() const { + return OnMain([&]() -> COLORREF { + wxListCtrl* lc = ListOf(this); + if (!lc) + return CLR_NONE; + COLORREF c = ListExtra(lc).textBkColor; + return c == CLR_DEFAULT ? FromWxColour(lc->GetBackgroundColour()) : c; + }); +} + +BOOL CListCtrl::SetTextBkColor(COLORREF cr) { + return OnMain([&]() -> BOOL { + wxListCtrl* lc = ListOf(this); + if (!lc) + return FALSE; + ListExtra(lc).textBkColor = cr; + return TRUE; + }); +} + +CImageList* CListCtrl::GetImageList(int nImageList) const { + return OnMain([&]() -> CImageList* { + wxListCtrl* lc = ListOf(this); + if (!lc || nImageList < 0 || nImageList > 2) + return nullptr; + return ListExtra(lc).imageLists[nImageList]; + }); +} + +CImageList* CListCtrl::SetImageList(CImageList* pImageList, int nImageListType) { + return OnMain([&]() -> CImageList* { + wxListCtrl* lc = ListOf(this); + if (!lc || nImageListType < 0 || nImageListType > 2) + return nullptr; + ListViewExtra& ex = ListExtra(lc); + CImageList* old = ex.imageLists[nImageListType]; + ex.imageLists[nImageListType] = pImageList; + int which = nImageListType == LVSIL_NORMAL ? wxIMAGE_LIST_NORMAL + : nImageListType == LVSIL_SMALL ? wxIMAGE_LIST_SMALL : wxIMAGE_LIST_STATE; + lc->SetImageList(WxImageList(pImageList), which); + return old; + }); +} + +int CListCtrl::GetItemCount() const { return static_cast(::SendMessage(m_hWnd, LVM_GETITEMCOUNT, 0, 0)); } + +BOOL CListCtrl::GetItem(LVITEM* pItem) const { + return OnMain([&]() -> BOOL { + wxListCtrl* lc = ListOf(this); + if (!lc || !pItem || !ValidItem(lc, pItem->iItem) || !ValidColumn(lc, pItem->iSubItem)) + return FALSE; + long item = pItem->iItem; + if (pItem->mask & LVIF_TEXT) + CopyText(lc->GetItemText(item, pItem->iSubItem), pItem->pszText, pItem->cchTextMax); + if (pItem->mask & LVIF_IMAGE) { + wxListItem li; + li.SetId(item); + li.SetColumn(pItem->iSubItem); + li.SetMask(wxLIST_MASK_IMAGE); + pItem->iImage = lc->GetItem(li) ? li.GetImage() : -1; + } + if (pItem->mask & LVIF_PARAM) + pItem->lParam = static_cast(lc->GetItemData(item)); + if (pItem->mask & LVIF_STATE) + pItem->state = ItemState(lc, item, pItem->stateMask); + if (pItem->mask & LVIF_INDENT) + pItem->iIndent = 0; + return TRUE; + }); +} + +BOOL CListCtrl::SetItem(const LVITEM* pItem) { + return OnMain([&]() -> BOOL { + wxListCtrl* lc = ListOf(this); + if (!lc || !pItem || !ValidItem(lc, pItem->iItem) || !ValidColumn(lc, pItem->iSubItem)) + return FALSE; + long item = pItem->iItem; + int col = pItem->iSubItem; + if (pItem->mask & LVIF_TEXT) + lc->SetItem(item, col, ItemText(pItem->pszText)); + if (pItem->mask & LVIF_IMAGE) + lc->SetItemColumnImage(item, col, pItem->iImage); + if (col == 0 && (pItem->mask & LVIF_PARAM)) + lc->SetItemPtrData(item, static_cast(pItem->lParam)); + if (col == 0 && (pItem->mask & LVIF_STATE)) + SetItemStateOne(lc, item, pItem->state, pItem->stateMask); + return TRUE; + }); +} + +BOOL CListCtrl::SetItem(int nItem, int nSubItem, UINT nMask, const char* lpszItem, int nImage, UINT nState, + UINT nStateMask, LPARAM lParam) { + LVITEM lvi; + memset(&lvi, 0, sizeof lvi); + lvi.mask = nMask; + lvi.iItem = nItem; + lvi.iSubItem = nSubItem; + lvi.state = nState; + lvi.stateMask = nStateMask; + lvi.pszText = const_cast(lpszItem); + lvi.iImage = nImage; + lvi.lParam = lParam; + return SetItem(&lvi); +} + +int CListCtrl::GetNextItem(int nItem, int nFlags) const { + return OnMain([&]() -> int { + wxListCtrl* lc = ListOf(this); + if (!lc) + return -1; + int count = lc->GetItemCount(); + UINT want = static_cast(nFlags) & (LVNI_SELECTED | LVNI_FOCUSED | LVNI_CUT | LVNI_DROPHILITED); + if (want & (LVNI_CUT | LVNI_DROPHILITED)) + return -1; + auto matches = [&](int i) { return (ItemState(lc, i, want) & want) == want; }; + if (nFlags & (LVNI_ABOVE | LVNI_TOLEFT)) { + for (int i = std::min(nItem, count) - 1; i >= 0; --i) + if (matches(i)) + return i; + return -1; + } + if (want == LVNI_SELECTED) + return static_cast(lc->GetNextItem(nItem, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED)); + for (int i = nItem < 0 ? 0 : nItem + 1; i < count; ++i) + if (matches(i)) + return i; + return -1; + }); +} + +POSITION CListCtrl::GetFirstSelectedItemPosition() const { + return reinterpret_cast(static_cast(1 + GetNextItem(-1, LVNI_SELECTED))); +} + +int CListCtrl::GetNextSelectedItem(POSITION& pos) const { + int old = static_cast(reinterpret_cast(pos)) - 1; + pos = reinterpret_cast(static_cast(1 + GetNextItem(old, LVNI_SELECTED))); + return old; +} + +BOOL CListCtrl::GetItemRect(int nItem, LPRECT lpRect, UINT nCode) const { + if (!lpRect) + return FALSE; + lpRect->left = static_cast(nCode); + return static_cast(::SendMessage(m_hWnd, LVM_GETITEMRECT, static_cast(nItem), + reinterpret_cast(lpRect))); +} + +BOOL CListCtrl::GetSubItemRect(int iItem, int iSubItem, int nArea, CRect& ref) { + RECT* r = &ref; + r->top = iSubItem; + r->left = nArea; + return static_cast(::SendMessage(m_hWnd, LVM_GETSUBITEMRECT, static_cast(iItem), + reinterpret_cast(r))); +} + +BOOL CListCtrl::GetItemPosition(int nItem, LPPOINT lpPoint) const { + return OnMain([&]() -> BOOL { + wxListCtrl* lc = ListOf(this); + wxPoint p; + if (!lc || !lpPoint || !ValidItem(lc, nItem) || !lc->GetItemPosition(nItem, p)) + return FALSE; + lpPoint->x = p.x; + lpPoint->y = p.y; + return TRUE; + }); +} + +int CListCtrl::GetStringWidth(const char* lpsz) const { + return OnMain([&]() -> int { + wxListCtrl* lc = ListOf(this); + return lc && lpsz ? lc->GetTextExtent(ToWx(lpsz)).x : 0; + }); +} + +BOOL CListCtrl::GetColumn(int nCol, LVCOLUMN* pColumn) const { + return OnMain([&]() -> BOOL { + wxListCtrl* lc = ListOf(this); + if (!lc || !pColumn || nCol < 0 || nCol >= lc->GetColumnCount()) + return FALSE; + wxListItem li; + li.SetMask(wxLIST_MASK_TEXT | wxLIST_MASK_WIDTH | wxLIST_MASK_FORMAT | wxLIST_MASK_IMAGE); + if (!lc->GetColumn(nCol, li)) + return FALSE; + if (pColumn->mask & LVCF_TEXT) + CopyText(li.GetText(), pColumn->pszText, pColumn->cchTextMax); + if (pColumn->mask & LVCF_WIDTH) + pColumn->cx = li.GetWidth(); + if (pColumn->mask & LVCF_FMT) + pColumn->fmt = FormatFromAlign(li.GetAlign()); + if (pColumn->mask & LVCF_IMAGE) + pColumn->iImage = li.GetImage(); + if (pColumn->mask & LVCF_SUBITEM) + pColumn->iSubItem = nCol; + if (pColumn->mask & LVCF_ORDER) + pColumn->iOrder = nCol; + return TRUE; + }); +} + +BOOL CListCtrl::SetColumn(int nCol, const LVCOLUMN* pColumn) { + return OnMain([&]() -> BOOL { + wxListCtrl* lc = ListOf(this); + if (!lc || !pColumn || nCol < 0 || nCol >= lc->GetColumnCount()) + return FALSE; + wxListItem li; + if (pColumn->mask & LVCF_TEXT) + li.SetText(ItemText(pColumn->pszText)); + if (pColumn->mask & LVCF_WIDTH) + li.SetWidth(pColumn->cx); + if (pColumn->mask & LVCF_FMT) + li.SetAlign(AlignFromFormat(pColumn->fmt)); + if (pColumn->mask & LVCF_IMAGE) + li.SetImage(pColumn->iImage); + return lc->SetColumn(nCol, li); + }); +} + +int CListCtrl::GetColumnWidth(int nCol) const { + return OnMain([&]() -> int { + wxListCtrl* lc = ListOf(this); + return lc && nCol >= 0 && nCol < lc->GetColumnCount() ? lc->GetColumnWidth(nCol) : 0; + }); +} + +BOOL CListCtrl::SetColumnWidth(int nCol, int cx) { + return OnMain([&]() -> BOOL { + wxListCtrl* lc = ListOf(this); + if (!lc || nCol < 0 || nCol >= lc->GetColumnCount()) + return FALSE; + int width = cx == LVSCW_AUTOSIZE ? wxLIST_AUTOSIZE : cx == LVSCW_AUTOSIZE_USEHEADER ? wxLIST_AUTOSIZE_USEHEADER : cx; + return lc->SetColumnWidth(nCol, width); + }); +} + +BOOL CListCtrl::GetViewRect(LPRECT lpRect) const { + return OnMain([&]() -> BOOL { + wxListCtrl* lc = ListOf(this); + if (!lc || !lpRect) + return FALSE; + wxRect r = lc->GetViewRect(); + lpRect->left = r.x; + lpRect->top = r.y; + lpRect->right = r.x + r.width; + lpRect->bottom = r.y + r.height; + return TRUE; + }); +} + +int CListCtrl::GetTopIndex() const { return static_cast(::SendMessage(m_hWnd, LVM_GETTOPINDEX, 0, 0)); } + +int CListCtrl::GetCountPerPage() const { + return OnMain([&]() -> int { + wxListCtrl* lc = ListOf(this); + return lc ? lc->GetCountPerPage() : 0; + }); +} + +BOOL CListCtrl::GetOrigin(LPPOINT lpPoint) const { + return OnMain([&]() -> BOOL { + wxListCtrl* lc = ListOf(this); + if (!lc || !lpPoint) + return FALSE; + lpPoint->x = 0; + lpPoint->y = 0; + return !(lc->InReportView() || lc->HasFlag(wxLC_LIST)); + }); +} + +BOOL CListCtrl::SetItemState(int nItem, LVITEM* pItem) { + if (!pItem) + return FALSE; + return SetItemState(nItem, pItem->state, pItem->stateMask); +} + +BOOL CListCtrl::SetItemState(int nItem, UINT nState, UINT nMask) { + return OnMain([&]() -> BOOL { + wxListCtrl* lc = ListOf(this); + return lc && SetItemStates(lc, nItem, nState, nMask); + }); +} + +UINT CListCtrl::GetItemState(int nItem, UINT nMask) const { + return OnMain([&]() -> UINT { + wxListCtrl* lc = ListOf(this); + return lc && ValidItem(lc, nItem) ? ItemState(lc, nItem, nMask) : 0; + }); +} + +CString CListCtrl::GetItemText(int nItem, int nSubItem) const { + return OnMain([&]() -> CString { + wxListCtrl* lc = ListOf(this); + if (!lc || !ValidItem(lc, nItem) || !ValidColumn(lc, nSubItem)) + return CString(); + return CStr(lc->GetItemText(nItem, nSubItem)); + }); +} + +int CListCtrl::GetItemText(int nItem, int nSubItem, char* lpszText, int nLen) const { + LVITEM lvi; + memset(&lvi, 0, sizeof lvi); + lvi.iSubItem = nSubItem; + lvi.cchTextMax = nLen; + lvi.pszText = lpszText; + return static_cast(::SendMessage(m_hWnd, LVM_GETITEMTEXT, static_cast(nItem), + reinterpret_cast(&lvi))); +} + +BOOL CListCtrl::SetItemText(int nItem, int nSubItem, const char* lpszText) { + return OnMain([&]() -> BOOL { + wxListCtrl* lc = ListOf(this); + if (!lc || !ValidItem(lc, nItem) || !ValidColumn(lc, nSubItem)) + return FALSE; + return lc->SetItem(nItem, nSubItem, ItemText(lpszText)); + }); +} + +void CListCtrl::SetItemCount(int nItems) { + OnMain([&] { + wxListCtrl* lc = ListOf(this); + if (lc && lc->IsVirtual()) + lc->SetItemCount(nItems); + }); +} + +BOOL CListCtrl::SetItemData(int nItem, DWORD_PTR dwData) { + return OnMain([&]() -> BOOL { + wxListCtrl* lc = ListOf(this); + return lc && ValidItem(lc, nItem) && lc->SetItemPtrData(nItem, static_cast(dwData)); + }); +} + +DWORD_PTR CListCtrl::GetItemData(int nItem) const { + return OnMain([&]() -> DWORD_PTR { + wxListCtrl* lc = ListOf(this); + return lc && ValidItem(lc, nItem) ? static_cast(lc->GetItemData(nItem)) : 0; + }); +} + +UINT CListCtrl::GetSelectedCount() const { + return OnMain([&]() -> UINT { + wxListCtrl* lc = ListOf(this); + return lc ? static_cast(lc->GetSelectedItemCount()) : 0; + }); +} + +int CListCtrl::GetSelectionMark() const { + return OnMain([&]() -> int { + wxListCtrl* lc = ListOf(this); + if (!lc) + return -1; + int mark = ListExtra(lc).selectionMark; + return ValidItem(lc, mark) ? mark : -1; + }); +} + +int CListCtrl::SetSelectionMark(int iIndex) { + return OnMain([&]() -> int { + wxListCtrl* lc = ListOf(this); + if (!lc) + return -1; + ListViewExtra& ex = ListExtra(lc); + int old = ex.selectionMark; + ex.selectionMark = iIndex; + return old; + }); +} + +DWORD CListCtrl::GetExtendedStyle() const { + return static_cast(::SendMessage(m_hWnd, LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0)); +} + +DWORD CListCtrl::SetExtendedStyle(DWORD dwNewStyle) { + return static_cast(::SendMessage(m_hWnd, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, static_cast(dwNewStyle))); +} + +CHeaderCtrl* CListCtrl::GetHeaderCtrl() const { + return m_hWnd ? static_cast(CWnd::FromHandle(m_hWnd)) : nullptr; +} + +int CListCtrl::InsertItem(const LVITEM* pItem) { + return OnMain([&]() -> int { + wxListCtrl* lc = ListOf(this); + if (!lc || !pItem || pItem->iSubItem != 0 || pItem->iItem < 0) + return -1; + long index = std::min(pItem->iItem, lc->GetItemCount()); + wxListItem li; + li.SetId(index); + li.SetText((pItem->mask & LVIF_TEXT) ? ItemText(pItem->pszText) : wxString()); + if (pItem->mask & LVIF_IMAGE) + li.SetImage(pItem->iImage); + long r = lc->InsertItem(li); + if (r < 0) + return -1; + if (pItem->mask & LVIF_PARAM) + lc->SetItemPtrData(r, static_cast(pItem->lParam)); + if (pItem->mask & LVIF_STATE) + SetItemStateOne(lc, r, pItem->state, pItem->stateMask); + return static_cast(r); + }); +} + +int CListCtrl::InsertItem(int nItem, const char* lpszItem) { + return InsertItem(LVIF_TEXT, nItem, lpszItem, 0, 0, 0, 0); +} + +int CListCtrl::InsertItem(int nItem, const char* lpszItem, int nImage) { + return InsertItem(LVIF_TEXT | LVIF_IMAGE, nItem, lpszItem, 0, 0, nImage, 0); +} + +int CListCtrl::InsertItem(UINT nMask, int nItem, const char* lpszItem, UINT nState, UINT nStateMask, int nImage, + LPARAM lParam) { + LVITEM lvi; + memset(&lvi, 0, sizeof lvi); + lvi.mask = nMask; + lvi.iItem = nItem; + lvi.state = nState; + lvi.stateMask = nStateMask; + lvi.pszText = const_cast(lpszItem); + lvi.iImage = nImage; + lvi.lParam = lParam; + return InsertItem(&lvi); +} + +BOOL CListCtrl::DeleteItem(int nItem) { + return OnMain([&]() -> BOOL { + wxListCtrl* lc = ListOf(this); + if (!lc || !ValidItem(lc, nItem)) + return FALSE; + ListViewExtra& ex = ListExtra(lc); + if (ex.selectionMark == nItem) + ex.selectionMark = -1; + else if (ex.selectionMark > nItem) + --ex.selectionMark; + return lc->DeleteItem(nItem); + }); +} + +BOOL CListCtrl::DeleteAllItems() { + return OnMain([&]() -> BOOL { + wxListCtrl* lc = ListOf(this); + if (!lc) + return FALSE; + ListExtra(lc).selectionMark = -1; + return lc->DeleteAllItems(); + }); +} + +int CListCtrl::FindItem(LVFINDINFO* pFindInfo, int nStart) const { + return OnMain([&]() -> int { + wxListCtrl* lc = ListOf(this); + if (!lc || !pFindInfo) + return -1; + int count = lc->GetItemCount(); + wxString needle = (pFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL)) && pFindInfo->psz ? ToWx(pFindInfo->psz) + : wxString(); + auto matches = [&](int i) { + if (pFindInfo->flags & LVFI_PARAM) + return static_cast(lc->GetItemData(i)) == pFindInfo->lParam; + if (pFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL)) { + wxString text = lc->GetItemText(i); + if (pFindInfo->flags & LVFI_PARTIAL) + return text.Lower().StartsWith(needle.Lower()); + return text.CmpNoCase(needle) == 0; + } + return false; + }; + for (int i = nStart + 1; i < count; ++i) + if (i >= 0 && matches(i)) + return i; + if (pFindInfo->flags & LVFI_WRAP) + for (int i = 0; i <= std::min(nStart, count - 1); ++i) + if (matches(i)) + return i; + return -1; + }); +} + +int CListCtrl::HitTest(LVHITTESTINFO* pHitTestInfo) const { + return OnMain([&]() -> int { + wxListCtrl* lc = ListOf(this); + if (!lc || !pHitTestInfo) + return -1; + ListHit hit = HitTestList(lc, wxPoint(pHitTestInfo->pt.x, pHitTestInfo->pt.y)); + pHitTestInfo->flags = hit.flags; + pHitTestInfo->iItem = static_cast(hit.item); + return static_cast(hit.item); + }); +} + +int CListCtrl::HitTest(CPoint pt, UINT* pFlags) const { + LVHITTESTINFO info; + memset(&info, 0, sizeof info); + info.pt = pt; + int item = HitTest(&info); + if (pFlags) + *pFlags = info.flags; + return item; +} + +int CListCtrl::SubItemHitTest(LVHITTESTINFO* pInfo) { + return static_cast(::SendMessage(m_hWnd, LVM_SUBITEMHITTEST, 0, reinterpret_cast(pInfo))); +} + +BOOL CListCtrl::EnsureVisible(int nItem, BOOL) { + return OnMain([&]() -> BOOL { + wxListCtrl* lc = ListOf(this); + return lc && ValidItem(lc, nItem) && lc->EnsureVisible(nItem); + }); +} + +BOOL CListCtrl::Scroll(CSize size) { + return OnMain([&]() -> BOOL { + wxListCtrl* lc = ListOf(this); + return lc && lc->ScrollList(size.cx, size.cy); + }); +} + +BOOL CListCtrl::RedrawItems(int nFirst, int nLast) { + return OnMain([&]() -> BOOL { + wxListCtrl* lc = ListOf(this); + if (!lc) + return FALSE; + int count = lc->GetItemCount(); + nFirst = std::max(0, nFirst); + nLast = std::min(count - 1, nLast); + if (nFirst <= nLast) + lc->RefreshItems(nFirst, nLast); + return TRUE; + }); +} + +int CListCtrl::InsertColumn(int nCol, const LVCOLUMN* pColumn) { + return OnMain([&]() -> int { + wxListCtrl* lc = ListOf(this); + if (!lc || !pColumn || nCol < 0) + return -1; + long col = std::min(nCol, lc->GetColumnCount()); + wxString heading = (pColumn->mask & LVCF_TEXT) ? ItemText(pColumn->pszText) : wxString(); + wxListColumnFormat fmt = (pColumn->mask & LVCF_FMT) ? AlignFromFormat(pColumn->fmt) : wxLIST_FORMAT_LEFT; + int width = (pColumn->mask & LVCF_WIDTH) ? pColumn->cx : wxLIST_DEFAULT_COL_WIDTH; + return static_cast(lc->InsertColumn(col, heading, fmt, width)); + }); +} + +int CListCtrl::InsertColumn(int nCol, const char* lpszColumnHeading, int nFormat, int nWidth, int nSubItem) { + LVCOLUMN column; + memset(&column, 0, sizeof column); + column.mask = LVCF_TEXT | LVCF_FMT; + column.pszText = const_cast(lpszColumnHeading); + column.fmt = nFormat; + if (nWidth != -1) { + column.mask |= LVCF_WIDTH; + column.cx = nWidth; + } + if (nSubItem != -1) { + column.mask |= LVCF_SUBITEM; + column.iSubItem = nSubItem; + } + return InsertColumn(nCol, &column); +} + +BOOL CListCtrl::DeleteColumn(int nCol) { + return OnMain([&]() -> BOOL { + wxListCtrl* lc = ListOf(this); + return lc && nCol >= 0 && nCol < lc->GetColumnCount() && lc->DeleteColumn(nCol); + }); +} + +BOOL CListCtrl::Update(int nItem) { + return OnMain([&]() -> BOOL { + wxListCtrl* lc = ListOf(this); + if (!lc || !ValidItem(lc, nItem)) + return FALSE; + lc->RefreshItem(nItem); + return TRUE; + }); +} + +BOOL CListCtrl::SortItems(int (*pfnCompare)(LPARAM, LPARAM, LPARAM), DWORD_PTR dwData) { + return OnMain([&]() -> BOOL { + wxListCtrl* lc = ListOf(this); + if (!lc || !pfnCompare) + return FALSE; + SortContext ctx{pfnCompare, static_cast(dwData)}; + return lc->SortItems(&SortTrampoline, reinterpret_cast(&ctx)); + }); +} + +BOOL CListCtrl::GetCheck(int nItem) const { + return OnMain([&]() -> BOOL { + wxListCtrl* lc = ListOf(this); + return lc && ValidItem(lc, nItem) && lc->HasCheckBoxes() && lc->IsItemChecked(nItem); + }); +} + +BOOL CListCtrl::SetCheck(int nItem, BOOL fCheck) { + return OnMain([&]() -> BOOL { + wxListCtrl* lc = ListOf(this); + if (!lc || !ValidItem(lc, nItem) || !lc->HasCheckBoxes()) + return FALSE; + lc->CheckItem(nItem, fCheck != FALSE); + return TRUE; + }); +} + +void CListCtrl::DrawItem(LPDRAWITEMSTRUCT) {} From 61285639736fbeee7fcf1a2489c0c1df859c1b19 Mon Sep 17 00:00:00 2001 From: Kamil Marczak Date: Tue, 6 Oct 2026 23:33:37 +0200 Subject: [PATCH 25/46] fix(mfcwx): decode dialog template control captions as UTF-8 --- port/mfcwx/src/dialog.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/port/mfcwx/src/dialog.cpp b/port/mfcwx/src/dialog.cpp index fc3b1616d..79c7ad354 100644 --- a/port/mfcwx/src/dialog.cpp +++ b/port/mfcwx/src/dialog.cpp @@ -57,7 +57,7 @@ bool CreateDialogControls(wxWindow* parent, const rc::Dialog& tmpl) { for (int i = 0; i < tmpl.controlCount; ++i) { const rc::Control& c = tmpl.controls[i]; wxRect r = DialogUnitsToPixels(parent, c.x, c.y, c.cx, c.cy); - wxWindow* w = CreateControl(parent, c.cls, c.text, c.id, r, c.style, c.exStyle); + wxWindow* w = CreateControl(parent, c.cls, Utf8ToAnsi(c.text).c_str(), c.id, r, c.style, c.exStyle); if (!w) continue; w->SetFont(parent->GetFont()); From cf966ceb9c57cd3cda1becde8fad630dffdb7f1f Mon Sep 17 00:00:00 2001 From: Kamil Marczak Date: Tue, 6 Oct 2026 23:37:53 +0200 Subject: [PATCH 26/46] feat(mfcwx): proxy Scintilla messages to wxStyledTextCtrl with ANSI text Translates text and positions between the ANSI code page and the UTF-8 control, converts notifications and implements CrypTool's container lexer; 191 checks pass on macOS and Linux. --- port/mfcwx/src/scintilla.cpp | 1317 ++++++++++++++++++++++++++ port/mfcwx/tests/scintilla_tests.cpp | 37 +- 2 files changed, 1340 insertions(+), 14 deletions(-) create mode 100644 port/mfcwx/src/scintilla.cpp diff --git a/port/mfcwx/src/scintilla.cpp b/port/mfcwx/src/scintilla.cpp new file mode 100644 index 000000000..998825a24 --- /dev/null +++ b/port/mfcwx/src/scintilla.cpp @@ -0,0 +1,1317 @@ +#include "windows_impl.h" + +#include + +// The application's Scintilla 1.77 headers; message ids are unchanged in wx's Scintilla. +#include "../../../scintilla/include/Scintilla.h" +#include "../../../scintilla/include/SciLexer.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static_assert(offsetof(SCNotification, nmhdr) == 0, "SCNotification must start with its header"); +static_assert(sizeof(NotifyHeader) == sizeof(NMHDR), "NotifyHeader must match NMHDR"); +static_assert(offsetof(NotifyHeader, code) == offsetof(NMHDR, code), "NotifyHeader must match NMHDR"); + +namespace mfcwx { +namespace { + +constexpr int kModInsertCheck = 0x100000; +constexpr int kWrapWhitespace = 3; +constexpr int kCrypToolLexer = SCLEX_AUTOMATIC + 1; +constexpr int kAlwaysWatched = SC_MOD_INSERTTEXT | SC_MOD_DELETETEXT | SC_MOD_BEFOREDELETE | kModInsertCheck; + +struct Codec { + int cp = 0; + unsigned char encLen[256]; + char enc[256][3]; + std::vector dec; +}; + +const Codec& CurrentCodec() { + static Codec codec; + int cp = GetAnsiCodePage(); + if (codec.cp == cp && !codec.dec.empty()) + return codec; + codec.cp = cp; + codec.dec.assign(0x10000, -1); + for (int b = 0; b < 256; ++b) { + uint32_t u = b < 0x80 ? static_cast(b) + : static_cast(AnsiToUnicode(static_cast(b))) & 0xFFFF; + char* o = codec.enc[b]; + if (u < 0x80) { + o[0] = static_cast(u); + codec.encLen[b] = 1; + } else if (u < 0x800) { + o[0] = static_cast(0xC0 | (u >> 6)); + o[1] = static_cast(0x80 | (u & 0x3F)); + codec.encLen[b] = 2; + } else { + o[0] = static_cast(0xE0 | (u >> 12)); + o[1] = static_cast(0x80 | ((u >> 6) & 0x3F)); + o[2] = static_cast(0x80 | (u & 0x3F)); + codec.encLen[b] = 3; + } + codec.dec[u] = static_cast(b); + } + return codec; +} + +// Like Scintilla, every byte of an invalid sequence counts as a character. +int Utf8Width(const unsigned char* s, size_t avail) { + unsigned c = s[0]; + if (c < 0xC2) + return 1; + int n; + if (c < 0xE0) + n = 2; + else if (c < 0xF0) + n = 3; + else if (c < 0xF5) + n = 4; + else + return 1; + if (static_cast(n) > avail) + return 1; + for (int i = 1; i < n; ++i) + if ((s[i] & 0xC0) != 0x80) + return 1; + if (n == 3) { + if ((c == 0xE0 && s[1] < 0xA0) || (c == 0xED && s[1] >= 0xA0)) + return 1; + if (c == 0xEF && s[1] == 0xBF && (s[2] == 0xBE || s[2] == 0xBF)) + return 1; + } else if (n == 4) { + if ((c == 0xF0 && s[1] < 0x90) || (c == 0xF4 && s[1] >= 0x90)) + return 1; + } + return n; +} + +uint32_t Utf8Decode(const unsigned char* s, int n) { + switch (n) { + case 1: + return s[0]; + case 2: + return ((s[0] & 0x1Fu) << 6) | (s[1] & 0x3Fu); + case 3: + return ((s[0] & 0x0Fu) << 12) | ((s[1] & 0x3Fu) << 6) | (s[2] & 0x3Fu); + default: + return ((s[0] & 0x07u) << 18) | ((s[1] & 0x3Fu) << 12) | ((s[2] & 0x3Fu) << 6) | (s[3] & 0x3Fu); + } +} + +bool AnsiFromCodePoint(const Codec& c, uint32_t u, char& out) { + if (u < 0x80) { + out = static_cast(u); + return true; + } + if (u < 0x10000 && c.dec[u] >= 0) { + out = static_cast(c.dec[u]); + return true; + } + out = '?'; + return false; +} + +bool AnsiAt(const Codec& c, const unsigned char* s, int n, char& out) { + if (n == 1 && s[0] >= 0x80) { + out = '?'; + return false; + } + return AnsiFromCodePoint(c, Utf8Decode(s, n), out); +} + +std::string Utf8FromAnsi(const char* s, size_t n) { + const Codec& c = CurrentCodec(); + std::string out; + out.reserve(n + n / 2); + for (size_t i = 0; i < n; ++i) { + unsigned char b = static_cast(s[i]); + if (b < 0x80) + out.push_back(static_cast(b)); + else + out.append(c.enc[b], c.encLen[b]); + } + return out; +} + +std::string Utf8FromAnsi(const char* s) { return s ? Utf8FromAnsi(s, strlen(s)) : std::string(); } + +std::string AnsiFromUtf8(const char* s, size_t n) { + const Codec& c = CurrentCodec(); + std::string out; + out.reserve(n); + const unsigned char* p = reinterpret_cast(s); + for (size_t i = 0; i < n;) { + if (p[i] < 0x80) { + out.push_back(static_cast(p[i++])); + continue; + } + int w = Utf8Width(p + i, n - i); + char a; + AnsiAt(c, p + i, w, a); + out.push_back(a); + i += static_cast(w); + } + return out; +} + +std::string AnsiFromWx(const wxString& s) { + const Codec& c = CurrentCodec(); + std::string out; + out.reserve(s.length()); + for (wxString::const_iterator it = s.begin(); it != s.end(); ++it) { + char a; + AnsiFromCodePoint(c, static_cast((*it).GetValue()), a); + out.push_back(a); + } + return out; +} + +const unsigned char* RangeBytes(wxStyledTextCtrl* s, int pos, int len) { + return reinterpret_cast(s->GetRangePointer(pos, len)); +} + +int CountRange(wxStyledTextCtrl* s, int pos, int len, bool* high) { + if (high) + *high = false; + if (len <= 0) + return 0; + const unsigned char* p = RangeBytes(s, pos, len); + int chars = 0; + for (int i = 0; i < len; ++chars) { + if (p[i] < 0x80) { + ++i; + continue; + } + if (high) + *high = true; + i += Utf8Width(p + i, static_cast(len - i)); + } + return chars; +} + +class PosMap { +public: + static constexpr int kStride = 256; + static constexpr int kChunk = 1 << 16; + + void Reset() { Restart(); } + + void Changed(wxStyledTextCtrl* s, int pos, int byteDelta, int charDelta, bool charsKnown, bool insertedNonAscii) { + if (ascii_) { + if (!insertedNonAscii) + return; + int before = s->GetLength() - byteDelta; + int keep = std::max(0, std::min(pos, before)) / kStride; + ascii_ = false; + complete_ = false; + firstHigh_ = INT_MAX; + marks_.resize(static_cast(keep) + 1); + for (int k = 0; k <= keep; ++k) + marks_[static_cast(k)] = k * kStride; + chars_ = bytes_ = keep * kStride; + total_ = charsKnown ? before + charDelta : -1; + return; + } + if (pos < 0) + pos = 0; + size_t k = static_cast(std::upper_bound(marks_.begin(), marks_.end(), pos) - marks_.begin()) - 1; + marks_.resize(k + 1); + chars_ = static_cast(k) * kStride; + bytes_ = marks_[k]; + complete_ = false; + if (firstHigh_ >= bytes_) + firstHigh_ = INT_MAX; + total_ = total_ >= 0 && charsKnown ? total_ + charDelta : -1; + } + + int ToBytes(wxStyledTextCtrl* s, int ansi) { + if (ansi <= 0 || ascii_) + return ansi; + Validate(s); + if (!complete_ && chars_ < ansi) + Scan(s, ansi, 0); + if (ascii_) + return ansi; + if (ansi >= chars_) + return bytes_ + (ansi - chars_); + size_t k = static_cast(ansi / kStride); + int off = marks_[k]; + int count = ansi - static_cast(k) * kStride; + int span = std::min(s->GetLength() - off, count * 4 + 4); + const unsigned char* p = RangeBytes(s, off, span); + int i = 0; + for (; count > 0 && i < span; --count) + i += p[i] < 0x80 ? 1 : Utf8Width(p + i, static_cast(span - i)); + return off + i; + } + + int ToAnsi(wxStyledTextCtrl* s, int bytes) { + if (bytes <= 0 || ascii_) + return bytes; + Validate(s); + if (!complete_ && bytes_ < bytes) + Scan(s, 0, bytes); + if (ascii_) + return bytes; + if (bytes >= bytes_) + return chars_ + (bytes - bytes_); + size_t k = static_cast(std::upper_bound(marks_.begin(), marks_.end(), bytes) - marks_.begin()) - 1; + int off = marks_[k]; + int idx = static_cast(k) * kStride; + int span = std::min(s->GetLength() - off, bytes - off + 4); + const unsigned char* p = RangeBytes(s, off, span); + for (int i = 0; off + i < bytes;) { + int w = p[i] < 0x80 ? 1 : Utf8Width(p + i, static_cast(span - i)); + if (off + i + w > bytes) + break; + i += w; + ++idx; + } + return idx; + } + + int AnsiLength(wxStyledTextCtrl* s) { + if (ascii_) + return s->GetLength(); + Validate(s); + if (total_ >= 0) + return total_; + Scan(s, INT_MAX, INT_MAX); + return ascii_ ? s->GetLength() : chars_; + } + +private: + void Restart() { + ascii_ = false; + complete_ = false; + chars_ = bytes_ = 0; + total_ = -1; + firstHigh_ = INT_MAX; + marks_.assign(1, 0); + } + + // Safety net against changes that bypassed the modification events. + void Validate(wxStyledTextCtrl* s) { + int n = s->GetLength(); + if (bytes_ > n || (complete_ && bytes_ != n) || total_ > n) + Restart(); + } + + void Scan(wxStyledTextCtrl* s, int targetChars, int targetBytes) { + int n = s->GetLength(); + while (bytes_ < n && (chars_ < targetChars || bytes_ < targetBytes)) { + int base = bytes_; + int chunk = std::min(n - base, kChunk); + const unsigned char* p = RangeBytes(s, base, chunk); + int i = 0; + while (i < chunk && (chars_ < targetChars || base + i < targetBytes)) { + int w = 1; + if (p[i] >= 0x80) { + if (chunk - i < 4 && base + chunk < n) + break; + w = Utf8Width(p + i, static_cast(chunk - i)); + if (firstHigh_ == INT_MAX) + firstHigh_ = base + i; + } + i += w; + ++chars_; + if (chars_ % kStride == 0) + marks_.push_back(base + i); + } + bytes_ = base + i; + } + if (bytes_ >= n) { + complete_ = true; + total_ = chars_; + if (firstHigh_ == INT_MAX) + ascii_ = true; + } + } + + bool ascii_ = true; + bool complete_ = true; + int chars_ = 0; + int bytes_ = 0; + int total_ = 0; + int firstHigh_ = INT_MAX; + std::vector marks_{0}; +}; + +struct SciData { + wxStyledTextCtrl* stc = nullptr; + PosMap map; + int appModMask = SC_MODEVENTMASKALL; + int appCodePage = 0; + bool cryptoolLexer = false; + std::map props; + bool alphabet[256] = {}; + bool alphabetEmpty = true; + int nonAlphabetStyle = 0; + int stylingAnsi = 0; + int lengthForEncode = -1; + std::string mirror; + bool mirrorValid = false; + int pendingDeletePos = -1; + int pendingDeleteLength = 0; + int pendingDeleteChars = 0; +}; + +std::unordered_map>& Registry() { + static std::unordered_map> registry; + return registry; +} + +SciData* FindData(wxWindow* w) { + auto it = Registry().find(w); + return it == Registry().end() ? nullptr : it->second.get(); +} + +int ControlModMask(int appMask) { return appMask | kAlwaysWatched; } + +int PosArg(uintptr_t v) { return static_cast(static_cast(v)); } +int PosArg(intptr_t v) { return static_cast(v); } +WPARAM PosW(int pos) { return static_cast(static_cast(pos)); } +LPARAM PosL(int pos) { return static_cast(pos); } + +LRESULT Pass(SciData& d, UINT msg, WPARAM wp, LPARAM lp) { + return static_cast(d.stc->SendMsg(static_cast(msg), static_cast(wp), static_cast(lp))); +} + +LPARAM Ptr(const std::string& s) { return reinterpret_cast(s.c_str()); } + +int A2B(SciData& d, int ansi) { return d.map.ToBytes(d.stc, ansi); } +int B2A(SciData& d, int bytes) { return d.map.ToAnsi(d.stc, bytes); } +LRESULT B2A(SciData& d, LRESULT bytes) { return B2A(d, static_cast(bytes)); } + +std::string AnsiOfBytes(SciData& d, int b0, int b1) { + int n = d.stc->GetLength(); + b0 = std::max(0, std::min(b0, n)); + b1 = std::max(b0, std::min(b1, n)); + if (b1 == b0) + return std::string(); + return AnsiFromUtf8(d.stc->GetRangePointer(b0, b1 - b0), static_cast(b1 - b0)); +} + +std::string AnsiRange(SciData& d, int a0, int a1) { + if (a1 <= a0) + return std::string(); + return AnsiOfBytes(d, A2B(d, std::max(a0, 0)), A2B(d, a1)); +} + +LRESULT PutString(const std::string& s, LPARAM buf) { + if (buf) { + char* out = reinterpret_cast(buf); + memcpy(out, s.data(), s.size()); + out[s.size()] = '\0'; + } + return static_cast(s.size()); +} + +std::string FetchString(SciData& d, UINT msg, WPARAM wp, bool sizeIncludesNul) { + LRESULT n = Pass(d, msg, wp, 0); + if (sizeIncludesNul) + --n; + if (n <= 0) + return std::string(); + std::vector buf(static_cast(n) + 2, '\0'); + Pass(d, msg, wp, reinterpret_cast(buf.data())); + return AnsiFromUtf8(buf.data(), static_cast(n)); +} + +void RebuildAlphabet(SciData& d) { + const std::string& a = d.props["cryptool.alphabet"]; + std::fill(std::begin(d.alphabet), std::end(d.alphabet), false); + for (unsigned char ch : a) + d.alphabet[ch] = true; + d.alphabetEmpty = a.empty(); + d.nonAlphabetStyle = atoi(d.props["cryptool.nonalphabetstyle"].c_str()); +} + +void Notify(wxWindow* w, SCNotification& scn) { NotifyParentNM(w, reinterpret_cast(&scn.nmhdr)); } + +SCNotification MakeNotification(unsigned code) { + SCNotification scn; + memset(&scn, 0, sizeof scn); + scn.nmhdr.code = code; + return scn; +} + +void LexCrypTool(SciData& d, int endPos) { + wxStyledTextCtrl* s = d.stc; + int length = s->GetLength(); + int end = std::min(endPos, length); + int start = s->PositionFromLine(s->LineFromPosition(s->GetEndStyled())); + if (end <= start) + return; + int n = end - start; + const unsigned char* p = reinterpret_cast(s->GetRangePointer(start, n)); + std::vector styles(static_cast(n), 0); + if (!d.alphabetEmpty) { + const Codec& c = CurrentCodec(); + char other = static_cast(d.nonAlphabetStyle); + for (int i = 0; i < n;) { + int w = p[i] < 0x80 ? 1 : Utf8Width(p + i, static_cast(n - i)); + char a; + bool known = AnsiAt(c, p + i, w, a); + if (!known || !d.alphabet[static_cast(a)]) + std::fill(styles.begin() + i, styles.begin() + i + w, other); + i += w; + } + } + s->SendMsg(SCI_STARTSTYLING, start, 0xff); + s->SendMsg(SCI_SETSTYLINGEX, n, reinterpret_cast(styles.data())); +} + +// Text typed, pasted or dropped is reduced to the ANSI code page, as on Windows. +void SanitizeInsertion(SciData& d, wxStyledTextEvent& e) { + wxString text = e.GetString(); + const Codec& c = CurrentCodec(); + bool bad = false; + for (wxString::const_iterator it = text.begin(); it != text.end() && !bad; ++it) { + char a; + bad = !AnsiFromCodePoint(c, static_cast((*it).GetValue()), a); + } + if (!bad) + return; + wxString clean; + clean.reserve(text.length()); + for (wxString::const_iterator it = text.begin(); it != text.end(); ++it) { + char a; + clean += AnsiFromCodePoint(c, static_cast((*it).GetValue()), a) ? *it : wxUniChar('?'); + } + d.stc->ChangeInsertion(static_cast(clean.utf8_str().length()), clean); +} + +void OnModified(wxWindow* w, wxStyledTextEvent& e) { + SciData* d = FindData(w); + if (!d) + return; + int type = e.GetModificationType(); + if (type & kModInsertCheck) { + SanitizeInsertion(*d, e); + return; + } + int pos = e.GetPosition(); + int len = e.GetLength(); + int ansiLength = -1; + if (type & SC_MOD_BEFOREDELETE) { + d->pendingDeletePos = pos; + d->pendingDeleteLength = len; + d->pendingDeleteChars = ansiLength = CountRange(d->stc, pos, len, nullptr); + } else if (type & SC_MOD_INSERTTEXT) { + bool high = false; + ansiLength = CountRange(d->stc, pos, len, &high); + d->map.Changed(d->stc, pos, len, ansiLength, true, high); + d->mirrorValid = false; + } else if (type & SC_MOD_DELETETEXT) { + bool known = d->pendingDeletePos == pos && d->pendingDeleteLength == len; + if (known) + ansiLength = d->pendingDeleteChars; + d->pendingDeletePos = -1; + d->map.Changed(d->stc, pos, -len, known ? -ansiLength : 0, known, false); + d->mirrorValid = false; + } + if (!(type & d->appModMask)) + return; + SCNotification scn = MakeNotification(SCN_MODIFIED); + scn.modificationType = type; + scn.linesAdded = e.GetLinesAdded(); + scn.line = e.GetLine(); + scn.foldLevelNow = e.GetFoldLevelNow(); + scn.foldLevelPrev = e.GetFoldLevelPrev(); + scn.position = B2A(*d, pos); + std::string text; + if (type & (SC_MOD_INSERTTEXT | SC_MOD_DELETETEXT | SC_MOD_BEFOREINSERT)) { + text = AnsiFromWx(e.GetString()); + scn.text = text.c_str(); + if (ansiLength < 0) + ansiLength = text.empty() && len > 0 ? len : static_cast(text.size()); + } else if (ansiLength < 0 && len > 0) { + ansiLength = B2A(*d, pos + len) - scn.position; + } + scn.length = std::max(ansiLength, 0); + if (!(type & (SC_MOD_CHANGESTYLE | SC_MOD_CHANGEINDICATOR))) + NotifyParent(w, SCEN_CHANGE); + Notify(w, scn); +} + +void OnStyleNeeded(wxWindow* w, wxStyledTextEvent& e) { + SciData* d = FindData(w); + if (!d) + return; + if (d->cryptoolLexer) { + LexCrypTool(*d, e.GetPosition()); + return; + } + SCNotification scn = MakeNotification(SCN_STYLENEEDED); + scn.position = B2A(*d, e.GetPosition()); + Notify(w, scn); +} + +int MacroLParam(const wxStyledTextEvent& e) { + switch (e.GetMessage()) { + case SCI_REPLACESEL: + case SCI_ADDTEXT: + case SCI_INSERTTEXT: + case SCI_APPENDTEXT: + case SCI_SEARCHNEXT: + case SCI_SEARCHPREV: + return 0; + default: + return e.GetLParam(); + } +} + +void ForwardEvent(wxWindow* w, wxStyledTextEvent& e) { + SciData* d = FindData(w); + if (!d || w->IsBeingDeleted()) + return; + wxEventType t = e.GetEventType(); + SCNotification scn = MakeNotification(0); + std::string text; + scn.modifiers = e.GetModifiers(); + if (t == wxEVT_STC_CHARADDED) { + char a; + AnsiFromCodePoint(CurrentCodec(), static_cast(e.GetKey()), a); + scn.nmhdr.code = SCN_CHARADDED; + scn.ch = static_cast(a); + } else if (t == wxEVT_STC_SAVEPOINTREACHED) { + scn.nmhdr.code = SCN_SAVEPOINTREACHED; + } else if (t == wxEVT_STC_SAVEPOINTLEFT) { + scn.nmhdr.code = SCN_SAVEPOINTLEFT; + } else if (t == wxEVT_STC_ROMODIFYATTEMPT) { + scn.nmhdr.code = SCN_MODIFYATTEMPTRO; + } else if (t == wxEVT_STC_DOUBLECLICK) { + scn.nmhdr.code = SCN_DOUBLECLICK; + scn.position = B2A(*d, e.GetPosition()); + scn.line = e.GetLine(); + } else if (t == wxEVT_STC_UPDATEUI) { + scn.nmhdr.code = SCN_UPDATEUI; + } else if (t == wxEVT_STC_MACRORECORD) { + scn.nmhdr.code = SCN_MACRORECORD; + scn.message = e.GetMessage(); + scn.wParam = static_cast(e.GetWParam()); + scn.lParam = MacroLParam(e); + } else if (t == wxEVT_STC_MARGINCLICK) { + scn.nmhdr.code = SCN_MARGINCLICK; + scn.position = B2A(*d, e.GetPosition()); + scn.margin = e.GetMargin(); + } else if (t == wxEVT_STC_NEEDSHOWN) { + scn.nmhdr.code = SCN_NEEDSHOWN; + scn.position = B2A(*d, e.GetPosition()); + scn.length = B2A(*d, e.GetPosition() + e.GetLength()) - scn.position; + } else if (t == wxEVT_STC_PAINTED) { + scn.nmhdr.code = SCN_PAINTED; + } else if (t == wxEVT_STC_USERLISTSELECTION || t == wxEVT_STC_AUTOCOMP_SELECTION) { + scn.nmhdr.code = t == wxEVT_STC_USERLISTSELECTION ? SCN_USERLISTSELECTION : SCN_AUTOCSELECTION; + scn.listType = e.GetListType(); + text = AnsiFromWx(e.GetString()); + scn.text = text.c_str(); + scn.lParam = B2A(*d, e.GetPosition()); + } else if (t == wxEVT_STC_DWELLSTART || t == wxEVT_STC_DWELLEND) { + scn.nmhdr.code = t == wxEVT_STC_DWELLSTART ? SCN_DWELLSTART : SCN_DWELLEND; + scn.position = B2A(*d, e.GetPosition()); + scn.x = e.GetX(); + scn.y = e.GetY(); + } else if (t == wxEVT_STC_ZOOM) { + scn.nmhdr.code = SCN_ZOOM; + } else if (t == wxEVT_STC_HOTSPOT_CLICK || t == wxEVT_STC_HOTSPOT_DCLICK) { + scn.nmhdr.code = t == wxEVT_STC_HOTSPOT_CLICK ? SCN_HOTSPOTCLICK : SCN_HOTSPOTDOUBLECLICK; + scn.position = B2A(*d, e.GetPosition()); + } else if (t == wxEVT_STC_CALLTIP_CLICK) { + scn.nmhdr.code = SCN_CALLTIPCLICK; + scn.position = e.GetPosition(); + } else if (t == wxEVT_STC_INDICATOR_CLICK || t == wxEVT_STC_INDICATOR_RELEASE) { + scn.nmhdr.code = t == wxEVT_STC_INDICATOR_CLICK ? SCN_INDICATORCLICK : SCN_INDICATORRELEASE; + scn.position = B2A(*d, e.GetPosition()); + } else if (t == wxEVT_STC_AUTOCOMP_CANCELLED) { + scn.nmhdr.code = SCN_AUTOCCANCELLED; + } else { + return; + } + Notify(w, scn); +} + +void BindEvents(wxWindow* w, wxStyledTextCtrl* s) { + s->Bind(wxEVT_STC_MODIFIED, [w](wxStyledTextEvent& e) { OnModified(w, e); }); + s->Bind(wxEVT_STC_STYLENEEDED, [w](wxStyledTextEvent& e) { OnStyleNeeded(w, e); }); + const wxEventTypeTag forwarded[] = { + wxEVT_STC_CHARADDED, wxEVT_STC_SAVEPOINTREACHED, wxEVT_STC_SAVEPOINTLEFT, wxEVT_STC_ROMODIFYATTEMPT, + wxEVT_STC_DOUBLECLICK, wxEVT_STC_UPDATEUI, wxEVT_STC_MACRORECORD, wxEVT_STC_MARGINCLICK, + wxEVT_STC_NEEDSHOWN, wxEVT_STC_PAINTED, wxEVT_STC_USERLISTSELECTION, wxEVT_STC_DWELLSTART, + wxEVT_STC_DWELLEND, wxEVT_STC_ZOOM, wxEVT_STC_HOTSPOT_CLICK, wxEVT_STC_HOTSPOT_DCLICK, + wxEVT_STC_CALLTIP_CLICK, wxEVT_STC_AUTOCOMP_SELECTION, wxEVT_STC_INDICATOR_CLICK, + wxEVT_STC_INDICATOR_RELEASE, wxEVT_STC_AUTOCOMP_CANCELLED, + }; + for (const auto& t : forwarded) + s->Bind(t, [w](wxStyledTextEvent& e) { ForwardEvent(w, e); }); + s->Bind(wxEVT_SET_FOCUS, [w](wxFocusEvent& e) { + e.Skip(); + if (FindData(w) && !w->IsBeingDeleted()) + NotifyParent(w, SCEN_SETFOCUS); + }); + s->Bind(wxEVT_KILL_FOCUS, [w](wxFocusEvent& e) { + e.Skip(); + if (FindData(w) && !w->IsBeingDeleted()) + NotifyParent(w, SCEN_KILLFOCUS); + }); + s->Bind(wxEVT_DESTROY, [w](wxWindowDestroyEvent& e) { + e.Skip(); + if (e.GetWindow() == w) + Registry().erase(w); + }); +} + +SciData* DataFor(wxWindow* w) { + if (SciData* d = FindData(w)) + return d; + auto* s = wxDynamicCast(w, wxStyledTextCtrl); + if (!s) + return nullptr; + std::unique_ptr owned(new SciData()); + SciData* d = owned.get(); + d->stc = s; + Registry()[w] = std::move(owned); + s->SendMsg(SCI_SETCODEPAGE, SC_CP_UTF8); + s->SendMsg(SCI_SETMODEVENTMASK, ControlModMask(d->appModMask)); + s->SendMsg(SCI_SETEOLMODE, SC_EOL_CRLF); + d->map.Reset(); + BindEvents(w, s); + return d; +} + +struct AppRangeToFormat { + void* hdc; + void* hdcTarget; + struct { + int left, top, right, bottom; + } rc, rcPage; + CharacterRange chrg; +}; + +LRESULT GetTextRange(SciData& d, long cpMin, long cpMax, char* out) { + if (!out) + return 0; + int total = d.map.AnsiLength(d.stc); + long end = cpMax == -1 ? total : cpMax; + long len = end - cpMin; + if (len < 0) + return 0; + std::string a = AnsiRange(d, static_cast(std::max(cpMin, 0L)), static_cast(std::min(end, total))); + size_t lead = cpMin < 0 ? static_cast(std::min(-cpMin, len)) : 0; + memset(out, 0, static_cast(len) + 1); + memcpy(out + lead, a.data(), std::min(a.size(), static_cast(len) - lead)); + return static_cast(len); +} + +LRESULT GetStyledText(SciData& d, TextRange* tr) { + if (!tr || !tr->lpstrText) + return 0; + int total = d.map.AnsiLength(d.stc); + int a0 = static_cast(std::max(0L, std::min(tr->chrg.cpMin, total))); + int a1 = static_cast(std::max(a0, std::min(tr->chrg.cpMax, total))); + int b0 = A2B(d, a0); + int b1 = A2B(d, a1); + std::vector cells(static_cast(b1 - b0) * 2 + 2, '\0'); + TextRange utf; + utf.chrg.cpMin = b0; + utf.chrg.cpMax = b1; + utf.lpstrText = cells.data(); + Pass(d, SCI_GETSTYLEDTEXT, 0, reinterpret_cast(&utf)); + std::vector chars(static_cast(b1 - b0)); + for (size_t i = 0; i < chars.size(); ++i) + chars[i] = static_cast(cells[i * 2]); + const Codec& c = CurrentCodec(); + char* out = tr->lpstrText; + int place = 0; + for (size_t i = 0; i < chars.size();) { + int w = chars[i] < 0x80 ? 1 : Utf8Width(&chars[i], chars.size() - i); + char a; + AnsiAt(c, &chars[i], w, a); + out[place++] = a; + out[place++] = cells[i * 2 + 1]; + i += static_cast(w); + } + out[place] = '\0'; + out[place + 1] = '\0'; + return place; +} + +LRESULT AddStyledText(SciData& d, WPARAM length, LPARAM cellsPtr) { + if (!cellsPtr) + return 0; + const char* cells = reinterpret_cast(cellsPtr); + const Codec& c = CurrentCodec(); + std::string out; + size_t n = static_cast(length) / 2; + out.reserve(n * 2 + n); + for (size_t i = 0; i < n; ++i) { + unsigned char ch = static_cast(cells[i * 2]); + char style = cells[i * 2 + 1]; + if (ch < 0x80) { + out.push_back(static_cast(ch)); + out.push_back(style); + continue; + } + for (int k = 0; k < c.encLen[ch]; ++k) { + out.push_back(c.enc[ch][k]); + out.push_back(style); + } + } + return Pass(d, SCI_ADDSTYLEDTEXT, out.size(), reinterpret_cast(out.data())); +} + +template +LRESULT FindTextIn(SciData& d, WPARAM flags, Find* ft) { + if (!ft || !ft->lpstrText) + return -1; + std::string text = Utf8FromAnsi(ft->lpstrText); + TextToFind utf; + utf.chrg.cpMin = A2B(d, static_cast(ft->chrg.cpMin)); + utf.chrg.cpMax = A2B(d, static_cast(ft->chrg.cpMax)); + utf.lpstrText = const_cast(text.c_str()); + utf.chrgText.cpMin = utf.chrgText.cpMax = -1; + LRESULT pos = Pass(d, SCI_FINDTEXT, flags, reinterpret_cast(&utf)); + if (pos < 0) + return pos; + ft->chrgText.cpMin = static_castchrgText.cpMin)>(B2A(d, static_cast(utf.chrgText.cpMin))); + ft->chrgText.cpMax = static_castchrgText.cpMax)>(B2A(d, static_cast(utf.chrgText.cpMax))); + return B2A(d, pos); +} + +wxDC* DCOf(void* hdc) { + if (!hdc) + return nullptr; + CDC* dc = CDC::FromHandle(reinterpret_cast(hdc)); + return dc ? dc->GetWx() : nullptr; +} + +LRESULT FormatRange(SciData& d, bool draw, AppRangeToFormat* fr) { + if (!fr) + return 0; + int total = d.map.AnsiLength(d.stc); + int cpMin = static_cast(std::max(0L, std::min(fr->chrg.cpMin, total))); + int cpMax = fr->chrg.cpMax < 0 || fr->chrg.cpMax > total ? total : static_cast(fr->chrg.cpMax); + wxDC* surface = DCOf(fr->hdc); + if (!surface) + return cpMax; + wxDC* target = DCOf(fr->hdcTarget); + wxRect render(wxPoint(fr->rc.left, fr->rc.top), wxPoint(fr->rc.right, fr->rc.bottom)); + wxRect page(wxPoint(fr->rcPage.left, fr->rcPage.top), wxPoint(fr->rcPage.right, fr->rcPage.bottom)); + int end = d.stc->FormatRange(draw, A2B(d, cpMin), A2B(d, cpMax), surface, target ? target : surface, render, page); + return B2A(d, end); +} + +LRESULT GetCharacterPointer(SciData& d) { + if (!d.mirrorValid) { + d.mirror = AnsiOfBytes(d, 0, d.stc->GetLength()); + d.mirrorValid = true; + } + return reinterpret_cast(d.mirror.c_str()); +} + +sptr_t DirectFunction(sptr_t ptr, unsigned int msg, uptr_t wp, sptr_t lp); + +LRESULT Proc(wxWindow* w, SciData& d, UINT msg, WPARAM wp, LPARAM lp, bool& handled); + +// Win32 edit messages that Scintilla 1.77 (ScintillaWin) maps onto its own messages. +UINT SciFromEM(UINT msg) { + switch (msg) { + case EM_CANPASTE: return SCI_CANPASTE; + case EM_CANUNDO: return SCI_CANUNDO; + case EM_EMPTYUNDOBUFFER: return SCI_EMPTYUNDOBUFFER; + case EM_GETFIRSTVISIBLELINE: return SCI_GETFIRSTVISIBLELINE; + case EM_GETLINECOUNT: return SCI_GETLINECOUNT; + case EM_GETSELTEXT: return SCI_GETSELTEXT; + case EM_HIDESELECTION: return SCI_HIDESELECTION; + case EM_LINEINDEX: return SCI_POSITIONFROMLINE; + case EM_LINESCROLL: return SCI_LINESCROLL; + case EM_REPLACESEL: return SCI_REPLACESEL; + case EM_SCROLLCARET: return SCI_SCROLLCARET; + case EM_SETREADONLY: return SCI_SETREADONLY; + case WM_CLEAR: return SCI_CLEAR; + case WM_COPY: return SCI_COPY; + case WM_CUT: return SCI_CUT; + case WM_SETTEXT: return SCI_SETTEXT; + case WM_GETTEXTLENGTH: return SCI_GETTEXTLENGTH; + case WM_PASTE: return SCI_PASTE; + case WM_UNDO: return SCI_UNDO; + default: return msg; + } +} + +void SetSelectionAnsi(SciData& d, int nStart, int nEnd) { + if (nStart > nEnd) + std::swap(nStart, nEnd); + Pass(d, SCI_SETSEL, PosW(A2B(d, nEnd)), PosL(A2B(d, nStart))); + Pass(d, SCI_SCROLLCARET, 0, 0); +} + +LRESULT EditProc(wxWindow* w, SciData& d, UINT msg, WPARAM wp, LPARAM lp, bool& handled) { + UINT mapped = SciFromEM(msg); + if (mapped != msg) + return Proc(w, d, mapped, wp, lp, handled); + switch (msg) { + case WM_GETTEXT: { + if (!wp || !lp) + return 0; + std::string a = AnsiRange(d, 0, d.map.AnsiLength(d.stc)); + size_t n = std::min(a.size(), static_cast(wp) - 1); + char* out = reinterpret_cast(lp); + memcpy(out, a.data(), n); + out[n] = '\0'; + return static_cast(n); + } + case EM_LINEFROMCHAR: { + int pos = PosArg(wp); + int b = pos < 0 ? static_cast(Pass(d, SCI_GETSELECTIONSTART, 0, 0)) : A2B(d, pos); + return Pass(d, SCI_LINEFROMPOSITION, PosW(b), 0); + } + case EM_EXLINEFROMCHAR: + return Pass(d, SCI_LINEFROMPOSITION, PosW(A2B(d, PosArg(lp))), 0); + case EM_GETSEL: { + int a = B2A(d, static_cast(Pass(d, SCI_GETSELECTIONSTART, 0, 0))); + int b = B2A(d, static_cast(Pass(d, SCI_GETSELECTIONEND, 0, 0))); + if (wp) + *reinterpret_cast(wp) = a; + if (lp) + *reinterpret_cast(lp) = b; + return MAKELONG(a, b); + } + case EM_EXGETSEL: { + if (!lp) + return 0; + auto* cr = reinterpret_cast(lp); + cr->cpMin = B2A(d, static_cast(Pass(d, SCI_GETSELECTIONSTART, 0, 0))); + cr->cpMax = B2A(d, static_cast(Pass(d, SCI_GETSELECTIONEND, 0, 0))); + return 0; + } + case EM_SETSEL: { + int nStart = PosArg(wp); + int nEnd = PosArg(lp); + if (nStart == 0 && nEnd == -1) + nEnd = d.map.AnsiLength(d.stc); + if (nStart == -1) + nStart = nEnd; + SetSelectionAnsi(d, nStart, nEnd); + return 0; + } + case EM_EXSETSEL: { + if (!lp) + return 0; + auto* cr = reinterpret_cast(lp); + int nEnd = cr->cpMin == 0 && cr->cpMax == -1 ? d.map.AnsiLength(d.stc) : static_cast(cr->cpMax); + Pass(d, SCI_SETSEL, PosW(A2B(d, static_cast(cr->cpMin))), PosL(A2B(d, nEnd))); + Pass(d, SCI_SCROLLCARET, 0, 0); + return Pass(d, SCI_LINEFROMPOSITION, static_cast(Pass(d, SCI_GETSELECTIONSTART, 0, 0)), 0); + } + case EM_GETTEXTRANGE: { + auto* tr = reinterpret_cast(lp); + return tr ? GetTextRange(d, tr->chrg.cpMin, tr->chrg.cpMax, tr->lpstrText) : 0; + } + case EM_FINDTEXTEX: + return FindTextIn(d, wp, reinterpret_cast(lp)); + case EM_FORMATRANGE: + return 0; + default: + handled = false; + return 0; + } +} + +LRESULT Proc(wxWindow* w, SciData& d, UINT msg, WPARAM wp, LPARAM lp, bool& handled) { + handled = true; + if (msg < SCI_START) + return EditProc(w, d, msg, wp, lp, handled); + switch (msg) { + case SCI_GETDIRECTFUNCTION: + return reinterpret_cast(&DirectFunction); + case SCI_GETDIRECTPOINTER: + return reinterpret_cast(w); + + case SCI_SETCODEPAGE: + d.appCodePage = static_cast(wp); + return 0; + case SCI_GETCODEPAGE: + return d.appCodePage; + case SCI_SETMODEVENTMASK: + d.appModMask = static_cast(wp); + return Pass(d, msg, static_cast(ControlModMask(d.appModMask)), 0); + case SCI_GETMODEVENTMASK: + return d.appModMask; + case SCI_SETWRAPMODE: + return Pass(d, msg, wp == SC_WRAP_WORD ? static_cast(kWrapWhitespace) : wp, lp); + case SCI_GETWRAPMODE: { + LRESULT r = Pass(d, msg, wp, lp); + return r == kWrapWhitespace ? SC_WRAP_WORD : r; + } + case SCI_SETDOCPOINTER: { + LRESULT r = Pass(d, msg, wp, lp); + Pass(d, SCI_SETCODEPAGE, SC_CP_UTF8, 0); + d.map.Reset(); + d.mirrorValid = false; + return r; + } + + case SCI_GETLENGTH: + case SCI_GETTEXTLENGTH: + return d.map.AnsiLength(d.stc); + case SCI_LINELENGTH: { + LRESULT start = Pass(d, SCI_POSITIONFROMLINE, wp, 0); + LRESULT len = Pass(d, msg, wp, lp); + if (start < 0) + return len; + return B2A(d, static_cast(start + len)) - B2A(d, static_cast(start)); + } + case SCI_GETCHARAT: { + int b = A2B(d, PosArg(wp)); + int n = d.stc->GetLength(); + if (b < 0 || b >= n) + return 0; + const unsigned char* p = reinterpret_cast(d.stc->GetRangePointer(b, std::min(4, n - b))); + char a; + AnsiAt(CurrentCodec(), p, p[0] < 0x80 ? 1 : Utf8Width(p, static_cast(std::min(4, n - b))), a); + return static_cast(a); + } + + case SCI_GOTOPOS: + case SCI_SETANCHOR: + case SCI_SETCURRENTPOS: + case SCI_SETSELECTIONSTART: + case SCI_SETSELECTIONEND: + case SCI_SETTARGETSTART: + case SCI_SETTARGETEND: + case SCI_LINEFROMPOSITION: + case SCI_GETCOLUMN: + case SCI_GETSTYLEAT: + case SCI_BRACEBADLIGHT: + case SCI_INDICATORALLONFOR: + return Pass(d, msg, PosW(A2B(d, PosArg(wp))), lp); + case SCI_BRACEMATCH: + case SCI_POSITIONBEFORE: + case SCI_POSITIONAFTER: + case SCI_WORDSTARTPOSITION: + case SCI_WORDENDPOSITION: + return B2A(d, Pass(d, msg, PosW(A2B(d, PosArg(wp))), lp)); + case SCI_SETSEL: + case SCI_BRACEHIGHLIGHT: + case SCI_COPYRANGE: + case SCI_COLOURISE: + return Pass(d, msg, PosW(A2B(d, PosArg(wp))), PosL(A2B(d, PosArg(lp)))); + case SCI_POINTXFROMPOSITION: + case SCI_POINTYFROMPOSITION: + case SCI_INDICATORVALUEAT: + return Pass(d, msg, wp, PosL(A2B(d, PosArg(lp)))); + case SCI_INDICATORSTART: + case SCI_INDICATOREND: + return B2A(d, Pass(d, msg, wp, PosL(A2B(d, PosArg(lp))))); + case SCI_INDICATORFILLRANGE: + case SCI_INDICATORCLEARRANGE: { + int a = PosArg(wp); + int b0 = A2B(d, a); + int b1 = A2B(d, a + PosArg(lp)); + return Pass(d, msg, PosW(b0), PosL(b1 - b0)); + } + + case SCI_GETCURRENTPOS: + case SCI_GETANCHOR: + case SCI_GETSELECTIONSTART: + case SCI_GETSELECTIONEND: + case SCI_GETTARGETSTART: + case SCI_GETTARGETEND: + case SCI_GETENDSTYLED: + case SCI_POSITIONFROMLINE: + case SCI_GETLINEENDPOSITION: + case SCI_GETLINEINDENTPOSITION: + case SCI_POSITIONFROMPOINT: + case SCI_POSITIONFROMPOINTCLOSE: + case SCI_CALLTIPPOSSTART: + case SCI_AUTOCPOSSTART: + case SCI_GETLINESELSTARTPOSITION: + case SCI_GETLINESELENDPOSITION: + case SCI_FINDCOLUMN: + return B2A(d, Pass(d, msg, wp, lp)); + + case SCI_SETTEXT: { + if (!lp) + return 0; + std::string u = Utf8FromAnsi(reinterpret_cast(lp)); + return Pass(d, msg, 0, Ptr(u)); + } + case SCI_ADDTEXT: + case SCI_APPENDTEXT: + case SCI_COPYTEXT: { + if (!lp || static_cast(wp) < 0) + return 0; + std::string u = Utf8FromAnsi(reinterpret_cast(lp), static_cast(wp)); + return Pass(d, msg, u.size(), Ptr(u)); + } + case SCI_INSERTTEXT: { + if (!lp) + return 0; + std::string u = Utf8FromAnsi(reinterpret_cast(lp)); + return Pass(d, msg, PosW(A2B(d, PosArg(wp))), Ptr(u)); + } + case SCI_REPLACESEL: { + if (!lp) + return 0; + std::string u = Utf8FromAnsi(reinterpret_cast(lp)); + return Pass(d, msg, 0, Ptr(u)); + } + case SCI_REPLACETARGET: + case SCI_REPLACETARGETRE: { + if (!lp) + return 0; + const char* t = reinterpret_cast(lp); + std::string u = static_cast(wp) < 0 ? Utf8FromAnsi(t) : Utf8FromAnsi(t, static_cast(wp)); + Pass(d, msg, u.size(), Ptr(u)); + int start = static_cast(Pass(d, SCI_GETTARGETSTART, 0, 0)); + int end = static_cast(Pass(d, SCI_GETTARGETEND, 0, 0)); + return B2A(d, end) - B2A(d, start); + } + case SCI_SEARCHINTARGET: { + if (!lp) + return -1; + const char* t = reinterpret_cast(lp); + std::string u = static_cast(wp) < 0 ? Utf8FromAnsi(t) : Utf8FromAnsi(t, static_cast(wp)); + return B2A(d, Pass(d, msg, u.size(), Ptr(u))); + } + case SCI_SEARCHNEXT: + case SCI_SEARCHPREV: { + if (!lp) + return -1; + std::string u = Utf8FromAnsi(reinterpret_cast(lp)); + return B2A(d, Pass(d, msg, wp, Ptr(u))); + } + case SCI_TEXTWIDTH: + case SCI_STYLESETFONT: + case SCI_SETKEYWORDS: + case SCI_AUTOCSTOPS: + case SCI_AUTOCSELECT: + case SCI_AUTOCSETFILLUPS: + case SCI_USERLISTSHOW: { + if (!lp) + return Pass(d, msg, wp, lp); + std::string u = Utf8FromAnsi(reinterpret_cast(lp)); + return Pass(d, msg, wp, Ptr(u)); + } + case SCI_CALLTIPSHOW: { + std::string u = Utf8FromAnsi(reinterpret_cast(lp)); + return Pass(d, msg, PosW(A2B(d, PosArg(wp))), Ptr(u)); + } + case SCI_AUTOCSHOW: { + int caret = static_cast(Pass(d, SCI_GETCURRENTPOS, 0, 0)); + int entered = caret - A2B(d, B2A(d, caret) - PosArg(wp)); + std::string u = Utf8FromAnsi(reinterpret_cast(lp)); + return Pass(d, msg, PosW(entered), Ptr(u)); + } + case SCI_SETWORDCHARS: + case SCI_SETWHITESPACECHARS: { + if (!lp) + return Pass(d, msg, wp, lp); + std::string ascii; + for (const char* p = reinterpret_cast(lp); *p; ++p) + if (static_cast(*p) < 0x80) + ascii.push_back(*p); + return Pass(d, msg, wp, Ptr(ascii)); + } + case SCI_SETLENGTHFORENCODE: + d.lengthForEncode = PosArg(wp); + return Pass(d, msg, wp, lp); + case SCI_ENCODEDFROMUTF8: { + const char* src = reinterpret_cast(wp); + if (!src) + return 0; + size_t n = d.lengthForEncode < 0 ? strlen(src) : static_cast(d.lengthForEncode); + return PutString(AnsiFromUtf8(src, n), lp); + } + + case SCI_GETTEXT: { + int total = d.map.AnsiLength(d.stc); + if (!lp) + return total + 1; + if (!wp) + return 0; + size_t want = static_cast(wp) - 1; + std::string a = AnsiRange(d, 0, static_cast(std::min(want, static_cast(total)))); + char* out = reinterpret_cast(lp); + memcpy(out, a.data(), a.size()); + memset(out + a.size(), 0, want - a.size() + 1); + return static_cast(want); + } + case SCI_GETSELTEXT: { + std::string a = FetchString(d, msg, 0, true); + if (!lp) + return static_cast(a.size()) + 1; + char* out = reinterpret_cast(lp); + memcpy(out, a.data(), a.size()); + out[a.size()] = '\0'; + return a.empty() ? 0 : static_cast(a.size()) + 1; + } + case SCI_GETLINE: { + std::string a = FetchString(d, msg, wp, false); + if (lp) + memcpy(reinterpret_cast(lp), a.data(), a.size()); + return static_cast(a.size()); + } + case SCI_GETCURLINE: { + int caret = static_cast(Pass(d, SCI_GETCURRENTPOS, 0, 0)); + LRESULT line = Pass(d, SCI_LINEFROMPOSITION, PosW(caret), 0); + int start = static_cast(Pass(d, SCI_POSITIONFROMLINE, static_cast(line), 0)); + int end = start + static_cast(Pass(d, SCI_LINELENGTH, static_cast(line), 0)); + std::string a = AnsiOfBytes(d, start, end); + if (!lp) + return static_cast(a.size()) + 1; + int caretInLine = B2A(d, caret) - B2A(d, start); + if (!wp) + return caretInLine; + size_t n = std::min(a.size(), static_cast(wp) - 1); + char* out = reinterpret_cast(lp); + memcpy(out, a.data(), n); + out[n] = '\0'; + return caretInLine; + } + case SCI_STYLEGETFONT: + return PutString(FetchString(d, msg, wp, false), lp); + case SCI_GETTEXTRANGE: { + auto* tr = reinterpret_cast(lp); + return tr ? GetTextRange(d, tr->chrg.cpMin, tr->chrg.cpMax, tr->lpstrText) : 0; + } + case SCI_GETSTYLEDTEXT: + return GetStyledText(d, reinterpret_cast(lp)); + case SCI_GETCHARACTERPOINTER: + return GetCharacterPointer(d); + case SCI_FINDTEXT: + return FindTextIn(d, wp, reinterpret_cast(lp)); + case SCI_FORMATRANGE: + return FormatRange(d, wp != 0, reinterpret_cast(lp)); + + case SCI_STARTSTYLING: + d.stylingAnsi = PosArg(wp); + return Pass(d, msg, PosW(A2B(d, d.stylingAnsi)), lp); + case SCI_SETSTYLING: { + int len = PosArg(wp); + int b0 = A2B(d, d.stylingAnsi); + int b1 = A2B(d, d.stylingAnsi + len); + d.stylingAnsi += len; + return Pass(d, msg, PosW(b1 - b0), lp); + } + case SCI_SETSTYLINGEX: { + int len = PosArg(wp); + if (len <= 0 || !lp) + return 0; + const char* styles = reinterpret_cast(lp); + int b0 = A2B(d, d.stylingAnsi); + int b1 = A2B(d, d.stylingAnsi + len); + int n = d.stc->GetLength(); + int bytes = std::min(b1, n) - b0; + std::vector expanded; + expanded.reserve(static_cast(std::max(bytes, 0))); + if (bytes > 0) { + const unsigned char* p = reinterpret_cast(d.stc->GetRangePointer(b0, bytes)); + for (int i = 0, ch = 0; i < bytes && ch < len; ++ch) { + int width = p[i] < 0x80 ? 1 : Utf8Width(p + i, static_cast(bytes - i)); + expanded.insert(expanded.end(), static_cast(width), styles[ch]); + i += width; + } + } + d.stylingAnsi += len; + return Pass(d, msg, expanded.size(), reinterpret_cast(expanded.data())); + } + case SCI_ADDSTYLEDTEXT: + return AddStyledText(d, wp, lp); + + case SCI_SETLEXER: + d.cryptoolLexer = static_cast(wp) == kCrypToolLexer; + return Pass(d, msg, d.cryptoolLexer ? static_cast(SCLEX_CONTAINER) : wp, lp); + case SCI_GETLEXER: + return d.cryptoolLexer ? kCrypToolLexer : Pass(d, msg, wp, lp); + case SCI_SETLEXERLANGUAGE: { + const char* name = reinterpret_cast(lp); + if (name && strcasecmp(name, "cryptool") == 0) { + d.cryptoolLexer = true; + return Pass(d, SCI_SETLEXER, SCLEX_CONTAINER, 0); + } + d.cryptoolLexer = false; + std::string u = Utf8FromAnsi(name); + return Pass(d, msg, wp, Ptr(u)); + } + case SCI_LOADLEXERLIBRARY: + return 0; + case SCI_SETPROPERTY: { + const char* key = reinterpret_cast(wp); + const char* value = reinterpret_cast(lp); + if (!key) + return 0; + d.props[key] = value ? value : ""; + if (!strcmp(key, "cryptool.alphabet") || !strcmp(key, "cryptool.nonalphabetstyle")) + RebuildAlphabet(d); + std::string k = Utf8FromAnsi(key); + std::string v = Utf8FromAnsi(value); + return Pass(d, msg, reinterpret_cast(k.c_str()), Ptr(v)); + } + case SCI_GETPROPERTY: + case SCI_GETPROPERTYEXPANDED: { + const char* key = reinterpret_cast(wp); + if (!key) + return 0; + auto it = d.props.find(key); + if (msg == SCI_GETPROPERTY && it != d.props.end()) + return PutString(it->second, lp); + std::string k = Utf8FromAnsi(key); + return PutString(FetchString(d, msg, reinterpret_cast(k.c_str()), false), lp); + } + case SCI_GETPROPERTYINT: { + const char* key = reinterpret_cast(wp); + if (!key) + return lp; + std::string k = Utf8FromAnsi(key); + return Pass(d, msg, reinterpret_cast(k.c_str()), lp); + } + + case SCI_PASTE: { + LRESULT r = Pass(d, msg, wp, lp); + NotifyParent(w, SCEN_CHANGE); + return r; + } + + default: + return Pass(d, msg, wp, lp); + } +} + +sptr_t DirectFunction(sptr_t ptr, unsigned int msg, uptr_t wp, sptr_t lp) { + wxWindow* w = reinterpret_cast(ptr); + return OnMain([&]() -> sptr_t { + bool handled = false; + return static_cast(ScintillaWindowProc(w, msg, static_cast(wp), static_cast(lp), handled)); + }); +} + +} // namespace + +void BindScintillaEvents(wxWindow* window) { DataFor(window); } + +LRESULT ScintillaWindowProc(wxWindow* window, UINT msg, WPARAM wParam, LPARAM lParam, bool& handled) { + SciData* d = DataFor(window); + if (!d) { + handled = false; + return 0; + } + return Proc(window, *d, msg, wParam, lParam, handled); +} + +} // namespace mfcwx diff --git a/port/mfcwx/tests/scintilla_tests.cpp b/port/mfcwx/tests/scintilla_tests.cpp index 1ca64d377..4f1697a46 100644 --- a/port/mfcwx/tests/scintilla_tests.cpp +++ b/port/mfcwx/tests/scintilla_tests.cpp @@ -1,6 +1,4 @@ -// Tests of the Scintilla proxy (src/scintilla.cpp): ANSI text and positions on top of a UTF-8 -// wxStyledTextCtrl. Built from scintilla.cpp and the code page sources only; the few window -// functions it calls are replaced by recording stubs below. +// Built without the rest of mfcwx: the window functions scintilla.cpp calls are recording stubs below. #include "windows_impl.h" @@ -244,7 +242,6 @@ void TestCp1250Positions() { CHECK_STR(buf, "\xAF\xD3\xA3\xC6"); CHECK_STR(Range(2, 6), "\xBF\xF3\xB3\xE6"); - // CScintillaWnd::SearchForward / ReplaceSearchedText Send(SCI_SETSEL, 0, static_cast(0)); Send(SCI_SETSEARCHFLAGS, SCFIND_MATCHCASE); Send(SCI_SETTARGETSTART, 0); @@ -256,7 +253,6 @@ void TestCp1250Positions() { CHECK_STR(GetAll(), "Za\xBF\xF3\xA3\xC6! g\xEA\x9Cl\xB9 ja\x9F\xF1\r\nZA\xAF\xD3\xA3\xC6 koniec"); CHECK_EQ(Send(SCI_GETLENGTH), len + 1); - // CScintillaWnd::ReplaceAll Send(SCI_SETTEXT, 0, "\xB9\xB9 a \xB9"); const char* find = "\xB9"; const char* repl = "\xA5\xA5"; @@ -528,7 +524,6 @@ void TestMisc() { Send(SCI_BRACEHIGHLIGHT, 0, 2); } -// Random edits against a model string, compared through the ANSI interface. void TestLargeDocument() { mfcwx::SetAnsiCodePage(1250); std::mt19937 rng(12345); @@ -553,23 +548,37 @@ void TestLargeDocument() { long long sum = 0; for (int i = 0; i < 20000; ++i) { int p = static_cast(rng() % (model.size() + 1)); - Send(SCI_GOTOPOS, W(p)); - sum += Send(SCI_GETCURRENTPOS) + Send(SCI_LINEFROMPOSITION, W(p)); + sum += Send(SCI_LINEFROMPOSITION, W(p)) + Send(SCI_POSITIONAFTER, W(p)); } for (int i = 0; i < 2000; ++i) { int p = static_cast(rng() % model.size()); Send(SCI_INSERTTEXT, W(p), "\xE4"); - Send(SCI_GOTOPOS, W(p + 1)); - sum += Send(SCI_GETCURRENTPOS); - Send(SCI_SETSEL, W(p), static_cast(p + 1)); - Send(SCI_CLEAR); + Send(SCI_SETTARGETSTART, W(p)); + Send(SCI_SETTARGETEND, W(p + 1)); + Send(SCI_REPLACETARGET, 0, ""); + sum += Send(SCI_GETLENGTH); } double proxyMs = std::chrono::duration(std::chrono::steady_clock::now() - t0).count(); - printf("large document: load + 20000 lookups + 2000 edits through the proxy: %.1f ms (%lld)\n", proxyMs, sum); + auto t1 = std::chrono::steady_clock::now(); + for (int i = 0; i < 20000; ++i) { + int p = static_cast(rng() % (model.size() + 1)); + sum += g_stc->SendMsg(SCI_LINEFROMPOSITION, p) + g_stc->SendMsg(SCI_POSITIONAFTER, p); + } + for (int i = 0; i < 2000; ++i) { + int p = static_cast(rng() % model.size()); + g_stc->SendMsg(SCI_INSERTTEXT, p, reinterpret_cast("\xC3\xA4")); + g_stc->SendMsg(SCI_SETTARGETSTART, p); + g_stc->SendMsg(SCI_SETTARGETEND, p + 2); + g_stc->SendMsg(SCI_REPLACETARGET, 0, reinterpret_cast("")); + sum += g_stc->SendMsg(SCI_GETLENGTH); + } + double rawMs = std::chrono::duration(std::chrono::steady_clock::now() - t1).count(); + printf("large document: load + 20000 lookups + 2000 edits: proxy %.1f ms, same calls sent to wx directly %.1f ms (%lld)\n", + proxyMs, rawMs, sum); CHECK(copy == model); CHECK(GetAll() == model); bool ok = Send(SCI_GETLENGTH) == static_cast(model.size()); - for (int i = 0; i < 2000 && ok; ++i) { + for (int i = 0; i < 300 && ok; ++i) { int p = static_cast(rng() % (model.size() + 1)); Send(SCI_GOTOPOS, W(p)); ok = Send(SCI_GETCURRENTPOS) == p && g_stc->GetCurrentPos() == g_stc->PositionRelative(0, p); From 0fb9fb69cca929cdea241aadfd9845c49c277b02 Mon Sep 17 00:00:00 2001 From: Kamil Marczak Date: Tue, 6 Oct 2026 23:38:28 +0200 Subject: [PATCH 27/46] fix(mfcwx): forward every message of a Scintilla window to the proxy --- port/mfcwx/src/controls.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/port/mfcwx/src/controls.cpp b/port/mfcwx/src/controls.cpp index 5396758e0..c112bde47 100644 --- a/port/mfcwx/src/controls.cpp +++ b/port/mfcwx/src/controls.cpp @@ -2471,9 +2471,7 @@ LRESULT ControlWindowProc(wxWindow* w, WindowState& st, UINT msg, WPARAM wParam, case ControlKind::ListView: return ListViewProc(w, st, msg, wParam, lParam, handled); case ControlKind::Scintilla: - if (msg >= 2000 || msg == WM_GETTEXT || msg == WM_SETTEXT || msg == WM_GETTEXTLENGTH) - return ScintillaWindowProc(w, msg, wParam, lParam, handled); - break; + return ScintillaWindowProc(w, msg, wParam, lParam, handled); default: break; } From 57ec062098731172f17c30b1afe6580ba35cddfb Mon Sep 17 00:00:00 2001 From: Kamil Marczak Date: Tue, 6 Oct 2026 23:42:05 +0200 Subject: [PATCH 28/46] fix(mfcwx): keep NUL bytes of copied Scintilla text in CF_TEXT The proxy records the exact ANSI bytes of a copy and GetClipboardData returns them while the system clipboard still holds that text, padded to the selection length so CScintillaWnd::CopyCut never reads past the buffer. --- port/mfcwx/src/scintilla.cpp | 28 +++++++++++++++- port/mfcwx/src/winapi.cpp | 49 ++++++++++++++++++++++++++++ port/mfcwx/src/windows_impl.h | 3 ++ port/mfcwx/tests/scintilla_tests.cpp | 48 +++++++++++++++++++++++++++ 4 files changed, 127 insertions(+), 1 deletion(-) diff --git a/port/mfcwx/src/scintilla.cpp b/port/mfcwx/src/scintilla.cpp index 998825a24..b94305974 100644 --- a/port/mfcwx/src/scintilla.cpp +++ b/port/mfcwx/src/scintilla.cpp @@ -431,6 +431,12 @@ std::string FetchString(SciData& d, UINT msg, WPARAM wp, bool sizeIncludesNul) { return AnsiFromUtf8(buf.data(), static_cast(n)); } +void RememberCopied(std::string ansiBytes, size_t minSize = 0) { + if (ansiBytes.empty()) + return; + RememberClipboardText(std::move(ansiBytes), minSize); +} + void RebuildAlphabet(SciData& d) { const std::string& a = d.props["cryptool.alphabet"]; std::fill(std::begin(d.alphabet), std::end(d.alphabet), false); @@ -1008,9 +1014,16 @@ LRESULT Proc(wxWindow* w, SciData& d, UINT msg, WPARAM wp, LPARAM lp, bool& hand return B2A(d, Pass(d, msg, PosW(A2B(d, PosArg(wp))), lp)); case SCI_SETSEL: case SCI_BRACEHIGHLIGHT: - case SCI_COPYRANGE: case SCI_COLOURISE: return Pass(d, msg, PosW(A2B(d, PosArg(wp))), PosL(A2B(d, PosArg(lp)))); + case SCI_COPYRANGE: { + int a = PosArg(wp); + int b = PosArg(lp); + if (a > b) + std::swap(a, b); + RememberCopied(AnsiRange(d, a, b)); + return Pass(d, msg, PosW(A2B(d, PosArg(wp))), PosL(A2B(d, PosArg(lp)))); + } case SCI_POINTXFROMPOSITION: case SCI_POINTYFROMPOSITION: case SCI_INDICATORVALUEAT: @@ -1056,6 +1069,8 @@ LRESULT Proc(wxWindow* w, SciData& d, UINT msg, WPARAM wp, LPARAM lp, bool& hand case SCI_COPYTEXT: { if (!lp || static_cast(wp) < 0) return 0; + if (msg == SCI_COPYTEXT) + RememberCopied(std::string(reinterpret_cast(lp), static_cast(wp))); std::string u = Utf8FromAnsi(reinterpret_cast(lp), static_cast(wp)); return Pass(d, msg, u.size(), Ptr(u)); } @@ -1282,6 +1297,17 @@ LRESULT Proc(wxWindow* w, SciData& d, UINT msg, WPARAM wp, LPARAM lp, bool& hand return Pass(d, msg, reinterpret_cast(k.c_str()), lp); } + case SCI_COPY: + case SCI_CUT: { + int b0 = static_cast(Pass(d, SCI_GETSELECTIONSTART, 0, 0)); + int b1 = static_cast(Pass(d, SCI_GETSELECTIONEND, 0, 0)); + size_t ansiLen = static_cast(std::max(B2A(d, b1) - B2A(d, b0), 0)); + // SCI_GETSELTEXT replaces NUL bytes, so a stream selection is read from the document + bool rect = Pass(d, SCI_SELECTIONISRECTANGLE, 0, 0) != 0; + RememberCopied(rect ? FetchString(d, SCI_GETSELTEXT, 0, true) : AnsiOfBytes(d, b0, b1), ansiLen); + return Pass(d, msg, wp, lp); + } + case SCI_PASTE: { LRESULT r = Pass(d, msg, wp, lp); NotifyParent(w, SCEN_CHANGE); diff --git a/port/mfcwx/src/winapi.cpp b/port/mfcwx/src/winapi.cpp index 1d9c35763..d41bf1dd1 100644 --- a/port/mfcwx/src/winapi.cpp +++ b/port/mfcwx/src/winapi.cpp @@ -1305,6 +1305,9 @@ struct ClipboardState { bool emptied = false; std::vector owned; std::map formats; + std::string remembered; + size_t rememberedMin = 0; + bool hasRemembered = false; }; ClipboardState& Clip() { @@ -1319,8 +1322,45 @@ std::string FormatName(UINT format) { return "CrypTool.Format." + std::to_string(format); } +void ForgetClipboardText() { + Clip().remembered.clear(); + Clip().rememberedMin = 0; + Clip().hasRemembered = false; +} + +// What wxStyledTextCtrl puts on the clipboard: NUL becomes a space and every line ending becomes LF. +wxString NormalizeCopiedText(const wxString& s) { + wxString out; + bool afterCr = false; + for (wxString::const_iterator it = s.begin(); it != s.end(); ++it) { + wxUniChar c = *it; + if (c == '\n' && afterCr) { + afterCr = false; + continue; + } + afterCr = c == '\r'; + out += c == '\r' ? wxUniChar('\n') : (c == wxUniChar(0) ? wxUniChar(' ') : c); + } + return out; +} + +bool RememberedMatches(const wxString& clipboardText) { + const ClipboardState& c = Clip(); + return c.hasRemembered && + NormalizeCopiedText(ToWx(c.remembered.data(), static_cast(c.remembered.size()))) == + NormalizeCopiedText(clipboardText); +} + } // namespace +void mfcwx::RememberClipboardText(std::string ansiBytes, size_t minSize) { + OnMain([&]() { + Clip().remembered = std::move(ansiBytes); + Clip().rememberedMin = minSize; + Clip().hasRemembered = true; + }); +} + BOOL OpenClipboard(HWND) { return OnMain([]() -> BOOL { if (Clip().open) @@ -1352,6 +1392,7 @@ BOOL EmptyClipboard() { if (!Clip().open) return FALSE; wxTheClipboard->Clear(); + ForgetClipboardText(); Clip().emptied = true; return TRUE; }); @@ -1366,6 +1407,7 @@ HANDLE SetClipboardData(UINT uFormat, HANDLE hMem) { if (!data) return nullptr; if (uFormat == CF_TEXT || uFormat == CF_OEMTEXT) { + ForgetClipboardText(); size_t len = strnlen(data, size); wxString text = ToWx(data, static_cast(len)); text.Replace("\r\n", "\n"); @@ -1391,6 +1433,13 @@ HANDLE GetClipboardData(UINT uFormat) { if (!wxTheClipboard->GetData(obj)) return nullptr; wxString text = obj.GetText(); + if (uFormat != CF_UNICODETEXT && RememberedMatches(text)) { + std::string a = Clip().remembered; + a.resize(std::max(a.size(), Clip().rememberedMin) + 1, '\0'); + HGLOBAL h = GlobalFromData(a.data(), a.size()); + Clip().owned.push_back(h); + return h; + } text.Replace("\r\n", "\n"); text.Replace("\n", "\r\n"); std::string a = FromWx(text); diff --git a/port/mfcwx/src/windows_impl.h b/port/mfcwx/src/windows_impl.h index 093db125d..ddac5754b 100644 --- a/port/mfcwx/src/windows_impl.h +++ b/port/mfcwx/src/windows_impl.h @@ -75,6 +75,9 @@ void SetControlText(wxWindow* window, const wxString& text); void BindScintillaEvents(wxWindow* window); LRESULT ScintillaWindowProc(wxWindow* window, UINT msg, WPARAM wParam, LPARAM lParam, bool& handled); +// winapi.cpp: exact ANSI bytes copied by a Scintilla window; GetClipboardData(CF_TEXT) returns them (zero-padded to minSize). +void RememberClipboardText(std::string ansiBytes, size_t minSize); + // gdi.cpp: an HBITMAP owning a copy of bmp HBITMAP CreateBitmapHandle(const wxBitmap& bmp); diff --git a/port/mfcwx/tests/scintilla_tests.cpp b/port/mfcwx/tests/scintilla_tests.cpp index 4f1697a46..1a9eca985 100644 --- a/port/mfcwx/tests/scintilla_tests.cpp +++ b/port/mfcwx/tests/scintilla_tests.cpp @@ -28,6 +28,7 @@ struct Note { std::vector g_notes; std::vector g_commands; +std::vector> g_remembered; std::function g_onNotify; int g_failures = 0; @@ -524,6 +525,50 @@ void TestMisc() { Send(SCI_BRACEHIGHLIGHT, 0, 2); } +void TestClipboardBytes() { + mfcwx::SetAnsiCodePage(1252); + const std::string t("ab\0c\xE9\r\nd\0", 9); + SetBytes(t); + + g_remembered.clear(); + Send(SCI_SETSEL, 1, 8); + Send(SCI_COPY); + CHECK_EQ(g_remembered.size(), 1); + CHECK(g_remembered.back().first == t.substr(1, 7)); + CHECK_EQ(g_remembered.back().second, 7); + + g_remembered.clear(); + Send(SCI_SETSEL, 3, 3); + Send(SCI_COPY); + CHECK_EQ(g_remembered.size(), 0); + + g_remembered.clear(); + Send(SCI_COPYRANGE, 2, 5); + CHECK_EQ(g_remembered.size(), 1); + CHECK(g_remembered.back().first == t.substr(2, 3)); + + g_remembered.clear(); + Send(SCI_COPYTEXT, 4, "x\0y\xFC"); + CHECK_EQ(g_remembered.size(), 1); + CHECK(g_remembered.back().first == std::string("x\0y\xFC", 4)); + + g_remembered.clear(); + Send(WM_COPY); + CHECK_EQ(g_remembered.size(), 0); + Send(SCI_SETSEL, 0, 9); + Send(WM_COPY); + CHECK_EQ(g_remembered.size(), 1); + CHECK(g_remembered.back().first == t); + + g_remembered.clear(); + Send(SCI_SETSEL, 2, 4); + Send(WM_CUT); + CHECK_EQ(g_remembered.size(), 1); + CHECK(g_remembered.back().first == t.substr(2, 2)); + CHECK_STR(GetAll(), t.substr(0, 2) + t.substr(4)); + mfcwx::SetAnsiCodePage(1252); +} + void TestLargeDocument() { mfcwx::SetAnsiCodePage(1250); std::mt19937 rng(12345); @@ -628,6 +673,7 @@ int RunTests() { TestNotifications(); TestSanitizedInput(); TestMisc(); + TestClipboardBytes(); TestLargeDocument(); frame->Destroy(); printf("%d checks, %d failures\n", g_checks, g_failures); @@ -667,6 +713,8 @@ LRESULT NotifyParentNM(wxWindow* control, NMHDR* hdr) { return 0; } +void RememberClipboardText(std::string ansiBytes, size_t minSize) { g_remembered.emplace_back(std::move(ansiBytes), minSize); } + bool IsMainThread() { return true; } void RunOnMainThread(const std::function& fn) { fn(); } From 0eecba48ed82f9448f58680ce80ce214a58babd9 Mon Sep 17 00:00:00 2001 From: Kamil Marczak Date: Tue, 6 Oct 2026 23:42:45 +0200 Subject: [PATCH 29/46] fix(app): make all application sources compile with clang and gcc Finishes the MSVC-ism cleanup (varargs CString casts, temporaries bound to references, header qualification), fixes 64-bit pointer truncations and a stack overflow in WinHelpInternal, and builds the homophonic substitution sources again as the VS2008 project does. --- CrypTool/AppDocument.cpp | 2 +- CrypTool/AutomaticAnalysis.cpp | 4 +-- CrypTool/BruteForceHeap.cpp | 1 + CrypTool/CrypToolVolRen.cpp | 2 +- CrypTool/CryptDocTemplate.h | 2 +- CrypTool/Cryptography.cpp | 4 +-- CrypTool/DiffieHellmanParty.cpp | 2 +- CrypTool/DlgRSADemo.cpp | 2 +- CrypTool/DlgSolitaire.cpp | 2 +- CrypTool/DlgVigenereAnalysisSchroedel.h | 2 +- CrypTool/HexEditBase.cpp | 10 +++--- CrypTool/HomophonicSubstitution.cpp | 2 +- CrypTool/OpenGLView.cpp | 4 +-- CrypTool/POLY.CPP | 4 +-- CrypTool/POLYMOD.CPP | 18 +++++------ CrypTool/POLYMOD.H | 8 +++-- CrypTool/PrimeTest.h | 2 +- CrypTool/ScintillaView.cpp | 2 +- CrypTool/ScintillaWnd.cpp | 42 ++++++++++++------------- CrypTool/ScintillaWnd.h | 6 ++-- CrypTool/SecudeTools.cpp | 6 ++-- CrypTool/SignatureAttack.cpp | 4 +-- CrypTool/Solitaire.cpp | 2 +- CrypTool/Solitaire.h | 1 + CrypTool/SymEncBase.cpp | 2 -- CrypTool/ZZXY.cpp | 2 +- CrypTool/ZZXY.h | 2 +- libVolRen/Volume.h | 9 ++++++ port/app/CMakeLists.txt | 4 +-- port/mfcwx/include/mfcwx/crtcompat.h | 1 + 30 files changed, 84 insertions(+), 70 deletions(-) diff --git a/CrypTool/AppDocument.cpp b/CrypTool/AppDocument.cpp index 9fd270b69..1279aa08e 100644 --- a/CrypTool/AppDocument.cpp +++ b/CrypTool/AppDocument.cpp @@ -21,7 +21,7 @@ #include "stdafx.h" #include "CrypToolApp.h" #include "AppDocument.h" -#include "Windows.h" +#include "windows.h" #ifdef _DEBUG #define new DEBUG_NEW diff --git a/CrypTool/AutomaticAnalysis.cpp b/CrypTool/AutomaticAnalysis.cpp index 489b88c83..9bed7132d 100644 --- a/CrypTool/AutomaticAnalysis.cpp +++ b/CrypTool/AutomaticAnalysis.cpp @@ -115,7 +115,7 @@ void CaesarAuto(const char *infile, const char *OldTitle) LoadString(AfxGetInstanceHandle(),IDS_STRING_CORRELATION,pc_str,STR_LAENGE_STRING_TABLE); MakeNewName2(line,sizeof(line),pc_str,theApp.TextOptions.getReferenceFileName(), OldTitle); - c.Show(OStream(name)<< OStream::Title(0) << OStream::Description(0) << OStream::Summary(0)); + { OStream os(name); c.Show(os << OStream::Title(0) << OStream::Description(0) << OStream::Summary(0)); } // == get the tmp name without file extension strcpy(name2, name); @@ -310,7 +310,7 @@ UINT VigenereAuto(PVOID p) LoadString(AfxGetInstanceHandle(),IDS_STRING_MSG_CORRELATION_CAESAR_REFERENCE,pc_str,STR_LAENGE_STRING_TABLE); sprintf(line,pc_str,i+1, (LPCTSTR)theApp.TextOptions.getReferenceFileName()); - c.Show(OStream(name)<< OStream::Title(0) << OStream::Description(0) << OStream::Summary(0)); + { OStream os(name); c.Show(os << OStream::Title(0) << OStream::Description(0) << OStream::Summary(0)); } // get the tmp name without file extension strcpy(name2, name); diff --git a/CrypTool/BruteForceHeap.cpp b/CrypTool/BruteForceHeap.cpp index b2cfc403a..dcb9fa17a 100644 --- a/CrypTool/BruteForceHeap.cpp +++ b/CrypTool/BruteForceHeap.cpp @@ -19,6 +19,7 @@ **************************************************************************/ #include "BruteForceHeap.h" +#include diff --git a/CrypTool/CrypToolVolRen.cpp b/CrypTool/CrypToolVolRen.cpp index b9cff0931..e976561f4 100644 --- a/CrypTool/CrypToolVolRen.cpp +++ b/CrypTool/CrypToolVolRen.cpp @@ -20,7 +20,7 @@ #include "stdafx.h" #include "CrypToolVolRen.h" -#include +#include #include #include diff --git a/CrypTool/CryptDocTemplate.h b/CrypTool/CryptDocTemplate.h index 27c98ee94..893e6ddd5 100644 --- a/CrypTool/CryptDocTemplate.h +++ b/CrypTool/CryptDocTemplate.h @@ -18,7 +18,7 @@ **************************************************************************/ -#include +#include class CCryptDocTemplate : public CMultiDocTemplate { diff --git a/CrypTool/Cryptography.cpp b/CrypTool/Cryptography.cpp index e3782b3e3..42035ac96 100644 --- a/CrypTool/Cryptography.cpp +++ b/CrypTool/Cryptography.cpp @@ -1152,7 +1152,7 @@ void HistogramASCII(const char *infile, const char *OldTitle) LoadString(AfxGetInstanceHandle(),IDS_STRING_ASCII_HISTOGRAM_OF,pc_str,STR_LAENGE_STRING_TABLE); MakeNewName2(line,sizeof(line),pc_str,_itoa(len, numbuff, 10),OldTitle); - distr.Show(OStream(name)<< OStream::Title(0) << OStream::Description(0) << OStream::Summary(0) << OStream::Percent()); + { OStream os(name); distr.Show(os << OStream::Title(0) << OStream::Description(0) << OStream::Summary(0) << OStream::Percent()); } // get the tmp name without file extension strcpy(name2, name); name2[strlen(name)-4] = 0x0; @@ -1195,7 +1195,7 @@ void HistogramBin(const char *infile, const char *OldTitle) int len = text.GetSize(); LoadString(AfxGetInstanceHandle(),IDS_STRING_BINARY_HISTOGRAM_OF,pc_str,STR_LAENGE_STRING_TABLE); MakeNewName2(line,sizeof(line),pc_str,_itoa(len, numbuff, 10),OldTitle); - distr.Show(OStream(name)<< OStream::Title(0) << OStream::Description(0) << OStream::Summary(0) << OStream::Percent()); + { OStream os(name); distr.Show(os << OStream::Title(0) << OStream::Description(0) << OStream::Summary(0) << OStream::Percent()); } // get the tmp name without file extension strcpy(name2, name); diff --git a/CrypTool/DiffieHellmanParty.cpp b/CrypTool/DiffieHellmanParty.cpp index 8d8c444d0..a5836a378 100644 --- a/CrypTool/DiffieHellmanParty.cpp +++ b/CrypTool/DiffieHellmanParty.cpp @@ -95,7 +95,7 @@ std::string DiffieHellmanParty::GetSessionKey(std::string k)throw(DHError) // Diese Funktion setzt das Geheimnis (ein zufälliges Geheimnis wird erzeugt) // Parameter: keine -void DiffieHellmanParty::SetSecret() +void DiffieHellmanParty::SetSecret()throw(DHError) { // Geheimnis "ausdenken" irand((long)time(NULL)); diff --git a/CrypTool/DlgRSADemo.cpp b/CrypTool/DlgRSADemo.cpp index db9be553d..10a15c27e 100644 --- a/CrypTool/DlgRSADemo.cpp +++ b/CrypTool/DlgRSADemo.cpp @@ -21,7 +21,7 @@ // RSA_mit_kleinenPZ.cpp: Implementierungsdatei // #include "stdafx.h" -#include "Afx.h" +#include "afx.h" #include "CrypToolApp.h" #include "DlgOptionsRSADemo.h" #include "DlgRSADemo.h" diff --git a/CrypTool/DlgSolitaire.cpp b/CrypTool/DlgSolitaire.cpp index 27ff1b9bb..c914bb42c 100644 --- a/CrypTool/DlgSolitaire.cpp +++ b/CrypTool/DlgSolitaire.cpp @@ -68,7 +68,7 @@ bool CDlgSolitaire::load_infile() { ifstream i_file; in_buffer_size = 0; - i_file.open( infile, ios::in || ios::binary ); + i_file.open( infile, ios::in ); // was "ios::in || ios::binary", which evaluates to ios::in if ( i_file.is_open() ) { if ( !in_buffer ) in_buffer = new unsigned char[4000]; diff --git a/CrypTool/DlgVigenereAnalysisSchroedel.h b/CrypTool/DlgVigenereAnalysisSchroedel.h index f1d338aa5..424e0489f 100644 --- a/CrypTool/DlgVigenereAnalysisSchroedel.h +++ b/CrypTool/DlgVigenereAnalysisSchroedel.h @@ -46,7 +46,7 @@ struct PossibleResult { // it wasn't very reliable; instead, we present ALL possible solutions to the // user; the order depends on (1) the length of the longest dictionary match, // and (2) the length of the longest key - bool operator<(const PossibleResult &_result) { + bool operator<(const PossibleResult &_result) const { if(longestDictionaryMatch.GetLength() == _result.longestDictionaryMatch.GetLength()) return (key.GetLength() > _result.key.GetLength()); else diff --git a/CrypTool/HexEditBase.cpp b/CrypTool/HexEditBase.cpp index 35886c140..67b25498d 100644 --- a/CrypTool/HexEditBase.cpp +++ b/CrypTool/HexEditBase.cpp @@ -817,7 +817,8 @@ void CHexEditBase::PaintHexData(CDC& cDC) cRect.right -= (pBuf-1+m_tPaintDetails.nBytesPerRow*3-pSelectionBufPtrEnd) * m_tPaintDetails.nCharacterWidth; CRect cSelectionRect(cRect); cSelectionRect.InflateRect(0, -1, +1, 0); - cDC.FillRect(cSelectionRect, &CBrush(bHasFocus ? m_tSelectedFousBkgCol : m_tSelectedNoFocusBkgCol)); + CBrush cSelectionBrush(bHasFocus ? m_tSelectedFousBkgCol : m_tSelectedNoFocusBkgCol); + cDC.FillRect(cSelectionRect, &cSelectionBrush); *pSelectionBufPtrEnd = '\0'; // set "end-mark" cDC.SetTextColor(bHasFocus ? m_tSelectedFousTxtCol : m_tSelectedNoFocusTxtCol); cDC.SetBkColor(m_tHighlightBkgCol); @@ -1006,7 +1007,8 @@ ASSERT(m_tPaintDetails.nBytesPerRow > 0); cRect.right -= (pBuf + m_tPaintDetails.nBytesPerRow - pSelectionBufPtrEnd)*m_tPaintDetails.nCharacterWidth; CRect cSelectionRect(cRect); cSelectionRect.InflateRect(0, -1, +1, 0); - cDC.FillRect(cSelectionRect, &CBrush(bHasFocus ? m_tSelectedFousBkgCol : m_tSelectedNoFocusBkgCol)); + CBrush cSelectionBrush(bHasFocus ? m_tSelectedFousBkgCol : m_tSelectedNoFocusBkgCol); + cDC.FillRect(cSelectionRect, &cSelectionBrush); *pSelectionBufPtrEnd = '\0'; // set "end-mark" cDC.SetTextColor(bHasFocus ? m_tSelectedFousTxtCol : m_tSelectedNoFocusTxtCol); cDC.SetBkColor(m_tHighlightBkgCol); @@ -1181,7 +1183,7 @@ void CHexEditBase::OnKillFocus(CWnd* pNewWnd) void CHexEditBase::OnSize(UINT nType, int cx, int cy) { CWnd::OnSize(nType, cx, cy); - CalculatePaintingDetails(CClientDC(this)); + { CClientDC cClientDC(this); CalculatePaintingDetails(cClientDC); } SetEditCaretPos(m_nCurrentAddress, m_bHighBits); SetScrollbarRanges(); } @@ -1350,7 +1352,7 @@ ASSERT(::IsWindow(m_hWnd)); return; } if(m_bRecalc) { - CalculatePaintingDetails(CClientDC(this)); + { CClientDC cClientDC(this); CalculatePaintingDetails(cClientDC); } } if(m_nCurrentAddress < m_nScrollPostionY*m_tPaintDetails.nBytesPerRow || (m_nCurrentAddress >= (m_nScrollPostionY + m_tPaintDetails.nVisibleLines)*m_tPaintDetails.nBytesPerRow) ) { diff --git a/CrypTool/HomophonicSubstitution.cpp b/CrypTool/HomophonicSubstitution.cpp index 97590172e..d0654cdd9 100644 --- a/CrypTool/HomophonicSubstitution.cpp +++ b/CrypTool/HomophonicSubstitution.cpp @@ -108,7 +108,7 @@ CString HomophonicSubstitution::exportKeyAsString() const { stringKeyEntryHomophones.Append(","); } } - stringKeyEntry.Format("%s/%s/%s/%s/%s/%s", stringKeyEntryCode, stringKeyEntryCharacter, stringKeyEntryCount, stringKeyEntryFrequency, stringKeyEntryCountHomophones, stringKeyEntryHomophones); + stringKeyEntry.Format("%s/%s/%s/%s/%s/%s", (LPCTSTR)stringKeyEntryCode, (LPCTSTR)stringKeyEntryCharacter, (LPCTSTR)stringKeyEntryCount, (LPCTSTR)stringKeyEntryFrequency, (LPCTSTR)stringKeyEntryCountHomophones, (LPCTSTR)stringKeyEntryHomophones); stringKey.Append(stringKeyEntry); if(indexKeyEntry + 1 < m_keyEntries.size()) { stringKey.Append(";"); diff --git a/CrypTool/OpenGLView.cpp b/CrypTool/OpenGLView.cpp index c25e37dc0..9d96b4918 100644 --- a/CrypTool/OpenGLView.cpp +++ b/CrypTool/OpenGLView.cpp @@ -201,8 +201,8 @@ int COpenGLView::Init() // Createan OpenGL rendering context m_hOpenGLContext = wglCreateContext(m_pDC->GetSafeHdc()); ASSERT(m_hOpenGLContext != NULL); - wglMakeCurrent(m_pDC->GetSafeHdc(), m_hOpenGLContext); - glEnable(GL_DEPTH_TEST); + if (wglMakeCurrent(m_pDC->GetSafeHdc(), m_hOpenGLContext)) + glEnable(GL_DEPTH_TEST); return 1; } diff --git a/CrypTool/POLY.CPP b/CrypTool/POLY.CPP index 1b1455881..c2ee5e3ee 100644 --- a/CrypTool/POLY.CPP +++ b/CrypTool/POLY.CPP @@ -719,10 +719,10 @@ ostream& operator<<(ostream& s,const Poly& p) s << " - "; } if (ptr->n==0) - cout << a; + cout << (Big)a; else { - if ((ZZn) a!=(ZZn)1) s << a << "*x"; + if ((ZZn) a!=(ZZn)1) s << (Big)a << "*x"; else s << "x"; if (ptr->n!=1) s << "^" << ptr->n; } diff --git a/CrypTool/POLYMOD.CPP b/CrypTool/POLYMOD.CPP index d61a839f6..2a16dd643 100644 --- a/CrypTool/POLYMOD.CPP +++ b/CrypTool/POLYMOD.CPP @@ -98,7 +98,7 @@ static int window(Big x,int i,int *nbs,int * nzs) PolyMod& PolyMod::operator*=(const PolyMod &b) { - int i,m,deg,dega,degb,degm=degree(modulus); + int i,m,deg,dega,degb,degm=degree(::modulus); big *G,*B,*R; BOOL squaring; term *ptr=p.start; @@ -164,7 +164,7 @@ PolyMod& PolyMod::operator*=(const PolyMod &b) if (!mr_poly_rem(deg,G,R)) { // reset the modulus - things have changed - setmod(modulus); + setmod(::modulus); mr_poly_rem(deg,G,R); } @@ -211,7 +211,7 @@ void reduce(const Poly& p,PolyMod& rem) big *G,*R; term *ptr,*pos=NULL; int n=degree(p); - int degm=degree(modulus); + int degm=degree(::modulus); if (n-degm < FFT_BREAK_EVEN) { rem=(PolyMod)p; @@ -230,7 +230,7 @@ void reduce(const Poly& p,PolyMod& rem) } if (!mr_poly_rem(n,G,R)) { // reset the modulus - things have changed - setmod(modulus); + setmod(::modulus); mr_poly_rem(n,G,R); } @@ -256,7 +256,7 @@ void setmod(const Poly& p) Poly h; term *ptr; big *f,*rf; - modulus=p; + ::modulus=p; n=degree(p); if (n @@ -276,7 +276,7 @@ LPSTR CScintillaWnd::GetText () if (pReturn != NULL) { *pReturn = '\0'; - SendMessage(SCI_GETTEXT, lLen, (long)pReturn); + SendMessage(SCI_GETTEXT, lLen, (LPARAM)pReturn); return pReturn; } } @@ -366,7 +366,7 @@ void CScintillaWnd::Serialize(CArchive& ar) do { tr.chrg.cpMin = i; tr.chrg.cpMax = i + nbuffer < len ? i + nbuffer : len; - n = SendMessage(SCI_GETTEXTRANGE, 0,(long)&tr); + n = SendMessage(SCI_GETTEXTRANGE, 0,(LPARAM)&tr); ar.Write(buffer,n); i += n; } while (i < len); @@ -402,7 +402,7 @@ BOOL CScintillaWnd::SaveFile ( tr.chrg.cpMax = -1; tr.lpstrText = pBuffer; //SendMessage(SCI_GETTEXT, buflen,(long)pBuffer); - SendMessage(SCI_GETTEXTRANGE, 0,(long)&tr); + SendMessage(SCI_GETTEXTRANGE, 0,(LPARAM)&tr); file.write(pBuffer, buflen-1); delete [] pBuffer; } @@ -462,7 +462,7 @@ int CScintillaWnd::GetLinenumberWidth () // get number of chars needed to display highest linenumber int nChars = GetLinenumberChars ()+1; // get width of character '9' in pixels - LRESULT lWidth = SendMessage(SCI_TEXTWIDTH, STYLE_LINENUMBER, (long)_T("9")); + LRESULT lWidth = SendMessage(SCI_TEXTWIDTH, STYLE_LINENUMBER, (LPARAM)_T("9")); return nChars * lWidth; } ///////////////////////////////////// @@ -685,7 +685,7 @@ int CScintillaWnd::GetTextLength() // void CScintillaWnd::SetFontname(int nStyle, LPCSTR szFontname) { - SendMessage(SCI_STYLESETFONT, nStyle, (long)szFontname); + SendMessage(SCI_STYLESETFONT, nStyle, (LPARAM)szFontname); } ///////////////////////////////////// // @mfunc Set the font height in points @@ -980,13 +980,13 @@ BOOL CScintillaWnd::SearchForward( // added new search behaviour to be compliant to common search tools (tf.chrg.cpMin = lPos+1;) tf.chrg.cpMin = lPos; tf.chrg.cpMax = SendMessage(SCI_GETLENGTH, 0, 0); - lPos = SendMessage(SCI_FINDTEXT, m_nSearchflags, (long)&tf); + lPos = SendMessage(SCI_FINDTEXT, m_nSearchflags, (LPARAM)&tf); if (lPos >= 0) { SetFocus(); GotoPosition(lPos); SendMessage(SCI_SETSEL, tf.chrgText.cpMin, tf.chrgText.cpMax); - SendMessage(SCI_FINDTEXT, m_nSearchflags, (long)&tf); + SendMessage(SCI_FINDTEXT, m_nSearchflags, (LPARAM)&tf); return TRUE; } return FALSE; @@ -1009,13 +1009,13 @@ BOOL CScintillaWnd::SearchBackward( else tf.chrg.cpMin = lPos-1; tf.chrg.cpMax = 0; - lPos = SendMessage(SCI_FINDTEXT, m_nSearchflags, (long)&tf); + lPos = SendMessage(SCI_FINDTEXT, m_nSearchflags, (LPARAM)&tf); if (lPos >= 0) { SetFocus(); GotoPosition(lPos); SendMessage(SCI_SETSEL, tf.chrgText.cpMin, tf.chrgText.cpMax); - SendMessage(SCI_FINDTEXT, m_nSearchflags, (long)&tf); + SendMessage(SCI_FINDTEXT, m_nSearchflags, (LPARAM)&tf); return TRUE; } return FALSE; @@ -1031,9 +1031,9 @@ void CScintillaWnd::ReplaceSearchedText( return; SendMessage(SCI_TARGETFROMSELECTION, 0, 0); if (m_nSearchflags & SCFIND_REGEXP) - SendMessage(SCI_REPLACETARGETRE, strlen(szText), (long)szText); + SendMessage(SCI_REPLACETARGETRE, strlen(szText), (LPARAM)szText); else - SendMessage(SCI_REPLACETARGET, strlen(szText), (long)szText); + SendMessage(SCI_REPLACETARGET, strlen(szText), (LPARAM)szText); } ///////////////////////////////////// // @mfunc Set lexer format - see scintilla.h @@ -1073,7 +1073,7 @@ CString CScintillaWnd::GetSelectedText() if (p != NULL) { *p = '\0'; - SendMessage(SCI_GETSELTEXT, 0, (long)p); + SendMessage(SCI_GETSELTEXT, 0, (LPARAM)p); CString strReturn = p; delete [] p; return strReturn; @@ -1102,21 +1102,21 @@ int CScintillaWnd::ReplaceAll( SendMessage(SCI_SETTARGETSTART, lStart); SendMessage(SCI_SETTARGETEND, lEnd); // try to find text in target for the first time - long lPos = SendMessage(SCI_SEARCHINTARGET, strlen(szFind), (long)szFind); + long lPos = SendMessage(SCI_SEARCHINTARGET, strlen(szFind), (LPARAM)szFind); // loop over selection until end of selection reached - moving the target start each time while (lPos < lEnd && lPos >= 0) { if (m_nSearchflags & SCFIND_REGEXP) // check for regular expression flag - lLen = SendMessage(SCI_REPLACETARGETRE, strlen(szReplace), (long)szReplace); + lLen = SendMessage(SCI_REPLACETARGETRE, strlen(szReplace), (LPARAM)szReplace); else - lLen = SendMessage(SCI_REPLACETARGET, strlen(szReplace), (long)szReplace); + lLen = SendMessage(SCI_REPLACETARGET, strlen(szReplace), (LPARAM)szReplace); // the end of the selection was changed - recalc the end lEnd = GetSelectionEnd(); // move start of target behind last change and end of target to new end of selection SendMessage(SCI_SETTARGETSTART, lPos+lLen); SendMessage(SCI_SETTARGETEND, lEnd); // find again - if nothing found loop exits - lPos = SendMessage(SCI_SEARCHINTARGET, strlen(szFind), (long)szFind); + lPos = SendMessage(SCI_SEARCHINTARGET, strlen(szFind), (LPARAM)szFind); nCount++; } } @@ -1130,21 +1130,21 @@ int CScintillaWnd::ReplaceAll( SendMessage(SCI_SETTARGETSTART, lStart, 0); SendMessage(SCI_SETTARGETEND, lEnd, 0); // try to find text in target for the first time - long lPos = SendMessage(SCI_SEARCHINTARGET, strlen(szFind), (long)szFind); + long lPos = SendMessage(SCI_SEARCHINTARGET, strlen(szFind), (LPARAM)szFind); // loop over selection until end of selection reached - moving the target start each time while (lPos < lEnd && lPos >= 0) { if (m_nSearchflags & SCFIND_REGEXP) // check for regular expression flag - lLen = SendMessage(SCI_REPLACETARGETRE, strlen(szReplace), (long)szReplace); + lLen = SendMessage(SCI_REPLACETARGETRE, strlen(szReplace), (LPARAM)szReplace); else - lLen = SendMessage(SCI_REPLACETARGET, strlen(szReplace), (long)szReplace); + lLen = SendMessage(SCI_REPLACETARGET, strlen(szReplace), (LPARAM)szReplace); // the end of the selection was changed - recalc the end lEnd = SendMessage(SCI_GETTEXTLENGTH, 0, 0);; // move start of target behind last change and end of target to new end of buffer SendMessage(SCI_SETTARGETSTART, lPos+lLen); SendMessage(SCI_SETTARGETEND, lEnd); // find again - if nothing found loop exits - lPos = SendMessage(SCI_SEARCHINTARGET, strlen(szFind), (long)szFind); + lPos = SendMessage(SCI_SEARCHINTARGET, strlen(szFind), (LPARAM)szFind); nCount++; } } diff --git a/CrypTool/ScintillaWnd.h b/CrypTool/ScintillaWnd.h index 4d54533e7..6f0264dae 100644 --- a/CrypTool/ScintillaWnd.h +++ b/CrypTool/ScintillaWnd.h @@ -29,9 +29,9 @@ #ifndef __SCINTILLAWND_H__ #define __SCINTILLAWND_H__ -#include "platform.h" -#include "scintilla.h" -#include "scilexer.h" +#include "Platform.h" +#include "Scintilla.h" +#include "SciLexer.h" //////////////////////////////////// // @class CScintillaWnd | Class of a GCL Scintilla syntax coloring edit control for MFC diff --git a/CrypTool/SecudeTools.cpp b/CrypTool/SecudeTools.cpp index 000c48169..10a82fdf6 100644 --- a/CrypTool/SecudeTools.cpp +++ b/CrypTool/SecudeTools.cpp @@ -82,7 +82,7 @@ ObjId * CrypToolPSEName_OID() } -static char * basename(char *psename) +static char * pse_basename(char *psename) { char *pos; @@ -169,7 +169,7 @@ OctetString *PKCS12_encode(PSE pse, OctetString * PIN, int iterationcount, int e P12safe.bags = NULL; P12bagp = &P12safe.bags; - name = basename(pse->pse_name); + name = pse_basename(pse->pse_name); // cryptool only supports one key pair per PSE. if this changes in the future, // this code must be extended @@ -255,7 +255,7 @@ OctetString *PKCS12_encode(PSE pse, OctetString * PIN, int iterationcount, int e int PKCS12_import(PSE pse, OctetString *input, OctetString *password, int newpse) { P12_Safe P12safe; - char verified; + Boolean verified; P12_Bag *thisbag; int rc, keys, certs, ii, ok, bags; Certificate *thiscert; diff --git a/CrypTool/SignatureAttack.cpp b/CrypTool/SignatureAttack.cpp index d83da1b2a..55f6b66d3 100644 --- a/CrypTool/SignatureAttack.cpp +++ b/CrypTool/SignatureAttack.cpp @@ -339,7 +339,7 @@ UINT SignatureAttack::Do_Floyd() } HashStore[jj] = 0; - fprintf(m_TestFile, "\nRun_No=%2.2d\nHashInit=%s\nCollSteps=%I64i\nConfSteps=%I64i", + fprintf(m_TestFile, "\nRun_No=%2.2d\nHashInit=%s\nCollSteps=%lld\nConfSteps=%lld", m_ResSigAtt->GetRuns(), HashStore, m_ResSigAtt->GetCollisionStepsOfRun(m_ResSigAtt->GetRuns() - 1), m_ResSigAtt->GetConfirmationStepsOfRun(m_ResSigAtt->GetRuns() - 1)); @@ -372,7 +372,7 @@ UINT SignatureAttack::Do_Floyd() if (m_OptSigAtt->GetTestMode()) { - fprintf(m_TestFile, "\nResult=%d\nTotalSteps=%I64i\nExpectedSteps=%I64i\nHashOps=%I64i\nElapsedTime=%.3f", + fprintf(m_TestFile, "\nResult=%d\nTotalSteps=%lld\nExpectedSteps=%lld\nHashOps=%lld\nElapsedTime=%.3f", m_ResSigAtt->GetFloydResult(), m_ResSigAtt->GetTotalSteps(), m_ResSigAtt->GetExpectedSteps(), m_ResSigAtt->GetHashOperationsPerformed(), m_ResSigAtt->GetEffectiveTime()); } diff --git a/CrypTool/Solitaire.cpp b/CrypTool/Solitaire.cpp index 727f1105d..d2c913840 100644 --- a/CrypTool/Solitaire.cpp +++ b/CrypTool/Solitaire.cpp @@ -419,7 +419,7 @@ long c_solitaire::crypt( solitaire_action encrypt, const char *f_in, const char { long error = 0; fstream fin, fout; - fin.open( f_in, ios::in || ios::binary ); + fin.open( f_in, ios::in ); // was "ios::in || ios::binary", which evaluates to ios::in if ( fin.is_open() ) { fout.open( f_out, ios::out ); diff --git a/CrypTool/Solitaire.h b/CrypTool/Solitaire.h index f3e1abd6e..05c89c2d0 100644 --- a/CrypTool/Solitaire.h +++ b/CrypTool/Solitaire.h @@ -64,6 +64,7 @@ class c_solitaire { }; long crypt_solitaire( solitaire_action encrypt, const char *f_in, const char *f_out, long cards, long ID, unsigned char *cardset, char *password = 0 ); +long crypt_solitaire( solitaire_action encrypt, const char *f_in, const char *f_out, CString &cardset ); #if 1 class Deck diff --git a/CrypTool/SymEncBase.cpp b/CrypTool/SymEncBase.cpp index aecff142e..45c0a4280 100644 --- a/CrypTool/SymEncBase.cpp +++ b/CrypTool/SymEncBase.cpp @@ -25,7 +25,6 @@ #include "secure.h" // Include-File von SECUDE -inline int sym_encrypt(int crypt_id, cryptProvider provider, char *key_hex, int key_bitlength, char *in, int in_bytelength, @@ -44,7 +43,6 @@ int sym_encrypt(int crypt_id, cryptProvider provider, return -1; } -inline int sym_decrypt(int crypt_id, cryptProvider provider, char *key_hex, int key_bitlength, char *in, int in_bytelength, diff --git a/CrypTool/ZZXY.cpp b/CrypTool/ZZXY.cpp index 49170d99e..96ed0f22d 100644 --- a/CrypTool/ZZXY.cpp +++ b/CrypTool/ZZXY.cpp @@ -116,7 +116,7 @@ ZZXY ZZXY::operator *(ZZX &B) return retval; } -ZZXY ZZXY::operator *(ZZ &B) +ZZXY ZZXY::operator *(const ZZ &B) { ZZXY retval; retval.data.SetLength(1); diff --git a/CrypTool/ZZXY.h b/CrypTool/ZZXY.h index 6aae5994d..ec76e1e2e 100644 --- a/CrypTool/ZZXY.h +++ b/CrypTool/ZZXY.h @@ -48,7 +48,7 @@ class ZZXY{ const ZZXY operator-(ZZXY& B); ZZXY operator*(ZZX& B); const ZZXY operator*(ZZXY& B); - ZZXY operator *(ZZ &B); + ZZXY operator *(const ZZ &B); const ZZXY operator+(ZZXY& B); const ZZXY operator /(ZZ d); const ZZXY operator /(ZZX b); diff --git a/libVolRen/Volume.h b/libVolRen/Volume.h index dc217ce50..c1c1a5dd0 100644 --- a/libVolRen/Volume.h +++ b/libVolRen/Volume.h @@ -26,6 +26,15 @@ #define VOLUME_MODE_STANDART_OPENGL #define VOLUME_MODE_STANDARD_OPENGL +#ifndef _WIN32 +// modes 2-4 need NVIDIA/WGL extensions loaded through glh_extensions: Windows only +#undef VOLUME_MODE_TEXTURE_PALETTE +#undef VOLUME_MODE_DEPENDENT_TEXTURE +#undef VOLUME_MODE_FRAGMENT_SHADER +#ifndef VOLUME_MODE_STANDARD_OPENGL +#define VOLUME_MODE_STANDARD_OPENGL +#endif +#endif #ifdef VOLUME_MODE_STANDARD_OPENGL #define VOLUME_MODE_SET diff --git a/port/app/CMakeLists.txt b/port/app/CMakeLists.txt index ecf4aff66..fcf9fe500 100644 --- a/port/app/CMakeLists.txt +++ b/port/app/CMakeLists.txt @@ -4,8 +4,8 @@ set(CT_SRC ${REPO_ROOT}/CrypTool) # *.CPP: the MIRACL wrapper copies (MONTY.CPP, ...); globbing is case-sensitive on Linux. file(GLOB CT_SOURCES CONFIGURE_DEPENDS ${CT_SRC}/*.cpp ${CT_SRC}/*.CPP ${CT_SRC}/zip/*.c) list(REMOVE_DUPLICATES CT_SOURCES) -list(FILTER CT_SOURCES EXCLUDE REGEX "/(Ber_Ell_Kurve|DlgCrypToolPRNGSeedCollector|DlgFindText|DlgHomophonicSubstitution|DlgMac|DlgSelHash|HomophonicSubstitution)\\.cpp$") -# zip/main.c is the standalone test driver of the zip code (not part of CrypTool.vcxproj). +list(FILTER CT_SOURCES EXCLUDE REGEX "/(Ber_Ell_Kurve|DlgCrypToolPRNGSeedCollector|DlgFindText|DlgMac|DlgSelHash)\\.cpp$") +# zip/main.c is the standalone test driver of the zip code (not part of CrypTool.vcproj). list(FILTER CT_SOURCES EXCLUDE REGEX "/zip/main\\.c$") # OLE-based picture control on Windows, wxWidgets-based elsewhere. if(WIN32) diff --git a/port/mfcwx/include/mfcwx/crtcompat.h b/port/mfcwx/include/mfcwx/crtcompat.h index c6c970e43..17fe75b03 100644 --- a/port/mfcwx/include/mfcwx/crtcompat.h +++ b/port/mfcwx/include/mfcwx/crtcompat.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include From 71440e78849024c5d2601c18193af1d6f87f2986 Mon Sep 17 00:00:00 2001 From: Kamil Marczak Date: Tue, 6 Oct 2026 23:43:54 +0200 Subject: [PATCH 30/46] feat(mfcwx): add COleDataObject clipboard access and CPrintDialog accessors Also includes the system GL header before wx/glcanvas.h, which GTK builds need. --- port/mfcwx/src/gl.cpp | 5 +++++ port/mfcwx/src/ole.cpp | 44 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 port/mfcwx/src/ole.cpp diff --git a/port/mfcwx/src/gl.cpp b/port/mfcwx/src/gl.cpp index 6bc70e07b..b3830ee93 100644 --- a/port/mfcwx/src/gl.cpp +++ b/port/mfcwx/src/gl.cpp @@ -1,5 +1,10 @@ #include "windows_impl.h" +#ifdef __APPLE__ +#include +#else +#include +#endif #include #include diff --git a/port/mfcwx/src/ole.cpp b/port/mfcwx/src/ole.cpp new file mode 100644 index 000000000..7c65f5012 --- /dev/null +++ b/port/mfcwx/src/ole.cpp @@ -0,0 +1,44 @@ +#include "afxole.h" +#include "afxdlgs.h" + +#include "globalmem.h" + +BOOL COleDataObject::AttachClipboard() { return TRUE; } + +BOOL COleDataObject::IsDataAvailable(CLIPFORMAT cfFormat, void*) const { + return ::IsClipboardFormatAvailable(cfFormat); +} + +HGLOBAL COleDataObject::GetGlobalData(CLIPFORMAT cfFormat, void*) const { + if (!::OpenClipboard(nullptr)) + return nullptr; + HGLOBAL copy = nullptr; + if (HANDLE h = ::GetClipboardData(cfFormat)) + copy = mfcwx::GlobalFromData(mfcwx::GlobalData(h), mfcwx::GlobalDataSize(h)); + ::CloseClipboard(); + return copy; +} + +void COleDataSource::CacheGlobalData(CLIPFORMAT cfFormat, HGLOBAL hGlobal, void*) { + if (m_data && m_data != hGlobal) + ::GlobalFree(m_data); + m_data = hGlobal; + m_format = cfFormat; +} + +DROPEFFECT COleDataSource::DoDragDrop(DWORD, LPCRECT, void*) { return DROPEFFECT_NONE; } + +void COleDataSource::SetClipboard() { + if (!m_data || !::OpenClipboard(nullptr)) + return; + ::EmptyClipboard(); + if (::SetClipboardData(m_format, m_data)) + m_data = nullptr; + ::CloseClipboard(); +} + +BOOL CPrintDialog::PrintSelection() const { return (m_pd.Flags & PD_SELECTION) != 0; } +BOOL CPrintDialog::PrintRange() const { return (m_pd.Flags & PD_PAGENUMS) != 0; } +BOOL CPrintDialog::PrintAll() const { return !PrintRange() && !PrintSelection(); } +int CPrintDialog::GetFromPage() const { return PrintRange() ? m_pd.nFromPage : -1; } +int CPrintDialog::GetToPage() const { return PrintRange() ? m_pd.nToPage : -1; } From 38d9552cc18e46240db949b2521c84d0c4333ffd Mon Sep 17 00:00:00 2001 From: Kamil Marczak Date: Tue, 6 Oct 2026 23:45:42 +0200 Subject: [PATCH 31/46] fix(mfcwx): provide APPDATA and USERPROFILE for the user key store CrypTool copies its key store below %APPDATA%\CrypTool and showed an error at startup when the variable was missing. --- port/mfcwx/src/app.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/port/mfcwx/src/app.cpp b/port/mfcwx/src/app.cpp index 507803af2..2200f1dbd 100644 --- a/port/mfcwx/src/app.cpp +++ b/port/mfcwx/src/app.cpp @@ -5,6 +5,7 @@ #include "runtime.h" #include +#include #include #include #include @@ -78,6 +79,20 @@ void PrepareEnvironment(int argc, wxChar** argv) { tempApp.pop_back(); wxSetEnv("TEMP", ToWx(tempApp.c_str())); wxSetEnv("TMP", ToWx(tempApp.c_str())); + if (!wxGetEnv("APPDATA", nullptr)) { + wxString home = wxGetHomeDir(); +#ifdef __APPLE__ + wxString appData = home + "/Library/Application Support"; +#else + wxString appData; + if (!wxGetEnv("XDG_DATA_HOME", &appData) || !appData.StartsWith("/")) + appData = home + "/.local/share"; +#endif + wxFileName::Mkdir(appData, 0700, wxPATH_MKDIR_FULL); + wxSetEnv("APPDATA", ToWx(AppPath(FromWx(appData).c_str()).c_str())); + if (!wxGetEnv("USERPROFILE", nullptr)) + wxSetEnv("USERPROFILE", ToWx(AppPath(FromWx(home).c_str()).c_str())); + } // The Windows code locates its data files next to the program it finds in the command line. std::string program = AppPath((GetDataDirectory() + "/CrypTool").c_str()); std::string commandLine = "\"" + program + "\""; From 332600d5a1752db5a8976752d9be2e3fc7e14e0f Mon Sep 17 00:00:00 2001 From: Kamil Marczak Date: Tue, 6 Oct 2026 23:48:31 +0200 Subject: [PATCH 32/46] feat(mfcwx): add environment-driven window snapshots for UI checks MFCWX_SNAPSHOT_DIR saves PNGs of the shown top-level windows on a timer and can dismiss modal dialogs between rounds. The dialog gallery now uses the same helper, and mfcwx_add_rc keeps its settings in the cache so it works from any directory. --- port/mfcwx/CMakeLists.txt | 6 +- port/mfcwx/src/app.cpp | 1 + port/mfcwx/src/internal.h | 2 + port/mfcwx/src/snapshot.cpp | 94 +++++++++++++++++++ .../gallery => mfcwx/src}/snapshot_mac.mm | 19 ++-- port/rc/rc2cpp.cmake | 11 ++- port/tools/gallery/CMakeLists.txt | 6 +- port/tools/gallery/gallery.cpp | 7 +- port/tools/gallery/snapshot_gtk.cpp | 21 ----- 9 files changed, 127 insertions(+), 40 deletions(-) create mode 100644 port/mfcwx/src/snapshot.cpp rename port/{tools/gallery => mfcwx/src}/snapshot_mac.mm (71%) delete mode 100644 port/tools/gallery/snapshot_gtk.cpp diff --git a/port/mfcwx/CMakeLists.txt b/port/mfcwx/CMakeLists.txt index 7b2849fb2..671ace4cb 100644 --- a/port/mfcwx/CMakeLists.txt +++ b/port/mfcwx/CMakeLists.txt @@ -1,4 +1,8 @@ file(GLOB MFCWX_SOURCES CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp) +if(APPLE) + enable_language(OBJCXX) + list(APPEND MFCWX_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/snapshot_mac.mm) +endif() add_library(mfcwx STATIC ${MFCWX_SOURCES}) target_include_directories(mfcwx PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include) @@ -9,7 +13,7 @@ target_compile_definitions(mfcwx PUBLIC ${wxWidgets_DEFINITIONS} MFCWX=1) target_compile_options(mfcwx PUBLIC ${wxWidgets_CXX_FLAGS}) target_link_libraries(mfcwx PUBLIC ${wxWidgets_LIBRARIES} OpenGL::GL Threads::Threads ${CMAKE_DL_LIBS}) if(APPLE) - target_link_libraries(mfcwx PUBLIC "-framework OpenGL") + target_link_libraries(mfcwx PUBLIC "-framework OpenGL" "-framework Cocoa") endif() option(MFCWX_TESTS "Build the mfcwx runtime tests" OFF) diff --git a/port/mfcwx/src/app.cpp b/port/mfcwx/src/app.cpp index 2200f1dbd..97512dbc9 100644 --- a/port/mfcwx/src/app.cpp +++ b/port/mfcwx/src/app.cpp @@ -123,6 +123,7 @@ bool MfcWxApp::OnInit() { app->m_lpCmdLine = &cmdLine[0]; app->m_nCmdShow = SW_SHOWNORMAL; Bind(wxEVT_IDLE, &MfcWxApp::OnIdle, this); + StartSnapshotTimerFromEnvironment(); if (!app->InitApplication() || !app->InitInstance()) { app->ExitInstance(); return false; diff --git a/port/mfcwx/src/internal.h b/port/mfcwx/src/internal.h index a76d72bac..44fbbf84d 100644 --- a/port/mfcwx/src/internal.h +++ b/port/mfcwx/src/internal.h @@ -225,4 +225,6 @@ wxWindow* MainWxWindow(); // Shows a message box; nType is the MB_* combination. Returns IDOK, IDCANCEL, ... int ShowMessageBox(wxWindow* parent, const char* text, const char* caption, UINT nType); +void StartSnapshotTimerFromEnvironment(); + } // namespace mfcwx diff --git a/port/mfcwx/src/snapshot.cpp b/port/mfcwx/src/snapshot.cpp new file mode 100644 index 000000000..886568845 --- /dev/null +++ b/port/mfcwx/src/snapshot.cpp @@ -0,0 +1,94 @@ +#include "windows_impl.h" + +#include +#include +#include +#include +#include + +#include + +namespace mfcwx { + +bool SnapshotWxWindow(wxWindow* w, const std::string& pngPath); + +#ifndef __APPLE__ +bool SnapshotWxWindow(wxWindow* w, const std::string& pngPath) { + if (!w) + return false; + wxClientDC src(w); + wxSize size = w->GetClientSize(); + if (size.x <= 0 || size.y <= 0) + return false; + wxBitmap bmp(size); + wxMemoryDC dst(bmp); + dst.Blit(0, 0, size.x, size.y, &src, 0, 0); + dst.SelectObject(wxNullBitmap); + return bmp.SaveFile(wxString::FromUTF8(pngPath.c_str()), wxBITMAP_TYPE_PNG); +} +#endif + +namespace { + +// Development aid: MFCWX_SNAPSHOT_DIR=dir saves PNGs of all shown top-level windows every +// MFCWX_SNAPSHOT_INTERVAL ms (default 1500) for MFCWX_SNAPSHOT_COUNT rounds (default 1). +// MFCWX_SNAPSHOT_DISMISS=1 cancels the active modal dialog after each round; the process exits +// after the last round. +class SnapshotTimer : public wxTimer { +public: + SnapshotTimer(std::string dir, int rounds, bool dismiss) : m_dir(std::move(dir)), m_rounds(rounds), m_dismiss(dismiss) {} + + void Notify() override { + ++m_round; + int index = 0; + for (wxWindow* w : wxTopLevelWindows) { + if (!w->IsShown()) + continue; + wxString title = w->GetLabel(); + std::string name; + for (wxUniChar c : title) + name += (c.IsAscii() && isalnum(static_cast(c.GetValue()))) ? static_cast(c.GetValue()) : '_'; + char path[1024]; + snprintf(path, sizeof path, "%s/%02d_%d_%s.png", m_dir.c_str(), m_round, index++, name.substr(0, 40).c_str()); + bool ok = SnapshotWxWindow(w, path); + fprintf(stderr, "mfcwx snapshot %s %s (%dx%d)\n", ok ? "saved" : "FAILED", path, w->GetSize().x, w->GetSize().y); + } + if (m_round >= m_rounds) { + fflush(stderr); + _exit(0); + } + if (m_dismiss) { + for (wxWindow* w : wxTopLevelWindows) { + auto* d = wxDynamicCast(w, wxDialog); + if (d && d->IsModal()) { + if (CWnd* wnd = WrapperFor(d)) + wnd->PostMessage(WM_COMMAND, IDCANCEL, 0); + else + d->EndModal(wxID_CANCEL); + break; + } + } + } + } + +private: + std::string m_dir; + int m_rounds; + bool m_dismiss; + int m_round = 0; +}; + +} // namespace + +void StartSnapshotTimerFromEnvironment() { + const char* dir = getenv("MFCWX_SNAPSHOT_DIR"); + if (!dir || !*dir) + return; + const char* interval = getenv("MFCWX_SNAPSHOT_INTERVAL"); + const char* count = getenv("MFCWX_SNAPSHOT_COUNT"); + const char* dismiss = getenv("MFCWX_SNAPSHOT_DISMISS"); + static SnapshotTimer timer(dir, count ? std::max(1, atoi(count)) : 1, dismiss && *dismiss == '1'); + timer.Start(interval ? std::max(100, atoi(interval)) : 1500); +} + +} // namespace mfcwx diff --git a/port/tools/gallery/snapshot_mac.mm b/port/mfcwx/src/snapshot_mac.mm similarity index 71% rename from port/tools/gallery/snapshot_mac.mm rename to port/mfcwx/src/snapshot_mac.mm index 5ca865e45..1fb405171 100644 --- a/port/tools/gallery/snapshot_mac.mm +++ b/port/mfcwx/src/snapshot_mac.mm @@ -1,17 +1,20 @@ -#include - #import -#include "afxwin.h" +#include + +#include + +namespace mfcwx { -bool SnapshotWindow(HWND hWnd, const std::string& pngPath) { - CWnd* wnd = CWnd::FromHandle(hWnd); - if (!wnd) +bool SnapshotWxWindow(wxWindow* w, const std::string& pngPath) { + if (!w) return false; - NSView* view = (NSView*)wnd->GetWx()->GetHandle(); + NSView* view = (NSView*)w->GetHandle(); NSView* content = [[view window] contentView]; if (!content) content = view; + if (!content) + return false; [content displayIfNeeded]; NSBitmapImageRep* rep = [content bitmapImageRepForCachingDisplayInRect:content.bounds]; if (!rep) @@ -20,3 +23,5 @@ bool SnapshotWindow(HWND hWnd, const std::string& pngPath) { NSData* png = [rep representationUsingType:NSBitmapImageFileTypePNG properties:@{}]; return [png writeToFile:[NSString stringWithUTF8String:pngPath.c_str()] atomically:YES]; } + +} // namespace mfcwx diff --git a/port/rc/rc2cpp.cmake b/port/rc/rc2cpp.cmake index 3ce2487da..2177cd9b7 100644 --- a/port/rc/rc2cpp.cmake +++ b/port/rc/rc2cpp.cmake @@ -2,8 +2,11 @@ include_guard(GLOBAL) find_package(Python3 COMPONENTS Interpreter REQUIRED) -set(MFCWX_RC2CPP_SCRIPT "${CMAKE_CURRENT_LIST_DIR}/rc2cpp.py") -get_filename_component(MFCWX_RC_INCLUDE_DIR "${CMAKE_CURRENT_LIST_DIR}/../mfcwx/include" ABSOLUTE) +# Cached so the function also works in directories other than the one that included this file first. +set(MFCWX_RC_PYTHON "${Python3_EXECUTABLE}" CACHE INTERNAL "") +set(MFCWX_RC2CPP_SCRIPT "${CMAKE_CURRENT_LIST_DIR}/rc2cpp.py" CACHE INTERNAL "") +get_filename_component(rc_include_dir "${CMAKE_CURRENT_LIST_DIR}/../mfcwx/include" ABSOLUTE) +set(MFCWX_RC_INCLUDE_DIR "${rc_include_dir}" CACHE INTERNAL "") # mfcwx_add_rc( RC [MODULE ] [HEADERS ...] [OUTPUT_DIR

] [INCLUDE_DIRS ...] [DEFINES ...] [CODEPAGE ]) function(mfcwx_add_rc target) @@ -48,7 +51,7 @@ function(mfcwx_add_rc target) # Output names depend on the language set; the build rewrites outputs_file when it changes, which re-runs CMake. set(outputs_file "${out_dir}/${module}_outputs.txt") execute_process( - COMMAND "${Python3_EXECUTABLE}" "${MFCWX_RC2CPP_SCRIPT}" ${args} --list-outputs --outputs-file "${outputs_file}" + COMMAND "${MFCWX_RC_PYTHON}" "${MFCWX_RC2CPP_SCRIPT}" ${args} --list-outputs --outputs-file "${outputs_file}" OUTPUT_VARIABLE listed ERROR_VARIABLE errors RESULT_VARIABLE result) @@ -71,7 +74,7 @@ function(mfcwx_add_rc target) add_custom_command( OUTPUT ${sources} "${files_list}" - COMMAND "${Python3_EXECUTABLE}" "${MFCWX_RC2CPP_SCRIPT}" ${args} --quiet --outputs-file "${outputs_file}" ${depfile_cli} + COMMAND "${MFCWX_RC_PYTHON}" "${MFCWX_RC2CPP_SCRIPT}" ${args} --quiet --outputs-file "${outputs_file}" ${depfile_cli} DEPENDS "${rc}" ${headers} "${MFCWX_RC2CPP_SCRIPT}" "${MFCWX_RC_INCLUDE_DIR}/mfcwx/winconst.h" "${MFCWX_RC_INCLUDE_DIR}/afxres.h" diff --git a/port/tools/gallery/CMakeLists.txt b/port/tools/gallery/CMakeLists.txt index 2fb0b7ef5..811cf73cc 100644 --- a/port/tools/gallery/CMakeLists.txt +++ b/port/tools/gallery/CMakeLists.txt @@ -1,11 +1,7 @@ include(${REPO_ROOT}/port/rc/rc2cpp.cmake) -add_executable(dialog_gallery gallery.cpp $,snapshot_mac.mm,snapshot_gtk.cpp>) +add_executable(dialog_gallery gallery.cpp) mfcwx_add_rc(dialog_gallery RC ${REPO_ROOT}/CrypTool/CrypTool.rc MODULE CrypTool HEADERS ${REPO_ROOT}/CrypTool/resource.h OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/rc) target_link_libraries(dialog_gallery PRIVATE mfcwx) target_compile_definitions(dialog_gallery PRIVATE CRYPTOOL_DEV_DATA_DIR="${CMAKE_BINARY_DIR}/data") -if(APPLE) - enable_language(OBJCXX) - target_link_libraries(dialog_gallery PRIVATE "-framework Cocoa") -endif() diff --git a/port/tools/gallery/gallery.cpp b/port/tools/gallery/gallery.cpp index d8847f0c3..e6dac9c2f 100644 --- a/port/tools/gallery/gallery.cpp +++ b/port/tools/gallery/gallery.cpp @@ -10,7 +10,10 @@ #include extern const mfcwx::rc::Module g_rcModule_CrypTool; -bool SnapshotWindow(HWND hWnd, const std::string& pngPath); +class wxWindow; +namespace mfcwx { +bool SnapshotWxWindow(wxWindow* w, const std::string& pngPath); +} namespace { @@ -77,7 +80,7 @@ BOOL GalleryApp::InitInstance() { PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE); char path[1024]; snprintf(path, sizeof path, "%s/%s_%05d_%s.png", out.c_str(), lang.c_str(), d.id, d.name ? d.name : ""); - if (SnapshotWindow(dlg.m_hWnd, path)) + if (mfcwx::SnapshotWxWindow(dlg.GetWx(), path)) ++saved; dlg.DestroyWindow(); } diff --git a/port/tools/gallery/snapshot_gtk.cpp b/port/tools/gallery/snapshot_gtk.cpp deleted file mode 100644 index b9657868c..000000000 --- a/port/tools/gallery/snapshot_gtk.cpp +++ /dev/null @@ -1,21 +0,0 @@ -#include - -#include -#include -#include - -#include "afxwin.h" - -bool SnapshotWindow(HWND hWnd, const std::string& pngPath) { - CWnd* wnd = CWnd::FromHandle(hWnd); - if (!wnd) - return false; - wxWindow* w = wnd->GetWx(); - wxClientDC src(w); - wxSize size = w->GetClientSize(); - wxBitmap bmp(size); - wxMemoryDC dst(bmp); - dst.Blit(0, 0, size.x, size.y, &src, 0, 0); - dst.SelectObject(wxNullBitmap); - return bmp.SaveFile(wxString::FromUTF8(pngPath.c_str()), wxBITMAP_TYPE_PNG); -} From e5f1119e7272e3ebdc9dbcc29161c4cafb43d5fa Mon Sep 17 00:00:00 2001 From: Kamil Marczak Date: Tue, 6 Oct 2026 23:50:12 +0200 Subject: [PATCH 33/46] fix(app): show the version in the title and size the main window from the screen rc2cpp gains --replace (REPLACE in mfcwx_add_rc) to do what the Windows pre-build script does for [CRYPTOOL_VERSION_FULL]; the project version is 1.4.43 as in that script. GetDesktopWindow now returns a pseudo window whose rect is the display, which CrypTool uses to size its main window. --- CMakeLists.txt | 2 +- port/app/CMakeLists.txt | 31 ++++++++++++++++++++++++++++++- port/mfcwx/src/winapi.cpp | 16 +++++++++++++++- port/rc/rc2cpp.cmake | 8 ++++++-- port/rc/rc2cpp.py | 11 +++++++++++ port/tools/gallery/CMakeLists.txt | 1 + 6 files changed, 64 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index bee6c0c91..3810ed603 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.20) -project(CrypTool1 VERSION 1.4.31 LANGUAGES C CXX) +project(CrypTool1 VERSION 1.4.43 LANGUAGES C CXX) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) diff --git a/port/app/CMakeLists.txt b/port/app/CMakeLists.txt index fcf9fe500..d7cbc8489 100644 --- a/port/app/CMakeLists.txt +++ b/port/app/CMakeLists.txt @@ -19,7 +19,8 @@ file(GLOB CT_EXTRA_SOURCES CONFIGURE_DEPENDS ${REPO_ROOT}/libVolRen/VolumeUtilities.cpp ${REPO_ROOT}/ChallengeResponse/*.cpp) add_executable(CrypTool ${CT_SOURCES} ${CT_EXTRA_SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/platform.cpp) -mfcwx_add_rc(CrypTool RC ${CT_SRC}/CrypTool.rc MODULE CrypTool HEADERS ${CT_SRC}/resource.h) +mfcwx_add_rc(CrypTool RC ${CT_SRC}/CrypTool.rc MODULE CrypTool HEADERS ${CT_SRC}/resource.h + REPLACE "[CRYPTOOL_VERSION_FULL]=CrypTool ${PROJECT_VERSION}" "[CRYPTOOL_VERSION]=${PROJECT_VERSION}") set_target_properties(CrypTool PROPERTIES CXX_STANDARD 14 CXX_EXTENSIONS ON) # Quoted includes only: etc. must resolve to the libraries, as on Windows. @@ -52,6 +53,30 @@ add_custom_target(cryptool_data ALL VERBATIM) add_dependencies(CrypTool cryptool_data) +# --------------------------------------------------------------------------------------------- +# Help: the HTML Help sources are mirrored into the data directory with the index the mfcwx +# HtmlHelp replacement reads (context ids, ALinks, keywords) and the generated readme page. + +set(CT_HELP_LANGUAGES en de) +set(CT_HELP_GENERATED CrypTool.helpindex hid_load_readme.html) +set(CT_HELP_OUTPUTS) +foreach(lang IN LISTS CT_HELP_LANGUAGES) + set(help_src ${CT_SRC}/hlp_${lang}) + set(help_out ${CRYPTOOL_DEV_DATA_DIR}/hlp_${lang}) + set(readme ${REPO_ROOT}/setup/template-${lang}/ReadMe-${lang}.txt) + file(GLOB help_inputs CONFIGURE_DEPENDS LIST_DIRECTORIES false ${help_src}/*) + list(TRANSFORM CT_HELP_GENERATED PREPEND ${help_out}/ OUTPUT_VARIABLE outputs) + add_custom_command(OUTPUT ${outputs} + COMMAND ${Python3_EXECUTABLE} ${REPO_ROOT}/port/tools/build_help_index.py --mirror --help-dir ${help_src} + --resource-header ${CT_SRC}/resource.h --readme ${readme} --out-dir ${help_out} + DEPENDS ${REPO_ROOT}/port/tools/build_help_index.py ${CT_SRC}/resource.h ${readme} ${help_inputs} + COMMENT "Generating the help index for hlp_${lang}" + VERBATIM) + list(APPEND CT_HELP_OUTPUTS ${outputs}) +endforeach() +add_custom_target(cryptool_help ALL DEPENDS ${CT_HELP_OUTPUTS}) +add_dependencies(CrypTool cryptool_help) + # --------------------------------------------------------------------------------------------- # Installation and packages @@ -78,6 +103,10 @@ foreach(help hlp_en hlp_de) install(DIRECTORY ${CT_SRC}/${help} DESTINATION ${CRYPTOOL_DATA_DEST} PATTERN "*.vcproj" EXCLUDE PATTERN "*.vcxproj*" EXCLUDE PATTERN "*.hhp" EXCLUDE) endforeach() +foreach(lang IN LISTS CT_HELP_LANGUAGES) + list(TRANSFORM CT_HELP_GENERATED PREPEND ${CRYPTOOL_DEV_DATA_DIR}/hlp_${lang}/ OUTPUT_VARIABLE outputs) + install(FILES ${outputs} DESTINATION ${CRYPTOOL_DATA_DEST}/hlp_${lang}) +endforeach() file(GLOB CT_TEMPLATE_ENTRIES RELATIVE ${REPO_ROOT}/setup/template ${REPO_ROOT}/setup/template/*) foreach(entry IN LISTS CT_TEMPLATE_ENTRIES) string(TOLOWER "${entry}" lower) diff --git a/port/mfcwx/src/winapi.cpp b/port/mfcwx/src/winapi.cpp index d41bf1dd1..adf0d727a 100644 --- a/port/mfcwx/src/winapi.cpp +++ b/port/mfcwx/src/winapi.cpp @@ -494,7 +494,13 @@ HWND GetWindow(HWND hWnd, UINT uCmd) { HWND GetNextWindow(HWND hWnd, UINT wCmd) { return GetWindow(hWnd, wCmd); } HWND GetTopWindow(HWND hWnd) { return GetWindow(hWnd, GW_CHILD); } -HWND GetDesktopWindow() { return nullptr; } +namespace { + +HWND DesktopHwnd() { return reinterpret_cast(static_cast(-16)); } + +} // namespace + +HWND GetDesktopWindow() { return DesktopHwnd(); } HWND GetActiveWindow() { return OnMain([]() -> HWND { return ToHwnd(ManagedAncestor(wxGetActiveWindow())); }); @@ -718,6 +724,10 @@ void ToRect(const wxRect& r, LPRECT out) { } // namespace BOOL GetWindowRect(HWND hWnd, LPRECT lpRect) { + if (hWnd == DesktopHwnd() && lpRect) { + OnMain([&] { ToRect(wxRect(wxGetDisplaySize()), lpRect); }); + return TRUE; + } if (!IsWindow(hWnd) || !lpRect) return FALSE; OnMain([&] { ToRect(ToWx(hWnd)->GetScreenRect(), lpRect); }); @@ -725,6 +735,10 @@ BOOL GetWindowRect(HWND hWnd, LPRECT lpRect) { } BOOL GetClientRect(HWND hWnd, LPRECT lpRect) { + if (hWnd == DesktopHwnd() && lpRect) { + OnMain([&] { ToRect(wxRect(wxGetDisplaySize()), lpRect); }); + return TRUE; + } if (!IsWindow(hWnd) || !lpRect) return FALSE; OnMain([&] { ToRect(wxRect(ToWx(hWnd)->GetClientSize()), lpRect); }); diff --git a/port/rc/rc2cpp.cmake b/port/rc/rc2cpp.cmake index 2177cd9b7..6c6307dac 100644 --- a/port/rc/rc2cpp.cmake +++ b/port/rc/rc2cpp.cmake @@ -8,9 +8,10 @@ set(MFCWX_RC2CPP_SCRIPT "${CMAKE_CURRENT_LIST_DIR}/rc2cpp.py" CACHE INTERNAL "") get_filename_component(rc_include_dir "${CMAKE_CURRENT_LIST_DIR}/../mfcwx/include" ABSOLUTE) set(MFCWX_RC_INCLUDE_DIR "${rc_include_dir}" CACHE INTERNAL "") -# mfcwx_add_rc( RC [MODULE ] [HEADERS ...] [OUTPUT_DIR ] [INCLUDE_DIRS ...] [DEFINES ...] [CODEPAGE ]) +# mfcwx_add_rc( RC [MODULE ] [HEADERS ...] [OUTPUT_DIR ] [INCLUDE_DIRS ...] [DEFINES ...] +# [REPLACE ...] [CODEPAGE ]) function(mfcwx_add_rc target) - cmake_parse_arguments(PARSE_ARGV 1 ARG "" "RC;MODULE;OUTPUT_DIR;CODEPAGE" "HEADERS;INCLUDE_DIRS;DEFINES") + cmake_parse_arguments(PARSE_ARGV 1 ARG "" "RC;MODULE;OUTPUT_DIR;CODEPAGE" "HEADERS;INCLUDE_DIRS;DEFINES;REPLACE") if(ARG_UNPARSED_ARGUMENTS) message(FATAL_ERROR "mfcwx_add_rc: unexpected arguments: ${ARG_UNPARSED_ARGUMENTS}") endif() @@ -44,6 +45,9 @@ function(mfcwx_add_rc target) foreach(d IN LISTS ARG_DEFINES) list(APPEND args -D "${d}") endforeach() + foreach(r IN LISTS ARG_REPLACE) + list(APPEND args --replace "${r}") + endforeach() if(ARG_CODEPAGE) list(APPEND args --codepage "${ARG_CODEPAGE}") endif() diff --git a/port/rc/rc2cpp.py b/port/rc/rc2cpp.py index eb3adf3d6..67bc5361d 100755 --- a/port/rc/rc2cpp.py +++ b/port/rc/rc2cpp.py @@ -914,6 +914,7 @@ def kw(t): class RcParser: def __init__(self, pp, rc_path, diag, dirs): + self.replacements = [] self.pp = pp self.ev = pp.ev self.diag = diag @@ -1006,6 +1007,8 @@ def expect_begin(self): def text(self, t): s = decode_rc_string(t.v, t.cp, t.x) + for old, new in self.replacements: + s = s.replace(old, new) if '�' in s: self.decode_errors += 1 self.diag.warn(self.loc(t), 'string contains bytes undefined in code page %d' % t.cp) @@ -1872,6 +1875,8 @@ def main(argv=None): ap.add_argument('-U', dest='undefines', action='append', default=[], metavar='NAME') ap.add_argument('--header', action='append', default=[], help='header read for #defines before the .rc') ap.add_argument('--no-default-headers', action='store_true', help='do not pre-include mfcwx/winconst.h') + ap.add_argument('--replace', action='append', default=[], metavar='TEXT=VALUE', + help='replace TEXT in every string literal (like the Windows pre-build step)') ap.add_argument('--codepage', type=int, default=1252, help='code page before the first #pragma code_page') ap.add_argument('--stats', action='store_true', help='print per-language resource counts') ap.add_argument('--list-outputs', action='store_true', help='print the files that would be generated and exit') @@ -1919,6 +1924,12 @@ def main(argv=None): pp.ev.freeze() parser = RcParser(pp, rc_path, diag, dirs) + for r in args.replace: + old, sep, new = r.partition('=') + if not sep or not old: + print('rc2cpp: error: --replace expects TEXT=VALUE, got %r' % r, file=sys.stderr) + return 1 + parser.replacements.append((old, new)) parser.parse() langs = list(parser.langs.values()) outs = output_names(module, langs) diff --git a/port/tools/gallery/CMakeLists.txt b/port/tools/gallery/CMakeLists.txt index 811cf73cc..5e41782d6 100644 --- a/port/tools/gallery/CMakeLists.txt +++ b/port/tools/gallery/CMakeLists.txt @@ -2,6 +2,7 @@ include(${REPO_ROOT}/port/rc/rc2cpp.cmake) add_executable(dialog_gallery gallery.cpp) mfcwx_add_rc(dialog_gallery RC ${REPO_ROOT}/CrypTool/CrypTool.rc MODULE CrypTool HEADERS ${REPO_ROOT}/CrypTool/resource.h + REPLACE "[CRYPTOOL_VERSION_FULL]=CrypTool ${PROJECT_VERSION}" "[CRYPTOOL_VERSION]=${PROJECT_VERSION}" OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/rc) target_link_libraries(dialog_gallery PRIVATE mfcwx) target_compile_definitions(dialog_gallery PRIVATE CRYPTOOL_DEV_DATA_DIR="${CMAKE_BINARY_DIR}/data") From 3bcb02cfa4bbe21bd8ff5a6d4382b54c7e5bef65 Mon Sep 17 00:00:00 2001 From: Kamil Marczak Date: Wed, 7 Oct 2026 00:00:32 +0200 Subject: [PATCH 34/46] feat(mfcwx): replace HTML Help with an in-app help browser build_help_index.py turns the .hhp/.hhc/.hhk sources and the alias headers into an index of context ids, ALinks and keywords and mirrors hlp_en/hlp_de into the data directory. ::HtmlHelp, CWinApp::WinHelp and HtmlHelp open the right page in a wxHtmlHelpController, also on top of modal dialogs. --- port/app/CMakeLists.txt | 3 +- port/app/assemble_data.cmake | 5 +- port/mfcwx/include/htmlhelp.h | 74 ++++ port/mfcwx/src/help.cpp | 681 +++++++++++++++++++++++++++++++++ port/tools/build_help_index.py | 341 +++++++++++++++++ 5 files changed, 1099 insertions(+), 5 deletions(-) create mode 100644 port/mfcwx/src/help.cpp create mode 100644 port/tools/build_help_index.py diff --git a/port/app/CMakeLists.txt b/port/app/CMakeLists.txt index d7cbc8489..83964ca0f 100644 --- a/port/app/CMakeLists.txt +++ b/port/app/CMakeLists.txt @@ -54,8 +54,7 @@ add_custom_target(cryptool_data ALL add_dependencies(CrypTool cryptool_data) # --------------------------------------------------------------------------------------------- -# Help: the HTML Help sources are mirrored into the data directory with the index the mfcwx -# HtmlHelp replacement reads (context ids, ALinks, keywords) and the generated readme page. +# Help: hlp_* mirrored into the data directory plus the index read by mfcwx (help.cpp) set(CT_HELP_LANGUAGES en de) set(CT_HELP_GENERATED CrypTool.helpindex hid_load_readme.html) diff --git a/port/app/assemble_data.cmake b/port/app/assemble_data.cmake index c228ede15..6c824fb3e 100644 --- a/port/app/assemble_data.cmake +++ b/port/app/assemble_data.cmake @@ -1,5 +1,6 @@ # Builds the data directory the application reads at run time (a development stand-in for the -# installed resources): links to the bitmaps, help and template files plus freshly generated key stores. +# installed resources): links to the bitmaps and template files plus freshly generated key stores. +# The help directories are set up by the cryptool_help target (port/tools/build_help_index.py). # cmake -DREPO_ROOT=... -DDATA_DIR=... -DMKPSE=... -P assemble_data.cmake file(MAKE_DIRECTORY "${DATA_DIR}") @@ -11,8 +12,6 @@ function(link_into name target) endfunction() link_into(res "${REPO_ROOT}/CrypTool/res") -link_into(hlp_en "${REPO_ROOT}/CrypTool/hlp_en") -link_into(hlp_de "${REPO_ROOT}/CrypTool/hlp_de") file(GLOB template_entries RELATIVE "${REPO_ROOT}/setup/template" "${REPO_ROOT}/setup/template/*") foreach(entry IN LISTS template_entries) diff --git a/port/mfcwx/include/htmlhelp.h b/port/mfcwx/include/htmlhelp.h index 1aff78a33..f1d9eee22 100644 --- a/port/mfcwx/include/htmlhelp.h +++ b/port/mfcwx/include/htmlhelp.h @@ -1,5 +1,38 @@ #pragma once #include "mfcwx/win32.h" + +#ifndef HH_SET_WIN_TYPE +#define HH_SET_WIN_TYPE 0x0004 +#define HH_GET_WIN_TYPE 0x0005 +#define HH_GET_WIN_HANDLE 0x0006 +#define HH_SYNC 0x0009 +#define HH_GET_LAST_ERROR 0x0014 +#endif + +#define HH_MAX_TABS 19 + +#define HHWIN_PROP_TAB_AUTOHIDESHOW (1 << 0) +#define HHWIN_PROP_ONTOP (1 << 1) +#define HHWIN_PROP_NOTITLEBAR (1 << 2) +#define HHWIN_PROP_NODEF_STYLES (1 << 3) +#define HHWIN_PROP_NODEF_EXSTYLES (1 << 4) +#define HHWIN_PROP_TRI_PANE (1 << 5) +#define HHWIN_PROP_NOTB_TEXT (1 << 6) +#define HHWIN_PROP_POST_QUIT (1 << 7) +#define HHWIN_PROP_AUTO_SYNC (1 << 8) +#define HHWIN_PROP_TRACKING (1 << 9) +#define HHWIN_PROP_TAB_SEARCH (1 << 10) +#define HHWIN_PROP_NO_TOOLBAR (1 << 15) + +#define HHWIN_PARAM_PROPERTIES (1 << 1) +#define HHWIN_PARAM_STYLES (1 << 2) +#define HHWIN_PARAM_EXSTYLES (1 << 3) +#define HHWIN_PARAM_RECT (1 << 4) +#define HHWIN_PARAM_NAV_WIDTH (1 << 5) +#define HHWIN_PARAM_SHOWSTATE (1 << 6) + +typedef DWORD HH_INFOTYPE, *PHH_INFOTYPE; + typedef struct tagHH_AKLINK { int cbStruct; BOOL fReserved; @@ -10,7 +43,48 @@ typedef struct tagHH_AKLINK { const char* pszWindow; BOOL fIndexOnFail; } HH_AKLINK, *PHH_AKLINK; + +typedef struct tagHH_WINTYPE { + int cbStruct; + BOOL fUniCodeStrings; + const char* pszType; + DWORD fsValidMembers; + DWORD fsWinProperties; + const char* pszCaption; + DWORD dwStyles; + DWORD dwExStyles; + RECT rcWindowPos; + int nShowState; + HWND hwndHelp; + HWND hwndCaller; + HH_INFOTYPE* paInfoTypes; + HWND hwndToolBar; + HWND hwndNavigation; + HWND hwndHTML; + int iNavWidth; + RECT rcHTML; + const char* pszToc; + const char* pszIndex; + const char* pszFile; + const char* pszHome; + DWORD fsToolBarFlags; + BOOL fNotExpanded; + int curNavType; + int tabpos; + int idNotify; + BYTE tabOrder[HH_MAX_TABS + 1]; + int cHistory; + const char* pszJump1; + const char* pszJump2; + const char* pszUrlJump1; + const char* pszUrlJump2; + RECT rcMinSize; + int cbInfoTypes; + const char* pszCustomTabs; +} HH_WINTYPE, *PHH_WINTYPE; + #ifdef __cplusplus +// Reads /hlp_de or hlp_en (UI language) with the index from build_help_index.py. HWND HtmlHelp(HWND hwndCaller, const char* pszFile, UINT uCommand, DWORD_PTR dwData); HWND HtmlHelpA(HWND hwndCaller, const char* pszFile, UINT uCommand, DWORD_PTR dwData); #endif diff --git a/port/mfcwx/src/help.cpp b/port/mfcwx/src/help.cpp new file mode 100644 index 000000000..37971f678 --- /dev/null +++ b/port/mfcwx/src/help.cpp @@ -0,0 +1,681 @@ +#include "windows_impl.h" + +#include "htmlhelp.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace mfcwx { + +namespace { + +const char kIndexFileName[] = "CrypTool.helpindex"; +const char kDataFileScheme[] = "cryptool-data:"; + +struct HelpIndex { + wxString dir; + wxString baseUrl; + wxString title; + wxString defaultTopic; + wxString contentsFile; + wxString indexFile; + std::map contexts; + std::multimap alinks; + std::multimap keywords; + std::map titles; + std::map pages; +}; + +wxString HelpRoot() { return wxFileName::DirName(wxString::FromUTF8(GetDataDirectory())).GetPath(); } + +wxString HelpDirectory() { + return HelpRoot() + wxFILE_SEP_PATH + (strcmp(GetResourceLanguage(), "de") == 0 ? "hlp_de" : "hlp_en"); +} + +wxString FileUrl(const wxString& path) { return wxFileSystem::FileNameToURL(wxFileName(path)); } + +// Help pages are served through a "cthelp:" protocol so they can be adjusted before wxHTML parses them. +const char kHelpProtocol[] = "cthelp"; + +wxString HelpUrl(const wxString& path) { return kHelpProtocol + FileUrl(path).Mid(4); } + +wxString PathFromHelpUrl(const wxString& url) { + return wxFileSystem::URLToFileName("file" + url.BeforeFirst('#').Mid(strlen(kHelpProtocol))).GetFullPath(); +} + +std::unique_ptr ReadIndex(const wxString& dir) { + std::ifstream in(std::string(wxFileName(dir, kIndexFileName).GetFullPath().utf8_str()), std::ios::binary); + std::string line; + if (!in || !std::getline(in, line) || line.compare(0, 18, "CrypToolHelpIndex\t") != 0) + return nullptr; + std::unique_ptr index(new HelpIndex); + index->dir = dir; + index->baseUrl = HelpUrl(dir + wxFILE_SEP_PATH); + if (!index->baseUrl.EndsWith("/")) + index->baseUrl += "/"; + while (std::getline(in, line)) { + if (!line.empty() && line.back() == '\r') + line.pop_back(); + wxArrayString f = wxSplit(wxString::FromUTF8(line.c_str()), '\t', 0); + if (f.size() < 2) + continue; + const wxString& kind = f[0]; + if (kind == "title") + index->title = f[1]; + else if (kind == "default") + index->defaultTopic = f[1]; + else if (kind == "contents") + index->contentsFile = f[1]; + else if (kind == "index") + index->indexFile = f[1]; + else if (f.size() < 3) + continue; + else if (kind == "context") + index->contexts[static_cast(wxAtol(f[1]))] = f[2]; + else if (kind == "alink") + index->alinks.emplace(f[1].Lower(), f[2]); + else if (kind == "keyword") + index->keywords.emplace(f[1].Lower(), f[2]); + else if (kind == "topic") { + index->titles[f[1]] = f[2]; + index->pages[f[1].Lower()] = f[1]; + } + } + return index; +} + +const HelpIndex* CurrentIndex() { + static std::map> cache; + wxString dir = HelpDirectory(); + auto it = cache.find(dir); + if (it == cache.end() || !it->second) + it = cache.emplace(dir, ReadIndex(dir)).first; + if (!it->second) { + cache.erase(it); + wxLogDebug("mfcwx: no help index in %s", dir); + return nullptr; + } + return it->second.get(); +} + +// Topic and image references in the help are not reliably cased. +wxString ResolveCaseInsensitive(const wxString& path) { + if (wxFileExists(path)) + return path; + wxString root = HelpRoot(); + if (!path.StartsWith(root + wxFILE_SEP_PATH)) + return wxString(); + wxString resolved = root; + wxStringTokenizer parts(path.Mid(root.length() + 1), wxFILE_SEP_PATH, wxTOKEN_STRTOK); + while (parts.HasMoreTokens()) { + wxString part = parts.GetNextToken(); + wxString exact = resolved + wxFILE_SEP_PATH + part; + if (wxFileName::Exists(exact)) { + resolved = exact; + continue; + } + wxDir dir(resolved); + wxString name, match; + for (bool more = dir.IsOpened() && dir.GetFirst(&name); more; more = dir.GetNext(&name)) + if (name.CmpNoCase(part) == 0) { + match = name; + break; + } + if (match.empty()) + return wxString(); + resolved += wxFILE_SEP_PATH + match; + } + return wxFileExists(resolved) ? resolved : wxString(); +} + +wxString BrowserUrl(const wxString& url) { + if (!url.StartsWith(wxString(kHelpProtocol) + ":")) + return url; + wxString anchor = url.AfterFirst('#'); + wxString path = ResolveCaseInsensitive(PathFromHelpUrl(url)); + return FileUrl(path.empty() ? PathFromHelpUrl(url) : path) + (anchor.empty() ? wxString() : "#" + anchor); +} + +// Pages without a charset are Windows-1252; script-only file links (helper.js parser()) become plain links. +std::string PreparePage(const std::string& page) { + static const std::string open = "onclick=\"parser('"; + static const std::string close = "')\""; + std::string lower(page); + std::transform(lower.begin(), lower.end(), lower.begin(), + [](unsigned char c) { return static_cast(tolower(c)); }); + std::string out; + if (lower.find("charset") == std::string::npos) + out = "\n"; + size_t pos = 0; + for (size_t hit; (hit = lower.find(open, pos)) != std::string::npos;) { + size_t start = hit + open.size(); + size_t end = page.find(close, start); + if (end == std::string::npos) + break; + bool web = lower.compare(start, 5, "http:") == 0 || lower.compare(start, 6, "https:") == 0; + out += page.substr(pos, hit - pos) + "href=\"" + (web ? "" : kDataFileScheme) + + page.substr(start, end - start) + "\""; + pos = end + close.size(); + } + return out + page.substr(pos); +} + +class HelpFSHandler : public wxFileSystemHandler { +public: + bool CanOpen(const wxString& location) override { return GetProtocol(location) == kHelpProtocol; } + + wxFSFile* OpenFile(wxFileSystem&, const wxString& location) override { + wxString path = ResolveCaseInsensitive(PathFromHelpUrl(location)); + if (path.empty()) + return nullptr; + std::unique_ptr stream(new wxFFileInputStream(path)); + if (!stream->IsOk()) + return nullptr; + wxString lower = path.Lower(); + if (lower.EndsWith(".html") || lower.EndsWith(".htm")) { + std::string page(static_cast(stream->GetLength()), '\0'); + stream->Read(&page[0], page.size()); + page = PreparePage(page.substr(0, stream->LastRead())); + wxMemoryOutputStream copy; + copy.Write(page.data(), page.size()); + stream.reset(new wxMemoryInputStream(copy)); + } + return new wxFSFile(stream.release(), location.BeforeFirst('#'), GetMimeTypeFromExt(path), + GetAnchor(location), wxDateTime(wxFileModificationTime(path))); + } +}; + +wxString UiString(UINT id, const char* fallback) { + std::string s; + return LoadResourceString(id, s) && !s.empty() ? ToWx(s.c_str()) : wxString(fallback); +} + +void OpenDataFile(wxWindow* parent, const wxString& relative) { + wxString rel = relative; + rel.Replace("\\", "/"); + wxString path = ResolveCaseInsensitive(HelpRoot() + wxFILE_SEP_PATH + rel); + if (path.empty() || !wxLaunchDefaultApplication(path)) { + wxString text = wxString::Format("%s\n%s", UiString(AFX_IDP_FAILED_TO_LAUNCH_HELP, "Failed to launch help."), + HelpRoot() + wxFILE_SEP_PATH + rel); + ShowMessageBox(parent, FromWx(text).c_str(), nullptr, MB_OK | MB_ICONEXCLAMATION); + } +} + +bool IsExternalUrl(const wxString& href) { + wxString scheme = href.BeforeFirst(':'); + if (scheme.length() < 2 || scheme.length() == href.length()) + return false; + for (wxUniChar c : scheme) + if (!wxIsalnum(c) && c != '+' && c != '-' && c != '.') + return false; + return scheme.CmpNoCase("file") != 0; +} + +int OpenInBrowserId() { + static const int id = wxWindow::NewControlId(); + return id; +} + +template +class HelpWindowShell : public Base { +public: + explicit HelpWindowShell(wxHtmlHelpData* data) : Base(data) { + this->Bind(wxEVT_HTML_LINK_CLICKED, &HelpWindowShell::OnLink, this); + this->Bind(wxEVT_TOOL, &HelpWindowShell::OnOpenInBrowser, this, OpenInBrowserId()); + } + + void AddToolbarButtons(wxToolBar* toolBar, int) override { + toolBar->AddSeparator(); + toolBar->AddTool(OpenInBrowserId(), _("Open in browser"), wxArtProvider::GetBitmap(wxART_HELP_PAGE, wxART_TOOLBAR), + _("Open this page in the web browser")); + } + +private: + void OnLink(wxHtmlLinkEvent& event) { + const wxMouseEvent* mouse = event.GetLinkInfo().GetEvent(); + wxString href = event.GetLinkInfo().GetHref(); + if (mouse && !mouse->LeftUp()) { + event.Skip(); + } else if (href.StartsWith(kDataFileScheme)) { + OpenDataFile(this, href.Mid(strlen(kDataFileScheme))); + } else if (IsExternalUrl(href)) { + wxLaunchDefaultBrowser(href); + } else { + event.Skip(); + } + } + + void OnOpenInBrowser(wxCommandEvent&) { + wxHtmlWindow* html = this->GetHelpWindow() ? this->GetHelpWindow()->GetHtmlWindow() : nullptr; + if (!html || html->GetOpenedPage().empty()) + return; + wxString url = html->GetOpenedPage(); + if (!html->GetOpenedAnchor().empty()) + url += "#" + html->GetOpenedAnchor(); + wxLaunchDefaultBrowser(BrowserUrl(url)); + } +}; + +using HelpFrame = HelpWindowShell; +using HelpDialog = HelpWindowShell; + +wxSize DefaultHelpSize(wxWindow* w) { return w->FromDIP(wxSize(1000, 720)); } + +class HelpController : public wxHtmlHelpController { +public: + explicit HelpController(const HelpIndex& index) : wxHtmlHelpController(wxHF_DEFAULT_STYLE) { + wxString title = index.title; + title.Replace("%", "%%"); + SetTitleFormat(title.empty() ? wxString("%s") : title + " - %s"); + wxFileSystem fs; + std::unique_ptr book(fs.OpenFile(FileUrl(wxFileName(index.dir, kIndexFileName).GetFullPath()))); + m_loaded = book && GetHelpData()->AddBookParam(*book, wxFONTENCODING_SYSTEM, index.title, index.contentsFile, + index.indexFile, index.defaultTopic, index.baseUrl); + } + + bool IsLoaded() const { return m_loaded; } + wxHtmlHelpFrame* Frame() const { return m_helpFrame; } + +protected: + wxHtmlHelpFrame* CreateHelpFrame(wxHtmlHelpData* data) override { + HelpFrame* frame = new HelpFrame(data); + frame->SetController(this); + frame->SetTitleFormat(m_titleFormat); +#if wxUSE_CONFIG + frame->Create(m_parentWindow, wxID_ANY, wxEmptyString, m_FrameStyle, m_Config, m_ConfigRoot); + bool configured = m_Config != nullptr; +#else + frame->Create(m_parentWindow, wxID_ANY, wxEmptyString, m_FrameStyle); + bool configured = false; +#endif + frame->SetShouldPreventAppExit(m_shouldPreventAppExit); + if (!configured) { + frame->SetSize(DefaultHelpSize(frame)); + frame->Centre(); + } + m_helpFrame = frame; + return frame; + } + +private: + bool m_loaded = false; +}; + +enum class Pane { Page, Contents, Index }; + +struct HelpRequest { + wxString topic; + Pane pane = Pane::Page; +}; + +struct HelpState { + HelpController* controller = nullptr; + wxString controllerDir; + std::vector> controllers; + HelpDialog* modalHelp = nullptr; + HelpRequest pending; + bool pendingScheduled = false; +}; + +// Never destroyed: the controllers must not outlive the wx windows they point to at exit. +HelpState& State() { + static HelpState* state = new HelpState; + return *state; +} + +HelpController* ControllerFor(const HelpIndex& index) { + HelpState& st = State(); + if (st.controller && st.controllerDir == index.dir) + return st.controller; + if (st.controller) + st.controller->Quit(); + static bool registered = false; + if (!registered) { + registered = true; + wxFileSystem::AddHandler(new HelpFSHandler); + } + std::unique_ptr c(new HelpController(index)); + if (!c->IsLoaded()) + return nullptr; + st.controller = c.get(); + st.controllerDir = index.dir; + st.controllers.push_back(std::move(c)); + return st.controller; +} + +wxDialog* ActiveModalDialog() { + wxDialog* found = nullptr; + for (wxWindow* w : wxTopLevelWindows) { + wxDialog* d = wxDynamicCast(w, wxDialog); + if (d && d->IsModal() && d->IsShown()) + found = d; + } + return found; +} + +HWND PseudoHelpWindow() { + static char tag; + return reinterpret_cast(&tag); +} + +HWND HelpWindowHandle() { + HelpState& st = State(); + if (st.modalHelp) + return ToHwnd(st.modalHelp); + if (st.controller && st.controller->Frame()) + return ToHwnd(st.controller->Frame()); + return PseudoHelpWindow(); +} + +void Present(wxHtmlHelpWindow* window, const HelpRequest& request) { + if (request.pane == Pane::Contents) + window->DisplayContents(); + else if (request.pane == Pane::Index) + window->DisplayIndex(); + if (!request.topic.empty()) + window->Display(request.topic); +} + +void RunModalHelp(wxDialog* parent, HelpController* controller, const wxString& title, const HelpRequest& request) { + HelpState& st = State(); + HelpDialog dlg(controller->GetHelpData()); + dlg.Create(parent, wxID_ANY, wxEmptyString, wxHF_DEFAULT_STYLE); + if (!title.empty()) + dlg.SetTitle(title); + dlg.SetSize(DefaultHelpSize(&dlg)); + dlg.CentreOnParent(); + Present(dlg.GetHelpWindow(), request); + st.modalHelp = &dlg; + dlg.ShowModal(); + st.modalHelp = nullptr; +} + +void Display(const HelpRequest& request) { + HelpState& st = State(); + const HelpIndex* index = CurrentIndex(); + HelpController* controller = index ? ControllerFor(*index) : nullptr; + if (!controller) + return; + if (st.modalHelp) { + Present(st.modalHelp->GetHelpWindow(), request); + st.modalHelp->Raise(); + } else if (wxDialog* modal = ActiveModalDialog()) { + RunModalHelp(modal, controller, index->title, request); + } else { + if (request.pane == Pane::Contents) + controller->DisplayContents(); + else if (request.pane == Pane::Index) + controller->DisplayIndex(); + if (!request.topic.empty()) + controller->Display(request.topic); + if (wxHtmlHelpFrame* frame = controller->Frame()) { + frame->Show(); + frame->Raise(); + } + } +} + +void ShowPending() { + HelpState& st = State(); + if (!st.pendingScheduled) + return; + st.pendingScheduled = false; + Display(st.pending); +} + +// Frames get no input while a modal dialog runs (macOS, GTK), so the help is then shown modally, deferred. +HWND Show(const HelpIndex& index, const HelpRequest& request) { + if (!ControllerFor(index)) + return nullptr; + HelpState& st = State(); + if (!st.modalHelp && !ActiveModalDialog()) { + Display(request); + } else { + st.pending = request; + if (!st.pendingScheduled) { + st.pendingScheduled = true; + wxTheApp->CallAfter(&ShowPending); + } + } + return HelpWindowHandle(); +} + +// "file.chm::/topic.html>window" or "topic.html#anchor" to a topic file relative to the help directory. +wxString TopicFromReference(const wxString& reference) { + wxString topic = reference; + int sep = topic.Find("::"); + if (sep != wxNOT_FOUND) + topic = topic.Mid(sep + 2); + topic = topic.BeforeFirst('>'); + topic.Replace("\\", "/"); + while (topic.StartsWith("/")) + topic.Remove(0, 1); + return topic; +} + +// A topic as the help data expects it: relative to the help directory, in the file's case. +wxString TopicPath(const HelpIndex& index, const wxString& topic) { + wxString file = topic.BeforeFirst('#'); + wxString anchor = topic.AfterFirst('#'); + auto page = index.pages.find(file.Lower()); + if (page != index.pages.end()) { + file = page->second; + } else { + wxString path = ResolveCaseInsensitive(index.dir + wxFILE_SEP_PATH + file); + if (file.empty() || path.empty()) + return wxString(); + file = path.Mid(index.dir.length() + 1); + file.Replace("\\", "/"); + } + return file + (anchor.empty() ? wxString() : "#" + anchor); +} + +HWND ShowTopic(const HelpIndex& index, const wxString& topic, Pane pane = Pane::Page) { + if (IsExternalUrl(topic) && !topic.Lower().StartsWith("mk:") && !topic.Lower().StartsWith("ms-its:")) { + wxLaunchDefaultBrowser(topic); + return PseudoHelpWindow(); + } + HelpRequest request; + request.topic = TopicPath(index, topic); + request.pane = pane; + if (request.topic.empty() && pane == Pane::Page) + return nullptr; + return Show(index, request); +} + +wxWindow* DialogParent(HWND caller) { + HelpState& st = State(); + if (st.modalHelp) + return st.modalHelp; + if (wxDialog* modal = ActiveModalDialog()) + return modal; + if (st.controller && st.controller->Frame() && st.controller->Frame()->IsShown()) + return st.controller->Frame(); + return caller ? ToWx(caller) : MainWxWindow(); +} + +HWND LinkLookup(HWND caller, const HelpIndex& index, const HH_AKLINK* link, bool alink) { + if (!link || !link->pszKeywords) + return nullptr; + const std::multimap& table = alink ? index.alinks : index.keywords; + std::vector pages; + wxStringTokenizer keywords(ToWx(link->pszKeywords), ";", wxTOKEN_STRTOK); + while (keywords.HasMoreTokens()) { + wxString keyword = keywords.GetNextToken().Trim().Trim(false).Lower(); + auto range = table.equal_range(keyword); + for (auto it = range.first; it != range.second; ++it) + if (std::find(pages.begin(), pages.end(), it->second) == pages.end()) + pages.push_back(it->second); + } + if (pages.empty()) { + if (link->pszUrl && *link->pszUrl) + return ShowTopic(index, TopicFromReference(ToWx(link->pszUrl))); + if (link->fIndexOnFail) + return ShowTopic(index, wxString(), Pane::Index); + if (link->pszMsgText && *link->pszMsgText) { + ShowMessageBox(DialogParent(caller), link->pszMsgText, link->pszMsgTitle, MB_OK | MB_ICONINFORMATION); + return HelpWindowHandle(); + } + return nullptr; + } + wxString page = pages.front(); + if (pages.size() > 1) { + wxArrayString choices; + for (const wxString& p : pages) { + auto t = index.titles.find(p); + choices.Add(t != index.titles.end() && !t->second.empty() ? t->second : p); + } + wxSingleChoiceDialog dlg(DialogParent(caller), _("Click a topic, then click Display."), _("Topics Found"), + choices); + if (dlg.ShowModal() != wxID_OK) + return HelpWindowHandle(); + page = pages[static_cast(dlg.GetSelection())]; + } + return ShowTopic(index, page); +} + +void CloseAll() { + HelpState& st = State(); + st.pendingScheduled = false; + if (st.modalHelp) + st.modalHelp->EndModal(wxID_CANCEL); + if (st.controller) + st.controller->Quit(); +} + +HWND HtmlHelpOnMain(HWND caller, const char* pszFile, UINT uCommand, DWORD_PTR dwData) { + switch (uCommand) { + case HH_INITIALIZE: + if (dwData) + *reinterpret_cast(dwData) = 1; + return PseudoHelpWindow(); + case HH_UNINITIALIZE: + case HH_CLOSE_ALL: + CloseAll(); + return PseudoHelpWindow(); + case HH_SET_WIN_TYPE: + case HH_SYNC: + return PseudoHelpWindow(); + case HH_GET_WIN_TYPE: + if (dwData) + *reinterpret_cast(dwData) = nullptr; + return nullptr; + case HH_GET_WIN_HANDLE: { + HWND h = HelpWindowHandle(); + return h == PseudoHelpWindow() ? nullptr : h; + } + default: + break; + } + + const HelpIndex* index = CurrentIndex(); + if (!index) + return nullptr; + switch (uCommand) { + case HH_DISPLAY_TOPIC: { + wxString topic; + if (dwData) + topic = TopicFromReference(ToWx(reinterpret_cast(dwData))); + else if (pszFile && strstr(pszFile, "::")) + topic = TopicFromReference(ToWx(pszFile)); + return ShowTopic(*index, topic.empty() ? index->defaultTopic : topic); + } + case HH_DISPLAY_TOC: + case HH_DISPLAY_SEARCH: + return ShowTopic(*index, wxString(), Pane::Contents); + case HH_DISPLAY_INDEX: + return ShowTopic(*index, wxString(), Pane::Index); + case HH_HELP_CONTEXT: { + auto it = index->contexts.find(static_cast(dwData)); + return it == index->contexts.end() ? nullptr : ShowTopic(*index, it->second); + } + case HH_ALINK_LOOKUP: + case HH_KEYWORD_LOOKUP: + return LinkLookup(caller, *index, reinterpret_cast(dwData), uCommand == HH_ALINK_LOOKUP); + default: + return nullptr; + } +} + +} // namespace + +} // namespace mfcwx + +using namespace mfcwx; + +HWND HtmlHelp(HWND hwndCaller, const char* pszFile, UINT uCommand, DWORD_PTR dwData) { + return OnMain([&]() -> HWND { return HtmlHelpOnMain(hwndCaller, pszFile, uCommand, dwData); }); +} + +HWND HtmlHelpA(HWND hwndCaller, const char* pszFile, UINT uCommand, DWORD_PTR dwData) { + return HtmlHelp(hwndCaller, pszFile, uCommand, dwData); +} + +int GetMenuStringW(HMENU hMenu, UINT uIDItem, LPWSTR lpString, int cchMax, UINT flags) { + int length = ::GetMenuString(hMenu, uIDItem, nullptr, 0, flags); + if (!lpString || cchMax <= 0) + return length; + std::vector ansi(static_cast(length) + 1); + ::GetMenuString(hMenu, uIDItem, ansi.data(), length + 1, flags); + int n = std::min(length, cchMax - 1); + for (int i = 0; i < n; ++i) + lpString[i] = AnsiToUnicode(static_cast(ansi[static_cast(i)])); + lpString[n] = 0; + return n; +} + +void CWinApp::WinHelp(DWORD_PTR dwData, UINT nCmd) { + m_bHelpMode = FALSE; + WinHelpInternal(dwData, nCmd); +} + +void CWinApp::WinHelpInternal(DWORD_PTR dwData, UINT nCmd) { + m_bHelpMode = FALSE; + switch (nCmd) { + case HELP_CONTEXT: + case HELP_CONTEXTPOPUP: + HtmlHelp(dwData, HH_HELP_CONTEXT); + break; + case HELP_CONTENTS: + HtmlHelp(0, HH_DISPLAY_TOC); + break; + case HELP_KEY: + case HELP_PARTIALKEY: { + HH_AKLINK link = {}; + link.cbStruct = sizeof link; + link.pszKeywords = reinterpret_cast(dwData); + link.fIndexOnFail = TRUE; + HtmlHelp(reinterpret_cast(&link), HH_KEYWORD_LOOKUP); + break; + } + case HELP_QUIT: + HtmlHelp(0, HH_CLOSE_ALL); + break; + default: + HtmlHelp(0, HH_DISPLAY_TOPIC); + break; + } +} + +void CWinApp::HtmlHelp(DWORD_PTR dwData, UINT nCmd) { + m_bHelpMode = FALSE; + CWnd* main = AfxGetMainWnd(); + if (!::HtmlHelp(main ? main->m_hWnd : nullptr, m_pszHelpFilePath, nCmd, dwData)) + AfxMessageBox(FromWx(UiString(AFX_IDP_FAILED_TO_LAUNCH_HELP, "Failed to launch help.")).c_str(), + MB_OK | MB_ICONINFORMATION); +} diff --git a/port/tools/build_help_index.py b/port/tools/build_help_index.py new file mode 100644 index 000000000..f42869850 --- /dev/null +++ b/port/tools/build_help_index.py @@ -0,0 +1,341 @@ +#!/usr/bin/env python3 +"""Builds the help index used by the mfcwx HTML Help replacement from an HTML Help Workshop project. + +The index maps HH_HELP_CONTEXT ids to topic files (the .hhp [MAP]/[ALIAS] sections, with +CrypTool_helpIDs.h recreated from resource.h like makehm + hh_generator.pl do), ALink and keyword +names to the pages declaring them, and records the default topic, contents and index files. +It also creates hid_load_readme.html from the readme text (like readme.pl). + + build_help_index.py --help-dir CrypTool/hlp_en --resource-header CrypTool/resource.h + --readme setup/template-en/ReadMe-en.txt --out-dir build/data/hlp_en [--mirror] + +--mirror turns the output directory into a copy of the help directory made of symbolic links +(development data directory); otherwise only the generated files are written. +""" +import argparse +import html +import os +import re +import shutil +import sys + +INDEX_NAME = 'CrypTool.helpindex' +README_NAME = 'hid_load_readme.html' +README_TEMPLATE = README_NAME + '.in' +MIRROR_EXCLUDE = re.compile(r'\.(vcproj|vcxproj.*|mak|dsp)$', re.I) + +# The makehm calls of CrypTool/MakeHtmlHelp.bat. +MAKEHM_RULES = [('ID_', 'HID_', 0x10000), ('IDM_', 'HIDM_', 0x10000), ('IDP_', 'HIDP_', 0x30000), + ('IDR_', 'HIDR_', 0x20000), ('IDD_', 'HIDD_', 0x20000), ('IDW_', 'HIDW_', 0x50000)] + +DEFINE_RE = re.compile(r'^\s*#\s*define\s+(\w+)\s+(0[xX][0-9a-fA-F]+|\d+)[uUlL]*\s*(?:$|//|/\*)') +MAP_LINE_RE = re.compile(r'^\s*(\w+)\s+(0[xX][0-9a-fA-F]+|\d+)\s*$') +INCLUDE_RE = re.compile(r'^\s*#\s*include\s+[<"]?([^>"\s]+)[>"]?\s*$', re.I) +ALIAS_RE = re.compile(r'^\s*(\w+)\s*=\s*(\S+)\s*$') +CHARSET_RE = re.compile(rb']+charset\s*=\s*["\']?([\w-]+)', re.I) +TITLE_RE = re.compile(r']*>(.*?)', re.I | re.S) +PARAM_RE = re.compile(r']*)>', re.I) +ATTR_RE = re.compile(r'(\w+)\s*=\s*(?:"([^"]*)"|\'([^\']*)\'|([^\s>]+))') + + +def read_text(path): + with open(path, 'rb') as f: + return f.read().decode('latin-1') + + +class Listing: + """Case-insensitive view of a directory: topic names in the project are not reliably cased.""" + + def __init__(self, directory, extra=()): + self.directory = directory + self.names = {} + for name in sorted(os.listdir(directory)): + self.names.setdefault(name.lower(), name) + for name in extra: + self.names.setdefault(name.lower(), name) + + def find(self, name): + return self.names.get(name.replace('\\', '/').lower()) + + def path(self, name): + real = self.find(name) + return os.path.join(self.directory, real) if real else None + + +def parse_hhp(path): + sections = {} + current = None + for line in read_text(path).splitlines(): + stripped = line.strip() + if stripped.startswith('[') and stripped.endswith(']'): + current = stripped[1:-1].upper() + sections.setdefault(current, []) + elif current is not None and stripped: + sections[current].append(stripped) + options = {} + for line in sections.get('OPTIONS', []): + key, sep, value = line.partition('=') + if sep: + options[key.strip().lower()] = value.strip() + return sections, options + + +def window_caption(sections, name): + for line in sections.get('WINDOWS', []): + key, sep, value = line.partition('=') + if sep and key.strip().lower() == name.lower(): + m = re.match(r'\s*"([^"]*)"', value) + if m: + return m.group(1) + return '' + + +def parse_defines(text): + for line in text.splitlines(): + m = DEFINE_RE.match(line) + if m: + yield m.group(1), int(m.group(2), 0) + + +def makehm(resource_header): + """CrypTool_helpIDs.h as produced by makehm + hh_generator.pl from resource.h.""" + out = [] + for name, value in parse_defines(read_text(resource_header)): + for prefix, hprefix, offset in MAKEHM_RULES: + if name.startswith(prefix): + out.append((hprefix + name[len(prefix):], value + offset)) + break + return out + + +def collect_map(sections, listing, resource_header, problems): + defines = [] + for line in sections.get('MAP', []): + m = INCLUDE_RE.match(line) + if m: + path = listing.path(m.group(1)) + if path: + defines += list(parse_defines(read_text(path))) + elif m.group(1).lower() == 'cryptool_helpids.h' and resource_header: + defines += makehm(resource_header) + else: + problems.append('[MAP] include not found: %s' % m.group(1)) + continue + m = DEFINE_RE.match(line) or MAP_LINE_RE.match(line) + if m: + defines.append((m.group(1), int(m.group(2), 0))) + return defines + + +def collect_aliases(sections, listing, problems): + aliases = {} + + def add_lines(lines): + for line in lines: + if line.startswith(';') or line.startswith('//'): + continue + m = ALIAS_RE.match(line) + if m: + aliases.setdefault(m.group(1).upper(), m.group(2)) + + for line in sections.get('ALIAS', []): + m = INCLUDE_RE.match(line) + if m: + path = listing.path(m.group(1)) + if path: + add_lines(read_text(path).splitlines()) + else: + problems.append('[ALIAS] include not found: %s' % m.group(1)) + else: + add_lines([line]) + return aliases + + +def resolve_topic(listing, target): + target = target.replace('\\', '/') + page, sep, anchor = target.partition('#') + real = listing.find(page) + if not real: + base, ext = os.path.splitext(page) + alternative = {'.htm': '.html', '.html': '.htm'}.get(ext.lower()) + real = listing.find(base + alternative) if alternative else None + return real + sep + anchor if real else None + + +def decode_page(data): + m = CHARSET_RE.search(data) + encoding = m.group(1).decode('ascii').lower() if m else 'cp1252' + try: + return data.decode(encoding) + except (LookupError, UnicodeDecodeError): + return data.decode('cp1252', errors='replace') + + +def clean(value): + return ' '.join(html.unescape(value).split()) + + +def scan_pages(listing, generated): + titles, alinks, keywords = {}, [], [] + for real in sorted(set(listing.names.values())): + if not re.search(r'\.html?$', real, re.I): + continue + source = generated.get(real) or os.path.join(listing.directory, real) + with open(source, 'rb') as f: + text = decode_page(f.read()) + m = TITLE_RE.search(text) + titles[real] = clean(re.sub(r'<[^>]*>', '', m.group(1))) if m else '' + for pm in PARAM_RE.finditer(text): + attrs = {a.group(1).lower(): next(v for v in a.groups()[1:] if v is not None) + for a in ATTR_RE.finditer(pm.group(1))} + name = attrs.get('name', '').strip().lower() + value = clean(attrs.get('value', '')) + if not value: + continue + if name == 'alink name': + alinks.append((value, real)) + elif name == 'keyword': + keywords.append((value, real)) + return titles, alinks, keywords + + +def make_readme(template_path, readme_path): + if not os.path.exists(readme_path): + readme_path = re.sub(r'-..([./\\])', r'-en\1', readme_path) + text = read_text(readme_path).replace('\r\n', '\n').replace('\r', '\n') + text = text.replace('&', '&').replace('<', '<').replace('>', '>') + text = re.sub(r'^( *)([.A-Z0-9]+)( +\.\.\.\.? +)(.*)$', r'\1\2\3\4', text, flags=re.M) + text = re.sub(r'^([.A-Z0-9]+\.) ([^.].*)', r'

\1 \2

', text, flags=re.M) + text = re.sub(r'----*', '', text) + return read_text(template_path).replace('__BODY__', text, 1).encode('latin-1') + + +def tsv(*fields): + return '\t'.join(f.replace('\t', ' ').replace('\n', ' ') for f in fields) + '\n' + + +def build(args): + help_dir = os.path.abspath(args.help_dir) + problems = [] + generated = {} + template = os.path.join(help_dir, README_TEMPLATE) + if args.readme and os.path.exists(template): + generated[README_NAME] = make_readme(template, args.readme) + + listing = Listing(help_dir, extra=generated) + hhp_name = args.project or next((n for n in listing.names.values() if n.lower().endswith('.hhp')), None) + if not hhp_name: + sys.exit('build_help_index: no .hhp project in %s' % help_dir) + sections, options = parse_hhp(os.path.join(help_dir, hhp_name)) + + defines = collect_map(sections, listing, args.resource_header, problems) + aliases = collect_aliases(sections, listing, problems) + + candidates = {} + unresolved = [] + for name, value in defines: + target = aliases.get(name.upper()) + topic = resolve_topic(listing, target if target else name.lower() + '.htm') + if topic: + entries = candidates.setdefault(value, []) + if (name, topic) not in entries: + entries.append((name, topic)) + else: + unresolved.append((name, value, target)) + + contexts = {} + collisions = [] + for value, entries in candidates.items(): + if len({t for _, t in entries}) > 1: + collisions.append((value, entries)) + # AFX_HIDD_* and HIDR_* ids can collide with CrypTool's dialogs and commands, which F1 asks for + best = sorted(entries, key=lambda e: 0 if e[0].startswith(('HIDD_', 'HID_')) else 1)[0] + contexts[value] = best[1] + + out_dir = os.path.abspath(args.out_dir) + prepare_out_dir(out_dir, help_dir, args.mirror, generated) + generated_paths = {} + for name, data in generated.items(): + generated_paths[name] = os.path.join(out_dir, name) + write(generated_paths[name], data) + + titles, alinks, keywords = scan_pages(listing, generated_paths) + + default_topic = resolve_topic(listing, options.get('default topic', '')) or '' + caption = window_caption(sections, options.get('default window', '')) or options.get('title', '') + lines = [tsv('CrypToolHelpIndex', '1'), + tsv('title', caption), + tsv('default', default_topic)] + for key, kind in (('contents file', 'contents'), ('index file', 'index')): + real = listing.find(options.get(key, '')) + if real: + lines.append(tsv(kind, real)) + for value in sorted(contexts): + lines.append(tsv('context', str(value), contexts[value])) + for keyword, page in sorted(set(alinks), key=lambda e: (e[0].lower(), e[1])): + lines.append(tsv('alink', keyword, page)) + for keyword, page in sorted(set(keywords), key=lambda e: (e[0].lower(), e[1])): + lines.append(tsv('keyword', keyword, page)) + for page in sorted(titles): + lines.append(tsv('topic', page, titles[page])) + write(os.path.join(out_dir, INDEX_NAME), ''.join(lines).encode('utf-8')) + + if args.stats or args.verbose: + print('%s: %d context ids mapped (%d MAP symbols, %d unresolved, %d ids with conflicting topics), ' + '%d ALinks (%d names), %d keywords, %d pages, default topic %s' % ( + os.path.basename(help_dir), len(contexts), len(defines), len(unresolved), len(collisions), + len(set(alinks)), len({a.lower() for a, _ in alinks}), len(set(keywords)), len(titles), + default_topic or '-')) + if args.verbose: + for name, value, target in unresolved: + print(' unresolved: %s (0x%X)%s' % (name, value, ' -> ' + target if target else '')) + for value, entries in collisions: + print(' conflict 0x%X: %s' % (value, ', '.join('%s=%s' % e for e in entries))) + for p in problems: + print(' ' + p) + + +def write(path, data): + with open(path, 'wb') as f: + f.write(data) + + +def prepare_out_dir(out_dir, help_dir, mirror, generated): + if mirror and os.path.islink(out_dir): + os.unlink(out_dir) + os.makedirs(out_dir, exist_ok=True) + if os.path.realpath(out_dir) == os.path.realpath(help_dir): + sys.exit('build_help_index: refusing to write into the help sources (%s)' % help_dir) + if not mirror: + return + wanted = {n for n in os.listdir(help_dir) if not MIRROR_EXCLUDE.search(n) and n not in generated} + for name in os.listdir(out_dir): + path = os.path.join(out_dir, name) + if os.path.islink(path) and (name not in wanted or not os.path.exists(path)): + os.unlink(path) + for name in sorted(wanted): + link = os.path.join(out_dir, name) + if os.path.lexists(link): + continue + src = os.path.join(help_dir, name) + try: + os.symlink(src, link) + except OSError: + (shutil.copytree if os.path.isdir(src) else shutil.copy2)(src, link) + + +def main(): + p = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) + p.add_argument('--help-dir', required=True, help='HTML Help Workshop project directory (hlp_en, ...)') + p.add_argument('--project', help='.hhp file name (default: the only .hhp in --help-dir)') + p.add_argument('--resource-header', help='resource.h to recreate CrypTool_helpIDs.h from') + p.add_argument('--readme', help='readme text for hid_load_readme.html') + p.add_argument('--out-dir', required=True) + p.add_argument('--mirror', action='store_true', help='link the help files into --out-dir') + p.add_argument('--stats', action='store_true', help='print a summary') + p.add_argument('--verbose', action='store_true', help='print the summary and every problem') + build(p.parse_args()) + + +if __name__ == '__main__': + main() From 3929b0c1da13a6be01c377bf328911a7a98b2a56 Mon Sep 17 00:00:00 2001 From: Kamil Marczak Date: Wed, 7 Oct 2026 00:00:55 +0200 Subject: [PATCH 35/46] fix(mfcwx): update status bar panes on idle and take titles after any separator Status bar panes now run UPDATE_COMMAND_UI through the frame as in MFC, so the editor shows line/column/position and the key indicators reflect the lock state. CDocument::SetPathName cuts the title at the last backslash, slash or colon like MFC. Snapshots also capture the native macOS toolbar. --- port/mfcwx/include/mfcwx/docview.h | 5 ++ port/mfcwx/src/app.cpp | 6 ++ port/mfcwx/src/docview.cpp | 112 ++++++++++++++++++++++++++--- port/mfcwx/src/snapshot_mac.mm | 33 +++++++-- 4 files changed, 140 insertions(+), 16 deletions(-) diff --git a/port/mfcwx/include/mfcwx/docview.h b/port/mfcwx/include/mfcwx/docview.h index 6bed5ea9b..57df9a525 100644 --- a/port/mfcwx/include/mfcwx/docview.h +++ b/port/mfcwx/include/mfcwx/docview.h @@ -210,6 +210,7 @@ class CFrameWnd : public CWnd { afx_msg void OnSize(UINT nType, int cx, int cy); afx_msg void OnSetFocus(CWnd* pOldWnd); afx_msg void OnUpdateControlBarMenu(CCmdUI* pCmdUI); + afx_msg void OnUpdateKeyIndicator(CCmdUI* pCmdUI); afx_msg BOOL OnBarCheck(UINT nID); afx_msg void OnHelp(); afx_msg void OnContextHelp(); @@ -383,6 +384,7 @@ class CControlBar : public CWnd { BOOL IsFloating() const { return FALSE; } CFrameWnd* GetDockingFrame() const { return GetParentFrame(); } virtual CSize CalcFixedLayout(BOOL bStretch, BOOL bHorz); + virtual void OnUpdateCmdUI(CFrameWnd* pTarget, BOOL bDisableIfNoHndler); DWORD m_dwStyle; DWORD m_dwDockStyle; }; @@ -452,8 +454,11 @@ class CStatusBar : public CControlBar { void SetPaneStyle(int nIndex, UINT nStyle); CString GetText() const { return GetPaneText(0); } void SetText(const char* lpsz) { SetPaneText(0, lpsz); } + void OnUpdateCmdUI(CFrameWnd* pTarget, BOOL bDisableIfNoHndler) override; std::vector m_indicators; + std::vector m_paneStyles; + std::vector m_paneTexts; }; class CDialogBar : public CControlBar { diff --git a/port/mfcwx/src/app.cpp b/port/mfcwx/src/app.cpp index 97512dbc9..4217b0439 100644 --- a/port/mfcwx/src/app.cpp +++ b/port/mfcwx/src/app.cpp @@ -270,6 +270,12 @@ int CWinApp::Run() { return wxTheApp ? wxTheApp->MainLoop() : 0; } BOOL CWinApp::OnIdle(LONG lCount) { if (lCount <= 0) { + if (m_pMainWnd && m_pMainWnd->m_hWnd && m_pMainWnd->IsKindOf(RUNTIME_CLASS(CFrameWnd))) { + auto* frame = static_cast(m_pMainWnd); + std::vector bars = frame->m_listControlBars; + for (CControlBar* bar : bars) + bar->OnUpdateCmdUI(frame, FALSE); + } for (CDocTemplate* t : m_templates) { POSITION pos = t->GetFirstDocPosition(); while (pos) diff --git a/port/mfcwx/src/docview.cpp b/port/mfcwx/src/docview.cpp index 1170fbacb..a7c194787 100644 --- a/port/mfcwx/src/docview.cpp +++ b/port/mfcwx/src/docview.cpp @@ -145,11 +145,13 @@ void CDocument::SetTitle(const char* lpszTitle) { void CDocument::SetPathName(const char* lpszPathName, BOOL bAddToMRU) { m_strPathName = lpszPathName; - CString title = m_strPathName; - int slash = title.ReverseFind('\\'); - if (slash >= 0) - title = title.Mid(slash + 1); - SetTitle(title); + int start = 0; + for (int i = 0; i < m_strPathName.GetLength(); ++i) { + char c = m_strPathName[i]; + if (c == '\\' || c == '/' || c == ':') + start = i + 1; + } + SetTitle(m_strPathName.Mid(start)); if (bAddToMRU && AfxGetApp()) AfxGetApp()->AddToRecentFileList(lpszPathName); } @@ -511,6 +513,9 @@ BEGIN_MESSAGE_MAP(CFrameWnd, CWnd) ON_WM_SETFOCUS() ON_UPDATE_COMMAND_UI(ID_VIEW_STATUS_BAR, &CFrameWnd::OnUpdateControlBarMenu) ON_UPDATE_COMMAND_UI(ID_VIEW_TOOLBAR, &CFrameWnd::OnUpdateControlBarMenu) + ON_UPDATE_COMMAND_UI(ID_INDICATOR_CAPS, &CFrameWnd::OnUpdateKeyIndicator) + ON_UPDATE_COMMAND_UI(ID_INDICATOR_NUM, &CFrameWnd::OnUpdateKeyIndicator) + ON_UPDATE_COMMAND_UI(ID_INDICATOR_SCRL, &CFrameWnd::OnUpdateKeyIndicator) ON_COMMAND_EX(ID_VIEW_STATUS_BAR, &CFrameWnd::OnBarCheck) ON_COMMAND_EX(ID_VIEW_TOOLBAR, &CFrameWnd::OnBarCheck) ON_COMMAND(ID_HELP, &CFrameWnd::OnHelp) @@ -826,6 +831,16 @@ void CFrameWnd::OnUpdateControlBarMenu(CCmdUI* pCmdUI) { pCmdUI->SetCheck(bar->IsWindowVisible()); } +void CFrameWnd::OnUpdateKeyIndicator(CCmdUI* pCmdUI) { + int vk = 0; + switch (pCmdUI->m_nID) { + case ID_INDICATOR_CAPS: vk = VK_CAPITAL; break; + case ID_INDICATOR_NUM: vk = VK_NUMLOCK; break; + case ID_INDICATOR_SCRL: vk = VK_SCROLL; break; + } + pCmdUI->Enable(vk && (::GetKeyState(vk) & 1)); +} + BOOL CFrameWnd::OnBarCheck(UINT nID) { CControlBar* bar = GetControlBar(nID); if (!bar) @@ -1401,6 +1416,7 @@ IMPLEMENT_DYNAMIC(CStatusBar, CControlBar) CControlBar::CControlBar() : m_dwStyle(0), m_dwDockStyle(0) {} CSize CControlBar::CalcFixedLayout(BOOL, BOOL) { return CSize(0, 0); } +void CControlBar::OnUpdateCmdUI(CFrameWnd*, BOOL) {} CToolBar::CToolBar() : m_nResourceID(0) {} @@ -1526,6 +1542,8 @@ BOOL CStatusBar::SetIndicators(const UINT* lpIDArray, int nIDCount) { sb->SetStatusText(ToWx(text), i); } sb->SetStatusWidths(nIDCount, widths.data()); + m_paneStyles.assign(static_cast(nIDCount), SBPS_NORMAL); + m_paneTexts.assign(static_cast(nIDCount), CString()); return TRUE; }); } @@ -1555,20 +1573,30 @@ void CStatusBar::GetItemRect(int nIndex, LPRECT lpRect) const { } CString CStatusBar::GetPaneText(int nIndex) const { + if (nIndex >= 0 && static_cast(nIndex) < m_paneStyles.size() && (m_paneStyles[static_cast(nIndex)] & SBPS_DISABLED)) + return m_paneTexts[static_cast(nIndex)]; return OnMain([&]() -> CString { auto* sb = wxDynamicCast(GetWx(), wxStatusBar); - return sb && nIndex < sb->GetFieldsCount() ? CStr(sb->GetStatusText(nIndex)) : CString(); + return sb && nIndex >= 0 && nIndex < sb->GetFieldsCount() ? CStr(sb->GetStatusText(nIndex)) : CString(); }); } void CStatusBar::GetPaneText(int nIndex, CString& rString) const { rString = GetPaneText(nIndex); } BOOL CStatusBar::SetPaneText(int nIndex, const char* lpszNewText, BOOL) { + const char* text = lpszNewText ? lpszNewText : ""; return OnMain([&]() -> BOOL { auto* sb = wxDynamicCast(GetWx(), wxStatusBar); - if (!sb || nIndex >= sb->GetFieldsCount()) + if (!sb || nIndex < 0 || nIndex >= sb->GetFieldsCount()) return FALSE; - sb->SetStatusText(ToWx(lpszNewText ? lpszNewText : ""), nIndex); + size_t i = static_cast(nIndex); + if (i < m_paneStyles.size() && (m_paneStyles[i] & SBPS_DISABLED)) { + m_paneTexts[i] = text; + return TRUE; + } + wxString wxText = ToWx(text); + if (sb->GetStatusText(nIndex) != wxText) + sb->SetStatusText(wxText, nIndex); return TRUE; }); } @@ -1598,8 +1626,72 @@ void CStatusBar::SetPaneInfo(int nIndex, UINT nID, UINT nStyle, int cxWidth) { }); } -UINT CStatusBar::GetPaneStyle(int) const { return SBPS_NORMAL; } -void CStatusBar::SetPaneStyle(int, UINT) {} +UINT CStatusBar::GetPaneStyle(int nIndex) const { + return nIndex >= 0 && static_cast(nIndex) < m_paneStyles.size() ? m_paneStyles[static_cast(nIndex)] : SBPS_NORMAL; +} + +void CStatusBar::SetPaneStyle(int nIndex, UINT nStyle) { + if (nIndex < 0 || static_cast(nIndex) >= m_paneStyles.size()) + return; + size_t i = static_cast(nIndex); + bool wasDisabled = (m_paneStyles[i] & SBPS_DISABLED) != 0; + bool disabled = (nStyle & SBPS_DISABLED) != 0; + if (wasDisabled == disabled) { + m_paneStyles[i] = nStyle; + return; + } + CString text = GetPaneText(nIndex); + m_paneStyles[i] = nStyle & ~SBPS_DISABLED; + if (disabled) { + SetPaneText(nIndex, ""); + m_paneTexts[i] = text; + m_paneStyles[i] = nStyle; + } else { + SetPaneText(nIndex, text); + } +} + +namespace { + +class CStatusCmdUI : public CCmdUI { +public: + void Enable(BOOL bOn) override { + m_bEnableChanged = TRUE; + auto* bar = static_cast(m_pOther); + UINT style = bar->GetPaneStyle(static_cast(m_nIndex)) & ~static_cast(SBPS_DISABLED); + if (!bOn) + style |= SBPS_DISABLED; + bar->SetPaneStyle(static_cast(m_nIndex), style); + } + + void SetCheck(int nCheck) override { + auto* bar = static_cast(m_pOther); + UINT style = bar->GetPaneStyle(static_cast(m_nIndex)) & ~static_cast(SBPS_POPOUT); + if (nCheck) + style |= SBPS_POPOUT; + bar->SetPaneStyle(static_cast(m_nIndex), style); + } + + void SetText(const char* lpszText) override { static_cast(m_pOther)->SetPaneText(static_cast(m_nIndex), lpszText); } +}; + +} // namespace + +void CStatusBar::OnUpdateCmdUI(CFrameWnd* pTarget, BOOL bDisableIfNoHndler) { + if (!pTarget || !m_hWnd) + return; + CStatusCmdUI state; + state.m_pOther = this; + state.m_nIndexMax = static_cast(m_indicators.size()); + for (state.m_nIndex = 0; state.m_nIndex < state.m_nIndexMax; ++state.m_nIndex) { + state.m_nID = m_indicators[state.m_nIndex]; + if (state.m_nID == 0) + continue; + if (CWnd::OnCmdMsg(state.m_nID, static_cast(CN_UPDATE_COMMAND_UI), &state, nullptr)) + continue; + state.DoUpdate(pTarget, bDisableIfNoHndler); + } +} // --------------------------------------------------------------------------------------------- // Rich edit view/document diff --git a/port/mfcwx/src/snapshot_mac.mm b/port/mfcwx/src/snapshot_mac.mm index 1fb405171..fa4880445 100644 --- a/port/mfcwx/src/snapshot_mac.mm +++ b/port/mfcwx/src/snapshot_mac.mm @@ -6,6 +6,28 @@ namespace mfcwx { +namespace { + +NSView* FindToolbarView(NSView* root) { + if ([NSStringFromClass([root class]) isEqualToString:@"NSToolbarView"]) + return root; + for (NSView* child in root.subviews) + if (NSView* found = FindToolbarView(child)) + return found; + return nil; +} + +bool WritePng(NSView* view, const std::string& path) { + NSBitmapImageRep* rep = [view bitmapImageRepForCachingDisplayInRect:view.bounds]; + if (!rep) + return false; + [view cacheDisplayInRect:view.bounds toBitmapImageRep:rep]; + NSData* png = [rep representationUsingType:NSBitmapImageFileTypePNG properties:@{}]; + return [png writeToFile:[NSString stringWithUTF8String:path.c_str()] atomically:YES]; +} + +} // namespace + bool SnapshotWxWindow(wxWindow* w, const std::string& pngPath) { if (!w) return false; @@ -16,12 +38,11 @@ bool SnapshotWxWindow(wxWindow* w, const std::string& pngPath) { if (!content) return false; [content displayIfNeeded]; - NSBitmapImageRep* rep = [content bitmapImageRepForCachingDisplayInRect:content.bounds]; - if (!rep) - return false; - [content cacheDisplayInRect:content.bounds toBitmapImageRep:rep]; - NSData* png = [rep representationUsingType:NSBitmapImageFileTypePNG properties:@{}]; - return [png writeToFile:[NSString stringWithUTF8String:pngPath.c_str()] atomically:YES]; + if (NSView* toolbar = FindToolbarView(content.superview ? content.superview : content)) { + if (toolbar.bounds.size.height > 0 && pngPath.size() > 4) + WritePng(toolbar, pngPath.substr(0, pngPath.size() - 4) + "_toolbar.png"); + } + return WritePng(content, pngPath); } } // namespace mfcwx From 3e4dfbd88754693624ff71ac6976b128906f5b7f Mon Sep 17 00:00:00 2001 From: Kamil Marczak Date: Thu, 8 Oct 2026 12:07:33 +0200 Subject: [PATCH 36/46] ci(status): fix test counting and publishing of the port status CTest 4 reports "100% tests passed out of N" without a failure count, which the status script read as zero tests. The status files are no longer tracked on xplat-port, and they are published from a separate clone so the workspace never has to switch branches. --- .github/workflows/port-status.yml | 20 +++++++++++--------- .gitignore | 1 + port/status/STATUS.md | 12 ------------ port/status/badge.json | 1 - port/status/status.json | 10 ---------- port/tools/port_status.py | 4 ++-- 6 files changed, 14 insertions(+), 34 deletions(-) delete mode 100644 port/status/STATUS.md delete mode 100644 port/status/badge.json delete mode 100644 port/status/status.json diff --git a/.github/workflows/port-status.yml b/.github/workflows/port-status.yml index ff0f74e57..4e490431a 100644 --- a/.github/workflows/port-status.yml +++ b/.github/workflows/port-status.yml @@ -23,19 +23,21 @@ jobs: run: python3 port/tools/port_status.py - name: Publish to the port-status branch run: | - out="$RUNNER_TEMP/status" - mkdir -p "$out" - cp port/status/* "$out/" + pub="$RUNNER_TEMP/port-status" + git init -q "$pub" + cd "$pub" git config user.name "port-status-bot" git config user.email "port-status-bot@users.noreply.github.com" - if git fetch --depth 1 origin port-status; then - git switch -c port-status FETCH_HEAD + git remote add origin "$(git -C "$GITHUB_WORKSPACE" config --get remote.origin.url)" + git -C "$GITHUB_WORKSPACE" config --get-regexp '^http\..*\.extraheader$' | while read -r key value; do + git config "$key" "$value" + done + if git fetch -q --depth 1 origin port-status; then + git checkout -q -b port-status FETCH_HEAD else - git switch --orphan port-status + git checkout -q --orphan port-status fi - git rm -rfq --ignore-unmatch . - git clean -fdxq - cp "$out"/* . + cp "$GITHUB_WORKSPACE"/port/status/* . git add badge.json status.json STATUS.md git diff --cached --quiet || git commit -m "chore(status): update port status for ${GITHUB_SHA::8}" git push origin port-status diff --git a/.gitignore b/.gitignore index f55be30cf..d64bc8a6e 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ port/thirdparty/build*/ port/rc/test/build*/ __pycache__/ port/status/ +.DS_Store diff --git a/port/status/STATUS.md b/port/status/STATUS.md deleted file mode 100644 index 1077f6d09..000000000 --- a/port/status/STATUS.md +++ /dev/null @@ -1,12 +0,0 @@ -# Port status (generated by port/tools/port_status.py) - -| Metric | Value | -|---|---| -| Progress | **58%** | -| Compile errors | 456 | -| CrypTool sources compiling | 200 / 296 | -| CrypTool links | no | -| Tests passing | 8 / 10 | -| Updated | 2026-10-06 10:24 UTC | - -Progress = 50% x sources compiling + 20% if the app links + 30% x tests passing. diff --git a/port/status/badge.json b/port/status/badge.json deleted file mode 100644 index bd5570629..000000000 --- a/port/status/badge.json +++ /dev/null @@ -1 +0,0 @@ -{"schemaVersion": 1, "label": "macOS/Linux port", "message": "58%", "color": "orange"} \ No newline at end of file diff --git a/port/status/status.json b/port/status/status.json deleted file mode 100644 index 3e3fdac2f..000000000 --- a/port/status/status.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "progress": 58, - "compileErrors": 456, - "sourcesCompiled": 200, - "sourcesTotal": 296, - "linked": false, - "testsPassed": 8, - "testsTotal": 10, - "updated": "2026-10-06 10:24 UTC" -} \ No newline at end of file diff --git a/port/tools/port_status.py b/port/tools/port_status.py index 182c9f117..7fe19d8f5 100644 --- a/port/tools/port_status.py +++ b/port/tools/port_status.py @@ -65,9 +65,9 @@ def main(): for p in ('CrypTool', 'CrypTool.app/Contents/MacOS/CrypTool')) tests = run(['ctest', '--test-dir', BUILD, '--timeout', '300']) - m = re.search(r'(\d+)% tests passed, (\d+) tests failed out of (\d+)', tests.stdout) + m = re.search(r'(\d+)% tests passed(?:, (\d+) tests? failed)? out of (\d+)', tests.stdout) total = int(m.group(3)) if m else 0 - failed = int(m.group(2)) if m else 0 + failed = int(m.group(2) or 0) if m else 0 passed = total - failed compile_share = compiled / len(units) if units else 0.0 From 2a9eba91775e2aea9ab7e6508bf75068e8852c64 Mon Sep 17 00:00:00 2001 From: Kamil Marczak Date: Thu, 8 Oct 2026 12:39:37 +0200 Subject: [PATCH 37/46] fix(mfcwx): make CrypTool start on Linux - Hide the executable's symbols: the zip code's global inflate replaced zlib's for FreeType, which crashed while fontconfig loaded fonts. - Defer fonts created by static initializers (ChallengeResponse's global computers) until the toolkit runs; GTK cannot query fonts before that. - Keep wx window and menu ids below 0x7fff: larger Windows ids such as AFX_IDW_PANE_FIRST and the 0x8000+ command range get compact ids. --- port/app/CMakeLists.txt | 3 ++ port/mfcwx/src/docview.cpp | 4 +-- port/mfcwx/src/gdi.cpp | 16 ++++++++++- port/mfcwx/src/gdi_internal.h | 1 + port/mfcwx/src/internal.h | 6 ++-- port/mfcwx/src/menu.cpp | 54 +++++++++++++++++++++++++++++++++++ 6 files changed, 79 insertions(+), 5 deletions(-) diff --git a/port/app/CMakeLists.txt b/port/app/CMakeLists.txt index 83964ca0f..2f92f38ad 100644 --- a/port/app/CMakeLists.txt +++ b/port/app/CMakeLists.txt @@ -23,6 +23,9 @@ mfcwx_add_rc(CrypTool RC ${CT_SRC}/CrypTool.rc MODULE CrypTool HEADERS ${CT_SRC} REPLACE "[CRYPTOOL_VERSION_FULL]=CrypTool ${PROJECT_VERSION}" "[CRYPTOOL_VERSION]=${PROJECT_VERSION}") set_target_properties(CrypTool PROPERTIES CXX_STANDARD 14 CXX_EXTENSIONS ON) +# The zip code defines inflate, deflate, error, ... globally; exported, they would replace zlib's and glibc's +# functions for the shared libraries (FreeType crashes in CrypTool's inflate on Linux). +set_target_properties(CrypTool PROPERTIES C_VISIBILITY_PRESET hidden CXX_VISIBILITY_PRESET hidden) # Quoted includes only: etc. must resolve to the libraries, as on Windows. target_compile_options(CrypTool PRIVATE "SHELL:-iquote ${CT_SRC}" "SHELL:-iquote ${REPO_ROOT}/libVolRen" "SHELL:-iquote ${REPO_ROOT}/ChallengeResponse") diff --git a/port/mfcwx/src/docview.cpp b/port/mfcwx/src/docview.cpp index a7c194787..8f2477a8d 100644 --- a/port/mfcwx/src/docview.cpp +++ b/port/mfcwx/src/docview.cpp @@ -45,7 +45,7 @@ class MfcFrame : public wxFrame { Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& e) { UpdateCommandUI(this, e); }); Bind(wxEVT_MENU_HIGHLIGHT, [this](wxMenuEvent& e) { CWnd* p = PermanentWnd(this); - if (p && p->IsKindOf(RUNTIME_CLASS(CFrameWnd)) && e.GetMenuId() >= kIdOffset) { + if (p && p->IsKindOf(RUNTIME_CLASS(CFrameWnd)) && IsCommandWxId(e.GetMenuId())) { CString text; static_cast(p)->GetMessageString(static_cast(MenuWinId(e.GetMenuId())), text); static_cast(p)->SetMessageText(text); @@ -61,7 +61,7 @@ class MfcFrame : public wxFrame { static void UpdateCommandUI(wxWindow* frame, wxUpdateUIEvent& e) { CWnd* p = PermanentWnd(frame); - if (!p || e.GetId() < kIdOffset - 1 || !p->IsKindOf(RUNTIME_CLASS(CFrameWnd))) { + if (!p || !IsCommandWxId(e.GetId()) || !p->IsKindOf(RUNTIME_CLASS(CFrameWnd))) { if (e.GetId() == wxID_ABOUT || e.GetId() == wxID_EXIT) e.Enable(true); return; diff --git a/port/mfcwx/src/gdi.cpp b/port/mfcwx/src/gdi.cpp index 4ed041428..853702798 100644 --- a/port/mfcwx/src/gdi.cpp +++ b/port/mfcwx/src/gdi.cpp @@ -464,7 +464,11 @@ HGDIOBJ NewFont(const LOGFONT* lf) { auto* impl = NewImpl(GdiKind::Font); impl->logFont = *lf; impl->logFont.lfFaceName[LF_FACESIZE - 1] = 0; - impl->font = MakeFont(impl->logFont, &impl->emPx); + // Fonts created by static initializers wait for the toolkit, which GTK needs before any font query. + if (wxTheApp) + impl->font = MakeFont(impl->logFont, &impl->emPx); + else + impl->fontPending = true; return RegisterGdiObject(impl); } @@ -751,10 +755,18 @@ wxBrush DeviceBrush(HBRUSH h) { return impl->brush; } +void RealizeFont(GdiObjectImpl* impl) { + if (impl && impl->fontPending) { + impl->fontPending = false; + impl->font = MakeFont(impl->logFont, &impl->emPx); + } +} + wxFont DeviceFont(HFONT h, double scale) { GdiObjectImpl* impl = GdiImpl(h, GdiKind::Font); if (!impl) impl = Stock(SYSTEM_FONT); + RealizeFont(impl); if (impl->stock || std::fabs(scale - 1.0) < 1e-6 || scale <= 0) return impl->font; if (!impl->scaledFont.IsOk() || std::fabs(impl->scaledFor - scale) > 1e-6) { @@ -769,6 +781,7 @@ double FontEmPixels(HFONT h) { GdiObjectImpl* impl = GdiImpl(h, GdiKind::Font); if (!impl) impl = Stock(SYSTEM_FONT); + RealizeFont(impl); return impl->emPx; } @@ -850,6 +863,7 @@ wxBrush BrushFromHandle(HBRUSH h) { wxFont FontFromHandle(HFONT h) { GdiLock lock; GdiObjectImpl* impl = GdiImpl(h, GdiKind::Font); + RealizeFont(impl); return impl ? impl->font : wxNullFont; } diff --git a/port/mfcwx/src/gdi_internal.h b/port/mfcwx/src/gdi_internal.h index e18a63be8..1e9e50441 100644 --- a/port/mfcwx/src/gdi_internal.h +++ b/port/mfcwx/src/gdi_internal.h @@ -53,6 +53,7 @@ struct GdiObjectImpl { wxBrush brush; LOGFONT logFont{}; + bool fontPending = false; wxFont font; double emPx = 0; double scaledFor = 1.0; diff --git a/port/mfcwx/src/internal.h b/port/mfcwx/src/internal.h index 44fbbf84d..f71b29000 100644 --- a/port/mfcwx/src/internal.h +++ b/port/mfcwx/src/internal.h @@ -61,8 +61,10 @@ wxString MenuTextToWx(const char* utf8Text); // Win32 control ids map to wx ids with an offset so they never collide with wx's stock ids. constexpr int kIdOffset = 20000; -inline int ToWxId(int winId) { return winId == -1 || winId == 0xFFFF ? wxID_ANY : winId + kIdOffset; } -inline int FromWxId(int wxId) { return wxId >= kIdOffset ? wxId - kIdOffset : (wxId == wxID_ANY ? -1 : wxId); } +// wx ids must stay below 0x7fff: small Windows ids are offset, larger ones get compact ids above wxID_HIGHEST. +int ToWxId(int winId); +int FromWxId(int wxId); +bool IsCommandWxId(int wxId); enum class ControlKind { Generic, // custom-drawn wxWindow (CWnd::Create, owner-drawn controls, views) diff --git a/port/mfcwx/src/menu.cpp b/port/mfcwx/src/menu.cpp index c0b77c015..e8467cac5 100644 --- a/port/mfcwx/src/menu.cpp +++ b/port/mfcwx/src/menu.cpp @@ -2,6 +2,7 @@ #include #include +#include #include namespace mfcwx { @@ -129,6 +130,59 @@ wxMenu* CloneMenu(wxMenu* src) { } // namespace +namespace { + +constexpr int kCompactBase = wxID_HIGHEST + 1; + +struct CompactIds { + std::mutex mutex; + std::unordered_map toWx; + std::vector toWin; +}; + +CompactIds& Compact() { + static CompactIds ids; + return ids; +} + +} // namespace + +int ToWxId(int winId) { + if (winId == -1 || winId == 0xFFFF) + return wxID_ANY; + if (winId + kIdOffset < 0x7fff) + return winId + kIdOffset; + CompactIds& c = Compact(); + std::lock_guard lock(c.mutex); + auto it = c.toWx.find(winId); + if (it != c.toWx.end()) + return it->second; + int compact = kCompactBase + static_cast(c.toWin.size()); + c.toWx.emplace(winId, compact); + c.toWin.push_back(winId); + return compact; +} + +int FromWxId(int wxId) { + if (wxId == wxID_ANY) + return -1; + if (wxId >= kIdOffset) + return wxId - kIdOffset; + CompactIds& c = Compact(); + std::lock_guard lock(c.mutex); + if (wxId >= kCompactBase && wxId < kCompactBase + static_cast(c.toWin.size())) + return c.toWin[static_cast(wxId - kCompactBase)]; + return wxId; +} + +bool IsCommandWxId(int wxId) { + if (wxId >= kIdOffset - 1) + return true; + CompactIds& c = Compact(); + std::lock_guard lock(c.mutex); + return wxId >= kCompactBase && wxId < kCompactBase + static_cast(c.toWin.size()); +} + int MenuWxId(int winId) { if (winId == ID_APP_ABOUT) return wxID_ABOUT; From 0147949c551beceb0442c67a8f3de8e18913e5ec Mon Sep 17 00:00:00 2001 From: Kamil Marczak Date: Thu, 8 Oct 2026 12:39:50 +0200 Subject: [PATCH 38/46] ci(linux): build, test and start CrypTool on Ubuntu Runs on pushes and pull requests: builds everything, runs the tests under Xvfb, starts the app with window snapshots enabled and uploads them. Also moves the status workflow to actions/checkout@v5 (Node 24). --- .github/workflows/linux.yml | 43 +++++++++++++++++++++++++++++++ .github/workflows/port-status.yml | 2 +- 2 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/linux.yml diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml new file mode 100644 index 000000000..d7064311b --- /dev/null +++ b/.github/workflows/linux.yml @@ -0,0 +1,43 @@ +name: Linux + +on: + push: + branches: [xplat-port] + pull_request: + workflow_dispatch: + +concurrency: + group: linux-${{ github.ref }} + cancel-in-progress: true + +jobs: + build: + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v5 + - name: Install dependencies + run: | + sudo apt-get update -qq + sudo apt-get install -y -qq ninja-build libwxgtk3.2-dev libssl-dev libgmp-dev libglu1-mesa-dev xvfb xauth + - name: Configure + run: cmake -S . -B build -G Ninja -DMFCWX_TESTS=ON -DCRYPTOOL_THIRDPARTY_TESTS=ON + - name: Build + run: ninja -C build + - name: Test + run: xvfb-run -a ctest --test-dir build --output-on-failure --timeout 300 + - name: Start CrypTool + env: + MFCWX_SNAPSHOT_DIR: ${{ runner.temp }}/snapshots + MFCWX_SNAPSHOT_COUNT: 3 + MFCWX_SNAPSHOT_DISMISS: 1 + MFCWX_SNAPSHOT_INTERVAL: 3000 + run: | + mkdir -p "$MFCWX_SNAPSHOT_DIR" + timeout 120 xvfb-run -a -s "-screen 0 1600x1000x24" build/port/app/CrypTool + ls "$MFCWX_SNAPSHOT_DIR"/*.png + - uses: actions/upload-artifact@v4 + if: always() + with: + name: linux-snapshots + path: ${{ runner.temp }}/snapshots + if-no-files-found: ignore diff --git a/.github/workflows/port-status.yml b/.github/workflows/port-status.yml index 4e490431a..c4aeb0bfd 100644 --- a/.github/workflows/port-status.yml +++ b/.github/workflows/port-status.yml @@ -16,7 +16,7 @@ jobs: status: runs-on: macos-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Install dependencies run: brew install wxwidgets@3.2 openssl@3 gmp ninja cmake - name: Measure From 6f7ccfbdebe73dbf4b2825045325dec57ddd4496 Mon Sep 17 00:00:00 2001 From: Kamil Marczak Date: Thu, 8 Oct 2026 12:40:17 +0200 Subject: [PATCH 39/46] docs(port): update porting status now that CrypTool runs on macOS and Linux --- port/PORTING_STATUS.md | 88 ++++++++++++++++++++++++------------------ 1 file changed, 51 insertions(+), 37 deletions(-) diff --git a/port/PORTING_STATUS.md b/port/PORTING_STATUS.md index 69cdfefdc..439db0443 100644 --- a/port/PORTING_STATUS.md +++ b/port/PORTING_STATUS.md @@ -1,20 +1,22 @@ # CrypTool 1 on macOS/Linux — porting status -Work in progress on branch `xplat-port`. Nothing builds into a runnable app yet. +Work in progress on branch `xplat-port`. CrypTool builds, links and starts on macOS (arm64) and +Linux (Ubuntu 24.04, GTK 3): the tip-of-the-day dialog and the starting example in the editor +come up. Most features have not been exercised yet. -**Overall progress:** measured automatically, see [STATUS.md on the port-status branch](https://github.com/wroclaw-tech/cryptool1/blob/port-status/STATUS.md) (`python3 port/tools/port_status.py` locally). -The table below is a manual estimate by area. +**Automatic status:** [STATUS.md on the port-status branch](https://github.com/wroclaw-tech/cryptool1/blob/port-status/STATUS.md) +(`python3 port/tools/port_status.py` locally). It only measures compiling, linking and the unit +tests, which all pass, so it shows 100%; the table below estimates the remaining work by area. | Area | Weight | Done | |---|---|---| | Resource compiler | 10% | 100% | | Third-party libraries | 15% | 100% | -| mfcwx headers | 10% | 90% | -| mfcwx implementation | 30% | 25% | -| SECUDE replacement | 10% | 40% | -| App sources compile | 10% | 80% | -| Link, run, UI tuning | 10% | 0% | -| Packaging (DMG, AppImage) | 5% | 0% | +| SECUDE replacement | 10% | 100% | +| mfcwx (MFC/Win32 on wxWidgets) | 30% | 85% | +| App sources compile and link | 10% | 100% | +| Run, feature testing, UI tuning | 20% | 15% | +| Packaging (DMG, deb/tar.gz) | 5% | 60% | ## Approach The original MFC sources are compiled almost unchanged against **mfcwx**, an MFC/Win32 @@ -22,35 +24,47 @@ compatibility layer implemented on wxWidgets 3.2. Windows resources (`.rc`) are C++ tables at build time. SECUDE (proprietary, Windows-only) is replaced by an OpenSSL 3 based implementation of the same API. -Build: `cmake -S . -B build -G Ninja && ninja -C build -k 0 CrypTool` -(macOS: Homebrew `wxwidgets@3.2`, `openssl@3`, `gmp`, `cmake`). +Build and run: +``` +cmake -S . -B build -G Ninja +ninja -C build +build/port/app/CrypTool.app/Contents/MacOS/CrypTool # macOS +build/port/app/CrypTool # Linux +``` +macOS: Homebrew `wxwidgets@3.2 openssl@3 gmp ninja cmake`. Linux (Ubuntu 24.04): +`libwxgtk3.2-dev libssl-dev libgmp-dev libglu1-mesa-dev ninja-build cmake`. +Tests: configure with `-DMFCWX_TESTS=ON -DCRYPTOOL_THIRDPARTY_TESTS=ON`, then `ctest`. +Packages: `cmake --install` / `cpack` (DMG on macOS, tar.gz and deb on Linux). ## Layout -| Path | Content | State | -|---|---|---| -| `CMakeLists.txt` | top-level build | done | -| `port/rc/` | `rc2cpp.py` resource compiler + CMake function | done, tested on all 4 .rc files | -| `port/thirdparty/` | libanalyse, MIRACL (32-bit digits), NTL 5.5.2, libec, cracklib, apfloat→GMP, cv act→OpenSSL, AES candidates, OpenSSL hash shims | done, tests pass on macOS + Linux (Docker) | -| `port/secude/` | SECUDE API on OpenSSL 3 | partial (agent stopped mid-work); `secude_compat` must also export `global_add_error` and `rabinstest` for libec | -| `port/mfcwx/include/` | MFC/Win32 headers (afx*.h, windows.h, …) | written; app compiles against them | -| `port/mfcwx/src/` | implementation | partial: CString, code pages, resources, CCmdTarget/message maps, CWnd + event bridge (`wnd.cpp`), controls (`controls.cpp`); other files may be partial from stopped agents | -| `port/tools/` | `fix_includes.py`, `fix_msgmaps.py`, `ccheck.py` (single-file syntax check) | done | +| Path | Content | +|---|---| +| `port/rc/` | `rc2cpp.py` resource compiler and the `mfcwx_add_rc` CMake function | +| `port/thirdparty/` | libanalyse, MIRACL, NTL, libec, cracklib, apfloat→GMP, cv act→OpenSSL, AES candidates | +| `port/secude/` | SECUDE API on OpenSSL 3 (key stores, CA, certificates, PKCS#12) | +| `port/mfcwx/` | MFC/Win32 headers and implementation, plus runtime/GDI/Scintilla tests | +| `port/app/` | CrypTool target, data directory assembly, bundle/install/CPack rules, icons | +| `port/tools/` | port status, help index generator, dialog gallery, source fix scripts | ## Key decisions -- HWND = `wxWindow*`; control ids are offset by `mfcwx::kIdOffset`; controls have no data members (temporary `CWnd` casts work as in MFC). -- `CString` is a byte string in the active ANSI code page (1252/1250/1253 by UI language); conversion happens at the wx boundary. -- Paths inside the app keep Windows form (`\\dir\\file`); every file API converts with `mfcwx::NativePath`. -- App is compiled as C++14 (C++17 `std::byte` clashes with the Windows `byte` typedef). -- Cross-thread `SendMessage` is marshalled to the main thread like on Windows. - -## Remaining work (in order) -1. Finish the partially written mfcwx parts: non-GUI runtime (`kernel.cpp` and files/archives/CRT), - GDI (`gdi.cpp`), control classes (`ctrlclasses.cpp`), Scintilla proxy (`scintilla.cpp`), help - (`help.cpp`), WGL on wxGLCanvas. -2. Fix the remaining compile errors in the app sources (see the port-status badge). -3. Link, run, visually tune dialog units and fonts. -4. Packaging (.app/DMG, AppImage/.deb). - -Done so far: resource compiler, third-party libraries, SECUDE replacement (10/10 tests), -message dispatch and controls, dialogs and DDX, menus, document/view with tabbed MDI, -CWinApp and startup glue. +- HWND = `wxWindow*`; Windows ids are offset by `kIdOffset`, ids that would exceed wx's 0x7fff + limit get compact ids; controls have no data members (temporary `CWnd` casts work as in MFC). +- `CString` is a byte string in the active ANSI code page (1252/1250/1253 by UI language); + conversion happens at the wx boundary. The Scintilla proxy keeps the editor in UTF-8 and + translates text and positions. +- Paths inside the app keep Windows form (`\dir\file`); every file API converts them. +- The registry is an INI file (`~/Library/Preferences/CrypTool1.ini`, `~/.config/cryptool1.ini`); + `APPDATA` points to `~/Library/Application Support` or `~/.local/share`. +- HTML Help is replaced by an in-app wxHtmlHelpController built from the `.hhp/.hhc/.hhk` sources. +- The app is compiled as C++14 with hidden symbol visibility. +- `MFCWX_SNAPSHOT_DIR` makes the app save PNGs of its windows (used by the Linux CI job). + +## Remaining work +1. Exercise the menus: encryption/analysis dialogs, hex editor, plots, OpenGL views, key store, + and fix what breaks (start with the dialog gallery, `-DCRYPTOOL_DIALOG_GALLERY=ON`). +2. UI tuning: dialog sizes and fonts, toolbar icons in dark mode, F1 on submenus + (`WM_MENUSELECT`), printing (currently a stub). +3. Data: ship the PDFs referenced by the help, check the external tools (AES animation, ANIMAL, …). +4. Packaging: sign/notarize the macOS bundle, test the deb on a clean system. + +Known approximations are listed in the commit messages of the respective mfcwx parts. From 418e3d4fb7ca869e8ed4bbf290989e020a7ea995 Mon Sep 17 00:00:00 2001 From: Kamil Marczak Date: Thu, 8 Oct 2026 12:45:55 +0200 Subject: [PATCH 40/46] ci(linux): run once per pull request commit and use upload-artifact v7 --- .github/workflows/linux.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index d7064311b..d92ae8838 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -2,7 +2,7 @@ name: Linux on: push: - branches: [xplat-port] + branches: [main] pull_request: workflow_dispatch: @@ -35,7 +35,7 @@ jobs: mkdir -p "$MFCWX_SNAPSHOT_DIR" timeout 120 xvfb-run -a -s "-screen 0 1600x1000x24" build/port/app/CrypTool ls "$MFCWX_SNAPSHOT_DIR"/*.png - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@v7 if: always() with: name: linux-snapshots From 74720e16b4b0454a7e4843b84076ada51e846314 Mon Sep 17 00:00:00 2001 From: Kamil Marczak Date: Thu, 8 Oct 2026 13:01:22 +0200 Subject: [PATCH 41/46] build(app): sign the macOS bundle and find Linux data next to the binary fixup_bundle rewrites the copied dylibs, which invalidates their signatures; the bundle is now signed ad hoc again so it starts on Apple silicon. On Linux the data directory is looked up in ../share/CrypTool relative to the executable, so relocated tar.gz installs work. --- port/app/CMakeLists.txt | 5 +++++ port/mfcwx/src/resources.cpp | 9 +++++++++ 2 files changed, 14 insertions(+) diff --git a/port/app/CMakeLists.txt b/port/app/CMakeLists.txt index 2f92f38ad..99f6c0f15 100644 --- a/port/app/CMakeLists.txt +++ b/port/app/CMakeLists.txt @@ -128,6 +128,11 @@ if(APPLE) install(CODE " include(BundleUtilities) fixup_bundle(\"\${CMAKE_INSTALL_PREFIX}/CrypTool.app\" \"\" \"\") + execute_process(COMMAND codesign --force --deep --sign - \"\${CMAKE_INSTALL_PREFIX}/CrypTool.app\" + RESULT_VARIABLE sign_result) + if(NOT sign_result EQUAL 0) + message(FATAL_ERROR \"codesign failed\") + endif() ") endif() diff --git a/port/mfcwx/src/resources.cpp b/port/mfcwx/src/resources.cpp index 0a3695361..904e2a507 100644 --- a/port/mfcwx/src/resources.cpp +++ b/port/mfcwx/src/resources.cpp @@ -189,6 +189,15 @@ std::string GetDataDirectory() { Ctx().dataDir = std::string(env.utf8_str()); } else { wxString installed = wxStandardPaths::Get().GetResourcesDir(); +#if !defined(__WXOSX__) + wxFileName exe(wxStandardPaths::Get().GetExecutablePath()); + wxString relative = exe.GetPath() + "/../share/" + exe.GetName(); + if (wxDirExists(relative + "/res")) { + wxFileName dir = wxFileName::DirName(relative); + dir.Normalize(wxPATH_NORM_DOTS | wxPATH_NORM_ABSOLUTE); + installed = dir.GetPath(); + } +#endif if (wxDirExists(installed + "/res") || Ctx().defaultDataDir.empty()) Ctx().dataDir = std::string(installed.utf8_str()); else From 87c73c4af99042f6f061d9db3d02af34ae059b10 Mon Sep 17 00:00:00 2001 From: Kamil Marczak Date: Thu, 8 Oct 2026 13:09:06 +0200 Subject: [PATCH 42/46] ci(release): build macOS and Linux packages and publish them on tags Pushing a v* tag (or running the workflow by hand with a tag) builds the DMG on macOS arm64 and deb/tar.gz on Linux x86_64 and arm64, smoke-starts the Linux build and attaches everything to a GitHub prerelease. Pull requests that touch packaging build the packages without releasing. Package file names now carry the OS and architecture. --- .github/workflows/release.yml | 94 +++++++++++++++++++++++++++++++++++ port/app/CMakeLists.txt | 14 ++++++ 2 files changed, 108 insertions(+) create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 000000000..5f507f0cc --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,94 @@ +name: Release + +on: + push: + tags: ['v*'] + pull_request: + paths: + - .github/workflows/release.yml + - port/app/** + - CMakeLists.txt + workflow_dispatch: + inputs: + tag: + description: Tag to create the release for (created from the selected ref if it does not exist) + required: true + +permissions: + contents: write + +jobs: + macos: + runs-on: macos-latest + steps: + - uses: actions/checkout@v5 + - name: Install dependencies + run: brew install wxwidgets@3.2 openssl@3 gmp ninja cmake + - name: Build + run: | + cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release + ninja -C build + - name: Package + run: cd build && cpack -G DragNDrop + - uses: actions/upload-artifact@v7 + with: + name: macos + path: build/CrypTool-*.dmg + if-no-files-found: error + + linux: + strategy: + matrix: + runner: [ubuntu-24.04, ubuntu-24.04-arm] + runs-on: ${{ matrix.runner }} + steps: + - uses: actions/checkout@v5 + - name: Install dependencies + run: | + sudo apt-get update -qq + sudo apt-get install -y -qq ninja-build libwxgtk3.2-dev libssl-dev libgmp-dev libglu1-mesa-dev xvfb xauth + - name: Build + run: | + cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr + ninja -C build + - name: Start CrypTool + env: + MFCWX_SNAPSHOT_DIR: ${{ runner.temp }}/snapshots + MFCWX_SNAPSHOT_COUNT: 2 + MFCWX_SNAPSHOT_DISMISS: 1 + MFCWX_SNAPSHOT_INTERVAL: 3000 + run: | + mkdir -p "$MFCWX_SNAPSHOT_DIR" + timeout 120 xvfb-run -a build/port/app/CrypTool + ls "$MFCWX_SNAPSHOT_DIR"/*.png + - name: Package + run: cd build && cpack -G "DEB;TGZ" + - uses: actions/upload-artifact@v7 + with: + name: linux-${{ matrix.runner }} + path: | + build/CrypTool-*.deb + build/CrypTool-*.tar.gz + if-no-files-found: error + + release: + needs: [macos, linux] + if: github.event_name != 'pull_request' + runs-on: ubuntu-24.04 + steps: + - uses: actions/download-artifact@v8 + with: + path: dist + merge-multiple: true + - name: Create the release + env: + GH_TOKEN: ${{ github.token }} + TAG: ${{ github.event_name == 'push' && github.ref_name || inputs.tag }} + run: | + ls -la dist + if gh release view "$TAG" --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1; then + gh release upload "$TAG" dist/* --clobber --repo "$GITHUB_REPOSITORY" + else + gh release create "$TAG" dist/* --repo "$GITHUB_REPOSITORY" --target "$GITHUB_SHA" \ + --title "CrypTool $TAG" --generate-notes --prerelease + fi diff --git a/port/app/CMakeLists.txt b/port/app/CMakeLists.txt index 99f6c0f15..57768291c 100644 --- a/port/app/CMakeLists.txt +++ b/port/app/CMakeLists.txt @@ -141,6 +141,20 @@ set(CPACK_PACKAGE_VENDOR "CrypTool project") set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "CrypTool 1 - learn and try out cryptography and cryptanalysis") set(CPACK_PACKAGE_VERSION ${PROJECT_VERSION}) set(CPACK_RESOURCE_FILE_LICENSE ${REPO_ROOT}/COPYING) +string(TOLOWER "${CMAKE_SYSTEM_PROCESSOR}" cpack_arch) +if(cpack_arch STREQUAL "amd64") + set(cpack_arch x86_64) +elseif(cpack_arch STREQUAL "aarch64") + set(cpack_arch arm64) +endif() +if(APPLE) + set(cpack_os macos) +elseif(WIN32) + set(cpack_os windows) +else() + set(cpack_os linux) +endif() +set(CPACK_PACKAGE_FILE_NAME "CrypTool-${PROJECT_VERSION}-${cpack_os}-${cpack_arch}") if(APPLE) set(CPACK_GENERATOR DragNDrop) set(CPACK_DMG_VOLUME_NAME "CrypTool ${PROJECT_VERSION}") From 960165dcb179987f3f028c36845ed662ceeb75ef Mon Sep 17 00:00:00 2001 From: Kamil Marczak Date: Thu, 8 Oct 2026 13:14:41 +0200 Subject: [PATCH 43/46] build(app): let fixup_bundle resolve @rpath dylibs from Homebrew --- port/app/CMakeLists.txt | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/port/app/CMakeLists.txt b/port/app/CMakeLists.txt index 57768291c..56037ee11 100644 --- a/port/app/CMakeLists.txt +++ b/port/app/CMakeLists.txt @@ -125,9 +125,17 @@ install(DIRECTORY ${CRYPTOOL_DEV_DATA_DIR}/PSE DESTINATION ${CRYPTOOL_DATA_DEST} if(APPLE) # Copies the Homebrew dylibs (wxWidgets, OpenSSL, GMP) into the bundle so it runs on other Macs. + execute_process(COMMAND brew --prefix OUTPUT_VARIABLE brew_prefix OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET) + set(bundle_lib_dirs) + foreach(prefix ${brew_prefix} /opt/homebrew /usr/local) + if(EXISTS ${prefix}/lib) + list(APPEND bundle_lib_dirs ${prefix}/lib) + endif() + endforeach() + list(REMOVE_DUPLICATES bundle_lib_dirs) install(CODE " include(BundleUtilities) - fixup_bundle(\"\${CMAKE_INSTALL_PREFIX}/CrypTool.app\" \"\" \"\") + fixup_bundle(\"\${CMAKE_INSTALL_PREFIX}/CrypTool.app\" \"\" \"${bundle_lib_dirs}\") execute_process(COMMAND codesign --force --deep --sign - \"\${CMAKE_INSTALL_PREFIX}/CrypTool.app\" RESULT_VARIABLE sign_result) if(NOT sign_result EQUAL 0) From 0a6052961a70147a2d021027cf0ab2af477c73ef Mon Sep 17 00:00:00 2001 From: Kamil Marczak Date: Thu, 8 Oct 2026 14:11:41 +0200 Subject: [PATCH 44/46] build(app): drop the unused Windows package name --- port/app/CMakeLists.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/port/app/CMakeLists.txt b/port/app/CMakeLists.txt index 56037ee11..18b2d0020 100644 --- a/port/app/CMakeLists.txt +++ b/port/app/CMakeLists.txt @@ -157,8 +157,6 @@ elseif(cpack_arch STREQUAL "aarch64") endif() if(APPLE) set(cpack_os macos) -elseif(WIN32) - set(cpack_os windows) else() set(cpack_os linux) endif() From dc3ffd649b60d3ae2c0a87d717cdb39388164644 Mon Sep 17 00:00:00 2001 From: Kamil Marczak Date: Thu, 8 Oct 2026 14:14:41 +0200 Subject: [PATCH 45/46] ci(release): publish a release after every merge to main The workflow no longer runs on pull requests or tags. Each push to main builds the macOS and Linux packages and creates the release v-build. with them. --- .github/workflows/release.yml | 28 ++++++++++------------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5f507f0cc..84c1b2be5 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -2,17 +2,8 @@ name: Release on: push: - tags: ['v*'] - pull_request: - paths: - - .github/workflows/release.yml - - port/app/** - - CMakeLists.txt + branches: [main] workflow_dispatch: - inputs: - tag: - description: Tag to create the release for (created from the selected ref if it does not exist) - required: true permissions: contents: write @@ -73,22 +64,23 @@ jobs: release: needs: [macos, linux] - if: github.event_name != 'pull_request' runs-on: ubuntu-24.04 steps: - uses: actions/download-artifact@v8 with: path: dist merge-multiple: true + - uses: actions/checkout@v5 + with: + sparse-checkout: CMakeLists.txt + sparse-checkout-cone-mode: false + path: src - name: Create the release env: GH_TOKEN: ${{ github.token }} - TAG: ${{ github.event_name == 'push' && github.ref_name || inputs.tag }} run: | + version=$(sed -n 's/^project([^ ]* VERSION \([0-9.]*\).*/\1/p' src/CMakeLists.txt) + tag="v${version}-build.${GITHUB_RUN_NUMBER}" ls -la dist - if gh release view "$TAG" --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1; then - gh release upload "$TAG" dist/* --clobber --repo "$GITHUB_REPOSITORY" - else - gh release create "$TAG" dist/* --repo "$GITHUB_REPOSITORY" --target "$GITHUB_SHA" \ - --title "CrypTool $TAG" --generate-notes --prerelease - fi + gh release create "$tag" dist/* --repo "$GITHUB_REPOSITORY" --target "$GITHUB_SHA" \ + --title "CrypTool ${version} (build ${GITHUB_RUN_NUMBER})" --generate-notes From cbf0ac0f41fe34211719ab436748470bb967799d Mon Sep 17 00:00:00 2001 From: Kamil Marczak Date: Thu, 8 Oct 2026 14:16:24 +0200 Subject: [PATCH 46/46] ci(release): build the packages on pull requests, publish only from main --- .github/workflows/release.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 84c1b2be5..e68f702d1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -3,6 +3,7 @@ name: Release on: push: branches: [main] + pull_request: workflow_dispatch: permissions: @@ -64,6 +65,7 @@ jobs: release: needs: [macos, linux] + if: github.event_name != 'pull_request' runs-on: ubuntu-24.04 steps: - uses: actions/download-artifact@v8