diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 733f51f..bbb45b5 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -34,62 +34,6 @@ foreach(config Debug Release RelWithDebInfo MinSizeRel) set(CMAKE_COMPILE_PDB_OUTPUT_DIRECTORY_${config_upper} "${MSWORD_BUILD_ROOT}/pdb/${config}") endforeach() -set(MSWORD_BOOTSTRAP_SOURCES - port/winword_x64.cpp - port/legacy_asm_port.cpp - port/legacy_asm_port.h - port/winword_x64.rc - port/winword.manifest -) - -# This is a migration aid only. It is deliberately excluded from the default -# build and may never claim the final WORD1.exe product name: that name is -# reserved for the executable linked from the original Opus source graph. -add_executable(WINWORD_BOOTSTRAP WIN32 EXCLUDE_FROM_ALL ${MSWORD_BOOTSTRAP_SOURCES}) -target_compile_features(WINWORD_BOOTSTRAP PRIVATE cxx_std_20) -target_compile_definitions(WINWORD_BOOTSTRAP PRIVATE UNICODE _UNICODE WIN32_LEAN_AND_MEAN NOMINMAX) -target_include_directories(WINWORD_BOOTSTRAP PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/port") -target_link_libraries(WINWORD_BOOTSTRAP PRIVATE - comctl32 - comdlg32 - gdi32 - ole32 - shell32 - user32 -) - -if(MSVC) - target_compile_options(WINWORD_BOOTSTRAP PRIVATE /W4 /permissive- /utf-8 /EHsc /guard:cf) - # The application manifest is compiled as resource 1; disable MSVC's - # second auto-generated manifest to avoid a duplicate RT_MANIFEST entry. - target_link_options(WINWORD_BOOTSTRAP PRIVATE /DYNAMICBASE /HIGHENTROPYVA /NXCOMPAT /MANIFEST:NO) - set_property(TARGET WINWORD_BOOTSTRAP PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>DLL") -endif() - -set_target_properties(WINWORD_BOOTSTRAP PROPERTIES - OUTPUT_NAME "WINWORD_BOOTSTRAP" - RUNTIME_OUTPUT_DIRECTORY "${MSWORD_BUILD_ROOT}/bootstrap" - VS_GLOBAL_ROOTNAMESPACE "MicrosoftWordX64Port" - # The target uses only Windows system libraries. Disable Visual Studio's - # global vcpkg app-local hook so it does not invoke an unrelated pwsh.exe. - VS_GLOBAL_VcpkgApplocalDeps "false" -) -foreach(config Debug Release RelWithDebInfo MinSizeRel) - string(TOUPPER "${config}" config_upper) - set_target_properties(WINWORD_BOOTSTRAP PROPERTIES - RUNTIME_OUTPUT_DIRECTORY_${config_upper} "${MSWORD_BUILD_ROOT}/bootstrap/${config}" - ) -endforeach() - -# The original assembly remains in the repository for behavioral reference, -# but no .asm file is permitted to enter the native target. -get_target_property(winword_sources WINWORD_BOOTSTRAP SOURCES) -foreach(source IN LISTS winword_sources) - if(source MATCHES "\\.[aA][sS][mM]$") - message(FATAL_ERROR "Legacy assembly leaked into the x64 target: ${source}") - endif() -endforeach() - file(GLOB_RECURSE LEGACY_ASSEMBLY CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/*.asm") set(LEGACY_ASSEMBLY_RELATIVE) foreach(assembly_file IN LISTS LEGACY_ASSEMBLY) @@ -99,23 +43,11 @@ foreach(assembly_file IN LISTS LEGACY_ASSEMBLY) endforeach() list(SORT LEGACY_ASSEMBLY_RELATIVE) -# Extract the explicit source paths from the C++ disposition table and compare -# names, not merely counts. Removing one module and adding another cannot pass. -file(READ "${CMAKE_CURRENT_SOURCE_DIR}/port/legacy_asm_port.cpp" ASM_PORT_SOURCE) -string(REGEX MATCHALL "AssemblyPortEntry\\{\"[^\"]+\\.asm\"" ASM_PORT_ROWS "${ASM_PORT_SOURCE}") -set(ASM_PORT_PATHS) -foreach(port_row IN LISTS ASM_PORT_ROWS) - string(REGEX REPLACE "AssemblyPortEntry\\{\"([^\"]+)\"" "\\1" port_path "${port_row}") - list(APPEND ASM_PORT_PATHS "${port_path}") -endforeach() -list(SORT ASM_PORT_PATHS) - list(LENGTH LEGACY_ASSEMBLY_RELATIVE LEGACY_ASSEMBLY_COUNT) -if(NOT LEGACY_ASSEMBLY_COUNT EQUAL 59 OR - NOT "${LEGACY_ASSEMBLY_RELATIVE}" STREQUAL "${ASM_PORT_PATHS}") +if(NOT LEGACY_ASSEMBLY_COUNT EQUAL 59) message(FATAL_ERROR - "The assembly-port manifest does not exactly match the legacy source tree " - "(${LEGACY_ASSEMBLY_COUNT} modules found). Update port/legacy_asm_port.cpp." + "The original assembly reference tree changed " + "(${LEGACY_ASSEMBLY_COUNT} modules found; expected 59). Review the x64 translations." ) endif() @@ -185,6 +117,35 @@ foreach(config Debug Release RelWithDebInfo MinSizeRel) ) endforeach() +add_executable(opus_cabi_tool EXCLUDE_FROM_ALL + port/tools/opus_cabi_tool.cpp +) +target_compile_features(opus_cabi_tool PRIVATE cxx_std_20) +target_compile_definitions(opus_cabi_tool PRIVATE + WIN WIN23 CRLF NONATIVE OPUS_X64 NOMINMAX +) +target_include_directories(opus_cabi_tool PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/generated/original" + "${CMAKE_CURRENT_SOURCE_DIR}/port/original" + "${CMAKE_CURRENT_SOURCE_DIR}/Opus/lib" + "${CMAKE_CURRENT_SOURCE_DIR}/Opus" +) +if(MSVC) + target_compile_options(opus_cabi_tool PRIVATE + /J /W3 /wd4005 /permissive-) +endif() +set_target_properties(opus_cabi_tool PROPERTIES + FOLDER "Original Opus x64/build tools" + RUNTIME_OUTPUT_DIRECTORY "${MSWORD_BUILD_ROOT}/tools" + VS_GLOBAL_VcpkgApplocalDeps "false" +) +foreach(config Debug Release RelWithDebInfo MinSizeRel) + string(TOUPPER "${config}" config_upper) + set_target_properties(opus_cabi_tool PROPERTIES + RUNTIME_OUTPUT_DIRECTORY_${config_upper} "${MSWORD_BUILD_ROOT}/tools/${config}" + ) +endforeach() + # Preserve Microsoft's original BITAPP resource conversion step. These # native x64 tool builds consume the checked-in Win16 .cur files and emit the # same byte initializers that SCREEN2.C historically included. @@ -353,6 +314,18 @@ set(OPUS_GENERATED_COMMAND_FILES "${OPUS_GENERATED_COMMAND_DIR}/opuscmd.asm" "${OPUS_GENERATED_COMMAND_DIR}/opuscmd_native.inc" ) +file(GLOB OPUS_RECONSTRUCTED_DIALOG_HEADERS CONFIGURE_DEPENDS + "${CMAKE_CURRENT_SOURCE_DIR}/port/original/*.hs") +set(OPUS_GENERATED_CABI_DIR "${OPUS_GENERATED_COMMAND_DIR}/cabi") +set(OPUS_GENERATED_CABI_OPEN "${OPUS_GENERATED_CABI_DIR}/open.hs") +add_custom_command( + OUTPUT "${OPUS_GENERATED_CABI_OPEN}" + COMMAND "${CMAKE_COMMAND}" -E make_directory "${OPUS_GENERATED_CABI_DIR}" + COMMAND "$" "${OPUS_GENERATED_CABI_DIR}" + DEPENDS opus_cabi_tool ${OPUS_RECONSTRUCTED_DIALOG_HEADERS} + COMMENT "Computing native SDM CAB initializers" + VERBATIM +) add_custom_command( OUTPUT ${OPUS_GENERATED_COMMAND_FILES} COMMAND "${CMAKE_COMMAND}" -E copy_if_different @@ -368,7 +341,8 @@ add_custom_command( "${CMAKE_CURRENT_SOURCE_DIR}/Opus/resource/ibcm.rel" "${OPUS_GENERATED_COMMAND_DIR}/IBCM.H" COMMAND "$" - -T@13 -s../../../src/Opus/dlg/ opuscmd keys menus + -T@13 -s../../../src/Opus/dlg/ -bcabi/ + opuscmd keys menus COMMAND "${CMAKE_COMMAND}" -DINPUT=${OPUS_GENERATED_COMMAND_DIR}/MENUHELP.TXT -DOUTPUT=${OPUS_GENERATED_COMMAND_DIR}/menuhelp.h @@ -380,6 +354,8 @@ add_custom_command( Opus/resource/menus.cmd Opus/resource/ibcm.rel Opus/dlg/elx.txt + "${OPUS_GENERATED_CABI_OPEN}" + ${OPUS_RECONSTRUCTED_DIALOG_HEADERS} cmake/GenerateMenuHelpHeader.cmake WORKING_DIRECTORY "${OPUS_GENERATED_COMMAND_DIR}" COMMENT "Regenerating original Opus command tables with MKCMD" @@ -893,6 +869,7 @@ endif() set_target_properties(WORD1 PROPERTIES FOLDER "Original Opus x64/product" OUTPUT_NAME "${MSWORD_PRODUCT_OUTPUT_NAME}" + VS_DEBUGGER_WORKING_DIRECTORY "${MSWORD_BIN_ROOT}" VS_GLOBAL_VcpkgApplocalDeps "false" ) @@ -965,10 +942,49 @@ foreach(config Debug Release RelWithDebInfo MinSizeRel) ) endforeach() +add_executable(opus_word1_ui_test + port/original/opus_word1_ui_test.cpp +) +add_dependencies(opus_word1_ui_test WORD1) +target_compile_features(opus_word1_ui_test PRIVATE cxx_std_20) +target_compile_definitions(opus_word1_ui_test PRIVATE NOMINMAX UNICODE _UNICODE) +target_link_libraries(opus_word1_ui_test PRIVATE user32 gdi32) +if(MSVC) + target_compile_options(opus_word1_ui_test PRIVATE /W4 /utf-8) +endif() +set_target_properties(opus_word1_ui_test PROPERTIES + FOLDER "Original Opus x64/tests" + RUNTIME_OUTPUT_DIRECTORY "${MSWORD_BUILD_ROOT}/tests" + VS_GLOBAL_VcpkgApplocalDeps "false" +) +foreach(config Debug Release RelWithDebInfo MinSizeRel) + string(TOUPPER "${config}" config_upper) + set_target_properties(opus_word1_ui_test PROPERTIES + RUNTIME_OUTPUT_DIRECTORY_${config_upper} "${MSWORD_BUILD_ROOT}/tests/${config}" + ) +endforeach() + enable_testing() add_test(NAME opus_original_strtbl_test COMMAND $) add_test(NAME opus_x64_runtime_test COMMAND $) add_test(NAME opus_original_sttb_test COMMAND $) add_test(NAME opus_original_plc_test COMMAND $) +add_test(NAME opus_sdm_cab_test COMMAND $) add_test(NAME opus_original_command_test COMMAND $) add_test(NAME word1_port_smoke_test COMMAND $ --self-test) +add_test(NAME opus_word1_ui_test + COMMAND $ $ +) +set_tests_properties(opus_word1_ui_test PROPERTIES TIMEOUT 20) +add_test(NAME opus_word1_typing_test + COMMAND $ $ --typing +) +set_tests_properties(opus_word1_typing_test PROPERTIES TIMEOUT 20) +add_test(NAME opus_word1_interaction_test + COMMAND $ $ --interaction +) +set_tests_properties(opus_word1_interaction_test PROPERTIES TIMEOUT 25) +add_test(NAME opus_word1_selection_test + COMMAND $ $ --selection +) +set_tests_properties(opus_word1_selection_test PROPERTIES TIMEOUT 20) diff --git a/src/Opus/debug/debug1.c b/src/Opus/debug/debug1.c index f141c88..f705c1d 100644 --- a/src/Opus/debug/debug1.c +++ b/src/Opus/debug/debug1.c @@ -374,7 +374,7 @@ DumpGrpchr() return; } CommSz( SzShared( "\r\n" ) ); - pchr = ((char *)pchr) + pchr->chrm; + pchr = ((char *)pchr) + CbFromChrm(pchr->chrm); } } diff --git a/src/Opus/debug/debuginf.c b/src/Opus/debug/debuginf.c index f72f907..896d49e 100644 --- a/src/Opus/debug/debuginf.c +++ b/src/Opus/debug/debuginf.c @@ -1719,7 +1719,7 @@ CMB *pcmb; CommSz("\n\r"); pch = rgch; } - (char *)pchr += chrm; + (char *)pchr += CbFromChrm(chrm); } return cmdOK; } diff --git a/src/Opus/debug/debugstr.c b/src/Opus/debug/debugstr.c index 32d3490..36ff43d 100644 --- a/src/Opus/debug/debugstr.c +++ b/src/Opus/debug/debugstr.c @@ -1117,7 +1117,7 @@ NATIVE CkVfli() /* makes CkVfli visible to hand NATIVE */ iAssert = 0; /* = because chrmVanish may preceed ichMac */ Assert (vfli.fSplatBreak || pchr->ich <= vfli.ichMac); - (char *)pchr += pchr->chrm; + (char *)pchr += CbFromChrm(pchr->chrm); } iAssert = 1; Assert (vfli.doc >= 0 && vfli.doc < docMac); diff --git a/src/Opus/dialog1.c b/src/Opus/dialog1.c index 17d8fc5..5f0ea53 100644 --- a/src/Opus/dialog1.c +++ b/src/Opus/dialog1.c @@ -155,7 +155,11 @@ CMB * pcmb; SetBytes(&dli, 0, sizeof (DLI)); dli.fdlg = fdlgModal | fdlgFedt; +#ifdef OPUS_X64 + dli.wRef = (UINT_PTR)pcmb; +#else dli.wRef = (WORD) pcmb; +#endif /* this returns from this func if there is not enough stack for AllocA below */ ReturnOnNoStack(cb, tmcError, fTrue); @@ -771,7 +775,13 @@ SuccessCCP: return(1L); case EM_SETSEL: +#ifdef OPUS_X64 + /* Win32 and later pass the start in wParam and the end in lParam. + * The Win16 edit message packed both 16-bit values into lParam. */ + FedtSetSel(hfedt, (int) wParam, (int) lParam); +#else FedtSetSel(hfedt, LOWORD(lParam), HIWORD(lParam)); +#endif FedtReleaseDC(hfedt); break; diff --git a/src/Opus/disp1.c b/src/Opus/disp1.c index 0c7784d..0d1f723 100644 --- a/src/Opus/disp1.c +++ b/src/Opus/disp1.c @@ -956,7 +956,7 @@ int dxpToXw, ywLine; ptPen.xp = xpNew; ptPen.yp = ypNew; fNoSkip = vfli.fPrint; - (char *) pchr += chrmFormula; + (char *) pchr += cbCHRF; } else if (chrm == chrmFormatGroup) { @@ -971,7 +971,7 @@ int dxpToXw, ywLine; } /* note that rcOpaque and rcErase will no longer change (rcErase not used) */ - (char *) pchr += chrmFormatGroup; + (char *) pchr += cbCHRFG; } else /*if (chrm == chrmEnd)*/ @@ -1718,8 +1718,8 @@ struct RC *prcwClip; else if (chrm == chrmEnd) break; - bchrCur += chrm; - (char *)pchr += chrm; + bchrCur += CbFromChrm(chrm); + (char *)pchr += CbFromChrm(chrm); } /* draw CRJ symbol if needed */ diff --git a/src/Opus/formula.c b/src/Opus/formula.c index 03a9aef..7143248 100644 --- a/src/Opus/formula.c +++ b/src/Opus/formula.c @@ -141,8 +141,9 @@ int *pbchr; { struct CHR *pchr; int bchr = vbchrMac; - if ((vbchrMac += chrm) > vbchrMax) - if (!FExpandGrpchr(chrm)) + int cb = CbFromChrm(chrm); + if ((vbchrMac += cb) > vbchrMax) + if (!FExpandGrpchr(cb)) return 0; pchr = &((**vhgrpchr)[*pbchr = bchr]); pchr->ich = ich; @@ -887,12 +888,12 @@ fraction line is dypFract above the base line (func of hps of *f command) if (vprsu.prid == pridLaser && vfli.fPrint) dypChSym = dypChSym * 9 / 8; #endif /* MAC */ - PchpSetChrDyp(pfma1->bchr - chrmChp, + PchpSetChrDyp(pfma1->bchr - cbCHR, dypChSym, chRootExt, fTrue)->fSpec = fTrue; FFormatChSpecSymbol( - PchpSetChrDyp((bchr = pfma1->bchr - chrmChp - chrmFormula - chrmChp), + PchpSetChrDyp((bchr = pfma1->bchr - cbCHR - cbCHRF - cbCHR), dypChSym, chRoot, fTrue), chRoot, &dxpChSym, &dxtChSym, @@ -915,7 +916,7 @@ fraction line is dypFract above the base line (func of hps of *f command) dytAscent = dytAscentCh1 - dyt2; /* update CHRF to goto start of root symbol */ - SetChrf(bchr - chrmFormula, dxp3 - dxpChSym, + SetChrf(bchr - cbCHRF, dxp3 - dxpChSym, dyp2, fFalse); #ifdef MAC /* win does not have prid */ @@ -924,7 +925,7 @@ same place as the root symbol and also ends up there. On other printers, extension is drawn with width dxp2 after the root symbol */ if (vprsu.prid == pridLaser && vfli.fPrint) { - SetChrf(bchr + chrmChp, - dxpChSym, + SetChrf(bchr + cbCHR, - dxpChSym, 0, fFalse); dxpLarger = dxpChSym; /* move right from ext to operand */ } @@ -1033,7 +1034,7 @@ On other printers, extension is drawn with width dxp2 after the root symbol */ Assert(pchrf->chrm == chrmFormula); /* for fixing up bounding symbols */ - bchr -= chrmChp + chrmFormula; + bchr -= cbCHR + cbCHRF; ich = pfma1->ich - 1; ch1 = pfma1->chLeft; @@ -1049,11 +1050,11 @@ On other printers, extension is drawn with width dxp2 after the root symbol */ { if (ch2 != 0) { - bchr -= chrmChp + chrmFormula; + bchr -= cbCHR + cbCHRF; ich--; } FFormatChSpecSymbol( - PchpSetChrDyp(bchr + chrmFormula, dyp1, ch1, fTrue), + PchpSetChrDyp(bchr + cbCHRF, dyp1, ch1, fTrue), ch1, &dxp2, &dxt2, &dypAscentCh1, &dypDescentCh1, &dytAscentCh1, &dytDescentCh1); SetChrf(bchr, 0, dyp2 = @@ -1065,7 +1066,7 @@ On other printers, extension is drawn with width dxp2 after the root symbol */ (dytDescentCh1 + dytAscentCh1 - dyt1) / 2; vfli.rgdxp[ich++] = dxp2; - bchr += chrmChp + chrmFormula; + bchr += cbCHR + cbCHRF; } /* right symbol */ @@ -1078,7 +1079,7 @@ On other printers, extension is drawn with width dxp2 after the root symbol */ else { FFormatChSpecSymbol( - PchpSetChrDyp(bchr + chrmFormula, dyp1, ch2, fTrue), + PchpSetChrDyp(bchr + cbCHRF, dyp1, ch2, fTrue), ch2, &dxp3, &dxt3, &dypAscentCh2, &dypDescentCh2, &dytAscentCh2, &dytDescentCh2); SetChrf(bchr, dxp1, -dyp2 + (dyp3 = @@ -1142,7 +1143,7 @@ On other printers, extension is drawn with width dxp2 after the root symbol */ dyt3 = pfmaEnd->dytAscent + pfmaEnd->dytDescent; /* calculate size of symbol */ - bchr = pfma1->bchr - chrmChp; + bchr = pfma1->bchr - cbCHR; ch1 = pfma1->chLeft; FFormatChSpecSymbol( @@ -1211,7 +1212,7 @@ On other printers, extension is drawn with width dxp2 after the root symbol */ dxtLarger = max(dxt1, dxt2); /* goto start of symbol */ - SetChrf(bchr - chrmFormula, 0, + SetChrf(bchr - cbCHRF, 0, dyp3, fFalse); @@ -1255,7 +1256,7 @@ On other printers, extension is drawn with width dxp2 after the root symbol */ dxt1 / 2 )) - dxtChSym / 2; /* goto start of symbol */ - SetChrf(bchr - chrmFormula, + SetChrf(bchr - cbCHRF, dxpLarger, dyp3, fFalse); diff --git a/src/Opus/help.c b/src/Opus/help.c index 91c1ebb..bd3ad78 100644 --- a/src/Opus/help.c +++ b/src/Opus/help.c @@ -108,8 +108,13 @@ extern int vwWinVersion; extern CHAR szNone[]; extern struct PREF vpref; extern long vcmsecHelp; +#ifdef OPUS_X64 +extern WPARAM vwParamHelp; +extern LPARAM vlParamHelp; +#else extern WORD vwParamHelp; extern LONG vlParamHelp; +#endif extern int wwCur; extern int mwCur; extern int vlm; @@ -137,12 +142,21 @@ extern struct FB vfbReplace; extern int far pascal initwin_q(); +#ifdef OPUS_X64 +int ImnuFromHMenu(HMENU, UINT_PTR); +#endif + /* G L O B A L S */ long vcmsecHelp = 0L; /* Time (in msec) to invalidate */ /* double-clicking for help mode. */ +#ifdef OPUS_X64 +WPARAM vwParamHelp; /* Saved native WM_MENUSELECT parameters */ +LPARAM vlParamHelp = 0; +#else WORD vwParamHelp; /* Saved for cxt computation */ LONG vlParamHelp = 0; /* Saved for cxt computation */ +#endif HWND vhwndCBT = NULL; /* CBT application window */ BOOL vfHelp = fFalse; /* whether we're in Help mode */ int vcxtHelp = cxtNil; /* current context */ @@ -581,17 +595,30 @@ HWND hwnd; one of the drop-downs). It is cxtDocMenuSelect if we have hilighted something in the Doc system menu. */ +#ifdef OPUS_X64 +int CxtMenuSelect(WPARAM wParam, LPARAM lParam, int cxt) +#else int CxtMenuSelect(wParam, lParam, cxt) WORD wParam; LONG lParam; int cxt; +#endif { +#ifdef OPUS_X64 + int mf = HIWORD(wParam); + WORD item = LOWORD(wParam); +#else int mf = LOWORD(lParam); +#endif Assert(cxt == cxtAppMenuSelect || cxt == cxtDocMenuSelect); /* this case should have been taken care of in SaveMenuHelpContext */ +#ifdef OPUS_X64 + Assert(lParam != 0); +#else Assert (HIWORD(lParam) != 0 && wParam != 0); +#endif /* Application or Document system menu */ if (mf & MF_SYSMENU) @@ -605,8 +632,13 @@ int cxt; the SC_ code of the menu item */ { return ((cxt == cxtAppMenuSelect) ? +#ifdef OPUS_X64 + CxtAppSysMenu(item) : + CxtDocSysMenu(item)); +#else CxtAppSysMenu(wParam) : CxtDocSysMenu(wParam)); +#endif } } @@ -614,7 +646,13 @@ int cxt; /* HIWORD is the main menu handle; wParam is the submenu handle */ if (mf & MF_POPUP) { +#ifdef OPUS_X64 + HMENU hMenu = (HMENU) lParam; + HMENU hPopup = GetSubMenu(hMenu, item); + int imnu = ImnuFromHMenu(hMenu, (UINT_PTR) hPopup); +#else int imnu = ImnuFromHMenu(HIWORD(lParam), wParam); +#endif #ifdef DEBUG #define imnuHelp 11 /* stolen from menuhelp.c */ /* give the right context for Help menu in debug version (debug @@ -631,7 +669,11 @@ int cxt; { /* MF_SYSMENU && !MF_POPUP case handled in CmdHelp */ Assert (!(mf & MF_SYSMENU)); +#ifdef OPUS_X64 + return (CxtFromBcm(item)); +#else return (CxtFromBcm(wParam)); +#endif } } @@ -748,8 +790,13 @@ CMD CmdHelp(pcmb) CMB *pcmb; { int cxt = vcxtHelp; /* because EndMenu can blow away vcxtHelp */ +#ifdef OPUS_X64 + WPARAM wParamSav = vwParamHelp; /* EndMenu resets these globals */ + LPARAM lParamSav = vlParamHelp; +#else WORD wParamSav = vwParamHelp; /* EndMenu resets these globals */ LONG lParamSav = vlParamHelp; +#endif EndMenu(); /* end menu mode if we're in it */ diff --git a/src/Opus/lib/sdm.h b/src/Opus/lib/sdm.h index 748ad10..77d4bf1 100644 --- a/src/Opus/lib/sdm.h +++ b/src/Opus/lib/sdm.h @@ -401,7 +401,11 @@ typedef struct _dli // DiaLog Initializer. HWND hwnd; int dx, dy; FDLG fdlg; +#ifdef OPUS_X64 + UINT_PTR wRef; +#else WORD wRef; +#endif BYTE * rgb; // App-supplied rgtmw (in sbDlg). #ifdef SDM_MULTI_SB @@ -419,7 +423,11 @@ typedef struct _dli // DiaLog Initializer. /////////////////////////////////////////////////////////////////////////////// // Misc Functions . +#ifdef OPUS_X64 +extern UINT_PTR PASCAL wRefDlgCur; // Cached pointer-sized value. +#else extern WORD PASCAL wRefDlgCur; // Cached value. +#endif extern HCAB PASCAL hcabDlgCur; // Cached value. #define WRefDlgCur() wRefDlgCur @@ -743,7 +751,11 @@ typedef struct _dlh // of mess in the sdm .c files. #endif //!SDM_ENV_PM +#ifdef OPUS_X64 + UINT_PTR wRef; // User supplied Dialog pointer/value. +#else WORD wRef; // User supplied Dialog word. +#endif FDLG fdlg; // Dialog flags. WORD hid; // Help ID. } DLH; // Public part of DLG structure. diff --git a/src/Opus/menuhelp.c b/src/Opus/menuhelp.c index 5e96a9a..5c056e6 100644 --- a/src/Opus/menuhelp.c +++ b/src/Opus/menuhelp.c @@ -32,6 +32,10 @@ extern CHAR vstPrompt[]; extern int vfSysMenu; char *PchGetStr(); +#ifdef OPUS_X64 +int ImnuFromHMenu(HMENU, UINT_PTR); +#endif + /* This file is generated by a series of processes from OpusCmd.CMD * it contains the help prompt strings in compressed form. */ @@ -103,10 +107,14 @@ csconst char csrgstMenuBarHelp [] [] = /* D R A W M E N U H E L P */ /* Called from WM_MENUSELECT handler... */ /* %%Function:DrawMenuHelp %%Owner:krishnam */ +#ifdef OPUS_X64 +void DrawMenuHelp(HMENU hMenu, UINT_PTR bcm, int mf) +#else DrawMenuHelp(hMenu, bcm, mf) HMENU hMenu; BCM bcm; int mf; +#endif { char FAR * lpst; int cch; @@ -153,11 +161,11 @@ int mf; goto LHaveString; } else if (mf & MF_SYSMENU) - bcm = BcmFromSysMenu(bcm); + bcm = BcmFromSysMenu((WORD) bcm); { CHAR szT[256]; - GetMenuHelpSz(bcm, szT); + GetMenuHelpSz((BCM) bcm, szT); SzToStInPlace(szT); *szT = min (*szT, cchPromptMax-1); CopySt (szT, vstPrompt); @@ -169,8 +177,12 @@ LHaveString: /* %%Function:ImnuFromMenu %%Owner:krishnam */ +#ifdef OPUS_X64 +int ImnuFromHMenu(HMENU hMenu, UINT_PTR bcm) +#else int ImnuFromHMenu(hMenu, bcm) HMENU hMenu; +#endif { int imnu = 0; HMENU hMenuT; @@ -180,7 +192,7 @@ HMENU hMenu; while ((hMenuT = GetSubMenu(vhMenu, imnu++)) != NULL) { - if (hMenuT == bcm) + if ((UINT_PTR) hMenuT == (UINT_PTR) bcm) { if (!vfSysMenu) imnu++; diff --git a/src/Opus/pic.c b/src/Opus/pic.c index 2e872e1..c32738b 100644 --- a/src/Opus/pic.c +++ b/src/Opus/pic.c @@ -511,7 +511,7 @@ DYY *pdyyAscent, *pdyyDescent; Profile( vpfi == pfiPictFmt ? StartProf( 30) : 0); /* find the CP for the picture, taking vanished text into account */ - for (vfmtss.cpRef = vfli.cpMin + ich; bchr < vbchrMac; bchr += pchr->chrm) + for (vfmtss.cpRef = vfli.cpMin + ich; bchr < vbchrMac; bchr += CbFromChrm(pchr->chrm)) { pchr = (char *) **vhgrpchr + bchr; if (pchr->ich > ich) diff --git a/src/Opus/preview.c b/src/Opus/preview.c index c195ac2..bf2bc1a 100644 --- a/src/Opus/preview.c +++ b/src/Opus/preview.c @@ -477,6 +477,8 @@ LEndPaint: case WM_VSCROLL: { + int sbMessage; + int posNew; /* Deals with mouse activity in vertical scroll bar. Jumps by 1 or 2 pages (depending on vpvs.fFacing flag) unless thumb is manually moved, in which case the @@ -491,10 +493,18 @@ LEndPaint: goto LRet; } - switch (wParam) +#ifdef OPUS_X64 + sbMessage = LOWORD(wParam); + posNew = HIWORD(wParam); +#else + sbMessage = wParam; + posNew = LOWORD(lParam); +#endif + + switch (sbMessage) { case SB_THUMBPOSITION: - fFail = !FGotoPagePrvw(hdc,LOWORD(lParam),dipgdRandom); + fFail = !FGotoPagePrvw(hdc, posNew, dipgdRandom); break; case SB_THUMBTRACK: @@ -507,10 +517,10 @@ LEndPaint: struct PLCPGD **hplcpgd = PdodDoc(vpvs.docPrvw)->hplcpgd; CP cpThumb; - if (LOWORD(lParam) == vpvs.ypLastScrlBar) + if (posNew == vpvs.ypLastScrlBar) cpThumb= cpMac-1; else - cpThumb = (cpMac*LOWORD(lParam))/(vpvs.ypLastScrlBar); + cpThumb = (cpMac*posNew)/(vpvs.ypLastScrlBar); if (hplcpgd == hNil || (ipgd=IInPlcCheck(hplcpgd,cpThumb)) < 0) ipgd = 0; if (ipgd != ipgdLast) diff --git a/src/Opus/print2.c b/src/Opus/print2.c index a0a41f3..4e646ff 100644 --- a/src/Opus/print2.c +++ b/src/Opus/print2.c @@ -1868,7 +1868,7 @@ struct RC *prc; pdxp = &vfli.rgdxp[0]; ich = 0; FreezeHp(); - for (pchr = &(**vhgrpchr)[0]; ; (char *)pchr += chrm) + for (pchr = &(**vhgrpchr)[0]; ; (char *)pchr += CbFromChrm(chrm)) { for (ichNext = pchr->ich; cpT < cpPic && ich < ichNext; ich++, cpT++) xp += *pdxp++; diff --git a/src/Opus/raremsg.c b/src/Opus/raremsg.c index 1481a8e..edc3919 100644 --- a/src/Opus/raremsg.c +++ b/src/Opus/raremsg.c @@ -227,11 +227,15 @@ int n1, n2; /* %%Function:AppWndProcRare %%Owner:chic */ +#ifdef OPUS_X64 +LRESULT AppWndProcRare(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +#else long AppWndProcRare(hwnd, message, wParam, lParam) HWND hwnd; int message; WORD wParam; LONG lParam; +#endif { HDLG hdlg; LPPOINT rgpt; @@ -328,7 +332,11 @@ LONG lParam; EnsureFocusInPane(); if (vfRecording && fRecordMove) +#ifdef OPUS_X64 + RecordAppMove((short) LOWORD(lParam), (short) HIWORD(lParam)); +#else RecordAppMove(LOWORD(lParam), HIWORD(lParam)); +#endif fRecordMove = fFalse; defproc: @@ -449,7 +457,11 @@ defproc: InvalidateRect(wParam, (LPRECT) NULL, fTrue); break; } +#ifdef OPUS_X64 + PaintClipboard((HWND) wParam, (HANDLE) lParam); +#else PaintClipboard( wParam, LOWORD(lParam) ); +#endif break; case WM_VSCROLLCLIPBOARD: @@ -484,7 +496,11 @@ defproc: if (vrf.fInDisplayFli) break; +#ifdef OPUS_X64 + SizeClipboard((HWND) wParam, (HANDLE) lParam); +#else SizeClipboard( wParam, LOWORD(lParam) ); +#endif break; case WM_ASKCBFORMATNAME: diff --git a/src/Opus/splitter.c b/src/Opus/splitter.c index f99a32d..50e44d1 100644 --- a/src/Opus/splitter.c +++ b/src/Opus/splitter.c @@ -706,8 +706,8 @@ long lParam; "FSplitBoxMouse"); break; } - pt.xp = LOWORD(lParam); - pt.yp = HIWORD(lParam); + pt.xp = (short) LOWORD(lParam); + pt.yp = (short) HIWORD(lParam); ClientToScreen(hwnd, (LPPOINT) &pt ); TrackSplitBar( pt, wk ); if ((wk == wkFtn || wk == wkAtn) && PwwdWw(wwCur)->wk == wk) diff --git a/src/Opus/wordtech/disp.h b/src/Opus/wordtech/disp.h index e5f596b..ca5cb98 100644 --- a/src/Opus/wordtech/disp.h +++ b/src/Opus/wordtech/disp.h @@ -17,6 +17,19 @@ struct EDL union { int grpfEdl; struct { +#ifdef OPUS_X64 + /* Win16 packed dcpDepend and the EDL flags into one word. A + * byte followed by int bitfields starts a second allocation unit + * under MSVC x64, so grpfEdl = 0 did not clear dlk and the other + * flags. Keep the original four-byte overlay explicitly. */ + unsigned int dcpDepend : 8; + unsigned int dlk : 3; + unsigned int fDirty : 1; + unsigned int fHasBitmap : 1; + unsigned int fTableDirty : 1; + unsigned int fColorMFP : 1; + unsigned int fNeedEnhance : 1; +#else unsigned char dcpDepend; int dlk : 3; int fDirty : 1; @@ -24,8 +37,18 @@ struct EDL int fTableDirty : 1; int fColorMFP: 1; /* color metafile (WIN) */ int fNeedEnhance: 1; /* postponed display (WIN) */ +#endif }; struct { +#ifdef OPUS_X64 + unsigned int : 8; + unsigned int fEnd : 1; + unsigned int fEndPage : 1; + unsigned int fEndDr : 1; + unsigned int : 1; + unsigned int fRMark : 1; + unsigned int : 3; +#else int : 8; /* set iff edl is the endmark. Other fields are then: cp = cpMac of doc, dcpMac = 0 */ @@ -39,6 +62,7 @@ cp = cpMac of doc, dcpMac = 0 */ /**/ int fRMark : 1; int : 3; +#endif }; }; struct { diff --git a/src/Opus/wordtech/format.c b/src/Opus/wordtech/format.c index fdba12c..fb1352c 100644 --- a/src/Opus/wordtech/format.c +++ b/src/Opus/wordtech/format.c @@ -2883,7 +2883,7 @@ LEndWord: cpChr = cpT; } ichChr = pchr->ich; - *pbchrBreak += chrm; + *pbchrBreak += CbFromChrm(chrm); } /* now ichLim is in the "break" run */ vfli.cpMac = cpChr + ichLim - ichChr; @@ -3010,7 +3010,7 @@ else if (chrm == chrmFormula) #endif if (chrm == chrmEnd) break; -(char *)pchr += chrm; +(char *)pchr += CbFromChrm(chrm); } if ((CP) ich >= dcp) { diff --git a/src/Opus/wordtech/format.h b/src/Opus/wordtech/format.h index 7dad4b9..9db33ee 100644 --- a/src/Opus/wordtech/format.h +++ b/src/Opus/wordtech/format.h @@ -335,10 +335,30 @@ struct CHRFG #define cbCHRFG sizeof(struct CHRFG) +/* The original 16-bit layout used each structure's byte length as its type +tag, which made grpchr records self-scanning. Native x64 alignment makes +some otherwise unrelated records the same size (CHR and CHRDF are both 32 +bytes), so the x64 port uses explicit unique tags and maps them back to their +storage lengths while scanning. */ +#ifdef OPUS_X64 +#define chrmEnd 1 +#define chrmChp 2 +#define chrmTab 3 +#define chrmVanish 4 +#define chrmFormula 5 +#define chrmDisplayField 6 +#define chrmFormatGroup 7 +#define CbFromChrm(chrm) \ + ((chrm) == chrmChp ? cbCHR : \ + (chrm) == chrmTab ? cbCHRT : \ + (chrm) == chrmVanish ? cbCHRV : \ + (chrm) == chrmFormula ? cbCHRF : \ + (chrm) == chrmDisplayField ? cbCHRDF : \ + (chrm) == chrmFormatGroup ? cbCHRFG : cbCHRE) +#else /* chrm's are also used as the lengths of the structures so that grpchr can be scanned easily. Note that in case of cbCHRT = cbCHRV we fudge the numbers so that they come out all different */ - #define chrmChp cbCHR /* 14(mac) 18(win) */ #define chrmTab cbCHRT /* 4 */ #define chrmVanish cbCHRV /* 6 */ @@ -348,6 +368,8 @@ numbers so that they come out all different */ /* the chrmEnd type chr has no separate definition, we just use the first two bytes of a chr */ #define chrmEnd cbCHRE /* 2 */ +#define CbFromChrm(chrm) (chrm) +#endif #ifdef MAC @@ -396,7 +418,11 @@ typedef union { int dyp; int dyt; }; +#ifdef OPUS_X64 + long long wlAll; /* covers both native 32-bit metric fields */ +#else long wlAll; +#endif } DYY; #else typedef union { diff --git a/src/Opus/wordtech/insert.c b/src/Opus/wordtech/insert.c index c4c4200..df2e043 100644 --- a/src/Opus/wordtech/insert.c +++ b/src/Opus/wordtech/insert.c @@ -1403,6 +1403,11 @@ LNoHyphen: } else { + /* Appending just beyond the terminal display PLC must dirty the DR + * itself, not only the WWD. InvalCp3 deliberately ignores ranges + * beyond the currently displayed cpMac, so without this the window + * is later marked clean with no EDL for the new paragraph. */ + pdrT->fDirty = fTrue; FreePdrf(&drfFetch); InvalCp(PcaSet(&caT, selCur.doc, cpMinInval, cpMinInval + 1)); UpdateWw(wwCur, fFalse); @@ -1636,6 +1641,14 @@ not following in cp space (yp space already checked above) */ /* next line is invalid if it exists (= IMacPlc(hplcedl) && cpInsNew >= vfli.cpMac) || +#endif (dl < IMacPlc(hplcedl) && ((GetPlc(hplcedl, dl, &edl), edl.fDirty) || edl.ypTop != ypTop + vfli.dypLine)) || diff --git a/src/Opus/wordtech/scc.h b/src/Opus/wordtech/scc.h index a2f3e6d..993b8cd 100644 --- a/src/Opus/wordtech/scc.h +++ b/src/Opus/wordtech/scc.h @@ -133,26 +133,40 @@ struct SCC }; struct CA ca; }; - int dcpDepend : 8; + unsigned char dcpDepend; #ifdef OPUS_X64 int fDirtyDr : 1; #else int fDirty : 1; #endif int fCpBad : 1; - int fLimSuspect: 1; - int : 5; + int fLimSuspect : 1; + int fNoParaStart : 1; + int fIncomplete : 1; + int fInTable : 1; + int fBottomTableFrame : 1; + int fForceWidth : 1; struct PLCEDL **hplcedl; int dypAbove; - int dxpOutLeft; - int dxpOutRight; - int idrFlow; /* text should flow to this dr */ - int lrk; /* type of lr from which this came */ - int fNoPrev : 1; /* redundant bit: 1 means there is no - dr with this dr as successor */ - int fConstrainLeft : 1; /* constrained by abs on left */ - int fConstrainRight : 1; /* on right */ - int wSpare1 : 13; /* spare for future use */ + int dxpOutLeft; + int dxpOutRight; + int idrFlow; /* text should flow to this dr */ + char ccolM1; + char lrk; /* type of lr from which this came */ + int fNoPrev : 1; /* redundant bit: 1 means there is no + dr with this dr as successor */ + int fConstrainLeft : 1; /* constrained by abs on left */ + int fConstrainRight : 1; /* on right */ + int fNewColOnly : 1; + int fFatLine : 1; + int fCantGrow : 1; + int fHdrFtr : 1; + int fForceFirstRow : 1; + int fRMark : 1; + int fSpaceBefore : 1; + int : 6; + int xwLimScroll; + int dxaBetween; /* Note: the DR definition now includes a PLCEDL header, but this is only for far PLCEDL's. Since the SCC is using a near PLCEDL defined at the @@ -212,6 +226,12 @@ struct SCC }; }; +#ifdef OPUS_X64 +typedef char OPUS_SCC_HPLCEDL_OFFSET_MUST_MATCH_DR[ + (offsetof(struct SCC, hplcedl) - offsetof(struct SCC, rgdr) == + offsetof(struct DR, hplcedl)) ? 1 : -1]; +#endif + #define cwSCC (sizeof (struct SCC) / sizeof (int)) #define cbBmbAbove 20000 diff --git a/src/Opus/wordtech/sel.h b/src/Opus/wordtech/sel.h index 3c5bf9e..049a9ac 100644 --- a/src/Opus/wordtech/sel.h +++ b/src/Opus/wordtech/sel.h @@ -23,21 +23,21 @@ struct SEL { union { struct { - int : 10; - int sk : 6; + unsigned int : 10; + unsigned int sk : 6; }; struct { - int fRightward : 1; - int fSelAtPara: 1; - int fWithinCell : 1; - int fTableAnchor : 1; - int : 6; - int fColumn : 1; - int fTable : 1; - int fGraphics : 1; - int fBlock : 1; - int fNil : 1; - int fIns : 1; + unsigned int fRightward : 1; + unsigned int fSelAtPara: 1; + unsigned int fWithinCell : 1; + unsigned int fTableAnchor : 1; + unsigned int : 6; + unsigned int fColumn : 1; + unsigned int fTable : 1; + unsigned int fGraphics : 1; + unsigned int fBlock : 1; + unsigned int fNil : 1; + unsigned int fIns : 1; }; }; unsigned char fForward; @@ -89,22 +89,22 @@ struct SEL int dyp; /* tick at which cursor was last blinked */ long tickOld; - int fUpdateChp : 1; - int fUpdateChpGray : 1; - int fUpdatePap : 1; - int fUpdateRibbon : 1; - int fUpdateRuler : 1; - int fUpdateStatLine : 1; - int fUpdateStatLine2: 1; + unsigned int fUpdateChp : 1; + unsigned int fUpdateChpGray : 1; + unsigned int fUpdatePap : 1; + unsigned int fUpdateRibbon : 1; + unsigned int fUpdateRuler : 1; + unsigned int fUpdateStatLine : 1; + unsigned int fUpdateStatLine2: 1; #ifdef MAC - int fNinchChp : 1; - int fNinchPap : 1; - int fNinchSep : 1; - int fItalic : 1; - int : 5; + unsigned int fNinchChp : 1; + unsigned int fNinchPap : 1; + unsigned int fNinchSep : 1; + unsigned int fItalic : 1; + unsigned int : 5; #else - int : 9; + unsigned int : 9; #endif /* MAC */ struct RC rcwClip; struct CHP chp; diff --git a/src/Opus/wordtech/select.c b/src/Opus/wordtech/select.c index aae28ab..6d4d48b 100644 --- a/src/Opus/wordtech/select.c +++ b/src/Opus/wordtech/select.c @@ -957,7 +957,7 @@ over xp, depending whichever is closer to the middle point of the char */ break; } /* note how chrm is cb of the variant chr structure! */ - (char *)pchr += chrm; + (char *)pchr += CbFromChrm(chrm); } LHaveCp: diff --git a/src/Opus/wordtech/word.h b/src/Opus/wordtech/word.h index 331edbd..db5af8b 100644 --- a/src/Opus/wordtech/word.h +++ b/src/Opus/wordtech/word.h @@ -107,6 +107,14 @@ typedef IB HUGE * HQ; /* for source compatibilty with Opus huge block manipulations */ struct BPS HUGE *HpbpsIbp(); +#ifdef OPUS_X64 +/* Native replacement for FILEWINN.ASM. This must be declared because an + * implicit-int call truncates the flat cache-page pointer on AMD64. */ +CHAR HUGE *HpOfBptbExt(int ibp); +/* DEBUG.H remaps HpchGetPn calls to the translated native entry. Declare + * that entry before the remap so C callers retain the full AMD64 pointer. */ +CHAR HUGE *N_HpchGetPn(int fn, int pn); +#endif diff --git a/src/Opus/wordwin.h b/src/Opus/wordwin.h index cb0f29a..c2c6478 100644 --- a/src/Opus/wordwin.h +++ b/src/Opus/wordwin.h @@ -644,7 +644,7 @@ struct PREF int fShowAllMacros: 1 ; int : 1 ; /* spare */ /* used in customize dialog */ - int iAS: 2; /* Auto Save frequency index. */ + unsigned int iAS: 2; /* Auto Save frequency index (0..3). */ int fDraftView: 1; int fZoomApp: 1; diff --git a/src/Opus/wproc.c b/src/Opus/wproc.c index 5f1ae67..c033961 100644 --- a/src/Opus/wproc.c +++ b/src/Opus/wproc.c @@ -316,8 +316,13 @@ extern HWND vhwndCBT; extern int vcxtHelp; /* help context id */ extern BOOL vfHelp; /* whether we're in Help Mode */ extern long vcmsecHelp; +#ifdef OPUS_X64 +extern WPARAM vwParamHelp; +extern LPARAM vlParamHelp; +#else extern WORD vwParamHelp; extern LONG vlParamHelp; +#endif extern HCURSOR vhcHourGlass; extern HCURSOR vhcIBeam; extern HCURSOR vhcArrow; @@ -390,7 +395,25 @@ extern int vfNoInval; void MwdVertScroll( int, WORD, int ); void MwdHorzScroll( int, WORD, int ); void WwPaneMouse( int, unsigned, WORD, POINT ); +#ifdef OPUS_X64 +LRESULT LMenuChar(HWND, WPARAM, LPARAM); +void DrawMenuHelp(HMENU, UINT_PTR, int); +void SaveMenuHelpContext(WPARAM, LPARAM, int); + +/* + * Opus used the sign bit of a Win16 WORD-sized int to distinguish its + * command ids from the SC_* values in WM_SYSCOMMAND. A Win64 int is 32 + * bits, so casting WPARAM to int makes every SC_* value positive and causes + * Word to swallow window move/size/close commands as application commands. + */ +#define FOpusCommandWParam(wParam) ((short) LOWORD(wParam) >= 0) +#define BcmFromWParam(wParam) ((WORD) LOWORD(wParam)) +#define WM_OPUS_X64_QUERY_SELECTION (WM_APP + 0x351) +#else LONG LMenuChar(HWND, WORD, LONG); +#define FOpusCommandWParam(wParam) ((int) (wParam) >= 0) +#define BcmFromWParam(wParam) (wParam) +#endif #ifdef DEBUG @@ -750,12 +773,21 @@ LONG lParam; { static int fEnteredIdle=fTrue; static HMENU hMenu; +#ifdef OPUS_X64 + static UINT_PTR bcm; +#else static int bcm; +#endif static int mf; int cxt; +#ifdef OPUS_X64 + LRESULT AppWndProcRare(HWND, UINT, WPARAM, LPARAM); + LRESULT AppWndProcAct(HWND, UINT, WPARAM, LPARAM); +#else long AppWndProcRare(); long AppWndProcAct(); +#endif struct RC rc; @@ -799,7 +831,7 @@ LONG lParam; /* CBT hook must be the first thing we do under WM_COMMAND */ if (vhwndCBT && !SendMessage(vhwndCBT, WM_CBTSEMEV, smvCommand, - MAKELONG(CxtFromBcm(wParam), 0 /* from menu */))) + MAKELONG(CxtFromBcm(BcmFromWParam(wParam)), 0 /* from menu */))) { return (0L); } @@ -813,12 +845,12 @@ LONG lParam; RestorePrompt(); - if (wParam == bcmNil) /* menu empty */ + if (BcmFromWParam(wParam) == bcmNil) /* menu empty */ return (0L); - Assert((int) wParam >= 0); + Assert(FOpusCommandWParam(wParam)); vcxtHelp = cxtNil; - FExecCmd(wParam); + FExecCmd(BcmFromWParam(wParam)); break; case WM_SYSCOMMAND: @@ -887,9 +919,9 @@ LONG lParam; Profile(vpfi == pfiMenu ? StartProf(30) : 0); EnsureFocusInPane(); vcxtHelp = cxtNil; - if ((int) wParam >= 0) + if (FOpusCommandWParam(wParam)) { - CmdExecBcmKc(wParam, kcNil); + CmdExecBcmKc(BcmFromWParam(wParam), kcNil); } else { @@ -955,9 +987,17 @@ LDefProc: if (vhwndStatLine) { +#ifdef OPUS_X64 + hMenu = (HMENU) lParam; + mf = HIWORD(wParam); + bcm = (mf & MF_POPUP) && hMenu != NULL ? + (UINT_PTR) GetSubMenu(hMenu, LOWORD(wParam)) : + (UINT_PTR) LOWORD(wParam); +#else hMenu = HIWORD(lParam); bcm = wParam; mf = LOWORD(lParam ); +#endif if (hMenu == 0 && fEnteredIdle) DrawMenuHelp(hMenu, bcm, mf); fEnteredIdle = fFalse; @@ -1026,9 +1066,17 @@ LONG lParam; extern int mwCreate; static int fEnteredIdle=fTrue; static HMENU hMenu; +#ifdef OPUS_X64 + static UINT_PTR bcm; +#else static int bcm; +#endif static int mf; +#ifdef OPUS_X64 + LRESULT MwdWndProcRare(HWND, UINT, WPARAM, LPARAM); +#else long MwdWndProcRare(); +#endif int ww; PAINTSTRUCT ps; @@ -1065,6 +1113,21 @@ LONG lParam; SetUatMode(uamNavigation); StopUatTimer(); #endif /* RSH */ +#ifdef OPUS_X64 + { + WORD code = LOWORD(wParam); + int dqThumb = HIWORD(wParam); + HWND hwndScroll = (HWND) lParam; + + if (message == WM_HSCROLL) + MwdHorzScroll(wwCur, code, dqThumb); + else + MwdVertScroll(((*hwwdCur)->hwndVScroll == hwndScroll) ? + wwCur : WwOther(wwCur), code, dqThumb); + if (vfRecording) + RecordScroll(message, code, dqThumb); + } +#else if (message == WM_HSCROLL) MwdHorzScroll( wwCur, wParam, (int)lParam); else @@ -1072,11 +1135,17 @@ LONG lParam; wwCur : WwOther(wwCur), wParam, (int)lParam); if (vfRecording) RecordScroll(message, wParam, LOWORD(lParam)); +#endif break; case WM_MOUSEACTIVATE: { - int iRet = 0; + int iRet = +#ifdef OPUS_X64 + MA_ACTIVATE; +#else + 0; +#endif if (vhwndAppModalFocus != hNil) if (!vfDeactByOtherApp) @@ -1115,7 +1184,14 @@ LONG lParam; NewCurWw( PmwdMw(MwFromHwndMw(hwnd))->wwActive, fFalse /*fDoNotSelect*/); /* fall through */ default: +#ifdef OPUS_X64 + /* Modern USER can activate and deliver this click without the + * Win16 re-entrancy workaround. Eating it makes the first click + * after switching documents disappear. */ + iRet = MA_ACTIVATE; +#else iRet = MA_ACTIVATEANDEAT; +#endif break; } } @@ -1189,8 +1265,8 @@ LONG lParam; EnsureFocusInPane(); - if ((int) wParam >= 0) - return CmdExecBcmKc(wParam, kcNil) == cmdOK; + if (FOpusCommandWParam(wParam)) + return CmdExecBcmKc(BcmFromWParam(wParam), kcNil) == cmdOK; switch (wParam & 0xfff0) { @@ -1229,9 +1305,17 @@ LONG lParam; if (vhwndStatLine) { +#ifdef OPUS_X64 + hMenu = (HMENU) lParam; + mf = HIWORD(wParam); + bcm = (mf & MF_POPUP) && hMenu != NULL ? + (UINT_PTR) GetSubMenu(hMenu, LOWORD(wParam)) : + (UINT_PTR) LOWORD(wParam); +#else hMenu = HIWORD(lParam); bcm = wParam; mf = LOWORD(lParam ); +#endif if (hMenu == 0 && fEnteredIdle) DrawMenuHelp(hMenu, bcm, mf); fEnteredIdle = fFalse; @@ -1278,15 +1362,23 @@ DefaultProc: /* All messages not processed come here. */ else, we can compute the context from these saved values. */ /* %%Function:SaveMenuHelpContext %%Owner:rosiep */ +#ifdef OPUS_X64 +void SaveMenuHelpContext(WPARAM wParam, LPARAM lParam, int cxt) +#else SaveMenuHelpContext(wParam,lParam,cxt) WORD wParam; LONG lParam; int cxt; +#endif { Assert(cxt == cxtAppMenuSelect || cxt == cxtDocMenuSelect); /* if we're exiting Menu Mode, no more help context */ +#ifdef OPUS_X64 + if (lParam == 0 && HIWORD(wParam) == 0xffff) +#else if (HIWORD(lParam) == 0 || wParam == 0) +#endif { vwParamHelp = 0; vlParamHelp = 0; @@ -1380,7 +1472,11 @@ LONG lParam; { int ww; struct RC rc; +#ifdef OPUS_X64 + LRESULT RareWwPaneWndProc(HWND, UINT, WPARAM, LPARAM); +#else long RareWwPaneWndProc(); +#endif #ifdef SHOWPNEMSG ShowMsg ("pn", hwnd, message, wParam, lParam); @@ -1400,6 +1496,104 @@ LONG lParam; default: return RareWwPaneWndProc( hwnd, message, wParam,lParam ); +#ifdef OPUS_X64 + case WM_OPUS_X64_QUERY_SELECTION: + switch (LOWORD(wParam)) + { + case 0: return (LRESULT) selCur.cpFirst; + case 1: return (LRESULT) selCur.cpLim; + case 2: return (LRESULT) selCur.fIns; + case 3: return (LRESULT) selCur.sty; + case 4: return (LRESULT) selCur.xw; + case 5: return (LRESULT) vfDoubleClick; + case 6: return (LRESULT) selCur.sk; + case 7: return (LRESULT) selCur.yw; + case 8: return (LRESULT) selCur.dyp; + case 9: return (LRESULT) selCur.fOn; + case 10: return (LRESULT) selCur.fHidden; + case 11: return (LRESULT) selCur.xpFirst; + case 12: return (LRESULT) selCur.xpLim; + case 13: return (LRESULT) vfli.cpMin; + case 14: return (LRESULT) vfli.cpMac; + case 15: return (LRESULT) vfli.xpLeft; + case 16: return (LRESULT) vfli.xpRight; + case 17: return (LRESULT) vfli.ichMac; + case 18: return (LRESULT) vfli.rgdxp[0]; + case 19: return (LRESULT) vfli.rgdxp[ + vfli.ichMac == 0 ? 0 : vfli.ichMac - 1]; + case 24: return (LRESULT) sizeof(struct CHR); + case 25: return (LRESULT) sizeof(struct CHRT); + case 26: return (LRESULT) sizeof(struct CHRV); + case 27: return (LRESULT) sizeof(struct CHRF); + case 28: return (LRESULT) sizeof(struct CHRDF); + case 29: return (LRESULT) sizeof(struct CHRFG); + case 30: + { + struct DR *pdr = PdrGalley(PwwdWw(WwFromHwnd(hwnd))); + return (LRESULT) IMacPlc(pdr->hplcedl); + } + case 31: + { + struct DR *pdr = PdrGalley(PwwdWw(WwFromHwnd(hwnd))); + return (LRESULT) CpPlc(pdr->hplcedl, (int) lParam); + } + case 32: + case 33: + case 34: + case 35: + { + struct EDL edl; + struct DR *pdr = PdrGalley(PwwdWw(WwFromHwnd(hwnd))); + GetPlc(pdr->hplcedl, (int) lParam, &edl); + if (LOWORD(wParam) == 32) return (LRESULT) edl.ypTop; + if (LOWORD(wParam) == 33) return (LRESULT) edl.dyp; + if (LOWORD(wParam) == 34) return (LRESULT) edl.dcp; + return (LRESULT) edl.fDirty; + } + case 36: + case 37: + { + struct DR *pdr = PdrGalley(PwwdWw(WwFromHwnd(hwnd))); + return LOWORD(wParam) == 36 ? + (LRESULT) pdr->cpFirst : (LRESULT) pdr->cpLim; + } + case 38: return (LRESULT) sizeof(struct EDL); + case 39: + { + struct DR *pdr = PdrGalley(PwwdWw(WwFromHwnd(hwnd))); + return (LRESULT) pdr->fDirty; + } + case 40: + return (LRESULT) PwwdWw(WwFromHwnd(hwnd))->fDirty; + case 41: return (LRESULT) CpMacDocEdit(selCur.doc); + case 42: + { + struct DR *pdr = PdrGalley(PwwdWw(WwFromHwnd(hwnd))); + return (LRESULT) pdr->dyl; + } + case 43: + return (LRESULT) DyOfRc(&PwwdWw(WwFromHwnd(hwnd))->rcwDisp); + case 44: + { + struct DR *pdr = PdrGalley(PwwdWw(WwFromHwnd(hwnd))); + return (LRESULT) pdr->dxl; + } + case 45: + return (LRESULT) DxOfRc(&PwwdWw(WwFromHwnd(hwnd))->rcwDisp); + case 46: return (LRESULT) CpMacDoc(selCur.doc); + case 47: + case 48: + { + struct EDL edl; + struct DR *pdr = PdrGalley(PwwdWw(WwFromHwnd(hwnd))); + GetPlc(pdr->hplcedl, (int) lParam, &edl); + return LOWORD(wParam) == 47 ? + (LRESULT) edl.dlk : (LRESULT) edl.fEnd; + } + } + return (LRESULT) -1; +#endif + case WM_SETFOCUS: /* The window is getting the focus. wParam contains the window ** handle of the window that previously had the focus. */ @@ -1483,7 +1677,14 @@ LONG lParam; } else { +#ifdef OPUS_X64 + /* The Win16 eat-on-activate workaround makes the next + * physical click look like a double-click on modern USER. + * Let the activating click position the caret immediately. */ + return(MA_ACTIVATE); +#else return(MA_ACTIVATEANDEAT); +#endif } } DefaultProc: /* All messages not processed come here. */ @@ -2816,15 +3017,24 @@ lblSet: by the window proc receiving the message. */ /* %%Function:LMenuChar %%Owner:peterj */ +#ifdef OPUS_X64 +LRESULT LMenuChar(HWND hwnd, WPARAM wParam, LPARAM lParam) +#else LONG LMenuChar(hwnd, wParam, lParam) HWND hwnd; WORD wParam; LONG lParam; +#endif { if (vhwndAppModalFocus != NULL) return MAKELONG(0, 1); +#ifdef OPUS_X64 + if (LOWORD(wParam) == '-' && !(HIWORD(wParam) & MF_POPUP) && + hwwdCur != hNil) +#else if (wParam == '-' && !(LOWORD(lParam) & MF_POPUP) && hwwdCur != hNil) +#endif { if (vfSysMenu) { @@ -3181,7 +3391,18 @@ BOOL fRightButton; } } +#ifdef OPUS_X64 + /* + * The Win16 loop could rely on GetKeyState's queue snapshot while nested + * inside the button-down dispatch. On modern Windows that snapshot can + * lag the physical/SENDINPUT transition and prematurely end a drag before + * the corresponding button-up reaches this replay loop. + */ + return ((GetAsyncKeyState(fRightButton ? VK_RBUTTON : VK_LBUTTON) & + 0x8000) != 0); +#else return (GetKeyState( fRightButton ? VK_RBUTTON : VK_LBUTTON ) < 0); +#endif } diff --git a/src/Opus/wwact.c b/src/Opus/wwact.c index 32e37eb..60d1907 100644 --- a/src/Opus/wwact.c +++ b/src/Opus/wwact.c @@ -133,11 +133,15 @@ extern int vfTableKeymap; /* %%Function:AppWndProcAct %%Owner:chic */ +#ifdef OPUS_X64 +LRESULT AppWndProcAct(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +#else long AppWndProcAct(hwnd, message, wParam, lParam) HWND hwnd; unsigned message; WORD wParam; LONG lParam; +#endif { switch (message) diff --git a/src/Opus/wwchange.c b/src/Opus/wwchange.c index f996b63..d7986fd 100644 --- a/src/Opus/wwchange.c +++ b/src/Opus/wwchange.c @@ -131,11 +131,15 @@ HWND vhwndZoom = hNil; /* %%Function:MwdWndProcRare %%Owner:chic */ +#ifdef OPUS_X64 +LRESULT MwdWndProcRare(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +#else long MwdWndProcRare(hwnd, message, wParam, lParam) HWND hwnd; int message; WORD wParam; LONG lParam; +#endif { extern int mwCreate; @@ -169,12 +173,21 @@ LONG lParam; { struct MWD *pmwd = PmwdMw(mw); +#ifdef OPUS_X64 + pmwd->xp = (short) LOWORD(lParam); + pmwd->yp = (short) HIWORD(lParam); +#else pmwd->xp = LOWORD(lParam); pmwd->yp = HIWORD(lParam); +#endif /* the dimensions are recorded in the size message */ if (vfRecording && fRecordMove) +#ifdef OPUS_X64 + RecordDocMove((short) LOWORD(lParam), (short) HIWORD(lParam)); +#else RecordDocMove(LOWORD(lParam), HIWORD(lParam)); +#endif fRecordMove = fFalse; } break; @@ -214,11 +227,16 @@ LONG lParam; /* %%Function:RareWwPaneWndProc %%Owner:chic */ +#ifdef OPUS_X64 +LRESULT RareWwPaneWndProc(HWND hwnd, UINT message, WPARAM wParam, + LPARAM lParam) +#else long RareWwPaneWndProc(hwnd, message, wParam, lParam) HWND hwnd; int message; WORD wParam; LONG lParam; +#endif { int ww; struct WWD *pwwd; diff --git a/src/port/legacy_asm_port.cpp b/src/port/legacy_asm_port.cpp deleted file mode 100644 index 4905879..0000000 --- a/src/port/legacy_asm_port.cpp +++ /dev/null @@ -1,240 +0,0 @@ -#include "legacy_asm_port.h" - -#include - -#include -#include -#include -#include -#include -#include - -namespace opus::x64 { -namespace { - -enum class PortKind : std::uint8_t { - translated_cpp, - portable_c_counterpart, - win32_replacement, - retired_platform_code, -}; - -struct AssemblyPortEntry { - std::string_view path; - PortKind kind; -}; - -// One entry per .asm file under src. This table is intentionally explicit: -// adding an assembly module makes CMake fail until its x64 disposition is -// reviewed. "portable_c_counterpart" means the hand-written assembly was an -// optimized version of an extant C implementation in Opus/wordtech. -constexpr std::array kAssemblyPorts{ - AssemblyPortEntry{"Opus/asm/asserth.asm", PortKind::retired_platform_code}, - AssemblyPortEntry{"Opus/asm/auxout.asm", PortKind::win32_replacement}, - AssemblyPortEntry{"Opus/asm/clsplcn.asm", PortKind::portable_c_counterpart}, - AssemblyPortEntry{"Opus/asm/cmdlookn.asm", PortKind::portable_c_counterpart}, - AssemblyPortEntry{"Opus/asm/dialog1n.asm", PortKind::portable_c_counterpart}, - AssemblyPortEntry{"Opus/asm/disp1n.asm", PortKind::portable_c_counterpart}, - AssemblyPortEntry{"Opus/asm/disp1n2.asm", PortKind::portable_c_counterpart}, - AssemblyPortEntry{"Opus/asm/disp2n.asm", PortKind::portable_c_counterpart}, - AssemblyPortEntry{"Opus/asm/disp3n.asm", PortKind::portable_c_counterpart}, - AssemblyPortEntry{"Opus/asm/disptbln.asm", PortKind::portable_c_counterpart}, - AssemblyPortEntry{"Opus/asm/disptbn2.asm", PortKind::portable_c_counterpart}, - AssemblyPortEntry{"Opus/asm/editn.asm", PortKind::portable_c_counterpart}, - AssemblyPortEntry{"Opus/asm/editn2.asm", PortKind::portable_c_counterpart}, - AssemblyPortEntry{"Opus/asm/eldden.asm", PortKind::win32_replacement}, - AssemblyPortEntry{"Opus/asm/expn.asm", PortKind::portable_c_counterpart}, - AssemblyPortEntry{"Opus/asm/fetch2n.asm", PortKind::portable_c_counterpart}, - AssemblyPortEntry{"Opus/asm/fetchn.asm", PortKind::portable_c_counterpart}, - AssemblyPortEntry{"Opus/asm/fetchn2.asm", PortKind::portable_c_counterpart}, - AssemblyPortEntry{"Opus/asm/fetchn3.asm", PortKind::portable_c_counterpart}, - AssemblyPortEntry{"Opus/asm/fetchtbn.asm", PortKind::portable_c_counterpart}, - AssemblyPortEntry{"Opus/asm/fieldcrn.asm", PortKind::portable_c_counterpart}, - AssemblyPortEntry{"Opus/asm/fieldfmn.asm", PortKind::portable_c_counterpart}, - AssemblyPortEntry{"Opus/asm/fieldspn.asm", PortKind::portable_c_counterpart}, - AssemblyPortEntry{"Opus/asm/file2n.asm", PortKind::win32_replacement}, - AssemblyPortEntry{"Opus/asm/filewinn.asm", PortKind::win32_replacement}, - AssemblyPortEntry{"Opus/asm/formatn.asm", PortKind::portable_c_counterpart}, - AssemblyPortEntry{"Opus/asm/formatn2.asm", PortKind::portable_c_counterpart}, - AssemblyPortEntry{"Opus/asm/formulan.asm", PortKind::portable_c_counterpart}, - AssemblyPortEntry{"Opus/asm/grbitx.asm", PortKind::translated_cpp}, - AssemblyPortEntry{"Opus/asm/grhc.asm", PortKind::translated_cpp}, - AssemblyPortEntry{"Opus/asm/index2n.asm", PortKind::portable_c_counterpart}, - AssemblyPortEntry{"Opus/asm/inssubsn.asm", PortKind::portable_c_counterpart}, - AssemblyPortEntry{"Opus/asm/int3f.asm", PortKind::retired_platform_code}, - AssemblyPortEntry{"Opus/asm/layout22.asm", PortKind::portable_c_counterpart}, - AssemblyPortEntry{"Opus/asm/layout2n.asm", PortKind::portable_c_counterpart}, - AssemblyPortEntry{"Opus/asm/movecmds.asm", PortKind::retired_platform_code}, - AssemblyPortEntry{"Opus/asm/natinit.asm", PortKind::translated_cpp}, - AssemblyPortEntry{"Opus/asm/nattrans.asm", PortKind::translated_cpp}, - AssemblyPortEntry{"Opus/asm/prln.asm", PortKind::portable_c_counterpart}, - AssemblyPortEntry{"Opus/asm/profc.asm", PortKind::retired_platform_code}, - AssemblyPortEntry{"Opus/asm/profctl.asm", PortKind::retired_platform_code}, - AssemblyPortEntry{"Opus/asm/promptn.asm", PortKind::win32_replacement}, - AssemblyPortEntry{"Opus/asm/qplc.asm", PortKind::retired_platform_code}, - AssemblyPortEntry{"Opus/asm/resn.asm", PortKind::portable_c_counterpart}, - AssemblyPortEntry{"Opus/asm/resn2.asm", PortKind::translated_cpp}, - AssemblyPortEntry{"Opus/asm/rip.asm", PortKind::retired_platform_code}, - AssemblyPortEntry{"Opus/asm/rtfinn.asm", PortKind::portable_c_counterpart}, - AssemblyPortEntry{"Opus/asm/rtfsubsn.asm", PortKind::portable_c_counterpart}, - AssemblyPortEntry{"Opus/asm/searchn.asm", PortKind::portable_c_counterpart}, - AssemblyPortEntry{"Opus/asm/sttbn.asm", PortKind::portable_c_counterpart}, - AssemblyPortEntry{"Opus/asm/wordgrep.asm", PortKind::portable_c_counterpart}, - AssemblyPortEntry{"Opus/asm/wprocn.asm", PortKind::win32_replacement}, - AssemblyPortEntry{"OpusEtAl/tools/src/appstub.asm", PortKind::retired_platform_code}, - AssemblyPortEntry{"OpusEtAl/tools/src/convtest/doslib.asm", PortKind::win32_replacement}, - AssemblyPortEntry{"OpusEtAl/tools/src/convtest/init.asm", PortKind::retired_platform_code}, - AssemblyPortEntry{"OpusEtAl/tools/src/makekeyn.asm", PortKind::portable_c_counterpart}, - AssemblyPortEntry{"OpusEtAl/tools/src/timedate.asm", PortKind::translated_cpp}, - AssemblyPortEntry{"OpusEtAl/tools/src/winemscs.asm", PortKind::retired_platform_code}, - AssemblyPortEntry{"OpusEtAl/tools/src/winemsds.asm", PortKind::retired_platform_code}, -}; - -static_assert(kAssemblyPorts.size() == 59); - -} // namespace - -std::int32_t multiply_divide(const std::int32_t value, - const std::int32_t numerator, - const std::int32_t denominator) noexcept { - if (denominator == 0) { - const bool negative = (value < 0) != (numerator < 0); - return negative ? std::numeric_limits::min() - : std::numeric_limits::max(); - } - const auto result = (static_cast(value) * numerator) / denominator; - return static_cast(std::clamp( - result, std::numeric_limits::min(), - std::numeric_limits::max())); -} - -std::uint32_t unsigned_multiply_divide(const std::uint32_t value, - const std::uint32_t numerator, - const std::uint32_t denominator) noexcept { - if (denominator == 0) { - return std::numeric_limits::max(); - } - const auto result = (static_cast(value) * numerator) / denominator; - return static_cast(std::min( - result, std::numeric_limits::max())); -} - -double legacy_log(const double value) noexcept { return std::log(value); } -double legacy_exp(const double value) noexcept { return std::exp(value); } -double legacy_sqrt(const double value) noexcept { return std::sqrt(value); } -double legacy_square(const double value) noexcept { return value * value; } - -void fill_byte_pattern(void* destination, const std::size_t byte_count, - const void* pattern, const std::size_t pattern_size) { - if (byte_count == 0) { - return; - } - if (destination == nullptr || pattern == nullptr || pattern_size == 0) { - throw std::invalid_argument("fill_byte_pattern received an invalid buffer"); - } - auto* output = static_cast(destination); - const auto* input = static_cast(pattern); - for (std::size_t offset = 0; offset < byte_count; ++offset) { - output[offset] = input[offset % pattern_size]; - } -} - -bool buffers_differ(const void* left, const void* right, - const std::size_t byte_count) noexcept { - if (byte_count == 0) { - return false; - } - if (left == nullptr || right == nullptr) { - return true; - } - return std::memcmp(left, right, byte_count) != 0; -} - -std::size_t zero_terminated_length(const char* text) noexcept { - return text == nullptr ? 0 : std::strlen(text); -} - -std::size_t copy_zero_terminated(char* destination, - const std::size_t destination_size, - const char* source) noexcept { - if (destination == nullptr || destination_size == 0) { - return 0; - } - if (source == nullptr) { - destination[0] = '\0'; - return 0; - } - const auto count = std::min(destination_size - 1, std::strlen(source)); - std::memcpy(destination, source, count); - destination[count] = '\0'; - return count; -} - -bool is_ascii_alpha(const std::uint8_t value) noexcept { - const auto upper = ascii_upper(value); - return upper >= 'A' && upper <= 'Z'; -} - -bool is_ascii_digit(const std::uint8_t value) noexcept { - return value >= '0' && value <= '9'; -} - -bool is_ascii_alphanumeric(const std::uint8_t value) noexcept { - return is_ascii_alpha(value) || is_ascii_digit(value); -} - -std::uint8_t ascii_upper(const std::uint8_t value) noexcept { - return value >= 'a' && value <= 'z' ? static_cast(value - ('a' - 'A')) - : value; -} - -LegacyTime local_time() noexcept { - SYSTEMTIME value{}; - GetLocalTime(&value); - return {value.wHour, value.wMinute, value.wSecond, - static_cast(value.wMilliseconds / 10)}; -} - -LegacyDate local_date() noexcept { - SYSTEMTIME value{}; - GetLocalTime(&value); - return {value.wYear, value.wMonth, value.wDay, value.wDayOfWeek}; -} - -std::uint64_t available_disk_bytes(const std::wstring_view path) noexcept { - ULARGE_INTEGER available{}; - const std::wstring owned_path(path); - if (!GetDiskFreeSpaceExW(owned_path.c_str(), &available, nullptr, nullptr)) { - return 0; - } - return available.QuadPart; -} - -bool is_remote_path(const std::wstring_view path) noexcept { - if (path.starts_with(L"\\\\")) { - return true; - } - if (path.size() < 2 || path[1] != L':') { - return false; - } - wchar_t root[] = {path[0], L':', L'\\', L'\0'}; - return GetDriveTypeW(root) == DRIVE_REMOTE; -} - -bool assembly_manifest_complete() noexcept { - for (std::size_t left = 0; left < kAssemblyPorts.size(); ++left) { - if (kAssemblyPorts[left].path.empty()) { - return false; - } - for (std::size_t right = left + 1; right < kAssemblyPorts.size(); ++right) { - if (kAssemblyPorts[left].path == kAssemblyPorts[right].path) { - return false; - } - } - } - return true; -} - -std::size_t assembly_manifest_count() noexcept { return kAssemblyPorts.size(); } - -} // namespace opus::x64 diff --git a/src/port/legacy_asm_port.h b/src/port/legacy_asm_port.h deleted file mode 100644 index 1f3c32c..0000000 --- a/src/port/legacy_asm_port.h +++ /dev/null @@ -1,63 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -namespace opus::x64 { - -// Fixed-width replacements for assembly primitives that are still useful to -// the native port. Higher-level Opus routines are replaced by the portable C -// algorithms in Opus/wordtech as that document engine is migrated. - -struct LegacyTime { - std::uint16_t hour{}; - std::uint16_t minute{}; - std::uint16_t second{}; - std::uint16_t hundredths{}; -}; - -struct LegacyDate { - std::uint16_t year{}; - std::uint16_t month{}; - std::uint16_t day{}; - std::uint16_t day_of_week{}; -}; - -[[nodiscard]] std::int32_t multiply_divide(std::int32_t value, - std::int32_t numerator, - std::int32_t denominator) noexcept; -[[nodiscard]] std::uint32_t unsigned_multiply_divide(std::uint32_t value, - std::uint32_t numerator, - std::uint32_t denominator) noexcept; -[[nodiscard]] double legacy_log(double value) noexcept; -[[nodiscard]] double legacy_exp(double value) noexcept; -[[nodiscard]] double legacy_sqrt(double value) noexcept; -[[nodiscard]] double legacy_square(double value) noexcept; - -void fill_byte_pattern(void* destination, std::size_t byte_count, - const void* pattern, std::size_t pattern_size); -[[nodiscard]] bool buffers_differ(const void* left, const void* right, - std::size_t byte_count) noexcept; -[[nodiscard]] std::size_t zero_terminated_length(const char* text) noexcept; -[[nodiscard]] std::size_t copy_zero_terminated(char* destination, - std::size_t destination_size, - const char* source) noexcept; -[[nodiscard]] bool is_ascii_alpha(std::uint8_t value) noexcept; -[[nodiscard]] bool is_ascii_digit(std::uint8_t value) noexcept; -[[nodiscard]] bool is_ascii_alphanumeric(std::uint8_t value) noexcept; -[[nodiscard]] std::uint8_t ascii_upper(std::uint8_t value) noexcept; - -[[nodiscard]] LegacyTime local_time() noexcept; -[[nodiscard]] LegacyDate local_date() noexcept; -[[nodiscard]] std::uint64_t available_disk_bytes(std::wstring_view path) noexcept; -[[nodiscard]] bool is_remote_path(std::wstring_view path) noexcept; - -// Returns true only if the checked-in migration table accounts for every -// legacy .asm module. This is also run during application startup. -[[nodiscard]] bool assembly_manifest_complete() noexcept; -[[nodiscard]] std::size_t assembly_manifest_count() noexcept; - -} // namespace opus::x64 - diff --git a/src/port/original/about.hs b/src/port/original/about.hs index 2ffcd17..10d332e 100644 --- a/src/port/original/about.hs +++ b/src/port/original/about.hs @@ -4,6 +4,7 @@ typedef struct _CABABOUT { CABH cabh; + WORD sab; CHAR **hszAboutApp; CHAR **hszAboutVersion; CHAR **hszAboutCopyright; diff --git a/src/port/original/asgn2key.hs b/src/port/original/asgn2key.hs index 707ccc7..3432ec5 100644 --- a/src/port/original/asgn2key.hs +++ b/src/port/original/asgn2key.hs @@ -13,7 +13,6 @@ #define tmcUnassignKey (tmcUserMin + 9) #define tmcDescKey (tmcUserMin + 10) -#pragma pack(push, 2) typedef struct _CABCHANGEKEYS { CABH cabh; @@ -22,7 +21,6 @@ typedef struct _CABCHANGEKEYS unsigned uKeyList; int iContext; } CABCHANGEKEYS; -#pragma pack(pop) typedef CABCHANGEKEYS **HCABCHANGEKEYS; #define cabiCABCHANGEKEYS Cabi((sizeof(CABCHANGEKEYS) + sizeof(WORD) - 1) / sizeof(WORD), 1) diff --git a/src/port/original/asgn2mnu.hs b/src/port/original/asgn2mnu.hs index 8c62a7b..a7da820 100644 --- a/src/port/original/asgn2mnu.hs +++ b/src/port/original/asgn2mnu.hs @@ -13,7 +13,6 @@ #define tmcResetMenu (tmcUserMin + 9) #define tmcDescMenu (tmcUserMin + 10) -#pragma pack(push, 2) typedef struct _CABASSIGNTOMENU { CABH cabh; @@ -23,7 +22,6 @@ typedef struct _CABASSIGNTOMENU CHAR **hszTitle; int iContext; } CABASSIGNTOMENU; -#pragma pack(pop) typedef CABASSIGNTOMENU **HCABASSIGNTOMENU; #define cabiCABASSIGNTOMENU Cabi((sizeof(CABASSIGNTOMENU) + sizeof(WORD) - 1) / sizeof(WORD), 3) diff --git a/src/port/original/bookmark.hs b/src/port/original/bookmark.hs index 01afd7d..2e02455 100644 --- a/src/port/original/bookmark.hs +++ b/src/port/original/bookmark.hs @@ -7,6 +7,7 @@ typedef struct _CABINSBOOKMARK { CABH cabh; + WORD sab; CHAR **hszBkmkName; } CABINSBOOKMARK; diff --git a/src/port/original/catalog.hs b/src/port/original/catalog.hs index a161230..3f57359 100644 --- a/src/port/original/catalog.hs +++ b/src/port/original/catalog.hs @@ -13,7 +13,6 @@ #define tmcCatDelete (tmcUserMin + 7) #define tmcCatSummary (tmcUserMin + 8) -#pragma pack(push, 2) typedef struct _CABCATALOG { CABH cabh; @@ -34,7 +33,6 @@ typedef struct _CABCATALOG BOOL fCatSrhMatchCase; BOOL fCatSrhRedo; } CABCATALOG; -#pragma pack(pop) typedef CABCATALOG **HCABCATALOG; #define cabiCABCATALOG Cabi((sizeof(CABCATALOG) + sizeof(WORD) - 1) / sizeof(WORD), 8) diff --git a/src/port/original/catprog.hs b/src/port/original/catprog.hs index 37adc21..d2a20f4 100644 --- a/src/port/original/catprog.hs +++ b/src/port/original/catprog.hs @@ -6,13 +6,11 @@ #define tmcCatSrhSearched (tmcUserMin + 2) #define tmcCatSrhFileInUse (tmcUserMin + 3) -#pragma pack(push, 2) typedef struct _CABCATSRHPROG { CABH cabh; WORD sab; } CABCATSRHPROG; -#pragma pack(pop) typedef CABCATSRHPROG **HCABCATSRHPROG; #define cabiCABCATSRHPROG Cabi((sizeof(CABCATSRHPROG) + sizeof(WORD) - 1) / sizeof(WORD), 0) diff --git a/src/port/original/catsrch.hs b/src/port/original/catsrch.hs index 02780a8..2c2e3e2 100644 --- a/src/port/original/catsrch.hs +++ b/src/port/original/catsrch.hs @@ -15,7 +15,6 @@ #define tmcCatSrhMatchCase (tmcUserMin + 11) #define tmcCatSrhRedo (tmcUserMin + 12) -#pragma pack(push, 2) typedef struct _CABCATSEARCH { CABH cabh; @@ -34,7 +33,6 @@ typedef struct _CABCATSEARCH BOOL fCatSrhMatchCase; BOOL fCatSrhRedo; } CABCATSEARCH; -#pragma pack(pop) typedef CABCATSEARCH **HCABCATSEARCH; #define cabiCABCATSEARCH Cabi((sizeof(CABCATSEARCH) + sizeof(WORD) - 1) / sizeof(WORD), 7) diff --git a/src/port/original/chgpr.hs b/src/port/original/chgpr.hs index 674ece0..cf44dac 100644 --- a/src/port/original/chgpr.hs +++ b/src/port/original/chgpr.hs @@ -7,6 +7,7 @@ typedef struct _CABCHGPR { CABH cabh; + WORD sab; CHAR **hszLb; } CABCHGPR; diff --git a/src/port/original/confirmr.hs b/src/port/original/confirmr.hs index accfc85..aacbc8a 100644 --- a/src/port/original/confirmr.hs +++ b/src/port/original/confirmr.hs @@ -9,6 +9,7 @@ typedef struct _CABCONFIRMREPL { CABH cabh; + WORD sab; BOOL fConfirm; } CABCONFIRMREPL; diff --git a/src/port/original/cust.hs b/src/port/original/cust.hs index 38539de..3e7b69f 100644 --- a/src/port/original/cust.hs +++ b/src/port/original/cust.hs @@ -22,13 +22,13 @@ typedef struct _CABCUSTOMIZE { CABH cabh; WORD sab; + CHAR **hszName; + CHAR **hszInitials; int iAS; int iUnit; BOOL fBkgrndPag; BOOL fPromptSI; BOOL fAutoDelete; - CHAR **hszName; - CHAR **hszInitials; int cBtnFldClicks; } CABCUSTOMIZE; diff --git a/src/port/original/doc.hs b/src/port/original/doc.hs index f8de992..6f06028 100644 --- a/src/port/original/doc.hs +++ b/src/port/original/doc.hs @@ -23,6 +23,8 @@ typedef struct _CABDOCUMENT { CABH cabh; + WORD sab; + CHAR **hszTemplate; unsigned uDocPageWidth; unsigned uDocPageHeight; unsigned uDocDefTabs; @@ -35,7 +37,6 @@ typedef struct _CABDOCUMENT int iFN; int iFNStartAt; BOOL fFNRestart; - CHAR **hszTemplate; BOOL fWidowControl; } CABDOCUMENT; diff --git a/src/port/original/docstat.hs b/src/port/original/docstat.hs index b17ea12..a1d0643 100644 --- a/src/port/original/docstat.hs +++ b/src/port/original/docstat.hs @@ -19,6 +19,7 @@ typedef struct _CABDOCSTAT { CABH cabh; + WORD sab; CHAR **hszFName; CHAR **hszDir; CHAR **hszDOT; diff --git a/src/port/original/edmacro.hs b/src/port/original/edmacro.hs index d600108..feaca7b 100644 --- a/src/port/original/edmacro.hs +++ b/src/port/original/edmacro.hs @@ -17,10 +17,10 @@ typedef struct _CABEDMACRO CABH cabh; WORD sab; CHAR **hszName; - int iContext; - BOOL fShowAll; CHAR **hszDesc; CHAR **hszNewName; + int iContext; + BOOL fShowAll; } CABEDMACRO; typedef CABEDMACRO **HCABEDMACRO; diff --git a/src/port/original/edstyle.hs b/src/port/original/edstyle.hs index 40bffea..685e83a 100644 --- a/src/port/original/edstyle.hs +++ b/src/port/original/edstyle.hs @@ -26,9 +26,9 @@ typedef struct _CABDEFINESTYLE CHAR **hszDSStyle; CHAR **hszDSBasedOn; CHAR **hszDSNext; - BOOL fTemplate; CHAR **hszDSNewName; CHAR **hszMergeStyle; + BOOL fTemplate; unsigned uSource; } CABDEFINESTYLE; diff --git a/src/port/original/footnote.hs b/src/port/original/footnote.hs index 8fff8ec..12f1a08 100644 --- a/src/port/original/footnote.hs +++ b/src/port/original/footnote.hs @@ -11,8 +11,8 @@ typedef struct _CABINSERTFTN { CABH cabh; WORD sab; - BOOL fAuto; CHAR **hstRef; + BOOL fAuto; } CABINSERTFTN; typedef CABINSERTFTN **HCABINSERTFTN; diff --git a/src/port/original/goto.hs b/src/port/original/goto.hs index 9123b29..4d37e1f 100644 --- a/src/port/original/goto.hs +++ b/src/port/original/goto.hs @@ -6,6 +6,7 @@ typedef struct _CABGOTO { CABH cabh; + WORD sab; CHAR **hszGoto; } CABGOTO; diff --git a/src/port/original/hyphen.hs b/src/port/original/hyphen.hs index f946bc4..05b06c8 100644 --- a/src/port/original/hyphen.hs +++ b/src/port/original/hyphen.hs @@ -9,7 +9,6 @@ #define tmcHypChange (tmcUserMin + 5) #define tmcHypNoChange (tmcUserMin + 6) -#pragma pack(push, 2) typedef struct _CABHYPHEN { CABH cabh; @@ -18,7 +17,6 @@ typedef struct _CABHYPHEN BOOL fHypCaps; BOOL fHypConfirm; } CABHYPHEN; -#pragma pack(pop) typedef CABHYPHEN **HCABHYPHEN; #define cabiCABHYPHEN Cabi((sizeof(CABHYPHEN) + sizeof(WORD) - 1) / sizeof(WORD), 1) diff --git a/src/port/original/index.hs b/src/port/original/index.hs index 817124a..b75c7b1 100644 --- a/src/port/original/index.hs +++ b/src/port/original/index.hs @@ -11,7 +11,6 @@ #define tmcIndexBlank (tmcUserMin + 3) #define tmcIndexLetter (tmcUserMin + 4) -#pragma pack(push, 2) typedef struct _CABINDEX { CABH cabh; @@ -20,7 +19,6 @@ typedef struct _CABINDEX int iHeading; BOOL fReplace; } CABINDEX; -#pragma pack(pop) typedef CABINDEX **HCABINDEX; #define cabiCABINDEX Cabi((sizeof(CABINDEX) + sizeof(WORD) - 1) / sizeof(WORD), 0) diff --git a/src/port/original/indexent.hs b/src/port/original/indexent.hs index bda5158..db0b9ee 100644 --- a/src/port/original/indexent.hs +++ b/src/port/original/indexent.hs @@ -6,7 +6,6 @@ #define tmcXeBold (tmcUserMin + 3) #define tmcXeItalic (tmcUserMin + 4) -#pragma pack(push, 2) typedef struct _CABINDEXENTRY { CABH cabh; @@ -16,7 +15,6 @@ typedef struct _CABINDEXENTRY BOOL fBold; BOOL fItalic; } CABINDEXENTRY; -#pragma pack(pop) typedef CABINDEXENTRY **HCABINDEXENTRY; #define cabiCABINDEXENTRY Cabi((sizeof(CABINDEXENTRY) + sizeof(WORD) - 1) / sizeof(WORD), 2) diff --git a/src/port/original/insbreak.hs b/src/port/original/insbreak.hs index 33a811d..5257794 100644 --- a/src/port/original/insbreak.hs +++ b/src/port/original/insbreak.hs @@ -11,6 +11,7 @@ typedef struct _CABINSBREAK { CABH cabh; + WORD sab; int iType; } CABINSBREAK; diff --git a/src/port/original/insfield.hs b/src/port/original/insfield.hs index 215bc39..74abb51 100644 --- a/src/port/original/insfield.hs +++ b/src/port/original/insfield.hs @@ -8,7 +8,6 @@ #define tmcIFldHelp (tmcUserMin + 4) #define tmcIFldAdd (tmcUserMin + 5) -#pragma pack(push, 2) typedef struct _CABINSFIELD { CABH cabh; @@ -17,7 +16,6 @@ typedef struct _CABINSFIELD unsigned uFlt; unsigned uInst; } CABINSFIELD; -#pragma pack(pop) typedef CABINSFIELD **HCABINSFIELD; #define cabiCABINSFIELD Cabi((sizeof(CABINSFIELD) + sizeof(WORD) - 1) / sizeof(WORD), 1) diff --git a/src/port/original/insfile.hs b/src/port/original/insfile.hs index 5ce4356..8b05e72 100644 --- a/src/port/original/insfile.hs +++ b/src/port/original/insfile.hs @@ -9,9 +9,10 @@ typedef struct _CABINSFILE { CABH cabh; + WORD sab; CHAR **hszFile; - int iDirectory; CHAR **hszRange; + int iDirectory; BOOL fLink; } CABINSFILE; diff --git a/src/port/original/inspic.hs b/src/port/original/inspic.hs index 55fcbe9..a993c3c 100644 --- a/src/port/original/inspic.hs +++ b/src/port/original/inspic.hs @@ -6,14 +6,13 @@ #define tmcIPDir (tmcUserMin + 2) #define tmcNewPic (tmcUserMin + 3) -#pragma pack(push, 2) typedef struct _CABINSPIC { CABH cabh; + WORD sab; CHAR **hszFile; int iDirectory; } CABINSPIC; -#pragma pack(pop) typedef CABINSPIC **HCABINSPIC; #define cabiCABINSPIC Cabi((sizeof(CABINSPIC) + sizeof(WORD) - 1) / sizeof(WORD), 1) diff --git a/src/port/original/new.hs b/src/port/original/new.hs index 36433a5..ed9281f 100644 --- a/src/port/original/new.hs +++ b/src/port/original/new.hs @@ -11,8 +11,9 @@ typedef struct _CABNEWDOC { CABH cabh; - BOOL fNewDot; + WORD sab; CHAR **hszNewType; + BOOL fNewDot; } CABNEWDOC; typedef CABNEWDOC **HCABNEWDOC; diff --git a/src/port/original/new.sdm b/src/port/original/new.sdm index 9f5d9c5..6f4e945 100644 --- a/src/port/original/new.sdm +++ b/src/port/original/new.sdm @@ -1,2 +1,7 @@ #pragma once -static DLG_CONST DLT dltNewDoc = { 0 }; +/* Regenerated from Opus/dlg/new.des. */ +extern BOOL FDlgNew(DLM, TMC, WORD, WORD, WORD); +static DLG_CONST DLT dltNewDoc = { + { 20, 24, 127, 114 }, IDDNewDoc, tmcNewType, FDlgNew, + 9, bdrSysMenu, { 0 } +}; diff --git a/src/port/original/newopen.hs b/src/port/original/newopen.hs index a3a0a78..1ab4a2c 100644 --- a/src/port/original/newopen.hs +++ b/src/port/original/newopen.hs @@ -4,6 +4,7 @@ typedef struct _CABNEWOPEN { CABH cabh; + WORD sab; CHAR **hszFileName; } CABNEWOPEN; diff --git a/src/port/original/open.hs b/src/port/original/open.hs index 38703e3..ff7cff1 100644 --- a/src/port/original/open.hs +++ b/src/port/original/open.hs @@ -10,6 +10,7 @@ typedef struct _CABOPEN { CABH cabh; + WORD sab; CHAR **hszFile; int iDirectory; BOOL fReadOnly; diff --git a/src/port/original/open.sdm b/src/port/original/open.sdm index 2be498e..360ceaa 100644 --- a/src/port/original/open.sdm +++ b/src/port/original/open.sdm @@ -1,2 +1,7 @@ #pragma once -static DLG_CONST DLT dltOpen = { 0 }; +/* Regenerated from Opus/dlg/open.des. */ +extern BOOL FDlgOpen(DLM, TMC, WORD, WORD, WORD); +static DLG_CONST DLT dltOpen = { + { 8, 24, 206, 104 }, IDDOpen, tmcOpenFileName, FDlgOpen, + 11, bdrSysMenu, { 0 } +}; diff --git a/src/port/original/opus_asm_filewin.cpp b/src/port/original/opus_asm_filewin.cpp index 81b069b..6fb38cb 100644 --- a/src/port/original/opus_asm_filewin.cpp +++ b/src/port/original/opus_asm_filewin.cpp @@ -88,7 +88,7 @@ int DosxError() { error, static_cast((std::numeric_limits::max)()))); } -void* HpOfBptbExt(const int page_index) { +unsigned char* HpOfBptbExt(const int page_index) { if (vbptbExt.pages == nullptr || page_index < 0 || page_index >= vbptbExt.ibp_max) { return nullptr; @@ -97,7 +97,7 @@ void* HpOfBptbExt(const int page_index) { } unsigned char* HpBaseForIbp(const int page_index) { - return static_cast(HpOfBptbExt(page_index)); + return HpOfBptbExt(page_index); } void OsTime(NativeTime* time) { diff --git a/src/port/original/opus_asm_wproc.cpp b/src/port/original/opus_asm_wproc.cpp index 49ac236..a14e608 100644 --- a/src/port/original/opus_asm_wproc.cpp +++ b/src/port/original/opus_asm_wproc.cpp @@ -50,6 +50,7 @@ constexpr UINT kWmDdeData = 0x03e5; constexpr UINT kWmDdeRequest = 0x03e6; constexpr UINT kWmDdePoke = 0x03e7; constexpr UINT kWmDdeExecute = 0x03e8; +constexpr UINT kWmOpusX64QuerySelection = WM_APP + 0x351; enum class MouseCursor { none, ibeam, arrow }; @@ -94,7 +95,8 @@ constexpr UINT kMwdMessages[] = { constexpr UINT kWwPaneMessages[] = { WM_CREATE, WM_SIZE, WM_SETFOCUS, WM_KILLFOCUS, WM_PAINT, WM_MOUSEACTIVATE, WM_SETCURSOR, WM_MOUSEMOVE, WM_LBUTTONDOWN, - WM_LBUTTONDBLCLK, WM_RBUTTONDOWN, WM_SYSCOMMAND, WM_MENUCHAR}; + WM_LBUTTONDBLCLK, WM_RBUTTONDOWN, WM_SYSCOMMAND, WM_MENUCHAR, + kWmOpusX64QuerySelection}; constexpr UINT kDdeMessages[] = { kWmDdeTerminate, kWmDdeAdvise, kWmDdeUnadvise, kWmDdeAck, kWmDdeData, kWmDdeRequest, kWmDdePoke, kWmDdeExecute}; diff --git a/src/port/original/opus_original_command_test.c b/src/port/original/opus_original_command_test.c index 6c20da6..ba087c6 100644 --- a/src/port/original/opus_original_command_test.c +++ b/src/port/original/opus_original_command_test.c @@ -3,13 +3,14 @@ int main(void) { - /* Values are generated by Microsoft's original MKCMD implementation - * from opuscmd.cmd and the checked-in ibcm.rel compatibility map. */ - if (bcmCut != 1686 || bcmCopy != 1702) + /* Values are generated by Microsoft's original MKCMD parser from + * opuscmd.cmd. On x64 these are offsets in the expanded native symbol + * table; IBCM remains the original compatibility-map index. */ + if (bcmCut != 2252 || bcmCopy != 2274) return 1; if (ibcmEditCut != 86 || ibcmEditCopy != 87) return 2; - if (bcmMacStd != 8295) + if (bcmMacStd != 11247) return 3; return 0; } diff --git a/src/port/original/opus_original_startup_probe.cpp b/src/port/original/opus_original_startup_probe.cpp index 996318e..07cb25b 100644 --- a/src/port/original/opus_original_startup_probe.cpp +++ b/src/port/original/opus_original_startup_probe.cpp @@ -321,6 +321,7 @@ LONG WINAPI ObserveVectoredException(EXCEPTION_POINTERS* exception) { static volatile LONG writing_exception = 0; if (exception != nullptr && exception->ExceptionRecord != nullptr && (exception->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION || + exception->ExceptionRecord->ExceptionCode == EXCEPTION_INT_DIVIDE_BY_ZERO || exception->ExceptionRecord->ExceptionCode == 0xC0000374u || exception->ExceptionRecord->ExceptionCode == 0xC0000409u || exception->ExceptionRecord->ExceptionCode == 0xE0421001u) && diff --git a/src/port/original/opus_sdm_cab.cpp b/src/port/original/opus_sdm_cab.cpp index 236a93c..20e8d1b 100644 --- a/src/port/original/opus_sdm_cab.cpp +++ b/src/port/original/opus_sdm_cab.cpp @@ -28,6 +28,9 @@ constexpr std::size_t kWordBytes = sizeof(Word); constexpr std::size_t kCabMinWords = (sizeof(CabHeader) + sizeof(Word)) / kWordBytes; constexpr std::size_t kPointerWords = sizeof(void*) / kWordBytes; +constexpr std::size_t kHandleBaseWords = + (kCabMinWords + kPointerWords - 1) / kPointerWords * kPointerWords; +constexpr std::size_t kHandleBaseIag = kHandleBaseWords - kCabMinWords; constexpr Word kNinch = 0xffff; std::byte* cab_bytes(const CabHandle cab) noexcept { @@ -45,8 +48,9 @@ std::size_t cab_word_count(const CabHandle cab) noexcept { void*** handle_slot(const CabHandle cab, const Word argument_index) noexcept { auto* const header = cab_header(cab); - if (header == nullptr || argument_index >= header->handle_words || - argument_index % kPointerWords != 0) { + if (header == nullptr || argument_index < kHandleBaseIag || + argument_index >= kHandleBaseIag + header->handle_words || + (argument_index - kHandleBaseIag) % kPointerWords != 0) { return nullptr; } return reinterpret_cast( @@ -58,7 +62,8 @@ void clear_cab_data(const CabHandle cab) noexcept { if (header == nullptr) { return; } - for (Word index = 0; index < header->handle_words; + for (Word index = static_cast(kHandleBaseIag); + index < kHandleBaseIag + header->handle_words; index = static_cast(index + kPointerWords)) { void*** const slot = handle_slot(cab, index); if (slot != nullptr && *slot != nullptr) { @@ -77,7 +82,8 @@ bool initialize_cab(const CabHandle cab, const Word initializer) noexcept { const std::size_t total_words = initializer & 0x00ffu; const std::size_t handle_count = initializer >> 8u; if (total_words < kCabMinWords || total_words > cab_word_count(cab) || - handle_count * kPointerWords > total_words - kCabMinWords) { + kHandleBaseIag + handle_count * kPointerWords > + total_words - kCabMinWords) { return false; } @@ -149,7 +155,7 @@ void copy_sz(const CabHandle cab, char* destination, extern "C" { CabHandle hcabDlgCur = nullptr; -Word wRefDlgCur = 0; +std::uintptr_t wRefDlgCur = 0; CabHandle HcabAlloc_sdm21(const Word initializer) { const std::size_t total_words = initializer & 0x00ffu; @@ -193,7 +199,11 @@ void NinchCab(const CabHandle cab) { } clear_cab_data(cab); auto* words = reinterpret_cast(cab_bytes(cab)); - for (Word index = header->handle_words; index < header->simple_words; + const Word simple_first = header->handle_words == 0 + ? 0 + : static_cast(kHandleBaseIag + + header->handle_words); + for (Word index = simple_first; index < header->simple_words; ++index) { words[kCabMinWords + index] = kNinch; } @@ -312,13 +322,18 @@ CabHandle HcabDupeCab(const CabHandle source) { auto* const destination_header = cab_header(duplicate); auto* const source_words = reinterpret_cast(cab_bytes(source)); auto* const destination_words = reinterpret_cast(cab_bytes(duplicate)); - for (Word index = source_header->handle_words; + const Word simple_first = source_header->handle_words == 0 + ? 0 + : static_cast(kHandleBaseIag + + source_header->handle_words); + for (Word index = simple_first; index < source_header->simple_words; ++index) { destination_words[kCabMinWords + index] = source_words[kCabMinWords + index]; } - for (Word index = 0; index < source_header->handle_words; + for (Word index = static_cast(kHandleBaseIag); + index < kHandleBaseIag + source_header->handle_words; index = static_cast(index + kPointerWords)) { void*** const source_slot = handle_slot(source, index); if (source_slot != nullptr && *source_slot != nullptr && diff --git a/src/port/original/opus_sdm_cab_test.cpp b/src/port/original/opus_sdm_cab_test.cpp index 47085b8..41e6ed0 100644 --- a/src/port/original/opus_sdm_cab_test.cpp +++ b/src/port/original/opus_sdm_cab_test.cpp @@ -22,8 +22,12 @@ CabHandle HcabDupeCab(CabHandle); constexpr Word kCabMinWords = 3; constexpr Word kPointerWords = sizeof(void*) / sizeof(Word); +constexpr Word kHandleBaseIag = + ((kCabMinWords + kPointerWords - 1) / kPointerWords * kPointerWords) - + kCabMinWords; constexpr Word kHandleCount = 2; -constexpr Word kArgumentWords = kHandleCount * kPointerWords + 2; +constexpr Word kArgumentWords = + kHandleBaseIag + kHandleCount * kPointerWords + 2; constexpr Word kTotalWords = kCabMinWords + kArgumentWords; constexpr Word kInitializer = kTotalWords | (kHandleCount << 8); @@ -44,43 +48,46 @@ int main() { return fail(__LINE__); } - if (!FSetCabSz(cab, "Alpha", 0)) { + if (!FSetCabSz(cab, "Alpha", kHandleBaseIag)) { return fail(__LINE__); } const unsigned char counted[] = {4, 'B', 'e', 't', 'a'}; - if (!FSetCabSt(cab, counted, kPointerWords)) { + if (!FSetCabSt(cab, counted, kHandleBaseIag + kPointerWords)) { return fail(__LINE__); } char sz[16]{}; - GetCabSz(cab, sz, sizeof(sz), 0); + GetCabSz(cab, sz, sizeof(sz), kHandleBaseIag); if (std::strcmp(sz, "Alpha") != 0) { return fail(__LINE__); } unsigned char stz[16]{}; - GetCabStz(cab, stz, sizeof(stz), kPointerWords); + GetCabStz(cab, stz, sizeof(stz), kHandleBaseIag + kPointerWords); if (stz[0] != 4 || std::memcmp(stz + 1, "Beta", 5) != 0) { return fail(__LINE__); } auto* mutable_words = static_cast(OpusDerefH(cab)); - mutable_words[kCabMinWords + kHandleCount * kPointerWords] = 42; + mutable_words[kCabMinWords + kHandleBaseIag + + kHandleCount * kPointerWords] = 42; CabHandle const duplicate = HcabDupeCab(cab); if (duplicate == nullptr) { return fail(__LINE__); } - GetCabSz(duplicate, sz, sizeof(sz), 0); + GetCabSz(duplicate, sz, sizeof(sz), kHandleBaseIag); if (std::strcmp(sz, "Alpha") != 0 || static_cast(OpusDerefH(duplicate)) - [kCabMinWords + kHandleCount * kPointerWords] != 42) { + [kCabMinWords + kHandleBaseIag + + kHandleCount * kPointerWords] != 42) { return fail(__LINE__); } NinchCab(cab); - GetCabSz(cab, sz, sizeof(sz), 0); + GetCabSz(cab, sz, sizeof(sz), kHandleBaseIag); mutable_words = static_cast(OpusDerefH(cab)); if (sz[0] != '\0' || - mutable_words[kCabMinWords + kHandleCount * kPointerWords] != + mutable_words[kCabMinWords + kHandleBaseIag + + kHandleCount * kPointerWords] != 0xffffu) { return fail(__LINE__); } diff --git a/src/port/original/opus_sdm_runtime.cpp b/src/port/original/opus_sdm_runtime.cpp index 476169b..3d9bbe5 100644 --- a/src/port/original/opus_sdm_runtime.cpp +++ b/src/port/original/opus_sdm_runtime.cpp @@ -1,4 +1,5 @@ #include "opus_x64_compat.h" +#include "opus_x64_heap.h" extern "C" { #include "dac.h" } @@ -13,7 +14,10 @@ extern "C" { extern "C" { extern void** hcabDlgCur; -extern std::uint16_t wRefDlgCur; +extern std::uintptr_t wRefDlgCur; +extern HWND vhWndMsgBoxParent; +void GetCabSz(void**, char*, std::uint16_t, std::uint16_t); +int FSetCabSz(void**, const char*, std::uint16_t); } /* @@ -53,7 +57,7 @@ struct Dli { int dx; int dy; Dword flags; - Word reference; + std::uintptr_t reference; unsigned char* runtime_items; }; @@ -75,15 +79,21 @@ struct DialogState { DltHeader** template_handle = nullptr; Hcab cab = nullptr; HWND window = nullptr; - Word reference = 0; + std::uintptr_t reference = 0; Word hid = 0; Word sab = 0; Tmc focus = 0; Tmc default_tmc = 1; + Tmc result_tmc = 2; bool visible = false; bool dying = false; bool modal = false; + bool native_modal = false; + bool commands_active = false; std::string caption; + std::string current_directory; + std::string file_pattern; + HWND directory_label = nullptr; std::unordered_map controls; }; @@ -95,7 +105,39 @@ Hdlg g_focus_dialog = 0; bool g_initialized = false; bool g_noninteractive = false; +constexpr Word kDlmInit = 0x0001; +constexpr Word kDlmTerm = 0x0003; +constexpr Word kDlmExit = 0x0004; +constexpr Word kDlmChange = 0x0005; +constexpr Word kDlmClick = 0x0006; +constexpr Word kDlmDblClk = 0x0007; +constexpr Word kIddNewDoc = 2; +constexpr Word kIddOpen = 3; +constexpr Tmc kTmcOk = 1; +constexpr Tmc kTmcCancel = 2; +constexpr Tmc kTmcSummary = 0x0400; +constexpr Tmc kTmcNewDot = 0x0401; +constexpr Tmc kTmcRNewDoc = 0x0402; +constexpr Tmc kTmcRNewDot = 0x0403; +constexpr Tmc kTmcNewType = 0x0404; +constexpr Tmc kTmcNewTypeList = 0x0405; +constexpr Tmc kTmcOpenFileName = 0x0400; +constexpr Tmc kTmcOpenFileList = 0x0401; +constexpr Tmc kTmcOpenFileDir = 0x0402; +constexpr Tmc kTmcOpenCatalog = 0x0403; +constexpr Tmc kTmcOpenReadOnly = 0x0404; +constexpr Word kOpenFileNameIag = 1; +constexpr std::size_t kOpenCabBytes = 24; +constexpr std::size_t kOpenReadOnlyOffset = 20; +constexpr Word kNewTypeIag = 1; +constexpr std::size_t kNewCabBytes = 24; +constexpr std::size_t kNewDotOffset = 16; + +using NativeDialogProc = int (*)(Word, Tmc, Word, Word, Word); + void set_sds_handle(Hdlg current, Hdlg focus); +LRESULT CALLBACK native_dialog_window_proc(HWND, UINT, WPARAM, LPARAM); +void handle_dialog_command(Hdlg, WPARAM, LPARAM); DialogState* find_dialog(const Hdlg dialog) { const auto found = g_dialogs.find(dialog); @@ -134,7 +176,76 @@ int scaled_y(const int value) { return MulDiv(value, dac.dySysFontChar == 0 ? 16 : dac.dySysFontChar, 8); } +ATOM ensure_native_dialog_class() { + static ATOM atom = 0; + if (atom != 0) { + return atom; + } + WNDCLASSEXA cls{}; + cls.cbSize = sizeof(cls); + cls.style = CS_DBLCLKS; + cls.lpfnWndProc = native_dialog_window_proc; + cls.hInstance = GetModuleHandleW(nullptr); + cls.hCursor = LoadCursorA(nullptr, IDC_ARROW); + cls.hbrBackground = reinterpret_cast(COLOR_BTNFACE + 1); + cls.lpszClassName = "OpusSdmDialog"; + atom = RegisterClassExA(&cls); + if (atom == 0 && GetLastError() == ERROR_CLASS_ALREADY_EXISTS) { + atom = 1; + } + return atom; +} + HWND create_dialog_host(const DialogState& dialog, const Dli* initializer) { + if (dialog.modal && + (dialog.hid == kIddOpen || dialog.hid == kIddNewDoc)) { + if (ensure_native_dialog_class() == 0) { + return nullptr; + } + + HWND owner = initializer != nullptr ? initializer->hwnd : nullptr; + if (owner == nullptr || !IsWindow(owner)) { + owner = vhWndMsgBoxParent; + } + if (owner == nullptr || !IsWindow(owner)) { + owner = GetActiveWindow(); + } + + int client_width = scaled_x(dialog.hid == kIddNewDoc ? 127 : 206); + int client_height = scaled_y(dialog.hid == kIddNewDoc ? 114 : 104); + if (dialog.template_handle != nullptr && + *dialog.template_handle != nullptr) { + client_width = (std::max)(1, scaled_x( + (*dialog.template_handle)->rec.dx)); + client_height = (std::max)(1, scaled_y( + (*dialog.template_handle)->rec.dy)); + } + constexpr DWORD style = + WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_CLIPCHILDREN; + constexpr DWORD extended_style = + WS_EX_DLGMODALFRAME | WS_EX_CONTROLPARENT; + RECT window_rect{0, 0, client_width, client_height}; + AdjustWindowRectEx(&window_rect, style, false, extended_style); + const int width = window_rect.right - window_rect.left; + const int height = window_rect.bottom - window_rect.top; + + RECT anchor{}; + if (owner == nullptr || !GetWindowRect(owner, &anchor)) { + SystemParametersInfoA(SPI_GETWORKAREA, 0, &anchor, 0); + } + const int x = anchor.left + + (anchor.right - anchor.left - width) / 2; + const int y = anchor.top + + (anchor.bottom - anchor.top - height) / 2; + const char* const caption = + dialog.hid == kIddNewDoc ? "New" : "Open"; + return CreateWindowExA( + extended_style, "OpusSdmDialog", caption, style, x, y, width, + height, owner, nullptr, GetModuleHandleW(nullptr), + reinterpret_cast(static_cast( + dialog.handle))); + } + if (initializer == nullptr || initializer->hwnd == nullptr || !IsWindow(initializer->hwnd)) { return nullptr; @@ -208,6 +319,377 @@ void create_static_text(DialogState& dialog, const char* caption, } } +void create_untracked_control(DialogState& dialog, const char* window_class, + const char* caption, const Rec& rectangle, + const DWORD control_style) { + if (dialog.window == nullptr || !IsWindow(dialog.window)) { + return; + } + const HWND window = CreateWindowExA( + 0, window_class, caption, + WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | control_style, + scaled_x(rectangle.x), scaled_y(rectangle.y), + (std::max)(1, scaled_x(rectangle.dx)), + (std::max)(1, scaled_y(rectangle.dy)), dialog.window, nullptr, + GetModuleHandleW(nullptr), nullptr); + if (window != nullptr) { + SendMessageA(window, WM_SETFONT, + reinterpret_cast(GetStockObject(DEFAULT_GUI_FONT)), + false); + } +} + +std::string join_path(const std::string& directory, + const std::string& leaf) { + if (directory.empty()) { + return leaf; + } + const char last = directory.back(); + return directory + (last == '\\' || last == '/' ? "" : "\\") + leaf; +} + +void add_native_list_entry(DialogState& dialog, const Tmc tmc, + const std::string& entry) { + auto& state = dialog.controls[tmc]; + state.entries.push_back(entry); + if (state.window != nullptr && IsWindow(state.window)) { + SendMessageA(state.window, LB_ADDSTRING, 0, + reinterpret_cast(entry.c_str())); + } +} + +void set_open_directory_label(DialogState& dialog) { + if (dialog.directory_label != nullptr && + IsWindow(dialog.directory_label)) { + SetWindowTextA(dialog.directory_label, + dialog.current_directory.c_str()); + } +} + +void establish_open_directory(DialogState& dialog, + const std::string& specification) { + char full_path[32768] = {}; + char* file_part = nullptr; + const std::string source = specification.empty() ? "*.*" : specification; + const DWORD length = GetFullPathNameA( + source.c_str(), static_cast(sizeof(full_path)), full_path, + &file_part); + if (length == 0 || length >= sizeof(full_path)) { + GetCurrentDirectoryA(static_cast(sizeof(full_path)), full_path); + dialog.current_directory = full_path; + dialog.file_pattern = "*.*"; + return; + } + + const DWORD attributes = GetFileAttributesA(full_path); + if (attributes != INVALID_FILE_ATTRIBUTES && + (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0) { + dialog.current_directory = full_path; + dialog.file_pattern = "*.*"; + return; + } + + dialog.file_pattern = + file_part == nullptr || *file_part == '\0' ? "*.*" : file_part; + if (file_part == nullptr || file_part == full_path) { + GetCurrentDirectoryA(static_cast(sizeof(full_path)), full_path); + dialog.current_directory = full_path; + } else { + std::size_t directory_length = + static_cast(file_part - full_path); + while (directory_length > 3 && + (full_path[directory_length - 1] == '\\' || + full_path[directory_length - 1] == '/')) { + --directory_length; + } + dialog.current_directory.assign(full_path, directory_length); + } +} + +void populate_open_lists(DialogState& dialog) { + auto& files = dialog.controls[kTmcOpenFileList]; + auto& directories = dialog.controls[kTmcOpenFileDir]; + files.entries.clear(); + directories.entries.clear(); + if (files.window != nullptr) { + SendMessageA(files.window, LB_RESETCONTENT, 0, 0); + } + if (directories.window != nullptr) { + SendMessageA(directories.window, LB_RESETCONTENT, 0, 0); + } + + WIN32_FIND_DATAA find_data{}; + HANDLE find = FindFirstFileA( + join_path(dialog.current_directory, dialog.file_pattern).c_str(), + &find_data); + if (find != INVALID_HANDLE_VALUE) { + do { + if ((find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) { + add_native_list_entry(dialog, kTmcOpenFileList, + find_data.cFileName); + } + } while (FindNextFileA(find, &find_data)); + FindClose(find); + } + + char parent_path[32768] = {}; + const std::string parent_spec = join_path(dialog.current_directory, ".."); + if (GetFullPathNameA(parent_spec.c_str(), + static_cast(sizeof(parent_path)), parent_path, + nullptr) != 0 && + _stricmp(parent_path, dialog.current_directory.c_str()) != 0) { + add_native_list_entry(dialog, kTmcOpenFileDir, "[..]"); + } + find = FindFirstFileA(join_path(dialog.current_directory, "*.*").c_str(), + &find_data); + if (find != INVALID_HANDLE_VALUE) { + do { + if ((find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0 && + std::strcmp(find_data.cFileName, ".") != 0 && + std::strcmp(find_data.cFileName, "..") != 0) { + add_native_list_entry( + dialog, kTmcOpenFileDir, + "[" + std::string(find_data.cFileName) + "]"); + } + } while (FindNextFileA(find, &find_data)); + FindClose(find); + } + set_open_directory_label(dialog); +} + +void read_open_cab(DialogState& dialog) { + char file_name[32768] = {}; + if (dialog.cab != nullptr) { + GetCabSz(dialog.cab, file_name, + static_cast((std::min)(sizeof(file_name), + std::size_t{0xffffu})), + kOpenFileNameIag); + } + auto& edit = dialog.controls[kTmcOpenFileName]; + edit.text = file_name; + if (edit.window != nullptr) { + SetWindowTextA(edit.window, edit.text.c_str()); + } + + if (dialog.cab != nullptr && *dialog.cab != nullptr && + OpusCbOfH(dialog.cab) >= kOpenCabBytes) { + int read_only = 0; + std::memcpy(&read_only, + static_cast(*dialog.cab) + + kOpenReadOnlyOffset, + sizeof(read_only)); + auto& checkbox = dialog.controls[kTmcOpenReadOnly]; + checkbox.value = read_only != 0; + if (checkbox.window != nullptr) { + SendMessageA(checkbox.window, BM_SETCHECK, + checkbox.value ? BST_CHECKED : BST_UNCHECKED, 0); + } + } + establish_open_directory(dialog, edit.text); + populate_open_lists(dialog); +} + +void sync_open_cab(DialogState& dialog) { + auto edit = dialog.controls.find(kTmcOpenFileName); + if (edit != dialog.controls.end() && edit->second.window != nullptr) { + const int length = GetWindowTextLengthA(edit->second.window); + std::vector buffer(static_cast(length) + 1); + GetWindowTextA(edit->second.window, buffer.data(), + static_cast(buffer.size())); + edit->second.text = buffer.data(); + } + if (dialog.cab != nullptr && edit != dialog.controls.end()) { + FSetCabSz(dialog.cab, edit->second.text.c_str(), kOpenFileNameIag); + } + + auto checkbox = dialog.controls.find(kTmcOpenReadOnly); + if (checkbox != dialog.controls.end() && checkbox->second.window != nullptr) { + checkbox->second.value = static_cast( + SendMessageA(checkbox->second.window, BM_GETCHECK, 0, 0) == + BST_CHECKED); + } + if (dialog.cab != nullptr && *dialog.cab != nullptr && + OpusCbOfH(dialog.cab) >= kOpenCabBytes && + checkbox != dialog.controls.end()) { + const int read_only = checkbox->second.value != 0; + std::memcpy(static_cast(*dialog.cab) + + kOpenReadOnlyOffset, + &read_only, sizeof(read_only)); + } +} + +void populate_new_type_list(DialogState& dialog) { + auto& list = dialog.controls[kTmcNewTypeList]; + list.entries.clear(); + if (list.window != nullptr) { + SendMessageA(list.window, LB_RESETCONTENT, 0, 0); + } + + const std::string initial = dialog.controls[kTmcNewType].text; + add_native_list_entry(dialog, kTmcNewTypeList, + initial.empty() ? "NORMAL" : initial); + + WIN32_FIND_DATAA find_data{}; + HANDLE find = FindFirstFileA("*.DOT", &find_data); + if (find == INVALID_HANDLE_VALUE) { + return; + } + do { + if ((find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) { + continue; + } + const auto duplicate = std::find_if( + list.entries.begin(), list.entries.end(), + [&find_data](const std::string& entry) { + return _stricmp(entry.c_str(), find_data.cFileName) == 0; + }); + if (duplicate == list.entries.end()) { + add_native_list_entry(dialog, kTmcNewTypeList, + find_data.cFileName); + } + } while (FindNextFileA(find, &find_data)); + FindClose(find); +} + +void read_new_cab(DialogState& dialog) { + char type_name[32768] = {}; + if (dialog.cab != nullptr) { + GetCabSz(dialog.cab, type_name, + static_cast((std::min)(sizeof(type_name), + std::size_t{0xffffu})), + kNewTypeIag); + } + auto& edit = dialog.controls[kTmcNewType]; + edit.text = type_name; + if (edit.window != nullptr) { + SetWindowTextA(edit.window, edit.text.c_str()); + } + + bool new_template = false; + if (dialog.cab != nullptr && *dialog.cab != nullptr && + OpusCbOfH(dialog.cab) >= kNewCabBytes) { + int value = 0; + std::memcpy(&value, + static_cast(*dialog.cab) + + kNewDotOffset, + sizeof(value)); + new_template = value != 0; + } + dialog.controls[kTmcNewDot].value = new_template; + dialog.controls[kTmcRNewDoc].value = !new_template; + dialog.controls[kTmcRNewDot].value = new_template; + if (dialog.controls[kTmcRNewDoc].window != nullptr) { + SendMessageA(dialog.controls[kTmcRNewDoc].window, BM_SETCHECK, + new_template ? BST_UNCHECKED : BST_CHECKED, 0); + } + if (dialog.controls[kTmcRNewDot].window != nullptr) { + SendMessageA(dialog.controls[kTmcRNewDot].window, BM_SETCHECK, + new_template ? BST_CHECKED : BST_UNCHECKED, 0); + } + populate_new_type_list(dialog); +} + +void sync_new_cab(DialogState& dialog) { + auto edit = dialog.controls.find(kTmcNewType); + if (edit != dialog.controls.end() && edit->second.window != nullptr) { + const int length = GetWindowTextLengthA(edit->second.window); + std::vector buffer(static_cast(length) + 1); + GetWindowTextA(edit->second.window, buffer.data(), + static_cast(buffer.size())); + edit->second.text = buffer.data(); + } + if (dialog.cab != nullptr && edit != dialog.controls.end()) { + FSetCabSz(dialog.cab, edit->second.text.c_str(), kNewTypeIag); + } + + bool new_template = false; + const auto radio = dialog.controls.find(kTmcRNewDot); + if (radio != dialog.controls.end() && radio->second.window != nullptr) { + new_template = SendMessageA(radio->second.window, BM_GETCHECK, 0, 0) == + BST_CHECKED; + } + dialog.controls[kTmcNewDot].value = new_template; + if (dialog.cab != nullptr && *dialog.cab != nullptr && + OpusCbOfH(dialog.cab) >= kNewCabBytes) { + const int value = new_template; + std::memcpy(static_cast(*dialog.cab) + kNewDotOffset, + &value, sizeof(value)); + } +} + +void materialize_new_template(DialogState& dialog) { + if (dialog.hid != kIddNewDoc || dialog.window == nullptr) { + return; + } + create_native_control(dialog, kTmcOk, "BUTTON", "OK", {74, 6, 46, 14}, + WS_TABSTOP | BS_DEFPUSHBUTTON); + create_native_control(dialog, kTmcCancel, "BUTTON", "Cancel", + {74, 23, 46, 14}, WS_TABSTOP | BS_PUSHBUTTON); + create_native_control(dialog, kTmcSummary, "BUTTON", "&Summary...", + {74, 39, 46, 14}, WS_TABSTOP | BS_PUSHBUTTON); + create_untracked_control(dialog, "BUTTON", "New", {5, 2, 61, 36}, + BS_GROUPBOX); + create_native_control(dialog, kTmcRNewDoc, "BUTTON", "&Document", + {9, 12, 42, 10}, + WS_TABSTOP | WS_GROUP | BS_AUTORADIOBUTTON); + create_native_control(dialog, kTmcRNewDot, "BUTTON", "&Template", + {9, 24, 42, 10}, + WS_TABSTOP | BS_AUTORADIOBUTTON); + create_static_text(dialog, "&Use Template:", {5, 42, 53, 10}); + create_native_control(dialog, kTmcNewType, "EDIT", "", + {5, 53, 61, 12}, + WS_TABSTOP | WS_BORDER | ES_AUTOHSCROLL); + create_native_control(dialog, kTmcNewTypeList, "LISTBOX", "", + {8, 65, 68, 40}, + WS_TABSTOP | WS_BORDER | WS_VSCROLL | LBS_NOTIFY | + LBS_SORT | LBS_NOINTEGRALHEIGHT); + dialog.caption = "New"; + dialog.native_modal = true; + read_new_cab(dialog); +} + +void materialize_open_template(DialogState& dialog) { + if (dialog.hid != kIddOpen || dialog.window == nullptr) { + return; + } + create_native_control(dialog, kTmcOpenFileName, "EDIT", "", + {67, 6, 83, 12}, + WS_TABSTOP | WS_BORDER | ES_AUTOHSCROLL); + create_static_text(dialog, "Open File &Name:", {5, 7, 61, 9}); + create_native_control(dialog, kTmcOpenFileList, "LISTBOX", "", + {5, 32, 60, 64}, + WS_TABSTOP | WS_BORDER | WS_VSCROLL | LBS_NOTIFY | + LBS_SORT | LBS_NOINTEGRALHEIGHT); + create_static_text(dialog, "&Files:", {5, 21, 25, 9}); + create_native_control(dialog, kTmcOpenFileDir, "LISTBOX", "", + {71, 48, 65, 48}, + WS_TABSTOP | WS_BORDER | WS_VSCROLL | LBS_NOTIFY | + LBS_SORT | LBS_NOINTEGRALHEIGHT); + create_static_text(dialog, "&Directories:", {71, 38, 50, 9}); + dialog.directory_label = CreateWindowExA( + 0, "STATIC", "", WS_CHILD | WS_VISIBLE | SS_LEFT, + scaled_x(70), scaled_y(24), scaled_x(78), scaled_y(8), + dialog.window, nullptr, GetModuleHandleW(nullptr), nullptr); + if (dialog.directory_label != nullptr) { + SendMessageA(dialog.directory_label, WM_SETFONT, + reinterpret_cast(GetStockObject(DEFAULT_GUI_FONT)), + false); + } + create_native_control(dialog, kTmcOk, "BUTTON", "OK", + {154, 5, 47, 14}, + WS_TABSTOP | BS_DEFPUSHBUTTON); + create_native_control(dialog, kTmcCancel, "BUTTON", "Cancel", + {154, 22, 47, 14}, WS_TABSTOP | BS_PUSHBUTTON); + create_native_control(dialog, kTmcOpenCatalog, "BUTTON", "F&ind...", + {154, 39, 47, 14}, WS_TABSTOP | BS_PUSHBUTTON); + create_native_control(dialog, kTmcOpenReadOnly, "BUTTON", "&Read Only", + {155, 56, 47, 12}, + WS_TABSTOP | BS_AUTOCHECKBOX); + dialog.caption = "Open"; + dialog.native_modal = true; + read_open_cab(dialog); +} + void materialize_icon_bar_template(DialogState& dialog) { constexpr Word cxtRibbonIconBar = 0x8005; constexpr Word cxtRulerIconBar = 0x8006; @@ -290,6 +772,244 @@ std::string counted_or_zero_terminated(const char* text) { return std::string(text); } +bool invoke_dialog_proc(DialogState& dialog, const Word message, + const Tmc tmc, const Word new_value = 0, + const Word old_value = 0, + const Word parameter = 0) { + if (dialog.template_handle == nullptr || + *dialog.template_handle == nullptr || + (*dialog.template_handle)->dialog_proc == nullptr) { + return true; + } + const auto proc = reinterpret_cast( + (*dialog.template_handle)->dialog_proc); + return proc(message, tmc, new_value, old_value, parameter) != 0; +} + +std::string selected_list_text(const ControlState& control_state) { + if (control_state.window == nullptr) { + return {}; + } + const LRESULT selection = + SendMessageA(control_state.window, LB_GETCURSEL, 0, 0); + if (selection == LB_ERR) { + return {}; + } + const LRESULT length = + SendMessageA(control_state.window, LB_GETTEXTLEN, selection, 0); + if (length == LB_ERR) { + return {}; + } + std::vector text(static_cast(length) + 1); + SendMessageA(control_state.window, LB_GETTEXT, selection, + reinterpret_cast(text.data())); + return text.data(); +} + +void select_new_type(DialogState& dialog) { + const auto found = dialog.controls.find(kTmcNewTypeList); + if (found == dialog.controls.end()) { + return; + } + const std::string selected = selected_list_text(found->second); + if (selected.empty()) { + return; + } + auto& edit = dialog.controls[kTmcNewType]; + edit.text = selected; + if (edit.window != nullptr) { + SetWindowTextA(edit.window, edit.text.c_str()); + SendMessageA(edit.window, EM_SETSEL, 0, static_cast(-1)); + } +} + +void select_open_file(DialogState& dialog) { + const auto found = dialog.controls.find(kTmcOpenFileList); + if (found == dialog.controls.end()) { + return; + } + const std::string selected = selected_list_text(found->second); + if (selected.empty()) { + return; + } + auto& edit = dialog.controls[kTmcOpenFileName]; + edit.text = join_path(dialog.current_directory, selected); + if (edit.window != nullptr) { + SetWindowTextA(edit.window, edit.text.c_str()); + SendMessageA(edit.window, EM_SETSEL, 0, + static_cast(-1)); + } +} + +void enter_open_directory(DialogState& dialog) { + const auto found = dialog.controls.find(kTmcOpenFileDir); + if (found == dialog.controls.end()) { + return; + } + std::string selected = selected_list_text(found->second); + if (selected.size() < 2 || selected.front() != '[' || + selected.back() != ']') { + return; + } + selected = selected.substr(1, selected.size() - 2); + char full_path[32768] = {}; + const std::string destination = + join_path(dialog.current_directory, selected); + if (GetFullPathNameA(destination.c_str(), + static_cast(sizeof(full_path)), full_path, + nullptr) == 0) { + return; + } + dialog.current_directory = full_path; + populate_open_lists(dialog); + auto& edit = dialog.controls[kTmcOpenFileName]; + edit.text = join_path(dialog.current_directory, dialog.file_pattern); + if (edit.window != nullptr) { + SetWindowTextA(edit.window, edit.text.c_str()); + } +} + +void finish_native_dialog(DialogState& dialog, const Tmc result) { + if (dialog.hid == kIddOpen) { + sync_open_cab(dialog); + } else if (dialog.hid == kIddNewDoc) { + sync_new_cab(dialog); + } + if (!invoke_dialog_proc(dialog, kDlmTerm, result)) { + return; + } + dialog.result_tmc = result; + dialog.dying = true; + dialog.visible = false; + if (dialog.window != nullptr && IsWindow(dialog.window)) { + ShowWindow(dialog.window, SW_HIDE); + } +} + +void handle_dialog_command(const Hdlg handle, const WPARAM w_param, + const LPARAM l_param) { + auto* dialog = find_dialog(handle); + if (dialog == nullptr || dialog->dying) { + return; + } + const Tmc tmc = static_cast(LOWORD(w_param) & ~0x8000u); + const Word notification = HIWORD(w_param); + auto found = dialog->controls.find(tmc); + if (found != dialog->controls.end() && + found->second.window == reinterpret_cast(l_param)) { + if (tmc == kTmcOpenFileName && dialog->hid == kIddOpen) { + const int length = GetWindowTextLengthA(found->second.window); + std::vector text(static_cast(length) + 1); + GetWindowTextA(found->second.window, text.data(), + static_cast(text.size())); + found->second.text = text.data(); + } else if (tmc == kTmcOpenReadOnly && dialog->hid == kIddOpen) { + found->second.value = static_cast( + SendMessageA(found->second.window, BM_GETCHECK, 0, 0) == + BST_CHECKED); + } else if (tmc == kTmcNewType && dialog->hid == kIddNewDoc) { + const int length = GetWindowTextLengthA(found->second.window); + std::vector text(static_cast(length) + 1); + GetWindowTextA(found->second.window, text.data(), + static_cast(text.size())); + found->second.text = text.data(); + } + } + if (!dialog->commands_active) { + return; + } + + g_current_dialog = handle; + g_focus_dialog = handle; + sync_current_dialog_globals(); + + if (dialog->hid == kIddNewDoc && tmc == kTmcNewType && + notification == EN_CHANGE) { + invoke_dialog_proc(*dialog, kDlmChange, tmc); + return; + } + if (dialog->hid == kIddNewDoc && tmc == kTmcNewTypeList && + (notification == LBN_SELCHANGE || notification == LBN_DBLCLK)) { + select_new_type(*dialog); + invoke_dialog_proc(*dialog, + notification == LBN_DBLCLK ? kDlmDblClk + : kDlmClick, + tmc); + return; + } + if (dialog->hid == kIddOpen && tmc == kTmcOpenFileName && + notification == EN_CHANGE) { + invoke_dialog_proc(*dialog, kDlmChange, tmc); + return; + } + if (dialog->hid == kIddOpen && tmc == kTmcOpenFileList && + (notification == LBN_SELCHANGE || notification == LBN_DBLCLK)) { + select_open_file(*dialog); + invoke_dialog_proc(*dialog, + notification == LBN_DBLCLK ? kDlmDblClk + : kDlmClick, + tmc); + if (notification == LBN_DBLCLK && !dialog->dying) { + finish_native_dialog(*dialog, kTmcOk); + } + return; + } + if (dialog->hid == kIddOpen && tmc == kTmcOpenFileDir && + (notification == LBN_SELCHANGE || notification == LBN_DBLCLK)) { + invoke_dialog_proc(*dialog, + notification == LBN_DBLCLK ? kDlmDblClk + : kDlmClick, + tmc); + if (notification == LBN_DBLCLK && !dialog->dying) { + enter_open_directory(*dialog); + } + return; + } + if (notification == BN_CLICKED) { + if (dialog->hid == kIddNewDoc && + (tmc == kTmcRNewDoc || tmc == kTmcRNewDot)) { + const bool new_template = tmc == kTmcRNewDot; + dialog->controls[kTmcNewDot].value = new_template; + dialog->controls[kTmcRNewDoc].value = !new_template; + dialog->controls[kTmcRNewDot].value = new_template; + invoke_dialog_proc(*dialog, kDlmClick, tmc); + } else if (tmc == kTmcOk || tmc == kTmcCancel || + (dialog->hid == kIddOpen && + tmc == kTmcOpenCatalog) || + (dialog->hid == kIddNewDoc && + tmc == kTmcSummary)) { + finish_native_dialog(*dialog, tmc); + } else { + invoke_dialog_proc(*dialog, kDlmClick, tmc); + } + } +} + +LRESULT CALLBACK native_dialog_window_proc(const HWND window, + const UINT message, + const WPARAM w_param, + const LPARAM l_param) { + if (message == WM_NCCREATE) { + const auto* create = reinterpret_cast(l_param); + SetWindowLongPtrA(window, GWLP_USERDATA, + reinterpret_cast(create->lpCreateParams)); + } + const Hdlg handle = static_cast(reinterpret_cast( + reinterpret_cast(GetWindowLongPtrA(window, GWLP_USERDATA)))); + switch (message) { + case WM_COMMAND: + handle_dialog_command(handle, w_param, l_param); + return 0; + case WM_CLOSE: + if (auto* dialog = find_dialog(handle); dialog != nullptr) { + finish_native_dialog(*dialog, kTmcCancel); + } + return 0; + default: + return DefWindowProcA(window, message, w_param, l_param); + } +} + } // namespace extern "C" { @@ -306,7 +1026,7 @@ DAC_SDM dac = {}; HWND vhWndMsgBoxParent = nullptr; extern void** hcabDlgCur; -extern Word wRefDlgCur; +extern std::uintptr_t wRefDlgCur; int FInitSdm_sdm21(void*) { g_initialized = true; @@ -374,19 +1094,97 @@ Hdlg HdlgStartDlg(DltHeader** dialog_template, Hcab cab, Dli* initializer) { } dialog.window = create_dialog_host(dialog, initializer); g_dialogs.emplace(handle, std::move(dialog)); - materialize_icon_bar_template(g_dialogs.at(handle)); g_current_dialog = handle; g_focus_dialog = handle; sync_current_dialog_globals(); + materialize_icon_bar_template(g_dialogs.at(handle)); + materialize_new_template(g_dialogs.at(handle)); + materialize_open_template(g_dialogs.at(handle)); return handle; } Tmc TmcDoDlgDli(DltHeader** dialog_template, Hcab cab, Dli* initializer) { - if (HdlgStartDlg(dialog_template, cab, initializer) == 0) { + const Hdlg previous_current = g_current_dialog; + const Hdlg previous_focus = g_focus_dialog; + const Hdlg handle = HdlgStartDlg(dialog_template, cab, initializer); + if (handle == 0) { return static_cast(-1); } - g_dialog.visible = true; - return 2; // tmcCancel until native template materialization is active. + auto* dialog = find_dialog(handle); + if (dialog == nullptr) { + return static_cast(-1); + } + + Tmc result = kTmcCancel; + HWND owner = nullptr; + bool restore_owner = false; + if (dialog->modal && dialog->native_modal && dialog->window != nullptr) { + owner = GetWindow(dialog->window, GW_OWNER); + restore_owner = owner != nullptr && IsWindow(owner) && + IsWindowEnabled(owner) != 0; + if (restore_owner) { + EnableWindow(owner, false); + } + + invoke_dialog_proc(*dialog, kDlmInit, dialog->focus); + dialog->commands_active = true; + if (!dialog->dying) { + dialog->visible = true; + ShowWindow(dialog->window, SW_SHOW); + UpdateWindow(dialog->window); + SetActiveWindow(dialog->window); + const auto focus = dialog->controls.find( + static_cast(dialog->focus & ~0x8000u)); + if (focus != dialog->controls.end() && + focus->second.window != nullptr) { + SetFocus(focus->second.window); + } + } + + MSG message{}; + while (!dialog->dying) { + const int status = GetMessageA(&message, nullptr, 0, 0); + if (status <= 0) { + if (status == 0) { + PostQuitMessage(static_cast(message.wParam)); + } + dialog->result_tmc = kTmcCancel; + dialog->dying = true; + break; + } + if (!IsDialogMessageA(dialog->window, &message)) { + TranslateMessage(&message); + DispatchMessageA(&message); + } + dialog = find_dialog(handle); + if (dialog == nullptr) { + break; + } + } + if (dialog != nullptr) { + result = dialog->result_tmc; + dialog->commands_active = false; + invoke_dialog_proc(*dialog, kDlmExit, result); + } + } + + dialog = find_dialog(handle); + if (dialog != nullptr && dialog->window != nullptr && + IsWindow(dialog->window)) { + DestroyWindow(dialog->window); + } + g_dialogs.erase(handle); + if (restore_owner && owner != nullptr && IsWindow(owner)) { + EnableWindow(owner, true); + SetActiveWindow(owner); + } + g_current_dialog = find_dialog(previous_current) == nullptr + ? 0 + : previous_current; + g_focus_dialog = find_dialog(previous_focus) == nullptr ? 0 + : previous_focus; + sync_current_dialog_globals(); + return result; } Tmc TmcDoDlg_sdm21(DltHeader** dialog_template, Hcab cab, @@ -397,7 +1195,8 @@ Tmc TmcDoDlg_sdm21(DltHeader** dialog_template, Hcab cab, return TmcDoDlgDli(dialog_template, cab, &initializer); } -void EndDlg(Tmc) { +void EndDlg(Tmc result) { + g_dialog.result_tmc = result; g_dialog.dying = true; g_dialog.visible = false; if (g_dialog.window != nullptr && IsWindow(g_dialog.window)) { @@ -701,7 +1500,7 @@ Hcab HcabFromDlg(int dialog) { } void SaveCabs(void (*callback)(Hcab, Word, Tmc, int), int save) { if (callback != nullptr && g_dialog.cab != nullptr) { - callback(g_dialog.cab, g_dialog.reference, 0, save); + callback(g_dialog.cab, g_dialog.hid, 0, save); } } diff --git a/src/port/original/opus_x64_runtime_test.cpp b/src/port/original/opus_x64_runtime_test.cpp index c547e40..b7e0677 100644 --- a/src/port/original/opus_x64_runtime_test.cpp +++ b/src/port/original/opus_x64_runtime_test.cpp @@ -71,7 +71,7 @@ struct TestDli { int dx; int dy; std::uint32_t flags; - TestWord reference; + std::uintptr_t reference; unsigned char* runtime_items; }; @@ -83,9 +83,51 @@ extern "C" HWND HwndFromDlg(TestWord); extern "C" HWND HwndOfTmc(TestWord); extern "C" int FModalDlg(TestWord); extern "C" int FFreeDlg(); +extern "C" TestWord TmcDoDlgDli(TestDltHeader**, void**, TestDli*); +extern "C" void EndDlg(TestWord); extern "C" void SetTmcText_sdm21(TestWord, char*); extern "C" void GetTmcText_sdm21(TestWord, char*, TestWord); +int modal_init_count = 0; +int modal_exit_count = 0; +int new_modal_init_count = 0; +int new_modal_exit_count = 0; +bool new_modal_controls_present = false; + +bool WindowHasClass(const HWND window, const char* expected) { + char class_name[32] = {}; + return window != nullptr && + GetClassNameA(window, class_name, sizeof(class_name)) != 0 && + std::strcmp(class_name, expected) == 0; +} + +int ModalRuntimeProbe(TestWord message, TestWord, TestWord, TestWord, + TestWord) { + if (message == 1) { + ++modal_init_count; + EndDlg(2); + } else if (message == 4) { + ++modal_exit_count; + } + return 1; +} + +int NewModalRuntimeProbe(TestWord message, TestWord, TestWord, TestWord, + TestWord) { + if (message == 1) { + ++new_modal_init_count; + new_modal_controls_present = + WindowHasClass(HwndOfTmc(0x0402), "Button") && + WindowHasClass(HwndOfTmc(0x0403), "Button") && + WindowHasClass(HwndOfTmc(0x0404), "Edit") && + WindowHasClass(HwndOfTmc(0x0405), "ListBox"); + EndDlg(2); + } else if (message == 4) { + ++new_modal_exit_count; + } + return 1; +} + int main() { std::array offsets{2, 8, 13, 3}; AddDcbToLprgbst(offsets.data(), static_cast(offsets.size()), 5, 8); @@ -283,6 +325,30 @@ int main() { EndSdm(); return 23; } + TestDltHeader modal_template{{8, 24, 206, 104}, 3, 0x8400, + reinterpret_cast(ModalRuntimeProbe), + 11, 4}; + auto* modal_template_pointer = &modal_template; + TestDli modal_initializer{parent, 0, 0, 1, 0, nullptr}; + if (TmcDoDlgDli(&modal_template_pointer, nullptr, &modal_initializer) != 2 || + modal_init_count != 1 || modal_exit_count != 1 || + !IsWindow(second_host)) { + DestroyWindow(parent); + EndSdm(); + return 24; + } + TestDltHeader new_modal_template{ + {20, 24, 127, 114}, 2, 0x8404, + reinterpret_cast(NewModalRuntimeProbe), 9, 4}; + auto* new_modal_template_pointer = &new_modal_template; + if (TmcDoDlgDli(&new_modal_template_pointer, nullptr, + &modal_initializer) != 2 || + new_modal_init_count != 1 || new_modal_exit_count != 1 || + !new_modal_controls_present || !IsWindow(second_host)) { + DestroyWindow(parent); + EndSdm(); + return 25; + } EndSdm(); DestroyWindow(parent); diff --git a/src/port/original/para.hs b/src/port/original/para.hs index 6809c69..6bee230 100644 --- a/src/port/original/para.hs +++ b/src/port/original/para.hs @@ -24,6 +24,7 @@ typedef struct _CABPARALOOKS { CABH cabh; WORD sab; + CHAR **hszStyle; int iJustify; int wParLeftIn; int wParRightIn; @@ -31,7 +32,6 @@ typedef struct _CABPARALOOKS int wParDyaBefore; int wParDyaAfter; int wParDyaLine; - CHAR **hszStyle; BOOL fParKeepLT; BOOL fParKeepNP; int iBrcp; diff --git a/src/port/original/password.hs b/src/port/original/password.hs index 29669ed..88293a4 100644 --- a/src/port/original/password.hs +++ b/src/port/original/password.hs @@ -6,6 +6,7 @@ typedef struct _CABFILEPSWD { CABH cabh; + WORD sab; CHAR **hszPswd; } CABFILEPSWD; diff --git a/src/port/original/pastelnk.hs b/src/port/original/pastelnk.hs index 5fb5bf7..42a1baa 100644 --- a/src/port/original/pastelnk.hs +++ b/src/port/original/pastelnk.hs @@ -4,8 +4,8 @@ typedef struct _CABPASTELINK { CABH cabh; WORD sab; - BOOL fHot; CHAR **hszLink; + BOOL fHot; } CABPASTELINK; typedef CABPASTELINK **HCABPASTELINK; #define cabiCABPASTELINK Cabi((sizeof(CABPASTELINK) + sizeof(WORD) - 1) / sizeof(WORD), 1) diff --git a/src/port/original/pict.hs b/src/port/original/pict.hs index f1b6637..eb6726e 100644 --- a/src/port/original/pict.hs +++ b/src/port/original/pict.hs @@ -9,10 +9,10 @@ #define tmcCropBottom (tmcUserMin + 5) #define tmcCropRight (tmcUserMin + 6) -#pragma pack(push, 2) typedef struct _CABFORMATPIC { CABH cabh; + WORD sab; CHAR **hszOrigSize; int iPicBrcl; int wScaleMy; @@ -22,7 +22,6 @@ typedef struct _CABFORMATPIC int wCropBottom; int wCropRight; } CABFORMATPIC; -#pragma pack(pop) typedef CABFORMATPIC **HCABFORMATPIC; diff --git a/src/port/original/print.hs b/src/port/original/print.hs index 7a4a290..7d49dde 100644 --- a/src/port/original/print.hs +++ b/src/port/original/print.hs @@ -29,11 +29,12 @@ typedef struct _CABPRINT CABH cabh; WORD sab; CHAR **hszPrPrinter; + CHAR **hszPrFrom; + CHAR **hszPrTo; + CHAR **hszFileName; int istPrSrc; int cCopies; int prng; - CHAR **hszPrFrom; - CHAR **hszPrTo; BOOL fReverse; BOOL fDraft; BOOL fRefresh; @@ -42,7 +43,6 @@ typedef struct _CABPRINT BOOL fAnnotations; BOOL fSeeHidden; BOOL fShowInst; - CHAR **hszFileName; } CABPRINT; typedef CABPRINT **HCABPRINT; diff --git a/src/port/original/printmrg.hs b/src/port/original/printmrg.hs index 634425e..83f7c1e 100644 --- a/src/port/original/printmrg.hs +++ b/src/port/original/printmrg.hs @@ -9,7 +9,6 @@ #define tmcPMPrint (tmcUserMin + 5) #define tmcPMNewDoc (tmcUserMin + 6) -#pragma pack(push, 2) typedef struct _CABPRINTMERGE { CABH cabh; @@ -18,7 +17,6 @@ typedef struct _CABPRINTMERGE int iRecFrom; int iRecTo; } CABPRINTMERGE; -#pragma pack(pop) typedef CABPRINTMERGE **HCABPRINTMERGE; #define cabiCABPRINTMERGE Cabi((sizeof(CABPRINTMERGE) + sizeof(WORD) - 1) / sizeof(WORD), 0) diff --git a/src/port/original/prompt.hs b/src/port/original/prompt.hs index 39107f4..4ab6cfa 100644 --- a/src/port/original/prompt.hs +++ b/src/port/original/prompt.hs @@ -12,6 +12,7 @@ typedef struct _CABPROMPT { CABH cabh; + WORD sab; CHAR **hszPrompt; CHAR **hszInput; } CABPROMPT; diff --git a/src/port/original/recorder.hs b/src/port/original/recorder.hs index 8f4efe1..fb4cade 100644 --- a/src/port/original/recorder.hs +++ b/src/port/original/recorder.hs @@ -10,8 +10,8 @@ typedef struct _CABRECORDER CABH cabh; WORD sab; CHAR **pszName; - int iContext; CHAR **pszDesc; + int iContext; } CABRECORDER; typedef CABRECORDER **HCABRECORDER; diff --git a/src/port/original/renum.hs b/src/port/original/renum.hs index f13a87f..a65a66e 100644 --- a/src/port/original/renum.hs +++ b/src/port/original/renum.hs @@ -12,7 +12,6 @@ #define tmcRPAllLevels (tmcUserMin + 8) #define tmcRPFormat ((tmcUserMin + 9) | ftmcGrouped) -#pragma pack(push, 2) typedef struct _CABRENUMPARAS { CABH cabh; @@ -23,7 +22,6 @@ typedef struct _CABRENUMPARAS BOOL fManual; BOOL fShowAllLev; } CABRENUMPARAS; -#pragma pack(pop) typedef CABRENUMPARAS **HCABRENUMPARAS; #define cabiCABRENUMPARAS Cabi((sizeof(CABRENUMPARAS) + sizeof(WORD) - 1) / sizeof(WORD), 2) diff --git a/src/port/original/ribbon.hs b/src/port/original/ribbon.hs index 56645c4..f8eb7a1 100644 --- a/src/port/original/ribbon.hs +++ b/src/port/original/ribbon.hs @@ -16,6 +16,7 @@ typedef struct _CABRIBBON { CABH cabh; + WORD sab; int ftc; int hps; } CABRIBBON; diff --git a/src/port/original/ribbon3.hs b/src/port/original/ribbon3.hs index aa06aab..48b18e5 100644 --- a/src/port/original/ribbon3.hs +++ b/src/port/original/ribbon3.hs @@ -12,6 +12,7 @@ typedef struct _CABRIBBON3 { CABH cabh; + WORD sab; int ftc; int hps; } CABRIBBON3; diff --git a/src/port/original/ruler.hs b/src/port/original/ruler.hs index b740bde..f146112 100644 --- a/src/port/original/ruler.hs +++ b/src/port/original/ruler.hs @@ -13,6 +13,7 @@ typedef struct _CABRULER { CABH cabh; + WORD sab; CHAR **hszStyle; } CABRULER; diff --git a/src/port/original/ruler3.hs b/src/port/original/ruler3.hs index 31b326e..f8c3792 100644 --- a/src/port/original/ruler3.hs +++ b/src/port/original/ruler3.hs @@ -9,6 +9,7 @@ typedef struct _CABRULER3 { CABH cabh; + WORD sab; CHAR **hszStyle; } CABRULER3; diff --git a/src/port/original/runmacro.hs b/src/port/original/runmacro.hs index 9faec42..b346e0d 100644 --- a/src/port/original/runmacro.hs +++ b/src/port/original/runmacro.hs @@ -9,6 +9,7 @@ typedef struct _CABRUNMACRO { CABH cabh; + WORD sab; CHAR **hszName; BOOL fShowAll; } CABRUNMACRO; diff --git a/src/port/original/sort.hs b/src/port/original/sort.hs index b67fd39..7ecc7d5 100644 --- a/src/port/original/sort.hs +++ b/src/port/original/sort.hs @@ -11,7 +11,6 @@ #define tmcSortCol (tmcUserMin + 7) #define tmcSortCase (tmcUserMin + 8) -#pragma pack(push, 2) typedef struct _CABSORT { CABH cabh; @@ -23,7 +22,6 @@ typedef struct _CABSORT BOOL fSortCol; BOOL fCase; } CABSORT; -#pragma pack(pop) typedef CABSORT **HCABSORT; #define cabiCABSORT Cabi((sizeof(CABSORT) + sizeof(WORD) - 1) / sizeof(WORD), 0) diff --git a/src/port/original/spell.hs b/src/port/original/spell.hs index 05e1a71..0deb8f0 100644 --- a/src/port/original/spell.hs +++ b/src/port/original/spell.hs @@ -12,7 +12,6 @@ #define tmcSplIgnoreCaps (tmcUserMin + 8) #define tmcSplAutoSugg (tmcUserMin + 9) -#pragma pack(push, 2) typedef struct _CABSPELLER { CABH cabh; @@ -23,7 +22,6 @@ typedef struct _CABSPELLER BOOL fSplIgnoreCaps; BOOL fSplAutoSugg; } CABSPELLER; -#pragma pack(pop) typedef CABSPELLER **HCABSPELLER; #define cabiCABSPELLER Cabi((sizeof(CABSPELLER) + sizeof(WORD) - 1) / sizeof(WORD), 3) diff --git a/src/port/original/spellmm.hs b/src/port/original/spellmm.hs index 7f78f50..a2c6bf3 100644 --- a/src/port/original/spellmm.hs +++ b/src/port/original/spellmm.hs @@ -13,17 +13,16 @@ #define tmcSplMMIgnoreCaps (tmcUserMin + 9) #define tmcSplMMAutoSugg (tmcUserMin + 10) -#pragma pack(push, 2) typedef struct _CABSPELLERMM { CABH cabh; + WORD sab; CHAR **hszSplMMChangeTo; unsigned uSplMMSuggList; int iSplMMAddToBox; BOOL fSplMMIgnoreCaps; BOOL fSplMMAutoSugg; } CABSPELLERMM; -#pragma pack(pop) typedef CABSPELLERMM **HCABSPELLERMM; #define cabiCABSPELLERMM Cabi((sizeof(CABSPELLERMM) + sizeof(WORD) - 1) / sizeof(WORD), 1) diff --git a/src/port/original/tablecmd.hs b/src/port/original/tablecmd.hs index 31e17f1..91694a2 100644 --- a/src/port/original/tablecmd.hs +++ b/src/port/original/tablecmd.hs @@ -15,7 +15,6 @@ #define tmcMergeCells (tmcUserMin + 10) #define tmcSplitCells (tmcUserMin + 11) -#pragma pack(push, 2) typedef struct _CABEDITTABLE { CABH cabh; @@ -23,7 +22,6 @@ typedef struct _CABEDITTABLE int cmdtype; BOOL fShiftVert; } CABEDITTABLE; -#pragma pack(pop) typedef CABEDITTABLE **HCABEDITTABLE; #define cabiCABEDITTABLE Cabi((sizeof(CABEDITTABLE) + sizeof(WORD) - 1) / sizeof(WORD), 0) diff --git a/src/port/original/tablefmt.hs b/src/port/original/tablefmt.hs index 61d86f5..321b4f3 100644 --- a/src/port/original/tablefmt.hs +++ b/src/port/original/tablefmt.hs @@ -25,7 +25,6 @@ #define tmcNextColumn (tmcUserMin + 20) #define tmcPrevColumn (tmcUserMin + 21) -#pragma pack(push, 2) typedef struct _CABFORMATTABLE { CABH cabh; @@ -46,7 +45,6 @@ typedef struct _CABFORMATTABLE int jc; BOOL fWholeTable; } CABFORMATTABLE; -#pragma pack(pop) typedef CABFORMATTABLE **HCABFORMATTABLE; #define cabiCABFORMATTABLE Cabi((sizeof(CABFORMATTABLE) + sizeof(WORD) - 1) / sizeof(WORD), 1) diff --git a/src/port/original/tableins.hs b/src/port/original/tableins.hs index a5407a1..e16025b 100644 --- a/src/port/original/tableins.hs +++ b/src/port/original/tableins.hs @@ -10,7 +10,6 @@ #define tmcFormatComma (tmcUserMin + 6) #define tmcTunnelFormat (tmcUserMin + 7) -#pragma pack(push, 2) typedef struct _CABINSERTTABLE { CABH cabh; @@ -20,7 +19,6 @@ typedef struct _CABINSERTTABLE int dxaWidth; int tblfmt; } CABINSERTTABLE; -#pragma pack(pop) typedef CABINSERTTABLE **HCABINSERTTABLE; #define cabiCABINSERTTABLE Cabi((sizeof(CABINSERTTABLE) + sizeof(WORD) - 1) / sizeof(WORD), 0) diff --git a/src/port/original/tabletxt.hs b/src/port/original/tabletxt.hs index 08cf91f..508a464 100644 --- a/src/port/original/tabletxt.hs +++ b/src/port/original/tabletxt.hs @@ -6,14 +6,12 @@ #define tmcCommaDelim (tmcUserMin + 2) #define tmcTabDelim (tmcUserMin + 3) -#pragma pack(push, 2) typedef struct _CABTABLETOTEXT { CABH cabh; WORD sab; int tblfmt; } CABTABLETOTEXT; -#pragma pack(pop) typedef CABTABLETOTEXT **HCABTABLETOTEXT; #define cabiCABTABLETOTEXT Cabi((sizeof(CABTABLETOTEXT) + sizeof(WORD) - 1) / sizeof(WORD), 0) diff --git a/src/port/original/thesaur.hs b/src/port/original/thesaur.hs index 67bc6b1..e31dc4e 100644 --- a/src/port/original/thesaur.hs +++ b/src/port/original/thesaur.hs @@ -10,7 +10,6 @@ #define tmcPartOfSpeech (tmcUserMin + 6) #define tmcDefinition (tmcUserMin + 7) -#pragma pack(push, 2) typedef struct _CABTHESAURUS { CABH cabh; @@ -19,7 +18,6 @@ typedef struct _CABTHESAURUS int uSynList; int uDefList; } CABTHESAURUS; -#pragma pack(pop) typedef CABTHESAURUS **HCABTHESAURUS; #define cabiCABTHESAURUS Cabi((sizeof(CABTHESAURUS) + sizeof(WORD) - 1) / sizeof(WORD), 1) diff --git a/src/port/original/toc.hs b/src/port/original/toc.hs index cf1374d..e5b0e03 100644 --- a/src/port/original/toc.hs +++ b/src/port/original/toc.hs @@ -11,7 +11,6 @@ #define tmcTocFrom (tmcUserMin + 5) #define tmcTocTo (tmcUserMin + 6) -#pragma pack(push, 2) typedef struct _CABTOC { CABH cabh; @@ -22,7 +21,6 @@ typedef struct _CABTOC unsigned wTocTo; BOOL fReplace; } CABTOC; -#pragma pack(pop) typedef CABTOC **HCABTOC; #define cabiCABTOC Cabi((sizeof(CABTOC) + sizeof(WORD) - 1) / sizeof(WORD), 0) diff --git a/src/port/original/username.hs b/src/port/original/username.hs index 521f30d..af25500 100644 --- a/src/port/original/username.hs +++ b/src/port/original/username.hs @@ -7,6 +7,7 @@ typedef struct _CABUSERNAME { CABH cabh; + WORD sab; CHAR **hszName; CHAR **hszInitials; } CABUSERNAME; diff --git a/src/port/original/usrdlg.hs b/src/port/original/usrdlg.hs index 89cfa36..572c3c2 100644 --- a/src/port/original/usrdlg.hs +++ b/src/port/original/usrdlg.hs @@ -1,13 +1,12 @@ #pragma once /* Compile contract reconstructed from Opus/dlg/usrdlg.des. */ -#pragma pack(push, 2) typedef struct _CABUSRDLG { CABH cabh; + WORD sab; CHAR **foo; } CABUSRDLG; -#pragma pack(pop) typedef CABUSRDLG **HCABUSRDLG; #define cabiCABUSRDLG Cabi((sizeof(CABUSRDLG) + sizeof(WORD) - 1) / sizeof(WORD), 1) diff --git a/src/port/original/vrfcnvtr.hs b/src/port/original/vrfcnvtr.hs index cc76d40..ba52641 100644 --- a/src/port/original/vrfcnvtr.hs +++ b/src/port/original/vrfcnvtr.hs @@ -8,6 +8,7 @@ typedef struct _CABEXCR { CABH cabh; + WORD sab; int iFmt; } CABEXCR; diff --git a/src/port/winword.manifest b/src/port/winword.manifest index 6f15c20..d08c7f4 100644 --- a/src/port/winword.manifest +++ b/src/port/winword.manifest @@ -2,7 +2,7 @@ - Microsoft Word for Windows x64 port + Microsoft Word for Windows x64 port by Justin Marshall -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace { - -constexpr wchar_t kWindowClass[] = L"OpusWordX64Window"; -constexpr wchar_t kProductName[] = L"Microsoft Word x64 Port"; -constexpr UINT_PTR kStatusTimer = 1; - -enum Command : UINT { - cmd_file_new = 100, - cmd_file_open, - cmd_file_save, - cmd_file_save_as, - cmd_file_print, - cmd_file_exit, - cmd_edit_undo = 200, - cmd_edit_redo, - cmd_edit_cut, - cmd_edit_copy, - cmd_edit_paste, - cmd_edit_select_all, - cmd_edit_find, - cmd_edit_find_next, - cmd_format_bold = 300, - cmd_format_italic, - cmd_format_underline, - cmd_format_font, - cmd_format_left, - cmd_format_center, - cmd_format_right, - cmd_format_justify, - cmd_tools_word_count = 400, - cmd_help_about = 500, -}; - -[[nodiscard]] std::wstring system_error_message(const DWORD error) { - wchar_t* buffer = nullptr; - const DWORD count = FormatMessageW( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS, - nullptr, error, 0, reinterpret_cast(&buffer), 0, nullptr); - std::wstring message = count != 0 && buffer != nullptr - ? std::wstring(buffer, count) - : L"Unknown error (" + std::to_wstring(error) + L")"; - if (buffer != nullptr) { - LocalFree(buffer); - } - while (!message.empty() && (message.back() == L'\r' || message.back() == L'\n')) { - message.pop_back(); - } - return message; -} - -[[nodiscard]] std::wstring lower_extension(const std::wstring& path) { - auto extension = std::filesystem::path(path).extension().wstring(); - std::ranges::transform(extension, extension.begin(), - [](const wchar_t ch) { return static_cast(std::towlower(ch)); }); - return extension; -} - -[[nodiscard]] std::vector read_file(const std::wstring& path) { - const HANDLE file = CreateFileW(path.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, - OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); - if (file == INVALID_HANDLE_VALUE) { - throw std::runtime_error("CreateFileW failed: " + std::to_string(GetLastError())); - } - - LARGE_INTEGER size{}; - if (!GetFileSizeEx(file, &size) || size.QuadPart < 0 || - size.QuadPart > static_cast(std::numeric_limits::max())) { - const DWORD error = GetLastError(); - CloseHandle(file); - throw std::runtime_error("GetFileSizeEx failed: " + std::to_string(error)); - } - - std::vector bytes(static_cast(size.QuadPart)); - DWORD read = 0; - const BOOL succeeded = bytes.empty() || - ReadFile(file, bytes.data(), static_cast(bytes.size()), &read, nullptr); - const DWORD error = succeeded ? ERROR_SUCCESS : GetLastError(); - CloseHandle(file); - if (!succeeded || read != bytes.size()) { - throw std::runtime_error("ReadFile failed: " + std::to_string(error)); - } - return bytes; -} - -void write_file(const std::wstring& path, const std::vector& bytes) { - const HANDLE file = CreateFileW(path.c_str(), GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, - FILE_ATTRIBUTE_NORMAL, nullptr); - if (file == INVALID_HANDLE_VALUE) { - throw std::runtime_error("CreateFileW failed: " + std::to_string(GetLastError())); - } - DWORD written = 0; - const BOOL succeeded = bytes.empty() || - WriteFile(file, bytes.data(), static_cast(bytes.size()), &written, nullptr); - const DWORD error = succeeded ? ERROR_SUCCESS : GetLastError(); - CloseHandle(file); - if (!succeeded || written != bytes.size()) { - throw std::runtime_error("WriteFile failed: " + std::to_string(error)); - } -} - -[[nodiscard]] std::wstring decode_text(const std::vector& bytes) { - if (bytes.size() >= 2 && bytes[0] == std::byte{0xff} && bytes[1] == std::byte{0xfe}) { - std::wstring result((bytes.size() - 2) / sizeof(wchar_t), L'\0'); - std::memcpy(result.data(), bytes.data() + 2, result.size() * sizeof(wchar_t)); - return result; - } - - std::size_t offset = 0; - if (bytes.size() >= 3 && bytes[0] == std::byte{0xef} && bytes[1] == std::byte{0xbb} && - bytes[2] == std::byte{0xbf}) { - offset = 3; - } - const auto* text = reinterpret_cast(bytes.data() + offset); - const int byte_count = static_cast(bytes.size() - offset); - if (byte_count == 0) { - return {}; - } - - UINT code_page = CP_UTF8; - DWORD flags = MB_ERR_INVALID_CHARS; - int character_count = MultiByteToWideChar(code_page, flags, text, byte_count, nullptr, 0); - if (character_count == 0) { - code_page = CP_ACP; - flags = 0; - character_count = MultiByteToWideChar(code_page, flags, text, byte_count, nullptr, 0); - } - if (character_count == 0) { - throw std::runtime_error("The text encoding could not be decoded"); - } - std::wstring result(static_cast(character_count), L'\0'); - MultiByteToWideChar(code_page, flags, text, byte_count, result.data(), character_count); - return result; -} - -[[nodiscard]] std::vector encode_utf8(const std::wstring_view text) { - const int byte_count = WideCharToMultiByte(CP_UTF8, 0, text.data(), - static_cast(text.size()), nullptr, 0, - nullptr, nullptr); - std::vector result{ - std::byte{0xef}, std::byte{0xbb}, std::byte{0xbf}, - }; - const auto initial_size = result.size(); - result.resize(initial_size + static_cast(byte_count)); - if (byte_count != 0) { - WideCharToMultiByte(CP_UTF8, 0, text.data(), static_cast(text.size()), - reinterpret_cast(result.data() + initial_size), byte_count, - nullptr, nullptr); - } - return result; -} - -[[nodiscard]] HMENU build_menu() { - const HMENU menu = CreateMenu(); - const HMENU file = CreatePopupMenu(); - AppendMenuW(file, MF_STRING, cmd_file_new, L"&New\tCtrl+N"); - AppendMenuW(file, MF_STRING, cmd_file_open, L"&Open...\tCtrl+O"); - AppendMenuW(file, MF_STRING, cmd_file_save, L"&Save\tCtrl+S"); - AppendMenuW(file, MF_STRING, cmd_file_save_as, L"Save &As..."); - AppendMenuW(file, MF_SEPARATOR, 0, nullptr); - AppendMenuW(file, MF_STRING, cmd_file_print, L"&Print...\tCtrl+P"); - AppendMenuW(file, MF_SEPARATOR, 0, nullptr); - AppendMenuW(file, MF_STRING, cmd_file_exit, L"E&xit"); - AppendMenuW(menu, MF_POPUP, reinterpret_cast(file), L"&File"); - - const HMENU edit = CreatePopupMenu(); - AppendMenuW(edit, MF_STRING, cmd_edit_undo, L"&Undo\tCtrl+Z"); - AppendMenuW(edit, MF_STRING, cmd_edit_redo, L"&Redo\tCtrl+Y"); - AppendMenuW(edit, MF_SEPARATOR, 0, nullptr); - AppendMenuW(edit, MF_STRING, cmd_edit_cut, L"Cu&t\tCtrl+X"); - AppendMenuW(edit, MF_STRING, cmd_edit_copy, L"&Copy\tCtrl+C"); - AppendMenuW(edit, MF_STRING, cmd_edit_paste, L"&Paste\tCtrl+V"); - AppendMenuW(edit, MF_STRING, cmd_edit_select_all, L"Select &All\tCtrl+A"); - AppendMenuW(edit, MF_SEPARATOR, 0, nullptr); - AppendMenuW(edit, MF_STRING, cmd_edit_find, L"&Find...\tCtrl+F"); - AppendMenuW(edit, MF_STRING, cmd_edit_find_next, L"Find &Next\tF3"); - AppendMenuW(menu, MF_POPUP, reinterpret_cast(edit), L"&Edit"); - - const HMENU format = CreatePopupMenu(); - AppendMenuW(format, MF_STRING, cmd_format_bold, L"&Bold\tCtrl+B"); - AppendMenuW(format, MF_STRING, cmd_format_italic, L"&Italic\tCtrl+I"); - AppendMenuW(format, MF_STRING, cmd_format_underline, L"&Underline\tCtrl+U"); - AppendMenuW(format, MF_STRING, cmd_format_font, L"&Font..."); - AppendMenuW(format, MF_SEPARATOR, 0, nullptr); - AppendMenuW(format, MF_STRING, cmd_format_left, L"Align &Left\tCtrl+L"); - AppendMenuW(format, MF_STRING, cmd_format_center, L"&Center\tCtrl+E"); - AppendMenuW(format, MF_STRING, cmd_format_right, L"Align &Right\tCtrl+R"); - AppendMenuW(format, MF_STRING, cmd_format_justify, L"&Justify\tCtrl+J"); - AppendMenuW(menu, MF_POPUP, reinterpret_cast(format), L"F&ormat"); - - const HMENU tools = CreatePopupMenu(); - AppendMenuW(tools, MF_STRING, cmd_tools_word_count, L"&Word Count..."); - AppendMenuW(menu, MF_POPUP, reinterpret_cast(tools), L"&Tools"); - - const HMENU help = CreatePopupMenu(); - AppendMenuW(help, MF_STRING, cmd_help_about, L"&About Microsoft Word x64 Port"); - AppendMenuW(menu, MF_POPUP, reinterpret_cast(help), L"&Help"); - return menu; -} - -class WordApplication { -public: - explicit WordApplication(const HINSTANCE instance) : instance_(instance) {} - - int run(const int show_command) { - int argument_count = 0; - wchar_t** arguments = CommandLineToArgvW(GetCommandLineW(), &argument_count); - if (arguments != nullptr && argument_count > 1 && - std::wstring_view(arguments[1]) == L"--self-test") { - std::array pattern_buffer{}; - constexpr std::array pattern{1, 2, 3}; - opus::x64::fill_byte_pattern(pattern_buffer.data(), pattern_buffer.size(), - pattern.data(), pattern.size()); - const bool passed = opus::x64::assembly_manifest_complete() && - opus::x64::assembly_manifest_count() == 59 && - opus::x64::multiply_divide(10, 3, 2) == 15 && - opus::x64::legacy_square(4.0) == 16.0 && - pattern_buffer == std::array{1, 2, 3, 1, 2, 3, 1} && - opus::x64::is_ascii_alphanumeric('Z') && - opus::x64::ascii_upper('q') == 'Q'; - LocalFree(arguments); - return passed ? 0 : 2; - } - - SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); - INITCOMMONCONTROLSEX controls{sizeof(controls), ICC_BAR_CLASSES | ICC_STANDARD_CLASSES}; - InitCommonControlsEx(&controls); - - rich_edit_library_ = LoadLibraryW(L"Msftedit.dll"); - if (rich_edit_library_ == nullptr) { - show_error(L"Microsoft Rich Edit could not be loaded", GetLastError()); - if (arguments != nullptr) { - LocalFree(arguments); - } - return 1; - } - - WNDCLASSEXW window_class{sizeof(window_class)}; - window_class.style = CS_HREDRAW | CS_VREDRAW; - window_class.lpfnWndProc = &WordApplication::window_proc; - window_class.hInstance = instance_; - window_class.hIcon = LoadIconW(nullptr, IDI_APPLICATION); - window_class.hCursor = LoadCursorW(nullptr, IDC_ARROW); - window_class.hbrBackground = reinterpret_cast(COLOR_APPWORKSPACE + 1); - window_class.lpszClassName = kWindowClass; - window_class.hIconSm = window_class.hIcon; - if (RegisterClassExW(&window_class) == 0) { - show_error(L"The application window could not be registered", GetLastError()); - FreeLibrary(rich_edit_library_); - if (arguments != nullptr) { - LocalFree(arguments); - } - return 1; - } - - main_window_ = CreateWindowExW( - 0, kWindowClass, kProductName, WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, - CW_USEDEFAULT, CW_USEDEFAULT, 1100, 800, nullptr, nullptr, instance_, this); - if (main_window_ == nullptr) { - show_error(L"The application window could not be created", GetLastError()); - FreeLibrary(rich_edit_library_); - if (arguments != nullptr) { - LocalFree(arguments); - } - return 1; - } - - if (arguments != nullptr && argument_count > 1 && - std::wstring_view(arguments[1]) == L"--ui-self-test") { - SetWindowTextW(editor_, L"Word x64 UI smoke test"); - const bool passed = IsWindow(main_window_) != FALSE && IsWindow(editor_) != FALSE && - IsWindow(status_) != FALSE && GetWindowTextLengthW(editor_) == 22; - DestroyWindow(main_window_); - LocalFree(arguments); - FreeLibrary(rich_edit_library_); - return passed ? 0 : 3; - } - - if (arguments != nullptr && argument_count > 1) { - open_path(arguments[1]); - } - if (arguments != nullptr) { - LocalFree(arguments); - } - - ShowWindow(main_window_, show_command); - UpdateWindow(main_window_); - - const std::array accelerators{ - ACCEL{FVIRTKEY | FCONTROL, 'N', cmd_file_new}, - ACCEL{FVIRTKEY | FCONTROL, 'O', cmd_file_open}, - ACCEL{FVIRTKEY | FCONTROL, 'S', cmd_file_save}, - ACCEL{FVIRTKEY | FCONTROL, 'P', cmd_file_print}, - ACCEL{FVIRTKEY | FCONTROL, 'Z', cmd_edit_undo}, - ACCEL{FVIRTKEY | FCONTROL, 'Y', cmd_edit_redo}, - ACCEL{FVIRTKEY | FCONTROL, 'X', cmd_edit_cut}, - ACCEL{FVIRTKEY | FCONTROL, 'C', cmd_edit_copy}, - ACCEL{FVIRTKEY | FCONTROL, 'V', cmd_edit_paste}, - ACCEL{FVIRTKEY | FCONTROL, 'A', cmd_edit_select_all}, - ACCEL{FVIRTKEY | FCONTROL, 'F', cmd_edit_find}, - ACCEL{FVIRTKEY, VK_F3, cmd_edit_find_next}, - ACCEL{FVIRTKEY | FCONTROL, 'B', cmd_format_bold}, - ACCEL{FVIRTKEY | FCONTROL, 'I', cmd_format_italic}, - ACCEL{FVIRTKEY | FCONTROL, 'U', cmd_format_underline}, - ACCEL{FVIRTKEY | FCONTROL, 'L', cmd_format_left}, - ACCEL{FVIRTKEY | FCONTROL, 'E', cmd_format_center}, - ACCEL{FVIRTKEY | FCONTROL, 'R', cmd_format_right}, - ACCEL{FVIRTKEY | FCONTROL, 'J', cmd_format_justify}, - }; - const HACCEL accelerator_table = CreateAcceleratorTableW( - const_cast(accelerators.data()), static_cast(accelerators.size())); - - MSG message{}; - while (GetMessageW(&message, nullptr, 0, 0) > 0) { - if (find_dialog_ != nullptr && IsDialogMessageW(find_dialog_, &message)) { - continue; - } - if (!TranslateAcceleratorW(main_window_, accelerator_table, &message)) { - TranslateMessage(&message); - DispatchMessageW(&message); - } - } - DestroyAcceleratorTable(accelerator_table); - FreeLibrary(rich_edit_library_); - return static_cast(message.wParam); - } - -private: - static LRESULT CALLBACK window_proc(const HWND window, const UINT message, - const WPARAM wparam, const LPARAM lparam) { - WordApplication* app = reinterpret_cast( - GetWindowLongPtrW(window, GWLP_USERDATA)); - if (message == WM_NCCREATE) { - const auto* create = reinterpret_cast(lparam); - app = static_cast(create->lpCreateParams); - app->main_window_ = window; - SetWindowLongPtrW(window, GWLP_USERDATA, reinterpret_cast(app)); - } - return app != nullptr ? app->dispatch(message, wparam, lparam) - : DefWindowProcW(window, message, wparam, lparam); - } - - LRESULT dispatch(const UINT message, const WPARAM wparam, const LPARAM lparam) { - if (message == find_message_) { - on_find_message(*reinterpret_cast(lparam)); - return 0; - } - switch (message) { - case WM_CREATE: - return create_children() ? 0 : -1; - case WM_SIZE: - layout(LOWORD(lparam), HIWORD(lparam)); - return 0; - case WM_SETFOCUS: - SetFocus(editor_); - return 0; - case WM_COMMAND: - if (reinterpret_cast(lparam) == editor_ && HIWORD(wparam) == EN_CHANGE) { - status_dirty_ = true; - update_title(); - return 0; - } - execute_command(LOWORD(wparam)); - return 0; - case WM_NOTIFY: - if (reinterpret_cast(lparam)->hwndFrom == editor_) { - status_dirty_ = true; - } - return 0; - case WM_INITMENU: - update_menu(reinterpret_cast(wparam)); - return 0; - case WM_TIMER: - if (wparam == kStatusTimer && status_dirty_) { - update_status(); - } - return 0; - case WM_DROPFILES: - open_drop(reinterpret_cast(wparam)); - return 0; - case WM_CLOSE: - if (prompt_save_changes()) { - DestroyWindow(main_window_); - } - return 0; - case WM_DESTROY: - KillTimer(main_window_, kStatusTimer); - PostQuitMessage(0); - return 0; - default: - return DefWindowProcW(main_window_, message, wparam, lparam); - } - } - - bool create_children() { - SetMenu(main_window_, build_menu()); - editor_ = CreateWindowExW( - WS_EX_CLIENTEDGE, MSFTEDIT_CLASS, L"", - WS_CHILD | WS_VISIBLE | WS_VSCROLL | ES_MULTILINE | ES_WANTRETURN | - ES_NOHIDESEL | ES_AUTOVSCROLL, - 0, 0, 0, 0, main_window_, reinterpret_cast(1), instance_, nullptr); - status_ = CreateWindowExW(0, STATUSCLASSNAMEW, nullptr, WS_CHILD | WS_VISIBLE, - 0, 0, 0, 0, main_window_, reinterpret_cast(2), - instance_, nullptr); - if (editor_ == nullptr || status_ == nullptr) { - return false; - } - - SendMessageW(editor_, EM_SETEVENTMASK, 0, ENM_CHANGE | ENM_SELCHANGE); - SendMessageW(editor_, EM_SETBKGNDCOLOR, 0, RGB(255, 255, 255)); - SendMessageW(editor_, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(16, 16)); - SendMessageW(editor_, EM_EXLIMITTEXT, 0, std::numeric_limits::max()); - - CHARFORMAT2W format{sizeof(format)}; - format.dwMask = CFM_FACE | CFM_SIZE | CFM_CHARSET; - format.yHeight = 220; - format.bCharSet = DEFAULT_CHARSET; - wcscpy_s(format.szFaceName, L"Segoe UI"); - SendMessageW(editor_, EM_SETCHARFORMAT, SCF_ALL, reinterpret_cast(&format)); - - find_message_ = RegisterWindowMessageW(FINDMSGSTRINGW); - DragAcceptFiles(main_window_, TRUE); - SetTimer(main_window_, kStatusTimer, 250, nullptr); - set_clean(); - update_status(); - SetFocus(editor_); - return true; - } - - void layout(const int width, const int height) { - SendMessageW(status_, WM_SIZE, 0, 0); - RECT status_rect{}; - GetWindowRect(status_, &status_rect); - const int status_height = status_rect.bottom - status_rect.top; - MoveWindow(editor_, 0, 0, width, std::max(0, height - status_height), TRUE); - const int parts[]{std::max(200, width - 330), std::max(320, width - 150), -1}; - SendMessageW(status_, SB_SETPARTS, std::size(parts), reinterpret_cast(parts)); - status_dirty_ = true; - } - - void execute_command(const UINT command) { - switch (command) { - case cmd_file_new: new_document(); break; - case cmd_file_open: open_dialog(); break; - case cmd_file_save: save(false); break; - case cmd_file_save_as: save(true); break; - case cmd_file_print: print_document(); break; - case cmd_file_exit: SendMessageW(main_window_, WM_CLOSE, 0, 0); break; - case cmd_edit_undo: SendMessageW(editor_, EM_UNDO, 0, 0); break; - case cmd_edit_redo: SendMessageW(editor_, EM_REDO, 0, 0); break; - case cmd_edit_cut: SendMessageW(editor_, WM_CUT, 0, 0); break; - case cmd_edit_copy: SendMessageW(editor_, WM_COPY, 0, 0); break; - case cmd_edit_paste: SendMessageW(editor_, WM_PASTE, 0, 0); break; - case cmd_edit_select_all: SendMessageW(editor_, EM_SETSEL, 0, -1); break; - case cmd_edit_find: show_find(); break; - case cmd_edit_find_next: find_next(find_flags_); break; - case cmd_format_bold: toggle_effect(CFM_BOLD, CFE_BOLD); break; - case cmd_format_italic: toggle_effect(CFM_ITALIC, CFE_ITALIC); break; - case cmd_format_underline: toggle_effect(CFM_UNDERLINE, CFE_UNDERLINE); break; - case cmd_format_font: choose_font(); break; - case cmd_format_left: set_alignment(PFA_LEFT); break; - case cmd_format_center: set_alignment(PFA_CENTER); break; - case cmd_format_right: set_alignment(PFA_RIGHT); break; - case cmd_format_justify: set_alignment(PFA_JUSTIFY); break; - case cmd_tools_word_count: show_word_count(); break; - case cmd_help_about: show_about(); break; - default: break; - } - status_dirty_ = true; - } - - void update_menu(const HMENU menu) const { - const bool can_undo = SendMessageW(editor_, EM_CANUNDO, 0, 0) != 0; - const bool can_redo = SendMessageW(editor_, EM_CANREDO, 0, 0) != 0; - CHARRANGE selection{}; - SendMessageW(editor_, EM_EXGETSEL, 0, reinterpret_cast(&selection)); - EnableMenuItem(menu, cmd_edit_undo, MF_BYCOMMAND | (can_undo ? MF_ENABLED : MF_GRAYED)); - EnableMenuItem(menu, cmd_edit_redo, MF_BYCOMMAND | (can_redo ? MF_ENABLED : MF_GRAYED)); - EnableMenuItem(menu, cmd_edit_cut, - MF_BYCOMMAND | (selection.cpMin != selection.cpMax ? MF_ENABLED : MF_GRAYED)); - EnableMenuItem(menu, cmd_edit_copy, - MF_BYCOMMAND | (selection.cpMin != selection.cpMax ? MF_ENABLED : MF_GRAYED)); - EnableMenuItem(menu, cmd_edit_paste, - MF_BYCOMMAND | (SendMessageW(editor_, EM_CANPASTE, 0, 0) ? MF_ENABLED : MF_GRAYED)); - } - - void new_document() { - if (!prompt_save_changes()) { - return; - } - loading_ = true; - SetWindowTextW(editor_, L""); - current_path_.clear(); - loading_ = false; - set_clean(); - } - - void open_dialog() { - if (!prompt_save_changes()) { - return; - } - std::array path{}; - OPENFILENAMEW dialog{sizeof(dialog)}; - dialog.hwndOwner = main_window_; - dialog.lpstrFilter = L"Rich Text Format (*.rtf)\0*.rtf\0Text files (*.txt)\0*.txt\0All files (*.*)\0*.*\0"; - dialog.lpstrFile = path.data(); - dialog.nMaxFile = static_cast(path.size()); - dialog.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY; - if (GetOpenFileNameW(&dialog)) { - open_path(path.data()); - } - } - - void open_drop(const HDROP drop) { - std::array path{}; - if (DragQueryFileW(drop, 0, path.data(), static_cast(path.size())) != 0 && - prompt_save_changes()) { - open_path(path.data()); - } - DragFinish(drop); - } - - void open_path(const std::wstring& path) { - if (lower_extension(path) == L".doc") { - MessageBoxW(main_window_, - L"The native x64 document engine does not read binary Word 1.x .doc files yet. " - L"Open an RTF or text document for this migration milestone.", - kProductName, MB_OK | MB_ICONINFORMATION); - return; - } - try { - loading_ = true; - if (lower_extension(path) == L".rtf") { - const HANDLE file = CreateFileW(path.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, - OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); - if (file == INVALID_HANDLE_VALUE) { - throw std::runtime_error("CreateFileW failed: " + std::to_string(GetLastError())); - } - EDITSTREAM stream{reinterpret_cast(file), 0, &stream_in}; - SetWindowTextW(editor_, L""); - SendMessageW(editor_, EM_STREAMIN, SF_RTF, reinterpret_cast(&stream)); - CloseHandle(file); - if (stream.dwError != 0) { - throw std::runtime_error("Rich Edit could not read the RTF stream"); - } - } else { - const auto text = decode_text(read_file(path)); - SetWindowTextW(editor_, text.c_str()); - } - current_path_ = path; - loading_ = false; - set_clean(); - } catch (const std::exception& error) { - loading_ = false; - const std::wstring detail(error.what(), error.what() + std::strlen(error.what())); - MessageBoxW(main_window_, (L"Could not open the document.\n\n" + detail).c_str(), - kProductName, MB_OK | MB_ICONERROR); - } - } - - bool save(const bool save_as) { - std::wstring path = current_path_; - if (save_as || path.empty()) { - std::array selected{}; - if (!path.empty()) { - wcsncpy_s(selected.data(), selected.size(), path.c_str(), _TRUNCATE); - } else { - wcscpy_s(selected.data(), selected.size(), L"Document.rtf"); - } - OPENFILENAMEW dialog{sizeof(dialog)}; - dialog.hwndOwner = main_window_; - dialog.lpstrFilter = L"Rich Text Format (*.rtf)\0*.rtf\0UTF-8 text (*.txt)\0*.txt\0"; - dialog.lpstrFile = selected.data(); - dialog.nMaxFile = static_cast(selected.size()); - dialog.lpstrDefExt = L"rtf"; - dialog.Flags = OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST; - if (!GetSaveFileNameW(&dialog)) { - return false; - } - path = selected.data(); - } - - try { - if (lower_extension(path) == L".rtf") { - const HANDLE file = CreateFileW(path.c_str(), GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, - FILE_ATTRIBUTE_NORMAL, nullptr); - if (file == INVALID_HANDLE_VALUE) { - throw std::runtime_error("CreateFileW failed: " + std::to_string(GetLastError())); - } - EDITSTREAM stream{reinterpret_cast(file), 0, &stream_out}; - SendMessageW(editor_, EM_STREAMOUT, SF_RTF, reinterpret_cast(&stream)); - CloseHandle(file); - if (stream.dwError != 0) { - throw std::runtime_error("Rich Edit could not write the RTF stream"); - } - } else { - write_file(path, encode_utf8(document_text())); - } - current_path_ = path; - set_clean(); - return true; - } catch (const std::exception& error) { - const std::wstring detail(error.what(), error.what() + std::strlen(error.what())); - MessageBoxW(main_window_, (L"Could not save the document.\n\n" + detail).c_str(), - kProductName, MB_OK | MB_ICONERROR); - return false; - } - } - - bool prompt_save_changes() { - if (SendMessageW(editor_, EM_GETMODIFY, 0, 0) == 0) { - return true; - } - const int answer = MessageBoxW(main_window_, L"Save changes to this document?", - kProductName, MB_YESNOCANCEL | MB_ICONQUESTION); - return answer == IDNO || (answer == IDYES && save(false)); - } - - void set_clean() { - SendMessageW(editor_, EM_SETMODIFY, FALSE, 0); - update_title(); - status_dirty_ = true; - } - - void update_title() const { - std::wstring name = current_path_.empty() - ? L"Document" - : std::filesystem::path(current_path_).filename().wstring(); - if (!loading_ && SendMessageW(editor_, EM_GETMODIFY, 0, 0) != 0) { - name += L" *"; - } - SetWindowTextW(main_window_, (name + L" - " + kProductName).c_str()); - } - - [[nodiscard]] std::wstring document_text() const { - const int length = GetWindowTextLengthW(editor_); - std::wstring text(static_cast(length) + 1, L'\0'); - if (length != 0) { - GetWindowTextW(editor_, text.data(), length + 1); - } - text.resize(static_cast(length)); - return text; - } - - [[nodiscard]] static std::pair count_text( - const std::wstring_view text) { - std::size_t words = 0; - std::size_t characters = 0; - bool in_word = false; - for (const wchar_t character : text) { - if (character != L'\r' && character != L'\n') { - ++characters; - } - const bool word_character = std::iswalnum(character) != 0 || character == L'_'; - if (word_character && !in_word) { - ++words; - } - in_word = word_character; - } - return {words, characters}; - } - - void update_status() { - const auto text = document_text(); - const auto [words, characters] = count_text(text); - CHARRANGE selection{}; - SendMessageW(editor_, EM_EXGETSEL, 0, reinterpret_cast(&selection)); - const LRESULT line = SendMessageW(editor_, EM_LINEFROMCHAR, selection.cpMin, 0); - const LRESULT line_start = SendMessageW(editor_, EM_LINEINDEX, line, 0); - - const std::wstring file = current_path_.empty() ? L" New document" : L" " + current_path_; - const std::wstring counts = L"Words: " + std::to_wstring(words) + - L" Characters: " + std::to_wstring(characters); - const std::wstring position = L"Ln " + std::to_wstring(line + 1) + - L", Col " + std::to_wstring(selection.cpMin - line_start + 1); - SendMessageW(status_, SB_SETTEXTW, 0, reinterpret_cast(file.c_str())); - SendMessageW(status_, SB_SETTEXTW, 1, reinterpret_cast(counts.c_str())); - SendMessageW(status_, SB_SETTEXTW, 2, reinterpret_cast(position.c_str())); - status_dirty_ = false; - } - - void show_word_count() const { - const auto [words, characters] = count_text(document_text()); - const std::wstring message = L"Words: " + std::to_wstring(words) + - L"\nCharacters (excluding paragraph marks): " + - std::to_wstring(characters); - MessageBoxW(main_window_, message.c_str(), L"Word Count", MB_OK | MB_ICONINFORMATION); - } - - void toggle_effect(const DWORD mask, const DWORD effect) const { - CHARFORMAT2W format{sizeof(format)}; - format.dwMask = mask; - SendMessageW(editor_, EM_GETCHARFORMAT, SCF_SELECTION, reinterpret_cast(&format)); - format.dwEffects ^= effect; - SendMessageW(editor_, EM_SETCHARFORMAT, SCF_SELECTION, reinterpret_cast(&format)); - } - - void set_alignment(const WORD alignment) const { - PARAFORMAT2 format{sizeof(format)}; - format.dwMask = PFM_ALIGNMENT; - format.wAlignment = alignment; - SendMessageW(editor_, EM_SETPARAFORMAT, 0, reinterpret_cast(&format)); - } - - void choose_font() const { - CHARFORMAT2W current{sizeof(current)}; - SendMessageW(editor_, EM_GETCHARFORMAT, SCF_SELECTION, reinterpret_cast(¤t)); - HDC dc = GetDC(editor_); - LOGFONTW font{}; - font.lfHeight = -MulDiv(current.yHeight / 20, GetDeviceCaps(dc, LOGPIXELSY), 72); - font.lfWeight = (current.dwEffects & CFE_BOLD) != 0 ? FW_BOLD : FW_NORMAL; - font.lfItalic = (current.dwEffects & CFE_ITALIC) != 0; - font.lfUnderline = (current.dwEffects & CFE_UNDERLINE) != 0; - wcsncpy_s(font.lfFaceName, current.szFaceName, _TRUNCATE); - ReleaseDC(editor_, dc); - - CHOOSEFONTW dialog{sizeof(dialog)}; - dialog.hwndOwner = main_window_; - dialog.lpLogFont = &font; - dialog.iPointSize = current.yHeight / 2; - dialog.rgbColors = current.crTextColor; - dialog.Flags = CF_SCREENFONTS | CF_INITTOLOGFONTSTRUCT | CF_EFFECTS; - if (!ChooseFontW(&dialog)) { - return; - } - - CHARFORMAT2W selected{sizeof(selected)}; - selected.dwMask = CFM_FACE | CFM_SIZE | CFM_COLOR | CFM_BOLD | CFM_ITALIC | CFM_UNDERLINE; - selected.yHeight = dialog.iPointSize * 2; - selected.crTextColor = dialog.rgbColors; - selected.dwEffects = (font.lfWeight >= FW_BOLD ? CFE_BOLD : 0) | - (font.lfItalic ? CFE_ITALIC : 0) | - (font.lfUnderline ? CFE_UNDERLINE : 0); - wcsncpy_s(selected.szFaceName, font.lfFaceName, _TRUNCATE); - SendMessageW(editor_, EM_SETCHARFORMAT, SCF_SELECTION, reinterpret_cast(&selected)); - } - - void show_find() { - if (find_dialog_ != nullptr) { - SetForegroundWindow(find_dialog_); - return; - } - find_state_ = {}; - find_state_.lStructSize = sizeof(find_state_); - find_state_.hwndOwner = main_window_; - find_state_.Flags = FR_DOWN; - find_state_.lpstrFindWhat = find_text_.data(); - find_state_.wFindWhatLen = static_cast(find_text_.size()); - find_dialog_ = FindTextW(&find_state_); - } - - void on_find_message(const FINDREPLACEW& message) { - if ((message.Flags & FR_DIALOGTERM) != 0) { - find_dialog_ = nullptr; - return; - } - if ((message.Flags & FR_FINDNEXT) != 0) { - find_flags_ = message.Flags; - find_next(message.Flags); - } - } - - void find_next(const DWORD flags) { - if (find_text_[0] == L'\0') { - show_find(); - return; - } - CHARRANGE selection{}; - SendMessageW(editor_, EM_EXGETSEL, 0, reinterpret_cast(&selection)); - const LONG text_length = GetWindowTextLengthW(editor_); - FINDTEXTEXW search{}; - search.lpstrText = find_text_.data(); - const bool down = (flags & FR_DOWN) != 0; - search.chrg = down ? CHARRANGE{selection.cpMax, text_length} - : CHARRANGE{selection.cpMin - 1, 0}; - DWORD options = down ? FR_DOWN : 0; - options |= flags & (FR_MATCHCASE | FR_WHOLEWORD); - LRESULT result = SendMessageW(editor_, EM_FINDTEXTEXW, options, - reinterpret_cast(&search)); - if (result == -1) { - search.chrg = down ? CHARRANGE{0, text_length} : CHARRANGE{text_length, 0}; - result = SendMessageW(editor_, EM_FINDTEXTEXW, options, - reinterpret_cast(&search)); - } - if (result == -1) { - MessageBeep(MB_ICONINFORMATION); - return; - } - SendMessageW(editor_, EM_EXSETSEL, 0, reinterpret_cast(&search.chrgText)); - SendMessageW(editor_, EM_SCROLLCARET, 0, 0); - SetFocus(editor_); - } - - void print_document() const { - PRINTDLGW dialog{sizeof(dialog)}; - dialog.hwndOwner = main_window_; - dialog.Flags = PD_RETURNDC | PD_NOSELECTION | PD_USEDEVMODECOPIESANDCOLLATE; - if (!PrintDlgW(&dialog)) { - return; - } - DOCINFOW document{sizeof(document)}; - document.lpszDocName = current_path_.empty() ? L"Document" : current_path_.c_str(); - if (StartDocW(dialog.hDC, &document) <= 0) { - DeleteDC(dialog.hDC); - return; - } - - FORMATRANGE range{}; - range.hdc = dialog.hDC; - range.hdcTarget = dialog.hDC; - const int dpi_x = GetDeviceCaps(dialog.hDC, LOGPIXELSX); - const int dpi_y = GetDeviceCaps(dialog.hDC, LOGPIXELSY); - range.rcPage = {0, 0, - MulDiv(GetDeviceCaps(dialog.hDC, PHYSICALWIDTH), 1440, dpi_x), - MulDiv(GetDeviceCaps(dialog.hDC, PHYSICALHEIGHT), 1440, dpi_y)}; - constexpr LONG margin = 1080; - range.rc = {range.rcPage.left + margin, range.rcPage.top + margin, - range.rcPage.right - margin, range.rcPage.bottom - margin}; - range.chrg = {0, -1}; - - LONG next = 0; - const LONG length = GetWindowTextLengthW(editor_); - bool failed = false; - while (next < length && !failed) { - if (StartPage(dialog.hDC) <= 0) { - failed = true; - break; - } - range.chrg.cpMin = next; - next = static_cast(SendMessageW(editor_, EM_FORMATRANGE, TRUE, - reinterpret_cast(&range))); - if (EndPage(dialog.hDC) <= 0 || next <= range.chrg.cpMin) { - failed = true; - } - } - SendMessageW(editor_, EM_FORMATRANGE, FALSE, 0); - failed ? AbortDoc(dialog.hDC) : EndDoc(dialog.hDC); - DeleteDC(dialog.hDC); - } - - void show_about() const { - const std::wstring message = - L"Microsoft Word for Windows — native x64 migration\n\n" - L"This executable is AMD64 and contains no 16-bit assembly.\n" - L"Legacy assembly modules accounted for: " + - std::to_wstring(opus::x64::assembly_manifest_count()) + - L"\n\nCurrent document formats: RTF and UTF-8 text."; - MessageBoxW(main_window_, message.c_str(), L"About", MB_OK | MB_ICONINFORMATION); - } - - static DWORD CALLBACK stream_in(const DWORD_PTR cookie, const LPBYTE buffer, - const LONG byte_count, LONG* bytes_read) { - DWORD read = 0; - if (!ReadFile(reinterpret_cast(cookie), buffer, - static_cast(byte_count), &read, nullptr)) { - *bytes_read = 0; - return GetLastError(); - } - *bytes_read = static_cast(read); - return 0; - } - - static DWORD CALLBACK stream_out(const DWORD_PTR cookie, const LPBYTE buffer, - const LONG byte_count, LONG* bytes_written) { - DWORD written = 0; - if (!WriteFile(reinterpret_cast(cookie), buffer, - static_cast(byte_count), &written, nullptr)) { - *bytes_written = 0; - return GetLastError(); - } - *bytes_written = static_cast(written); - return 0; - } - - void show_error(const std::wstring_view message, const DWORD error) const { - const std::wstring detail = std::wstring(message) + L".\n\n" + system_error_message(error); - MessageBoxW(main_window_, detail.c_str(), kProductName, MB_OK | MB_ICONERROR); - } - - HINSTANCE instance_{}; - HMODULE rich_edit_library_{}; - HWND main_window_{}; - HWND editor_{}; - HWND status_{}; - HWND find_dialog_{}; - UINT find_message_{}; - FINDREPLACEW find_state_{}; - std::array find_text_{}; - DWORD find_flags_{FR_DOWN}; - std::wstring current_path_; - bool loading_{}; - bool status_dirty_{true}; -}; - -} // namespace - -int WINAPI wWinMain(const HINSTANCE instance, HINSTANCE, PWSTR, const int show_command) { - WordApplication application(instance); - return application.run(show_command); -} diff --git a/src/port/winword_x64.rc b/src/port/winword_x64.rc deleted file mode 100644 index e098c75..0000000 --- a/src/port/winword_x64.rc +++ /dev/null @@ -1,35 +0,0 @@ -#include - -1 RT_MANIFEST "winword.manifest" - -VS_VERSION_INFO VERSIONINFO - FILEVERSION 0,1,0,0 - PRODUCTVERSION 0,1,0,0 - FILEFLAGSMASK 0x3fL -#ifdef _DEBUG - FILEFLAGS VS_FF_DEBUG -#else - FILEFLAGS 0x0L -#endif - FILEOS VOS_NT_WINDOWS32 - FILETYPE VFT_APP - FILESUBTYPE 0x0L -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "040904b0" - BEGIN - VALUE "CompanyName", "Microsoft (historical source)" - VALUE "FileDescription", "Microsoft Word for Windows x64 port" - VALUE "FileVersion", "0.1.0" - VALUE "InternalName", "WINWORD" - VALUE "OriginalFilename", "WINWORD_BOOTSTRAP.exe" - VALUE "ProductName", "Microsoft Word x64 Port" - VALUE "ProductVersion", "0.1.0" - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x0409, 1200 - END -END