Reconstruct readable C++ and organize cohesive source subsystems

Administrative source import: native x86 compilation passed for 423 authored units. Source placement and submission states are migrated separately; this commit does not imply peer acceptance.

Previous head: 35d2a8cb36
Payload SHA256: 592f016d2730c5593bb653aa054c1e5f76a6df72c8f5a09e66f74876e2e33dee
This commit is contained in:
ragnarok-clanker 2026-10-25 07:42:41 +00:00
commit a47d20f8be
890 changed files with 11796 additions and 16664 deletions

View file

@ -4,7 +4,7 @@ NamespaceIndentation: All
FixNamespaceComments: false FixNamespaceComments: false
TabWidth: 2 TabWidth: 2
UseTab: Never UseTab: Never
ColumnLimit: 120 ColumnLimit: 160
AlignAfterOpenBracket: BlockIndent AlignAfterOpenBracket: BlockIndent
ContinuationIndentWidth: 2 ContinuationIndentWidth: 2
AlwaysBreakAfterReturnType: None AlwaysBreakAfterReturnType: None
@ -12,7 +12,7 @@ BreakBeforeBraces: Custom
BraceWrapping: BraceWrapping:
AfterFunction: false AfterFunction: false
AllowShortIfStatementsOnASingleLine: Never AllowShortIfStatementsOnASingleLine: Never
InsertBraces: true InsertBraces: false
AllowShortLoopsOnASingleLine: false AllowShortLoopsOnASingleLine: false
AllowShortFunctionsOnASingleLine: None AllowShortFunctionsOnASingleLine: None
AllowShortBlocksOnASingleLine: Never AllowShortBlocksOnASingleLine: Never
@ -21,25 +21,13 @@ SpaceBeforeParens: Custom
SpaceBeforeParensOptions: SpaceBeforeParensOptions:
AfterControlStatements: true AfterControlStatements: true
AfterFunctionDefinitionName: false AfterFunctionDefinitionName: false
BinPackParameters: false BinPackArguments: true
AllowAllParametersOfDeclarationOnNextLine: false BinPackParameters: BinPack
PenaltyBreakBeforeFirstCallParameter: 0 AllowAllParametersOfDeclarationOnNextLine: true
PenaltyBreakBeforeFirstCallParameter: 19
PointerAlignment: Left PointerAlignment: Left
# Include sorting: BreakStringLiterals: false
# 1. pch (must stay first) 2. other WoW.exe project headers ReflowComments: Never
# 3. generated gen/ headers 4. system <...> headers MaxEmptyLinesToKeep: 1
# Each group is alphabetised; Regroup re-buckets across blank lines. SortIncludes: Never
# IncludeIsMainRegex is set to never match so no header is auto-pinned above pch IncludeBlocks: Preserve
# (clang-format otherwise forces the "main" header to the very top).
SortIncludes: CaseSensitive
IncludeBlocks: Regroup
IncludeIsMainRegex: "(WILL_NEVER_MATCH_zzz)$"
IncludeCategories:
- Regex: '^"pch(-impl)?\.h"'
Priority: 1
- Regex: '^"gen/'
Priority: 3
- Regex: '^<'
Priority: 4
- Regex: '^"'
Priority: 2

View file

@ -10,12 +10,12 @@ add_subdirectory(ThirdParty)
add_library(RagnarokUtility STATIC add_library(RagnarokUtility STATIC
Source/Utility/XorBytes.cpp Source/Utility/XorBytes.cpp
Source/Utility/Base64.cpp Source/Utility/Base64.cpp
Source/Utility/GroupedInteger.cpp Source/Utility/Text/GroupedInteger.cpp
Source/Utility/LanguageCharset.cpp Source/Utility/Text/LanguageCharset.cpp
Source/Utility/ClientAngles.cpp Source/Utility/Math/ClientAngles.cpp
Source/Utility/AffineMatrix.cpp Source/Utility/Math/AffineMatrix.cpp
Source/Utility/AffineLookAt.cpp Source/Utility/Math/AffineLookAt.cpp
Source/Utility/RayTriangle.cpp Source/Utility/Math/RayTriangle.cpp
Source/Utility/Rijndael.cpp Source/Utility/Rijndael.cpp
) )
@ -28,21 +28,21 @@ set_target_properties(RagnarokUtility PROPERTIES CXX_EXTENSIONS OFF)
if(WIN32) if(WIN32)
target_sources(RagnarokUtility PRIVATE target_sources(RagnarokUtility PRIVATE
Source/Utility/GuidParse.cpp Source/Utility/GuidParse.cpp
Source/Utility/NSyncObject.cpp Source/Utility/Threading/NSyncObject.cpp
Source/Utility/CSyncObject.cpp Source/Utility/Threading/CSyncObject.cpp
Source/Utility/NThread.cpp Source/Utility/Threading/NThread.cpp
Source/Utility/NMessageQueue.cpp Source/Utility/Async/NMessageQueue.cpp
Source/Utility/NMessageMgr.cpp Source/Utility/Async/NMessageMgr.cpp
Source/Utility/NObject.cpp Source/Utility/Async/NObject.cpp
Source/Utility/NRecycleObject.cpp Source/Utility/Async/NRecycleObject.cpp
Source/Utility/NRecycleObjectPoolMgr.cpp Source/Utility/Async/NRecycleObjectPoolMgr.cpp
Source/Utility/NAsyncWork.cpp Source/Utility/Async/NAsyncWork.cpp
Source/Utility/NAsyncWorkQueue.cpp Source/Utility/Async/NAsyncWorkQueue.cpp
Source/Utility/NAsyncWorkThread.cpp Source/Utility/Async/NAsyncWorkThread.cpp
Source/Utility/NAsyncWorkMgr.cpp Source/Utility/Async/NAsyncWorkMgr.cpp
Source/Utility/AsyncDiagnostic.cpp Source/Utility/Async/AsyncDiagnostic.cpp
Source/Contents/InstantVar.cpp Source/Contents/InstantVar.cpp
Source/Utility/FormatString.cpp Source/Utility/Text/FormatString.cpp
) )
target_compile_definitions(RagnarokUtility PUBLIC target_compile_definitions(RagnarokUtility PUBLIC
WIN32_LEAN_AND_MEAN NOMINMAX _ITERATOR_DEBUG_LEVEL=0 WIN32_LEAN_AND_MEAN NOMINMAX _ITERATOR_DEBUG_LEVEL=0
@ -69,31 +69,32 @@ if(WIN32)
set_target_properties(RagnarokArchive PROPERTIES CXX_EXTENSIONS OFF) set_target_properties(RagnarokArchive PROPERTIES CXX_EXTENSIONS OFF)
add_library(RagnarokClientStartup STATIC add_library(RagnarokClientStartup STATIC
Source/Client/Launcher.cpp Source/Client/Application/Launcher.cpp
Source/Client/ApplicationWindow.cpp Source/Client/Application/ApplicationWindow.cpp
Source/Client/ErrorDialog.cpp Source/Client/Application/ErrorDialog.cpp
Source/Client/UnhandledExceptionHandler.cpp Source/Client/Application/UnhandledExceptionHandler.cpp
Source/Client/ModeCursor.cpp Source/Client/Mode/ModeCursor.cpp
Source/Client/Mouse.cpp Source/Client/Mode/ActiveGameMode.cpp
Source/Client/Input/Mouse.cpp
Source/Client/ClientOptions.cpp Source/Client/ClientOptions.cpp
Source/Client/TextInputBuffer.cpp Source/Client/Input/TextInputBuffer.cpp
Source/Client/ImeInput.cpp Source/Client/Input/ImeInput.cpp
Source/Client/TraditionalKeyInput.cpp Source/Client/Input/TraditionalKeyInput.cpp
Source/Client/InputCharset.cpp Source/Client/Input/InputCharset.cpp
Source/Client/BrowserWindowSizing.cpp Source/Client/Browser/BrowserWindowSizing.cpp
Source/Client/BrowserInterface.cpp Source/Client/Browser/BrowserInterface.cpp
Source/Client/BrowserCursor.cpp Source/Client/Browser/BrowserCursor.cpp
Source/Client/BrowserAddressEdit.cpp Source/Client/Browser/BrowserAddressEdit.cpp
Source/Client/BrowserHostWindow.cpp Source/Client/Browser/BrowserHostWindow.cpp
Source/Client/BrowserCommandTarget.cpp Source/Client/Browser/BrowserCommandTarget.cpp
Source/Client/BrowserInPlace.cpp Source/Client/Browser/BrowserInPlace.cpp
Source/Client/BrowserDocumentUI.cpp Source/Client/Browser/BrowserDocumentUI.cpp
Source/Client/BrowserClientSite.cpp Source/Client/Browser/BrowserClientSite.cpp
Source/Client/BrowserAddressBar.cpp Source/Client/Browser/BrowserAddressBar.cpp
Source/Client/BrowserFrame.cpp Source/Client/Browser/BrowserFrame.cpp
Source/Client/BrowserSiteAttachment.cpp Source/Client/Browser/BrowserSiteAttachment.cpp
Source/Client/BrowserClose.cpp Source/Client/Browser/BrowserClose.cpp
Source/Client/BrowserCreation.cpp Source/Client/Browser/BrowserCreation.cpp
) )
target_include_directories(RagnarokClientStartup PUBLIC target_include_directories(RagnarokClientStartup PUBLIC
"${CMAKE_CURRENT_SOURCE_DIR}/Source" "${CMAKE_CURRENT_SOURCE_DIR}/Source"
@ -104,74 +105,74 @@ if(WIN32)
set_target_properties(RagnarokClientStartup PROPERTIES CXX_EXTENSIONS OFF) set_target_properties(RagnarokClientStartup PROPERTIES CXX_EXTENSIONS OFF)
add_library(RagnarokRender STATIC add_library(RagnarokRender STATIC
Source/Render/RenderPrimitive.cpp Source/Render/Primitives/RenderPrimitive.cpp
Source/Render/RenderPrimitivePool.cpp Source/Render/Primitives/RenderPrimitivePool.cpp
Source/Render/RenderCommandPool.cpp Source/Render/Primitives/RenderCommandPool.cpp
Source/Render/LightmappedPrimitive.cpp Source/Render/Primitives/LightmappedPrimitive.cpp
Source/Render/DepthAlpha.cpp Source/Render/Primitives/DepthAlpha.cpp
Source/Render/RenderVertexPool.cpp Source/Render/Primitives/RenderVertexPool.cpp
Source/Render/RenderProjection.cpp Source/Render/Projection/RenderProjection.cpp
Source/Render/RendererViewport.cpp Source/Render/Projection/RendererViewport.cpp
Source/Render/RenderRay.cpp Source/Render/Projection/RenderRay.cpp
Source/Render/BitmapPixels.cpp Source/Render/Textures/BitmapPixels.cpp
Source/Render/Surface.cpp Source/Render/Textures/Surface.cpp
Source/Render/RenderQueues.cpp Source/Render/Primitives/RenderQueues.cpp
Source/Render/Texture.cpp Source/Render/Textures/Texture.cpp
Source/Render/UIRPData.cpp Source/Render/Primitives/UIRPData.cpp
Source/Render/SurfaceDX9.cpp Source/Render/DX9/SurfaceDX9.cpp
Source/Render/SurfaceDX7.cpp Source/Render/DX7/SurfaceDX7.cpp
Source/Render/D3DX9Runtime.cpp Source/Render/DX9/D3DX9Runtime.cpp
Source/Render/TextureDX9.cpp Source/Render/DX9/TextureDX9.cpp
Source/Render/TextureDX7.cpp Source/Render/DX7/TextureDX7.cpp
Source/Render/TextureBindingDX7.cpp Source/Render/DX7/TextureBindingDX7.cpp
Source/Render/TextureAllocationDX7.cpp Source/Render/DX7/TextureAllocationDX7.cpp
Source/Render/BitmapTextureCache.cpp Source/Render/Textures/BitmapTextureCache.cpp
Source/Render/SpriteAtlas.cpp Source/Render/Textures/SpriteAtlas.cpp
Source/Render/DeviceContext.cpp Source/Render/DeviceContext/DeviceContext.cpp
Source/Render/DCBitmap.cpp Source/Render/DeviceContext/DCBitmap.cpp
Source/Render/DCSurface.cpp Source/Render/DeviceContext/DCSurface.cpp
Source/Render/DeviceStateDX9.cpp Source/Render/DX9/DeviceStateDX9.cpp
Source/Render/DeviceOwnerDX9.cpp Source/Render/DX9/DeviceOwnerDX9.cpp
Source/Render/DeviceOwnerDX7.cpp Source/Render/DX7/DeviceOwnerDX7.cpp
Source/Render/DeviceCapabilitiesDX7.cpp Source/Render/DX7/DeviceCapabilitiesDX7.cpp
Source/Render/TextureCalibrationDX7.cpp Source/Render/DX7/TextureCalibrationDX7.cpp
Source/Render/RenderAdapter.cpp Source/Render/RenderAdapter.cpp
Source/Render/RenderAdapterDX7.cpp Source/Render/DX7/RenderAdapterDX7.cpp
Source/Render/RenderBackends.cpp Source/Render/RenderBackends.cpp
Source/Render/DisplayMode.cpp Source/Render/DisplayMode.cpp
Source/Render/DisplayModeDX7.cpp Source/Render/DX7/DisplayModeDX7.cpp
Source/Render/DirectDrawEnumeration.cpp Source/Render/DX7/DirectDrawEnumeration.cpp
Source/Render/DeviceRecoveryDX9.cpp Source/Render/DX9/DeviceRecoveryDX9.cpp
Source/Render/BackBufferDX9.cpp Source/Render/DX9/BackBufferDX9.cpp
Source/Render/BackBufferDX7.cpp Source/Render/DX7/BackBufferDX7.cpp
Source/Render/Direct3DErrors.cpp Source/Render/Direct3DErrors.cpp
Source/Render/RendererBackBuffer.cpp Source/Render/DX9/RendererBackBuffer.cpp
Source/Render/Renderer.cpp Source/Render/Renderer.cpp
Source/Render/RendererDX9.cpp Source/Render/DX9/RendererDX9.cpp
Source/Render/RenderSystemDX7.cpp Source/Render/DX7/RenderSystemDX7.cpp
Source/Render/RenderSystemDX9.cpp Source/Render/DX9/RenderSystemDX9.cpp
Source/Render/RendererDX9Operations.cpp Source/Render/DX9/RendererDX9Operations.cpp
Source/Render/RendererDX7Operations.cpp Source/Render/DX7/RendererDX7Operations.cpp
Source/Render/RenderToTextureDX7.cpp Source/Render/DX7/RenderToTextureDX7.cpp
Source/Render/TextureModesDX9.cpp Source/Render/DX9/TextureModesDX9.cpp
Source/Render/TextureModesDX7.cpp Source/Render/DX7/TextureModesDX7.cpp
Source/Render/TextureStageModes.cpp Source/Render/TextureStageModes.cpp
Source/Render/RendererFogDX9.cpp Source/Render/DX9/RendererFogDX9.cpp
Source/Render/RendererFogDX7.cpp Source/Render/DX7/RendererFogDX7.cpp
Source/Render/OpaqueQueuesDX9.cpp Source/Render/DX9/OpaqueQueuesDX9.cpp
Source/Render/GroundQueuesDX9.cpp Source/Render/DX9/GroundQueuesDX9.cpp
Source/Render/AlphaQueuesDX9.cpp Source/Render/DX9/AlphaQueuesDX9.cpp
Source/Render/QueuedDrawingDX9.cpp Source/Render/DX9/QueuedDrawingDX9.cpp
Source/Render/RendererFrameDX9.cpp Source/Render/DX9/RendererFrameDX9.cpp
Source/Render/ScreenshotDX9.cpp Source/Render/DX9/ScreenshotDX9.cpp
Source/Render/ScreenshotDX7.cpp Source/Render/DX7/ScreenshotDX7.cpp
Source/Contents/Effect.cpp Source/Contents/Effects/Effect.cpp
Source/Contents/EffectLists.cpp Source/Contents/Effects/EffectLists.cpp
Source/Render/BlendColor.cpp Source/Render/BlendColor.cpp
Source/Render/RendererPresentation.cpp Source/Render/DX9/RendererPresentation.cpp
Source/Render/RendererPresentationDX7.cpp Source/Render/DX7/RendererPresentationDX7.cpp
Source/Render/FontCache.cpp Source/Render/Text/FontCache.cpp
Source/Render/TextContext.cpp Source/Render/Text/TextContext.cpp
) )
target_include_directories(RagnarokRender PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/Source") target_include_directories(RagnarokRender PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/Source")
target_compile_features(RagnarokRender PUBLIC cxx_std_20) target_compile_features(RagnarokRender PUBLIC cxx_std_20)
@ -180,116 +181,116 @@ if(WIN32)
target_link_libraries(RagnarokRender PUBLIC gdi32 winmm gdiplus d3d9 ddraw dxguid) target_link_libraries(RagnarokRender PUBLIC gdi32 winmm gdiplus d3d9 ddraw dxguid)
add_library(RagnarokUI STATIC add_library(RagnarokUI STATIC
Source/UI/WindowGeometry.cpp Source/UI/Layout/WindowGeometry.cpp
Source/UI/WindowCoordinates.cpp Source/UI/Layout/WindowCoordinates.cpp
Source/UI/WindowAncestry.cpp Source/UI/Window/WindowAncestry.cpp
Source/UI/WindowRegistration.cpp Source/UI/Window/WindowRegistration.cpp
Source/UI/WindowRetirement.cpp Source/UI/Window/WindowRetirement.cpp
Source/UI/WindowOrdering.cpp Source/UI/Window/WindowOrdering.cpp
Source/UI/WindowMouseHit.cpp Source/UI/Window/WindowMouseHit.cpp
Source/UI/UIWindow.cpp Source/UI/Window/UIWindow.cpp
Source/UI/UIControl.cpp Source/UI/Window/UIControl.cpp
Source/UI/UIDropDownBox.cpp Source/UI/Lists/UIDropDownBox.cpp
Source/UI/ChatSuggestions.cpp Source/UI/Chat/ChatSuggestions.cpp
Source/UI/ItemTagRetirement.cpp Source/UI/Edit/ItemTagRetirement.cpp
Source/UI/SnapRectangle.cpp Source/UI/Layout/SnapRectangle.cpp
Source/UI/SnapObject.cpp Source/UI/Layout/SnapObject.cpp
Source/UI/SnapMgr.cpp Source/UI/Layout/SnapMgr.cpp
Source/UI/WindowSnapping.cpp Source/UI/Layout/WindowSnapping.cpp
Source/UI/FrameTextCache.cpp Source/UI/Window/FrameTextCache.cpp
Source/UI/FrameFocus.cpp Source/UI/Window/FrameFocus.cpp
Source/UI/InputFocus.cpp Source/UI/Window/InputFocus.cpp
Source/UI/UIFrameWnd.cpp Source/UI/Window/UIFrameWnd.cpp
Source/UI/ShortcutGrid.cpp Source/UI/Shortcuts/ShortcutGrid.cpp
Source/UI/WindowPolicy.cpp Source/UI/Window/WindowPolicy.cpp
Source/UI/UIButton.cpp Source/UI/Buttons/UIButton.cpp
Source/UI/UIBasicButton.cpp Source/UI/Buttons/UIBasicButton.cpp
Source/UI/UITextButton.cpp Source/UI/Buttons/UITextButton.cpp
Source/UI/UINaviLinkButton.cpp Source/UI/Buttons/UINaviLinkButton.cpp
Source/UI/UIEditCtrl.cpp Source/UI/Edit/UIEditCtrl.cpp
Source/UI/UIEditVariants.cpp Source/UI/Edit/UIEditVariants.cpp
Source/UI/UIEditLinks.cpp Source/UI/Edit/UIEditLinks.cpp
Source/UI/UIBitmapButton.cpp Source/UI/Buttons/UIBitmapButton.cpp
Source/UI/UIBmpTextButton.cpp Source/UI/Buttons/UIBmpTextButton.cpp
Source/UI/UIJobSlotButton.cpp Source/UI/Buttons/UIJobSlotButton.cpp
Source/UI/UIPartyInfoWnd.cpp Source/UI/Party/UIPartyInfoWnd.cpp
Source/UI/UIAdvenPartyBoardWnd.cpp Source/UI/Party/UIAdvenPartyBoardWnd.cpp
Source/UI/UIApplyForPartyWnd.cpp Source/UI/Party/UIApplyForPartyWnd.cpp
Source/UI/UIRegisterPartyWnd.cpp Source/UI/Party/UIRegisterPartyWnd.cpp
Source/UI/UIRequestJoinPartyMgr.cpp Source/UI/Party/UIRequestJoinPartyMgr.cpp
Source/UI/UIRequestJoinPartyWnd.cpp Source/UI/Party/UIRequestJoinPartyWnd.cpp
Source/UI/UIChooseSellBuyWnd.cpp Source/UI/Acceptance/UIChooseSellBuyWnd.cpp
Source/UI/UIExchangeAcceptWnd.cpp Source/UI/Acceptance/UIExchangeAcceptWnd.cpp
Source/UI/UIJoinPartyAcceptWnd.cpp Source/UI/Acceptance/UIJoinPartyAcceptWnd.cpp
Source/Network/PartyJoinRequest.cpp Source/Network/Packets/PartyJoinRequest.cpp
Source/UI/UICheckBox.cpp Source/UI/Buttons/UICheckBox.cpp
Source/UI/UIScrollBar.cpp Source/UI/Lists/UIScrollBar.cpp
Source/UI/UIScrollBarNavi.cpp Source/UI/Lists/UIScrollBarNavi.cpp
Source/UI/UIListBox.cpp Source/UI/Lists/UIListBox.cpp
Source/UI/UIListBoxEx.cpp Source/UI/Lists/UIListBoxEx.cpp
Source/UI/UIComboBoxEx.cpp Source/UI/Lists/UIComboBoxEx.cpp
Source/UI/UIComboBoxDefWnd.cpp Source/UI/Lists/UIComboBoxDefWnd.cpp
Source/UI/TextMarkup.cpp Source/UI/Text/TextMarkup.cpp
Source/UI/PixelTextWrapping.cpp Source/UI/Text/PixelTextWrapping.cpp
Source/UI/ItemLinkDisplay.cpp Source/UI/Text/ItemLinkDisplay.cpp
Source/UI/UITextViewer.cpp Source/UI/Text/UITextViewer.cpp
Source/UI/UITextViewerLinks.cpp Source/UI/Text/UITextViewerLinks.cpp
Source/UI/UITextViewerMessages.cpp Source/UI/Text/UITextViewerMessages.cpp
Source/UI/UIStaticBitmap.cpp Source/UI/UIStaticBitmap.cpp
Source/UI/UIStaticText.cpp Source/UI/Text/UIStaticText.cpp
Source/UI/UIMessageBox.cpp Source/UI/Window/UIMessageBox.cpp
Source/UI/WindowDialogs.cpp Source/UI/Window/WindowDialogs.cpp
Source/Client/ReplayPacketRecorder.cpp Source/Client/ReplayPacketRecorder.cpp
Source/UI/CooldownState.cpp Source/UI/Cooldowns/CooldownState.cpp
Source/UI/CooldownDrawing.cpp Source/UI/Cooldowns/CooldownDrawing.cpp
Source/UI/CooldownOwner.cpp Source/UI/Cooldowns/CooldownOwner.cpp
Source/UI/ShortcutCooldowns.cpp Source/UI/Shortcuts/ShortcutCooldowns.cpp
Source/UI/ItemDrawing.cpp Source/UI/Shop/ItemDrawing.cpp
Source/UI/ShortcutPainting.cpp Source/UI/Shortcuts/ShortcutPainting.cpp
Source/UI/ShortcutBindings.cpp Source/UI/Shortcuts/ShortcutBindings.cpp
Source/UI/ShortcutLayout.cpp Source/UI/Shortcuts/ShortcutLayout.cpp
Source/UI/ShortcutActivation.cpp Source/UI/Shortcuts/ShortcutActivation.cpp
Source/UI/ShortcutControls.cpp Source/UI/Shortcuts/ShortcutControls.cpp
Source/UI/ShortcutControlEvents.cpp Source/UI/Shortcuts/ShortcutControlEvents.cpp
Source/UI/ShortcutTooltip.cpp Source/UI/Shortcuts/ShortcutTooltip.cpp
Source/UI/ShortcutPress.cpp Source/UI/Shortcuts/ShortcutPress.cpp
Source/UI/ShortcutDrop.cpp Source/UI/Shortcuts/ShortcutDrop.cpp
Source/UI/ShortcutUse.cpp Source/UI/Shortcuts/ShortcutUse.cpp
Source/UI/ShortcutVisibility.cpp Source/UI/Shortcuts/ShortcutVisibility.cpp
Source/UI/ShortcutDetails.cpp Source/UI/Shortcuts/ShortcutDetails.cpp
Source/UI/SkillDetailClose.cpp Source/UI/Shortcuts/SkillDetailClose.cpp
Source/UI/NormalItemSlotLoad.cpp Source/UI/Shortcuts/NormalItemSlotLoad.cpp
Source/UI/UIShortCutWnd.cpp Source/UI/Shortcuts/UIShortCutWnd.cpp
Source/UI/TextWrapping.cpp Source/UI/Text/TextWrapping.cpp
Source/UI/ChatNotifications.cpp Source/UI/Chat/ChatNotifications.cpp
Source/UI/BrowserFactory.cpp Source/Client/Browser/BrowserFactory.cpp
Source/UI/BrowserPopupDispatch.cpp Source/Client/Browser/BrowserPopupDispatch.cpp
Source/UI/WindowFrameUpdate.cpp Source/UI/Window/WindowFrameUpdate.cpp
Source/UI/UIBalloonText.cpp Source/UI/Text/UIBalloonText.cpp
Source/UI/UITransBalloonText.cpp Source/UI/Text/UITransBalloonText.cpp
Source/UI/ColoredText.cpp Source/UI/Text/ColoredText.cpp
Source/UI/WindowBorder.cpp Source/UI/Window/WindowBorder.cpp
Source/UI/EditTextState.cpp Source/UI/Edit/EditTextState.cpp
Source/UI/EditViewport.cpp Source/UI/Edit/EditViewport.cpp
Source/UI/EditPainting.cpp Source/UI/Edit/EditPainting.cpp
Source/UI/PriceColor.cpp Source/UI/Shop/PriceColor.cpp
Source/UI/MerchantShopRefresh.cpp Source/UI/Shop/MerchantShopRefresh.cpp
Source/UI/NavigationLink.cpp Source/UI/Text/NavigationLink.cpp
Source/UI/TextFitting.cpp Source/UI/Text/TextFitting.cpp
Source/Client/ModeDragState.cpp Source/Client/Mode/ModeDragState.cpp
Source/Client/ModeTooltip.cpp Source/Client/Mode/ModeTooltip.cpp
Source/Contents/CharacterConfigLoad.cpp Source/Contents/UserData/CharacterConfigLoad.cpp
Source/Contents/CharacterLinkedUserDataLoadAsyncWork.cpp Source/Contents/UserData/CharacterLinkedUserDataLoadAsyncWork.cpp
Source/Contents/EmblemCache.cpp Source/Contents/Emblems/EmblemCache.cpp
Source/Contents/EmblemUpload.cpp Source/Contents/Emblems/EmblemUpload.cpp
Source/Contents/ChangePartyMasterCompletion.cpp Source/Contents/Party/ChangePartyMasterCompletion.cpp
Source/Contents/PartyListCompletion.cpp Source/Contents/Party/PartyListCompletion.cpp
Source/Contents/StopRecruitingCompletion.cpp Source/Contents/Party/StopRecruitingCompletion.cpp
Source/Contents/MyPartyCompletion.cpp Source/Contents/Party/MyPartyCompletion.cpp
Source/Contents/PartyRegistrationCompletion.cpp Source/Contents/Party/PartyRegistrationCompletion.cpp
Source/Contents/PartyInfoCompletion.cpp Source/Contents/Party/PartyInfoCompletion.cpp
Source/Contents/PartyJoinResult.cpp Source/Contents/Party/PartyJoinResult.cpp
Source/Contents/PartyInvitation.cpp Source/Contents/Party/PartyInvitation.cpp
) )
target_include_directories(RagnarokUI PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/Source") target_include_directories(RagnarokUI PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/Source")
target_compile_features(RagnarokUI PUBLIC cxx_std_20) target_compile_features(RagnarokUI PUBLIC cxx_std_20)
@ -310,68 +311,68 @@ if(WIN32)
add_library(RagnarokResource STATIC add_library(RagnarokResource STATIC
Source/Resource/ResourceAliases.cpp Source/Resource/ResourceAliases.cpp
Source/Resource/BitmapImage.cpp Source/Resource/Bitmap/BitmapImage.cpp
Source/Resource/BitmapImagePng.cpp Source/Resource/Bitmap/BitmapImagePng.cpp
Source/Resource/BitmapImageJpeg.cpp Source/Resource/Bitmap/BitmapImageJpeg.cpp
Source/Resource/IJL15Runtime.cpp Source/Resource/Bitmap/IJL15Runtime.cpp
Source/Resource/MilesRuntime.cpp Source/Resource/Audio/MilesRuntime.cpp
Source/Resource/WaveResource.cpp Source/Resource/Audio/WaveResource.cpp
Source/Resource/Resource.cpp Source/Resource/Resource.cpp
Source/Resource/BitmapResource.cpp Source/Resource/Bitmap/BitmapResource.cpp
Source/Resource/SpriteResource.cpp Source/Resource/Sprites/SpriteResource.cpp
Source/Resource/SpriteEncoding.cpp Source/Resource/Sprites/SpriteEncoding.cpp
Source/Resource/ActionResource.cpp Source/Resource/Sprites/ActionResource.cpp
Source/Resource/TerrainResource.cpp Source/Resource/World/TerrainResource.cpp
Source/Resource/GroundResource.cpp Source/Resource/World/GroundResource.cpp
Source/Resource/WorldResource.cpp Source/Resource/World/WorldResource.cpp
Source/Resource/WorldEnvironment.cpp Source/Resource/World/WorldEnvironment.cpp
Source/Resource/WorldObjects.cpp Source/Resource/World/WorldObjects.cpp
Source/Resource/ModelMesh.cpp Source/Resource/Models/ModelMesh.cpp
Source/Resource/ModelResource.cpp Source/Resource/Models/ModelResource.cpp
Source/Resource/ModelNodeLoading.cpp Source/Resource/Models/ModelNodeLoading.cpp
Source/Resource/ModelResourceV2.cpp Source/Resource/Models/ModelResourceV2.cpp
Source/Resource/ModelNodeLoadingV2.cpp Source/Resource/Models/ModelNodeLoadingV2.cpp
Source/Resource/AnimatedModelResource.cpp Source/Resource/Models/AnimatedModelResource.cpp
Source/Contents/ModelAnimation.cpp Source/Contents/Models/ModelAnimation.cpp
Source/Contents/ModelQuaternion.cpp Source/Contents/Models/ModelQuaternion.cpp
Source/Contents/ModelNode.cpp Source/Contents/Models/ModelNode.cpp
Source/Contents/ModelTextureAnimation.cpp Source/Contents/Models/ModelTextureAnimation.cpp
Source/Contents/ModelRendering.cpp Source/Contents/Models/ModelRendering.cpp
Source/Contents/ModelTextureUV.cpp Source/Contents/Models/ModelTextureUV.cpp
Source/Contents/ModelLighting.cpp Source/Contents/Models/ModelLighting.cpp
Source/Contents/ModelNodeBounds.cpp Source/Contents/Models/ModelNodeBounds.cpp
Source/Contents/ModelActor.cpp Source/Contents/Models/ModelActor.cpp
Source/Contents/ModelActorBounds.cpp Source/Contents/Models/ModelActorBounds.cpp
Source/Contents/ModelPlayback.cpp Source/Contents/Models/ModelPlayback.cpp
Source/Contents/ModelOcclusion.cpp Source/Contents/Models/ModelOcclusion.cpp
Source/Contents/ModelMovies.cpp Source/Contents/Models/ModelMovies.cpp
Source/Contents/WorldModelPlacement.cpp Source/Contents/World/WorldModelPlacement.cpp
Source/Contents/WorldModelLoading.cpp Source/Contents/World/WorldModelLoading.cpp
Source/Contents/WorldGroundImport.cpp Source/Contents/World/WorldGroundImport.cpp
Source/Contents/WorldEnvironment.cpp Source/Contents/World/WorldEnvironment.cpp
Source/Contents/WorldSpatialTree.cpp Source/Contents/World/WorldSpatialTree.cpp
Source/Contents/WorldSpatialInitialization.cpp Source/Contents/World/WorldSpatialInitialization.cpp
Source/Contents/WorldRayQuery.cpp Source/Contents/World/WorldRayQuery.cpp
Source/Contents/WorldOcclusion.cpp Source/Contents/World/WorldOcclusion.cpp
Source/Contents/WorldModelView.cpp Source/Contents/World/WorldModelView.cpp
Source/Contents/WorldViewSpatial.cpp Source/Contents/World/WorldViewSpatial.cpp
Source/Contents/CameraSmoothing.cpp Source/Contents/Camera/CameraSmoothing.cpp
Source/Contents/CameraShake.cpp Source/Contents/Camera/CameraShake.cpp
Source/Contents/CameraFrame.cpp Source/Contents/Camera/CameraFrame.cpp
Source/Contents/CameraQueries.cpp Source/Contents/Camera/CameraQueries.cpp
Source/Contents/ModelSpatial.cpp Source/Contents/Models/ModelSpatial.cpp
Source/Contents/GroundSpatial.cpp Source/Contents/Ground/GroundSpatial.cpp
Source/Contents/GroundGeometry.cpp Source/Contents/Ground/GroundGeometry.cpp
Source/Contents/GroundLightmaps.cpp Source/Contents/Ground/GroundLightmaps.cpp
Source/Contents/GroundRendering.cpp Source/Contents/Ground/GroundRendering.cpp
Source/Contents/GroundPicking.cpp Source/Contents/Ground/GroundPicking.cpp
Source/Contents/GroundDecals.cpp Source/Contents/Ground/GroundDecals.cpp
Source/Contents/GroundWaterState.cpp Source/Contents/Ground/GroundWaterState.cpp
Source/Contents/GroundWaterRendering.cpp Source/Contents/Ground/GroundWaterRendering.cpp
Source/Contents/GroundLighting.cpp Source/Contents/Ground/GroundLighting.cpp
Source/Contents/GroundSamples.cpp Source/Contents/Ground/GroundSamples.cpp
Source/Contents/GroundLightingInitialization.cpp Source/Contents/Ground/GroundLightingInitialization.cpp
Source/Render/TextureCache.cpp Source/Render/Textures/TextureCache.cpp
Source/Resource/EZeffectResource.cpp Source/Resource/EZeffectResource.cpp
Source/Resource/ResourceMgr.cpp Source/Resource/ResourceMgr.cpp
Source/Resource/ResourceFailures.cpp Source/Resource/ResourceFailures.cpp
@ -379,9 +380,9 @@ if(WIN32)
Source/Resource/TextureNames.cpp Source/Resource/TextureNames.cpp
Source/Contents/SkinInfo.cpp Source/Contents/SkinInfo.cpp
Source/Resource/BinkRuntime.cpp Source/Resource/BinkRuntime.cpp
Source/Contents/BinkMovie.cpp Source/Contents/Movies/BinkMovie.cpp
Source/Contents/BinkMovies.cpp Source/Contents/Movies/BinkMovies.cpp
Source/Render/ClientTextureSources.cpp Source/Render/Textures/ClientTextureSources.cpp
) )
target_include_directories(RagnarokResource PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/Source") target_include_directories(RagnarokResource PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/Source")
target_compile_features(RagnarokResource PUBLIC cxx_std_20) target_compile_features(RagnarokResource PUBLIC cxx_std_20)
@ -390,15 +391,15 @@ if(WIN32)
set_target_properties(RagnarokResource PROPERTIES CXX_EXTENSIONS OFF) set_target_properties(RagnarokResource PROPERTIES CXX_EXTENSIONS OFF)
add_library(RagnarokNetwork STATIC add_library(RagnarokNetwork STATIC
Source/Network/CURLMgr.cpp Source/Network/Http/CURLMgr.cpp
Source/Network/CURLPostHander.cpp Source/Network/Http/CURLPostHander.cpp
Source/Network/TextEncoding.cpp Source/Network/TextEncoding.cpp
Source/Network/ConnectionBuffer.cpp Source/Network/Connection/ConnectionBuffer.cpp
Source/Network/Connection.cpp Source/Network/Connection/Connection.cpp
Source/Network/PacketLengths.cpp Source/Network/Packets/PacketLengths.cpp
Source/Network/ConnectionRuntime.cpp Source/Network/Connection/ConnectionRuntime.cpp
Source/Network/ShortcutPacket.cpp Source/Network/Packets/ShortcutPacket.cpp
Source/Network/PartyJoinPacket.cpp Source/Network/Packets/PartyJoinPacket.cpp
) )
target_include_directories(RagnarokNetwork PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/Source") target_include_directories(RagnarokNetwork PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/Source")
target_compile_features(RagnarokNetwork PUBLIC cxx_std_20) target_compile_features(RagnarokNetwork PUBLIC cxx_std_20)
@ -406,104 +407,104 @@ if(WIN32)
set_target_properties(RagnarokNetwork PROPERTIES CXX_EXTENSIONS OFF) set_target_properties(RagnarokNetwork PROPERTIES CXX_EXTENSIONS OFF)
add_library(RagnarokContents STATIC add_library(RagnarokContents STATIC
Source/Contents/WhisperBlockMgr.cpp Source/Contents/Messenger/WhisperBlockMgr.cpp
Source/Contents/CashEmotionMgr.cpp Source/Contents/CashEmotionMgr.cpp
Source/Contents/AccountUserData.cpp Source/Contents/UserData/AccountUserData.cpp
Source/Contents/EmotionHotkeys.cpp Source/Contents/Hotkeys/EmotionHotkeys.cpp
Source/Contents/UserHotkeyScript.cpp Source/Contents/Hotkeys/UserHotkeyScript.cpp
Source/Contents/UserHotkeyMgr.cpp Source/Contents/Hotkeys/UserHotkeyMgr.cpp
Source/Contents/AccountLinkedUserDataMgr.cpp Source/Contents/UserData/AccountLinkedUserDataMgr.cpp
Source/Contents/AccountService.cpp Source/Contents/UserData/AccountService.cpp
Source/Contents/ScriptOptions.cpp Source/Contents/Configuration/ScriptOptions.cpp
Source/Contents/GraphicsConfiguration.cpp Source/Contents/Configuration/GraphicsConfiguration.cpp
Source/Contents/SoundConfiguration.cpp Source/Contents/Sound/SoundConfiguration.cpp
Source/Contents/SoundMgr.cpp Source/Contents/Sound/SoundMgr.cpp
Source/Contents/OptionWriter.cpp Source/Contents/Configuration/OptionWriter.cpp
Source/Contents/MessageTable.cpp Source/Contents/MessageTable.cpp
Source/Contents/AccountLinkedUserDataSaveAsyncWork.cpp Source/Contents/UserData/AccountLinkedUserDataSaveAsyncWork.cpp
Source/Contents/AccountLinkedUserDataLoadAsyncWork.cpp Source/Contents/UserData/AccountLinkedUserDataLoadAsyncWork.cpp
Source/Contents/CharacterLinkedUserDataMgr.cpp Source/Contents/UserData/CharacterLinkedUserDataMgr.cpp
Source/Contents/CharacterLinkedUserDataSaveAsyncWork.cpp Source/Contents/UserData/CharacterLinkedUserDataSaveAsyncWork.cpp
Source/Contents/CharacterConfigSave.cpp Source/Contents/UserData/CharacterConfigSave.cpp
Source/Contents/SkillLevelConfig.cpp Source/Contents/Skills/SkillLevelConfig.cpp
Source/Contents/SkillLevelRegistry.cpp Source/Contents/Skills/SkillLevelRegistry.cpp
Source/Contents/ServiceOptions.cpp Source/Contents/Configuration/ServiceOptions.cpp
Source/Contents/NormalItemSlots.cpp Source/Contents/UserData/NormalItemSlots.cpp
Source/Contents/SkillInfo.cpp Source/Contents/Skills/SkillInfo.cpp
Source/Contents/PlayerSkillBundle.cpp Source/Contents/Skills/PlayerSkillBundle.cpp
Source/Contents/SkillTreeScript.cpp Source/Contents/Skills/SkillTreeScript.cpp
Source/Contents/ClientSkills.cpp Source/Contents/Skills/ClientSkills.cpp
Source/Network/SkillUpdatePacket.cpp Source/Network/Packets/SkillUpdatePacket.cpp
Source/Contents/ItemInfo.cpp Source/Contents/Items/ItemInfo.cpp
Source/Contents/ItemDescription.cpp Source/Contents/Items/ItemDescription.cpp
Source/Contents/ItemLink.cpp Source/Contents/Items/ItemLink.cpp
Source/Contents/TipBoxTitles.cpp Source/Contents/TipBoxTitles.cpp
Source/Contents/AutomaticSkills.cpp Source/Contents/Skills/AutomaticSkills.cpp
Source/Contents/AttackMotion.cpp Source/Contents/Actors/AttackMotion.cpp
Source/Client/ApplicationActivation.cpp Source/Client/Application/ApplicationActivation.cpp
Source/Client/ApplicationMessages.cpp Source/Client/Application/ApplicationMessages.cpp
Source/Client/BrowserInputHandoff.cpp Source/Client/Browser/BrowserInputHandoff.cpp
Source/Client/WebBrowser.cpp Source/Client/Browser/WebBrowser.cpp
Source/Client/BrowserRegistry.cpp Source/Client/Browser/BrowserRegistry.cpp
Source/Client/WebEventReceiver.cpp Source/Client/Browser/WebEventReceiver.cpp
Source/Client/BrowserMessages.cpp Source/Client/Browser/BrowserMessages.cpp
Source/Contents/HotkeyInfo.cpp Source/Contents/Hotkeys/HotkeyInfo.cpp
Source/Contents/PlayerNameDictionary.cpp Source/Contents/PlayerNameDictionary.cpp
Source/Contents/ItemNaming.cpp Source/Contents/Items/ItemNaming.cpp
Source/Contents/ItemResources.cpp Source/Contents/Items/ItemResources.cpp
Source/Contents/StoreBackupData.cpp Source/Contents/Store/StoreBackupData.cpp
Source/Contents/MCStoreInfoBackup.cpp Source/Contents/Store/MCStoreInfoBackup.cpp
Source/Contents/MCStoreInfoBackupAsyncWork.cpp Source/Contents/Store/MCStoreInfoBackupAsyncWork.cpp
Source/Contents/EmblemDownload.cpp Source/Contents/Emblems/EmblemDownload.cpp
Source/Contents/EmblemDataDownloadAsyncWork.cpp Source/Contents/Emblems/EmblemDataDownloadAsyncWork.cpp
Source/Contents/EmblemDataMgr.cpp Source/Contents/Emblems/EmblemDataMgr.cpp
Source/Contents/GuildEmblemReqBundle.cpp Source/Contents/Emblems/GuildEmblemReqBundle.cpp
Source/Contents/EmblemDataUploadAsyncWork.cpp Source/Contents/Emblems/EmblemDataUploadAsyncWork.cpp
Source/Contents/AdvenPartyInfo.cpp Source/Contents/Party/AdvenPartyInfo.cpp
Source/Contents/AdventurerAgencyMgr.cpp Source/Contents/Party/AdventurerAgencyMgr.cpp
Source/Contents/ChangePartyMasterAsyncWork.cpp Source/Contents/Party/ChangePartyMasterAsyncWork.cpp
Source/Contents/LoadPartyListAsyncWork.cpp Source/Contents/Party/LoadPartyListAsyncWork.cpp
Source/Contents/StopRecruitingAsyncWork.cpp Source/Contents/Party/StopRecruitingAsyncWork.cpp
Source/Contents/LoadMyPartyAsyncWork.cpp Source/Contents/Party/LoadMyPartyAsyncWork.cpp
Source/Contents/RegisterPartyAsyncWork.cpp Source/Contents/Party/RegisterPartyAsyncWork.cpp
Source/Contents/LoadPartyInfoAsyncWork.cpp Source/Contents/Party/LoadPartyInfoAsyncWork.cpp
Source/Contents/AsyncWorkRecycleObjectPoolMgr.cpp Source/Contents/Async/AsyncWorkRecycleObjectPoolMgr.cpp
Source/Contents/AsyncWorkMgr.cpp Source/Contents/Async/AsyncWorkMgr.cpp
Source/Contents/AgencyRequests.cpp Source/Contents/Party/AgencyRequests.cpp
Source/Contents/ForbiddenWords.cpp Source/Contents/ForbiddenWords.cpp
Source/Contents/JobNames.cpp Source/Contents/Jobs/JobNames.cpp
Source/Contents/SpriteDrawingPolicy.cpp Source/Contents/Actors/SpriteDrawingPolicy.cpp
Source/Contents/JobInheritance.cpp Source/Contents/Jobs/JobInheritance.cpp
Source/Contents/MessengerMember.cpp Source/Contents/Messenger/MessengerMember.cpp
Source/Contents/ClientMessenger.cpp Source/Contents/Messenger/ClientMessenger.cpp
Source/Contents/RenderObjectState.cpp Source/Contents/Actors/RenderObjectState.cpp
Source/Contents/GameObject.cpp Source/Contents/Actors/GameObject.cpp
Source/Contents/RenderObject.cpp Source/Contents/Actors/RenderObject.cpp
Source/Contents/WorldObjects.cpp Source/Contents/World/WorldObjects.cpp
Source/Contents/WorldActors.cpp Source/Contents/World/WorldActors.cpp
Source/Contents/EffectAttachments.cpp Source/Contents/Effects/EffectAttachments.cpp
Source/Contents/EffectTypes.cpp Source/Contents/Effects/EffectTypes.cpp
Source/Contents/RagEffectState.cpp Source/Contents/Effects/RagEffectState.cpp
Source/Contents/RagEffectInitialization.cpp Source/Contents/Effects/RagEffectInitialization.cpp
Source/Contents/RagEffectResourceSelection.cpp Source/Contents/Effects/RagEffectResourceSelection.cpp
Source/Contents/EffectPrimitiveState.cpp Source/Contents/Effects/EffectPrimitiveState.cpp
Source/Contents/EffectPrimitiveColor.cpp Source/Contents/Effects/EffectPrimitiveColor.cpp
Source/Contents/EffectPrimitiveMotion.cpp Source/Contents/Effects/EffectPrimitiveMotion.cpp
Source/Contents/EffectPrimitiveRendering.cpp Source/Contents/Effects/EffectPrimitiveRendering.cpp
Source/Contents/EffectPrimitiveRecords.cpp Source/Contents/Effects/EffectPrimitiveRecords.cpp
Source/Contents/EffectParticle.cpp Source/Contents/Effects/EffectParticle.cpp
Source/Contents/PartyEffect.cpp Source/Contents/Effects/PartyEffect.cpp
Source/Contents/EffectActor.cpp Source/Contents/Effects/EffectActor.cpp
Source/Contents/AttachedEffect.cpp Source/Contents/Effects/AttachedEffect.cpp
Source/Contents/PartyMemberEffects.cpp Source/Contents/Effects/PartyMemberEffects.cpp
Source/Contents/EffectVisibility.cpp Source/Contents/Effects/EffectVisibility.cpp
Source/Contents/TerrainHeight.cpp Source/Contents/Ground/TerrainHeight.cpp
Source/Contents/TerrainCoordinates.cpp Source/Contents/Ground/TerrainCoordinates.cpp
Source/Contents/TerrainPicking.cpp Source/Contents/Ground/TerrainPicking.cpp
Source/Contents/TerrainPlane.cpp Source/Contents/Ground/TerrainPlane.cpp
Source/Contents/WorldTerrainPicking.cpp Source/Contents/World/WorldTerrainPicking.cpp
Source/Contents/WorldProximity.cpp Source/Contents/World/WorldProximity.cpp
Source/Contents/JobNameInitialization.cpp Source/Contents/Jobs/JobNameInitialization.cpp
Source/Contents/ShowRankingScheduler.cpp Source/Contents/ShowRankingScheduler.cpp
) )
target_include_directories(RagnarokContents PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/Source") target_include_directories(RagnarokContents PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/Source")

View file

@ -80,7 +80,7 @@ static void BuildFullKeyName(const char* keyname, char* buffer) {
changes. Include the real prerequisite interfaces, implementations and affected callers, or land them first. changes. Include the real prerequisite interfaces, implementations and affected callers, or land them first.
- Preserve functions that already match. Fix compile errors and structural differences; byte identity is a - Preserve functions that already match. Fix compile errors and structural differences; byte identity is a
bonus, not permission to distort source or spend unbounded time tuning it. bonus, not permission to distort source or spend unbounded time tuning it.
- Do not add WoW `// VERIFY:` hashes or use `re/tools/verify.py`; neither is this project's verification workflow. - Do not add game `// VERIFY:` hashes or use `re/tools/verify.py`; neither is this project's verification workflow.
- Claims, submission scope and peer-review procedure follow `AGENTS.md` and current server guidance. - Claims, submission scope and peer-review procedure follow `AGENTS.md` and current server guidance.
## 2b. Cast chains mean the types are wrong ## 2b. Cast chains mean the types are wrong
@ -152,7 +152,7 @@ class CResourceCache {
- Reuse the appropriate header under `Source/` for shared enums and data types. - Reuse the appropriate header under `Source/` for shared enums and data types.
- Use an existing `Types.h` if the subsystem has one; do not create a repository-wide `Types.h` migration. - Use an existing `Types.h` if the subsystem has one; do not create a repository-wide `Types.h` migration.
- Keep dependencies small and avoid cycles. Do not introduce WoW-specific DB or event headers. - Keep dependencies small and avoid cycles. Do not introduce game-specific DB or event headers.
## 6. Diagnostic file and line arguments preserve original behavior ## 6. Diagnostic file and line arguments preserve original behavior
@ -166,6 +166,25 @@ the original instructions.
- This target is Windows x86. Keep code in its assigned existing translation unit and subsystem headers. - This target is Windows x86. Keep code in its assigned existing translation unit and subsystem headers.
- Use the original Win32, DirectDraw, Direct3D, Lua and codec ABIs where evidenced. Preserve calling - Use the original Win32, DirectDraw, Direct3D, Lua and codec ABIs where evidenced. Preserve calling
conventions, ownership, field widths and failure behavior. conventions, ownership, field widths and failure behavior.
- Do not introduce WoW architecture macros, parallel `Win32/` / `Win64/` trees or a new portability layer.
- Compile individual units with the configured Clank toolchain and `compile.json`, not a new build system. - Compile individual units with the configured Clank toolchain and `compile.json`, not a new build system.
- Missing original instructions are a blocker, not permission to invent a fallback or partial implementation. - Missing original instructions are a blocker, not permission to invent a fallback or partial implementation.
## 8. Reconstruct source, not instructions
- Prefer the original programmer's types and algorithms over instruction-shaped C++. Byte identity is a bonus,
never a reason for cryptic code. Use compiler intrinsics only with clear evidence that the original source
used them; an optimized SIMD instruction alone is not evidence. Scalar data is not `__m128i`.
- Recover records and members instead of dereferencing integer pointers cast onto byte buffers. At a real
binary-format boundary, use a typed record or `memcpy` for unaligned fields; preserve layout and aliasing.
- Apply namespace and API conventions consistently throughout a subsystem, including declarations and callers.
- Group cohesive implementations and their headers in subsystem folders, e.g. `Client/Browser/`. Group by
actual ownership and dependencies, not a loose keyword; update includes and build metadata with every move.
- Use SDK/library constants and identifiers directly: `WS_CHILD | WS_VISIBLE`, `CGID_DocHostCommandHandler`.
Do not duplicate documented definitions. Name game-specific choices with an enum, or `constexpr` when an enum
is unsuitable; do not turn serialized data, hashes or lookup tables into invented symbolic constants.
- Do not include `<mmsystem.h>`; include the specific Windows API headers actually required. Formatter-control
comments are forbidden too. Preserve include dependencies without `clang-format` suppression comments.
- Separate different logical phases with a blank line; keep related assignments together. Use a 160-column
limit, rather than wrapping short calls into instruction-like fragments. Formatting must not change behavior.
- Leave exactly one blank line after an include block before declarations, including in headers. Never keep
more than one consecutive blank line; whitespace-only lines count as blank.

View file

@ -3,5 +3,5 @@
This tree is coordinated with Clank Together. Functions and data carry stable symbol IDs This tree is coordinated with Clank Together. Functions and data carry stable symbol IDs
instead of addresses (`// FUNCTION: fn_01234`, `// GLOBAL: gl_00042`); the coordinator's symbol database holds instead of addresses (`// FUNCTION: fn_01234`, `// GLOBAL: gl_00042`); the coordinator's symbol database holds
their addresses in every client build, so a new client version needs new addresses, not a retagged tree. their addresses in every client build, so a new client version needs new addresses, not a retagged tree.
`decomp.json` and `config/compile.json` pin the VS compiler and the exact command line of every source. `decomp.json` and `compile.json` pin the VS compiler and the exact command line of every source.
See `AGENTS.md` for how to contribute. See `AGENTS.md` for how to contribute.

View file

@ -4,11 +4,9 @@
#include <algorithm> #include <algorithm>
#include <cstdlib> #include <cstdlib>
#include <cstring> #include <cstring>
#include <emmintrin.h>
#include <zlib.h> #include <zlib.h>
namespace { namespace {
std::uint32_t Read32(const std::uint8_t* bytes) { std::uint32_t Read32(const std::uint8_t* bytes) {
std::uint32_t value; std::uint32_t value;
std::memcpy(&value, bytes, sizeof(value)); std::memcpy(&value, bytes, sizeof(value));
@ -29,22 +27,18 @@ namespace {
// FUNCTION: fn_05116 // FUNCTION: fn_05116
std::uint64_t ReadAuxiliaryIdentifier(const void* data, std::size_t size) { std::uint64_t ReadAuxiliaryIdentifier(const void* data, std::size_t size) {
if (size != 0) { if (size != 0) {
const __m128i header = _mm_loadu_si128(static_cast<const __m128i*>(data)); const auto* header = static_cast<const std::uint8_t*>(data);
if (static_cast<std::uint8_t>(_mm_cvtsi128_si32(header)) <= 1) { if (header[0] <= 1) {
std::uint64_t identifier; return Read64(header + 4);
_mm_storel_epi64(reinterpret_cast<__m128i*>(&identifier), _mm_srli_si128(header, 4));
return identifier;
} }
} }
return UINT64_MAX; return UINT64_MAX;
} }
void SortEntries(std::vector<myGPack::PakEntry>& entries) { void SortEntries(std::vector<myGPack::PakEntry>& entries) {
std::sort(entries.begin(), entries.end(), [](const auto& first, const auto& second) { std::sort(entries.begin(), entries.end(), [](const auto& first, const auto& second) { return first.name < second.name; });
return first.name < second.name;
});
} }
} }
// OFFSET: 0x00E555A0 // OFFSET: 0x00E555A0
@ -80,6 +74,7 @@ bool CGPak::Open(CPakFile* file) {
if (!file) { if (!file) {
return false; return false;
} }
this->m_file = file; this->m_file = file;
const std::uint8_t* header = file->Read(0, 46); const std::uint8_t* header = file->Read(0, 46);
if (std::memcmp(header, "Event Horizon", 14) == 0) { if (std::memcmp(header, "Event Horizon", 14) == 0) {
@ -95,6 +90,7 @@ bool CGPak::Open(CPakFile* file) {
} }
bool loaded; bool loaded;
switch (this->m_version & 0xFF00) { switch (this->m_version & 0xFF00) {
case 0x100: case 0x100:
loaded = this->ReadTableV1(); loaded = this->ReadTableV1();
@ -109,9 +105,11 @@ bool CGPak::Open(CPakFile* file) {
loaded = false; loaded = false;
break; break;
} }
if (loaded) { if (loaded) {
return true; return true;
} }
this->Reset(); this->Reset();
return false; return false;
} }
@ -119,20 +117,22 @@ bool CGPak::Open(CPakFile* file) {
// OFFSET: 0x00AEC050 // OFFSET: 0x00AEC050
// FUNCTION: fn_18133 // FUNCTION: fn_18133
bool CGPak::ReadTableV1() { bool CGPak::ReadTableV1() {
this->m_tableSize = this->m_tableSize = static_cast<std::uint32_t>(this->m_file->GetSize()) - static_cast<std::uint32_t>(this->m_tableOffset);
static_cast<std::uint32_t>(this->m_file->GetSize()) - static_cast<std::uint32_t>(this->m_tableOffset);
const std::uint8_t* cursor = this->m_file->Read(this->m_tableOffset, this->m_tableSize); const std::uint8_t* cursor = this->m_file->Read(this->m_tableOffset, this->m_tableSize);
this->m_entries.resize(this->m_entryCount); this->m_entries.resize(this->m_entryCount);
std::uint32_t maximum = 0; std::uint32_t maximum = 0;
std::uint32_t sequence = 1; std::uint32_t sequence = 1;
std::int32_t legacyKey = 95001; std::int32_t legacyKey = 95001;
for (std::uint32_t i = 0; i < this->m_entryCount; ++i) { for (std::uint32_t i = 0; i < this->m_entryCount; ++i) {
std::uint32_t nameSize = Read32(cursor); std::uint32_t nameSize = Read32(cursor);
cursor += 4; cursor += 4;
char name[256]; char name[256];
if (this->m_version < 0x101) { if (this->m_version < 0x101) {
std::int32_t length = static_cast<std::int32_t>(std::strlen(reinterpret_cast<const char*>(cursor)) + 1); std::int32_t length = static_cast<std::int32_t>(std::strlen(reinterpret_cast<const char*>(cursor)) + 1);
for (std::int32_t j = 0; j < length; ++j) { for (std::int32_t j = 0; j < length; ++j) {
name[j] = static_cast<char>(SwapNibbles(cursor[j])); name[j] = static_cast<char>(SwapNibbles(cursor[j]));
} }
@ -140,18 +140,23 @@ bool CGPak::ReadTableV1() {
if (this->m_version > 0x103) { if (this->m_version > 0x103) {
return false; return false;
} }
cursor += 2; cursor += 2;
std::int32_t length = static_cast<std::int32_t>(nameSize - 6); std::int32_t length = static_cast<std::int32_t>(nameSize - 6);
for (std::int32_t j = 0; j < length; ++j) { for (std::int32_t j = 0; j < length; ++j) {
name[j] = static_cast<char>(SwapNibbles(cursor[j])); name[j] = static_cast<char>(SwapNibbles(cursor[j]));
} }
std::int32_t number = legacyKey; std::int32_t number = legacyKey;
if (this->m_version != 0x101) { if (this->m_version != 0x101) {
number = static_cast<std::int32_t>(3 * (31669u - (sequence >> 1))); number = static_cast<std::int32_t>(3 * (31669u - (sequence >> 1)));
} }
if (number < 10000) { if (number < 10000) {
number += 85000; number += 85000;
} }
char digits[20]; char digits[20];
_itoa(number, digits, 10); _itoa(number, digits, 10);
std::uint8_t key[8]; std::uint8_t key[8];
@ -178,13 +183,14 @@ bool CGPak::ReadTableV1() {
entry.name.Assign(name); entry.name.Assign(name);
const char* extension = std::strrchr(name, '.'); const char* extension = std::strrchr(name, '.');
bool special = extension && (_stricmp(extension, ".gnd") == 0 || _stricmp(extension, ".gat") == 0 || bool special = extension && (_stricmp(extension, ".gnd") == 0 || _stricmp(extension, ".gat") == 0 || _stricmp(extension, ".act") == 0 ||
_stricmp(extension, ".act") == 0 || _stricmp(extension, ".str") == 0); _stricmp(extension, ".str") == 0);
entry.flags |= special ? 4 : 2; entry.flags |= special ? 4 : 2;
maximum = std::max(maximum, entry.alignedSize); maximum = std::max(maximum, entry.alignedSize);
sequence += 5; sequence += 5;
legacyKey -= 2; legacyKey -= 2;
} }
SortEntries(this->m_entries); SortEntries(this->m_entries);
this->m_scratch.resize(maximum); this->m_scratch.resize(maximum);
return true; return true;
@ -200,6 +206,7 @@ bool CGPak::ReadTableV2() {
const std::uint8_t* source = this->m_file->Read(this->m_tableOffset + 8, compressed); const std::uint8_t* source = this->m_file->Read(this->m_tableOffset + 8, compressed);
uLongf length = original + 8; uLongf length = original + 8;
std::vector<std::uint8_t> decoded(length); std::vector<std::uint8_t> decoded(length);
if (uncompress(decoded.data(), &length, source, compressed) != Z_OK) { if (uncompress(decoded.data(), &length, source, compressed) != Z_OK) {
return false; return false;
} }
@ -207,6 +214,7 @@ bool CGPak::ReadTableV2() {
this->m_entries.resize(this->m_entryCount); this->m_entries.resize(this->m_entryCount);
const std::uint8_t* cursor = decoded.data(); const std::uint8_t* cursor = decoded.data();
std::uint32_t maximum = 0; std::uint32_t maximum = 0;
for (std::uint32_t i = 0; i < this->m_entryCount; ++i) { for (std::uint32_t i = 0; i < this->m_entryCount; ++i) {
const char* name = reinterpret_cast<const char*>(cursor); const char* name = reinterpret_cast<const char*>(cursor);
const std::uint8_t* metadata = cursor + std::strlen(name) + 1; const std::uint8_t* metadata = cursor + std::strlen(name) + 1;
@ -220,6 +228,7 @@ bool CGPak::ReadTableV2() {
entry.name.Assign(name); entry.name.Assign(name);
maximum = std::max(maximum, entry.alignedSize); maximum = std::max(maximum, entry.alignedSize);
} }
SortEntries(this->m_entries); SortEntries(this->m_entries);
this->m_scratch.resize(maximum); this->m_scratch.resize(maximum);
return true; return true;
@ -236,6 +245,7 @@ bool CGPak::ReadTableV3() {
const std::uint8_t* source = this->m_file->Read(this->m_tableOffset + 12, compressed); const std::uint8_t* source = this->m_file->Read(this->m_tableOffset + 12, compressed);
uLongf length = original + 8; uLongf length = original + 8;
std::vector<std::uint8_t> decoded(length); std::vector<std::uint8_t> decoded(length);
if (uncompress(decoded.data(), &length, source, compressed) != Z_OK) { if (uncompress(decoded.data(), &length, source, compressed) != Z_OK) {
return false; return false;
} }
@ -243,6 +253,7 @@ bool CGPak::ReadTableV3() {
this->m_entries.resize(this->m_entryCount); this->m_entries.resize(this->m_entryCount);
const std::uint8_t* cursor = decoded.data(); const std::uint8_t* cursor = decoded.data();
std::uint32_t maximum = 0; std::uint32_t maximum = 0;
for (std::uint32_t i = 0; i < this->m_entryCount; ++i) { for (std::uint32_t i = 0; i < this->m_entryCount; ++i) {
const char* name = reinterpret_cast<const char*>(cursor); const char* name = reinterpret_cast<const char*>(cursor);
const std::uint8_t* metadata = cursor + std::strlen(name) + 1; const std::uint8_t* metadata = cursor + std::strlen(name) + 1;
@ -252,12 +263,11 @@ bool CGPak::ReadTableV3() {
entry.alignedSize = Read32(metadata + 4); entry.alignedSize = Read32(metadata + 4);
entry.originalSize = Read32(metadata + 8); entry.originalSize = Read32(metadata + 8);
entry.flags = metadata[12]; entry.flags = metadata[12];
const std::uint32_t offsetLow = Read32(metadata + 13); entry.dataOffset = Read64(metadata + 13);
reinterpret_cast<std::uint32_t*>(&entry.dataOffset)[1] = Read32(metadata + 17);
reinterpret_cast<std::uint32_t*>(&entry.dataOffset)[0] = offsetLow;
entry.name.Assign(name); entry.name.Assign(name);
maximum = std::max(maximum, entry.alignedSize); maximum = std::max(maximum, entry.alignedSize);
} }
SortEntries(this->m_entries); SortEntries(this->m_entries);
this->m_scratch.resize(maximum); this->m_scratch.resize(maximum);
return true; return true;
@ -266,16 +276,17 @@ bool CGPak::ReadTableV3() {
// OFFSET: 0x00AEBA30 // OFFSET: 0x00AEBA30
// FUNCTION: fn_18128 // FUNCTION: fn_18128
bool CGPak::Find(const myGPack::PakName& name, myGPack::PakEntry* entry) const { bool CGPak::Find(const myGPack::PakName& name, myGPack::PakEntry* entry) const {
auto found = std::lower_bound( auto found = std::lower_bound(this->m_entries.begin(), this->m_entries.end(), name, [](const myGPack::PakEntry& candidate, const myGPack::PakName& key) {
this->m_entries.begin(), this->m_entries.end(), name, return candidate.name < key;
[](const myGPack::PakEntry& candidate, const myGPack::PakName& key) { return candidate.name < key; } });
);
if (found == this->m_entries.end() || found->name != name) { if (found == this->m_entries.end() || found->name != name) {
return false; return false;
} }
if (entry) { if (entry) {
std::memcpy(entry, &*found, sizeof(*entry)); std::memcpy(entry, &*found, sizeof(*entry));
} }
return true; return true;
} }
@ -290,6 +301,7 @@ void CGPak::ReadAuxiliary(std::vector<std::uint8_t>& output, std::uint64_t* iden
std::uint32_t size; std::uint32_t size;
std::uint64_t offset; std::uint64_t offset;
switch (this->m_version & 0xFF00) { switch (this->m_version & 0xFF00) {
case 0x200: case 0x200:
if (this->m_version < 0x201) { if (this->m_version < 0x201) {

View file

@ -1,14 +1,10 @@
#include "Archive/MemMapPakFile.h" #include "Archive/MemMapPakFile.h"
#include <bit>
// OFFSET: 0x00AE99D0 // OFFSET: 0x00AE99D0
// FUNCTION: fn_18111 // FUNCTION: fn_18111
// OFFSET: 0x00AECAA0 // OFFSET: 0x00AECAA0
// FUNCTION: fn_18140 // FUNCTION: fn_18140
CMemMapPakFile::CMemMapPakFile() CMemMapPakFile::CMemMapPakFile() : m_handle(nullptr), m_mapping(nullptr), m_viewSize(0), m_windowSize(0x20000000), m_size(0), m_viewOffset(0), m_view(nullptr) {
: m_handle(nullptr), m_mapping(nullptr), m_viewSize(0), m_windowSize(0x20000000), m_size(0), m_viewOffset(0),
m_view(nullptr) {
SYSTEM_INFO information; SYSTEM_INFO information;
GetSystemInfo(&information); GetSystemInfo(&information);
this->m_offsetMask = 0xFFFFFFFF00000000ULL | static_cast<std::uint32_t>(~(information.dwAllocationGranularity - 1)); this->m_offsetMask = 0xFFFFFFFF00000000ULL | static_cast<std::uint32_t>(~(information.dwAllocationGranularity - 1));
@ -30,8 +26,7 @@ std::uint64_t CMemMapPakFile::GetSize() const {
// OFFSET: 0x00AECB10 // OFFSET: 0x00AECB10
// FUNCTION: fn_18141 // FUNCTION: fn_18141
bool CMemMapPakFile::Open(const char* filename) { bool CMemMapPakFile::Open(const char* filename) {
this->m_handle = this->m_handle = CreateFileA(filename, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
CreateFileA(filename, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
if (this->m_handle == INVALID_HANDLE_VALUE) { if (this->m_handle == INVALID_HANDLE_VALUE) {
this->m_handle = nullptr; this->m_handle = nullptr;
return false; return false;
@ -39,13 +34,15 @@ bool CMemMapPakFile::Open(const char* filename) {
LARGE_INTEGER size; LARGE_INTEGER size;
GetFileSizeEx(this->m_handle, &size); GetFileSizeEx(this->m_handle, &size);
this->m_size = std::bit_cast<std::uint64_t>(size.QuadPart); this->m_size = static_cast<std::uint64_t>(size.QuadPart);
this->m_viewOffset = 0; this->m_viewOffset = 0;
this->m_viewSize = 0; this->m_viewSize = 0;
this->m_mapping = CreateFileMappingA(this->m_handle, nullptr, PAGE_READONLY, 0, 0, nullptr); this->m_mapping = CreateFileMappingA(this->m_handle, nullptr, PAGE_READONLY, 0, 0, nullptr);
if (this->m_mapping) { if (this->m_mapping) {
return true; return true;
} }
this->m_windowSize = 0; this->m_windowSize = 0;
return false; return false;
} }
@ -57,10 +54,12 @@ void CMemMapPakFile::Close() {
UnmapViewOfFile(this->m_view); UnmapViewOfFile(this->m_view);
this->m_view = nullptr; this->m_view = nullptr;
} }
if (this->m_mapping) { if (this->m_mapping) {
CloseHandle(this->m_mapping); CloseHandle(this->m_mapping);
this->m_mapping = nullptr; this->m_mapping = nullptr;
} }
if (this->m_handle) { if (this->m_handle) {
CloseHandle(this->m_handle); CloseHandle(this->m_handle);
this->m_handle = nullptr; this->m_handle = nullptr;
@ -74,27 +73,29 @@ std::uint8_t* CMemMapPakFile::Read(std::uint64_t offset, std::uint32_t length) {
if (offset >= this->m_viewOffset && offset + length <= this->m_viewOffset + this->m_viewSize) { if (offset >= this->m_viewOffset && offset + length <= this->m_viewOffset + this->m_viewSize) {
return this->m_view + static_cast<std::uint32_t>(offset - this->m_viewOffset); return this->m_view + static_cast<std::uint32_t>(offset - this->m_viewOffset);
} }
if (this->m_view) { if (this->m_view) {
UnmapViewOfFile(this->m_view); UnmapViewOfFile(this->m_view);
this->m_view = nullptr; this->m_view = nullptr;
} }
while (this->m_windowSize >= while (this->m_windowSize >= length + (static_cast<std::uint32_t>(offset) & ~static_cast<std::uint32_t>(this->m_offsetMask))) {
length + (static_cast<std::uint32_t>(offset) & ~static_cast<std::uint32_t>(this->m_offsetMask))) {
this->m_viewOffset = offset & this->m_offsetMask; this->m_viewOffset = offset & this->m_offsetMask;
this->m_viewSize = this->m_windowSize; this->m_viewSize = this->m_windowSize;
SIZE_T bytes = this->m_windowSize; SIZE_T bytes = this->m_windowSize;
if (this->m_viewOffset + this->m_windowSize > this->m_size) { if (this->m_viewOffset + this->m_windowSize > this->m_size) {
bytes = 0; bytes = 0;
this->m_viewSize = static_cast<std::uint32_t>(this->m_size - this->m_viewOffset); this->m_viewSize = static_cast<std::uint32_t>(this->m_size - this->m_viewOffset);
} }
this->m_view = static_cast<std::uint8_t*>(MapViewOfFile(
this->m_mapping, FILE_MAP_READ, static_cast<DWORD>(this->m_viewOffset >> 32), this->m_view = static_cast<std::uint8_t*>(
static_cast<DWORD>(this->m_viewOffset), bytes MapViewOfFile(this->m_mapping, FILE_MAP_READ, static_cast<DWORD>(this->m_viewOffset >> 32), static_cast<DWORD>(this->m_viewOffset), bytes)
)); );
if (this->m_view) { if (this->m_view) {
return this->m_view + static_cast<std::uint32_t>(offset - this->m_viewOffset); return this->m_view + static_cast<std::uint32_t>(offset - this->m_viewOffset);
} }
this->m_windowSize >>= 1; this->m_windowSize >>= 1;
if (!this->m_windowSize) { if (!this->m_windowSize) {
break; break;
@ -113,14 +114,16 @@ std::uint8_t* CMemMapPakFile::Read(std::uint64_t offset, std::uint32_t length) {
this->m_buffer.resize(length); this->m_buffer.resize(length);
this->m_view = this->m_buffer.data(); this->m_view = this->m_buffer.data();
} }
LARGE_INTEGER position; LARGE_INTEGER position;
position.QuadPart = std::bit_cast<std::int64_t>(offset); position.QuadPart = static_cast<std::int64_t>(offset);
SetFilePointerEx(this->m_handle, position, nullptr, FILE_BEGIN); SetFilePointerEx(this->m_handle, position, nullptr, FILE_BEGIN);
DWORD read = 0; DWORD read = 0;
if (ReadFile(this->m_handle, this->m_buffer.data(), length, &read, nullptr)) { if (ReadFile(this->m_handle, this->m_buffer.data(), length, &read, nullptr)) {
// The original accepts a successful short read; it does not compare read.
this->m_view = this->m_buffer.empty() ? nullptr : this->m_buffer.data(); this->m_view = this->m_buffer.empty() ? nullptr : this->m_buffer.data();
return this->m_view; return this->m_view;
} }
return nullptr; return nullptr;
} }

View file

@ -3,7 +3,6 @@
#include "Archive/PakFile.h" #include "Archive/PakFile.h"
// Class name established by original RTTI; field/method names reconstructed.
class CMemMapPakFile : public CPakFile { class CMemMapPakFile : public CPakFile {
public: public:
CMemMapPakFile(); CMemMapPakFile();

View file

@ -5,21 +5,18 @@
namespace myGPack { namespace myGPack {
namespace { namespace {
// STATIC: 0x01159EC8 // STATIC: 0x01159EC8
// GLOBAL: gl_13428 // GLOBAL: gl_13428
constexpr std::uint8_t s_initialPermutation[64] = { constexpr std::uint8_t s_initialPermutation[64] = {
58, 50, 42, 34, 26, 18, 10, 2, 60, 52, 44, 36, 28, 20, 12, 4, 62, 54, 46, 38, 30, 22, 58, 50, 42, 34, 26, 18, 10, 2, 60, 52, 44, 36, 28, 20, 12, 4, 62, 54, 46, 38, 30, 22, 14, 6, 64, 56, 48, 40, 32, 24, 16, 8,
14, 6, 64, 56, 48, 40, 32, 24, 16, 8, 57, 49, 41, 33, 25, 17, 9, 1, 59, 51, 43, 35, 57, 49, 41, 33, 25, 17, 9, 1, 59, 51, 43, 35, 27, 19, 11, 3, 61, 53, 45, 37, 29, 21, 13, 5, 63, 55, 47, 39, 31, 23, 15, 7,
27, 19, 11, 3, 61, 53, 45, 37, 29, 21, 13, 5, 63, 55, 47, 39, 31, 23, 15, 7,
}; };
// STATIC: 0x01159F08 // STATIC: 0x01159F08
// GLOBAL: gl_13429 // GLOBAL: gl_13429
constexpr std::uint8_t s_finalPermutation[64] = { constexpr std::uint8_t s_finalPermutation[64] = {
40, 8, 48, 16, 56, 24, 64, 32, 39, 7, 47, 15, 55, 23, 63, 31, 38, 6, 46, 14, 54, 22, 40, 8, 48, 16, 56, 24, 64, 32, 39, 7, 47, 15, 55, 23, 63, 31, 38, 6, 46, 14, 54, 22, 62, 30, 37, 5, 45, 13, 53, 21, 61, 29,
62, 30, 37, 5, 45, 13, 53, 21, 61, 29, 36, 4, 44, 12, 52, 20, 60, 28, 35, 3, 43, 11, 36, 4, 44, 12, 52, 20, 60, 28, 35, 3, 43, 11, 51, 19, 59, 27, 34, 2, 42, 10, 50, 18, 58, 26, 33, 1, 41, 9, 49, 17, 57, 25,
51, 19, 59, 27, 34, 2, 42, 10, 50, 18, 58, 26, 33, 1, 41, 9, 49, 17, 57, 25,
}; };
// STATIC: 0x01159FC0 // STATIC: 0x01159FC0
@ -32,42 +29,38 @@ namespace myGPack {
// STATIC: 0x01159FF0 // STATIC: 0x01159FF0
// GLOBAL: gl_13431 // GLOBAL: gl_13431
constexpr std::uint8_t s_roundPermutation[32] = { constexpr std::uint8_t s_roundPermutation[32] = {
16, 7, 20, 21, 29, 12, 28, 17, 1, 15, 23, 26, 5, 18, 31, 10, 16, 7, 20, 21, 29, 12, 28, 17, 1, 15, 23, 26, 5, 18, 31, 10, 2, 8, 24, 14, 32, 27, 3, 9, 19, 13, 30, 6, 22, 11, 4, 25,
2, 8, 24, 14, 32, 27, 3, 9, 19, 13, 30, 6, 22, 11, 4, 25,
}; };
// STATIC: 0x0115A010 // STATIC: 0x0115A010
// GLOBAL: gl_13432 // GLOBAL: gl_13432
constexpr std::uint8_t s_substitution[512] = { constexpr std::uint8_t s_substitution[512] = {
14, 0, 4, 15, 13, 7, 1, 4, 2, 14, 15, 2, 11, 13, 8, 1, 3, 10, 10, 6, 6, 12, 12, 11, 5, 9, 9, 14, 0, 4, 15, 13, 7, 1, 4, 2, 14, 15, 2, 11, 13, 8, 1, 3, 10, 10, 6, 6, 12, 12, 11, 5, 9, 9, 5, 0, 3, 7, 8, 4, 15, 1, 12, 14,
5, 0, 3, 7, 8, 4, 15, 1, 12, 14, 8, 8, 2, 13, 4, 6, 9, 2, 1, 11, 7, 15, 5, 12, 11, 9, 3, 8, 8, 2, 13, 4, 6, 9, 2, 1, 11, 7, 15, 5, 12, 11, 9, 3, 7, 14, 3, 10, 10, 0, 5, 6, 0, 13, 15, 3, 1, 13, 8, 4, 14, 7, 6, 15,
7, 14, 3, 10, 10, 0, 5, 6, 0, 13, 15, 3, 1, 13, 8, 4, 14, 7, 6, 15, 11, 2, 3, 8, 4, 14, 9, 11, 2, 3, 8, 4, 14, 9, 12, 7, 0, 2, 1, 13, 10, 12, 6, 0, 9, 5, 11, 10, 5, 0, 13, 14, 8, 7, 10, 11, 1, 10, 3, 4, 15, 13, 4, 1,
12, 7, 0, 2, 1, 13, 10, 12, 6, 0, 9, 5, 11, 10, 5, 0, 13, 14, 8, 7, 10, 11, 1, 10, 3, 4, 15, 2, 5, 11, 8, 6, 12, 7, 6, 12, 9, 0, 3, 5, 2, 14, 15, 9, 10, 13, 0, 7, 9, 0, 14, 9, 6, 3, 3, 4, 15, 6, 5, 10, 1, 2, 13, 8,
13, 4, 1, 2, 5, 11, 8, 6, 12, 7, 6, 12, 9, 0, 3, 5, 2, 14, 15, 9, 10, 13, 0, 7, 9, 0, 14, 12, 5, 7, 14, 11, 12, 4, 11, 2, 15, 8, 1, 13, 1, 6, 10, 4, 13, 9, 0, 8, 6, 15, 9, 3, 8, 0, 7, 11, 4, 1, 15, 2, 14, 12, 3, 5,
9, 6, 3, 3, 4, 15, 6, 5, 10, 1, 2, 13, 8, 12, 5, 7, 14, 11, 12, 4, 11, 2, 15, 8, 1, 13, 1, 11, 10, 5, 14, 2, 7, 12, 7, 13, 13, 8, 14, 11, 3, 5, 0, 6, 6, 15, 9, 0, 10, 3, 1, 4, 2, 7, 8, 2, 5, 12, 11, 1, 12, 10, 4, 14,
6, 10, 4, 13, 9, 0, 8, 6, 15, 9, 3, 8, 0, 7, 11, 4, 1, 15, 2, 14, 12, 3, 5, 11, 10, 5, 14, 15, 9, 10, 3, 6, 15, 9, 0, 0, 6, 12, 10, 11, 1, 7, 13, 13, 8, 15, 9, 1, 4, 3, 5, 14, 11, 5, 12, 2, 7, 8, 2, 4, 14, 2, 14, 12,
2, 7, 12, 7, 13, 13, 8, 14, 11, 3, 5, 0, 6, 6, 15, 9, 0, 10, 3, 1, 4, 2, 7, 8, 2, 5, 12, 11, 4, 2, 1, 12, 7, 4, 10, 7, 11, 13, 6, 1, 8, 5, 5, 0, 3, 15, 15, 10, 13, 3, 0, 9, 14, 8, 9, 6, 4, 11, 2, 8, 1, 12, 11, 7,
11, 1, 12, 10, 4, 14, 15, 9, 10, 3, 6, 15, 9, 0, 0, 6, 12, 10, 11, 1, 7, 13, 13, 8, 15, 9, 1, 10, 1, 13, 14, 7, 2, 8, 13, 15, 6, 9, 15, 12, 0, 5, 9, 6, 10, 3, 4, 0, 5, 14, 3, 12, 10, 1, 15, 10, 4, 15, 2, 9, 7, 2, 12, 6,
4, 3, 5, 14, 11, 5, 12, 2, 7, 8, 2, 4, 14, 2, 14, 12, 11, 4, 2, 1, 12, 7, 4, 10, 7, 11, 13, 9, 8, 5, 0, 6, 13, 1, 3, 13, 4, 14, 14, 0, 7, 11, 5, 3, 11, 8, 9, 4, 14, 3, 15, 2, 5, 12, 2, 9, 8, 5, 12, 15, 3, 10, 7, 11,
6, 1, 8, 5, 5, 0, 3, 15, 15, 10, 13, 3, 0, 9, 14, 8, 9, 6, 4, 11, 2, 8, 1, 12, 11, 7, 10, 0, 14, 4, 1, 10, 7, 1, 6, 13, 0, 11, 8, 6, 13, 4, 13, 11, 0, 2, 11, 14, 7, 15, 4, 0, 9, 8, 1, 13, 10, 3, 14, 12, 3, 9, 5, 7,
1, 13, 14, 7, 2, 8, 13, 15, 6, 9, 15, 12, 0, 5, 9, 6, 10, 3, 4, 0, 5, 14, 3, 12, 10, 1, 15, 12, 5, 2, 10, 15, 6, 8, 1, 6, 1, 6, 4, 11, 11, 13, 13, 8, 12, 1, 3, 4, 7, 10, 14, 7, 10, 9, 15, 5, 6, 0, 8, 15, 0, 14, 5, 2,
10, 4, 15, 2, 9, 7, 2, 12, 6, 9, 8, 5, 0, 6, 13, 1, 3, 13, 4, 14, 14, 0, 7, 11, 5, 3, 11, 9, 3, 2, 12, 13, 1, 2, 15, 8, 13, 4, 8, 6, 10, 15, 3, 11, 7, 1, 4, 10, 12, 9, 5, 3, 6, 14, 11, 5, 0, 0, 14, 12, 9, 7, 2, 7,
8, 9, 4, 14, 3, 15, 2, 5, 12, 2, 9, 8, 5, 12, 15, 3, 10, 7, 11, 0, 14, 4, 1, 10, 7, 1, 6, 2, 11, 1, 4, 14, 1, 7, 9, 4, 12, 10, 14, 8, 2, 13, 0, 15, 6, 12, 10, 9, 13, 0, 15, 3, 3, 5, 5, 6, 8, 11,
13, 0, 11, 8, 6, 13, 4, 13, 11, 0, 2, 11, 14, 7, 15, 4, 0, 9, 8, 1, 13, 10, 3, 14, 12, 3, 9,
5, 7, 12, 5, 2, 10, 15, 6, 8, 1, 6, 1, 6, 4, 11, 11, 13, 13, 8, 12, 1, 3, 4, 7, 10, 14, 7,
10, 9, 15, 5, 6, 0, 8, 15, 0, 14, 5, 2, 9, 3, 2, 12, 13, 1, 2, 15, 8, 13, 4, 8, 6, 10, 15,
3, 11, 7, 1, 4, 10, 12, 9, 5, 3, 6, 14, 11, 5, 0, 0, 14, 12, 9, 7, 2, 7, 2, 11, 1, 4, 14,
1, 7, 9, 4, 12, 10, 14, 8, 2, 13, 0, 15, 6, 12, 10, 9, 13, 0, 15, 3, 3, 5, 5, 6, 8, 11,
}; };
void Permute(std::uint8_t* block, const std::uint8_t* table, unsigned count) { void Permute(std::uint8_t* block, const std::uint8_t* table, unsigned count) {
std::uint8_t result[8] = {}; std::uint8_t result[8] = {};
for (unsigned i = 0; i < count; ++i) { for (unsigned i = 0; i < count; ++i) {
unsigned bit = table[i] - 1; unsigned bit = table[i] - 1;
if (block[bit >> 3] & (0x80u >> (bit & 7))) { if (block[bit >> 3] & (0x80u >> (bit & 7))) {
result[i >> 3] |= static_cast<std::uint8_t>(0x80u >> (i & 7)); result[i >> 3] |= static_cast<std::uint8_t>(0x80u >> (i & 7));
} }
} }
std::memcpy(block, result, sizeof(result)); std::memcpy(block, result, sizeof(result));
} }
@ -105,7 +98,6 @@ namespace myGPack {
return value; return value;
} }
} }
} }
// OFFSET: 0x00AE8480 // OFFSET: 0x00AE8480
@ -118,9 +110,6 @@ namespace myGPack {
// OFFSET: 0x00AE92F0 // OFFSET: 0x00AE92F0
// FUNCTION: fn_18105 // FUNCTION: fn_18105
PakCipher::PakCipher(const std::uint8_t* key) { PakCipher::PakCipher(const std::uint8_t* key) {
// The original key permutation ANDs into zeroed state. Every resulting
// round key is zero, independent of the eight key bytes it reads.
// This is intentional original behavior, not standard DES key expansion.
(void)key; (void)key;
std::memset(this->m_roundKeys, 0, sizeof(this->m_roundKeys)); std::memset(this->m_roundKeys, 0, sizeof(this->m_roundKeys));
} }
@ -133,22 +122,30 @@ namespace myGPack {
// OFFSET: 0x00AE8E70 // OFFSET: 0x00AE8E70
// FUNCTION: fn_18101 // FUNCTION: fn_18101
int PakCipher::DecodeBlock(int rounds, const std::uint8_t* input, std::uint8_t* output) const { int PakCipher::DecodeBlock(int rounds, const std::uint8_t* input, std::uint8_t* output) const {
std::memcpy(output, input, 8); std::uint32_t inputLow;
std::uint32_t inputHigh;
std::memcpy(&inputLow, input, sizeof(inputLow));
std::memcpy(&inputHigh, input + 4, sizeof(inputHigh));
std::memcpy(output, &inputLow, sizeof(inputLow));
std::memcpy(output + 4, &inputHigh, sizeof(inputHigh));
Permute(output, s_initialPermutation, 64); Permute(output, s_initialPermutation, 64);
for (int round = 0; round < rounds; ++round) { for (int round = 0; round < rounds; ++round) {
std::uint8_t expanded[8] = {}; std::uint8_t expanded[8] = {};
for (unsigned i = 0; i < 48; ++i) { for (unsigned i = 0; i < 48; ++i) {
unsigned bit = s_expansion[i] + 31; unsigned bit = s_expansion[i] + 31;
if (output[bit >> 3] & (0x80u >> (bit & 7))) { if (output[bit >> 3] & (0x80u >> (bit & 7))) {
expanded[i / 6] |= static_cast<std::uint8_t>(0x80u >> (i % 6)); expanded[i / 6] |= static_cast<std::uint8_t>(0x80u >> (i % 6));
} }
} }
for (unsigned i = 0; i < 8; ++i) { for (unsigned i = 0; i < 8; ++i) {
expanded[i] ^= this->m_roundKeys[15 - round][i]; expanded[i] ^= this->m_roundKeys[15 - round][i];
} }
std::uint8_t substituted[4] = {}; std::uint8_t substituted[4] = {};
for (unsigned i = 0; i < 8; ++i) { for (unsigned i = 0; i < 8; ++i) {
std::uint8_t value = s_substitution[64 * i + (expanded[i] >> 2)]; std::uint8_t value = s_substitution[64 * i + (expanded[i] >> 2)];
if (i & 1) { if (i & 1) {
@ -159,15 +156,18 @@ namespace myGPack {
} }
std::uint8_t transformed[4] = {}; std::uint8_t transformed[4] = {};
for (unsigned i = 0; i < 32; ++i) { for (unsigned i = 0; i < 32; ++i) {
unsigned bit = s_roundPermutation[i] - 1; unsigned bit = s_roundPermutation[i] - 1;
if (substituted[bit >> 3] & (0x80u >> (bit & 7))) { if (substituted[bit >> 3] & (0x80u >> (bit & 7))) {
transformed[i >> 3] |= static_cast<std::uint8_t>(0x80u >> (i & 7)); transformed[i >> 3] |= static_cast<std::uint8_t>(0x80u >> (i & 7));
} }
} }
std::uint8_t previous[4]; std::uint8_t previous[4];
std::memcpy(previous, output, 4); std::memcpy(previous, output, 4);
std::memcpy(output, output + 4, 4); std::memcpy(output, output + 4, 4);
for (unsigned i = 0; i < 4; ++i) { for (unsigned i = 0; i < 4; ++i) {
output[i + 4] = transformed[i] ^ previous[i]; output[i + 4] = transformed[i] ^ previous[i];
} }
@ -176,22 +176,18 @@ namespace myGPack {
for (unsigned i = 0; i < 4; ++i) { for (unsigned i = 0; i < 4; ++i) {
std::swap(output[i], output[i + 4]); std::swap(output[i], output[i + 4]);
} }
Permute(output, s_finalPermutation, 64); Permute(output, s_finalPermutation, 64);
return 1; return 1;
} }
// OFFSET: 0x00AE84A0 // OFFSET: 0x00AE84A0
// FUNCTION: fn_18099 // FUNCTION: fn_18099
int PakCipher::Decode( int PakCipher::Decode(const std::uint8_t* input, int length, std::uint8_t* output, int* outputLength, int cycle) const {
const std::uint8_t* input,
int length,
std::uint8_t* output,
int* outputLength,
int cycle
) const {
if (!input || !output || !outputLength) { if (!input || !output || !outputLength) {
return 0; return 0;
} }
*outputLength = length; *outputLength = length;
if (cycle < 3) { if (cycle < 3) {
cycle = 1; cycle = 1;
@ -206,10 +202,10 @@ namespace myGPack {
int shuffled = 0; int shuffled = 0;
int block = 0; int block = 0;
int offset = 0; int offset = 0;
do { do {
if (block >= 20 && block % cycle != 0) { if (block >= 20 && block % cycle != 0) {
if (shuffled == 7) { if (shuffled == 7) {
// Preserve the original store/load order, including in-place use.
std::uint16_t pair; std::uint16_t pair;
std::memcpy(&pair, input + offset + 3, 2); std::memcpy(&pair, input + offset + 3, 2);
std::memcpy(output + offset, &pair, 2); std::memcpy(output + offset, &pair, 2);
@ -221,11 +217,12 @@ namespace myGPack {
output[offset + 7] = SubstituteLastByte(input[offset + 7]); output[offset + 7] = SubstituteLastByte(input[offset + 7]);
shuffled = 1; shuffled = 1;
} else { } else {
std::uint32_t word; std::uint32_t low;
std::memcpy(&word, input + offset, 4); std::uint32_t high;
std::memcpy(output + offset, &word, 4); std::memcpy(&low, input + offset, sizeof(low));
std::memcpy(&word, input + offset + 4, 4); std::memcpy(&high, input + offset + 4, sizeof(high));
std::memcpy(output + offset + 4, &word, 4); std::memcpy(output + offset, &low, sizeof(low));
std::memcpy(output + offset + 4, &high, sizeof(high));
++shuffled; ++shuffled;
} }
} else { } else {
@ -235,9 +232,11 @@ namespace myGPack {
this->DecodeBlock(1, blockInput, blockOutput); this->DecodeBlock(1, blockInput, blockOutput);
std::memcpy(output + offset, blockOutput, 8); std::memcpy(output + offset, blockOutput, 8);
} }
offset += 8; offset += 8;
++block; ++block;
} while (offset < length); } while (offset < length);
return 1; return 1;
} }
@ -248,6 +247,7 @@ namespace myGPack {
std::memcpy(output, input, static_cast<std::uint32_t>(length)); std::memcpy(output, input, static_cast<std::uint32_t>(length));
int offset = 0; int offset = 0;
int block = 0; int block = 0;
do { do {
std::uint8_t blockInput[8]; std::uint8_t blockInput[8];
std::uint8_t decoded[8]; std::uint8_t decoded[8];
@ -257,6 +257,7 @@ namespace myGPack {
offset += 8; offset += 8;
++block; ++block;
} while (offset < length && block < 20); } while (offset < length && block < 20);
return 1; return 1;
} }
@ -264,49 +265,56 @@ namespace myGPack {
// FUNCTION: fn_18104 // FUNCTION: fn_18104
int __stdcall InitialPermutation(std::uint8_t* block) { int __stdcall InitialPermutation(std::uint8_t* block) {
std::uint8_t result[8] = {}; std::uint8_t result[8] = {};
for (unsigned i = 0; i < 64; ++i) { for (unsigned i = 0; i < 64; ++i) {
unsigned bit = s_initialPermutation[i] - 1; unsigned bit = s_initialPermutation[i] - 1;
if (block[bit >> 3] & (0x80u >> (bit & 7))) { if (block[bit >> 3] & (0x80u >> (bit & 7))) {
result[i >> 3] |= static_cast<std::uint8_t>(0x80u >> (i & 7)); result[i >> 3] |= static_cast<std::uint8_t>(0x80u >> (i & 7));
} }
} }
std::memcpy(block, result, 8); std::memcpy(block, result, 8);
return *reinterpret_cast<const int*>(result + 4); int high;
std::memcpy(&high, result + 4, sizeof(high));
return high;
} }
// OFFSET: 0x00AE91D0 // OFFSET: 0x00AE91D0
// FUNCTION: fn_18103 // FUNCTION: fn_18103
int __stdcall FinalPermutation(std::uint8_t* block) { int __stdcall FinalPermutation(std::uint8_t* block) {
std::uint8_t result[8] = {}; std::uint8_t result[8] = {};
for (unsigned i = 0; i < 64; ++i) { for (unsigned i = 0; i < 64; ++i) {
unsigned bit = s_finalPermutation[i] - 1; unsigned bit = s_finalPermutation[i] - 1;
if (block[bit >> 3] & (0x80u >> (bit & 7))) { if (block[bit >> 3] & (0x80u >> (bit & 7))) {
result[i >> 3] |= static_cast<std::uint8_t>(0x80u >> (i & 7)); result[i >> 3] |= static_cast<std::uint8_t>(0x80u >> (i & 7));
} }
} }
std::memcpy(block, result, 8); std::memcpy(block, result, 8);
return *reinterpret_cast<const int*>(result + 4); int high;
std::memcpy(&high, result + 4, sizeof(high));
return high;
} }
// OFFSET: 0x00AE9020 // OFFSET: 0x00AE9020
// FUNCTION: fn_18102 // FUNCTION: fn_18102
int __stdcall EncodeBlock( int __stdcall EncodeBlock(int rounds, const std::uint8_t (*roundKeys)[8], std::uint32_t inputLow, std::uint32_t inputHigh, std::uint8_t* output) {
int rounds,
const std::uint8_t (*roundKeys)[8],
std::uint32_t inputLow,
std::uint32_t inputHigh,
std::uint8_t* output
) {
std::memcpy(output, &inputLow, 4); std::memcpy(output, &inputLow, 4);
std::memcpy(output + 4, &inputHigh, 4); std::memcpy(output + 4, &inputHigh, 4);
InitialPermutation(output); InitialPermutation(output);
if (rounds > 0) { if (rounds > 0) {
std::uint32_t left = *reinterpret_cast<const std::uint32_t*>(output); std::uint32_t left;
std::uint32_t right = *reinterpret_cast<const std::uint32_t*>(output + 4); std::uint32_t right;
std::memcpy(&left, output, sizeof(left));
std::memcpy(&right, output + 4, sizeof(right));
for (int round = 0; round < rounds; ++round) { for (int round = 0; round < rounds; ++round) {
std::uint8_t expanded[8] = {}; std::uint8_t expanded[8] = {};
for (unsigned i = 0; i < 48; ++i) { for (unsigned i = 0; i < 48; ++i) {
unsigned bit = s_expansion[i] + 31; unsigned bit = s_expansion[i] + 31;
if (output[bit >> 3] & (0x80u >> (bit & 7))) { if (output[bit >> 3] & (0x80u >> (bit & 7))) {
@ -319,6 +327,7 @@ namespace myGPack {
} }
std::uint8_t substituted[4] = {}; std::uint8_t substituted[4] = {};
for (unsigned i = 0; i < 8; ++i) { for (unsigned i = 0; i < 8; ++i) {
std::uint8_t value = s_substitution[64 * i + (expanded[i] >> 2)]; std::uint8_t value = s_substitution[64 * i + (expanded[i] >> 2)];
if (i & 1) { if (i & 1) {
@ -329,6 +338,7 @@ namespace myGPack {
} }
std::uint8_t transformed[4] = {}; std::uint8_t transformed[4] = {};
for (unsigned i = 0; i < 32; ++i) { for (unsigned i = 0; i < 32; ++i) {
unsigned bit = s_roundPermutation[i] - 1; unsigned bit = s_roundPermutation[i] - 1;
if (substituted[bit >> 3] & (0x80u >> (bit & 7))) { if (substituted[bit >> 3] & (0x80u >> (bit & 7))) {
@ -336,19 +346,24 @@ namespace myGPack {
} }
} }
std::uint32_t newRight = left ^ *reinterpret_cast<const std::uint32_t*>(transformed); std::uint32_t transformedWord;
std::memcpy(&transformedWord, transformed, sizeof(transformedWord));
std::uint32_t newRight = left ^ transformedWord;
left = right; left = right;
right = newRight; right = newRight;
*reinterpret_cast<std::uint32_t*>(output) = left; std::memcpy(output, &left, sizeof(left));
*reinterpret_cast<std::uint32_t*>(output + 4) = right; std::memcpy(output + 4, &right, sizeof(right));
} }
*reinterpret_cast<std::uint32_t*>(output) = right; std::memcpy(output, &right, sizeof(right));
*reinterpret_cast<std::uint32_t*>(output + 4) = left; std::memcpy(output + 4, &left, sizeof(left));
} else { } else {
std::uint32_t temp = *reinterpret_cast<const std::uint32_t*>(output); std::uint32_t left;
*reinterpret_cast<std::uint32_t*>(output) = *reinterpret_cast<const std::uint32_t*>(output + 4); std::uint32_t right;
*reinterpret_cast<std::uint32_t*>(output + 4) = temp; std::memcpy(&left, output, sizeof(left));
std::memcpy(&right, output + 4, sizeof(right));
std::memcpy(output, &right, sizeof(right));
std::memcpy(output + 4, &left, sizeof(left));
} }
FinalPermutation(output); FinalPermutation(output);
@ -382,5 +397,4 @@ namespace myGPack {
return key; return key;
} }
} }

View file

@ -5,9 +5,6 @@
namespace myGPack { namespace myGPack {
// Reconstructed semantic class name; original symbol unavailable.
// The original stream routine processes complete eight-byte blocks, including
// one block for length <= 0. Callers must provide corresponding buffer space.
class PakCipher { class PakCipher {
public: public:
PakCipher(); PakCipher();
@ -25,13 +22,7 @@ namespace myGPack {
std::uint8_t* __stdcall DerivePakKey(const char* name, std::uint8_t* key); std::uint8_t* __stdcall DerivePakKey(const char* name, std::uint8_t* key);
int __stdcall InitialPermutation(std::uint8_t* block); int __stdcall InitialPermutation(std::uint8_t* block);
int __stdcall FinalPermutation(std::uint8_t* block); int __stdcall FinalPermutation(std::uint8_t* block);
int __stdcall EncodeBlock( int __stdcall EncodeBlock(int rounds, const std::uint8_t (*roundKeys)[8], std::uint32_t inputLow, std::uint32_t inputHigh, std::uint8_t* output);
int rounds,
const std::uint8_t (*roundKeys)[8],
std::uint32_t inputLow,
std::uint32_t inputHigh,
std::uint8_t* output
);
} }

View file

@ -1,13 +1,10 @@
#include "Archive/PakFile.h" #include "Archive/PakFile.h"
#include <bit>
// OFFSET: 0x005EF660 // OFFSET: 0x005EF660
// FUNCTION: fn_04009 // FUNCTION: fn_04009
CPakFile::~CPakFile() = default; CPakFile::~CPakFile() = default;
namespace myGPack { namespace myGPack {
// OFFSET: 0x005F7790 // OFFSET: 0x005F7790
// FUNCTION: fn_04046 // FUNCTION: fn_04046
CReadPakFile::CReadPakFile() : m_handle(INVALID_HANDLE_VALUE), m_size(0) { CReadPakFile::CReadPakFile() : m_handle(INVALID_HANDLE_VALUE), m_size(0) {
@ -31,7 +28,6 @@ namespace myGPack {
// OFFSET: 0x005F7810 // OFFSET: 0x005F7810
// FUNCTION: fn_04047 // FUNCTION: fn_04047
void CReadPakFile::Close() { void CReadPakFile::Close() {
// The original tests for null, not INVALID_HANDLE_VALUE.
if (this->m_handle) { if (this->m_handle) {
CloseHandle(this->m_handle); CloseHandle(this->m_handle);
this->m_handle = nullptr; this->m_handle = nullptr;
@ -41,18 +37,15 @@ namespace myGPack {
// OFFSET: 0x005F7890 // OFFSET: 0x005F7890
// FUNCTION: fn_04048 // FUNCTION: fn_04048
bool CWritePakFile::Open(const char* filename) { bool CWritePakFile::Open(const char* filename) {
this->m_handle = CreateFileA( this->m_handle = CreateFileA(filename, GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_ALWAYS, FILE_FLAG_WRITE_THROUGH | FILE_ATTRIBUTE_NORMAL, nullptr);
filename, GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_ALWAYS, FILE_FLAG_WRITE_THROUGH | FILE_ATTRIBUTE_NORMAL,
nullptr
);
if (this->m_handle == INVALID_HANDLE_VALUE) { if (this->m_handle == INVALID_HANDLE_VALUE) {
return false; return false;
} }
LARGE_INTEGER size; LARGE_INTEGER size;
// The original does not check GetFileSizeEx's return value.
GetFileSizeEx(this->m_handle, &size); GetFileSizeEx(this->m_handle, &size);
this->m_size = std::bit_cast<std::uint64_t>(size.QuadPart); this->m_size = static_cast<std::uint64_t>(size.QuadPart);
return true; return true;
} }
@ -66,14 +59,15 @@ namespace myGPack {
this->m_buffer.resize(length); this->m_buffer.resize(length);
LARGE_INTEGER position; LARGE_INTEGER position;
position.QuadPart = std::bit_cast<std::int64_t>(offset); position.QuadPart = static_cast<std::int64_t>(offset);
// Seeking errors are not checked before the original ReadFile call.
SetFilePointerEx(this->m_handle, position, nullptr, FILE_BEGIN); SetFilePointerEx(this->m_handle, position, nullptr, FILE_BEGIN);
DWORD read; DWORD read;
if (ReadFile(this->m_handle, this->m_buffer.data(), length, &read, nullptr) && read == length) { if (ReadFile(this->m_handle, this->m_buffer.data(), length, &read, nullptr) && read == length) {
return this->m_buffer.data(); return this->m_buffer.data();
} }
return nullptr; return nullptr;
} }
@ -81,7 +75,7 @@ namespace myGPack {
// FUNCTION: fn_04050 // FUNCTION: fn_04050
bool CWritePakFile::Write(std::uint64_t offset, const void* bytes, std::uint32_t length) { bool CWritePakFile::Write(std::uint64_t offset, const void* bytes, std::uint32_t length) {
LARGE_INTEGER position; LARGE_INTEGER position;
position.QuadPart = std::bit_cast<std::int64_t>(offset); position.QuadPart = static_cast<std::int64_t>(offset);
SetFilePointerEx(this->m_handle, position, nullptr, FILE_BEGIN); SetFilePointerEx(this->m_handle, position, nullptr, FILE_BEGIN);
DWORD written; DWORD written;
@ -93,7 +87,7 @@ namespace myGPack {
if (this->m_size < end) { if (this->m_size < end) {
this->m_size = end; this->m_size = end;
} }
return true; return true;
} }
} }

View file

@ -5,8 +5,6 @@
#include <vector> #include <vector>
#include <windows.h> #include <windows.h>
// Class names and inheritance are established by original RTTI.
// Method and member names are reconstructed from their behavior.
class CPakFile { class CPakFile {
public: public:
virtual ~CPakFile(); virtual ~CPakFile();

View file

@ -4,7 +4,6 @@
#include <cstring> #include <cstring>
namespace myGPack { namespace myGPack {
// OFFSET: 0x00AECE00 // OFFSET: 0x00AECE00
// FUNCTION: fn_18145 // FUNCTION: fn_18145
PakName::PakName() { PakName::PakName() {
@ -19,14 +18,15 @@ namespace myGPack {
} }
void AssignNameKey(std::uint32_t& result, char* destination, const char* source) { void AssignNameKey(std::uint32_t& result, char* destination, const char* source) {
// Source helper: copies through NUL without bounds checking.
std::strcpy(destination, source); std::strcpy(destination, source);
_strlwr(destination); _strlwr(destination);
const std::size_t length = std::strlen(destination); const std::size_t length = std::strlen(destination);
std::uint32_t hash = 5381; std::uint32_t hash = 5381;
for (std::size_t i = 0; i < length; ++i) { for (std::size_t i = 0; i < length; ++i) {
hash = 33 * hash + static_cast<std::int8_t>(destination[i]); hash = 33 * hash + static_cast<std::int8_t>(destination[i]);
} }
result = hash; result = hash;
} }
@ -48,7 +48,7 @@ namespace myGPack {
if (this->m_hash == other.m_hash) { if (this->m_hash == other.m_hash) {
return std::strcmp(this->m_name, other.m_name) < 0; return std::strcmp(this->m_name, other.m_name) < 0;
} }
return this->m_hash < other.m_hash; return this->m_hash < other.m_hash;
} }
} }

View file

@ -6,10 +6,8 @@
namespace myGPack { namespace myGPack {
// Shared machine algorithm used with different owner-reserved buffer extents.
void AssignNameKey(std::uint32_t& hash, char* destination, const char* source); void AssignNameKey(std::uint32_t& hash, char* destination, const char* source);
// Reconstructed name for the key embedded at archive entry offset 0x1C.
class PakName { class PakName {
public: public:
PakName(); PakName();

View file

@ -12,6 +12,7 @@ PakRegistry::~PakRegistry() {
delete archive.first; delete archive.first;
delete archive.second; delete archive.second;
} }
this->m_archives.clear(); this->m_archives.clear();
} }
@ -23,6 +24,7 @@ bool PakRegistry::Add(const char* filename) {
if (file) { if (file) {
file->Open(filename); file->Open(filename);
} }
this->m_archives.emplace_front(file, archive); this->m_archives.emplace_front(file, archive);
return this->m_archives.front().second->Open(this->m_archives.front().first); return this->m_archives.front().second->Open(this->m_archives.front().first);
} }
@ -79,6 +81,7 @@ bool PakRegistry::Exists(const char* filename, bool looseOnly) const {
if (search == INVALID_HANDLE_VALUE) { if (search == INVALID_HANDLE_VALUE) {
return false; return false;
} }
FindClose(search); FindClose(search);
return true; return true;
} }

View file

@ -0,0 +1,19 @@
#include "Client/Application/ApplicationActivation.h"
#include "Client/Application/ApplicationWindow.h"
#include "Contents/Configuration/ServiceOptions.h"
// OFFSET: 0x00E5A190
// FUNCTION: fn_24369
void ApplicationActivation::Set(int value) {
this->active = value;
if (value != 0) {
SetPriorityClass(GetCurrentProcess(), this->priority);
if (this->service.Get(ServiceOption::ConfineCursor) != 0) {
ClipApplicationCursor(this->window);
}
} else {
SetPriorityClass(GetCurrentProcess(), IDLE_PRIORITY_CLASS);
ClipCursor(nullptr);
this->automaticSkills.DisableAll();
}
}

View file

@ -0,0 +1,17 @@
#ifndef RAGNAROK_SOURCE_CLIENT_APPLICATION_APPLICATIONACTIVATION_H
#define RAGNAROK_SOURCE_CLIENT_APPLICATION_APPLICATIONACTIVATION_H
#include "Contents/Skills/AutomaticSkills.h"
#include "Contents/Configuration/ServiceOptions.h"
struct ApplicationActivation {
HWND& window;
const DWORD& priority;
int& active;
ServiceOptions& service;
AutomaticSkills& automaticSkills;
void Set(int value);
};
#endif

View file

@ -0,0 +1,46 @@
#include "Client/Application/ApplicationMessages.h"
#include "Client/Application/ApplicationWindow.h"
#include "Client/Input/TraditionalKeyInput.h"
#include "Contents/Configuration/ServiceOptions.h"
#include <cstdint>
#include <imm.h>
// OFFSET: 0x00E57380
// FUNCTION: fn_24357
void ApplicationMessages::Pump() {
MSG message;
while (PeekMessageA(&message, nullptr, 0, 0, PM_REMOVE)) {
if (message.message == WM_KEYDOWN && message.wParam == VK_PROCESSKEY) {
const UINT key = ImmGetVirtualKey(message.hwnd);
if (this->traditional) {
this->traditional->Key(static_cast<int>(key));
}
}
TranslateMessage(&message);
DispatchMessageA(&message);
}
if (this->dragging == 1) {
POINT point;
GetCursorPos(&point);
const int deltaX = point.x - this->dragStart.x;
const int deltaY = point.y - this->dragStart.y;
if (deltaX != 0 || deltaY != 0) {
MoveWindow(
this->window, this->dragRectangle.left + deltaX, this->dragRectangle.top + deltaY, this->dragRectangle.right - this->dragRectangle.left,
this->dragRectangle.bottom - this->dragRectangle.top, TRUE
);
}
if ((GetAsyncKeyState(VK_LBUTTON) & 0x8000) == 0) {
this->dragging = 0;
if (this->service.Get(ServiceOption::ConfineCursor) != 0) {
ClipApplicationCursor(this->window);
}
}
}
}

View file

@ -0,0 +1,20 @@
#ifndef RAGNAROK_SOURCE_CLIENT_APPLICATION_APPLICATIONMESSAGES_H
#define RAGNAROK_SOURCE_CLIENT_APPLICATION_APPLICATIONMESSAGES_H
#include <windows.h>
struct TraditionalKeyInput;
struct ServiceOptions;
struct ApplicationMessages {
HWND& window;
unsigned char& dragging;
POINT& dragStart;
RECT& dragRectangle;
TraditionalKeyInput* const& traditional;
ServiceOptions& service;
void Pump();
};
#endif

View file

@ -0,0 +1,24 @@
#include "Client/Application/ApplicationWindow.h"
// OFFSET: 0x03DE9F37
// FUNCTION: fn_45411
HWND CreateApplicationWindow(
DWORD extendedStyle, const char* className, const char*, DWORD style, int x, int y, int width, int height, HWND parent, HMENU menu, HINSTANCE instance,
void* parameter
) {
return CreateWindowExA(extendedStyle, className, "Ragnarok 2026", style | WS_SYSMENU, x, y, width, height, parent, menu, instance, parameter);
}
// OFFSET: 0x00E57DD0
// FUNCTION: fn_24360
BOOL ClipApplicationCursor(HWND window) {
RECT rectangle;
GetClientRect(window, &rectangle);
POINT first{rectangle.left, rectangle.top};
POINT last{rectangle.right, rectangle.bottom};
ClientToScreen(window, &first);
ClientToScreen(window, &last);
RECT clip{first.x, first.y, last.x, last.y};
return ClipCursor(&clip);
}

View file

@ -0,0 +1,13 @@
#ifndef RAGNAROK_SOURCE_CLIENT_APPLICATION_APPLICATIONWINDOW_H
#define RAGNAROK_SOURCE_CLIENT_APPLICATION_APPLICATIONWINDOW_H
#include <windows.h>
HWND CreateApplicationWindow(
DWORD extendedStyle, const char* className, const char* ignoredTitle, DWORD style, int x, int y, int width, int height, HWND parent, HMENU menu,
HINSTANCE instance, void* parameter
);
BOOL ClipApplicationCursor(HWND window);
#endif

View file

@ -1,4 +1,4 @@
#include "Client/ErrorDialog.h" #include "Client/Application/ErrorDialog.h"
#include "Render/RenderSystemBase.h" #include "Render/RenderSystemBase.h"
// OFFSET: 0x00AD6360 // OFFSET: 0x00AD6360
@ -7,5 +7,6 @@ int ShowErrorDialog(CRenderSystemBase* renderSystem, const char* text, UINT type
if (renderSystem) { if (renderSystem) {
renderSystem->PrepareErrorDialog(); renderSystem->PrepareErrorDialog();
} }
return MessageBoxA(nullptr, text, "Error", type); return MessageBoxA(nullptr, text, "Error", type);
} }

View file

@ -0,0 +1,10 @@
#ifndef RAGNAROK_SOURCE_CLIENT_APPLICATION_ERRORDIALOG_H
#define RAGNAROK_SOURCE_CLIENT_APPLICATION_ERRORDIALOG_H
#include <windows.h>
class CRenderSystemBase;
int ShowErrorDialog(CRenderSystemBase* renderSystem, const char* text, UINT type);
#endif

View file

@ -0,0 +1,40 @@
#include "Client/Application/Launcher.h"
#include <cstring>
#include <windows.h>
// OFFSET: 0x00E58820
// FUNCTION: fn_24363
bool RestartFromCommandLine(char* commandLine) {
char* marker = std::strstr(commandLine, "/launcherRESTART:");
if (!marker) {
return false;
}
STARTUPINFOA startup = {};
startup.cb = sizeof(startup);
startup.dwFlags = STARTF_USESHOWWINDOW;
startup.wShowWindow = SW_SHOWNORMAL;
PROCESS_INFORMATION process;
CreateProcessA(nullptr, marker + std::strlen("/launcherRESTART:"), nullptr, nullptr, FALSE, 0, nullptr, nullptr, &startup, &process);
CloseHandle(process.hProcess);
CloseHandle(process.hThread);
return true;
}
// OFFSET: 0x00E5D390
// FUNCTION: fn_24380
void ExitForProtectionCheck(const char* commandLine) {
if (std::strstr(commandLine, "/check_themida")) {
ExitProcess(0);
}
}
// OFFSET: 0x00E5D390
// FUNCTION: fn_24380
bool AcquireInstanceMutex(HANDLE* mutex) {
*mutex = CreateMutexA(nullptr, TRUE, "MutexInit");
return GetLastError() != ERROR_ALREADY_EXISTS;
}

View file

@ -0,0 +1,11 @@
#ifndef RAGNAROK_SOURCE_CLIENT_APPLICATION_LAUNCHER_H
#define RAGNAROK_SOURCE_CLIENT_APPLICATION_LAUNCHER_H
#include <windows.h>
bool RestartFromCommandLine(char* commandLine);
void ExitForProtectionCheck(const char* commandLine);
bool AcquireInstanceMutex(HANDLE* mutex);
#endif

View file

@ -1,4 +1,5 @@
#include "Client/UnhandledExceptionHandler.h" #include "Client/Mode/ActiveGameMode.h"
#include "Client/Application/UnhandledExceptionHandler.h"
#include <windows.h> #include <windows.h>
#include <dbghelp.h> #include <dbghelp.h>
@ -56,35 +57,36 @@ extern int64_t g_loginTimeTicks;
// GLOBAL: gl_24474 // GLOBAL: gl_24474
extern const uint8_t g_session[]; extern const uint8_t g_session[];
// STATIC: 0x013495b8
// GLOBAL: gl_24475
extern const uint8_t g_world[];
namespace { namespace {
int ReadPlayerJob() { int ReadPlayerJob() {
return *reinterpret_cast<const int*>(reinterpret_cast<uintptr_t>(g_session) + 0x1628); int job;
std::memcpy(&job, g_session + 0x1628, sizeof(job));
return job;
} }
const char* ReadPlayerJobName(int jobId) { const char* ReadPlayerJobName(int jobId) {
const auto* const* namesBegin = *reinterpret_cast<const char* const* const*>(reinterpret_cast<uintptr_t>(g_session) + 0xFA8); const char* const* namesBegin;
const auto* const* namesEnd = *reinterpret_cast<const char* const* const*>(reinterpret_cast<uintptr_t>(g_session) + 0xFAC); const char* const* namesEnd;
std::memcpy(&namesBegin, g_session + 0xFA8, sizeof(namesBegin));
std::memcpy(&namesEnd, g_session + 0xFAC, sizeof(namesEnd));
if (!namesBegin || !namesEnd || namesBegin >= namesEnd) { if (!namesBegin || !namesEnd || namesBegin >= namesEnd) {
return ""; return "";
} }
size_t count = namesEnd - namesBegin; size_t count = namesEnd - namesBegin;
if (static_cast<size_t>(jobId) >= count || !namesBegin[jobId]) { if (static_cast<size_t>(jobId) >= count || !namesBegin[jobId]) {
return ""; return "";
} }
return namesBegin[jobId]; return namesBegin[jobId];
} }
const char* ReadCurrentMapName() { const char* ReadCurrentMapName() {
if (*reinterpret_cast<const int*>(reinterpret_cast<uintptr_t>(g_world) + 0x58) == 1) { if (g_world.state == 1 && g_world.gameMode != nullptr) {
void* zone = *reinterpret_cast<void* const*>(reinterpret_cast<uintptr_t>(g_world) + 0x04); return reinterpret_cast<const char*>(g_world.gameMode) + 0x68;
if (zone != nullptr) {
return reinterpret_cast<const char*>(reinterpret_cast<uintptr_t>(zone) + 0x68);
}
} }
return nullptr; return nullptr;
} }
} }
@ -149,6 +151,7 @@ int CleanupUnhandledExceptionHandler() {
delete g_unhandledExceptionHandler; delete g_unhandledExceptionHandler;
g_unhandledExceptionHandler = nullptr; g_unhandledExceptionHandler = nullptr;
} }
return 0; return 0;
} }
@ -216,13 +219,7 @@ BOOL CALLBACK EnumerateLoadedModulesCallback(PCSTR moduleName, ULONG moduleBase,
// OFFSET: 0x00635E00 // OFFSET: 0x00635E00
// FUNCTION: fn_04707 // FUNCTION: fn_04707
void __cdecl InvalidParameterHandler( void __cdecl InvalidParameterHandler(const wchar_t* expression, const wchar_t* function, const wchar_t* file, unsigned int line, uintptr_t pReserved) {
const wchar_t* expression,
const wchar_t* function,
const wchar_t* file,
unsigned int line,
uintptr_t pReserved
) {
EXCEPTION_RECORD record = {}; EXCEPTION_RECORD record = {};
CONTEXT context = {}; CONTEXT context = {};
EXCEPTION_POINTERS exceptionInfo = {&record, &context}; EXCEPTION_POINTERS exceptionInfo = {&record, &context};
@ -262,17 +259,14 @@ LONG WINAPI TopLevelExceptionFilter(EXCEPTION_POINTERS* exceptionInfo) {
if (dbgHelp != nullptr) { if (dbgHelp != nullptr) {
auto pStackWalk = reinterpret_cast<BOOL(WINAPI*)( auto pStackWalk = reinterpret_cast<BOOL(WINAPI*)(
DWORD, HANDLE, HANDLE, LPSTACKFRAME, PVOID, PREAD_PROCESS_MEMORY_ROUTINE, DWORD, HANDLE, HANDLE, LPSTACKFRAME, PVOID, PREAD_PROCESS_MEMORY_ROUTINE, PFUNCTION_TABLE_ACCESS_ROUTINE, PGET_MODULE_BASE_ROUTINE,
PFUNCTION_TABLE_ACCESS_ROUTINE, PGET_MODULE_BASE_ROUTINE, PTRANSLATE_ADDRESS_ROUTINE PTRANSLATE_ADDRESS_ROUTINE
)>(GetProcAddress(dbgHelp, "StackWalk")); )>(GetProcAddress(dbgHelp, "StackWalk"));
auto pGetTimestamp = reinterpret_cast<DWORD(WINAPI*)(HMODULE)>( auto pGetTimestamp = reinterpret_cast<DWORD(WINAPI*)(HMODULE)>(GetProcAddress(dbgHelp, "GetTimestampForLoadedLibrary"));
GetProcAddress(dbgHelp, "GetTimestampForLoadedLibrary")
);
auto pEnumerateLoadedModules = reinterpret_cast<BOOL(WINAPI*)( auto pEnumerateLoadedModules =
HANDLE, PENUMLOADED_MODULES_CALLBACK, PVOID reinterpret_cast<BOOL(WINAPI*)(HANDLE, PENUMLOADED_MODULES_CALLBACK, PVOID)>(GetProcAddress(dbgHelp, "EnumerateLoadedModules"));
)>(GetProcAddress(dbgHelp, "EnumerateLoadedModules"));
HMODULE module = GetModuleHandleA(nullptr); HMODULE module = GetModuleHandleA(nullptr);
CHAR filename[256] = {}; CHAR filename[256] = {};
@ -281,7 +275,9 @@ LONG WINAPI TopLevelExceptionFilter(EXCEPTION_POINTERS* exceptionInfo) {
DWORD timestamp = pGetTimestamp ? pGetTimestamp(module) : 0; DWORD timestamp = pGetTimestamp ? pGetTimestamp(module) : 0;
sprintf(&g_crashString[strlen(g_crashString)], "Module Name: %s\r\n", filename); sprintf(&g_crashString[strlen(g_crashString)], "Module Name: %s\r\n", filename);
const char* timeString = _ctime32(reinterpret_cast<const __time32_t*>(&timestamp)); __time32_t timestampTime;
std::memcpy(&timestampTime, &timestamp, sizeof(timestampTime));
const char* timeString = _ctime32(&timestampTime);
sprintf(&g_crashString[strlen(g_crashString)], "Time Stamp: 0x%08x - %s\r\n", timestamp, timeString); sprintf(&g_crashString[strlen(g_crashString)], "Time Stamp: 0x%08x - %s\r\n", timestamp, timeString);
sprintf(&g_crashString[strlen(g_crashString)], "Version: 0x%08x\r\n", g_clientVersion); sprintf(&g_crashString[strlen(g_crashString)], "Version: 0x%08x\r\n", g_clientVersion);
sprintf(&g_crashString[strlen(g_crashString)], "\r\n"); sprintf(&g_crashString[strlen(g_crashString)], "\r\n");
@ -310,6 +306,7 @@ LONG WINAPI TopLevelExceptionFilter(EXCEPTION_POINTERS* exceptionInfo) {
if (frame.AddrPC.Offset == 0) { if (frame.AddrPC.Offset == 0) {
break; break;
} }
if (!pStackWalk(IMAGE_FILE_MACHINE_I386, process, thread, &frame, context, nullptr, nullptr, nullptr, nullptr)) { if (!pStackWalk(IMAGE_FILE_MACHINE_I386, process, thread, &frame, context, nullptr, nullptr, nullptr, nullptr)) {
break; break;
} }
@ -322,26 +319,28 @@ LONG WINAPI TopLevelExceptionFilter(EXCEPTION_POINTERS* exceptionInfo) {
const auto* stack = reinterpret_cast<const unsigned char*>(context->Esp); const auto* stack = reinterpret_cast<const unsigned char*>(context->Esp);
sprintf(&g_crashString[strlen(g_crashString)], "\r\n"); sprintf(&g_crashString[strlen(g_crashString)], "\r\n");
sprintf( sprintf(
&g_crashString[strlen(g_crashString)], &g_crashString[strlen(g_crashString)], "stack %08x - %08x\r\n", static_cast<unsigned int>(reinterpret_cast<uintptr_t>(stack)),
"stack %08x - %08x\r\n",
static_cast<unsigned int>(reinterpret_cast<uintptr_t>(stack)),
static_cast<unsigned int>(reinterpret_cast<uintptr_t>(stack + 1024)) static_cast<unsigned int>(reinterpret_cast<uintptr_t>(stack + 1024))
); );
for (int i = 0; i < 16; ++i) { for (int i = 0; i < 16; ++i) {
sprintf(&g_crashString[strlen(g_crashString)], "%08X : ", static_cast<unsigned int>(reinterpret_cast<uintptr_t>(stack))); sprintf(&g_crashString[strlen(g_crashString)], "%08X : ", static_cast<unsigned int>(reinterpret_cast<uintptr_t>(stack)));
for (int j = 0; j < 16; ++j) { for (int j = 0; j < 16; ++j) {
sprintf(&g_crashString[strlen(g_crashString)], "%02X ", *stack++); sprintf(&g_crashString[strlen(g_crashString)], "%02X ", *stack++);
} }
sprintf(&g_crashString[strlen(g_crashString)], "\r\n"); sprintf(&g_crashString[strlen(g_crashString)], "\r\n");
} }
sprintf(&g_crashString[strlen(g_crashString)], "\r\nLaunch Info \r\n"); sprintf(&g_crashString[strlen(g_crashString)], "\r\nLaunch Info \r\n");
int launchIndex = 0; int launchIndex = 0;
for (int k = 0; k < 4; ++k) { for (int k = 0; k < 4; ++k) {
for (int m = 0; m < 8; ++m) { for (int m = 0; m < 8; ++m) {
sprintf(&g_crashString[strlen(g_crashString)], "%04X ", g_launchInfo[launchIndex++]); sprintf(&g_crashString[strlen(g_crashString)], "%04X ", g_launchInfo[launchIndex++]);
} }
sprintf(&g_crashString[strlen(g_crashString)], "\r\n"); sprintf(&g_crashString[strlen(g_crashString)], "\r\n");
} }
@ -354,12 +353,7 @@ LONG WINAPI TopLevelExceptionFilter(EXCEPTION_POINTERS* exceptionInfo) {
sprintf(&g_crashString[strlen(g_crashString)], "%s %d %d\r\n", mapName, g_playerX, g_playerY); sprintf(&g_crashString[strlen(g_crashString)], "%s %d %d\r\n", mapName, g_playerX, g_playerY);
} }
sprintf( sprintf(&g_crashString[strlen(g_crashString)], "\r\n%I64d - %I64d", g_startTimeTicks / 10000000LL, g_loginTimeTicks / 10000000LL);
&g_crashString[strlen(g_crashString)],
"\r\n%I64d - %I64d",
g_startTimeTicks / 10000000LL,
g_loginTimeTicks / 10000000LL
);
int64_t reportTime = _Xtime_get_ticks() / 10000000LL; int64_t reportTime = _Xtime_get_ticks() / 10000000LL;
PROCESS_MEMORY_COUNTERS memCounters = {}; PROCESS_MEMORY_COUNTERS memCounters = {};

View file

@ -1,5 +1,5 @@
#ifndef RAGNAROK_SOURCE_CLIENT_UNHANDLEDEXCEPTIONHANDLER_H #ifndef RAGNAROK_SOURCE_CLIENT_APPLICATION_UNHANDLEDEXCEPTIONHANDLER_H
#define RAGNAROK_SOURCE_CLIENT_UNHANDLEDEXCEPTIONHANDLER_H #define RAGNAROK_SOURCE_CLIENT_APPLICATION_UNHANDLEDEXCEPTIONHANDLER_H
#include <windows.h> #include <windows.h>
#include <cstdint> #include <cstdint>
@ -39,13 +39,7 @@ BOOL CALLBACK EnumerateLoadedModulesCallback(PCSTR moduleName, ULONG moduleBase,
// OFFSET: 0x00635E00 // OFFSET: 0x00635E00
// FUNCTION: fn_04707 // FUNCTION: fn_04707
void __cdecl InvalidParameterHandler( void __cdecl InvalidParameterHandler(const wchar_t* expression, const wchar_t* function, const wchar_t* file, unsigned int line, uintptr_t pReserved);
const wchar_t* expression,
const wchar_t* function,
const wchar_t* file,
unsigned int line,
uintptr_t pReserved
);
// OFFSET: 0x00635E90 // OFFSET: 0x00635E90
// FUNCTION: fn_04708 // FUNCTION: fn_04708
@ -55,4 +49,4 @@ void __cdecl PurecallHandler();
// FUNCTION: fn_04709 // FUNCTION: fn_04709
LONG WINAPI TopLevelExceptionFilter(EXCEPTION_POINTERS* exceptionInfo); LONG WINAPI TopLevelExceptionFilter(EXCEPTION_POINTERS* exceptionInfo);
#endif // RAGNAROK_SOURCE_CLIENT_UNHANDLEDEXCEPTIONHANDLER_H #endif

View file

@ -1,20 +0,0 @@
#include "Client/ApplicationActivation.h"
#include "Client/ApplicationWindow.h"
namespace client {
// OFFSET: 0x00E5A190
// FUNCTION: fn_24369
void ApplicationActivation::Set(int value) {
this->active = value;
if (value != 0) {
SetPriorityClass(GetCurrentProcess(), this->priority);
if (this->service.Get(271) != 0) {
ClipApplicationCursor(this->window);
}
} else {
SetPriorityClass(GetCurrentProcess(), IDLE_PRIORITY_CLASS);
ClipCursor(nullptr);
this->automaticSkills.DisableAll();
}
}
}

View file

@ -1,23 +0,0 @@
#ifndef RAGNAROK_SOURCE_CLIENT_APPLICATIONACTIVATION_H
#define RAGNAROK_SOURCE_CLIENT_APPLICATIONACTIVATION_H
#include "Contents/AutomaticSkills.h"
#include "Contents/ServiceOptions.h"
namespace client {
// Borrowed original main-window/priority/active globals and concrete owners.
struct ApplicationActivation {
HWND& window;
const DWORD& priority;
int& active;
ServiceOptions& service;
AutomaticSkills& automaticSkills;
// Publishes value verbatim. Nonzero restores configured process priority
// and clips only when option 271 is enabled. Zero selects idle priority,
// releases clipping, then cancels automatic skills through their connection.
void Set(int value);
};
}
#endif

View file

@ -1,47 +0,0 @@
#include "Client/ApplicationMessages.h"
#include "Client/ApplicationWindow.h"
#include "Client/TraditionalKeyInput.h"
#include "Contents/ServiceOptions.h"
#include <cstdint>
#include <imm.h>
namespace client {
// OFFSET: 0x00E57380
// FUNCTION: fn_24357
void ApplicationMessages::Pump() {
MSG message;
while (PeekMessageA(&message, nullptr, 0, 0, PM_REMOVE)) {
if (message.message == WM_KEYDOWN && message.wParam == VK_PROCESSKEY) {
const UINT key = ImmGetVirtualKey(message.hwnd);
if (this->traditional) {
this->traditional->Key(static_cast<int>(key));
}
}
TranslateMessage(&message);
DispatchMessageA(&message);
}
if (this->dragging == 1) {
POINT point;
GetCursorPos(&point);
const int deltaX = point.x - this->dragStart.x;
const int deltaY = point.y - this->dragStart.y;
if (deltaX != 0 || deltaY != 0) {
MoveWindow(
this->window, this->dragRectangle.left + deltaX, this->dragRectangle.top + deltaY,
this->dragRectangle.right - this->dragRectangle.left, this->dragRectangle.bottom - this->dragRectangle.top,
TRUE
);
}
if ((GetAsyncKeyState(VK_LBUTTON) & 0x8000) == 0) {
this->dragging = 0;
if (this->service.Get(271) != 0) {
ClipApplicationCursor(this->window);
}
}
}
}
}

View file

@ -1,26 +0,0 @@
#ifndef RAGNAROK_SOURCE_CLIENT_APPLICATIONMESSAGES_H
#define RAGNAROK_SOURCE_CLIENT_APPLICATIONMESSAGES_H
#include <windows.h>
struct TraditionalKeyInput;
struct ServiceOptions;
namespace client {
// Borrowed application globals. A non-null traditional input view denotes
// CLanguageTC; base/TH/ARB virtual-key delivery is an established empty entry.
struct ApplicationMessages {
HWND& window;
unsigned char& dragging;
POINT& dragStart;
RECT& dragRectangle;
TraditionalKeyInput* const& traditional;
ServiceOptions& service;
// Drains all messages (including WM_QUIT) through Translate/Dispatch, then
// updates native window dragging. Does not invent the main window procedure.
void Pump();
};
}
#endif

View file

@ -1,39 +0,0 @@
#include "Client/ApplicationWindow.h"
namespace client {
// OFFSET: 0x03DE9F37
// FUNCTION: fn_45411
HWND CreateApplicationWindow(
DWORD extendedStyle,
const char* className,
const char*,
DWORD style,
int x,
int y,
int width,
int height,
HWND parent,
HMENU menu,
HINSTANCE instance,
void* parameter
) {
return CreateWindowExA(
extendedStyle, className, "Ragnarok 2026", style | WS_SYSMENU, x, y, width, height, parent, menu, instance,
parameter
);
}
// OFFSET: 0x00E57DD0
// FUNCTION: fn_24360
BOOL ClipApplicationCursor(HWND window) {
RECT rectangle;
GetClientRect(window, &rectangle);
POINT first{rectangle.left, rectangle.top};
POINT last{rectangle.right, rectangle.bottom};
ClientToScreen(window, &first);
ClientToScreen(window, &last);
RECT clip{first.x, first.y, last.x, last.y};
return ClipCursor(&clip);
}
}

View file

@ -1,29 +0,0 @@
#ifndef RAGNAROK_SOURCE_CLIENT_APPLICATIONWINDOW_H
#define RAGNAROK_SOURCE_CLIENT_APPLICATIONWINDOW_H
#include <windows.h>
namespace client {
// Recovered CreateWindowExA wrapper, not the full application initializer.
// Forces the title "Ragnarok 2026" and adds WS_SYSMENU to the caller's style.
HWND CreateApplicationWindow(
DWORD extendedStyle,
const char* className,
const char* ignoredTitle,
DWORD style,
int x,
int y,
int width,
int height,
HWND parent,
HMENU menu,
HINSTANCE instance,
void* parameter
);
// Original global main-window handle is borrowed; clips its client rectangle
// after converting both corners to screen coordinates. Returns ClipCursor.
BOOL ClipApplicationCursor(HWND window);
}
#endif

View file

@ -1,4 +1,4 @@
#include "Client/BrowserAddressBar.h" #include "Client/Browser/BrowserAddressBar.h"
#include <commctrl.h> #include <commctrl.h>
@ -6,7 +6,10 @@
// FUNCTION: fn_17416 // FUNCTION: fn_17416
bool BrowserAddressBar::Create(int width, int requestedHeight) { bool BrowserAddressBar::Create(int width, int requestedHeight) {
this->rebar = CreateWindowExA( this->rebar = CreateWindowExA(
0x80, "ReBarWindow32", "rebar", 0x56809648, 0, 0, width, 0, this->host, nullptr, GetModuleHandleA(nullptr), nullptr WS_EX_TOOLWINDOW, REBARCLASSNAMEA, "rebar",
WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_BORDER | RBS_VARHEIGHT | RBS_BANDBORDERS | RBS_REGISTERDROP | RBS_DBLCLKTOGGLE |
CCS_NOPARENTALIGN | CCS_NODIVIDER,
0, 0, width, 0, this->host, nullptr, GetModuleHandleA(nullptr), nullptr
); );
if (!this->rebar) { if (!this->rebar) {
return false; return false;
@ -19,33 +22,31 @@ bool BrowserAddressBar::Create(int width, int requestedHeight) {
} }
HWND combo = CreateWindowExA( HWND combo = CreateWindowExA(
0x200, "COMBOBOX", "addressbar", 0x50000002, 10, 10, 160, 22, this->rebar, nullptr, GetModuleHandleA(nullptr), WS_EX_CLIENTEDGE, WC_COMBOBOXA, "addressbar", WS_CHILD | WS_VISIBLE | CBS_DROPDOWN, 10, 10, 160, 22, this->rebar, nullptr, GetModuleHandleA(nullptr),
nullptr nullptr
); );
REBARBANDINFOA band{}; REBARBANDINFOA band{};
band.cbSize = sizeof(band); band.cbSize = sizeof(band);
band.fMask = 0xF1; band.fMask = RBBIM_STYLE | RBBIM_CHILD | RBBIM_CHILDSIZE | RBBIM_SIZE | RBBIM_BACKGROUND;
band.fStyle = 0x24; band.fStyle = RBBS_CHILDEDGE | RBBS_FIXEDBMP;
band.hwndChild = combo; band.hwndChild = combo;
band.cxMinChild = 0; band.cxMinChild = 0;
band.cyMinChild = requestedHeight; band.cyMinChild = requestedHeight;
band.cx = 160; band.cx = 160;
band.hbmBack = LoadBitmapA(GetModuleHandleA(nullptr), nullptr); band.hbmBack = LoadBitmapA(GetModuleHandleA(nullptr), nullptr);
if (!SendMessageA(this->rebar, RB_INSERTBANDA, static_cast<WPARAM>(-1), reinterpret_cast<LPARAM>(&band))) { if (!SendMessageA(this->rebar, RB_INSERTBANDA, static_cast<WPARAM>(-1), reinterpret_cast<LPARAM>(&band))) {
return false; return false;
} }
this->combo = combo; this->combo = combo;
HWND edit = GetWindow(combo, GW_CHILD); HWND edit = GetWindow(combo, GW_CHILD);
this->previous = this->previous = reinterpret_cast<WNDPROC>(SetWindowLongPtrA(edit, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(this->procedure)));
reinterpret_cast<WNDPROC>(SetWindowLongPtrA(edit, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(this->procedure)));
RECT rectangle; RECT rectangle;
GetClientRect(this->host, &rectangle); GetClientRect(this->host, &rectangle);
SetWindowPos( SetWindowPos(this->rebar, nullptr, 0, 0, rectangle.right - rectangle.left, rectangle.bottom - rectangle.top, SWP_NOMOVE);
this->rebar, nullptr, 0, 0, rectangle.right - rectangle.left, rectangle.bottom - rectangle.top, SWP_NOMOVE
);
ShowWindow(this->rebar, SW_HIDE); ShowWindow(this->rebar, SW_HIDE);
this->height = 0; this->height = 0;

View file

@ -0,0 +1,18 @@
#ifndef RAGNAROK_SOURCE_CLIENT_BROWSER_BROWSERADDRESSBAR_H
#define RAGNAROK_SOURCE_CLIENT_BROWSER_BROWSERADDRESSBAR_H
#include <windows.h>
struct BrowserAddressBar {
HWND const& host;
HWND& rebar;
HWND& combo;
int& height;
const unsigned char& visible;
WNDPROC& previous;
WNDPROC procedure;
bool Create(int width, int height);
};
#endif

View file

@ -1,4 +1,5 @@
#include "Client/BrowserAddressEdit.h" #include "Client/Browser/BrowserAddressEdit.h"
#include "Client/Browser/BrowserMessages.h"
// OFFSET: 0x00AB21F0 // OFFSET: 0x00AB21F0
// FUNCTION: fn_17412 // FUNCTION: fn_17412
@ -9,7 +10,7 @@ LRESULT BrowserAddressEdit::Message(HWND window, UINT message, WPARAM value, LPA
} }
if (message == WM_KEYDOWN && this->activeHost) { if (message == WM_KEYDOWN && this->activeHost) {
SendMessageA(*this->activeHost, WM_COMMAND, 1000, 0); SendMessageA(*this->activeHost, WM_COMMAND, static_cast<WPARAM>(BrowserCommand::Navigate), 0);
return 0; return 0;
} }
} }

View file

@ -0,0 +1,13 @@
#ifndef RAGNAROK_SOURCE_CLIENT_BROWSER_BROWSERADDRESSEDIT_H
#define RAGNAROK_SOURCE_CLIENT_BROWSER_BROWSERADDRESSEDIT_H
#include <windows.h>
struct BrowserAddressEdit {
WNDPROC const& previous;
HWND const* const& activeHost;
LRESULT Message(HWND window, UINT message, WPARAM value, LPARAM flags);
};
#endif

View file

@ -1,16 +1,10 @@
#include "Client/BrowserClientSite.h" #include "Client/Browser/BrowserClientSite.h"
// OFFSET: 0x00AB1310 // OFFSET: 0x00AB1310
// FUNCTION: fn_17334 // FUNCTION: fn_17334
BrowserClientSite::BrowserClientSite( BrowserClientSite::BrowserClientSite(HWND window, IUnknown& browser, BrowserFactory& factory, const int& width, const int& height)
HWND window, : m_window(window), m_browser(&browser), m_events(*this, factory, width, height), m_inPlace(*this, this->m_window, this->m_browser), m_documentUI(*this),
IUnknown& browser, m_commands(*this) {
BrowserFactory& factory,
const int& width,
const int& height
)
: m_window(window), m_browser(&browser), m_events(*this, factory, width, height),
m_inPlace(*this, this->m_window, this->m_browser), m_documentUI(*this), m_commands(*this) {
this->m_browser->AddRef(); this->m_browser->AddRef();
} }
@ -40,8 +34,6 @@ HRESULT STDMETHODCALLTYPE BrowserClientSite::QueryInterface(REFIID iid, void** o
} else if (iid == IID_IOleCommandTarget) { } else if (iid == IID_IOleCommandTarget) {
*output = static_cast<IOleCommandTarget*>(&this->m_commands); *output = static_cast<IOleCommandTarget*>(&this->m_commands);
} else { } else {
// Unlike standard COM helpers, the original does not clear a failed output
// and does not AddRef a successful result.
return E_NOINTERFACE; return E_NOINTERFACE;
} }

View file

@ -1,16 +1,11 @@
#ifndef RAGNAROK_SOURCE_CLIENT_BROWSERCLIENTSITE_H #ifndef RAGNAROK_SOURCE_CLIENT_BROWSER_BROWSERCLIENTSITE_H
#define RAGNAROK_SOURCE_CLIENT_BROWSERCLIENTSITE_H #define RAGNAROK_SOURCE_CLIENT_BROWSER_BROWSERCLIENTSITE_H
#include "Client/BrowserCommandTarget.h" #include "Client/Browser/BrowserCommandTarget.h"
#include "Client/BrowserDocumentUI.h" #include "Client/Browser/BrowserDocumentUI.h"
#include "Client/BrowserInPlace.h" #include "Client/Browser/BrowserInPlace.h"
#include "UI/BrowserPopupDispatch.h" #include "Client/Browser/BrowserPopupDispatch.h"
// Recovered site COM contracts with its owned popup-dispatch tear-off.
// Factory and renderer dimensions must outlive the site. QueryInterface does
// not acquire references; the containing browser controls this site's lifetime.
// Retains the browser until destruction. COM reference calls return one;
// lifetime is controlled by the containing CWebBrowser, not COM reference counts.
class BrowserClientSite final : public IOleClientSite { class BrowserClientSite final : public IOleClientSite {
public: public:
BrowserClientSite(HWND window, IUnknown& browser, BrowserFactory& factory, const int& width, const int& height); BrowserClientSite(HWND window, IUnknown& browser, BrowserFactory& factory, const int& width, const int& height);

View file

@ -1,5 +1,5 @@
#include "Client/BrowserClose.h" #include "Client/Browser/BrowserClose.h"
#include "Client/Mouse.h" #include "Client/Input/Mouse.h"
// OFFSET: 0x00AB24D0 // OFFSET: 0x00AB24D0
// FUNCTION: fn_45432 // FUNCTION: fn_45432

View file

@ -0,0 +1,21 @@
#ifndef RAGNAROK_SOURCE_CLIENT_BROWSER_BROWSERCLOSE_H
#define RAGNAROK_SOURCE_CLIENT_BROWSER_BROWSERCLOSE_H
#include "Client/Browser/BrowserSiteAttachment.h"
class CMouse;
struct BrowserClose {
BrowserSiteAttachment& attachment;
IWebBrowser2*& browser;
IUnknown*& externalEvents;
HWND& host;
HWND const& browserWindow;
HWND const& applicationWindow;
int& cursorState;
CMouse& mouse;
bool Close();
};
#endif

View file

@ -1,4 +1,6 @@
#include "Client/BrowserCommandTarget.h" #include "Client/Browser/BrowserCommandTarget.h"
#include <mshtmhst.h>
// OFFSET: 0x00AB1A80 // OFFSET: 0x00AB1A80
// FUNCTION: fn_17379 // FUNCTION: fn_17379
@ -21,7 +23,6 @@ ULONG STDMETHODCALLTYPE BrowserCommandTarget::Release() {
// OFFSET: 0x00AB1C50 // OFFSET: 0x00AB1C50
// FUNCTION: fn_17385 // FUNCTION: fn_17385
HRESULT STDMETHODCALLTYPE BrowserCommandTarget::QueryStatus(const GUID*, ULONG, OLECMD[], OLECMDTEXT*) { HRESULT STDMETHODCALLTYPE BrowserCommandTarget::QueryStatus(const GUID*, ULONG, OLECMD[], OLECMDTEXT*) {
// The original reports success without reading or publishing command flags.
return S_OK; return S_OK;
} }
@ -29,15 +30,11 @@ HRESULT STDMETHODCALLTYPE BrowserCommandTarget::QueryStatus(const GUID*, ULONG,
// FUNCTION: fn_17353 // FUNCTION: fn_17353
HRESULT STDMETHODCALLTYPE BrowserCommandTarget::Exec(const GUID* group, DWORD command, DWORD, VARIANT*, VARIANT*) { HRESULT STDMETHODCALLTYPE BrowserCommandTarget::Exec(const GUID* group, DWORD command, DWORD, VARIANT*, VARIANT*) {
// STATIC: 0x011B0880 (CGID_DocHostCommandHandler) // STATIC: 0x011B0880 (CGID_DocHostCommandHandler)
static constexpr GUID documentHostCommands = { if (!group || !IsEqualGUID(*group, CGID_DocHostCommandHandler)) {
0xF38BC242, 0xB950, 0x11D1, {0x89, 0x18, 0x00, 0xC0, 0x4F, 0xC2, 0xC8, 0x36}
};
if (!group || !IsEqualGUID(*group, documentHostCommands)) {
return OLECMDERR_E_UNKNOWNGROUP; return OLECMDERR_E_UNKNOWNGROUP;
} }
if (command == 40 || command == 41) { if (command == OLECMDID_SHOWSCRIPTERROR || command == OLECMDID_SHOWMESSAGE) {
return S_OK; return S_OK;
} }

View file

@ -1,15 +1,10 @@
#ifndef RAGNAROK_SOURCE_CLIENT_BROWSERCOMMANDTARGET_H #ifndef RAGNAROK_SOURCE_CLIENT_BROWSER_BROWSERCOMMANDTARGET_H
#define RAGNAROK_SOURCE_CLIENT_BROWSERCOMMANDTARGET_H #define RAGNAROK_SOURCE_CLIENT_BROWSER_BROWSERCOMMANDTARGET_H
// clang-format off
#include <windows.h> #include <windows.h>
#include <ole2.h> #include <ole2.h>
#include <docobj.h> #include <docobj.h>
// clang-format on
// CWebBrowser's site owns this tear-off interface.
// Identity delegates to that site; AddRef/Release deliberately return one.
// The caller must keep the site alive for this interface's entire lifetime.
class BrowserCommandTarget final : public IOleCommandTarget { class BrowserCommandTarget final : public IOleCommandTarget {
public: public:
explicit BrowserCommandTarget(IUnknown& site) : m_site(site) { explicit BrowserCommandTarget(IUnknown& site) : m_site(site) {
@ -19,8 +14,7 @@ public:
ULONG STDMETHODCALLTYPE AddRef() override; ULONG STDMETHODCALLTYPE AddRef() override;
ULONG STDMETHODCALLTYPE Release() override; ULONG STDMETHODCALLTYPE Release() override;
HRESULT STDMETHODCALLTYPE QueryStatus(const GUID* group, ULONG count, OLECMD commands[], OLECMDTEXT* text) override; HRESULT STDMETHODCALLTYPE QueryStatus(const GUID* group, ULONG count, OLECMD commands[], OLECMDTEXT* text) override;
HRESULT STDMETHODCALLTYPE HRESULT STDMETHODCALLTYPE Exec(const GUID* group, DWORD command, DWORD options, VARIANT* input, VARIANT* output) override;
Exec(const GUID* group, DWORD command, DWORD options, VARIANT* input, VARIANT* output) override;
private: private:
IUnknown& m_site; IUnknown& m_site;

View file

@ -1,5 +1,5 @@
#include "Client/BrowserCreation.h" #include "Client/Browser/BrowserCreation.h"
#include "Client/BrowserInterface.h" #include "Client/Browser/BrowserInterface.h"
// OFFSET: 0x00AB22B0 // OFFSET: 0x00AB22B0
// FUNCTION: fn_17413 // FUNCTION: fn_17413

View file

@ -0,0 +1,18 @@
#ifndef RAGNAROK_SOURCE_CLIENT_BROWSER_BROWSERCREATION_H
#define RAGNAROK_SOURCE_CLIENT_BROWSER_BROWSERCREATION_H
#include "Client/Browser/BrowserAddressBar.h"
#include "Client/Browser/BrowserHostWindow.h"
#include "Client/Browser/BrowserSiteAttachment.h"
struct BrowserCreation {
IWebBrowser2*& browser;
HWND& browserWindow;
BrowserHostWindow& window;
BrowserSiteAttachment& attachment;
BrowserAddressBar& addressBar;
bool Create(HWND parent, HINSTANCE instance, int width, int height);
};
#endif

View file

@ -1,5 +1,5 @@
#include "Client/BrowserCursor.h" #include "Client/Browser/BrowserCursor.h"
#include "Client/Mouse.h" #include "Client/Input/Mouse.h"
#include "Render/RenderSystemBase.h" #include "Render/RenderSystemBase.h"
// OFFSET: 0x00AB32E0 // OFFSET: 0x00AB32E0
@ -10,9 +10,8 @@ void BrowserCursor::Set(int value) {
} }
if (value != 0) { if (value != 0) {
SetCursor(LoadCursorA(nullptr, MAKEINTRESOURCEA(32512))); SetCursor(LoadCursorA(nullptr, reinterpret_cast<LPCSTR>(IDC_ARROW)));
// Original mouse centering queries Width then Height before replacing
// both coordinates with -100. Retain these virtual calls and their order.
this->mouse.SetX(static_cast<int>(this->renderSystem.Width()) / 2); this->mouse.SetX(static_cast<int>(this->renderSystem.Width()) / 2);
this->mouse.SetY(static_cast<int>(this->renderSystem.Height()) / 2); this->mouse.SetY(static_cast<int>(this->renderSystem.Height()) / 2);
this->mouse.SetX(-100); this->mouse.SetX(-100);

View file

@ -0,0 +1,19 @@
#ifndef RAGNAROK_SOURCE_CLIENT_BROWSER_BROWSERCURSOR_H
#define RAGNAROK_SOURCE_CLIENT_BROWSER_BROWSERCURSOR_H
#include <windows.h>
class CMouse;
class CRenderSystemBase;
struct BrowserCursor {
int& state;
CMouse& mouse;
CRenderSystemBase& renderSystem;
HWND const& applicationWindow;
void Set(int value);
void Poll();
};
#endif

View file

@ -1,4 +1,4 @@
#include "Client/BrowserDocumentUI.h" #include "Client/Browser/BrowserDocumentUI.h"
// OFFSET: 0x00AB1AC0 // OFFSET: 0x00AB1AC0
// FUNCTION: fn_17381 // FUNCTION: fn_17381
@ -27,23 +27,16 @@ HRESULT STDMETHODCALLTYPE BrowserDocumentUI::ShowContextMenu(DWORD, POINT*, IUnk
// OFFSET: 0x00AB1770 // OFFSET: 0x00AB1770
// FUNCTION: fn_17359 // FUNCTION: fn_17359
HRESULT STDMETHODCALLTYPE BrowserDocumentUI::GetHostInfo(DOCHOSTUIINFO* info) { HRESULT STDMETHODCALLTYPE BrowserDocumentUI::GetHostInfo(DOCHOSTUIINFO* info) {
// Original x86 cbSize, even when compiling for a wider host. The two optional info->cbSize = sizeof(*info);
// string pointers are not initialized by this operation. info->dwFlags = DOCHOSTUIFLAG_NO3DBORDER;
info->cbSize = 20; info->dwDoubleClick = DOCHOSTUIDBLCLK_DEFAULT;
info->dwFlags = 4;
info->dwDoubleClick = 0;
return S_OK; return S_OK;
} }
// OFFSET: 0x00AB1D80 // OFFSET: 0x00AB1D80
// FUNCTION: fn_17404 // FUNCTION: fn_17404
HRESULT STDMETHODCALLTYPE BrowserDocumentUI::ShowUI( HRESULT STDMETHODCALLTYPE BrowserDocumentUI::ShowUI(DWORD, IOleInPlaceActiveObject*, IOleCommandTarget*, IOleInPlaceFrame*, IOleInPlaceUIWindow*) {
DWORD,
IOleInPlaceActiveObject*,
IOleCommandTarget*,
IOleInPlaceFrame*,
IOleInPlaceUIWindow*
) {
return S_OK; return S_OK;
} }

View file

@ -1,14 +1,10 @@
#ifndef RAGNAROK_SOURCE_CLIENT_BROWSERDOCUMENTUI_H #ifndef RAGNAROK_SOURCE_CLIENT_BROWSER_BROWSERDOCUMENTUI_H
#define RAGNAROK_SOURCE_CLIENT_BROWSERDOCUMENTUI_H #define RAGNAROK_SOURCE_CLIENT_BROWSER_BROWSERDOCUMENTUI_H
// clang-format off
#include <windows.h> #include <windows.h>
#include <ole2.h> #include <ole2.h>
#include <mshtmhst.h> #include <mshtmhst.h>
// clang-format on
// Site-owned IDocHostUIHandler tear-off. Identity delegates to the borrowed
// site; reference calls do not manage lifetime. Unwritten outputs stay unwritten.
class BrowserDocumentUI final : public IDocHostUIHandler { class BrowserDocumentUI final : public IDocHostUIHandler {
public: public:
explicit BrowserDocumentUI(IUnknown& site) : m_site(site) { explicit BrowserDocumentUI(IUnknown& site) : m_site(site) {
@ -19,13 +15,8 @@ public:
ULONG STDMETHODCALLTYPE Release() override; ULONG STDMETHODCALLTYPE Release() override;
HRESULT STDMETHODCALLTYPE ShowContextMenu(DWORD id, POINT* point, IUnknown* reserved, IDispatch* dispatch) override; HRESULT STDMETHODCALLTYPE ShowContextMenu(DWORD id, POINT* point, IUnknown* reserved, IDispatch* dispatch) override;
HRESULT STDMETHODCALLTYPE GetHostInfo(DOCHOSTUIINFO* info) override; HRESULT STDMETHODCALLTYPE GetHostInfo(DOCHOSTUIINFO* info) override;
HRESULT STDMETHODCALLTYPE ShowUI( HRESULT STDMETHODCALLTYPE
DWORD id, ShowUI(DWORD id, IOleInPlaceActiveObject* active, IOleCommandTarget* commands, IOleInPlaceFrame* frame, IOleInPlaceUIWindow* document) override;
IOleInPlaceActiveObject* active,
IOleCommandTarget* commands,
IOleInPlaceFrame* frame,
IOleInPlaceUIWindow* document
) override;
HRESULT STDMETHODCALLTYPE HideUI() override; HRESULT STDMETHODCALLTYPE HideUI() override;
HRESULT STDMETHODCALLTYPE UpdateUI() override; HRESULT STDMETHODCALLTYPE UpdateUI() override;
HRESULT STDMETHODCALLTYPE EnableModeless(BOOL enable) override; HRESULT STDMETHODCALLTYPE EnableModeless(BOOL enable) override;

View file

@ -1,8 +1,8 @@
#include "Client/BrowserRegistry.h" #include "Client/Browser/BrowserRegistry.h"
#include "Client/WebBrowser.h" #include "Client/Browser/WebBrowser.h"
#include "Contents/MessageTable.h" #include "Contents/MessageTable.h"
#include "UI/BrowserFactory.h" #include "Client/Browser/BrowserFactory.h"
#include "UI/ChatNotifications.h" #include "UI/Chat/ChatNotifications.h"
#include <new> #include <new>
@ -13,6 +13,7 @@ CWebBrowser* BrowserFactory::Create(int width, int height) {
this->notifications.Refresh(); this->notifications.Refresh();
const char* message = LookupMessage(1578, this->languagePrefix, this->serviceType, this->language); const char* message = LookupMessage(1578, this->languagePrefix, this->serviceType, this->language);
this->notifications.Text(message, 255, nullptr, 0, false); this->notifications.Text(message, 255, nullptr, 0, false);
return nullptr; return nullptr;
} }

View file

@ -1,5 +1,5 @@
#ifndef RAGNAROK_SOURCE_UI_BROWSERFACTORY_H #ifndef RAGNAROK_SOURCE_CLIENT_BROWSER_BROWSERFACTORY_H
#define RAGNAROK_SOURCE_UI_BROWSERFACTORY_H #define RAGNAROK_SOURCE_CLIENT_BROWSER_BROWSERFACTORY_H
#include <windows.h> #include <windows.h>
@ -8,9 +8,6 @@ struct WebBrowserContext;
struct BrowserRegistry; struct BrowserRegistry;
struct ChatNotifications; struct ChatNotifications;
// Ordinary browser creation is distinct from the special-browser factory.
// The limit counts all map nodes, not nonnull entries. Failed ordinary creation
// retains the allocated browser in the original; it is not published or deleted.
struct BrowserFactory { struct BrowserFactory {
BrowserRegistry& registry; BrowserRegistry& registry;
WebBrowserContext& context; WebBrowserContext& context;

View file

@ -1,4 +1,4 @@
#include "Client/BrowserFrame.h" #include "Client/Browser/BrowserFrame.h"
// OFFSET: 0x00AB2610 // OFFSET: 0x00AB2610
// FUNCTION: fn_17414 // FUNCTION: fn_17414
@ -9,10 +9,7 @@ void BrowserFrame::Normalize() {
RECT rectangle; RECT rectangle;
GetWindowRect(this->browserWindow, &rectangle); GetWindowRect(this->browserWindow, &rectangle);
if ( if (rectangle.right - rectangle.left != this->sizing.screenWidth || rectangle.bottom - rectangle.top != this->sizing.screenHeight) {
rectangle.right - rectangle.left != this->sizing.screenWidth ||
rectangle.bottom - rectangle.top != this->sizing.screenHeight
) {
return; return;
} }

View file

@ -0,0 +1,18 @@
#ifndef RAGNAROK_SOURCE_CLIENT_BROWSER_BROWSERFRAME_H
#define RAGNAROK_SOURCE_CLIENT_BROWSER_BROWSERFRAME_H
#include "Client/Browser/BrowserCursor.h"
#include "Client/Browser/BrowserWindowSizing.h"
struct BrowserFrame {
BrowserWindowSizing& sizing;
BrowserCursor& cursor;
HWND const& host;
HWND const& browserWindow;
const bool& hasExternalEvents;
void Normalize();
void Poll();
};
#endif

View file

@ -1,4 +1,4 @@
#include "Client/BrowserHostWindow.h" #include "Client/Browser/BrowserHostWindow.h"
// OFFSET: 0x00AB29D0 // OFFSET: 0x00AB29D0
// FUNCTION: fn_45435 // FUNCTION: fn_45435
@ -11,10 +11,7 @@ bool BrowserHostWindow::Create(HWND parent, HINSTANCE instance, int width, int h
windowClass.lpszClassName = "ROWebBrowser"; windowClass.lpszClassName = "ROWebBrowser";
RegisterClassExA(&windowClass); RegisterClassExA(&windowClass);
this->host = CreateWindowExA( this->host = CreateWindowExA(0, "ROWebBrowser", this->title, WS_POPUP | WS_OVERLAPPEDWINDOW, 0, 0, width, height, parent, nullptr, instance, nullptr);
0, "ROWebBrowser", this->title, WS_POPUP | WS_OVERLAPPEDWINDOW, 0, 0, width, height, parent, nullptr, instance,
nullptr
);
if (!this->host) { if (!this->host) {
OutputDebugStringA("failed : CreateWindow() \n"); OutputDebugStringA("failed : CreateWindow() \n");
return false; return false;
@ -23,5 +20,6 @@ bool BrowserHostWindow::Create(HWND parent, HINSTANCE instance, int width, int h
UpdateWindow(this->host); UpdateWindow(this->host);
SetParent(this->browser, this->host); SetParent(this->browser, this->host);
SetWindowLongA(this->browser, GWL_STYLE, WS_CHILD); SetWindowLongA(this->browser, GWL_STYLE, WS_CHILD);
return true; return true;
} }

View file

@ -0,0 +1,15 @@
#ifndef RAGNAROK_SOURCE_CLIENT_BROWSER_BROWSERHOSTWINDOW_H
#define RAGNAROK_SOURCE_CLIENT_BROWSER_BROWSERHOSTWINDOW_H
#include <windows.h>
struct BrowserHostWindow {
HWND& host;
HWND const& browser;
const char* const& title;
WNDPROC procedure;
bool Create(HWND parent, HINSTANCE instance, int width, int height);
};
#endif

View file

@ -1,4 +1,4 @@
#include "Client/BrowserInPlace.h" #include "Client/Browser/BrowserInPlace.h"
// OFFSET: 0x00AB1AE0 // OFFSET: 0x00AB1AE0
// FUNCTION: fn_17382 // FUNCTION: fn_17382
@ -142,19 +142,15 @@ HRESULT STDMETHODCALLTYPE BrowserInPlaceSite::OnUIActivate() {
// OFFSET: 0x00AB1820 // OFFSET: 0x00AB1820
// FUNCTION: fn_17367 // FUNCTION: fn_17367
HRESULT STDMETHODCALLTYPE BrowserInPlaceSite::GetWindowContext( HRESULT STDMETHODCALLTYPE
IOleInPlaceFrame** frame, BrowserInPlaceSite::GetWindowContext(IOleInPlaceFrame** frame, IOleInPlaceUIWindow** document, RECT*, RECT*, OLEINPLACEFRAMEINFO* info) {
IOleInPlaceUIWindow** document,
RECT*,
RECT*,
OLEINPLACEFRAMEINFO* info
) {
*frame = &this->m_frame; *frame = &this->m_frame;
*document = nullptr; *document = nullptr;
info->fMDIApp = FALSE; info->fMDIApp = FALSE;
info->hwndFrame = this->m_window; info->hwndFrame = this->m_window;
info->haccel = nullptr; info->haccel = nullptr;
info->cAccelEntries = 0; info->cAccelEntries = 0;
return S_OK; return S_OK;
} }
@ -191,8 +187,6 @@ HRESULT STDMETHODCALLTYPE BrowserInPlaceSite::DeactivateAndUndo() {
// OFFSET: 0x00AB19F0 // OFFSET: 0x00AB19F0
// FUNCTION: fn_17375 // FUNCTION: fn_17375
HRESULT STDMETHODCALLTYPE BrowserInPlaceSite::OnPosRectChange(const RECT* position) { HRESULT STDMETHODCALLTYPE BrowserInPlaceSite::OnPosRectChange(const RECT* position) {
// The original requires an attached browser and retains this IOleObject
// reference, including when its subsequent in-place QueryInterface fails.
IOleObject* object; IOleObject* object;
HRESULT result = this->m_browser->QueryInterface(IID_IOleObject, reinterpret_cast<void**>(&object)); HRESULT result = this->m_browser->QueryInterface(IID_IOleObject, reinterpret_cast<void**>(&object));
if (SUCCEEDED(result)) { if (SUCCEEDED(result)) {

View file

@ -1,13 +1,9 @@
#ifndef RAGNAROK_SOURCE_CLIENT_BROWSERINPLACE_H #ifndef RAGNAROK_SOURCE_CLIENT_BROWSER_BROWSERINPLACE_H
#define RAGNAROK_SOURCE_CLIENT_BROWSERINPLACE_H #define RAGNAROK_SOURCE_CLIENT_BROWSER_BROWSERINPLACE_H
// clang-format off
#include <windows.h> #include <windows.h>
#include <ole2.h> #include <ole2.h>
// clang-format on
// Site-owned tear-offs: borrowed identity, HWND and browser, with fixed-one
// reference counts. These interfaces do not own or extend the site's lifetime.
class BrowserInPlaceFrame final : public IOleInPlaceFrame { class BrowserInPlaceFrame final : public IOleInPlaceFrame {
public: public:
BrowserInPlaceFrame(IUnknown& site, HWND const& window) : m_site(site), m_window(window) { BrowserInPlaceFrame(IUnknown& site, HWND const& window) : m_site(site), m_window(window) {
@ -36,8 +32,7 @@ private:
class BrowserInPlaceSite final : public IOleInPlaceSite { class BrowserInPlaceSite final : public IOleInPlaceSite {
public: public:
BrowserInPlaceSite(IUnknown& site, HWND const& window, IUnknown* const& browser) BrowserInPlaceSite(IUnknown& site, HWND const& window, IUnknown* const& browser) : m_site(site), m_window(window), m_browser(browser), m_frame(site, window) {
: m_site(site), m_window(window), m_browser(browser), m_frame(site, window) {
} }
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void** output) override; HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void** output) override;
@ -48,13 +43,8 @@ public:
HRESULT STDMETHODCALLTYPE CanInPlaceActivate() override; HRESULT STDMETHODCALLTYPE CanInPlaceActivate() override;
HRESULT STDMETHODCALLTYPE OnInPlaceActivate() override; HRESULT STDMETHODCALLTYPE OnInPlaceActivate() override;
HRESULT STDMETHODCALLTYPE OnUIActivate() override; HRESULT STDMETHODCALLTYPE OnUIActivate() override;
HRESULT STDMETHODCALLTYPE GetWindowContext( HRESULT STDMETHODCALLTYPE
IOleInPlaceFrame** frame, GetWindowContext(IOleInPlaceFrame** frame, IOleInPlaceUIWindow** document, RECT* position, RECT* clip, OLEINPLACEFRAMEINFO* info) override;
IOleInPlaceUIWindow** document,
RECT* position,
RECT* clip,
OLEINPLACEFRAMEINFO* info
) override;
HRESULT STDMETHODCALLTYPE Scroll(SIZE extent) override; HRESULT STDMETHODCALLTYPE Scroll(SIZE extent) override;
HRESULT STDMETHODCALLTYPE OnUIDeactivate(BOOL undoable) override; HRESULT STDMETHODCALLTYPE OnUIDeactivate(BOOL undoable) override;
HRESULT STDMETHODCALLTYPE OnInPlaceDeactivate() override; HRESULT STDMETHODCALLTYPE OnInPlaceDeactivate() override;

View file

@ -1,17 +1,17 @@
#include "Client/ApplicationActivation.h" #include "Client/Application/ApplicationActivation.h"
#include "Client/BrowserInputHandoff.h" #include "Client/Browser/BrowserInputHandoff.h"
#include "Client/Mouse.h" #include "Client/Input/Mouse.h"
#include "Contents/ServiceOptions.h" #include "Contents/Configuration/ServiceOptions.h"
// OFFSET: 0x00AB2720 // OFFSET: 0x00AB2720
// FUNCTION: fn_45433 // FUNCTION: fn_45433
void BrowserInputHandoff::Enter() { void BrowserInputHandoff::Enter() {
if (this->service.Get(271) == 0) { if (this->service.Get(ServiceOption::ConfineCursor) == 0) {
return; return;
} }
POINT position{this->mouse.x, this->mouse.y}; POINT position{this->mouse.x, this->mouse.y};
this->service.Set(271, 0); this->service.Set(ServiceOption::ConfineCursor, 0);
this->mouse.SetX(position.x); this->mouse.SetX(position.x);
this->mouse.SetY(position.y); this->mouse.SetY(position.y);
this->restoreOption = 1; this->restoreOption = 1;
@ -29,7 +29,7 @@ void BrowserInputHandoff::Restore() {
POINT position; POINT position;
GetCursorPos(&position); GetCursorPos(&position);
ScreenToClient(this->applicationWindow, &position); ScreenToClient(this->applicationWindow, &position);
this->service.Set(271, 1); this->service.Set(ServiceOption::ConfineCursor, 1);
this->mouse.SetX(position.x); this->mouse.SetX(position.x);
this->mouse.SetY(position.y); this->mouse.SetY(position.y);
SetActiveWindow(this->applicationWindow); SetActiveWindow(this->applicationWindow);

View file

@ -0,0 +1,21 @@
#ifndef RAGNAROK_SOURCE_CLIENT_BROWSER_BROWSERINPUTHANDOFF_H
#define RAGNAROK_SOURCE_CLIENT_BROWSER_BROWSERINPUTHANDOFF_H
#include <windows.h>
class CMouse;
struct ServiceOptions;
struct ApplicationActivation;
struct BrowserInputHandoff {
unsigned char& restoreOption;
HWND const& applicationWindow;
ServiceOptions& service;
CMouse& mouse;
ApplicationActivation& activation;
void Enter();
void Restore();
};
#endif

View file

@ -1,4 +1,4 @@
#include "Client/BrowserInterface.h" #include "Client/Browser/BrowserInterface.h"
#include <memory> #include <memory>
#include <ocidl.h> #include <ocidl.h>
@ -6,19 +6,17 @@
// OFFSET: 0x00AB2CD0 // OFFSET: 0x00AB2CD0
// FUNCTION: fn_45437 // FUNCTION: fn_45437
bool CreateBrowserInterface(IWebBrowser2*& browser, HWND& window) { bool CreateBrowserInterface(IWebBrowser2*& browser, HWND& window) {
const HRESULT result = CoCreateInstance( const HRESULT result =
CLSID_WebBrowser, nullptr, CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER, IID_IWebBrowser2, CoCreateInstance(CLSID_WebBrowser, nullptr, CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER, IID_IWebBrowser2, reinterpret_cast<void**>(&browser));
reinterpret_cast<void**>(&browser)
);
if (FAILED(result)) { if (FAILED(result)) {
OutputDebugStringA("error : MakeWebBrowserInterface_InProc FAILED \n"); OutputDebugStringA("error : MakeWebBrowserInterface_InProc FAILED \n");
HLOCAL message = nullptr; HLOCAL message = nullptr;
FormatMessageA( FormatMessageA(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM, nullptr, FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM, nullptr, GetLastError(), 0,
GetLastError(), 0, reinterpret_cast<char*>(&message), 0, nullptr reinterpret_cast<char*>(&message), 0, nullptr
); );
// The reference locks and reports this buffer without freeing it.
OutputDebugStringA(static_cast<const char*>(LocalLock(message))); OutputDebugStringA(static_cast<const char*>(LocalLock(message)));
return false; return false;
} }
@ -33,18 +31,17 @@ bool CreateBrowserInterface(IWebBrowser2*& browser, HWND& window) {
// OFFSET: 0x00AB2C10 // OFFSET: 0x00AB2C10
// FUNCTION: fn_45436 // FUNCTION: fn_45436
bool CreateExternalBrowserInterface(IWebBrowser2*& browser, HWND& window, HWND const& host) { bool CreateExternalBrowserInterface(IWebBrowser2*& browser, HWND& window, HWND const& host) {
const HRESULT result = CoCreateInstance( const HRESULT result = CoCreateInstance(CLSID_InternetExplorer, nullptr, CLSCTX_LOCAL_SERVER, IID_IWebBrowser2, reinterpret_cast<void**>(&browser));
CLSID_InternetExplorer, nullptr, CLSCTX_LOCAL_SERVER, IID_IWebBrowser2, reinterpret_cast<void**>(&browser)
);
if (FAILED(result)) { if (FAILED(result)) {
OutputDebugStringA("error : MakeWebBrowserInterface FAILED \n"); OutputDebugStringA("error : MakeWebBrowserInterface FAILED \n");
HLOCAL message = nullptr; HLOCAL message = nullptr;
FormatMessageA( FormatMessageA(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM, nullptr, FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM, nullptr, GetLastError(), 0,
GetLastError(), 0, reinterpret_cast<char*>(&message), 0, nullptr reinterpret_cast<char*>(&message), 0, nullptr
); );
OutputDebugStringA(static_cast<const char*>(LocalLock(message))); OutputDebugStringA(static_cast<const char*>(LocalLock(message)));
return false; return false;
} }
@ -103,6 +100,7 @@ HRESULT AdviseBrowserEvents(IUnknown* source, IUnknown* sink, REFIID events, DWO
IConnectionPointContainer* container = nullptr; IConnectionPointContainer* container = nullptr;
IConnectionPoint* point = nullptr; IConnectionPoint* point = nullptr;
HRESULT result = source->QueryInterface(IID_IConnectionPointContainer, reinterpret_cast<void**>(&container)); HRESULT result = source->QueryInterface(IID_IConnectionPointContainer, reinterpret_cast<void**>(&container));
if (SUCCEEDED(result)) { if (SUCCEEDED(result)) {
result = container->FindConnectionPoint(events, &point); result = container->FindConnectionPoint(events, &point);
if (SUCCEEDED(result)) { if (SUCCEEDED(result)) {
@ -113,9 +111,11 @@ HRESULT AdviseBrowserEvents(IUnknown* source, IUnknown* sink, REFIID events, DWO
if (point) { if (point) {
point->Release(); point->Release();
} }
if (container) { if (container) {
container->Release(); container->Release();
} }
return result; return result;
} }
@ -129,6 +129,7 @@ HRESULT UnadviseBrowserEvents(IUnknown* source, REFIID events, DWORD cookie) {
IConnectionPointContainer* container = nullptr; IConnectionPointContainer* container = nullptr;
IConnectionPoint* point = nullptr; IConnectionPoint* point = nullptr;
HRESULT result = source->QueryInterface(IID_IConnectionPointContainer, reinterpret_cast<void**>(&container)); HRESULT result = source->QueryInterface(IID_IConnectionPointContainer, reinterpret_cast<void**>(&container));
if (SUCCEEDED(result)) { if (SUCCEEDED(result)) {
result = container->FindConnectionPoint(events, &point); result = container->FindConnectionPoint(events, &point);
if (SUCCEEDED(result)) { if (SUCCEEDED(result)) {
@ -139,8 +140,10 @@ HRESULT UnadviseBrowserEvents(IUnknown* source, REFIID events, DWORD cookie) {
if (point) { if (point) {
point->Release(); point->Release();
} }
if (container) { if (container) {
container->Release(); container->Release();
} }
return result; return result;
} }

View file

@ -0,0 +1,19 @@
#ifndef RAGNAROK_SOURCE_CLIENT_BROWSER_BROWSERINTERFACE_H
#define RAGNAROK_SOURCE_CLIENT_BROWSER_BROWSERINTERFACE_H
#include <windows.h>
#include <ole2.h>
#include <exdisp.h>
#include <string>
bool CreateBrowserInterface(IWebBrowser2*& browser, HWND& window);
bool CreateExternalBrowserInterface(IWebBrowser2*& browser, HWND& window, HWND const& host);
bool NavigateBrowser(IWebBrowser2* const& browser, HWND const& host, HWND const& title, std::string address);
HRESULT AdviseBrowserEvents(IUnknown* source, IUnknown* sink, REFIID events, DWORD* cookie);
HRESULT UnadviseBrowserEvents(IUnknown* source, REFIID events, DWORD cookie);
#endif

View file

@ -1,6 +1,6 @@
#include "Client/BrowserMessages.h" #include "Client/Browser/BrowserMessages.h"
#include "Client/BrowserRegistry.h" #include "Client/Browser/BrowserRegistry.h"
#include "Client/WebBrowser.h" #include "Client/Browser/WebBrowser.h"
#include <cstdint> #include <cstdint>
@ -36,7 +36,7 @@ LRESULT BrowserMessages::Message(HWND window, UINT message, WPARAM value, LPARAM
} }
break; break;
case WM_COMMAND: case WM_COMMAND:
if (static_cast<std::uint16_t>(value) == 1000 && browser) { if (LOWORD(value) == static_cast<WORD>(BrowserCommand::Navigate) && browser) {
char address[512]; char address[512];
GetWindowTextA(browser->combo, address, sizeof(address)); GetWindowTextA(browser->combo, address, sizeof(address));
browser->Navigate(address); browser->Navigate(address);

View file

@ -1,13 +1,13 @@
#ifndef RAGNAROK_SOURCE_CLIENT_BROWSERMESSAGES_H #ifndef RAGNAROK_SOURCE_CLIENT_BROWSER_BROWSERMESSAGES_H
#define RAGNAROK_SOURCE_CLIENT_BROWSERMESSAGES_H #define RAGNAROK_SOURCE_CLIENT_BROWSER_BROWSERMESSAGES_H
#include <windows.h> #include <windows.h>
class CWebBrowser; class CWebBrowser;
struct BrowserRegistry; struct BrowserRegistry;
// Borrowed UIWindowMgr browser registry, active browser and main HWND. enum class BrowserCommand : WORD { Navigate = 1000 };
// Caller installs its native thunk; this implements the entire original body.
struct BrowserMessages { struct BrowserMessages {
BrowserRegistry& registry; BrowserRegistry& registry;
CWebBrowser*& active; CWebBrowser*& active;

View file

@ -1,10 +1,11 @@
#include "Client/WebBrowser.h" #include "Client/Browser/WebBrowser.h"
#include "UI/BrowserFactory.h" #include "Client/Browser/BrowserFactory.h"
#include "UI/BrowserPopupDispatch.h" #include "Client/Browser/BrowserPopupDispatch.h"
#include "Utility/Math/ScalarConversion.h"
#include <exdispid.h>
#include <cwchar> #include <cwchar>
#include <memory> #include <memory>
#include <xmmintrin.h>
// OFFSET: 0x00AB1AA0 // OFFSET: 0x00AB1AA0
// FUNCTION: fn_17380 // FUNCTION: fn_17380
@ -44,23 +45,22 @@ HRESULT STDMETHODCALLTYPE BrowserPopupDispatch::GetIDsOfNames(REFIID, LPOLESTR*,
// OFFSET: 0x00AB1880 // OFFSET: 0x00AB1880
// FUNCTION: fn_17370 // FUNCTION: fn_17370
HRESULT STDMETHODCALLTYPE HRESULT STDMETHODCALLTYPE BrowserPopupDispatch::Invoke(DISPID id, REFIID, LCID, WORD, DISPPARAMS* args, VARIANT*, EXCEPINFO*, UINT*) {
BrowserPopupDispatch::Invoke(DISPID id, REFIID, LCID, WORD, DISPPARAMS* args, VARIANT*, EXCEPINFO*, UINT*) { if (id != DISPID_NEWWINDOW3) {
if (id != 273) {
return S_OK; return S_OK;
} }
const wchar_t* url = args->rgvarg[0].bstrVal; const wchar_t* url = args->rgvarg[0].bstrVal;
// Original writes literal 11 through Cancel's by-reference boolean. Preserve
// that noncanonical true value, and write it before the factory callback.
*args->rgvarg[3].pboolVal = 11; *args->rgvarg[3].pboolVal = 11;
// STATIC: 0x01115448 // STATIC: 0x01115448
// GLOBAL: gl_13426 // GLOBAL: gl_13426
const __m128 scale = _mm_set_ss(0.7f); const float scale = 0.7f;
const int height = _mm_cvtt_ss2si(_mm_mul_ss(_mm_cvtsi32_ss(_mm_setzero_ps(), this->m_height), scale)); const int height = TruncateToInt(static_cast<float>(this->m_height) * scale);
const int width = _mm_cvtt_ss2si(_mm_mul_ss(_mm_cvtsi32_ss(_mm_setzero_ps(), this->m_width), scale)); const int width = TruncateToInt(static_cast<float>(this->m_width) * scale);
CWebBrowser* browser = this->m_factory.Create(width, height); CWebBrowser* browser = this->m_factory.Create(width, height);
if (!browser) { if (!browser) {
return S_OK; return S_OK;
} }
@ -70,5 +70,6 @@ BrowserPopupDispatch::Invoke(DISPID id, REFIID, LCID, WORD, DISPPARAMS* args, VA
WideCharToMultiByte(CP_ACP, 0, url, -1, converted.get(), static_cast<int>(length), nullptr, nullptr); WideCharToMultiByte(CP_ACP, 0, url, -1, converted.get(), static_cast<int>(length), nullptr, nullptr);
converted[length] = '\0'; converted[length] = '\0';
browser->Navigate(converted.get()); browser->Navigate(converted.get());
return S_OK; return S_OK;
} }

View file

@ -1,16 +1,11 @@
#ifndef RAGNAROK_SOURCE_UI_BROWSERPOPUPDISPATCH_H #ifndef RAGNAROK_SOURCE_CLIENT_BROWSER_BROWSERPOPUPDISPATCH_H
#define RAGNAROK_SOURCE_UI_BROWSERPOPUPDISPATCH_H #define RAGNAROK_SOURCE_CLIENT_BROWSER_BROWSERPOPUPDISPATCH_H
// clang-format off
#include <windows.h> #include <windows.h>
#include <ole2.h> #include <ole2.h>
// clang-format on
struct BrowserFactory; struct BrowserFactory;
// Site-owned dispatch tear-off. Identity and renderer dimensions are borrowed.
// Fixed-one reference methods match the other site tear-offs, not
// CWebEventReceiver's independently reference-counted lifetime.
class BrowserPopupDispatch final : public IDispatch { class BrowserPopupDispatch final : public IDispatch {
public: public:
BrowserPopupDispatch(IUnknown& site, BrowserFactory& factory, const int& width, const int& height) BrowserPopupDispatch(IUnknown& site, BrowserFactory& factory, const int& width, const int& height)
@ -23,16 +18,8 @@ public:
HRESULT STDMETHODCALLTYPE GetTypeInfoCount(UINT* count) override; HRESULT STDMETHODCALLTYPE GetTypeInfoCount(UINT* count) override;
HRESULT STDMETHODCALLTYPE GetTypeInfo(UINT index, LCID locale, ITypeInfo** output) override; HRESULT STDMETHODCALLTYPE GetTypeInfo(UINT index, LCID locale, ITypeInfo** output) override;
HRESULT STDMETHODCALLTYPE GetIDsOfNames(REFIID iid, LPOLESTR* names, UINT count, LCID locale, DISPID* ids) override; HRESULT STDMETHODCALLTYPE GetIDsOfNames(REFIID iid, LPOLESTR* names, UINT count, LCID locale, DISPID* ids) override;
HRESULT STDMETHODCALLTYPE Invoke( HRESULT STDMETHODCALLTYPE
DISPID id, Invoke(DISPID id, REFIID iid, LCID locale, WORD flags, DISPPARAMS* args, VARIANT* result, EXCEPINFO* error, UINT* argumentError) override;
REFIID iid,
LCID locale,
WORD flags,
DISPPARAMS* args,
VARIANT* result,
EXCEPINFO* error,
UINT* argumentError
) override;
private: private:
IUnknown& m_site; IUnknown& m_site;

View file

@ -1,5 +1,5 @@
#include "Client/BrowserRegistry.h" #include "Client/Browser/BrowserRegistry.h"
#include "Client/WebBrowser.h" #include "Client/Browser/WebBrowser.h"
#include <new> #include <new>
@ -28,8 +28,6 @@ void BrowserRegistry::Remove(HWND window) {
return; return;
} }
// The reference dereferences the special browser on this miss path before
// checking it; a nonnull special owner is a caller precondition here.
if (window == this->special->host) { if (window == this->special->host) {
delete this->special; delete this->special;
this->special = nullptr; this->special = nullptr;

View file

@ -1,5 +1,5 @@
#ifndef RAGNAROK_SOURCE_CLIENT_BROWSERREGISTRY_H #ifndef RAGNAROK_SOURCE_CLIENT_BROWSER_BROWSERREGISTRY_H
#define RAGNAROK_SOURCE_CLIENT_BROWSERREGISTRY_H #define RAGNAROK_SOURCE_CLIENT_BROWSER_BROWSERREGISTRY_H
#include <map> #include <map>
#include <windows.h> #include <windows.h>
@ -7,9 +7,6 @@
class CWebBrowser; class CWebBrowser;
struct WebBrowserContext; struct WebBrowserContext;
// Borrowed UIWindowMgr browser map and special-browser pointer.
// Map nodes survive browser destruction, permitting native callbacks to look up
// the original entry. The special alias is cleared before mapped destruction.
struct BrowserRegistry { struct BrowserRegistry {
std::map<HWND, CWebBrowser*>& browsers; std::map<HWND, CWebBrowser*>& browsers;
CWebBrowser*& special; CWebBrowser*& special;

View file

@ -1,5 +1,5 @@
#include "Client/BrowserInterface.h" #include "Client/Browser/BrowserInterface.h"
#include "Client/BrowserSiteAttachment.h" #include "Client/Browser/BrowserSiteAttachment.h"
#include <cstdint> #include <cstdint>
#include <new> #include <new>
@ -7,8 +7,7 @@
// OFFSET: 0x00AB2AB0 // OFFSET: 0x00AB2AB0
// FUNCTION: fn_17417 // FUNCTION: fn_17417
bool BrowserSiteAttachment::Attach() { bool BrowserSiteAttachment::Attach() {
this->site = new (std::nothrow) this->site = new (std::nothrow) BrowserClientSite(this->host, *this->sizing.browser, this->factory, this->renderWidth, this->renderHeight);
BrowserClientSite(this->host, *this->sizing.browser, this->factory, this->renderWidth, this->renderHeight);
IOleObject* object; IOleObject* object;
HRESULT result = this->sizing.browser->QueryInterface(IID_IOleObject, reinterpret_cast<void**>(&object)); HRESULT result = this->sizing.browser->QueryInterface(IID_IOleObject, reinterpret_cast<void**>(&object));
@ -22,6 +21,7 @@ bool BrowserSiteAttachment::Attach() {
RECT rectangle; RECT rectangle;
GetClientRect(this->host, &rectangle); GetClientRect(this->host, &rectangle);
result = object->DoVerb(OLEIVERB_INPLACEACTIVATE, nullptr, this->site, 0, this->host, &rectangle); result = object->DoVerb(OLEIVERB_INPLACEACTIVATE, nullptr, this->site, 0, this->host, &rectangle);
if (SUCCEEDED(result)) { if (SUCCEEDED(result)) {
const int width = rectangle.right - rectangle.left; const int width = rectangle.right - rectangle.left;
const int height = rectangle.bottom - rectangle.top; const int height = rectangle.bottom - rectangle.top;

View file

@ -0,0 +1,20 @@
#ifndef RAGNAROK_SOURCE_CLIENT_BROWSER_BROWSERSITEATTACHMENT_H
#define RAGNAROK_SOURCE_CLIENT_BROWSER_BROWSERSITEATTACHMENT_H
#include "Client/Browser/BrowserClientSite.h"
#include "Client/Browser/BrowserWindowSizing.h"
struct BrowserSiteAttachment {
BrowserWindowSizing& sizing;
HWND const& host;
BrowserClientSite*& site;
DWORD& cookie;
BrowserFactory& factory;
const int& renderWidth;
const int& renderHeight;
bool Attach();
void Detach();
};
#endif

View file

@ -1,4 +1,4 @@
#include "Client/BrowserWindowSizing.h" #include "Client/Browser/BrowserWindowSizing.h"
// OFFSET: 0x00AB3060 // OFFSET: 0x00AB3060
// FUNCTION: fn_17421 // FUNCTION: fn_17421
@ -67,6 +67,7 @@ void BrowserAuxiliaryVisibility::Show(unsigned char value) {
GetClientRect(this->sizing.auxiliary, &rectangle); GetClientRect(this->sizing.auxiliary, &rectangle);
top = rectangle.bottom - rectangle.top + 4; top = rectangle.bottom - rectangle.top + 4;
} }
this->sizing.browser->put_Top(top); this->sizing.browser->put_Top(top);
RECT rectangle; RECT rectangle;

View file

@ -1,14 +1,10 @@
#ifndef RAGNAROK_SOURCE_CLIENT_BROWSERWINDOWSIZING_H #ifndef RAGNAROK_SOURCE_CLIENT_BROWSER_BROWSERWINDOWSIZING_H
#define RAGNAROK_SOURCE_CLIENT_BROWSERWINDOWSIZING_H #define RAGNAROK_SOURCE_CLIENT_BROWSER_BROWSERWINDOWSIZING_H
// clang-format off
#include <windows.h> #include <windows.h>
#include <ole2.h> #include <ole2.h>
#include <exdisp.h> #include <exdisp.h>
// clang-format on
// Borrowed IWebBrowser2 and original browser-owner dimensions. COM lifetime,
// activation and navigation are separate; this view owns no references.
struct BrowserWindowSizing { struct BrowserWindowSizing {
IWebBrowser2* const& browser; IWebBrowser2* const& browser;
const int& screenWidth; const int& screenWidth;
@ -18,13 +14,10 @@ struct BrowserWindowSizing {
int& width; int& width;
int& height; int& height;
// Exact screen dimensions are reduced by one, then optional auxiliary
// client height plus four is removed. Native setter errors are ignored.
void Resize(int requestedWidth, int requestedHeight); void Resize(int requestedWidth, int requestedHeight);
void Refresh(); void Refresh();
}; };
// Browser host-site presence, host HWND and auxiliary-visible byte.
struct BrowserAuxiliaryVisibility { struct BrowserAuxiliaryVisibility {
BrowserWindowSizing& sizing; BrowserWindowSizing& sizing;
const bool& hasSite; const bool& hasSite;

View file

@ -1,31 +1,27 @@
#include "Client/BrowserClose.h" #include "Client/Browser/BrowserClose.h"
#include "Client/BrowserCreation.h" #include "Client/Browser/BrowserCreation.h"
#include "Client/BrowserFrame.h" #include "Client/Browser/BrowserFrame.h"
#include "Client/BrowserInputHandoff.h" #include "Client/Browser/BrowserInputHandoff.h"
#include "Client/WebBrowser.h" #include "Client/Browser/WebBrowser.h"
#include "Client/WebEventReceiver.h" #include "Client/Browser/WebEventReceiver.h"
#include <new> #include <new>
#include <utility> #include <utility>
namespace { namespace {
BrowserWindowSizing Sizing(CWebBrowser& owner) { BrowserWindowSizing Sizing(CWebBrowser& owner) {
return {owner.browser, owner.screenWidth, owner.screenHeight, owner.rebar, return {owner.browser, owner.screenWidth, owner.screenHeight, owner.rebar, owner.auxiliaryVisible, owner.width, owner.height};
owner.auxiliaryVisible, owner.width, owner.height};
} }
} }
// OFFSET: 0x00AB1DE0 // OFFSET: 0x00AB1DE0
// FUNCTION: fn_17409 // FUNCTION: fn_17409
CWebBrowser::CWebBrowser(WebBrowserContext& context) CWebBrowser::CWebBrowser(WebBrowserContext& context)
: browser(nullptr), externalEvents(nullptr), site(nullptr), host(nullptr), browserWindow(nullptr), cursorState(0), : browser(nullptr), externalEvents(nullptr), site(nullptr), host(nullptr), browserWindow(nullptr), cursorState(0), auxiliaryVisible(0),
auxiliaryVisible(0), screenWidth(GetSystemMetrics(SM_CXSCREEN)), screenHeight(GetSystemMetrics(SM_CYSCREEN)), screenWidth(GetSystemMetrics(SM_CXSCREEN)), screenHeight(GetSystemMetrics(SM_CYSCREEN)), cookie(0), rebar(nullptr), combo(nullptr), barHeight(0),
cookie(0), rebar(nullptr), combo(nullptr), barHeight(0), width(0), height(0), restoreOption(0), width(0), height(0), restoreOption(0), context(&context) {
context(&context) {
OutputDebugStringA("CWebBrowser() \n"); OutputDebugStringA("CWebBrowser() \n");
BrowserInputHandoff handoff{ BrowserInputHandoff handoff{this->restoreOption, context.applicationWindow, context.service, context.mouse, context.activation};
this->restoreOption, context.applicationWindow, context.service, context.mouse, context.activation
};
handoff.Enter(); handoff.Enter();
} }
@ -34,10 +30,7 @@ CWebBrowser::CWebBrowser(WebBrowserContext& context)
CWebBrowser::~CWebBrowser() { CWebBrowser::~CWebBrowser() {
OutputDebugStringA("~CWebBrowser() \n"); OutputDebugStringA("~CWebBrowser() \n");
auto& dependencies = *this->context; auto& dependencies = *this->context;
BrowserInputHandoff handoff{ BrowserInputHandoff handoff{this->restoreOption, dependencies.applicationWindow, dependencies.service, dependencies.mouse, dependencies.activation};
this->restoreOption, dependencies.applicationWindow, dependencies.service, dependencies.mouse,
dependencies.activation
};
handoff.Restore(); handoff.Restore();
this->Close(); this->Close();
} }
@ -49,24 +42,13 @@ bool CWebBrowser::Create(int requestedWidth, int requestedHeight) {
auto sizing = Sizing(*this); auto sizing = Sizing(*this);
BrowserHostWindow window{this->host, this->browserWindow, dependencies.title, dependencies.hostProcedure}; BrowserHostWindow window{this->host, this->browserWindow, dependencies.title, dependencies.hostProcedure};
BrowserSiteAttachment attachment{ BrowserSiteAttachment attachment{
sizing, sizing, this->host, this->site, this->cookie, dependencies.popupFactory, dependencies.renderWidth, dependencies.renderHeight
this->host,
this->site,
this->cookie,
dependencies.popupFactory,
dependencies.renderWidth,
dependencies.renderHeight
}; };
BrowserAddressBar addressBar{ BrowserAddressBar addressBar{
this->host, this->host, this->rebar, this->combo, this->barHeight, this->auxiliaryVisible, dependencies.previousEditProcedure, dependencies.editProcedure
this->rebar,
this->combo,
this->barHeight,
this->auxiliaryVisible,
dependencies.previousEditProcedure,
dependencies.editProcedure
}; };
BrowserCreation creation{this->browser, this->browserWindow, window, attachment, addressBar}; BrowserCreation creation{this->browser, this->browserWindow, window, attachment, addressBar};
return creation.Create(dependencies.applicationWindow, dependencies.instance, requestedWidth, requestedHeight); return creation.Create(dependencies.applicationWindow, dependencies.instance, requestedWidth, requestedHeight);
} }
@ -76,17 +58,11 @@ bool CWebBrowser::Close() {
auto& dependencies = *this->context; auto& dependencies = *this->context;
auto sizing = Sizing(*this); auto sizing = Sizing(*this);
BrowserSiteAttachment attachment{ BrowserSiteAttachment attachment{
sizing, sizing, this->host, this->site, this->cookie, dependencies.popupFactory, dependencies.renderWidth, dependencies.renderHeight
this->host,
this->site,
this->cookie,
dependencies.popupFactory,
dependencies.renderWidth,
dependencies.renderHeight
}; };
BrowserClose operation{attachment, this->browser, this->externalEvents, BrowserClose operation{attachment, this->browser, this->externalEvents, this->host, this->browserWindow, dependencies.applicationWindow,
this->host, this->browserWindow, dependencies.applicationWindow,
this->cursorState, dependencies.mouse}; this->cursorState, dependencies.mouse};
return operation.Close(); return operation.Close();
} }
@ -96,6 +72,7 @@ bool CWebBrowser::AttachExternalEvents(BrowserRegistry& registry) {
auto* receiver = new (std::nothrow) CWebEventReceiver(registry); auto* receiver = new (std::nothrow) CWebEventReceiver(registry);
this->externalEvents = receiver; this->externalEvents = receiver;
receiver->AddRef(); receiver->AddRef();
return receiver->Register(this->browser, this->host); return receiver->Register(this->browser, this->host);
} }
@ -111,9 +88,7 @@ void CWebBrowser::Resize(int requestedWidth, int requestedHeight) {
void CWebBrowser::Poll() { void CWebBrowser::Poll() {
auto& dependencies = *this->context; auto& dependencies = *this->context;
auto sizing = Sizing(*this); auto sizing = Sizing(*this);
BrowserCursor cursor{ BrowserCursor cursor{this->cursorState, dependencies.mouse, dependencies.renderSystem, dependencies.applicationWindow};
this->cursorState, dependencies.mouse, dependencies.renderSystem, dependencies.applicationWindow
};
const bool hasExternalEvents = this->externalEvents != nullptr; const bool hasExternalEvents = this->externalEvents != nullptr;
BrowserFrame frame{sizing, cursor, this->host, this->browserWindow, hasExternalEvents}; BrowserFrame frame{sizing, cursor, this->host, this->browserWindow, hasExternalEvents};
frame.Poll(); frame.Poll();
@ -129,7 +104,7 @@ bool CWebBrowser::Navigate(std::string address) {
// FUNCTION: fn_17422 // FUNCTION: fn_17422
void CWebBrowser::ShowAuxiliary(unsigned char value) { void CWebBrowser::ShowAuxiliary(unsigned char value) {
auto sizing = Sizing(*this); auto sizing = Sizing(*this);
// The original gate is external-event receiver presence at +4, not site +8.
const bool hasExternalEvents = this->externalEvents != nullptr; const bool hasExternalEvents = this->externalEvents != nullptr;
BrowserAuxiliaryVisibility visibility{sizing, hasExternalEvents, this->host, this->auxiliaryVisible}; BrowserAuxiliaryVisibility visibility{sizing, hasExternalEvents, this->host, this->auxiliaryVisible};
visibility.Show(value); visibility.Show(value);

View file

@ -1,7 +1,7 @@
#ifndef RAGNAROK_SOURCE_CLIENT_WEBBROWSER_H #ifndef RAGNAROK_SOURCE_CLIENT_BROWSER_WEBBROWSER_H
#define RAGNAROK_SOURCE_CLIENT_WEBBROWSER_H #define RAGNAROK_SOURCE_CLIENT_BROWSER_WEBBROWSER_H
#include "Client/BrowserInterface.h" #include "Client/Browser/BrowserInterface.h"
#include <cstddef> #include <cstddef>
@ -11,13 +11,8 @@ class CRenderSystemBase;
struct ServiceOptions; struct ServiceOptions;
struct BrowserRegistry; struct BrowserRegistry;
struct BrowserFactory; struct BrowserFactory;
namespace client {
struct ApplicationActivation; struct ApplicationActivation;
}
// Borrowed application globals, concrete popup factory and native procedures.
// All must outlive the browser. Renderer dimensions retain their original
// separately owned globals rather than querying virtuals during popup dispatch.
struct WebBrowserContext { struct WebBrowserContext {
HWND const& applicationWindow; HWND const& applicationWindow;
HINSTANCE const& instance; HINSTANCE const& instance;
@ -25,7 +20,7 @@ struct WebBrowserContext {
CMouse& mouse; CMouse& mouse;
CRenderSystemBase& renderSystem; CRenderSystemBase& renderSystem;
ServiceOptions& service; ServiceOptions& service;
client::ApplicationActivation& activation; ApplicationActivation& activation;
BrowserFactory& popupFactory; BrowserFactory& popupFactory;
const int& renderWidth; const int& renderWidth;
const int& renderHeight; const int& renderHeight;
@ -34,8 +29,6 @@ struct WebBrowserContext {
WNDPROC& previousEditProcedure; WNDPROC& previousEditProcedure;
}; };
// Original nonpolymorphic 64-byte CWebBrowser prefix. Context follows it as a
// host dependency view; no original executable addresses are used at runtime.
class CWebBrowser { class CWebBrowser {
public: public:
explicit CWebBrowser(WebBrowserContext& context); explicit CWebBrowser(WebBrowserContext& context);

View file

@ -1,13 +1,15 @@
#include "Client/BrowserRegistry.h" #include "Client/Browser/BrowserRegistry.h"
#include "Client/WebEventReceiver.h" #include "Client/Browser/WebEventReceiver.h"
#include <exdispid.h>
#include <mshtmdid.h>
#include <mshtml.h> #include <mshtml.h>
// OFFSET: 0x00AB3710 // OFFSET: 0x00AB3710
// FUNCTION: fn_17424 // FUNCTION: fn_17424
CWebEventReceiver::CWebEventReceiver(BrowserRegistry& registry) CWebEventReceiver::CWebEventReceiver(BrowserRegistry& registry)
: m_references(0), m_browser(nullptr), m_browserCookie(0), m_document(nullptr), m_documentCookie(0), : m_references(0), m_browser(nullptr), m_browserCookie(0), m_document(nullptr), m_documentCookie(0), m_host(nullptr), m_registry(registry) {
m_host(nullptr), m_registry(registry) {
OutputDebugStringA("CWebEventReceiver() \n"); OutputDebugStringA("CWebEventReceiver() \n");
} }
@ -22,6 +24,7 @@ bool CWebEventReceiver::Register(IWebBrowser2* browser, HWND host) {
this->m_browser = browser; this->m_browser = browser;
this->m_host = host; this->m_host = host;
this->m_browser->AddRef(); this->m_browser->AddRef();
return SUCCEEDED(AdviseBrowserEvents(this->m_browser, this, DIID_DWebBrowserEvents2, &this->m_browserCookie)); return SUCCEEDED(AdviseBrowserEvents(this->m_browser, this, DIID_DWebBrowserEvents2, &this->m_browserCookie));
} }
@ -98,20 +101,17 @@ HRESULT CWebEventReceiver::Disconnect() {
// OFFSET: 0x00AB3B20 // OFFSET: 0x00AB3B20
// FUNCTION: fn_17433 // FUNCTION: fn_17433
HRESULT STDMETHODCALLTYPE HRESULT STDMETHODCALLTYPE CWebEventReceiver::Invoke(DISPID id, REFIID, LCID, WORD, DISPPARAMS* args, VARIANT*, EXCEPINFO*, UINT*) {
CWebEventReceiver::Invoke(DISPID id, REFIID, LCID, WORD, DISPPARAMS* args, VARIANT*, EXCEPINFO*, UINT*) { if (id == DISPID_ONQUIT) {
if (id == 253) {
return this->Disconnect(); return this->Disconnect();
} }
if (id == 259) { if (id == DISPID_DOCUMENTCOMPLETE) {
IDispatch* browserIdentity = nullptr; IDispatch* browserIdentity = nullptr;
if (FAILED(this->m_browser->QueryInterface(IID_IDispatch, reinterpret_cast<void**>(&browserIdentity)))) { if (FAILED(this->m_browser->QueryInterface(IID_IDispatch, reinterpret_cast<void**>(&browserIdentity)))) {
return S_OK; return S_OK;
} }
// DocumentComplete's pDisp is the second reverse-order VARIANT. The
// reference retains browserIdentity on an identity mismatch.
if (args->rgvarg[1].pdispVal != browserIdentity) { if (args->rgvarg[1].pdispVal != browserIdentity) {
return S_OK; return S_OK;
} }
@ -125,7 +125,7 @@ CWebEventReceiver::Invoke(DISPID id, REFIID, LCID, WORD, DISPPARAMS* args, VARIA
return S_OK; return S_OK;
} }
if (id != -602 && id != static_cast<DISPID>(0x8001000A) && id != 1023) { if (id != DISPID_HTMLDOCUMENTEVENTS2_ONKEYDOWN && id != DISPID_HTMLDOCUMENTEVENTS2_ONHELP && id != DISPID_HTMLDOCUMENTEVENTS2_ONCONTEXTMENU) {
return S_OK; return S_OK;
} }
@ -134,26 +134,26 @@ CWebEventReceiver::Invoke(DISPID id, REFIID, LCID, WORD, DISPPARAMS* args, VARIA
return S_OK; return S_OK;
} }
if (id == -602) { if (id == DISPID_HTMLDOCUMENTEVENTS2_ONKEYDOWN) {
long key = 0; long key = 0;
if (FAILED(event->get_keyCode(&key))) { if (FAILED(event->get_keyCode(&key))) {
return S_OK; return S_OK;
} }
switch (key) { switch (key) {
case 18: case VK_MENU:
case 112: case VK_F1:
case 114: case VK_F3:
case 115: case VK_F4:
case 122: case VK_F11:
break; break;
default: default:
// Deliberately retained reference on the original non-filtered branch.
return S_OK; return S_OK;
} }
} }
if (id != 1023) { if (id != DISPID_HTMLDOCUMENTEVENTS2_ONCONTEXTMENU) {
event->put_keyCode(0); event->put_keyCode(0);
} }
@ -163,5 +163,6 @@ CWebEventReceiver::Invoke(DISPID id, REFIID, LCID, WORD, DISPPARAMS* args, VARIA
cancelled.boolVal = VARIANT_FALSE; cancelled.boolVal = VARIANT_FALSE;
event->put_returnValue(cancelled); event->put_returnValue(cancelled);
event->Release(); event->Release();
return id == -602 ? S_FALSE : S_OK;
return id == DISPID_HTMLDOCUMENTEVENTS2_ONKEYDOWN ? S_FALSE : S_OK;
} }

View file

@ -1,13 +1,10 @@
#ifndef RAGNAROK_SOURCE_CLIENT_WEBEVENTRECEIVER_H #ifndef RAGNAROK_SOURCE_CLIENT_BROWSER_WEBEVENTRECEIVER_H
#define RAGNAROK_SOURCE_CLIENT_WEBEVENTRECEIVER_H #define RAGNAROK_SOURCE_CLIENT_BROWSER_WEBEVENTRECEIVER_H
#include "Client/BrowserInterface.h" #include "Client/Browser/BrowserInterface.h"
struct BrowserRegistry; struct BrowserRegistry;
// Original receiver starts with zero references. Caller acquires its owning
// reference before registration. Destruction only reports diagnostics; explicit
// Disconnect performs COM and registry teardown, not the destructor.
class CWebEventReceiver final : public IDispatch { class CWebEventReceiver final : public IDispatch {
public: public:
explicit CWebEventReceiver(BrowserRegistry& registry); explicit CWebEventReceiver(BrowserRegistry& registry);
@ -20,16 +17,8 @@ public:
HRESULT STDMETHODCALLTYPE GetTypeInfoCount(UINT* count) override; HRESULT STDMETHODCALLTYPE GetTypeInfoCount(UINT* count) override;
HRESULT STDMETHODCALLTYPE GetTypeInfo(UINT index, LCID locale, ITypeInfo** output) override; HRESULT STDMETHODCALLTYPE GetTypeInfo(UINT index, LCID locale, ITypeInfo** output) override;
HRESULT STDMETHODCALLTYPE GetIDsOfNames(REFIID iid, LPOLESTR* names, UINT count, LCID locale, DISPID* ids) override; HRESULT STDMETHODCALLTYPE GetIDsOfNames(REFIID iid, LPOLESTR* names, UINT count, LCID locale, DISPID* ids) override;
HRESULT STDMETHODCALLTYPE Invoke( HRESULT STDMETHODCALLTYPE
DISPID id, Invoke(DISPID id, REFIID iid, LCID locale, WORD flags, DISPPARAMS* args, VARIANT* result, EXCEPINFO* error, UINT* argumentError) override;
REFIID iid,
LCID locale,
WORD flags,
DISPPARAMS* args,
VARIANT* result,
EXCEPINFO* error,
UINT* argumentError
) override;
private: private:
~CWebEventReceiver(); ~CWebEventReceiver();

View file

@ -1,23 +0,0 @@
#ifndef RAGNAROK_SOURCE_CLIENT_BROWSERADDRESSBAR_H
#define RAGNAROK_SOURCE_CLIENT_BROWSERADDRESSBAR_H
#include <windows.h>
// Borrowed host, rebar, combo, height, visible, and shared
// previous-edit-procedure global. Caller initializes common controls and supplies
// the actual BrowserAddressEdit thunk. Native verification requires v6 controls
// for the original 100-byte x86 band; startup activation policy is not recovered.
// Failed creation retains native children; normal host destruction owns them.
struct BrowserAddressBar {
HWND const& host;
HWND& rebar;
HWND& combo;
int& height;
const unsigned char& visible;
WNDPROC& previous;
WNDPROC procedure;
bool Create(int width, int height);
};
#endif

View file

@ -1,16 +0,0 @@
#ifndef RAGNAROK_SOURCE_CLIENT_BROWSERADDRESSEDIT_H
#define RAGNAROK_SOURCE_CLIENT_BROWSERADDRESSEDIT_H
#include <windows.h>
// Borrowed original subclass procedure and active browser-host selection.
// Null activeHost denotes no active CWebBrowser, not a null HWND field inside
// an otherwise active object.
struct BrowserAddressEdit {
WNDPROC const& previous;
HWND const* const& activeHost;
LRESULT Message(HWND window, UINT message, WPARAM value, LPARAM flags);
};
#endif

View file

@ -1,24 +0,0 @@
#ifndef RAGNAROK_SOURCE_CLIENT_BROWSERCLOSE_H
#define RAGNAROK_SOURCE_CLIENT_BROWSERCLOSE_H
#include "Client/BrowserSiteAttachment.h"
class CMouse;
// Borrowed original browser fields. External events use a separate COM reference
// independent of the client-site dispatch dependency. Auxiliary HWNDs and
// cookie remain untouched even after their host destroys the native children.
struct BrowserClose {
BrowserSiteAttachment& attachment;
IWebBrowser2*& browser;
IUnknown*& externalEvents;
HWND& host;
HWND const& browserWindow;
HWND const& applicationWindow;
int& cursorState;
CMouse& mouse;
bool Close();
};
#endif

View file

@ -1,21 +0,0 @@
#ifndef RAGNAROK_SOURCE_CLIENT_BROWSERCREATION_H
#define RAGNAROK_SOURCE_CLIENT_BROWSERCREATION_H
#include "Client/BrowserAddressBar.h"
#include "Client/BrowserHostWindow.h"
#include "Client/BrowserSiteAttachment.h"
// Views must refer to the same CWebBrowser fields. Caller supplies the complete
// host procedure, edit subclass and site event dependency. No rollback on error:
// browser, host, site and native children remain published at their original steps.
struct BrowserCreation {
IWebBrowser2*& browser;
HWND& browserWindow;
BrowserHostWindow& window;
BrowserSiteAttachment& attachment;
BrowserAddressBar& addressBar;
bool Create(HWND parent, HINSTANCE instance, int width, int height);
};
#endif

View file

@ -1,21 +0,0 @@
#ifndef RAGNAROK_SOURCE_CLIENT_BROWSERCURSOR_H
#define RAGNAROK_SOURCE_CLIENT_BROWSERCURSOR_H
#include <windows.h>
class CMouse;
class CRenderSystemBase;
// Borrowed CWebBrowser cursor state and the actual global mouse/render
// owners. ShowCursor receives the full integer, not a normalized boolean.
struct BrowserCursor {
int& state;
CMouse& mouse;
CRenderSystemBase& renderSystem;
HWND const& applicationWindow;
void Set(int value);
void Poll();
};
#endif

View file

@ -1,20 +0,0 @@
#ifndef RAGNAROK_SOURCE_CLIENT_BROWSERFRAME_H
#define RAGNAROK_SOURCE_CLIENT_BROWSERFRAME_H
#include "Client/BrowserCursor.h"
#include "Client/BrowserWindowSizing.h"
// Borrowed original browser owner. Presence means the separate external event
// receiver is attached, not merely that an OLE client site exists.
struct BrowserFrame {
BrowserWindowSizing& sizing;
BrowserCursor& cursor;
HWND const& host;
HWND const& browserWindow;
const bool& hasExternalEvents;
void Normalize();
void Poll();
};
#endif

View file

@ -1,18 +0,0 @@
#ifndef RAGNAROK_SOURCE_CLIENT_BROWSERHOSTWINDOW_H
#define RAGNAROK_SOURCE_CLIENT_BROWSERHOSTWINDOW_H
#include <windows.h>
// Borrowed CWebBrowser host and browser HWND, application
// title and complete browser window procedure. This operation does not replace
// that procedure with DefWindowProc or invent the browser host-site lifecycle.
struct BrowserHostWindow {
HWND& host;
HWND const& browser;
const char* const& title;
WNDPROC procedure;
bool Create(HWND parent, HINSTANCE instance, int width, int height);
};
#endif

View file

@ -1,25 +0,0 @@
#ifndef RAGNAROK_SOURCE_CLIENT_BROWSERINPUTHANDOFF_H
#define RAGNAROK_SOURCE_CLIENT_BROWSERINPUTHANDOFF_H
#include <windows.h>
class CMouse;
struct ServiceOptions;
namespace client {
struct ApplicationActivation;
}
// Borrowed original CWebBrowser service-option latch, main HWND and global
// input owners. Restoration retains the latch; it is not a one-shot operation.
struct BrowserInputHandoff {
unsigned char& restoreOption;
HWND const& applicationWindow;
ServiceOptions& service;
CMouse& mouse;
client::ApplicationActivation& activation;
void Enter();
void Restore();
};
#endif

View file

@ -1,30 +0,0 @@
#ifndef RAGNAROK_SOURCE_CLIENT_BROWSERINTERFACE_H
#define RAGNAROK_SOURCE_CLIENT_BROWSERINTERFACE_H
// clang-format off
#include <windows.h>
#include <ole2.h>
#include <exdisp.h>
// clang-format on
#include <string>
// Caller supplies the original owner fields and controls COM initialization
// and release. Successful creation publishes HWND only when browser is nonnull;
// get_HWND failure does not change the creation result.
bool CreateBrowserInterface(IWebBrowser2*& browser, HWND& window);
// Distinct original local-server route; never falls back to the in-process
// control. Success reparents its HWND and clears Top, Left and StatusBar.
bool CreateExternalBrowserInterface(IWebBrowser2*& browser, HWND& window, HWND const& host);
// Navigation's four optional arguments alias one VT_EMPTY VARIANT. Host show,
// browser visibility and title publication occur even when Navigate fails.
bool NavigateBrowser(IWebBrowser2* const& browser, HWND const& host, HWND const& title, std::string address);
// Connection-point helpers retain native HRESULTs; cookie writes belong to
// Advise. Release the connection point before its container on every path.
HRESULT AdviseBrowserEvents(IUnknown* source, IUnknown* sink, REFIID events, DWORD* cookie);
HRESULT UnadviseBrowserEvents(IUnknown* source, REFIID events, DWORD cookie);
#endif

View file

@ -1,23 +0,0 @@
#ifndef RAGNAROK_SOURCE_CLIENT_BROWSERSITEATTACHMENT_H
#define RAGNAROK_SOURCE_CLIENT_BROWSERSITEATTACHMENT_H
#include "Client/BrowserClientSite.h"
#include "Client/BrowserWindowSizing.h"
// Borrowed original owner fields, real popup factory and renderer dimensions.
// Site publication precedes COM work and remains on failure. Detach destroys the
// site even when QueryInterface fails; it does not clear the COM client site.
struct BrowserSiteAttachment {
BrowserWindowSizing& sizing;
HWND const& host;
BrowserClientSite*& site;
DWORD& cookie;
BrowserFactory& factory;
const int& renderWidth;
const int& renderHeight;
bool Attach();
void Detach();
};
#endif

View file

@ -1,17 +1,14 @@
#include "Client/ClientOptions.h" #include "Client/ClientOptions.h"
#include <bit>
// OFFSET: 0x0091BE20 // OFFSET: 0x0091BE20
// FUNCTION: fn_12610 // FUNCTION: fn_12610
int ClientOptions::Get(int id) const { int ClientOptions::Get(int id) const {
return std::bit_cast<int>(static_cast<std::uint32_t>(this->values.Get(id))); return static_cast<int>(this->values.Get(id));
} }
// OFFSET: 0x005E7970 // OFFSET: 0x005E7970
// FUNCTION: fn_03937 // FUNCTION: fn_03937
void ClientOptions::Set(int id, int value, int extra) { void ClientOptions::Set(int id, int value, int extra) {
const std::uint64_t bits = const std::uint64_t bits = static_cast<std::uint32_t>(value) | (static_cast<std::uint64_t>(extra) << 32);
static_cast<std::uint32_t>(value) | (static_cast<std::uint64_t>(static_cast<std::uint32_t>(extra)) << 32); this->values.Set(id, static_cast<std::int64_t>(bits));
this->values.Set(id, std::bit_cast<std::int64_t>(bits));
} }

View file

@ -3,7 +3,6 @@
#include "Contents/InstantVar.h" #include "Contents/InstantVar.h"
// Low/high-word client operations on its CInstantVar at +0x7FA8.
struct ClientOptions { struct ClientOptions {
CInstantVar& values; CInstantVar& values;
int Get(int id) const; int Get(int id) const;

View file

@ -1,12 +0,0 @@
#ifndef RAGNAROK_SOURCE_CLIENT_ERRORDIALOG_H
#define RAGNAROK_SOURCE_CLIENT_ERRORDIALOG_H
#include <windows.h>
class CRenderSystemBase;
// Obtained through the render-system owner.
// Keep the real owner dependency explicit until its lifecycle is reconstructed.
int ShowErrorDialog(CRenderSystemBase* renderSystem, const char* text, UINT type);
#endif

View file

@ -1,6 +1,6 @@
#include "Client/ImeInput.h" #include "Client/Input/ImeInput.h"
#include "Client/TraditionalKeyInput.h" #include "Client/Input/TraditionalKeyInput.h"
#include "Utility/LanguageCharset.h" #include "Utility/Text/LanguageCharset.h"
#include <algorithm> #include <algorithm>
#include <cstring> #include <cstring>
@ -10,6 +10,7 @@
// FUNCTION: fn_17793 // FUNCTION: fn_17793
LRESULT ImeInput::Message(HWND, UINT message, WPARAM value, LPARAM flags, std::uint8_t& handled) { LRESULT ImeInput::Message(HWND, UINT message, WPARAM value, LPARAM flags, std::uint8_t& handled) {
const auto options = static_cast<std::uint16_t>(flags); const auto options = static_cast<std::uint16_t>(flags);
switch (message) { switch (message) {
case WM_INPUTLANGCHANGE: case WM_INPUTLANGCHANGE:
this->language = options; this->language = options;
@ -48,13 +49,13 @@ LRESULT ImeInput::Message(HWND, UINT message, WPARAM value, LPARAM flags, std::u
if (context) { if (context) {
WCHAR wide[256]; WCHAR wide[256];
char converted[256]; char converted[256];
const auto bytes = const auto bytes = static_cast<std::uint32_t>(ImmGetCompositionStringW(context, GCS_RESULTSTR, wide, sizeof(wide)));
static_cast<std::uint32_t>(ImmGetCompositionStringW(context, GCS_RESULTSTR, wide, sizeof(wide))); const unsigned size = WideCharToMultiByte(this->codePage, 0, wide, bytes >> 1, converted, sizeof(converted), nullptr, nullptr);
const unsigned size =
WideCharToMultiByte(this->codePage, 0, wide, bytes >> 1, converted, sizeof(converted), nullptr, nullptr);
if (size >= sizeof(converted)) { if (size >= sizeof(converted)) {
__fastfail(8); __fastfail(FAST_FAIL_RANGE_CHECK_FAILURE);
} }
converted[size] = 0; converted[size] = 0;
ImmReleaseContext(this->input.window, context); ImmReleaseContext(this->input.window, context);
this->input.Insert(converted); this->input.Insert(converted);
@ -78,6 +79,7 @@ LRESULT ImeInput::Message(HWND, UINT message, WPARAM value, LPARAM flags, std::u
} else { } else {
this->input.compositionState = 0; this->input.compositionState = 0;
} }
ImmReleaseContext(this->input.window, context); ImmReleaseContext(this->input.window, context);
} else { } else {
this->input.compositionState = 0; this->input.compositionState = 0;
@ -109,5 +111,6 @@ const char* ImeInput::Candidate(int index) const {
const WCHAR* text = reinterpret_cast<const WCHAR*>(bytes + this->candidateList->dwOffset[index]); const WCHAR* text = reinterpret_cast<const WCHAR*>(bytes + this->candidateList->dwOffset[index]);
const unsigned size = WideCharToMultiByte(this->codePage, 0, text, -1, output, sizeof(output), nullptr, nullptr); const unsigned size = WideCharToMultiByte(this->codePage, 0, text, -1, output, sizeof(output), nullptr, nullptr);
output[std::min<unsigned>(size, 255)] = 0; output[std::min<unsigned>(size, 255)] = 0;
return output; return output;
} }

View file

@ -1,7 +1,7 @@
#ifndef RAGNAROK_SOURCE_CLIENT_IMEINPUT_H #ifndef RAGNAROK_SOURCE_CLIENT_INPUT_IMEINPUT_H
#define RAGNAROK_SOURCE_CLIENT_IMEINPUT_H #define RAGNAROK_SOURCE_CLIENT_INPUT_IMEINPUT_H
#include "Client/TextInputBuffer.h" #include "Client/Input/TextInputBuffer.h"
#include <cstdint> #include <cstdint>
#include <imm.h> #include <imm.h>
@ -9,8 +9,6 @@
struct TraditionalKeyInput; struct TraditionalKeyInput;
// Borrowed CLanguage fields, with optional concrete CLanguageTC key view.
// Base/TH/ARB composition hooks are established empty entries.
struct ImeInput { struct ImeInput {
TextInputBuffer& input; TextInputBuffer& input;
std::uint16_t& language; std::uint16_t& language;
@ -24,11 +22,8 @@ struct ImeInput {
int& compositionCursor; int& compositionCursor;
TraditionalKeyInput* traditional; TraditionalKeyInput* traditional;
// Original ignores the passed HWND and uses the global main-window handle
// borrowed by input. Flags are truncated to the low WORD.
LRESULT Message(HWND unused, UINT message, WPARAM value, LPARAM flags, std::uint8_t& handled); LRESULT Message(HWND unused, UINT message, WPARAM value, LPARAM flags, std::uint8_t& handled);
// Shared 256-byte output; later successful lookups overwrite prior strings.
// Original checks index < count only (no negative-index guard).
const char* Candidate(int index) const; const char* Candidate(int index) const;
}; };

View file

@ -1,5 +1,5 @@
#include "Client/InputCharset.h" #include "Client/Input/InputCharset.h"
#include "Utility/LanguageCharset.h" #include "Utility/Text/LanguageCharset.h"
#include <cstdio> #include <cstdio>

View file

@ -1,5 +1,5 @@
#ifndef RAGNAROK_SOURCE_CLIENT_INPUTCHARSET_H #ifndef RAGNAROK_SOURCE_CLIENT_INPUT_INPUTCHARSET_H
#define RAGNAROK_SOURCE_CLIENT_INPUTCHARSET_H #define RAGNAROK_SOURCE_CLIENT_INPUT_INPUTCHARSET_H
#include <cstdint> #include <cstdint>
@ -9,7 +9,7 @@ struct InputCharset {
const std::uint16_t& alternateLanguage; const std::uint16_t& alternateLanguage;
const char* Prefix(bool alternate) const; const char* Prefix(bool alternate) const;
// Shared selection used by composition refresh/language-change handling.
unsigned CodePage(unsigned fallback) const; unsigned CodePage(unsigned fallback) const;
}; };

View file

@ -1,11 +1,8 @@
#include "Client/Mouse.h" #include "Client/Input/Mouse.h"
#include "Render/DCSurface.h" #include "Render/DeviceContext/DCSurface.h"
#include <bit>
#include <windows.h> #include <windows.h>
// clang-format off #include <timeapi.h>
#include <mmsystem.h>
// clang-format on
// OFFSET: 0x006370C0 // OFFSET: 0x006370C0
// FUNCTION: fn_04724 // FUNCTION: fn_04724
@ -20,11 +17,12 @@ void CMouse::Update() {
return; return;
} }
this->deltaX = std::bit_cast<int>(static_cast<std::uint32_t>(this->pendingX) - static_cast<std::uint32_t>(this->x)); this->deltaX = this->pendingX - this->x;
this->deltaY = std::bit_cast<int>(static_cast<std::uint32_t>(this->pendingY) - static_cast<std::uint32_t>(this->y)); this->deltaY = this->pendingY - this->y;
this->x = this->pendingX; this->x = this->pendingX;
this->y = this->pendingY; this->y = this->pendingY;
const std::uint32_t now = timeGetTime(); const std::uint32_t now = timeGetTime();
for (unsigned index = 0; index < 2; ++index) { for (unsigned index = 0; index < 2; ++index) {
if (this->deltaX != 0 || this->deltaY != 0) { if (this->deltaX != 0 || this->deltaY != 0) {
this->pressTicks[index] = now - this->doubleClickTime; this->pressTicks[index] = now - this->doubleClickTime;
@ -71,6 +69,7 @@ int CMouse::Center() {
const int halfHeight = static_cast<int>(GetRenderSystemHolder()->Height()) / 2; const int halfHeight = static_cast<int>(GetRenderSystemHolder()->Height()) / 2;
this->pendingY = halfHeight; this->pendingY = halfHeight;
this->y = halfHeight; this->y = halfHeight;
return halfHeight; return halfHeight;
} }
@ -120,7 +119,7 @@ void CMouse::Message(CMouse& global, HWND mainWindow, HWND source, UINT message,
this->pendingButtons[1] = 0; this->pendingButtons[1] = 0;
break; break;
case WM_MOUSEWHEEL: case WM_MOUSEWHEEL:
this->pendingWheel = std::bit_cast<std::int16_t>(static_cast<std::uint16_t>(keys >> 16)); this->pendingWheel = GET_WHEEL_DELTA_WPARAM(keys);
break; break;
} }
} }

View file

@ -1,11 +1,9 @@
#ifndef RAGNAROK_SOURCE_CLIENT_MOUSE_H #ifndef RAGNAROK_SOURCE_CLIENT_INPUT_MOUSE_H
#define RAGNAROK_SOURCE_CLIENT_MOUSE_H #define RAGNAROK_SOURCE_CLIENT_INPUT_MOUSE_H
#include <cstdint> #include <cstdint>
#include <windows.h> #include <windows.h>
// RTTI CMouse. Defaults below are the original
// static-data initializer, not evidence of a separate allocating constructor.
class CMouse { class CMouse {
public: public:
virtual ~CMouse(); virtual ~CMouse();
@ -14,9 +12,7 @@ public:
int Center(); int Center();
void SetX(int value); void SetX(int value);
void SetY(int value); void SetY(int value);
// Message position comes from GetCursorPos/mainWindow, not lParam or source.
// The original resets the global mouse on first message; pass that owner
// separately so non-global instances retain the same distinction.
void Message(CMouse& global, HWND mainWindow, HWND source, UINT message, WPARAM keys, LPARAM unused); void Message(CMouse& global, HWND mainWindow, HWND source, UINT message, WPARAM keys, LPARAM unused);
/* 0004 */ int x = 0; /* 0004 */ int x = 0;

View file

@ -1,9 +1,8 @@
#include "Client/TextInputBuffer.h" #include "Client/Input/TextInputBuffer.h"
#include "UI/UIWindow.h" #include "UI/Window/UIWindow.h"
#include "UI/WindowManagerWindows.h" #include "UI/Window/WindowManagerWindows.h"
#include <algorithm> #include <algorithm>
#include <bit>
#include <cstdint> #include <cstdint>
#include <cstring> #include <cstring>
#include <imm.h> #include <imm.h>
@ -14,7 +13,7 @@ void TextInputBuffer::CancelComposition() {
HIMC context = ImmGetContext(this->window); HIMC context = ImmGetContext(this->window);
if (context) { if (context) {
ImmNotifyIME(context, 0x15, 4, 0); ImmNotifyIME(context, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
ImmReleaseContext(this->window, context); ImmReleaseContext(this->window, context);
} }
@ -34,8 +33,7 @@ void TextInputBuffer::DeleteSelection() {
} }
const int begin = std::min(this->cursor, this->anchor); const int begin = std::min(this->cursor, this->anchor);
const auto length = const int length = std::max(this->cursor, this->anchor) - begin;
static_cast<std::uint32_t>(std::max(this->cursor, this->anchor)) - static_cast<std::uint32_t>(begin);
this->text.erase(static_cast<std::size_t>(begin), length); this->text.erase(static_cast<std::size_t>(begin), length);
this->cursor = begin; this->cursor = begin;
this->anchor = begin; this->anchor = begin;
@ -50,9 +48,7 @@ void TextInputBuffer::DeleteSelection() {
void TextInputBuffer::Insert(const char* value) { void TextInputBuffer::Insert(const char* value) {
this->DeleteSelection(); this->DeleteSelection();
this->text.insert(static_cast<std::size_t>(this->cursor), value, std::strlen(value)); this->text.insert(static_cast<std::size_t>(this->cursor), value, std::strlen(value));
this->anchor = std::bit_cast<std::int32_t>( this->anchor = this->cursor + static_cast<int>(std::strlen(value));
static_cast<std::uint32_t>(std::strlen(value)) + static_cast<std::uint32_t>(this->cursor)
);
this->cursor = this->anchor; this->cursor = this->anchor;
if (this->suggestionsEnabled && this->chatOpen && this->chatInput && this->focused == this->chatInput) { if (this->suggestionsEnabled && this->chatOpen && this->chatInput && this->focused == this->chatInput) {
@ -77,9 +73,7 @@ void TextInputBuffer::CopyDisplayText(std::string& output) const {
} }
if (!this->candidate.empty()) { if (!this->candidate.empty()) {
output.insert( output.insert(static_cast<std::size_t>(this->cursor), this->candidate.c_str(), std::strlen(this->candidate.c_str()));
static_cast<std::size_t>(this->cursor), this->candidate.c_str(), std::strlen(this->candidate.c_str())
);
} }
} }
@ -102,8 +96,7 @@ void TextInputBuffer::RefreshComposition(unsigned codePage) {
WCHAR wide[256]; WCHAR wide[256];
char converted[256]; char converted[256];
const auto bytes = static_cast<std::uint32_t>(ImmGetCompositionStringW(context, GCS_COMPSTR, wide, sizeof(wide))); const auto bytes = static_cast<std::uint32_t>(ImmGetCompositionStringW(context, GCS_COMPSTR, wide, sizeof(wide)));
const unsigned size = const unsigned size = WideCharToMultiByte(codePage, 0, wide, bytes >> 1, converted, sizeof(converted), nullptr, nullptr);
WideCharToMultiByte(codePage, 0, wide, bytes >> 1, converted, sizeof(converted), nullptr, nullptr);
ImmReleaseContext(this->window, context); ImmReleaseContext(this->window, context);
this->candidate.assign(converted, size); this->candidate.assign(converted, size);
@ -122,8 +115,7 @@ void TextInputBuffer::RefreshComposition(unsigned codePage) {
// OFFSET: 0x00ACD5C0 // OFFSET: 0x00ACD5C0
// FUNCTION: fn_17765 // FUNCTION: fn_17765
void TextInputBuffer::CopySelection(bool password) { void TextInputBuffer::CopySelection(bool password) {
const auto difference = static_cast<std::uint32_t>(this->cursor) - static_cast<std::uint32_t>(this->anchor); const int length = std::max(this->cursor, this->anchor) - std::min(this->cursor, this->anchor);
const auto length = std::bit_cast<std::int32_t>(difference) < 0 ? 0u - difference : difference;
if (!length || !OpenClipboard(this->window)) { if (!length || !OpenClipboard(this->window)) {
return; return;
} }
@ -138,6 +130,7 @@ void TextInputBuffer::CopySelection(bool password) {
} else { } else {
std::memcpy(data, this->text.data() + begin, length); std::memcpy(data, this->text.data() + begin, length);
} }
data[length] = 0; data[length] = 0;
GlobalUnWire(memory); GlobalUnWire(memory);
EmptyClipboard(); EmptyClipboard();
@ -200,7 +193,7 @@ void TextInputBuffer::MoveForward(unsigned codePage) {
const auto length = static_cast<std::uint32_t>(this->text.size()); const auto length = static_cast<std::uint32_t>(this->text.size());
const auto position = static_cast<std::uint32_t>(this->cursor); const auto position = static_cast<std::uint32_t>(this->cursor);
if (position > length) { if (position > length) {
this->cursor = std::bit_cast<int>(length); this->cursor = static_cast<int>(length);
} else if (position < length) { } else if (position < length) {
const char* begin = this->text.c_str(); const char* begin = this->text.c_str();
this->cursor = static_cast<int>(CharNextExA(static_cast<WORD>(codePage), begin + position, 0) - begin); this->cursor = static_cast<int>(CharNextExA(static_cast<WORD>(codePage), begin + position, 0) - begin);
@ -214,6 +207,7 @@ void TextInputBuffer::Character(unsigned value, LPARAM, unsigned codePage) {
if (this->cursor == this->anchor) { if (this->cursor == this->anchor) {
this->MoveBack(codePage); this->MoveBack(codePage);
} }
this->DeleteSelection(); this->DeleteSelection();
return; return;
} }
@ -222,8 +216,6 @@ void TextInputBuffer::Character(unsigned value, LPARAM, unsigned codePage) {
return; return;
} }
// Keep the original little-endian DWORD representation and stop at its first
// zero byte; valid WM_CHAR payloads provide that terminator within the word.
char bytes[sizeof(value)]; char bytes[sizeof(value)];
std::memcpy(bytes, &value, sizeof(value)); std::memcpy(bytes, &value, sizeof(value));
this->Insert(bytes); this->Insert(bytes);
@ -232,13 +224,7 @@ void TextInputBuffer::Character(unsigned value, LPARAM, unsigned codePage) {
// OFFSET: 0x00ACE880 // OFFSET: 0x00ACE880
// FUNCTION: fn_17792 // FUNCTION: fn_17792
void TextInputBuffer::KeyDown( void TextInputBuffer::KeyDown(
unsigned key, unsigned key, LPARAM, unsigned codePage, bool password, UIWindow* blockedPasteFocus, WindowManagerWindows& windows, UIWindow* const& chatArrows
LPARAM,
unsigned codePage,
bool password,
UIWindow* blockedPasteFocus,
WindowManagerWindows& windows,
UIWindow* const& chatArrows
) { ) {
const auto cut = [&]() { const auto cut = [&]() {
this->CopySelection(password); this->CopySelection(password);
@ -266,6 +252,7 @@ void TextInputBuffer::KeyDown(
cut(); cut();
break; break;
} }
return; return;
} }
@ -285,6 +272,7 @@ void TextInputBuffer::KeyDown(
this->cursor = std::max(static_cast<int>(this->text.size()), 0); this->cursor = std::max(static_cast<int>(this->text.size()), 0);
break; break;
} }
return; return;
} }
@ -305,6 +293,7 @@ void TextInputBuffer::KeyDown(
} else { } else {
this->MoveForward(codePage); this->MoveForward(codePage);
} }
this->anchor = this->cursor; this->anchor = this->cursor;
} }
break; break;

View file

@ -0,0 +1,57 @@
#ifndef RAGNAROK_SOURCE_CLIENT_INPUT_TEXTINPUTBUFFER_H
#define RAGNAROK_SOURCE_CLIENT_INPUT_TEXTINPUTBUFFER_H
#include <string>
#include <windows.h>
class UIWindow;
class WindowManagerWindows;
class TextInputSuggestions {
public:
virtual void Update(const char* text) = 0;
protected:
~TextInputSuggestions() = default;
};
struct TextInputBuffer {
std::string& text;
int& cursor;
int& anchor;
std::string& composition;
std::string& candidate;
int& compositionState;
HWND& window;
const bool& suggestionsEnabled;
UIWindow* const& focused;
UIWindow* const& chatInput;
const bool& chatOpen;
TextInputSuggestions& suggestions;
void CancelComposition();
void DeleteSelection();
void Insert(const char* value);
void Select(int anchor, int cursor);
void CopyDisplayText(std::string& output) const;
void Selection(int& anchor, int& cursor) const;
void SelectionRange(int& begin, int& end) const;
void RefreshComposition(unsigned codePage);
void CopySelection(bool password);
void Paste(UIWindow* blockedFocus, WindowManagerWindows& windows);
void MoveBack(unsigned codePage);
void MoveForward(unsigned codePage);
void Character(unsigned value, LPARAM unused, unsigned codePage);
void KeyDown(
unsigned key, LPARAM unused, unsigned codePage, bool password, UIWindow* blockedPasteFocus, WindowManagerWindows& windows, UIWindow* const& chatArrows
);
};
#endif

View file

@ -1,4 +1,4 @@
#include "Client/TraditionalKeyInput.h" #include "Client/Input/TraditionalKeyInput.h"
#include "Network/TextEncoding.h" #include "Network/TextEncoding.h"
#include <windows.h> #include <windows.h>
@ -7,17 +7,16 @@
void TraditionalKeyInput::InitializeMappings() { void TraditionalKeyInput::InitializeMappings() {
// STATIC: 0x011594E0 // STATIC: 0x011594E0
// GLOBAL: gl_13427 // GLOBAL: gl_13427
static constexpr std::uint8_t bytes[] = {0xA3, 0x74, 0xA3, 0x78, 0xA3, 0xBE, 0xA3, 0xBF, 0xA3, 0xA4, 0xA3, 0xBD, static constexpr std::uint8_t bytes[] = {0xA3, 0x74, 0xA3, 0x78, 0xA3, 0xBE, 0xA3, 0xBF, 0xA3, 0xA4, 0xA3, 0xBD, 0xA3, 0xBB, 0xA3, 0xAB, 0xA3,
0xA3, 0xBB, 0xA3, 0xAB, 0xA3, 0xAF, 0xA3, 0xB3, 0xA3, 0x76, 0xA3, 0xA7, 0xAF, 0xA3, 0xB3, 0xA3, 0x76, 0xA3, 0xA7, 0xA3, 0x74, 0xA3, 0x74, 0xA3, 0x74, 0xA3, 0xA2, 0xA3, 0xA6,
0xA3, 0x74, 0xA3, 0x74, 0xA3, 0x74, 0xA3, 0xA2, 0xA3, 0xA6, 0xA3, 0xA9, 0xA3, 0xA9, 0xA3, 0xAC, 0xA3, 0xB9, 0xA3, 0xAD, 0xA3, 0xB1, 0xA3, 0xAA, 0xA3, 0xBA, 0xA3, 0xB0, 0xA3,
0xA3, 0xAC, 0xA3, 0xB9, 0xA3, 0xAD, 0xA3, 0xB1, 0xA3, 0xAA, 0xA3, 0xBA, 0xB4, 0xA3, 0x75, 0xA3, 0xA1, 0xA3, 0x7A, 0xA3, 0xA5, 0xA3, 0x74, 0xA3, 0xA3, 0xA3, 0x79, 0xA3, 0x74,
0xA3, 0xB0, 0xA3, 0xB4, 0xA3, 0x75, 0xA3, 0xA1, 0xA3, 0x7A, 0xA3, 0xA5, 0xA3, 0xA8, 0xA3, 0x77, 0xA3, 0xB7, 0xA3, 0xB5, 0xA3, 0xAE, 0xA3, 0xB2, 0xA3, 0xB6};
0xA3, 0x74, 0xA3, 0xA3, 0xA3, 0x79, 0xA3, 0x74, 0xA3, 0xA8, 0xA3, 0x77,
0xA3, 0xB7, 0xA3, 0xB5, 0xA3, 0xAE, 0xA3, 0xB2, 0xA3, 0xB6};
static constexpr char categories[] = "11441443331111111132331233111121111133333"; static constexpr char categories[] = "11441443331111111132331233111121111133333";
static constexpr unsigned char keys[] = "1234567890ABCDEFGHIJKLNMOPQRSTUVWXYZ\xBD\xBA\xBC\xBE\xBF"; static constexpr unsigned char keys[] = "1234567890ABCDEFGHIJKLNMOPQRSTUVWXYZ\xBD\xBA\xBC\xBE\xBF";
this->unknown04C1 = 0; this->unknown04C1 = 0;
for (auto& mapping : this->mappings) { for (auto& mapping : this->mappings) {
mapping.category = 4; mapping.category = 4;
} }
@ -50,6 +49,7 @@ void TraditionalKeyInput::Key(int virtualKey) {
if (!this->keys.empty()) { if (!this->keys.empty()) {
this->keys.pop_back(); this->keys.pop_back();
} }
return; return;
} }
@ -59,6 +59,7 @@ void TraditionalKeyInput::Key(int virtualKey) {
break; break;
} }
} }
this->keys.push_back(key); this->keys.push_back(key);
} }
@ -72,6 +73,7 @@ const char* TraditionalKeyInput::DisplayText() const {
// FUNCTION: fn_17778 // FUNCTION: fn_17778
const char* TraditionalKeyInput::Display() { const char* TraditionalKeyInput::Display() {
char bytes[8] = {}; char bytes[8] = {};
for (std::uint8_t key : this->keys) { for (std::uint8_t key : this->keys) {
const auto& mapping = this->mappings[key]; const auto& mapping = this->mappings[key];
if (mapping.category < 4) { if (mapping.category < 4) {
@ -96,6 +98,7 @@ int TraditionalKeyInput::Count() const {
if (this->keys.size() == 1 && this->mappings[this->keys.back()].category == 4) { if (this->keys.size() == 1 && this->mappings[this->keys.back()].category == 4) {
return 0; return 0;
} }
return static_cast<int>(this->keys.size()); return static_cast<int>(this->keys.size());
} }

View file

@ -1,5 +1,5 @@
#ifndef RAGNAROK_SOURCE_CLIENT_TRADITIONALKEYINPUT_H #ifndef RAGNAROK_SOURCE_CLIENT_INPUT_TRADITIONALKEYINPUT_H
#define RAGNAROK_SOURCE_CLIENT_TRADITIONALKEYINPUT_H #define RAGNAROK_SOURCE_CLIENT_INPUT_TRADITIONALKEYINPUT_H
#include <array> #include <array>
#include <cstdint> #include <cstdint>
@ -11,15 +11,12 @@ struct TraditionalKeyMapping {
std::uint8_t bytes[3]; std::uint8_t bytes[3];
}; };
// Borrowed CLanguageTC fields; not a substitute for its base-language/IME
// lifecycle. Key sequence is +0xB8, mappings +0xC0, display +0x58, flag +0x4C1.
struct TraditionalKeyInput { struct TraditionalKeyInput {
std::list<std::uint8_t>& keys; std::list<std::uint8_t>& keys;
std::array<TraditionalKeyMapping, 256>& mappings; std::array<TraditionalKeyMapping, 256>& mappings;
std::string& display; std::string& display;
std::uint8_t& unknown04C1; std::uint8_t& unknown04C1;
// Unmapped byte triples are deliberately untouched; their category becomes 4.
void InitializeMappings(); void InitializeMappings();
void Key(int virtualKey); void Key(int virtualKey);
void DefaultKey(int virtualKey); void DefaultKey(int virtualKey);

View file

@ -1,46 +0,0 @@
#include "Client/Launcher.h"
#include <cstring>
#include <windows.h>
namespace client {
// OFFSET: 0x00E58820
// FUNCTION: fn_24363
bool RestartFromCommandLine(char* commandLine) {
char* marker = std::strstr(commandLine, "/launcherRESTART:");
if (!marker) {
return false;
}
STARTUPINFOA startup = {};
startup.cb = sizeof(startup);
startup.dwFlags = STARTF_USESHOWWINDOW;
startup.wShowWindow = SW_SHOWNORMAL;
PROCESS_INFORMATION process;
// Original behavior ignores CreateProcessA failure and still returns true.
CreateProcessA(
nullptr, marker + std::strlen("/launcherRESTART:"), nullptr, nullptr, FALSE, 0, nullptr, nullptr, &startup,
&process
);
CloseHandle(process.hProcess);
CloseHandle(process.hThread);
return true;
}
// OFFSET: 0x00E5D390
// FUNCTION: fn_24380
void ExitForProtectionCheck(const char* commandLine) {
if (std::strstr(commandLine, "/check_themida")) {
ExitProcess(0);
}
}
// OFFSET: 0x00E5D390
// FUNCTION: fn_24380
bool AcquireInstanceMutex(HANDLE* mutex) {
*mutex = CreateMutexA(nullptr, TRUE, "MutexInit");
return GetLastError() != ERROR_ALREADY_EXISTS;
}
}

View file

@ -1,18 +0,0 @@
#ifndef RAGNAROK_SOURCE_CLIENT_LAUNCHER_H
#define RAGNAROK_SOURCE_CLIENT_LAUNCHER_H
#include <windows.h>
namespace client {
// Reconstructed semantic name for the original launcher restart branch.
// A mutable buffer is required because CreateProcessA may modify its tail.
bool RestartFromCommandLine(char* commandLine);
// Named source extractions of the established WinMain guard blocks.
void ExitForProtectionCheck(const char* commandLine);
bool AcquireInstanceMutex(HANDLE* mutex);
}
#endif

View file

@ -1,4 +1,4 @@
#include "Client/ActiveGameMode.h" #include "Client/Mode/ActiveGameMode.h"
// FUNCTION: fn_17831 // FUNCTION: fn_17831
CGameMode* CWorld::ActiveGameMode() { CGameMode* CWorld::ActiveGameMode() {

Some files were not shown because too many files have changed in this diff Show more