Repository: Kagamia/WzComparerR2 Branch: master Commit: db2dc902e065 Files: 542 Total size: 3.6 MB Directory structure: gitextract_7zirm0f1/ ├── .gitattributes ├── .gitignore ├── .gitmodules ├── Build/ │ ├── Common.props │ └── WcR2Plugin.targets ├── LICENSE ├── README.md ├── UpdateLogs/ │ ├── dev.md │ ├── v2.0.1.md │ ├── v2.0.9.md │ ├── v2.1.1.md │ └── v2.1.md ├── WzComparerR2/ │ ├── AnimateEncoderFactory.cs │ ├── CharaSim/ │ │ ├── CharaEquip.cs │ │ ├── CharaProp.cs │ │ ├── CharaSimLoader.cs │ │ ├── Character.cs │ │ └── CharacterStatus.cs │ ├── CharaSimControl/ │ │ ├── AControl.cs │ │ ├── ACtrlButton.cs │ │ ├── ACtrlVScroll.cs │ │ ├── AfrmEquip.cs │ │ ├── AfrmItem.cs │ │ ├── AfrmStat.cs │ │ ├── AfrmTooltip.cs │ │ ├── ButtonState.cs │ │ ├── CharaSimControlGroup.cs │ │ ├── DamageSkinTooltipRender.cs │ │ ├── FamiliarTooltipRender.cs │ │ ├── GearGraphics.cs │ │ ├── GearTooltipRender.cs │ │ ├── GearTooltipRender2.cs │ │ ├── ItemMouseEventArgs.cs │ │ ├── ItemMouseEventHandler.cs │ │ ├── ItemTooltipRender.cs │ │ ├── ItemTooltipRender2.cs │ │ ├── MobTooltipRenderer.cs │ │ ├── NpcTooltipRenderer.cs │ │ ├── RecipeTooltipRender.cs │ │ ├── RenderHelper.cs │ │ ├── SetItemTooltipRender.cs │ │ ├── SkillTooltipRender.cs │ │ ├── SkillTooltipRender2.cs │ │ ├── TextBlock.cs │ │ └── TooltipRender.cs │ ├── Comparer/ │ │ ├── CompareDifference.cs │ │ ├── DifferenceType.cs │ │ ├── EasyComparer.cs │ │ ├── WzFileComparer.cs │ │ ├── WzPngComparison.cs │ │ └── WzVirtualNode.cs │ ├── Config/ │ │ ├── CharaSimConfig.cs │ │ ├── CharaSimDamageSkinConfig.cs │ │ ├── CharaSimGearConfig.cs │ │ ├── CharaSimItemConfig.cs │ │ ├── CharaSimMobConfig.cs │ │ ├── CharaSimNpcConfig.cs │ │ ├── CharaSimRecipeConfig.cs │ │ ├── CharaSimSkillConfig.cs │ │ ├── CustomCSSConfig.cs │ │ ├── ImageHandlerConfig.cs │ │ ├── MosaicInfo.cs │ │ └── WcR2Config.cs │ ├── DBConnection.cs │ ├── Dotnet4Polyfill.cs │ ├── Dotnet6Patches.cs │ ├── DownloadingItem.cs │ ├── FrmAbout.Designer.cs │ ├── FrmAbout.cs │ ├── FrmAbout.resx │ ├── FrmCustomCSS.Designer.cs │ ├── FrmCustomCSS.cs │ ├── FrmCustomCSS.resx │ ├── FrmGifClipOptions.Designer.cs │ ├── FrmGifClipOptions.cs │ ├── FrmGifClipOptions.resx │ ├── FrmGifMaker.Designer.cs │ ├── FrmGifMaker.cs │ ├── FrmGifMaker.resx │ ├── FrmGifSetting.Designer.cs │ ├── FrmGifSetting.cs │ ├── FrmGifSetting.resx │ ├── FrmOptions.Designer.cs │ ├── FrmOptions.cs │ ├── FrmOptions.resx │ ├── FrmPatcher.Designer.cs │ ├── FrmPatcher.cs │ ├── FrmPatcher.resx │ ├── FrmQuickViewSetting.Designer.cs │ ├── FrmQuickViewSetting.cs │ ├── FrmQuickViewSetting.resx │ ├── FrmUpdater.Designer.cs │ ├── FrmUpdater.cs │ ├── FrmUpdater.resx │ ├── HistoryList.cs │ ├── ImageDragHandler.cs │ ├── MainForm.Designer.cs │ ├── MainForm.cs │ ├── MainForm.resx │ ├── MemoryTributary.cs │ ├── Patcher/ │ │ ├── Builder/ │ │ │ ├── BuildInstruction.cs │ │ │ ├── BuildType.cs │ │ │ ├── CheckSum.cs │ │ │ ├── InflateStream.cs │ │ │ ├── PatchPart.cs │ │ │ ├── PatchType.cs │ │ │ ├── StreamUtils.cs │ │ │ ├── WzPatcherReader.cs │ │ │ └── WzPatcherWriter.cs │ │ ├── PatchPartContext.cs │ │ ├── PatcherSetting.cs │ │ ├── PatcherSettingCollection.cs │ │ ├── PatchingEventArgs.cs │ │ ├── PatchingState.cs │ │ ├── ReversePatcherBuilder.cs │ │ └── WzPatcher.cs │ ├── PictureBoxEx.cs │ ├── PluginLoadContext.cs │ ├── Program.cs │ ├── Properties/ │ │ ├── AssemblyInfo.cs │ │ ├── Resources.Designer.cs │ │ └── Resources.resx │ ├── QueryPerformance.cs │ ├── SoundPlayer/ │ │ ├── BassSoundPlayer.cs │ │ ├── CustomSoundFile.cs │ │ ├── ISoundFile.cs │ │ ├── ISoundPlayer.cs │ │ └── PlayState.cs │ ├── Updater.cs │ ├── WzComparerR2.csproj │ ├── app.config │ └── app.manifest ├── WzComparerR2.Avatar/ │ ├── Action.cs │ ├── ActionFrame.cs │ ├── AvatarCanvas.cs │ ├── AvatarPart.cs │ ├── Bone.cs │ ├── Entry.cs │ ├── Properties/ │ │ ├── AssemblyInfo.cs │ │ ├── Resources.Designer.cs │ │ └── Resources.resx │ ├── Skin.cs │ ├── UI/ │ │ ├── AvatarCodeForm.Designer.cs │ │ ├── AvatarCodeForm.cs │ │ ├── AvatarCodeForm.resx │ │ ├── AvatarContainer.Designer.cs │ │ ├── AvatarContainer.cs │ │ ├── AvatarForm.Designer.cs │ │ ├── AvatarForm.cs │ │ ├── AvatarForm.resx │ │ ├── AvatarPartButtonItem.Designer.cs │ │ ├── AvatarPartButtonItem.cs │ │ └── AvatarPartButtonItem.resx │ └── WzComparerR2.Avatar.csproj ├── WzComparerR2.Common/ │ ├── Animation/ │ │ ├── Frame.cs │ │ ├── FrameAnimationData.cs │ │ ├── FrameAnimator.cs │ │ ├── ISpineAnimationData.cs │ │ ├── ISpineAnimator.cs │ │ ├── KeyFrame.cs │ │ ├── MaplestoryCanvasVideoLoader.cs │ │ ├── ModelBound.cs │ │ ├── SpineAnimationDataV2.cs │ │ ├── SpineAnimationDataV4.cs │ │ ├── SpineAnimatorV2.cs │ │ ├── SpineAnimatorV4.cs │ │ └── WzSpineTextureLoader.cs │ ├── BitmapOrigin.cs │ ├── Calculator.cs │ ├── CharaSim/ │ │ ├── Addition.cs │ │ ├── AdditionType.cs │ │ ├── AlienStone.cs │ │ ├── AlienStoneGrade.cs │ │ ├── DamageSkin.cs │ │ ├── ExclusiveEquip.cs │ │ ├── Familiar.cs │ │ ├── FormulaVersion.cs │ │ ├── Gear.cs │ │ ├── GearGrade.cs │ │ ├── GearLevelInfo.cs │ │ ├── GearPropType.cs │ │ ├── GearSealedInfo.cs │ │ ├── GearState.cs │ │ ├── GearType.cs │ │ ├── HyperSkillType.cs │ │ ├── Item.cs │ │ ├── ItemBase.cs │ │ ├── ItemBaseType.cs │ │ ├── ItemPropType.cs │ │ ├── ItemSpecType.cs │ │ ├── ItemStringHelper.cs │ │ ├── Mob.cs │ │ ├── MobElemAttr.cs │ │ ├── Npc.cs │ │ ├── Potential.cs │ │ ├── Recipe.cs │ │ ├── RecipeItemInfo.cs │ │ ├── RecipePropType.cs │ │ ├── SetItem.cs │ │ ├── SetItemActiveSkill.cs │ │ ├── SetItemBonusByTime.cs │ │ ├── SetItemEffect.cs │ │ ├── SetItemIDList.cs │ │ ├── SetItemIDPart.cs │ │ ├── SetItemOptionToMob.cs │ │ ├── Skill.cs │ │ ├── SummaryParams.cs │ │ └── SummaryParser.cs │ ├── Config/ │ │ ├── ConfigArrayList.cs │ │ ├── ConfigItem.cs │ │ ├── ConfigItemCollectionBase.cs │ │ ├── ConfigManager.cs │ │ ├── ConfigSectionBase.cs │ │ └── SectionNameAttribute.cs │ ├── Controls/ │ │ ├── AlphaForm.cs │ │ ├── AnimationClipOptions.cs │ │ ├── AnimationControl.Designer.cs │ │ ├── AnimationControl.cs │ │ ├── AnimationItem.cs │ │ ├── AnimationItemEventArgs.cs │ │ ├── AnimationRecoder.cs │ │ ├── FrmProgressDialog.Designer.cs │ │ ├── FrmProgressDialog.cs │ │ ├── FrmProgressDialog.resx │ │ ├── GraphicsDeviceControl.cs │ │ ├── GraphicsDeviceService.cs │ │ ├── ProgressDialog.cs │ │ ├── ProgressDialogContext.cs │ │ └── ServiceContainer.cs │ ├── Encoders/ │ │ ├── BuildInApngEncoder.cs │ │ ├── BuildInGifEncoder.cs │ │ ├── FFmpegEncoder.cs │ │ ├── GifEncoder.cs │ │ ├── GifEncoderCompatibility.cs │ │ └── IndexGifEncoder.cs │ ├── Gif.cs │ ├── GifCanvas.cs │ ├── GifFrame.cs │ ├── GifLayer.cs │ ├── GlobalFindNodeFunction.cs │ ├── IGifFrame.cs │ ├── ImageDataObject.cs │ ├── Properties/ │ │ └── AssemblyInfo.cs │ ├── Rendering/ │ │ ├── AnimationGraphics.cs │ │ ├── BlendEx.cs │ │ ├── D2DContext.cs │ │ ├── D2DFactory.cs │ │ ├── D2DFont.cs │ │ ├── D2DRenderer.cs │ │ ├── DxExtension.cs │ │ ├── Effect/ │ │ │ ├── EffectCompiler.bat │ │ │ ├── Macros.fxh │ │ │ ├── PngEffect.3.8.0.1641.mgfxo │ │ │ ├── PngEffect.3.8.1.303.mgfxo │ │ │ ├── PngEffect.3.8.2.1105.mgfxo │ │ │ └── PngEffect.fx │ │ ├── EffectCompiler/ │ │ │ ├── ConstantBuffer.cs │ │ │ ├── Internal/ │ │ │ │ ├── ConstantBufferData.cs │ │ │ │ ├── D3DXObjects.cs │ │ │ │ ├── EffectObject.cs │ │ │ │ └── ShaderData.cs │ │ │ ├── SampleInfo.cs │ │ │ ├── ShaderConverter.cs │ │ │ └── ShaderStage.cs │ │ ├── MonogameUtils.cs │ │ ├── PngEffect.cs │ │ ├── SpriteBatchEx.cs │ │ ├── SurfaceFormatEx.cs │ │ ├── TextUtils.cs │ │ ├── Texture2DEx.cs │ │ ├── WzLibExtension.cs │ │ ├── XnaFont.cs │ │ └── XnaFontRenderer.cs │ ├── SpineLoader.cs │ ├── StringLinker.cs │ ├── StringResult.cs │ ├── Text/ │ │ ├── DocumentElements.cs │ │ ├── Parser.cs │ │ ├── TextAlignment.cs │ │ └── TextRenderer.cs │ ├── VpxVideoDecoder.cs │ ├── WzComparerR2.Common.csproj │ └── Wz_NodeExtension2.cs ├── WzComparerR2.LuaConsole/ │ ├── AppSyntaxModeProvider.cs │ ├── Config/ │ │ └── LuaConsoleConfig.cs │ ├── Entry.cs │ ├── Examples/ │ │ ├── DumpAnimations.lua │ │ ├── DumpImages.lua │ │ ├── DumpSounds.lua │ │ ├── DumpXml.lua │ │ └── Helper.lua │ ├── FrmConsole.Designer.cs │ ├── FrmConsole.cs │ ├── FrmConsole.resx │ ├── FrmLuaEditor.Designer.cs │ ├── FrmLuaEditor.cs │ ├── FrmLuaEditor.resx │ ├── Lua/ │ │ └── CLRPackage.lua │ ├── LuaSandbox.cs │ ├── Properties/ │ │ ├── AssemblyInfo.cs │ │ ├── Resources.Designer.cs │ │ └── Resources.resx │ ├── Resources/ │ │ ├── Lua.xshd │ │ └── SharpLua.xshd │ └── WzComparerR2.LuaConsole.csproj ├── WzComparerR2.MapRender/ │ ├── Animation/ │ │ ├── IStateMachineAnimationData.cs │ │ ├── RepeatableFrameAnimationData.cs │ │ ├── RepeatableFrameAnimator.cs │ │ └── StateMachineAnimator.cs │ ├── Camera.cs │ ├── Chat.cs │ ├── Config/ │ │ └── MapRenderConfig.cs │ ├── Coroutine.cs │ ├── Effects/ │ │ ├── EffectResources.cs │ │ ├── NativeShaderDesc.cs │ │ ├── Resources/ │ │ │ └── Native/ │ │ │ ├── GrayTintWorldMask │ │ │ ├── alpha │ │ │ ├── alphaMap │ │ │ ├── alphaMaps │ │ │ ├── blurRadial │ │ │ ├── blurX │ │ │ ├── blurY │ │ │ ├── colorModulate │ │ │ ├── default │ │ │ ├── glitch │ │ │ ├── grayScale │ │ │ ├── lens │ │ │ ├── light │ │ │ ├── overlayBlend │ │ │ ├── pointLight │ │ │ ├── refract │ │ │ ├── rope │ │ │ ├── textureBinaryMask │ │ │ ├── textureMask │ │ │ ├── vs_position_color_texture │ │ │ ├── waterBack │ │ │ ├── waterFront │ │ │ └── wave │ │ └── ShaderMaterials.cs │ ├── Entry.cs │ ├── FpsCounter.cs │ ├── FrmMapRender.cs │ ├── FrmMapRender2.SceneManager.cs │ ├── FrmMapRender2.SceneRendering.cs │ ├── FrmMapRender2.cs │ ├── GameExt.cs │ ├── IRandom.cs │ ├── ITooltip.cs │ ├── IWcR2Font.cs │ ├── InputState.cs │ ├── LifeInfo.cs │ ├── LightRenderer.cs │ ├── LineListMesh.cs │ ├── MapData.cs │ ├── MapEvent.cs │ ├── MapLight.cs │ ├── MapRenderFonts.cs │ ├── MapScene.cs │ ├── MathHelper2.cs │ ├── MeshBatcher.cs │ ├── MeshItem.cs │ ├── MiniMap.cs │ ├── MouseButton.cs │ ├── MsCustomSprite.cs │ ├── MsSpriteRenderer.cs │ ├── Music.cs │ ├── Particle.cs │ ├── ParticleDesc.cs │ ├── ParticleDesc1.cs │ ├── ParticleDesc3.cs │ ├── ParticleEmitter.cs │ ├── ParticleRandom.cs │ ├── ParticleSystem.cs │ ├── PatchVisibility.cs │ ├── Patches/ │ │ ├── BackPatch.cs │ │ ├── FootholdPatch.cs │ │ ├── LadderRopePatch.cs │ │ ├── LifePatch.cs │ │ ├── ObjTilePatch.cs │ │ ├── PortalPatch.cs │ │ ├── ReactorPatch.cs │ │ ├── RenderObjectType.cs │ │ ├── RenderPatch.cs │ │ └── TooltipPatch.cs │ ├── Patches2/ │ │ ├── BackItem.cs │ │ ├── FootholdItem.cs │ │ ├── IlluminantClusterItem.cs │ │ ├── ItemEvent.cs │ │ ├── LadderRopeItem.cs │ │ ├── LifeItem.cs │ │ ├── ObjItem.cs │ │ ├── ParticleItem.cs │ │ ├── PortalItem.cs │ │ ├── QuestInfo.cs │ │ ├── ReactorItem.cs │ │ ├── SceneItem.cs │ │ ├── SkyWhaleItem.cs │ │ ├── TileItem.cs │ │ └── TooltipItem.cs │ ├── Properties/ │ │ ├── AssemblyInfo.cs │ │ ├── Resources.Designer.cs │ │ └── Resources.resx │ ├── RenderAnimate.cs │ ├── RenderArgs.cs │ ├── RenderEnv.cs │ ├── RenderFrame.cs │ ├── ResourceLoader.cs │ ├── SceneNode.cs │ ├── TextMesh.cs │ ├── TextureAtlas.cs │ ├── TextureLoader.cs │ ├── TileMode.cs │ ├── UI/ │ │ ├── ColorWConverter.cs │ │ ├── HitMap.cs │ │ ├── ITooltipTarget.cs │ │ ├── LCRBrush.cs │ │ ├── MapRenderButtonStyle.cs │ │ ├── MapRenderResourceKey.cs │ │ ├── MapRenderUIRoot.cs │ │ ├── MessageBoxBackgroundBrush.cs │ │ ├── MessageBoxStyle.cs │ │ ├── NineFormResource.cs │ │ ├── NinePatchBrush.cs │ │ ├── TCBBrush.cs │ │ ├── TextBoxEx.cs │ │ ├── Tooltip.cs │ │ ├── Tooltip2.cs │ │ ├── TooltipHelper.cs │ │ ├── UIChatBox.cs │ │ ├── UIDanmaku.cs │ │ ├── UIGraphics.cs │ │ ├── UIHelper.cs │ │ ├── UIMiniMap.cs │ │ ├── UIMinimap2.cs │ │ ├── UIMirrorFrame.cs │ │ ├── UIOptions.cs │ │ ├── UITeleport.cs │ │ ├── UITopBar.cs │ │ ├── UIWorldMap.cs │ │ ├── WcR2Engine.cs │ │ ├── WcR2Renderer.cs │ │ └── WindowEx.cs │ └── WzComparerR2.MapRender.csproj ├── WzComparerR2.Network/ │ ├── Contracts/ │ │ ├── ByteArrayConverter.cs │ │ ├── PackCryptReq.cs │ │ ├── PackCryptResp.cs │ │ ├── PackCustomPackage.cs │ │ ├── PackGetAllUsersReq.cs │ │ ├── PackGetAllUsersResp.cs │ │ ├── PackGetServerInfoReq.cs │ │ ├── PackGetServerInfoResp.cs │ │ ├── PackHeartBeat.cs │ │ ├── PackLoginReq.cs │ │ ├── PackLoginResp.cs │ │ ├── PackOnChat.cs │ │ ├── PackOnCustomPackage.cs │ │ ├── PackOnServerMessage.cs │ │ ├── PackOnUserUpdate.cs │ │ ├── PackSendChat.cs │ │ ├── PackUserProfileUpdateReq.cs │ │ └── TypeNameBinder.cs │ ├── Entry.cs │ ├── Log.cs │ ├── LoggerForm.Designer.cs │ ├── LoggerForm.cs │ ├── LoggerForm.resx │ ├── NativeMethods.cs │ ├── NetworkConfig.cs │ ├── Properties/ │ │ └── AssemblyInfo.cs │ ├── RC4CryptoServiceProvider.cs │ ├── RingBufferStream.cs │ ├── WcClient.cs │ └── WzComparerR2.Network.csproj ├── WzComparerR2.PluginBase/ │ ├── FindWzEventArgs.cs │ ├── FindWzEventHandler.cs │ ├── PluginContext.cs │ ├── PluginContextProvider.cs │ ├── PluginEntry.cs │ ├── PluginInfo.cs │ ├── PluginManager.cs │ ├── Properties/ │ │ └── AssemblyInfo.cs │ ├── WzComparerR2.PluginBase.csproj │ ├── WzNodeEventArgs.cs │ └── WzStructureEventArgs.cs ├── WzComparerR2.Updater/ │ ├── App.config │ ├── Program.cs │ ├── Properties/ │ │ ├── AssemblyInfo.cs │ │ ├── Resources.Designer.cs │ │ └── Resources.resx │ ├── WzComparerR2.Updater.csproj │ └── app.manifest ├── WzComparerR2.WzLib/ │ ├── Compatibility/ │ │ ├── WzDirStringReader.cs │ │ ├── WzOffsetCalc.cs │ │ ├── WzPreReadProvider.cs │ │ ├── WzVersionProfile.cs │ │ └── WzVersionVerifier.cs │ ├── Cryptography/ │ │ ├── ChaCha20CryptoTransform.cs │ │ └── Snow2CryptoTransform.cs │ ├── IMapleStoryBlob.cs │ ├── IMapleStoryFile.cs │ ├── IMapleStoryFileEntry.cs │ ├── Interop.cs │ ├── Mcv_Types.cs │ ├── Ms_Entry.cs │ ├── Ms_File.cs │ ├── Ms_FileV2.cs │ ├── Ms_Header.cs │ ├── Ms_Image.cs │ ├── Ms_ImageV2.cs │ ├── Properties/ │ │ └── AssemblyInfo.cs │ ├── Utilities/ │ │ ├── ChunkedEncryptedInputStream.cs │ │ ├── CollectionsMarshal.cs │ │ ├── ConcatenatedStream.cs │ │ ├── IWzDecrypter.cs │ │ ├── IWzStringPool.cs │ │ ├── ImageCodec.cs │ │ ├── MathHelper.cs │ │ ├── PartialStream.cs │ │ ├── SimpleWzStringPool.cs │ │ ├── StreamExtension.cs │ │ ├── WzBinaryReader.cs │ │ └── WzStreamReader.cs │ ├── WzComparerR2.WzLib.csproj │ ├── Wz_Capabilities.cs │ ├── Wz_Convex.cs │ ├── Wz_Crypto.cs │ ├── Wz_Directory.cs │ ├── Wz_File.cs │ ├── Wz_Header.cs │ ├── Wz_Image.cs │ ├── Wz_Node.cs │ ├── Wz_Png.cs │ ├── Wz_RawData.cs │ ├── Wz_Sound.cs │ ├── Wz_SoundType.cs │ ├── Wz_Structure.cs │ ├── Wz_Type.cs │ ├── Wz_Uol.cs │ ├── Wz_Vector.cs │ └── Wz_Video.cs ├── WzComparerR2.sln └── azure-pipelines.yml ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitattributes ================================================ # Auto detect text files and perform LF normalization * text=auto # Custom for Visual Studio *.cs diff=csharp # Standard to msysgit *.doc diff=astextplain *.DOC diff=astextplain *.docx diff=astextplain *.DOCX diff=astextplain *.dot diff=astextplain *.DOT diff=astextplain *.pdf diff=astextplain *.PDF diff=astextplain *.rtf diff=astextplain *.RTF diff=astextplain ================================================ FILE: .gitignore ================================================ ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. # User-specific files *.suo *.user *.userosscache *.sln.docstates # User-specific files (MonoDevelop/Xamarin Studio) *.userprefs # Build results [Dd]ebug/ [Dd]ebugPublic/ [Rr]elease/ [Rr]eleases/ x64/ x86/ # build/ bld/ [Bb]in/ [Oo]bj/ # Visual Studo 2015 cache/options directory .vs/ # MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* # NUNIT *.VisualState.xml TestResult.xml # Build Results of an ATL Project [Dd]ebugPS/ [Rr]eleasePS/ dlldata.c *_i.c *_p.c *_i.h *.ilk *.meta *.obj *.pch *.pdb *.pgc *.pgd *.rsp *.sbr *.tlb *.tli *.tlh *.tmp *.tmp_proj *.log *.vspscc *.vssscc .builds *.pidb *.svclog *.scc # Chutzpah Test files _Chutzpah* # Visual C++ cache files ipch/ *.aps *.ncb *.opensdf *.sdf *.cachefile # Visual Studio profiler *.psess *.vsp *.vspx # TFS 2012 Local Workspace $tf/ # Guidance Automation Toolkit *.gpState # ReSharper is a .NET coding add-in _ReSharper*/ *.[Rr]e[Ss]harper *.DotSettings.user # JustCode is a .NET coding addin-in .JustCode # TeamCity is a build add-in _TeamCity* # DotCover is a Code Coverage Tool *.dotCover # NCrunch _NCrunch_* .*crunch*.local.xml # MightyMoose *.mm.* AutoTest.Net/ # Web workbench (sass) .sass-cache/ # Installshield output folder [Ee]xpress/ # DocProject is a documentation generator add-in DocProject/buildhelp/ DocProject/Help/*.HxT DocProject/Help/*.HxC DocProject/Help/*.hhc DocProject/Help/*.hhk DocProject/Help/*.hhp DocProject/Help/Html2 DocProject/Help/html # Click-Once directory publish/ # Publish Web Output *.[Pp]ublish.xml *.azurePubxml # TODO: Comment the next line if you want to checkin your web deploy settings # but database connection strings (with potential passwords) will be unencrypted *.pubxml *.publishproj # NuGet Packages *.nupkg # The packages folder can be ignored because of Package Restore **/packages/* # except build/, which is used as an MSBuild target. !**/packages/build/ # Uncomment if necessary however generally it will be regenerated when needed #!**/packages/repositories.config # Windows Azure Build Output csx/ *.build.csdef # Windows Store app package directory AppPackages/ # Others *.[Cc]ache ClientBin/ [Ss]tyle[Cc]op.* ~$* *~ *.dbmdl *.dbproj.schemaview *.pfx *.publishsettings node_modules/ bower_components/ # RIA/Silverlight projects Generated_Code/ # Backup & report files from converting an old project file # to a newer Visual Studio version. Backup files are not needed, # because we have git ;-) _UpgradeReport_Files/ Backup*/ UpgradeLog*.XML UpgradeLog*.htm # SQL Server files *.mdf *.ldf # Business Intelligence projects *.rdl.data *.bim.layout *.bim_*.settings # Microsoft Fakes FakesAssemblies/ # Node.js Tools for Visual Studio .ntvs_analysis.dat # Visual Studio 6 build log *.plg # Visual Studio 6 workspace options file *.opt # Ignore WcR2 temp directory OldVer/ !References/x86/ !References/x64/ ================================================ FILE: .gitmodules ================================================ [submodule "CharaSimResource"] path = CharaSimResource url = https://github.com/Kagamia/CharaSimResource.git ================================================ FILE: Build/Common.props ================================================ True false latest disable false false false disable CA1416 core 3.8.2.1105 8.0.11 disable CA1416 core 3.8.1.303 6.0.0 true framework 3.8.0.1641 4.2.0 8.0.0 true ================================================ FILE: Build/WcR2Plugin.targets ================================================ $(SolutionDir)WzComparerR2\bin\$(Configuration)\$(TargetFramework) $(MainProgramOutputDir)\Plugin\$(MSBuildProjectName) $(MainProgramOutputDir)\Lib ================================================ FILE: LICENSE ================================================ The MIT License (MIT) Copyright © 2015 Kagamia Studio Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ *使用前先大喊 niconiconi! poi! duang!以减少bug发生率* [![Build Status](https://dev.azure.com/kagamiastudio/WzComparerR2/_apis/build/status/Kagamia.WzComparerR2?branchName=master)](https://dev.azure.com/kagamiastudio/WzComparerR2/_build/latest?definitionId=4&branchName=master) # Maintenance Status ⚠️ The WzComparerR2 project is now in deep maintenance status. This means that only critical bugs or wz file format breaking changes are being considered for inclusion by owner. Expect slow replies to issues. # WzComparerR2 这是一个用C# latest/.Net4.62+.Net8组装的冒险岛提取器... 包含了一些奇怪的机能比如stringWZ搜索 客户端对比 装备模拟 地图模拟等等.. tips: WcR2将尽力维持每周更新,Releases里**不会**提供最稳定版下载,最新版会通过azure-pipeline自动发布。 links: [\[更新日志\]](https://github.com/Kagamia/WzComparerR2/tree/master/UpdateLogs) [\[版本计划\]](https://github.com/Kagamia/WzComparerR2/wiki/Roadmap) [\[最新版下载\]](https://github.com/Kagamia/WzComparerR2/releases/tag/ci-build) # Modules - **WzComparerR2** 主程序 - **WzComparerR2.Common** 一些通用类 - **WzComparerR2.PluginBase** 插件管理器 - **WzComparerR2.WzLib** wz文件读取相关 - **CharaSimResource** 用于装备模拟的资源文件 - **WzComparerR2.LuaConsole** (可选插件)Lua控制台 - **WzComparerR2.MapRender** (可选插件)地图仿真器 - **WzComparerR2.Avatar** (可选插件)纸娃娃 - **WzComparerR2.Network** (可选插件)在线聊天室 # Prerequisite - **2.x**: Win7sp1+/.net4.6.2+/dx11.0 - **1.x**: WinXp+/.net2.0+/dx9.0 # Installation ```sh git clone --recurse-submodules -j8 git://github.com/Kagamia/WzComparerR2.git ``` Clone repository with submodules. # Compile - vs2022 or higher/.net 8 SDK # Credits and Acknowledgement - **Fiel** ([Southperry](http://www.southperry.net)) wz文件读取代码改造自WzExtract 以及WzPatcher - **Index** ([Exrpg](http://bbs.exrpg.com/space-uid-137285.html)) MapRender的原始代码 以及libgif - **Deneo** For .ms file format and video format - [DotNetBar](http://www.devcomponents.com/) - [SharpDX](https://github.com/sharpdx/SharpDX) & [Monogame](https://github.com/MonoGame/MonoGame) - [BassLibrary](http://www.un4seen.com/) - [IMEHelper](https://github.com/JLChnToZ/IMEHelper) - [Spine-Runtime](https://github.com/EsotericSoftware/spine-runtimes) - [EmptyKeysUI](https://github.com/EmptyKeys) - [libvpx](https://www.webmproject.org/code/) & [libyuv](https://chromium.googlesource.com/libyuv/libyuv/) for video decoding - [VC-LTL5](https://github.com/Chuyu-Team/VC-LTL5) for native library build - All testers from CMST tester group. ================================================ FILE: UpdateLogs/dev.md ================================================ ## 2018.7.5 ### 共通 - 添加了一个api以利于插件输出error.log。 - 更新了buildin gif encoder以修复某些场合导出错误的bug。 ### WzLib - 修复了上次修复Uol链接推断的bug。 - 修复了上次新增支持读取独立的img文件的bug。 ### CharaSim - 修复某些动作下发型渲染错误的bug。 - 支持了宠物套装的渲染,添加宠物过期时间。 - 支持显示GMS按时间奖励的套装效果。 ### MapRender - 调整了粒子系统渲染效果。 - Worldmap支持了KMST1070的QuestLimit分阶段渲染机制。 ## 2018.6.20 ### 共通 - 支持了按照imgID进行排序。 - 配置文件中默认开启wz自动排序和自动加载扩展wz。 - File-Option里添加了一些新的配置项。 ### WzLib - 修复了wz类型推断的bug。 - 修复Uol链接推断的bug。 - 支持读取独立的img文件。 - 支持跳过img校验和检测,以识别老旧版本的客户端。 ### CharaSim - 称号支持模拟包含任务数量宏字符串。 - 添加Ark特殊装备类型的模拟。 - 添加KMST1069的新属性nbdR识别。 ### MapRender - 修复了阿斯旺地图特有的wz怪物声明的识别。 ## 2018.4.25 ### WzLib - 重新设计数据结构,以减少内存占用。 ### CharaSim - 支持套装显示点装图标。 - 支持徽章的tag显示。 - 龙神的v5技能模拟可以超过等级上限了。 - 重新设计StringLinker数据结构,以减少内存占用。 ### MapRender - 修复了MapRender窗口反复开关引发的的内存泄露。 ## 2018.4.13 ### 共通 - 修复了BGR565格式纹理对于win8前系统的支持。 ### MapRender - 屏蔽了因输入框失去焦点导致的按键处理异常的错误。 ### Patcher - 添加了CMS的补丁地址。 ## 2018.4.10 ### MapRender - 修复下拉菜单点击无效的bug。 - 支持更多交互命令。 ## 2018.4.9 ### MapRender - 支持隐藏NPC和怪物名字。 - 更新了MessageBox样式。 - 修复了UIChatBox渲染模糊和操作上的bug。 ## 2018.4.8 ### WzLib - 修复了format517图片解码错误的bug。 ### MapRender - 添加UIMessageBox用于各种提示信息。 - 添加UIChatBox,并支持了输入法,用于交互和显示提示。 - 临时修复粒子系统的渲染错误。 - UIWorldmap支持右键返回。 - 修复了back图层的渲染错误,支持blend模式。 - UI布局略微调整。 ## 2018.3.23 ### CharaSim - 支持了KMST1066版本拆分的03xx.img物品识别。 ## 2018.3.22 ### Wz提取 - 修复了一个可能导致Gif/Apng导出有锯齿的bug。 ### WzLib - 修复了GetValue()导致搜索效率低下的bug。 - 调整string.intern过滤条件,减少字符串池常驻内存占用。 ### MapRender - UIWorldMap支持更丰富的tooltip信息,并且支持点击传送了。 - 修复UI绘图资源的潜在内存泄露。 ================================================ FILE: UpdateLogs/v2.0.1.md ================================================ WzComparerR2 2.0.1 ================================== ##共通: - 程序已升级至C#6.0/.net4.0编译 - 程序已支持anyCPU编译 wcR2.exe(32位) wcR2.anyCPU.exe(32/64位自适应) - 配置文件系统完全重制 旧有文件废弃并不继承 新的配置文件为setting.config - 图片浏览模块完全重制 使用Monogame 3.4/DX11作为绘图引擎 - 图片保存模块完全重制 原因参考上一条 - 为了支持上述环境 程序最低运行环境为win7以上/显示卡支持dx11以上 - 目前没有计划重新支持xp/GL环境 但是理论上是可以支持的 您可以自行编译试试:) ##基础提取: - 支持dxt5图片格式 - 支持CMST115版本后的套装属性合并的显示方式 - 支持KMST1033版本后的图片链接方式 - 支持Spine动画预览 方式为选择.altas节点 点击ExtractGif按钮 ##UI变化: - HandleUol按钮移动到wz内容浏览的右键菜单中 - Gif保存添加了更多选项 包括图片背景和文件命名方式 - 图片的拖拽保存需要按住ctrl才会执行了 普通的拖拽只是移动而已:) ##CharaSim: - 爆破手职业装备支持 - 勋章预览支持 - 怪物卡片 NPC卡片功能已内置 - 技能等级上限提升至100 - 属性表达式计算器完全重制 如果有bug请报告 - 添加了更多的显示控制项 自行感受 ##MapRender: - 插件已废弃 准备重制 ##MonsterCard: - 插件已废弃 部分功能合并到主程序 ================================================ FILE: UpdateLogs/v2.0.9.md ================================================ ## 2017.7.6 ### MapRender - 更新至正式版本。 ## 2017.6.25 ### SoundPlayer - 重新支持Bass插件,把插件放置在Lib/x86和Lib/x64中生效。 ### MapRender2 - 更新了一个测试版本。 ## 2017.6.18 ### Avatar - 修复了一个因主程序更新无法初始化的bug。 ## 2017.6.11 ### WzComparer - Options实装,添加了一些全局设置,以解决GMS无法正确解析字符串的bug。 ### Patcher - 修复了多开时config文件保存不正确的bug ## 2017.6.2 ### Comparer - 使用大小写敏感的方式寻找link,大幅度优化对比速度。 - 修复了一个对比过程中手动回收img可能导致错误的bug。 - 稍微调整了输出文件样式,默认隐藏掉因开启ResolvePngLink无变动项的img对比结果。 ## 2017.5.12 ### Comparer - 修复了在补丁对比过程中ResolvePngLink无法正常工作的bug。 ## 2017.4.25 ### CharaSim - 装备模拟:能正确显示多行的套装效果了。 ## 2017.3.20 ### Comparer - 修复了开启ResolvePngLink会出现OutOfMemoryException的bug,现在在对比中打开的image会自动回收了。 ### WzLib - 优化了忽略大小写的wz节点搜索方法。 ### CharaSim - 装备模拟:能量源重新正确的识别了。 ## 2017.3.15 ### CharaSim - 装备模拟:支持识别更多特性,支持多行装备特性的排版。 - 修复上版本装备加载异常的bug。 - 默认字体调整回“宋体”。 ## 2017.3.14 积累更新 ### WzComparer - 优化了wz节点搜索效率,不再突然卡死了。 - 关于里面可以看到插件的文件版本了。 ### WzLib - 添加了自定义Encoding的支持,用于特定场合下解决ansi字符串解析的bug。 ### CharaSim - 更新支持同步至最新CMST。 - 装备模拟:支持显示装备属性与标准属性差异,支持限时属性的模拟。 - 技能模拟:链接属性时不再识别数字开头的属性名称,以修复某些场合的解析bug。 ### Comparer - 支持对于Link的图片智能链接对比,以减少输出对比报告的文件大小。 ## 2017.1.11 积累更新 ### 共通 - 绘图引擎更新,大部分场合使用Bgra32替代Color作为后备绘图缓冲区像素格式,以提高文件保存与加载效率。 ### CharaSim - 迎接第5次转职,更新技能/道具/装备的仿真效果至最新CMST。 - 为了支持CharaSim其他语言版本开发,更新了支持非等宽字体和word-wrap的排版算法。 - 默认字体由宋体改为新宋体。 - 添加了一个韩文字体供测试。 - 装备模拟:移除魔法防御力/命中/回避,调整关于暴击伤害的显示,添加支持戒指特殊潜能显示。 - 道具模拟:添加道具等级显示,支持道具限时的显示。 - 略微调整文字坐标以适配最新游戏效果。 ### Issue讨论中未更新内容 - #20:Ansi编码导致某些字符串显示错误问题(已确认,待解决) - #19:装备特殊潜能等级的显示问题(CMS未确认) - #15:技能模拟中宏变量引用无视大小写导致链接错误问题(GMS已确认,未更新) - #14:套装模拟中文字范围溢出错误(已确认,未更新) ## 2016.10.27 ### WzLib - 修复了因CMS加密方式变更 导致Lua无法正常提取的bug ## 2016.10.18 ### WzLib - img导出xml移至WzLib作为扩展函数出现 - 修复了xml导出时对于SoundType.Binary无法正常导出的bug ### LuaConsole - 实现lua文件载入和保存按钮功能 - 添加了一个使用脚本批量导出的example ## 2016.10.08 ### 共通 - 添加了可以对img导出为xml的功能 ## 2016.09.26 ### CharaSim - 修复Item无法识别link没有图标的bug ## 2016.09.16 ### 共通 - wzlib读取结构变更 Wz_Image.Node.Value不再指向Wz_Image自身的引用 - 修复因上一条导致的若干运行效果不正确的场合 ### WzCompare - 支持了CMS/TMS等wz合并对比时同名节点冲突的场合,目前可以分别对比 ### CharaSim - 修复恶魔盾牌MP/DF显示bug ### LuaConsole - 添加了可以获取全局插件环境和事件的接口 因此在Lua中可以获取当前选中的Wz节点了 ## 2016.08.11 ### 共通 - 整理Lua的提取方式 直接绑定在顶层节点上 可能会影响对比 ### Avatar - 修复了纸娃娃无法识别Link的Bug ## 2016.08.07 ### 共通 - 临时支持了CMST117新增的Lua节点特殊格式 ##2016.07.31 ### 共通 - 修复了JMSwz无法解析的bug - 加入了一个设置项 允许wz打开时自动排序 这个设置项可通过手动编辑setting文件配置 - wz显示的部分代码整理 - 动画部分代码整理 以兼容新的MapRender ### CharaSim - 支持显示KMST新增属性incARC ### Patcher - 支持了新的64位长度标记的Patch.exe文件格式 ## 2016.07.11 ### 共通 - 修复了IndexGifEncoder编码纯色帧导致gif文件损坏的bug - 修复了MonoGame多Device共存可能出现的bug ### CharaSim - Eval计算支持了KMST新的log公式,说不定未来会用到 - 修复了属性Eval计算时因公式出现空白符解析失败的bug ### MapRender - Xna引擎升级至MonoGame,功能部分恢复到之前的版本,并未支持KMST的压缩版客户端资源链接方式 - 暂时移除没用的输入法和聊天模块 ================================================ FILE: UpdateLogs/v2.1.1.md ================================================ ## 2018.3.15 久违的积累更新 ### 共通 - 支持了Apng格式动画导出 ### WzLib - 支持自动检测扩展wz(如map2, map001)的设置 ### CharaSim - 对于V5技能模拟,提高等级上限至100。 - 支持新职业装备,更新模拟效果至最新的CMST。 - 对于装备附带技能,可以在模拟中显示技能说明了。 - 支持互斥装备的显示说明。(ExclusiveEquip) - 修复了bodyattack默认值的错误。 - 改进怪物卡数值的显示格式。 ### Comparer - 修复了在应用补丁中同类型wz对比时,输出文件覆盖的bug。 ### Avatar - 支持arc职业耳朵的模拟。 ### MapRender - 支持了D2D渲染引擎的支持,可用于文字和简单图元渲染。 - 支持了粒子特效渲染。 - 大幅度优化运行效率,内存占用与GC。 - 修复了潜在的内存泄露。 ================================================ FILE: UpdateLogs/v2.1.md ================================================ ## 2017.10.10 积累更新 ### Network - ### Patcher - 追加了全世界冒险岛补丁下载地址。 - 修复了ftp开头的地址无法探测文件更新时间的bug。 ### WzLib - 兼容2G以上的wz文件。 ### CharaSim - 更新至CMST最新版。 ## 2017.8.8 ### Avatar - 修复骑宠相关的大量bug,实验性支持flip,removeBody等特性。 - 修复了z层排序异常的错误。 - 重构缓冲算法,不再因为总图像范围过大无法渲染了。 ## 2017.8.5 ### CharaSim - 支持套装属性的扩展显示和默认装备名称显示。 - 对于封印解除属性显示时自动合并同类属性。 ### Avatar - 支持骑宠的强制身体动作字段。 - 加载骑宠时将自动设置为关联的身体动作和表情。 - 优化了预渲染效率。 ## 2017.8.2 ### 共通 - 支持了渲染Spine骨骼动画的新特性。 ### MapRender - 新增了一个Option界面和配置文件,TopBar回归。 ### CharaSim - 忽略属性表达式中的'%',以强行屏蔽一个bug。 - 修复了宏字符串中'#'会丢失字符的bug。 ### Avatar - 支持了选择ear图层类型,以支持KMST新职业。 ## 2017.7.23 ### 共通 - GearGraphics.TextRenderer重构,排版与渲染分离,支持Monogame。 ### MapRender - 完整支持了UIWorldMap。 ## 2017.7.20 ### 基础 - 修复了技能编号8001xxxx无法自动链接的bug。 ## 2017.7.8 积累更新 ### 共通 - 因为发现了一个特殊的wz结构,调整了全部解决uol引用的代码。这影响到了全部动画加载相关的模块。 ### WzLib - 添加了可以支持自动挂接扩展wz文件(mob2, map2, etc.)的选项。 ### MapRender - 修复了一些可能造成内存泄漏的bug(但并无卵用)。 ================================================ FILE: WzComparerR2/AnimateEncoderFactory.cs ================================================ using System; using System.Collections.Generic; using WzComparerR2.Config; using WzComparerR2.Encoders; namespace WzComparerR2 { public static class AnimateEncoderFactory { static AnimateEncoderFactory() { registeredEncoders = new Dictionary(); RegisterEncoders(); } private static Dictionary registeredEncoders; private static void RegisterEncoders() { registeredEncoders.Add(0, new AnimateEncoderProvider { ID = 0, Name = nameof(BuildInGifEncoder), CreateEncoderCallback = () => new BuildInGifEncoder(), }); registeredEncoders.Add(1, new AnimateEncoderProvider { ID = 1, Name = nameof(IndexGifEncoder), CreateEncoderCallback = () => new IndexGifEncoder(), }); registeredEncoders.Add(2, new AnimateEncoderProvider { ID = 2, Name = nameof(BuildInApngEncoder), CreateEncoderCallback = () => new BuildInApngEncoder(), ConfigureEncoderCallback = (encoder, config) => { encoder.OptimizeEnabled = config.PaletteOptimized; } }); registeredEncoders.Add(3, new AnimateEncoderProvider { ID = 3, Name = nameof(FFmpegEncoder), CreateEncoderCallback = () => new FFmpegEncoder(), ConfigureEncoderCallback = (encoder, config) => { encoder.FFmpegBinPath = config.FFmpegBinPath; encoder.FFmpegArgumentFormat = config.FFmpegArgument; encoder.OutputFileExtension = config.FFmpegOutputFileExtension; } }); } public static GifEncoder CreateEncoder(ImageHandlerConfig config) { return CreateEncoder(config.GifEncoder, config); } public static GifEncoder CreateEncoder(int id, ImageHandlerConfig config) { if (!registeredEncoders.TryGetValue(id, out var provider)) { throw new Exception($"Encoder ID {id} has not registered"); } var encoder = provider.CreateEncoder(); provider.ConfigureEncoder(encoder, config); return encoder; } public interface IAnimateEncoderProvider { GifEncoder CreateEncoder(); void ConfigureEncoder(GifEncoder encoder, ImageHandlerConfig config); } public class AnimateEncoderProvider : IAnimateEncoderProvider where T : GifEncoder { public int ID { get; set; } public string Name { get; set; } public Func CreateEncoderCallback { get; set; } public Action ConfigureEncoderCallback { get; set; } public GifEncoder CreateEncoder() { if (this.CreateEncoderCallback == null) { throw new ArgumentNullException(nameof(CreateEncoderCallback)); } return this.CreateEncoderCallback(); } public void ConfigureEncoder(GifEncoder encoder, ImageHandlerConfig config) { if (this.ConfigureEncoderCallback != null) { this.ConfigureEncoderCallback((T)encoder, config); } } } } } ================================================ FILE: WzComparerR2/CharaSim/CharaEquip.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace WzComparerR2.CharaSim { public class CharaEquip { public CharaEquip() { int slotsCount = 66; gearSlots = new Gear[slotsCount]; cashGearSlots = new Gear[slotsCount]; } public const int RingCount = 6; public const int PendantCount = 2; private Gear[] gearSlots; private Gear[] cashGearSlots; public Gear[] GearSlots { get { return gearSlots; } } public Gear[] CashGearSlots { get { return cashGearSlots; } } public int GetGearSlot(GearType type, int index) { switch (type) { //line 0 case GearType.badge: return 0; case GearType.cap: return 1; case GearType.ring: switch (index) { case 0: return 8; case 1: return 9; case 2: return 23; case 3: return 24; case 4: return 2; case 5: return 7; default: return -1; } case GearType.android: return 3; case GearType.machineHeart: return 4; //line 1 case GearType.medal: return 5; case GearType.faceAccessory: return 6; //line 2 case GearType.pocket: return 10; case GearType.eyeAccessory: return 11; case GearType.pendant: switch (index) { case 0: return 17; case 1: return 12; default: return -1; } case GearType.earrings: return 13; case GearType.shoulderPad: return 14; //line 3 case GearType.cape: return 15; case GearType.coat: case GearType.longcoat: return 16; default: if (Gear.IsLeftWeapon(type) || Gear.IsDoubleHandWeapon(type)) return 18; else if (Gear.IsSubWeapon(type)) return 19; else return -1; //line 4 case GearType.glove: return 20; case GearType.pants: return 21; case GearType.belt: return 22; //line 5 case GearType.shoes: return 27; //dragon case GearType.dragonMask: return 35; case GearType.dragonPendant: return 36; case GearType.dragonWings: return 37; case GearType.dragonTail: return 38; //machine case GearType.machineTransistors: return 39; case GearType.machineEngine: return 40; case GearType.machineBody: return 41; case GearType.machineArms: return 42; case GearType.machineLegs: return 43; //totem case GearType.totem: switch (index) { case 0: return 44; case 1: return 45; case 2: return 46; default: return -1; } } } public IEnumerable GearsEquiped { get { foreach (Gear gear in gearSlots) { if (gear != null) yield return gear; } foreach (Gear gear in cashGearSlots) { if (gear != null) yield return gear; } } } public bool AddGear(Gear gear, out Gear[] removedGears) { if (gear == null) { removedGears = new Gear[0]; return false; } int emptyIdx = GetEmptySlotIndex(gear.type, gear.Cash); return AddGear(gear, emptyIdx, out removedGears); } public bool AddGear(Gear gear, int index, out Gear[] removedGears) { if (gear == null) { removedGears = new Gear[0]; return false; } int slotIdx = GetGearSlot(gear.type, index); if (slotIdx == -1) { removedGears = new Gear[0]; return false; } List removedGearList = new List(); Gear[] slotList = gear.Cash ? cashGearSlots : gearSlots; if (slotList[slotIdx] != null) //移除同槽 { removedGearList.Add(slotList[slotIdx]); } slotList[slotIdx] = gear; //装备上 Gear preRemove = getPreRemoveGears(gear.type, gear.Cash); if (preRemove != null) //移除冲突装备 { removedGearList.Add(preRemove); } removedGears = removedGearList.ToArray(); return true; } public int GetEmptySlotIndex(GearType gearType, bool cash) { Gear[] slotList = cash ? cashGearSlots : gearSlots; int max; switch (gearType) { case GearType.ring: max = RingCount; break; case GearType.pendant: max = PendantCount; break; default: return 0; } for (int i = 0; i < max; i++) { if (slotList[GetGearSlot(gearType, i)] == null) return i; } return 0; } private Gear getPreRemoveGears(GearType newGearType, bool cash) { Gear[] slotList = cash ? cashGearSlots : gearSlots; if (Gear.IsDoubleHandWeapon(newGearType)) //双手 移除副手 { Gear gear = slotList[GetGearSlot(GearType.shield, 0)]; if (gear != null) return gear; } else if (Gear.IsSubWeapon(newGearType)) //副手 移除双手 { Gear gear = slotList[GetGearSlot(GearType.ohSword, 0)]; if (gear != null && (Gear.IsDoubleHandWeapon(gear.type) || (newGearType != GearType.magicArrow && gear.type == GearType.dualBow))) //非魔法箭 移除双弓的主手 return gear; } else if (newGearType == GearType.dualBow) //双弩 移除非魔法箭的副手 { Gear gear = slotList[GetGearSlot(GearType.magicArrow, 0)]; if (gear != null && gear.type != GearType.magicArrow) { return gear; } } else if (newGearType == GearType.pants) //下装 移除套服 { Gear gear = slotList[GetGearSlot(GearType.longcoat, 0)]; if (gear != null && gear.type == GearType.longcoat) { return gear; } } else if (newGearType == GearType.pants) //套服 移除下装 { Gear gear = slotList[GetGearSlot(GearType.pants, 0)]; if (gear != null && gear.type == GearType.pants) { return gear; } } return null; } } } ================================================ FILE: WzComparerR2/CharaSim/CharaProp.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace WzComparerR2.CharaSim { public class CharaProp { public CharaProp() { } public CharaProp(int totalMax) { this.totalMax = totalMax; } private int baseVal; //基础值 private int gearAdd; //装备附加值 private int buffAdd; //技能buff增加值 private int eBuffAdd; //技能增加的enhance值 private int rate; //装备潜能百分比 private int aBuffRate; //主动buff百分比 如骰子 private int pBuffRate; //被动buff百分比 如盾防精通 private int totalMax; private bool smart; //当前的技能buff增加值是否为smart public int BaseVal { get { return baseVal; } set { baseVal = value; } } public int GearAdd { get { return gearAdd; } set { gearAdd = value; } } public int BuffAdd { get { return buffAdd; } set { buffAdd = value; } } public int EBuffAdd { get { return eBuffAdd; } set { eBuffAdd = value; } } public int Rate { get { return rate; } set { rate = value; } } public int ABuffRate { get { return aBuffRate; } set { aBuffRate = value; } } public int PBuffRate { get { return pBuffRate; } set { pBuffRate = value; } } public bool Smart { get { return smart; } set { smart = value; } } public int TotalMax { get { return totalMax; } set { totalMax = value; } } public int GetSum() { int origSum = (baseVal + gearAdd + buffAdd + eBuffAdd) * (100 + rate + aBuffRate + pBuffRate) / 100; return this.totalMax > 0 ? Math.Min(this.totalMax, origSum) : origSum; } public int GetGearReqSum() { int origSum = (baseVal + gearAdd + buffAdd) * (100 + rate + aBuffRate + pBuffRate) / 100; return this.totalMax > 0 ? Math.Min(this.totalMax, origSum) : origSum; } public void ResetAdd() { gearAdd = 0; eBuffAdd = 0; buffAdd = 0; rate = 0; aBuffRate = 0; pBuffRate = 0; smart = false; } public void ResetAll() { baseVal = 0; ResetAdd(); } public override string ToString() { int sum = GetSum(); return baseVal == sum ? baseVal.ToString() : string.Format("{0} ({1}+{2})", sum, baseVal, sum - baseVal); } public string ToStringDetail(out int red) { int sum = GetSum(); int baseSum = (baseVal + gearAdd) + (baseVal + gearAdd + buffAdd + eBuffAdd) * (rate + aBuffRate) / 100; if (buffAdd == 0 && eBuffAdd == 0 && pBuffRate == 0 && baseSum <= sum) { red = Math.Sign(aBuffRate); return baseSum.ToString(); } red = Math.Sign(sum - baseSum); return (sum == baseSum) ? sum.ToString() : string.Format("{0} ({1}{2}{3})", sum, baseSum, (sum - baseSum >= 0) ? "+" : "-", sum - baseSum); } } } ================================================ FILE: WzComparerR2/CharaSim/CharaSimLoader.cs ================================================ using System; using System.Collections.Generic; using System.Text; using System.Drawing; using WzComparerR2.WzLib; using WzComparerR2.PluginBase; namespace WzComparerR2.CharaSim { public static class CharaSimLoader { static CharaSimLoader() { LoadedSetItems = new Dictionary(); LoadedExclusiveEquips = new Dictionary(); } public static Dictionary LoadedSetItems { get; private set; } public static Dictionary LoadedExclusiveEquips { get; private set; } public static void LoadSetItemsIfEmpty() { if (LoadedSetItems.Count == 0) { LoadSetItems(); } } public static void LoadSetItems() { //搜索setItemInfo.img Wz_Node etcWz = PluginManager.FindWz(Wz_Type.Etc); if (etcWz == null) return; Wz_Node setItemNode = etcWz.FindNodeByPath("SetItemInfo.img", true); if (setItemNode == null) return; //搜索ItemOption.img Wz_Node itemWz = PluginManager.FindWz(Wz_Type.Item); if (itemWz == null) return; Wz_Node optionNode = itemWz.FindNodeByPath("ItemOption.img", true); if (optionNode == null) return; LoadedSetItems.Clear(); foreach (Wz_Node node in setItemNode.Nodes) { int setItemIndex; if (Int32.TryParse(node.Text, out setItemIndex)) { SetItem setItem = SetItem.CreateFromNode(node, optionNode); if (setItem != null) LoadedSetItems[setItemIndex] = setItem; } } } public static void LoadExclusiveEquipsIfEmpty() { if (LoadedExclusiveEquips.Count == 0) { LoadExclusiveEquips(); } } public static void LoadExclusiveEquips() { Wz_Node exclusiveNode = PluginManager.FindWz("Etc/ExclusiveEquip.img"); if (exclusiveNode == null) return; LoadedExclusiveEquips.Clear(); foreach (Wz_Node node in exclusiveNode.Nodes) { int exclusiveEquipIndex; if (Int32.TryParse(node.Text, out exclusiveEquipIndex)) { ExclusiveEquip exclusiveEquip = ExclusiveEquip.CreateFromNode(node); if (exclusiveEquip != null) LoadedExclusiveEquips[exclusiveEquipIndex] = exclusiveEquip; } } } public static void ClearAll() { LoadedSetItems.Clear(); LoadedExclusiveEquips.Clear(); } public static int GetActionDelay(string actionName) { if (string.IsNullOrEmpty(actionName)) { return 0; } Wz_Node actionNode = PluginManager.FindWz("Character/00002000.img/" + actionName); if (actionNode == null) { return 0; } int delay = 0; foreach (Wz_Node frameNode in actionNode.Nodes) { Wz_Node delayNode = frameNode.Nodes["delay"]; if (delayNode != null) { delay += Math.Abs(delayNode.GetValue()); } } return delay; } } } ================================================ FILE: WzComparerR2/CharaSim/Character.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace WzComparerR2.CharaSim { public class Character { public Character() { this.status = new CharacterStatus(); this.status.Job = 0; this.status.Level = 1; this.status.MaxHP.BaseVal = 50; this.status.HP = 50; this.status.MaxMP.BaseVal = 10; this.status.MP = 10; this.status.Strength.BaseVal = 12; this.status.Dexterity.BaseVal = 5; this.status.Intelligence.BaseVal = 4; this.status.Luck.BaseVal = 4; this.status.CriticalRate.BaseVal = 5; this.status.MoveSpeed.BaseVal = 100; this.status.Jump.BaseVal = 100; this.status.CriticalDamageMax.BaseVal = 150; this.status.CriticalDamageMin.BaseVal = 120; this.itemSlots = new ItemBase[5][]; for (int i = 0; i < this.itemSlots.Length; i++) { this.itemSlots[i] = new ItemBase[96]; } this.equip = new CharaEquip(); } private static FormulaVersion version; /// /// 获取或设置角色属性计算的公式版本。 /// public static FormulaVersion Version { get { return Character.version; } set { Character.version = value; } } private string name; private string guild; private CharacterStatus status; private ItemBase[][] itemSlots; private CharaEquip equip; public string Name { get { return name; } set { name = value; } } public string Guild { get { return guild; } set { guild = value; } } public CharacterStatus Status { get { return status; } } public ItemBase[][] ItemSlots { get { return itemSlots; } } public CharaEquip Equip { get { return equip; } set { equip = value; } } public void UpdateProps() { status.Strength.ResetAdd(); status.Dexterity.ResetAdd(); status.Intelligence.ResetAdd(); status.Luck.ResetAdd(); status.MaxHP.ResetAdd(); status.MaxMP.ResetAdd(); status.PADamage.ResetAll(); status.MADamage.ResetAll(); status.PDDamage.ResetAll(); status.MDDamage.ResetAll(); status.PAccurate.ResetAll(); status.MAccurate.ResetAll(); status.PEvasion.ResetAll(); status.MEvasion.ResetAll(); status.MoveSpeed.ResetAdd(); status.Jump.ResetAdd(); status.CriticalRate.ResetAdd(); status.CriticalDamageMax.ResetAdd(); status.CriticalDamageMin.ResetAdd(); status.DamageRate.ResetAll(); //foreach (Buff buff in buffs) //{ // foreach (KeyValuePair prop in buff.props) // { // AddBuffProp(prop.Key, prop.Value, buff.Type == BuffType.passiveSkill); // } //} foreach (Gear gear in equip.GearsEquiped) { if (gear.State == GearState.enable) { foreach (KeyValuePair prop in gear.Props) { addProp(prop.Key, prop.Value); } foreach (Potential potential in gear.Options) { if (potential != null) { foreach (KeyValuePair prop in potential.props) { addProp(prop.Key, prop.Value); } } } foreach (Addition addition in gear.Additions) { foreach (KeyValuePair prop in getAdditionProps(addition)) { addProp(prop.Key, prop.Value); } } } } checkSetItemEnabled(); foreach (SetItem setItem in CharaSimLoader.LoadedSetItems.Values) { foreach (SetItemEffect effect in setItem.Effects.Values) { if (effect.Enabled) { foreach (KeyValuePair prop in effect.Props) { if (prop.Key == GearPropType.Option) { List potens = prop.Value as List; foreach (Potential p in potens) { foreach (KeyValuePair pprop in p.props) { addProp(pprop.Key, pprop.Value); } } } else { addProp(prop.Key, Convert.ToInt32(prop.Value)); } } } } } int[] sum = new int[4] { status.Strength.GetSum(), status.Dexterity.GetSum(), status.Intelligence.GetSum(), status.Luck.GetSum() }; if (version == FormulaVersion.Bigbang) { status.PDDamage.BaseVal = (int)Math.Floor(sum[0] * 1.2 + sum[1] * 0.5 + sum[2] * 0.4 + sum[3] * 0.5); status.MDDamage.BaseVal = (int)Math.Floor(sum[0] * 0.4 + sum[1] * 0.5 + sum[2] * 1.2 + sum[3] * 0.5); status.PAccurate.BaseVal = (int)Math.Floor(sum[1] * 1.2 + sum[3] * 1.0); status.MAccurate.BaseVal = (int)Math.Floor(sum[2] * 1.2 + sum[3] * 1.0); status.PEvasion.BaseVal = sum[1] * 1 + sum[3] * 2; status.MEvasion.BaseVal = sum[2] * 1 + sum[3] * 2; } else if (version == FormulaVersion.Chaos) { status.PDDamage.BaseVal = (int)Math.Floor(sum[0] * 1.5 + sum[1] * 0.4 + sum[2] * 0 + sum[3] * 0.4); status.MDDamage.BaseVal = (int)Math.Floor(sum[0] * 0 + sum[1] * 0.4 + sum[2] * 1.5 + sum[3] * 0.4); status.PAccurate.BaseVal = (int)Math.Floor(sum[0] * 0.4 + sum[1] * 1.6 + sum[2] * 0 + sum[3] * 0.8); status.MAccurate.BaseVal = (int)Math.Floor(sum[0] * 0 + sum[1] * 0.4 + sum[2] * 1.6 + sum[3] * 0.8); status.PEvasion.BaseVal = (int)Math.Floor(sum[0] * 0.2 + sum[1] * 0.6 + sum[2] * 0 + sum[3] * 1.4); status.MEvasion.BaseVal = (int)Math.Floor(sum[0] * 0 + sum[1] * 0.2 + sum[2] * 0.6 + sum[3] * 1.4); } } private void addProp(GearPropType type, int value) { switch (type) { case GearPropType.incSTR: status.Strength.GearAdd += value; break; case GearPropType.incSTRr: status.Strength.Rate += value; break; case GearPropType.incDEX: status.Dexterity.GearAdd += value; break; case GearPropType.incDEXr: status.Dexterity.Rate += value; break; case GearPropType.incINT: status.Intelligence.GearAdd += value; break; case GearPropType.incINTr: status.Intelligence.Rate += value; break; case GearPropType.incLUK: status.Luck.GearAdd += value; break; case GearPropType.incLUKr: status.Luck.Rate += value; break; case GearPropType.incAllStat: status.Strength.GearAdd += value; status.Dexterity.GearAdd += value; status.Intelligence.GearAdd += value; status.Luck.GearAdd += value; break; case GearPropType.incPAD: status.PADamage.GearAdd += value; break; case GearPropType.incPADr: status.PADamage.Rate += value; break; case GearPropType.incPDD: status.PDDamage.GearAdd += value; break; case GearPropType.incPDDr: status.PDDamage.Rate += value; break; case GearPropType.incMAD: status.MADamage.GearAdd += value; break; case GearPropType.incMADr: status.MADamage.Rate += value; break; case GearPropType.incMDD: status.MDDamage.GearAdd += value; break; case GearPropType.incMDDr: status.MDDamage.Rate += value; break; case GearPropType.incACC: status.PAccurate.GearAdd += value; status.MAccurate.GearAdd += value; break; case GearPropType.incACCr: status.PAccurate.Rate += value; status.MAccurate.Rate += value; break; case GearPropType.incEVA: status.PEvasion.GearAdd += value; status.MEvasion.GearAdd += value; break; case GearPropType.incEVAr: status.PEvasion.Rate += value; status.MEvasion.Rate += value; break; case GearPropType.incCr: status.CriticalRate.GearAdd += value; break; case GearPropType.incMHP: status.MaxHP.GearAdd += value; break; case GearPropType.incMHPr: status.MaxHP.Rate += value; break; case GearPropType.incMMP: status.MaxMP.GearAdd += value; break; case GearPropType.incMMPr: status.MaxMP.Rate += value; break; case GearPropType.incSpeed: status.MoveSpeed.GearAdd += value; break; case GearPropType.incJump: status.Jump.GearAdd += value; break; case GearPropType.incCriticaldamageMax: status.CriticalDamageMax.GearAdd += value; break; case GearPropType.incCriticaldamageMin: status.CriticalDamageMin.GearAdd += value; break; } } private Dictionary getAdditionProps(Addition addition) { Dictionary props = new Dictionary(); if (addition != null && (addition.Type == AdditionType.critical || addition.Type == AdditionType.statinc)) { bool con = false; switch (addition.ConType) { case GearPropType.reqLevel: con = (this.status.Level >= addition.ConValue[0]); break; case GearPropType.reqJob: foreach (int val in addition.ConValue) { con |= this.status.Job == val; } break; case GearPropType.reqCraft: default: con = true; break; } if (con) { string strcr; int cr; if (addition.Props.TryGetValue("prob", out strcr) && Int32.TryParse(strcr, out cr)) props.Add(GearPropType.incCr, cr); if (addition.Type == AdditionType.statinc) { foreach (var kv in addition.Props) { try { GearPropType propType = (GearPropType)Enum.Parse(typeof(GearPropType), kv.Key); if ((int)propType > 0 && (int)propType < 100) props.Add(propType, Convert.ToInt32(kv.Value)); } catch { } } } } } return props; } private void checkSetItemEnabled() { //重置所有setItem List idList = new List(); foreach (SetItem setItem in CharaSimLoader.LoadedSetItems.Values) { foreach (KeyValuePair idPart in setItem.ItemIDs.Parts) { idList.AddRange(idPart.Value.ItemIDs.Keys); foreach (int id in idList) { idPart.Value.ItemIDs[id] = false; } idList.Clear(); } setItem.currentCount = 0; } //验证有效装备 int setItemID; foreach (Gear gear in equip.GearsEquiped) { if (gear.State == GearState.enable && gear.Props.TryGetValue(GearPropType.setItemID, out setItemID)) { CharaSimLoader.LoadedSetItems[setItemID].ItemIDs[gear.ItemID] = true; CharaSimLoader.LoadedSetItems[setItemID].currentCount++; } } //验证所有setItem foreach (SetItem setItem in CharaSimLoader.LoadedSetItems.Values) { foreach (KeyValuePair effect in setItem.Effects) { effect.Value.Enabled = (setItem.currentCount >= effect.Key); } } } public void ChangeGear(Gear newGear) { int emptyIdx = this.equip.GetEmptySlotIndex(newGear.type, newGear.Cash); ChangeGear(newGear, emptyIdx); } public void ChangeGear(Gear newGear, int index) { ItemBase[] itemTab = this.itemSlots[0]; int newGearIndex = Array.IndexOf(itemTab, newGear); if (newGearIndex < 0 || newGear.State != GearState.itemList) { throw new InvalidOperationException("未知错误:装备不在背包。"); } int onlyEquip; if (newGear.Props.TryGetValue(GearPropType.onlyEquip, out onlyEquip) && onlyEquip > 0) { foreach (Gear gear in this.equip.GearsEquiped) { if (gear.ItemID == newGear.ItemID) { throw new InvalidOperationException("该道具只能同时装备一个。"); } } } string errorString; if (!checkGearReq(newGear, out errorString)) { throw new InvalidOperationException(errorString); } Gear[] removedGear; if (!this.equip.AddGear(newGear, out removedGear)) { throw new InvalidOperationException("未知错误:添加装备失败。"); } CheckGearEnabled(); if (newGear.State == GearState.enable) { Queue emptyItemSlot = new Queue(); emptyItemSlot.Enqueue(newGearIndex); if (removedGear.Length > 1) //检查剩余背包大小 { for (int i = 0; i < itemTab.Length; i++) { if (itemTab[i] == null) { emptyItemSlot.Enqueue(i); } } } if (emptyItemSlot.Count >= removedGear.Length) { for (int i = 0; i < removedGear.Length; i++) { Gear gear = removedGear[i]; gear.State = GearState.itemList; itemTab[emptyItemSlot.Dequeue()] = gear; } return; //函数出口 } else { errorString = "背包已满。"; } } else { errorString = "能力值不足,无法装备道具。"; } //还原装备 foreach (Gear gear in removedGear) { Gear[] arg; this.equip.AddGear(gear, index, out arg); //可以证明直接输入index是可以还原的。 } newGear.State = GearState.itemList; throw new InvalidOperationException(errorString); } private bool checkGearReq(Gear gear, out string errorMessage) { if (Gear.IsMechanicGear(gear.type) && status.Job / 100 != 35) { errorMessage = "只有机械师才能装备。"; return false; } if (Gear.IsDragonGear(gear.type) && status.Job / 100 != 22) { errorMessage = "只有龙神才能装备。"; return false; } if (gear.type == GearType.katara && status.Job / 10 != 43) { errorMessage = "只有暗影双刀才能装备。"; return false; } if (gear.type == GearType.shield && (status.Job / 10 == 43 || status.Job / 100 == 23 || status.Job / 100 == 31)) { errorMessage = "该职业无法装备盾牌。"; return false; } if (gear.type == GearType.magicArrow && status.Job / 100 != 23) { errorMessage = "只有双弩精灵职业才能装备。"; return false; } if (gear.type == GearType.demonShield && status.Job / 100 != 31) { errorMessage = "只有恶魔猎手职业才能装备。"; return false; } if (!checkGearPropReq(gear)) { errorMessage = "能力值不足,无法装备道具。"; return false; } errorMessage = null; return true; } private bool checkGearPropReq(Gear gear) { return checkGearPropReq(gear.Props, GearPropType.reqSTR, status.Strength.GetGearReqSum()) && checkGearPropReq(gear.Props, GearPropType.reqDEX, status.Dexterity.GetGearReqSum()) && checkGearPropReq(gear.Props, GearPropType.reqINT, status.Intelligence.GetGearReqSum()) && checkGearPropReq(gear.Props, GearPropType.reqLUK, status.Luck.GetGearReqSum()) && checkGearPropReq(gear.Props, GearPropType.reqLevel, status.Level) && checkGearPropReq(gear.Props, GearPropType.reqPOP, status.Pop) && checkGearJobReq(gear.Props, gear.type); } private bool checkGearPropReq(Dictionary props, GearPropType prop, int value) { int v; if (!props.TryGetValue(prop, out v) || value >= v) { return true; } return false; } private bool checkGearJobReq(Dictionary props, GearType type) { int reqJob; props.TryGetValue(GearPropType.reqJob, out reqJob); int jobClass = status.Job % 1000 / 100; if (reqJob == 0) //全职 return true; if (reqJob == -1) //新手 return jobClass == 0; return (reqJob & (1 << (jobClass - 1))) != 0; } /// /// 检查指定的职业ID是否归属于标准职业。 /// /// 要检查的职业ID。 /// 标准职业代码。0-新手 1-战士 2-法师 3-弓手 4-飞侠 5-海盗 /// public static bool CheckJobReq(int jobID, int baseJob) { switch (jobID / 100) { case 27: return baseJob == 2; //夜光 case 36: return baseJob == 4 || baseJob == 5; //煎饼 default: return jobID / 100 % 10 == baseJob; } } public bool CheckGearEnabled() { List gearsEquip = new List(this.equip.GearsEquiped); List oldStates = new List(gearsEquip.Count); foreach (Gear gear in gearsEquip) { oldStates.Add(gear.State); gear.State = GearState.enable; } while (true) { bool reset = false; //逐个装备判定装备要求 foreach (Gear gear in gearsEquip) { if (gear.State == GearState.enable) { gear.State = GearState.disable; UpdateProps(); //判定装备要求 if (!checkGearPropReq(gear)) { reset = true; //如果不符合 无效化装备 进行下一轮判断 break; } //恢复有效性 gear.State = GearState.enable; } } if (!reset) //如果本轮判断没变化则停止 可以证明是不会进入死循环的 break; } for (int i = 0; i < gearsEquip.Count; i++) { if (gearsEquip[i].State != oldStates[i]) //装备状态变化 { return true; } } return false; } public void CalcAttack(out double max, out double min) { int sign; CalcAttack(out max, out min, out sign); } public void CalcAttack(out double max, out double min, out int sign) { max = CalcAttack(status.Strength.GetSum(), status.Dexterity.GetSum(), status.Intelligence.GetSum(), status.Luck.GetSum(), status.PADamage.GetSum(), status.MADamage.GetSum(), GearType.totem, version); min = max * status.Mastery.GetSum() / 100; sign = 0; } public static double CalcAttack(int str, int dex, int inte, int luk, int pad, int mad, GearType WeaponType, FormulaVersion version) { switch (WeaponType) { case GearType.ohSword: case GearType.ohAxe: case GearType.ohBlunt: return (str * 4 + dex) * 1.2 * pad * 0.01; case GearType.dagger: return (str + dex + luk * 4) * 1.3 * pad * 0.01; case GearType.cane: return (dex + luk * 4) * 1.3 * pad * 0.01; case GearType.wand: case GearType.staff: return (inte * 4 + luk) * 1.0 * mad * 0.01; case GearType.barehand: return (str * 4 + dex) * 1.43 * 1 * 0.01; case GearType.thSword: case GearType.thAxe: case GearType.thBlunt: if (version == FormulaVersion.Bigbang) return (str * 4 + dex) * 1.32 * pad * 0.01; else if (version == FormulaVersion.Chaos) return (str * 4 + dex) * 1.34 * pad * 0.01; break; case GearType.spear: case GearType.polearm: return (str * 4 + dex) * 1.49 * pad * 0.01; case GearType.bow: if (version == FormulaVersion.Bigbang) return (dex * 4 + str) * 1.2 * pad * 0.01; else if (version == FormulaVersion.Chaos) return (dex * 4 + str) * 1.3 * pad * 0.01; break; case GearType.crossbow: return (dex * 4 + str) * 1.35 * pad * 0.01; case GearType.throwingGlove: return (dex + luk * 4) * 1.75 * pad * 0.01; case GearType.knuckle: return (str * 4 + dex) * 1.7 * pad * 0.01; case GearType.gun: return (dex * 4 + str) * 1.5 * pad * 0.01; case GearType.dualBow: return (dex * 4 + str) * 1.3 * pad * 0.01; case GearType.handCannon: return (str * 4 + dex) * 1.5 * pad * 0.01; } return 0; } private static int[] _exptnl = new int[] { 15,34,57,92, 135,372,560,840, 1242,19136,479143,10063200 }; public static int ExpToNextLevel(int level) { int exp; if (level < 1 || level > 200) return -1; if (level < 10) return _exptnl[level - 1]; if (level >= 10 && level <= 14) return _exptnl[8]; if (level >= 15 && level <= 29) { exp = _exptnl[8]; //level 10 while (level > 14) { exp = (int)Math.Round(exp * 1.2, MidpointRounding.AwayFromZero); level -= 1; } return exp; } if (level >= 30 && level <= 34) { return _exptnl[9];//level 30 } if (level >= 35 && level <= 39) { exp = ExpToNextLevel(34); while (level > 34) { exp = (int)Math.Round(exp * 1.2, MidpointRounding.AwayFromZero); level -= 1; } return exp; } if (level >= 40 && level <= 69) { exp = ExpToNextLevel(39); while (level > 39) { exp = (int)Math.Round(exp * 1.08, MidpointRounding.AwayFromZero); level -= 1; } return exp; } if (level >= 70 && level <= 74) { return _exptnl[10];//level 70 } if (level >= 75 && level <= 119) { exp = ExpToNextLevel(74); while (level > 74) { exp = (int)Math.Round(exp * 1.07, MidpointRounding.AwayFromZero); level -= 1; } return exp; } if (level >= 120 && level <= 124) { return _exptnl[11];//level 120 } if (level >= 125 && level <= 159) { exp = _exptnl[11]; while (level > 124) { exp = (int)Math.Round(exp * 1.07, MidpointRounding.AwayFromZero); level -= 1; } return exp; } if (level >= 160 && level <= 199) { exp = ExpToNextLevel(159); while (level > 159) { exp = (int)Math.Round(exp * 1.06, MidpointRounding.AwayFromZero); level -= 1; } return exp; } return -1; } } } ================================================ FILE: WzComparerR2/CharaSim/CharacterStatus.cs ================================================ using System; using System.Collections.Generic; using System.Text; using System.Reflection; namespace WzComparerR2.CharaSim { public class CharacterStatus { public CharacterStatus() { this.maxHP = new CharaProp(99999); this.maxMP = new CharaProp(99999); this.pdd = new CharaProp(9999); this.mdd = new CharaProp(9999); this.pAcc = new CharaProp(9999); this.mAcc = new CharaProp(9999); this.pEva = new CharaProp(9999); this.mEva = new CharaProp(9999); FieldInfo[] fields = this.GetType().GetFields(BindingFlags.Instance | BindingFlags.NonPublic); foreach (FieldInfo f in fields) { if (f.FieldType == typeof(CharaProp) && f.GetValue(this) == null) { f.SetValue(this, new CharaProp()); } } } private int job; private int level; private int hp; private int mp; private int exp; private int ap; private int pop; private CharaProp maxHP; private CharaProp maxMP; private CharaProp str = null; private CharaProp dex = null; private CharaProp inte = null; private CharaProp luk = null; private CharaProp pad = null; private CharaProp mad = null; private CharaProp pdd; private CharaProp mdd; private CharaProp pAcc; private CharaProp mAcc; private CharaProp pEva; private CharaProp mEva; private CharaProp crit = null; private CharaProp move = null; private CharaProp jump = null; private CharaProp critDamMax = null; private CharaProp critDamMin = null; private CharaProp mastery = null; private CharaProp damR = null; private CharaProp bossDamR = null; #region 基础属性 /// /// 获取或设置角色的职业代码。 /// public int Job { get { return job; } set { job = value; } } /// /// 获取或设置角色的等级。 /// public int Level { get { return level; } set { level = value; } } /// /// 获取或设置角色的当前HP。 /// public int HP { get { hp = Math.Max(0, Math.Min(maxHP.GetSum(), hp)); return hp; } set { value = Math.Max(0, Math.Min(maxHP.GetSum(), value)); hp = value; } } /// /// 获取角色的HP上限。 /// public CharaProp MaxHP { get { return maxHP; } } /// /// 获取或设置角色的当前MP。 /// public int MP { get { mp = Math.Max(0, Math.Min(maxMP.GetSum(), mp)); return mp; } set { value = Math.Max(0, Math.Min(maxMP.GetSum(), value)); mp = value; } } /// /// 获取角色的MP上限。 /// public CharaProp MaxMP { get { return maxMP; } } /// /// 获取或设置角色的当前经验值。 /// public int Exp { get { exp = (Exptnl == -1) ? -1 : Math.Max(0, Math.Min(Exptnl - 1, exp)); return exp; } set { value = (Exptnl == -1) ? -1 : Math.Max(0, Math.Min(Exptnl - 1, value)); exp = value; } } /// /// 获取角色当前升级经验值。 /// public int Exptnl { get { return Character.ExpToNextLevel(this.level); } } /// /// 获取或设置角色的人气度。 /// public int Pop { get { return pop; } set { pop = value; } } /// /// 获取或设置角色的可分配AP。 /// public int Ap { get { return ap; } set { if (value >= 0)ap = value; } } /// /// 获取角色的力量值。 /// public CharaProp Strength { get { return str; } } /// /// 获取角色的敏捷值。 /// public CharaProp Dexterity { get { return dex; } } /// /// 获取角色的智力值。 /// public CharaProp Intelligence { get { return inte; } } /// /// 获取角色的运气值。 /// public CharaProp Luck { get { return luk; } } #endregion #region 扩展属性 /// /// 获取角色的攻击力,这是一个隐藏属性。 /// public CharaProp PADamage { get { return pad; } } /// /// 获取角色的魔法攻击力,这是一个隐藏属性。 /// public CharaProp MADamage { get { return mad; } } /// /// 获取角色的物理防御力。 /// public CharaProp PDDamage { get { return pdd; } } /// /// 获取角色的魔法防御力。 /// public CharaProp MDDamage { get { return mdd; } } /// /// 获取角色的物理命中率。 /// public CharaProp PAccurate { get { return pAcc; } } /// /// 获取角色的魔法命中率。 /// public CharaProp MAccurate { get { return mAcc; } } /// /// 获取角色的物理回避率。 /// public CharaProp PEvasion { get { return pEva; } } /// /// 获取角色的魔法回避率。 /// public CharaProp MEvasion { get { return mEva; } } /// /// 获取角色的暴击率,这是一个百分比属性。 /// public CharaProp CriticalRate { get { return crit; } } /// /// 获取角色的移动速度,这是一个百分比属性。 /// public CharaProp MoveSpeed { get { return move; } } /// /// 获取角色的跳跃力,这是一个百分比属性。 /// public CharaProp Jump { get { return jump; } } /// /// 获取角色的暴击最大伤害,这是一个隐藏的百分比属性。 /// public CharaProp CriticalDamageMax { get { return critDamMax; } } /// /// 获取角色的暴击最小伤害,这是一个隐藏的百分比属性。 /// public CharaProp CriticalDamageMin { get { return critDamMin; } } /// /// 获取角色的攻击熟练度,这是一个隐藏的百分比属性。 /// public CharaProp Mastery { get { return mastery; } } /// /// 获取角色的攻击力百分比加成,这是一个隐藏的百分比属性。 /// public CharaProp DamageRate { get { return damR; } } /// /// 获取角色的BOSS攻击力百分比加成,这是一个隐藏的百分比属性。 /// public CharaProp BossDamageRate { get { return bossDamR; } } #endregion } } ================================================ FILE: WzComparerR2/CharaSimControl/AControl.cs ================================================ using System; using System.Collections.Generic; using System.Text; using System.Drawing; using System.Windows.Forms; namespace WzComparerR2.CharaSimControl { public abstract class AControl { public AControl() { } private Point location; private Size size; private bool visible; public Point Location { get { return location; } set { location = value; } } public Size Size { get { return size; } set { size = value; } } public bool Visible { get { return visible; } set { visible = value; } } public Rectangle Rectangle { get { return new Rectangle(this.location, this.size); } } public abstract void Draw(Graphics g); public virtual void OnMouseClick(MouseEventArgs e) { if (IsMouseContains(e.Location)) { if (this.MouseClick != null) this.MouseClick(this, e); } } public virtual void OnMouseDown(MouseEventArgs e) { } public virtual void OnMouseUp(MouseEventArgs e) { } public virtual void OnMouseMove(MouseEventArgs e) { } public virtual void OnMouseWheel(MouseEventArgs e) { } protected virtual bool IsMouseContains(Point mouseLocation) { return this.visible && this.Rectangle.Contains(mouseLocation); } protected MouseEventArgs ToChildEventargs(MouseEventArgs e) { return new MouseEventArgs(e.Button, e.Clicks, e.X - this.Location.X, e.Y - this.Location.Y, e.Delta); } public event MouseEventHandler MouseClick; } } ================================================ FILE: WzComparerR2/CharaSimControl/ACtrlButton.cs ================================================ using System; using System.Collections.Generic; using System.Text; using System.Drawing; using System.Windows.Forms; namespace WzComparerR2.CharaSimControl { public class ACtrlButton : AControl { public ACtrlButton() { this.Visible = true; } private BitmapOrigin normal; private BitmapOrigin pressed; private BitmapOrigin mouseOver; private BitmapOrigin disabled; private ButtonState state; public BitmapOrigin Normal { get { return normal; } set { normal = value; } } public BitmapOrigin Pressed { get { return pressed; } set { pressed = value; } } public BitmapOrigin MouseOver { get { return mouseOver; } set { mouseOver = value; } } public BitmapOrigin Disabled { get { return disabled; } set { disabled = value; } } public ButtonState State { get { return state; } set { if (state != value) { state = value; OnButtonStateChanged(); } } } public BitmapOrigin CurrentBitmap { get { switch (this.state) { default: case ButtonState.Normal: return this.normal; case ButtonState.Pressed: return this.pressed; case ButtonState.MouseOver: return this.mouseOver; case ButtonState.Disabled: return this.disabled; } } } /// /// 应用当前的ButtonState和Location,绘制对应的图像。 /// /// 要绘制的绘图表面。 public override void Draw(Graphics g) { this.Draw(g, new Point(0, 0)); } /// /// 应用当前的ButtonState、Location以及给定的坐标偏移,绘制对应的图像。 /// /// 要绘制的绘图表面。 /// 表示对于绘图原点的坐标偏移。 public void Draw(Graphics g, Point offset) { if (g == null || !this.Visible) return; BitmapOrigin bmp = this.CurrentBitmap; if (bmp.Bitmap != null) g.DrawImage(bmp.Bitmap, bmp.OpOrigin.X + this.Location.X + offset.X, bmp.OpOrigin.Y + this.Location.Y + offset.Y); } public override void OnMouseMove(MouseEventArgs e) { if (this.IsMouseContains(e.Location)) { if (this.State == ButtonState.Normal) { this.State = ButtonState.MouseOver; } } else if (this.state != ButtonState.Disabled) { this.State = ButtonState.Normal; } base.OnMouseMove(e); } public override void OnMouseDown(MouseEventArgs e) { if (this.IsMouseContains(e.Location)) { if (this.State == ButtonState.Normal || this.State == ButtonState.MouseOver) { this.State = ButtonState.Pressed; } } base.OnMouseDown(e); } public override void OnMouseUp(MouseEventArgs e) { if (this.IsMouseContains(e.Location)) { if (this.State == ButtonState.Pressed) { this.State = ButtonState.MouseOver; } } base.OnMouseUp(e); } protected virtual void OnButtonStateChanged() { if (this.ButtonStateChanged != null) { this.ButtonStateChanged(this, EventArgs.Empty); } } public override void OnMouseClick(MouseEventArgs e) { if (this.state != ButtonState.Disabled) { base.OnMouseClick(e); } } public event EventHandler ButtonStateChanged; } } ================================================ FILE: WzComparerR2/CharaSimControl/ACtrlVScroll.cs ================================================ using System; using System.Collections.Generic; using System.Text; using System.Drawing; using System.Windows.Forms; namespace WzComparerR2.CharaSimControl { public class ACtrlVScroll : AControl { public ACtrlVScroll() { this.btnPrev = new ACtrlButton(); this.btnNext = new ACtrlButton(); this.btnThumb = new ACtrlButton(); this.picBase = new ACtrlButton(); this.btnPrev.ButtonStateChanged += new EventHandler(childBtn_ButtonStateChanged); this.btnPrev.MouseClick += new MouseEventHandler(btnPrev_MouseClick); this.btnNext.ButtonStateChanged += new EventHandler(childBtn_ButtonStateChanged); this.btnNext.MouseClick += new MouseEventHandler(btnNext_MouseClick); this.btnThumb.ButtonStateChanged += new EventHandler(childBtn_ButtonStateChanged); this.picBase.ButtonStateChanged += new EventHandler(childBtn_ButtonStateChanged); } ACtrlButton btnPrev; ACtrlButton btnNext; ACtrlButton btnThumb; ACtrlButton picBase; int minimum; int maximum; int value; bool isScrolling; public int Minimum { get { return minimum; } set { if (this.minimum != value) { value = Math.Min(value, maximum); minimum = value; this.Value = this.Value; this.OnValueChanged(); } } } public int Maximum { get { return maximum; } set { if (this.maximum != value) { value = Math.Max(minimum, value); maximum = value; this.Value = this.Value; this.OnValueChanged(); } } } public int Value { get { return this.value; } set { value = Math.Min(Math.Max(this.minimum, value), this.maximum); if (this.value != value) { this.value = value; this.OnValueChanged(); } } } public ACtrlButton BtnPrev { get { return btnPrev; } } public ACtrlButton BtnNext { get { return btnNext; } } public ACtrlButton BtnThumb { get { return btnThumb; } } public ACtrlButton PicBase { get { return picBase; } } public bool Enabled { get { return !(this.minimum == this.maximum); } } public override void OnMouseMove(MouseEventArgs e) { if (!this.Enabled) { setAllState(ButtonState.Disabled); return; } if (IsMouseContains(e.Location)) { MouseEventArgs e2 = ToChildEventargs(e); foreach (ACtrlButton btn in this.buttons) { btn.OnMouseMove(e2); } if (this.isScrolling) { this.scrolling(e.Location); } } else { setAllState(ButtonState.Normal); } base.OnMouseMove(e); } public override void OnMouseDown(MouseEventArgs e) { if (!this.Enabled) { setAllState(ButtonState.Disabled); return; } if (IsMouseContains(e.Location)) { this.isScrolling = true; MouseEventArgs e2 = ToChildEventargs(e); foreach (ACtrlButton btn in this.buttons) { btn.OnMouseDown(e2); } } base.OnMouseDown(e); } public override void OnMouseUp(MouseEventArgs e) { if (!this.Enabled) { setAllState(ButtonState.Disabled); return; } if (IsMouseContains(e.Location)) { MouseEventArgs e2 = ToChildEventargs(e); foreach (ACtrlButton btn in this.buttons) { btn.OnMouseUp(e2); } } this.isScrolling = false; base.OnMouseUp(e); } public override void OnMouseClick(MouseEventArgs e) { if (!this.Enabled) { setAllState(ButtonState.Disabled); return; } if (IsMouseContains(e.Location)) { MouseEventArgs e2 = ToChildEventargs(e); foreach (ACtrlButton btn in this.buttons) { btn.OnMouseClick(e2); } } base.OnMouseClick(e); } public override void OnMouseWheel(MouseEventArgs e) { if (!this.Enabled) { setAllState(ButtonState.Disabled); return; } if (this.IsMouseContains(e.Location)) { if (e.Delta > 0) { this.Value -= 1; } else if (e.Delta < 0) { this.Value += 1; } } base.OnMouseWheel(e); } private void btnPrev_MouseClick(object sender, MouseEventArgs e) { this.Value -= 1; } private void btnNext_MouseClick(object sender, MouseEventArgs e) { this.Value += 1; } private void setAllState(ButtonState buttonState) { btnPrev.State = buttonState; btnNext.State = buttonState; btnThumb.State = buttonState; picBase.State = buttonState; } private IEnumerable buttons { get { yield return btnPrev; yield return btnNext; yield return btnThumb; } } public override void Draw(Graphics g) { if (g == null || !this.Visible) return; if (picBase != null) { BitmapOrigin curBmp = picBase.CurrentBitmap; g.SetClip(this.Rectangle); for (int h = 0; h < Size.Height; h += curBmp.Bitmap.Size.Height) { g.DrawImage(curBmp.Bitmap, Location.X, Location.Y + h); } g.ResetClip(); } if (btnPrev != null) { btnPrev.Draw(g, this.Location); } if (btnNext != null) { btnNext.Draw(g, this.Location); } if (btnThumb != null) { btnThumb.Location = new Point(0, calcThumbLocationY()); btnThumb.Draw(g, this.Location); } } private int calcThumbLocationY() { if (this.minimum == this.maximum) return 0; int totalHeight = this.Size.Height - this.btnPrev.Size.Height - this.btnNext.Size.Height - this.btnThumb.Size.Height; int thumbY = totalHeight * this.value / (this.maximum - this.minimum); return thumbY + this.btnPrev.Size.Height; } private void childBtn_ButtonStateChanged(object sender, EventArgs e) { this.OnChildButtonStateChanged(); } private void scrolling(Point mouseLocation) { if (this.minimum == this.maximum) return; Point origin = new Point(this.Location.X, this.Location.Y + this.btnPrev.Size.Height + this.btnThumb.Size.Height / 2); Size size = new Size(this.Size.Width, this.Size.Height - this.btnPrev.Size.Height - this.btnNext.Size.Height - this.btnThumb.Size.Height); Rectangle scrollingRect = new Rectangle(origin, size); this.Value = (int)Math.Round(1.0 * (this.maximum - this.minimum) * (mouseLocation.Y - scrollingRect.Y) / scrollingRect.Height); } protected virtual void OnValueChanged() { if (this.ValueChanged != null) { this.ValueChanged(this, EventArgs.Empty); } } protected virtual void OnChildButtonStateChanged() { if (this.ChildButtonStateChanged != null) { this.ChildButtonStateChanged(this, EventArgs.Empty); } } public event EventHandler ValueChanged; public event EventHandler ChildButtonStateChanged; } } ================================================ FILE: WzComparerR2/CharaSimControl/AfrmEquip.cs ================================================ using System; using System.Collections.Generic; using System.Collections.Specialized; using System.Windows.Forms; using System.Drawing; using System.Text; using CharaSimResource; using WzComparerR2.CharaSim; using WzComparerR2.Common; using WzComparerR2.Controls; namespace WzComparerR2.CharaSimControl { public class AfrmEquip : AlphaForm { public AfrmEquip() { sec = new int[5]; for (int i = 0; i < sec.Length; i++) sec[i] = 1 << i; initCtrl(); this.AllowDrop = true; this.TotemVisible = true; } private BitVector32 partVisible; private int[] sec; private Point baseOffset; private Point newLocation; private bool waitForRefresh; private Character character; private ACtrlButton btnPet; private ACtrlButton btnDragon; private ACtrlButton btnMechanic; private ACtrlButton btnAndroid; private ACtrlButton btnClose; public Character Character { get { return character; } set { character = value; } } public bool PetVisible { get { return partVisible[sec[0]]; } private set { partVisible[sec[0]] = value; } } public bool DragonVisible { get { return partVisible[sec[1]]; } private set { partVisible[sec[1]] = value; if (value) { partVisible[sec[2]] = false; partVisible[sec[3]] = false; } } } public bool MechanicVisible { get { return partVisible[sec[2]]; } private set { partVisible[sec[2]] = value; if (value) { partVisible[sec[1]] = false; partVisible[sec[3]] = false; } } } public bool AndroidVisible { get { return partVisible[sec[3]]; } private set { partVisible[sec[3]] = value; if (value) { partVisible[sec[1]] = false; partVisible[sec[2]] = false; } } } public bool TotemVisible { get { return partVisible[sec[4]]; } private set { partVisible[sec[4]] = value; } } private Rectangle DragonRect { get { return new Rectangle( new Point(baseOffset.X - Resource.Equip_dragon_backgrnd.Width, baseOffset.Y), Resource.Equip_dragon_backgrnd.Size); } } private Rectangle MechanicRect { get { return new Rectangle( new Point(baseOffset.X - Resource.Equip_mechanic_backgrnd.Width, baseOffset.Y), Resource.Equip_mechanic_backgrnd.Size); } } private Rectangle AndroidRect { get { return new Rectangle( new Point(baseOffset.X - Resource.Equip_Android_backgrnd.Width, baseOffset.Y), Resource.Equip_Android_backgrnd.Size); } } private Rectangle PetRect { get { return new Rectangle( new Point(baseOffset.X + Resource.Equip_character_backgrnd.Width, baseOffset.Y + Resource.Equip_character_backgrnd.Height - Resource.Equip_pet_backgrnd.Height), Resource.Equip_pet_backgrnd.Size); } } private Rectangle TotemRect { get { return new Rectangle( new Point(baseOffset.X - Resource.Equip_totem_backgrnd.Width, baseOffset.Y + Resource.Equip_character_backgrnd.Height - Resource.Equip_totem_backgrnd.Height), Resource.Equip_pet_backgrnd.Size); } } private void initCtrl() { this.btnPet = new ACtrlButton(); this.btnPet.Normal = new BitmapOrigin(Resource.Equip_character_BtPet_normal_0); this.btnPet.Pressed = new BitmapOrigin(Resource.Equip_character_BtPet_pressed_0); this.btnPet.MouseOver = new BitmapOrigin(Resource.Equip_character_BtPet_mouseOver_0); this.btnPet.Disabled = new BitmapOrigin(Resource.Equip_character_BtPet_disabled_0); this.btnPet.Location = new Point(139, 264); this.btnPet.Size = new Size(36, 17); this.btnPet.ButtonStateChanged += new EventHandler(aCtrl_RefreshCall); this.btnPet.MouseClick += new System.Windows.Forms.MouseEventHandler(btnPet_MouseClick); this.btnDragon = new ACtrlButton(); this.btnDragon.Normal = new BitmapOrigin(Resource.Equip_character_BtDragon_normal_0); this.btnDragon.Pressed = new BitmapOrigin(Resource.Equip_character_BtDragon_pressed_0); this.btnDragon.MouseOver = new BitmapOrigin(Resource.Equip_character_BtDragon_mouseOver_0); this.btnDragon.Disabled = new BitmapOrigin(Resource.Equip_character_BtDragon_disabled_0); this.btnDragon.Location = new Point(10, 264); this.btnDragon.Size = new Size(43, 17); this.btnDragon.ButtonStateChanged += new EventHandler(aCtrl_RefreshCall); this.btnDragon.MouseClick += new MouseEventHandler(btnDragon_MouseClick); this.btnMechanic = new ACtrlButton(); this.btnMechanic.Normal = new BitmapOrigin(Resource.Equip_character_BtMechanic_normal_0); this.btnMechanic.Pressed = new BitmapOrigin(Resource.Equip_character_BtMechanic_pressed_0); this.btnMechanic.MouseOver = new BitmapOrigin(Resource.Equip_character_BtMechanic_mouseOver_0); this.btnMechanic.Disabled = new BitmapOrigin(Resource.Equip_character_BtMechanic_disabled_0); this.btnMechanic.Location = new Point(10, 264); this.btnMechanic.Size = new Size(43, 17); this.btnMechanic.ButtonStateChanged += new EventHandler(aCtrl_RefreshCall); this.btnMechanic.MouseClick += new MouseEventHandler(btnMechanic_MouseClick); this.btnAndroid = new ACtrlButton(); this.btnAndroid.Normal = new BitmapOrigin(Resource.Equip_character_BtAndroid_normal_0); this.btnAndroid.Pressed = new BitmapOrigin(Resource.Equip_character_BtAndroid_pressed_0); this.btnAndroid.MouseOver = new BitmapOrigin(Resource.Equip_character_BtAndroid_mouseOver_0); this.btnAndroid.Disabled = new BitmapOrigin(Resource.Equip_character_BtAndroid_disabled_0); this.btnAndroid.Location = new Point(65, 266); this.btnAndroid.Size = new Size(25, 12); this.btnAndroid.ButtonStateChanged += new EventHandler(aCtrl_RefreshCall); this.btnAndroid.MouseClick += new MouseEventHandler(btnAndroid_MouseClick); this.btnClose = new ACtrlButton(); this.btnClose.Normal = new BitmapOrigin(Resource.BtClose3_normal_0); this.btnClose.Pressed = new BitmapOrigin(Resource.BtClose3_pressed_0); this.btnClose.MouseOver = new BitmapOrigin(Resource.BtClose3_mouseOver_0); this.btnClose.Disabled = new BitmapOrigin(Resource.BtClose3_disabled_0); this.btnClose.Location = new Point(162, 6); this.btnClose.Size = new Size(13, 13); this.btnClose.ButtonStateChanged += new EventHandler(aCtrl_RefreshCall); this.btnClose.MouseClick += new MouseEventHandler(btnClose_MouseClick); } public override void Refresh() { this.preRender(); this.SetBitmap(this.Bitmap); this.CaptionRectangle = new Rectangle(this.baseOffset, new Size(Resource.Equip_character_backgrnd.Width, 24)); this.Location = newLocation; base.Refresh(); } protected override bool captionHitTest(Point point) { Rectangle rect = this.btnClose.Rectangle; rect.Offset(this.baseOffset); if (rect.Contains(point)) return false; return base.captionHitTest(point); } private void preRender() { if (Bitmap != null) Bitmap.Dispose(); //处理按钮可见 setControlState(); //计算图像大小 Point baseOffsetnew = calcRenderBaseOffset(); Size size = Resource.Equip_character_backgrnd.Size; size.Width += baseOffsetnew.X; if (this.PetVisible) size.Width += Resource.Equip_pet_backgrnd.Width; //处理偏移 this.newLocation = new Point(this.Location.X + this.baseOffset.X - baseOffsetnew.X, this.Location.Y + this.baseOffset.Y - baseOffsetnew.Y); this.baseOffset = baseOffsetnew; //绘制图像 Bitmap bitmap = new Bitmap(size.Width, size.Height); Graphics g = Graphics.FromImage(bitmap); renderBase(g); if (this.DragonVisible) renderDragon(g); else if (this.MechanicVisible) renderMechanic(g); else if (this.AndroidVisible) renderAndroid(g); if (this.PetVisible) renderPet(g); if (this.TotemVisible) renderTotem(g); g.Dispose(); this.Bitmap = bitmap; } private Point calcRenderBaseOffset() { if (this.DragonVisible) return new Point(Resource.Equip_dragon_backgrnd.Width, 0); else if (this.MechanicVisible) return new Point(Resource.Equip_mechanic_backgrnd.Width, 0); else if (this.AndroidVisible) return new Point(Resource.Equip_Android_backgrnd.Width, 0); else return new Point(Resource.Equip_totem_backgrnd.Width, 0); } private void setControlState() { if (this.character == null) { this.btnDragon.Visible = false; this.btnMechanic.Visible = false; this.DragonVisible = false; this.MechanicVisible = false; } else { if (this.character.Status.Job / 100 == 22) //龙神 { this.btnDragon.Visible = true; } else { this.btnDragon.Visible = false; this.DragonVisible = false; } if (this.character.Status.Job / 100 == 35) //机械 { this.btnMechanic.Visible = true; } else { this.btnMechanic.Visible = false; this.MechanicVisible = false; } } } private void renderBase(Graphics g) { g.TranslateTransform(baseOffset.X, baseOffset.Y); g.DrawImage(Resource.Equip_character_backgrnd, 0, 0); g.DrawImage(Resource.Equip_character_backgrnd2, 6, 22); g.DrawImage(Resource.Equip_character_backgrnd3, 10, 27); g.DrawImage(Resource.Equip_character_cashPendant, 76, 93); g.DrawImage(Resource.Equip_character_charmPocket, 10, 93); if (this.character != null && (this.character.Status.Job / 100 == 23 || this.character.Status.Job == 2002)) { g.DrawImage(Resource.Equip_character_magicArrow, 142, 126); } foreach (AControl aCtrl in this.aControls) { aCtrl.Draw(g); } if (this.character != null) { for (int i = 0; i < 30; i++) { Gear gear = this.character.Equip.GearSlots[i]; if (gear != null) { int dx = 10 + i % 5 * 33, dy = 27 + i / 5 * 33; drawGearIcon(gear, g, dx, dy); } } } g.ResetTransform(); } private void renderDragon(Graphics g) { Rectangle rect = this.DragonRect; g.TranslateTransform(rect.X, rect.Y); g.DrawImage(Resource.Equip_dragon_backgrnd, 0, 0); g.DrawImage(Resource.Equip_dragon_backgrnd2, 6, 22); g.DrawImage(Resource.Equip_dragon_backgrnd3, 10, 29); if (this.character != null) { for (int i = 35; i < 39; i++) { Gear gear = this.character.Equip.GearSlots[i]; if (gear != null) { int dx = 10 + (i - 35) * 33, dy = 22 + (((i - 1) % 2) + 1) * 33; drawGearIcon(gear, g, dx, dy); } } } g.ResetTransform(); } private void renderMechanic(Graphics g) { Rectangle rect = this.MechanicRect; g.TranslateTransform(rect.X, rect.Y); g.DrawImage(Resource.Equip_mechanic_backgrnd, 0, 0); g.DrawImage(Resource.Equip_mechanic_backgrnd2, 6, 22); g.DrawImage(Resource.Equip_mechanic_backgrnd3, 12, 35); if (this.character != null) { int dx, dy; for (int i = 39; i < 44; i++) { Gear gear = this.character.Equip.GearSlots[i]; if (gear != null) { switch(i) { case 39: dx = 1; dy = 1; break; case 40: dx = 1; dy = 2; break; case 41: dx = 2; dy = 2; break; case 42: dx = 0; dy = 3; break; case 43: dx = 1; dy = 3; break; default: continue; } dx = 10 + dx * 33; dy = 22 + dy * 33; drawGearIcon(gear, g, dx, dy); } } } g.ResetTransform(); } private void renderPet(Graphics g) { Rectangle rect = this.PetRect; g.TranslateTransform(rect.X, rect.Y); g.DrawImage(Resource.Equip_pet_backgrnd, 0, 0); g.DrawImage(Resource.Equip_pet_backgrnd2, 6, 21); g.DrawImage(Resource.Equip_pet_backgrnd3, 11, 27); g.ResetTransform(); } private void renderAndroid(Graphics g) { Rectangle rect = this.AndroidRect; g.TranslateTransform(rect.X, rect.Y); g.DrawImage(Resource.Equip_Android_backgrnd, 0, 0); g.DrawImage(Resource.Equip_Android_backgrnd2, 6, 24); g.DrawImage(Resource.Equip_Android_backgrnd3, 12, 28); g.ResetTransform(); } private void renderTotem(Graphics g) { Rectangle rect = this.TotemRect; g.TranslateTransform(rect.X, rect.Y); g.DrawImage(Resource.Equip_totem_backgrnd, 0, 0); g.ResetTransform(); } private void drawGearIcon(Gear gear, Graphics g, int x, int y) { if (gear == null || g == null) return; if (gear.State == GearState.disable) g.DrawImage(Resource.Equip_character_disabled, x, y); Pen pen = GearGraphics.GetGearItemBorderPen(gear.Grade); if (pen != null) { Point[] path = GearGraphics.GetIconBorderPath(x, y); g.DrawLines(pen, path); } g.DrawImage(gear.Icon.Bitmap, x - gear.Icon.Origin.X, y + 32 - gear.Icon.Origin.Y); } private IEnumerable aControls { get { yield return btnDragon; yield return btnMechanic; yield return btnPet; yield return btnClose; yield return btnAndroid; } } private void aCtrl_RefreshCall(object sender, EventArgs e) { this.waitForRefresh = true; } private void btnPet_MouseClick(object sender, MouseEventArgs e) { this.PetVisible = !this.PetVisible; this.waitForRefresh = true; } private void btnDragon_MouseClick(object sender, MouseEventArgs e) { this.DragonVisible = !this.DragonVisible; this.waitForRefresh = true; } private void btnMechanic_MouseClick(object sender, MouseEventArgs e) { this.MechanicVisible = !this.MechanicVisible; this.waitForRefresh = true; } private void btnAndroid_MouseClick(object sender, MouseEventArgs e) { this.AndroidVisible = !this.AndroidVisible; this.waitForRefresh = true; } private void btnClose_MouseClick(object sender, MouseEventArgs e) { this.Visible = false; } protected override void OnMouseMove(MouseEventArgs e) { MouseEventArgs childArgs = new MouseEventArgs(e.Button, e.Clicks, e.X - baseOffset.X, e.Y - baseOffset.Y, e.Delta); foreach (AControl ctrl in this.aControls) { ctrl.OnMouseMove(childArgs); } if (this.waitForRefresh) { this.Refresh(); waitForRefresh = false; } base.OnMouseMove(e); } protected override void OnMouseDown(MouseEventArgs e) { MouseEventArgs childArgs = new MouseEventArgs(e.Button, e.Clicks, e.X - baseOffset.X, e.Y - baseOffset.Y, e.Delta); foreach (AControl ctrl in this.aControls) { ctrl.OnMouseDown(childArgs); } if (this.waitForRefresh) { this.Refresh(); waitForRefresh = false; } base.OnMouseDown(e); } protected override void OnMouseUp(MouseEventArgs e) { MouseEventArgs childArgs = new MouseEventArgs(e.Button, e.Clicks, e.X - baseOffset.X, e.Y - baseOffset.Y, e.Delta); foreach (AControl ctrl in this.aControls) { ctrl.OnMouseUp(childArgs); } if (this.waitForRefresh) { this.Refresh(); waitForRefresh = false; } base.OnMouseUp(e); } protected override void OnMouseClick(MouseEventArgs e) { MouseEventArgs childArgs = new MouseEventArgs(e.Button, e.Clicks, e.X - baseOffset.X, e.Y - baseOffset.Y, e.Delta); foreach (AControl ctrl in this.aControls) { ctrl.OnMouseClick(childArgs); } if (this.waitForRefresh) { this.Refresh(); waitForRefresh = false; } base.OnMouseClick(e); } } } ================================================ FILE: WzComparerR2/CharaSimControl/AfrmItem.cs ================================================ using System; using System.Drawing; using System.Collections.Generic; using System.Windows.Forms; using System.Text; using CharaSimResource; using WzComparerR2.CharaSim; using WzComparerR2.Common; using WzComparerR2.Controls; namespace WzComparerR2.CharaSimControl { public class AfrmItem : AlphaForm { public AfrmItem() { this.AllowDrop = true; initCtrl(); } private ItemTab[] itemTabs; private bool fullMode; private bool selectedIndexChanging; private ACtrlVScroll vScroll; private Character character; private ACtrlButton btnFull; private ACtrlButton btnSmall; private ACtrlButton btnCoin; private ACtrlButton btnGather; private ACtrlButton btnSort; private ACtrlButton btnClose; private bool waitForRefresh; public event ItemMouseEventHandler ItemMouseDown; public event ItemMouseEventHandler ItemMouseUp; public event ItemMouseEventHandler ItemMouseClick; public event ItemMouseEventHandler ItemMouseMove; public event EventHandler ItemMouseLeave; public Character Character { get { return character; } set { character = value; for (int i = 0; i < this.itemTabs.Length; i++) { if (this.character == null || !this.itemTabs[i].SetItemSource(character.ItemSlots[i])) { this.itemTabs[i].ClearItems(); } } } } private void initCtrl() { this.itemTabs = new ItemTab[5]; for (int i = 0; i < itemTabs.Length; i++) { this.itemTabs[i] = new ItemTab(this); this.itemTabs[i].TabEnabled = new BitmapOrigin((Bitmap)Resource.ResourceManager.GetObject("Item_Tab_enabled_" + i), -9 - 31 * i, -26); this.itemTabs[i].TabDisabled = new BitmapOrigin((Bitmap)Resource.ResourceManager.GetObject("Item_Tab_disabled_" + i), -9 - 31 * i, -26); } this.itemTabs[0].Selected = true; this.vScroll = new ACtrlVScroll(); this.vScroll.PicBase.Normal = new BitmapOrigin(Resource.VScr9_enabled_base); this.vScroll.PicBase.Disabled = new BitmapOrigin(Resource.VScr9_disabled_base); this.vScroll.BtnPrev.Normal = new BitmapOrigin(Resource.VScr9_enabled_prev0); this.vScroll.BtnPrev.Pressed = new BitmapOrigin(Resource.VScr9_enabled_prev1); this.vScroll.BtnPrev.MouseOver = new BitmapOrigin(Resource.VScr9_enabled_prev2); this.vScroll.BtnPrev.Disabled = new BitmapOrigin(Resource.VScr9_disabled_prev); this.vScroll.BtnPrev.Size = this.vScroll.BtnPrev.Normal.Bitmap.Size; this.vScroll.BtnPrev.Location = new Point(0, 0); this.vScroll.BtnNext.Normal = new BitmapOrigin(Resource.VScr9_enabled_next0); this.vScroll.BtnNext.Pressed = new BitmapOrigin(Resource.VScr9_enabled_next1); this.vScroll.BtnNext.MouseOver = new BitmapOrigin(Resource.VScr9_enabled_next2); this.vScroll.BtnNext.Disabled = new BitmapOrigin(Resource.VScr9_disabled_next); this.vScroll.BtnNext.Size = this.vScroll.BtnNext.Normal.Bitmap.Size; this.vScroll.BtnNext.Location = new Point(0, 195); this.vScroll.BtnThumb.Normal = new BitmapOrigin(Resource.VScr9_enabled_thumb0); this.vScroll.BtnThumb.Pressed = new BitmapOrigin(Resource.VScr9_enabled_thumb1); this.vScroll.BtnThumb.MouseOver = new BitmapOrigin(Resource.VScr9_enabled_thumb2); this.vScroll.BtnThumb.Size = this.vScroll.BtnThumb.Normal.Bitmap.Size; this.vScroll.Location = new Point(152, 51); this.vScroll.Size = new Size(11, 207); this.vScroll.ValueChanged += new EventHandler(vScroll_ValueChanged); this.vScroll.ChildButtonStateChanged += new EventHandler(aCtrl_RefreshCall); this.btnFull = new ACtrlButton(); this.btnFull.Normal = new BitmapOrigin(Resource.Item_BtFull_normal_0); this.btnFull.Pressed = new BitmapOrigin(Resource.Item_BtFull_pressed_0); this.btnFull.MouseOver = new BitmapOrigin(Resource.Item_BtFull_mouseOver_0); this.btnFull.Disabled = new BitmapOrigin(Resource.Item_BtFull_disabled_0); this.btnFull.Location = new Point(147, 267); this.btnFull.Size = new Size(16, 16); this.btnFull.MouseClick += new MouseEventHandler(btnFull_MouseClick); this.btnFull.ButtonStateChanged += new EventHandler(aCtrl_RefreshCall); this.btnSmall = new ACtrlButton(); this.btnSmall.Normal = new BitmapOrigin(Resource.Item_BtSmall_normal_0); this.btnSmall.Pressed = new BitmapOrigin(Resource.Item_BtSmall_pressed_0); this.btnSmall.MouseOver = new BitmapOrigin(Resource.Item_BtSmall_mouseOver_0); this.btnSmall.Disabled = new BitmapOrigin(Resource.Item_BtSmall_disabled_0); this.btnSmall.Location = new Point(147, 267); this.btnSmall.Size = new Size(16, 16); this.btnSmall.MouseClick += new MouseEventHandler(btnSmall_MouseClick); this.btnSmall.ButtonStateChanged += new EventHandler(aCtrl_RefreshCall); this.btnCoin = new ACtrlButton(); this.btnCoin.Normal = new BitmapOrigin(Resource.Item_BtCoin_normal_0); this.btnCoin.Pressed = new BitmapOrigin(Resource.Item_BtCoin_pressed_0); this.btnCoin.MouseOver = new BitmapOrigin(Resource.Item_BtCoin_mouseOver_0); this.btnCoin.Disabled = new BitmapOrigin(Resource.Item_BtCoin_disabled_0); this.btnCoin.Location = new Point(9, 267); this.btnCoin.Size = new Size(40, 16); this.btnCoin.ButtonStateChanged += new EventHandler(aCtrl_RefreshCall); this.btnGather = new ACtrlButton(); this.btnGather.Normal = new BitmapOrigin(Resource.Item_BtGather_normal_0); this.btnGather.Pressed = new BitmapOrigin(Resource.Item_BtGather_pressed_0); this.btnGather.MouseOver = new BitmapOrigin(Resource.Item_BtGather_mouseOver_0); this.btnGather.Disabled = new BitmapOrigin(Resource.Item_BtGather_disabled_0); this.btnGather.Location = new Point(129, 267); this.btnGather.Size = new Size(16, 16); this.btnGather.ButtonStateChanged += new EventHandler(aCtrl_RefreshCall); this.btnGather.MouseClick += new MouseEventHandler(btnGather_MouseClick); this.btnSort = new ACtrlButton(); this.btnSort.Normal = new BitmapOrigin(Resource.Item_BtSort_normal_0); this.btnSort.Pressed = new BitmapOrigin(Resource.Item_BtSort_pressed_0); this.btnSort.MouseOver = new BitmapOrigin(Resource.Item_BtSort_mouseOver_0); this.btnSort.Disabled = new BitmapOrigin(Resource.Item_BtSort_disabled_0); this.btnSort.Location = new Point(129, 267); this.btnSort.Size = new Size(16, 16); this.btnSort.ButtonStateChanged += new EventHandler(aCtrl_RefreshCall); this.btnSort.MouseClick += new MouseEventHandler(btnSort_MouseClick); this.btnClose = new ACtrlButton(); this.btnClose.Normal = new BitmapOrigin(Resource.BtClose3_normal_0); this.btnClose.Pressed = new BitmapOrigin(Resource.BtClose3_pressed_0); this.btnClose.MouseOver = new BitmapOrigin(Resource.BtClose3_mouseOver_0); this.btnClose.Disabled = new BitmapOrigin(Resource.BtClose3_disabled_0); this.btnClose.Location = new Point(150, 6); this.btnClose.Size = new Size(13, 13); this.btnClose.ButtonStateChanged += new EventHandler(aCtrl_RefreshCall); this.btnClose.MouseClick += new MouseEventHandler(btnClose_MouseClick); } public override void Refresh() { this.preRender(); this.SetBitmap(this.Bitmap); this.CaptionRectangle = new Rectangle(0, 0, this.Bitmap.Width, 24); base.Refresh(); } protected override bool captionHitTest(Point point) { if (this.btnClose.Rectangle.Contains(point)) return false; return base.captionHitTest(point); } private void preRender() { if (Bitmap != null) Bitmap.Dispose(); if (this.btnGather.Visible) { this.btnSort.Visible = false; } else { this.btnSort.Visible = true; } if (this.fullMode) { this.btnFull.Visible = false; this.btnSmall.Visible = true; this.vScroll.Visible = false; this.btnClose.Location = new Point(574, 6); renderFull(); } else { this.btnFull.Visible = true; this.btnSmall.Visible = false; this.vScroll.Visible = true; this.vScroll.Maximum = this.SelectedTab.ScrollMaxValue - 6; this.vScroll.Value = this.SelectedTab.ScrollValue; this.btnClose.Location = new Point(150, 6); renderSmall(); } } private void renderSmall() { this.Bitmap = new Bitmap(Resource.Item_backgrnd); Graphics g = Graphics.FromImage(this.Bitmap); g.DrawImage(Resource.Item_backgrnd2, 6, 23); renderTabs(g); g.DrawImage(Resource.Item_backgrnd3, 7, 45); foreach (AControl ctrl in this.aControls) { ctrl.Draw(g); } ItemBase[] itemArray = this.SelectedTab.Items; int idxOffset = 4 * this.SelectedTab.ScrollValue; for (int i = 0; i < 24; i++) { Point origin = getItemIconOrigin(i); origin.Offset(0, 32); renderItemBase(g, itemArray[i + idxOffset], origin); } g.Dispose(); } private void renderFull() { this.Bitmap = new Bitmap(Resource.Item_FullBackgrnd); Graphics g = Graphics.FromImage(this.Bitmap); g.DrawImage(Resource.Item_FullBackgrnd2, 6, 23); renderTabs(g); g.DrawImage(Resource.Item_FullBackgrnd3, 10, 51); foreach (AControl ctrl in this.aControls) { ctrl.Draw(g); } ItemBase[] itemArray = this.SelectedTab.Items; for (int i = 0; i < itemArray.Length; i++) { int idx = i % 24, group = i / 24; Point origin = getItemIconOrigin(i); origin.Offset(0, 32); renderItemBase(g, itemArray[i], origin); } g.Dispose(); } private void renderTabs(Graphics g) { for (int i = 0; i < this.itemTabs.Length; i++) { if (this.itemTabs[i].Selected) { Point pos = this.itemTabs[i].TabEnabled.OpOrigin; // if (this.fullMode) pos.Offset(0, -2); g.DrawImage(this.itemTabs[i].TabEnabled.Bitmap, pos); } else { g.DrawImage(this.itemTabs[i].TabDisabled.Bitmap, this.itemTabs[i].TabEnabled.OpOrigin); } } } private void renderItemBase(Graphics g, ItemBase itemBase, Point origin) { if (itemBase is Gear) renderGear(g, itemBase as Gear, origin); else if (itemBase is Item) renderItem(g, itemBase as Item, origin); } private void renderGear(Graphics g, Gear gear, Point origin) { if (g == null || gear == null) return; Pen pen = GearGraphics.GetGearItemBorderPen(gear.Grade); if (pen != null) { Point[] path = GearGraphics.GetIconBorderPath(origin.X, origin.Y - 32); g.DrawLines(pen, path); } g.DrawImage(Resource.Item_shadow, origin.X + 3, origin.Y - 6); if (gear.IconRaw.Bitmap != null) { g.DrawImage(gear.IconRaw.Bitmap, origin.X - gear.IconRaw.Origin.X, origin.Y - gear.IconRaw.Origin.Y); } if (gear.Cash) { /* int value; if (gear.Props.TryGetValue(GearPropType.royalSpecial, out value) && value > 0) g.DrawImage(Resource.CashItem_label_0, origin.X + 20, origin.Y - 12); else if (gear.Props.TryGetValue(GearPropType.masterSpecial, out value) && value > 0) g.DrawImage(Resource.CashItem_label_3, origin.X + 20, origin.Y - 12); else */ g.DrawImage(Resource.CashItem_0, origin.X + 20, origin.Y - 12); } } private void renderItem(Graphics g, Item item, Point origin) { if (g == null || item == null) return; g.DrawImage(Resource.Item_shadow, origin.X + 3, origin.Y - 6); if (item.IconRaw.Bitmap != null) { g.DrawImage(item.IconRaw.Bitmap, origin.X - item.IconRaw.Origin.X, origin.Y - item.IconRaw.Origin.Y); } if (item.Cash) { if (item.Props.TryGetValue(ItemPropType.wonderGrade, out long value) && value > 0) { Image label = Resource.ResourceManager.GetObject("CashItem_label_" + (value + 3)) as Image; if (label != null) { g.DrawImage(new Bitmap(label), origin.X + 20, origin.Y - 12); } } else { g.DrawImage(Resource.CashItem_0, origin.X + 20, origin.Y - 12); } } } private Point getItemIconOrigin(int index) { int idx = index % 24, group = index / 24; Point p = new Point((idx % 4 + group * 4) * 36, idx / 4 * 35); p.Offset(10, 51); return p; } public int GetSlotIndexByPoint(Point point) { Point p = point; p.Offset(-10, -51); if (p.X < 0 || p.Y < 0) return -1; int x = p.X / 36, y = p.Y / 35; if (y >= 6 || (fullMode ? x >= 24 : x >= 4)) return -1; int idx = y * 4 + x % 4 + x / 4 * 24; if (new Rectangle(getItemIconOrigin(idx), new Size(33, 33)).Contains(point)) return idx; else return -1; } public int GetItemIndexByPoint(Point point) { int slotIdx = GetSlotIndexByPoint(point); if (slotIdx != -1 && !this.fullMode) { slotIdx += 4 * this.SelectedTab.ScrollValue; } return slotIdx; } public ItemBase GetItemByPoint(Point point) { int itemIdx = GetItemIndexByPoint(point); if (itemIdx > -1 && itemIdx < this.SelectedTab.Items.Length) return this.SelectedTab.Items[itemIdx]; else return null; } #region 重写和响应事件 protected override void OnMouseMove(MouseEventArgs e) { foreach (AControl ctrl in this.aControls) { ctrl.OnMouseMove(e); } if (this.waitForRefresh) { this.Refresh(); waitForRefresh = false; } base.OnMouseMove(e); ItemBase item = GetItemByPoint(e.Location); if (item != null) this.OnItemMouseMove(new ItemMouseEventArgs(e, item)); else this.OnItemMouseLeave(EventArgs.Empty); } protected override void OnMouseDown(MouseEventArgs e) { foreach (AControl ctrl in this.aControls) { ctrl.OnMouseDown(e); } if (this.waitForRefresh) { this.Refresh(); waitForRefresh = false; } base.OnMouseDown(e); ItemBase item = GetItemByPoint(e.Location); if (item != null) this.OnItemMouseDown(new ItemMouseEventArgs(e, item)); } protected override void OnMouseUp(MouseEventArgs e) { foreach (AControl ctrl in this.aControls) { ctrl.OnMouseUp(e); } if (this.waitForRefresh) { this.Refresh(); waitForRefresh = false; } base.OnMouseUp(e); ItemBase item = GetItemByPoint(e.Location); if (item != null) this.OnItemMouseUp(new ItemMouseEventArgs(e, item)); } protected override void OnMouseClick(MouseEventArgs e) { //处理选择选项卡 tab_OnMouseClick(e); foreach (AControl ctrl in this.aControls) { ctrl.OnMouseClick(e); } if (this.waitForRefresh) { this.Refresh(); waitForRefresh = false; } base.OnMouseClick(e); ItemBase item = GetItemByPoint(e.Location); if (item != null) this.OnItemMouseClick(new ItemMouseEventArgs(e, item)); } protected override void OnMouseWheel(MouseEventArgs e) { foreach (AControl ctrl in this.aControls) { ctrl.OnMouseWheel(e); } if (this.waitForRefresh) { this.Refresh(); waitForRefresh = false; } base.OnMouseWheel(e); } protected virtual void OnItemMouseDown(ItemMouseEventArgs e) { if (this.ItemMouseDown != null) this.ItemMouseDown(this, e); } protected virtual void OnItemMouseUp(ItemMouseEventArgs e) { if (this.ItemMouseUp != null) this.ItemMouseUp(this, e); } protected virtual void OnItemMouseClick(ItemMouseEventArgs e) { if (this.ItemMouseClick != null) this.ItemMouseClick(this, e); } protected virtual void OnItemMouseMove(ItemMouseEventArgs e) { if (this.ItemMouseMove != null) this.ItemMouseMove(this, e); } protected virtual void OnItemMouseLeave(EventArgs e) { if (this.ItemMouseLeave != null) this.ItemMouseLeave(this, e); } #endregion private void tab_OnMouseClick(MouseEventArgs e) { if (e.Button == MouseButtons.Left) { for (int i = 0; i < this.itemTabs.Length; i++) { Rectangle rect; if (this.itemTabs[i].Selected) { rect = this.itemTabs[i].TabEnabled.Rectangle; } else { rect = this.itemTabs[i].TabDisabled.Rectangle; } if (rect.Contains(e.Location)) { if (this.SelectedIndex != i) { this.SelectedIndex = i; this.btnGather.Visible = true; //切换tab时重置btnGather状态 this.waitForRefresh = true; } break; } } } } private void btnSmall_MouseClick(object sender, MouseEventArgs e) { this.fullMode = false; this.waitForRefresh = true; } private void btnFull_MouseClick(object sender, MouseEventArgs e) { this.fullMode = true; this.waitForRefresh = true; } private void vScroll_ValueChanged(object sender, EventArgs e) { this.SelectedTab.ScrollValue = this.vScroll.Value; this.waitForRefresh = true; } private void btnGather_MouseClick(object sender, MouseEventArgs e) { this.btnGather.Visible = !this.btnGather.Visible; this.gather(); this.waitForRefresh = true; } private void btnSort_MouseClick(object sender, MouseEventArgs e) { this.btnGather.Visible = !this.btnGather.Visible; this.sort(); this.waitForRefresh = true; } private void btnClose_MouseClick(object sender, MouseEventArgs e) { this.Visible = false; } private void aCtrl_RefreshCall(object sender, EventArgs e) { this.waitForRefresh = true; } private void gather() { ItemBase[] itemArray = this.SelectedTab.Items; Queue nullQueue = new Queue(); for (int i = 0; i < itemArray.Length; i++) { if (itemArray[i] == null) { nullQueue.Enqueue(i); } else if (nullQueue.Count > 0) { int nullIdx = nullQueue.Dequeue(); itemArray[nullIdx] = itemArray[i]; itemArray[i] = null; nullQueue.Enqueue(i); } } } private void sort() { ItemBase[] itemArray = this.SelectedTab.Items; Array.Sort(itemArray, (a, b) => { if (a == null) return 1; if (b == null) return -1; return a.ItemID - b.ItemID; }); } private IEnumerable aControls { get { yield return this.vScroll; yield return this.btnFull; yield return this.btnSmall; yield return this.btnCoin; yield return this.btnGather; yield return this.btnSort; yield return this.btnClose; } } public ItemTab[] ItemTabs { get { return this.itemTabs; } } /// /// 获取或设置一个bool值,它表示是否当前背包显示为大背包模式。 /// public bool FullMode { get { return fullMode; } set { fullMode = value; } } /// /// 获取或设置正在选中的背包选项卡的索引。 /// public int SelectedIndex { get { for (int i = 0; i < this.itemTabs.Length; i++) { if (this.itemTabs[i].Selected) return i; } this.itemTabs[0].Selected = true; return 0; } set { value = Math.Min(Math.Max(value, 0), this.itemTabs.Length - 1); this.selectedIndexChanging = true; for (int i = 0; i < this.itemTabs.Length; i++) { this.itemTabs[i].Selected = (i == value); } this.selectedIndexChanging = false; } } public bool AddItem(ItemBase item) { if (item == null) return false; int idx; switch (item.Type) { case ItemBaseType.Equip: idx = 0; break; case ItemBaseType.Consume: idx = 1; break; case ItemBaseType.Install: idx = 3; break; case ItemBaseType.Etc: idx = 2; break; case ItemBaseType.Cash: idx = 4; break; default: return false; } ItemBase[] itemArray = this.itemTabs[idx].Items; for (int i = 0; i < itemArray.Length; i++) { if (itemArray[i] == null) { itemArray[i] = (ItemBase)item.Clone(); this.SelectedIndex = idx; this.itemTabs[idx].ScrollValue = (i - 20) / 4; this.Refresh(); return true; } } return false; } public void RemoveItem(int index) { ItemBase[] itemArray = this.SelectedTab.Items; if (index >= 0 && index < itemArray.Length && itemArray[index] != null) { itemArray[index] = null; this.Refresh(); } } /// /// 获取或设置正在选中的背包选项卡。 /// public ItemTab SelectedTab { get { return this.itemTabs[this.SelectedIndex]; } set { this.SelectedIndex = Array.IndexOf(this.itemTabs, value); } } public class ItemTab { public ItemTab(AfrmItem owner) { this.owner = owner; this.items = new ItemBase[ItemCount]; } public const int ItemCount = 96; private AfrmItem owner; private ItemBase[] items; private bool selected; private BitmapOrigin tabEnabled; private BitmapOrigin tabDisabled; private int scrollValue; public ItemBase[] Items { get { return this.items; } } public bool Selected { get { return selected; } set { if (selected != value) { if (!this.owner.selectedIndexChanging) this.owner.SelectedIndex = Array.IndexOf(this.owner.itemTabs, this); this.selected = value; } } } public bool SetItemSource(ItemBase[] items) { if (items == null || items.Length != ItemCount) { return false; } else { this.items = items; return true; } } public void ClearItems() { for (int i = 0; i < this.items.Length; i++) { this.items[i] = null; } } public BitmapOrigin TabEnabled { get { return tabEnabled; } set { tabEnabled = value; } } public BitmapOrigin TabDisabled { get { return tabDisabled; } set { tabDisabled = value; } } public int ScrollMaxValue { get { return this.items.Length / 4; } } public int ScrollValue { get { return scrollValue; } set { value = Math.Min(Math.Max(0, value), ScrollMaxValue); scrollValue = value; } } } } } ================================================ FILE: WzComparerR2/CharaSimControl/AfrmStat.cs ================================================ using System; using System.Drawing; using System.Collections.Generic; using System.Windows.Forms; using System.Text; using CharaSimResource; using WzComparerR2.CharaSim; using WzComparerR2.Common; using WzComparerR2.Controls; namespace WzComparerR2.CharaSimControl { public class AfrmStat : AlphaForm { public AfrmStat() { initCtrl(); } private bool fullMode; private Character character; private ACtrlButton btnHPUp; private ACtrlButton btnMPUp; private ACtrlButton btnStrUp; private ACtrlButton btnDexUp; private ACtrlButton btnIntUp; private ACtrlButton btnLukUp; private ACtrlButton btnAuto; private ACtrlButton btnClose; private ACtrlButton btnDetailOpen; private ACtrlButton btnDetailClose; private bool waitForRefresh; public Character Character { get { return character; } set { character = value; } } private void initCtrl() { this.btnHPUp = new ACtrlButton(); this.btnHPUp.Location = new Point(167, 157); this.btnMPUp = new ACtrlButton(); this.btnMPUp.Location = new Point(167, 175); this.btnStrUp = new ACtrlButton(); this.btnStrUp.Location = new Point(167, 262); this.btnDexUp = new ACtrlButton(); this.btnDexUp.Location = new Point(167, 280); this.btnIntUp = new ACtrlButton(); this.btnIntUp.Location = new Point(167, 298); this.btnLukUp = new ACtrlButton(); this.btnLukUp.Location = new Point(167, 316); ACtrlButton[] addBtnList = new ACtrlButton[] { btnHPUp, btnMPUp, btnStrUp, btnDexUp, btnIntUp, btnLukUp }; for (int i = 0; i < addBtnList.Length; i++) { addBtnList[i].Normal = new BitmapOrigin(Resource.Stat_main_BtUp_normal_0); addBtnList[i].MouseOver = new BitmapOrigin(Resource.Stat_main_BtUp_mouseOver_0); addBtnList[i].Pressed = new BitmapOrigin(Resource.Stat_main_BtUp_pressed_0); addBtnList[i].Disabled = new BitmapOrigin(Resource.Stat_main_BtUp_disabled_0); addBtnList[i].Size = new Size(12, 12); addBtnList[i].ButtonStateChanged += new EventHandler(aCtrl_RefreshCall); } this.btnClose = new ACtrlButton(); this.btnClose.Normal = new BitmapOrigin(Resource.BtClose3_normal_0); this.btnClose.Pressed = new BitmapOrigin(Resource.BtClose3_pressed_0); this.btnClose.MouseOver = new BitmapOrigin(Resource.BtClose3_mouseOver_0); this.btnClose.Disabled = new BitmapOrigin(Resource.BtClose3_disabled_0); this.btnClose.Location = new Point(170, 6); this.btnClose.Size = new Size(13, 13); this.btnClose.ButtonStateChanged += new EventHandler(aCtrl_RefreshCall); this.btnClose.MouseClick += new MouseEventHandler(btnClose_MouseClick); this.btnDetailOpen = new ACtrlButton(); this.btnDetailOpen.Normal = new BitmapOrigin(Resource.Stat_main_BtDetailOpen_normal_0); this.btnDetailOpen.Pressed = new BitmapOrigin(Resource.Stat_main_BtDetailOpen_pressed_0); this.btnDetailOpen.MouseOver = new BitmapOrigin(Resource.Stat_main_BtDetailOpen_mouseOver_0); this.btnDetailOpen.Disabled = new BitmapOrigin(Resource.Stat_main_BtDetailOpen_disabled_0); this.btnDetailOpen.Location = new Point(112,344); this.btnDetailOpen.Size = new Size(68, 16); this.btnDetailOpen.ButtonStateChanged += new EventHandler(aCtrl_RefreshCall); this.btnDetailOpen.MouseClick += new MouseEventHandler(btnDetailOpen_MouseClick); this.btnDetailClose = new ACtrlButton(); this.btnDetailClose.Normal = new BitmapOrigin(Resource.Stat_main_BtDetailClose_normal_0); this.btnDetailClose.Pressed = new BitmapOrigin(Resource.Stat_main_BtDetailClose_pressed_0); this.btnDetailClose.MouseOver = new BitmapOrigin(Resource.Stat_main_BtDetailClose_mouseOver_0); this.btnDetailClose.Disabled = new BitmapOrigin(Resource.Stat_main_BtDetailClose_disabled_0); this.btnDetailClose.Location = new Point(112, 344); this.btnDetailClose.Size = new Size(68, 16); this.btnDetailClose.ButtonStateChanged += new EventHandler(aCtrl_RefreshCall); this.btnDetailClose.MouseClick += new MouseEventHandler(btnDetailClose_MouseClick); this.btnAuto = new ACtrlButton(); this.btnAuto.Normal = new BitmapOrigin(Resource.Stat_main_BtAuto_normal_3); this.btnAuto.Pressed = new BitmapOrigin(Resource.Stat_main_BtAuto_pressed_0); this.btnAuto.MouseOver = new BitmapOrigin(Resource.Stat_main_BtAuto_mouseOver_0); this.btnAuto.Disabled = new BitmapOrigin(Resource.Stat_main_BtAuto_disabled_0); this.btnAuto.Location = new Point(108, 217); this.btnAuto.Size = new Size(67, 34); this.btnAuto.ButtonStateChanged += new EventHandler(aCtrl_RefreshCall); } private IEnumerable aControls { get { yield return btnHPUp; yield return btnMPUp; yield return btnStrUp; yield return btnDexUp; yield return btnIntUp; yield return btnLukUp; yield return btnAuto; yield return btnClose; yield return btnDetailOpen; yield return btnDetailClose; } } public override void Refresh() { this.preRender(); this.SetBitmap(this.Bitmap); this.CaptionRectangle = new Rectangle(0, 0, this.Bitmap.Width, 24); base.Refresh(); } protected override bool captionHitTest(Point point) { if (this.btnClose.Rectangle.Contains(point)) return false; return base.captionHitTest(point); } private void preRender() { if (Bitmap != null) Bitmap.Dispose(); setControlState(); Size size = Resource.Stat_main_backgrnd.Size; if (fullMode) size = new Size(size.Width + Resource.Stat_detail_backgrnd.Width, size.Height); //绘制背景 Bitmap stat = new Bitmap(size.Width, size.Height); Graphics g = Graphics.FromImage(stat); renderBase(g); if (fullMode) renderDetail(g); //绘制按钮 foreach (AControl ctrl in this.aControls) { ctrl.Draw(g); } g.Dispose(); this.Bitmap = stat; } private void setControlState() { if (this.fullMode) { this.btnDetailOpen.Visible = true; this.btnDetailClose.Visible = false; } else { this.btnDetailOpen.Visible = false; this.btnDetailClose.Visible = true; } if (this.character != null) { CharacterStatus charStat = this.character.Status; setButtonEnabled(this.btnHPUp, charStat.Ap > 0 && charStat.MaxHP.BaseVal < charStat.MaxHP.TotalMax); setButtonEnabled(this.btnMPUp, charStat.Ap > 0 && charStat.MaxMP.BaseVal < charStat.MaxMP.TotalMax); setButtonEnabled(this.btnStrUp, charStat.Ap > 0 && charStat.Strength.BaseVal <= 999); setButtonEnabled(this.btnDexUp, charStat.Ap > 0 && charStat.Dexterity.BaseVal <= 999); setButtonEnabled(this.btnIntUp, charStat.Ap > 0 && charStat.Intelligence.BaseVal <= 999); setButtonEnabled(this.btnLukUp, charStat.Ap > 0 && charStat.Luck.BaseVal <= 999); setButtonEnabled(this.btnAuto, charStat.Ap > 0); } else { foreach (AControl ctrl in this.aControls) { setButtonEnabled(ctrl as ACtrlButton, true); } } } private void setButtonEnabled(ACtrlButton button, bool enabled) { if (button == null) return; if (enabled) { if (button.State == ButtonState.Disabled) { button.State = ButtonState.Normal; } } else { if (button.State != ButtonState.Disabled) { button.State = ButtonState.Disabled; } } } private void renderBase(Graphics g) { g.DrawImage(Resource.Stat_main_backgrnd, 0, 0); g.DrawImage(Resource.Stat_main_backgrnd2, 6, 22); g.DrawImage(Resource.Stat_main_backgrnd3, 7, 211); if (this.character != null) { CharacterStatus charStat = this.character.Status; //绘制自动分配 // g.DrawImage(charStat.Ap > 0 ? Resource.Stat_main_BtAuto_normal_3 : Resource.Stat_main_BtAuto_disabled_0, 94, 180); switch (charStat.Job / 100 % 10)//绘制角色属性灰色背景 { case 0: case 1: case 3: case 5: g.DrawImage(Resource.Stat_main_Disabled_INT, 11, 296); g.DrawImage(Resource.Stat_main_Disabled_LUK, 11, 314); break; case 2: g.DrawImage(Resource.Stat_main_Disabled_STR, 11, 260); g.DrawImage(Resource.Stat_main_Disabled_DEX, 11, 278); break; case 4: g.DrawImage(Resource.Stat_main_Disabled_STR, 11, 260); g.DrawImage(Resource.Stat_main_Disabled_INT, 11, 296); break; } g.DrawString(this.character.Name, GearGraphics.ItemDetailFont, GearGraphics.StatDetailGrayBrush, 72f, 32f); g.DrawString(ItemStringHelper.GetJobName(charStat.Job), GearGraphics.ItemDetailFont, GearGraphics.StatDetailGrayBrush, 72f, 50f); g.DrawString(charStat.Level.ToString(), GearGraphics.ItemDetailFont, GearGraphics.StatDetailGrayBrush, 72f, 68f); g.DrawString(charStat.Exp + " (" + (charStat.Exp == -1 ? 0 : ((long)charStat.Exp * 100 / charStat.Exptnl)) + "%)", GearGraphics.ItemDetailFont, GearGraphics.StatDetailGrayBrush, 72f, 86f); g.DrawString("1", GearGraphics.ItemDetailFont, GearGraphics.StatDetailGrayBrush, 72f, 104f); g.DrawString("0 (0%)", GearGraphics.ItemDetailFont, GearGraphics.StatDetailGrayBrush, 72f, 122f); g.DrawString(string.IsNullOrEmpty(this.character.Guild) ? "-" : this.character.Guild, GearGraphics.ItemDetailFont, GearGraphics.StatDetailGrayBrush, 72f, 140f); g.DrawString(charStat.HP + " / " + charStat.MaxHP.GetSum(), GearGraphics.ItemDetailFont, GearGraphics.StatDetailGrayBrush, 72f, 158f); g.DrawString(charStat.MP + " / " + charStat.MaxMP.GetSum(), GearGraphics.ItemDetailFont, GearGraphics.StatDetailGrayBrush, 72f, 176f); g.DrawString(charStat.Pop.ToString(), GearGraphics.ItemDetailFont, GearGraphics.StatDetailGrayBrush, 72f, 194f); g.DrawString(charStat.Ap.ToString().PadLeft(4), GearGraphics.ItemDetailFont, GearGraphics.StatDetailGrayBrush, 64f, 236f); g.DrawString(charStat.Strength.ToString(), GearGraphics.ItemDetailFont, GearGraphics.StatDetailGrayBrush, 72f, 263f); g.DrawString(charStat.Dexterity.ToString(), GearGraphics.ItemDetailFont, GearGraphics.StatDetailGrayBrush, 72f, 281f); g.DrawString(charStat.Intelligence.ToString(), GearGraphics.ItemDetailFont, GearGraphics.StatDetailGrayBrush, 72f, 299f); g.DrawString(charStat.Luck.ToString(), GearGraphics.ItemDetailFont, GearGraphics.StatDetailGrayBrush, 72f, 317f); } } private void renderDetail(Graphics g) { g.TranslateTransform(Resource.Stat_main_backgrnd.Width, Resource.Stat_main_backgrnd.Height - Resource.Stat_detail_backgrnd.Height); g.DrawImage(Resource.Stat_detail_backgrnd, 0, 0); g.DrawImage(Resource.Stat_detail_backgrnd2, 6, 7); if (this.character != null) { CharacterStatus charStat = this.character.Status; //g.DrawString("0 ( 0% )", GearGraphics.GearDetailFont, getDetailBrush(0), 72f, 16f); //g.DrawString("0", GearGraphics.GearDetailFont, getDetailBrush(0), 72f, 34f); int brushSign; double max, min; this.character.CalcAttack(out max, out min, out brushSign); float y = 16f; g.DrawString(max == 0 ? "0" : Math.Round(min) + " ~ " + Math.Round(max), GearGraphics.ItemDetailFont, getDetailBrush(brushSign), 72f, y); g.DrawString(charStat.CriticalRate.GetSum() + "%", GearGraphics.ItemDetailFont, charStat.CriticalRate.BuffAdd > 0 ? Brushes.Red : GearGraphics.StatDetailGrayBrush, 72f, (y += 18f)); g.DrawString(charStat.PDDamage.ToStringDetail(out brushSign), GearGraphics.ItemDetailFont, getDetailBrush(brushSign), 72f, (y += 18f)); g.DrawString(charStat.MDDamage.ToStringDetail(out brushSign), GearGraphics.ItemDetailFont, getDetailBrush(brushSign), 72f, (y += 18f)); g.DrawString(charStat.PAccurate.ToStringDetail(out brushSign), GearGraphics.ItemDetailFont, getDetailBrush(brushSign), 72f, (y += 18f)); g.DrawString(charStat.MAccurate.ToStringDetail(out brushSign), GearGraphics.ItemDetailFont, getDetailBrush(brushSign), 72f, (y += 18f)); g.DrawString(charStat.PEvasion.ToStringDetail(out brushSign), GearGraphics.ItemDetailFont, getDetailBrush(brushSign), 72f, (y += 18f)); g.DrawString(charStat.MEvasion.ToStringDetail(out brushSign), GearGraphics.ItemDetailFont, getDetailBrush(brushSign), 72f, (y += 18f)); g.DrawString(charStat.MoveSpeed.GetSum() + "%", GearGraphics.ItemDetailFont, getDetailBrush(0), 72f, (y += 18f)); g.DrawString(charStat.Jump.GetSum() + "%", GearGraphics.ItemDetailFont, getDetailBrush(0), 72f, (y += 18f)); } g.ResetTransform(); } private Brush getDetailBrush(int sign) { switch (sign) { case 1: return Brushes.Red; case -1: return Brushes.Blue; case 0: default: return GearGraphics.StatDetailGrayBrush; } } private void btnClose_MouseClick(object sender, MouseEventArgs e) { this.Visible = false; } private void aCtrl_RefreshCall(object sender, EventArgs e) { this.waitForRefresh = true; } private void btnDetailOpen_MouseClick(object sender, MouseEventArgs e) { this.fullMode = false; this.waitForRefresh = true; } private void btnDetailClose_MouseClick(object sender, MouseEventArgs e) { this.fullMode = true; this.waitForRefresh = true; } protected override void OnMouseMove(MouseEventArgs e) { foreach (AControl ctrl in this.aControls) { ctrl.OnMouseMove(e); } if (this.waitForRefresh) { this.Refresh(); waitForRefresh = false; } base.OnMouseMove(e); } protected override void OnMouseDown(MouseEventArgs e) { foreach (AControl ctrl in this.aControls) { ctrl.OnMouseDown(e); } if (this.waitForRefresh) { this.Refresh(); waitForRefresh = false; } base.OnMouseDown(e); } protected override void OnMouseUp(MouseEventArgs e) { foreach (AControl ctrl in this.aControls) { ctrl.OnMouseUp(e); } if (this.waitForRefresh) { this.Refresh(); waitForRefresh = false; } base.OnMouseUp(e); } protected override void OnMouseClick(MouseEventArgs e) { foreach (AControl ctrl in this.aControls) { ctrl.OnMouseClick(e); } if (this.waitForRefresh) { this.Refresh(); waitForRefresh = false; } base.OnMouseClick(e); } } } ================================================ FILE: WzComparerR2/CharaSimControl/AfrmTooltip.cs ================================================ using System; using System.Collections.Generic; using System.Text; using System.Drawing; using System.Windows.Forms; using WzComparerR2.Common; using WzComparerR2.CharaSim; using WzComparerR2.Controls; namespace WzComparerR2.CharaSimControl { public class AfrmTooltip : AlphaForm { public AfrmTooltip() { this.menu = new ContextMenuStrip(); this.menu.Items.Add(new ToolStripMenuItem("复制(&C)", null, tsmiCopy_Click)); this.menu.Items.Add(new ToolStripMenuItem("保存(&S)", null, tsmiSave_Click)); this.ContextMenuStrip = this.menu; this.Size = new Size(1, 1); this.HideOnHover = true; this.FamiliarRender = new FamiliarTooltipRender(); this.GearRender = new GearTooltipRender2(); this.ItemRender = new ItemTooltipRender2(); this.SkillRender = new SkillTooltipRender2(); this.RecipeRender = new RecipeTooltipRender(); this.MobRender = new MobTooltipRenderer(); this.NpcRender = new NpcTooltipRenderer(); this.SizeChanged += AfrmTooltip_SizeChanged; this.MouseClick += AfrmTooltip_MouseClick; } private object item; private ContextMenuStrip menu; private bool showMenu; private bool showID; public Object TargetItem { get { return item; } set { item = value; } } public StringLinker StringLinker { get; set; } public Character Character { get; set; } public FamiliarTooltipRender FamiliarRender { get; private set; } public GearTooltipRender2 GearRender { get; private set; } public ItemTooltipRender2 ItemRender { get; private set; } public SkillTooltipRender2 SkillRender { get; private set; } public RecipeTooltipRender RecipeRender { get; private set; } public MobTooltipRenderer MobRender { get; private set; } public NpcTooltipRenderer NpcRender { get; private set; } public string ImageFileName { get; set; } public bool ShowID { get { return this.showID; } set { this.showID = value; this.FamiliarRender.ShowObjectID = value; this.GearRender.ShowObjectID = value; this.ItemRender.ShowObjectID = value; this.SkillRender.ShowObjectID = value; this.RecipeRender.ShowObjectID = value; } } public bool ShowMenu { get { return showMenu; } set { showMenu = value; } } public override void Refresh() { this.PreRender(); if (this.Bitmap != null) { this.SetBitmap(Bitmap); this.CaptionRectangle = new Rectangle(0, 0, Bitmap.Width, Bitmap.Height); base.Refresh(); } } public void PreRender() { if (this.item == null) return; TooltipRender renderer; if (item is Item) { renderer = ItemRender; ItemRender.Item = this.item as Item; } else if (item is Familiar) { renderer = FamiliarRender; FamiliarRender.Familiar = this.item as Familiar; // FamiliarRender.UseAssembleUI = EnableAssembleTooltip; } else if (item is Gear) { renderer = GearRender; GearRender.Gear = this.TargetItem as Gear; if (false) { Gear g = GearRender.Gear; if (this.StringLinker.StringEqp.ContainsKey(g.ItemID)) { this.StringLinker.StringEqp[g.ItemID].Name = "暴君之高卡文黑锅"; this.StringLinker.StringEqp[g.ItemID].Desc = @"""#c这个锅 我背了!#"" ————gaokawen"; } g.Star = 25; g.Grade = GearGrade.SS; g.AdditionGrade = GearGrade.B; g.Props[GearPropType.reqLevel] = 250; g.Props[GearPropType.reqSTR] = 6; g.Props[GearPropType.reqDEX] = 6; g.Props[GearPropType.reqINT] = 6; g.Props[GearPropType.reqLUK] = 6; g.Props[GearPropType.reqPOP] = 666; g.Props[GearPropType.level] = 1; g.Props[GearPropType.reqJob] = 0; g.Props[GearPropType.incPAD] = 6; g.Props[GearPropType.incMAD] = 6; g.Props[GearPropType.incPDD] = 666; g.Props[GearPropType.incMDD] = 666; g.Props[GearPropType.tuc] = 66; g.Props[GearPropType.superiorEqp] = 1; g.Props[GearPropType.tradeAvailable] = 2; //g.Props[GearPropType.charismaEXP] = 88; //g.Props[GearPropType.willEXP] = 88; //g.Props[GearPropType.charmEXP] = 88; g.Props[GearPropType.nActivatedSocket] = 1; //g.Props[GearPropType.setItemID] = 135; //g.Options[0] = Potential.LoadFromWz(60001, 3); //g.Options[1] = Potential.LoadFromWz(60001, 3); //g.Options[2] = Potential.LoadFromWz(60001, 3); //g.AdditionalOptions[0] = Potential.LoadFromWz(32086, 10); //g.AdditionalOptions[1] = Potential.LoadFromWz(32086, 10); //g.AdditionalOptions[2] = Potential.LoadFromWz(32086, 10); } } else if (item is Skill) { renderer = SkillRender; SkillRender.Skill = this.item as Skill; } else if (item is Recipe) { renderer = RecipeRender; RecipeRender.Recipe = this.item as Recipe; } else if (item is Mob) { renderer = MobRender; MobRender.MobInfo = this.item as Mob; } else if (item is Npc) { renderer = NpcRender; NpcRender.NpcInfo = this.item as Npc; } else { this.Bitmap = null; renderer = null; return; } renderer.StringLinker = StringLinker; this.Bitmap = renderer.Render(); } void AfrmTooltip_MouseClick(object sender, System.Windows.Forms.MouseEventArgs e) { if (e.Button == MouseButtons.Right && this.showMenu) { this.menu.Show(this, e.Location); } } void tsmiCopy_Click(object sender, EventArgs e) { if (this.Bitmap != null) { var dataObj = new ImageDataObject(this.Bitmap, this.ImageFileName); Clipboard.SetDataObject(dataObj, false); } } void tsmiSave_Click(object sender, EventArgs e) { if (this.Bitmap != null && this.item != null) { using (SaveFileDialog dlg = new SaveFileDialog()) { dlg.Filter = "*.png|*.png|*.*|*.*"; dlg.FileName = this.ImageFileName; if (dlg.ShowDialog() == DialogResult.OK) { this.Bitmap.Save(dlg.FileName, System.Drawing.Imaging.ImageFormat.Png); } } } } void AfrmTooltip_SizeChanged(object sender, EventArgs e) { if (this.Bitmap != null) this.SetClientSizeCore(this.Bitmap.Width, this.Bitmap.Height); } } } ================================================ FILE: WzComparerR2/CharaSimControl/ButtonState.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace WzComparerR2.CharaSimControl { public enum ButtonState { Normal = 0, Pressed, MouseOver, Disabled } } ================================================ FILE: WzComparerR2/CharaSimControl/CharaSimControlGroup.cs ================================================ using System; using System.Collections.Generic; using System.Windows.Forms; using System.Drawing; using System.Text; using WzComparerR2.Common; using WzComparerR2.CharaSim; using WzComparerR2.Controls; namespace WzComparerR2.CharaSimControl { public class CharaSimControlGroup { public CharaSimControlGroup() { tooltip = new AfrmTooltip(); tooltip.TopMost = true; } private AfrmTooltip tooltip; private AfrmItem frmItem; private AfrmStat frmStat; private AfrmEquip frmEquip; private Character character; private StringLinker stringLinker; public AfrmItem UIItem { get { if (frmItem == null) { frmItem = new AfrmItem(); frmItem.KeyDown += new KeyEventHandler(afrm_KeyDown); frmItem.MouseDown += new MouseEventHandler(frmItem_MouseDown); frmItem.DragOver += new DragEventHandler(frmItem_DragOver); frmItem.DragDrop += new DragEventHandler(frmItem_DragDrop); frmItem.ItemMouseMove += new ItemMouseEventHandler(frmItem_ItemMouseMove); frmItem.ItemMouseLeave += new EventHandler(frmItem_ItemMouseLeave); frmItem.Character = this.character; } return frmItem; } } public AfrmStat UIStat { get { if (frmStat == null) { frmStat = new AfrmStat(); frmStat.KeyDown += new KeyEventHandler(afrm_KeyDown); frmStat.Character = this.character; } return frmStat; } } public AfrmEquip UIEquip { get { if (frmEquip == null) { frmEquip = new AfrmEquip(); frmEquip.KeyDown += new KeyEventHandler(afrm_KeyDown); frmEquip.MouseDown += new MouseEventHandler(frmEquip_MouseDown); frmEquip.DragOver += new DragEventHandler(frmEquip_DragOver); frmEquip.DragDrop += new DragEventHandler(frmEquip_DragDrop); frmEquip.Character = this.character; } return frmEquip; } } public Character Character { get { return character; } set { this.character = value; this.tooltip.Character = value; if (frmEquip != null) this.frmEquip.Character = value; if (frmStat != null) this.frmStat.Character = value; if (frmEquip != null) this.frmEquip.Character = value; } } public StringLinker StringLinker { get { return stringLinker; } set { this.stringLinker = value; this.tooltip.StringLinker = value; } } private void afrm_KeyDown(object sender, KeyEventArgs e) { Form frm = sender as Form; if (frm == null) return; switch (e.KeyCode) { case Keys.Escape: frm.Hide(); break; case Keys.F1: break; case Keys.Up: frm.Top -= 1; break; case Keys.Down: frm.Top += 1; break; case Keys.Left: frm.Left -= 1; break; case Keys.Right: frm.Left += 1; break; } } private void frmItem_MouseDown(object sender, MouseEventArgs e) { ItemBase dragItem = frmItem.GetItemByPoint(e.Location); if (dragItem != null) { if (Control.ModifierKeys == Keys.Control) { int originIdx = Array.IndexOf(frmItem.SelectedTab.Items, dragItem); if (originIdx > -1) { frmItem.SelectedTab.Items[originIdx] = null; frmItem.Refresh(); } } else { frmItem.DoDragDrop(dragItem, DragDropEffects.Move); tooltip.Visible = false; } } } private ItemBase getDragDataItem(IDataObject data) { ItemBase dragItem; if (data != null && ((dragItem = data.GetData(typeof(Item)) as Item) != null || (dragItem = data.GetData(typeof(Gear)) as Gear) != null)) { return dragItem; } return null; } private void frmItem_DragOver(object sender, DragEventArgs e) { ItemBase dragItem; int idx; if ((e.AllowedEffect & DragDropEffects.Move) != 0 && (idx = frmItem.GetItemIndexByPoint(frmItem.PointToClient(new Point(e.X, e.Y)))) != -1 && (dragItem = getDragDataItem(e.Data)) != null) { e.Effect = DragDropEffects.Move; } else { e.Effect = DragDropEffects.None; } } private void frmItem_DragDrop(object sender, DragEventArgs e) { if ((e.Effect & (DragDropEffects.Move)) == 0) { return; } ItemBase dragItem; int dropIdx, originIdx; if ((dragItem = getDragDataItem(e.Data)) == null || (originIdx = Array.IndexOf(frmItem.SelectedTab.Items, dragItem)) == -1) { return; } dropIdx = frmItem.GetItemIndexByPoint(frmItem.PointToClient(new Point(e.X, e.Y))); if (dropIdx == -1) //移除装备 { frmItem.SelectedTab.Items[originIdx] = null; } else if (originIdx != dropIdx) { frmItem.SelectedTab.Items[originIdx] = frmItem.SelectedTab.Items[dropIdx]; frmItem.SelectedTab.Items[dropIdx] = dragItem; } else { return; } frmItem.Refresh(); } private void frmItem_ItemMouseMove(object sender, ItemMouseEventArgs e) { if (e.Item == null) { tooltip.Visible = false; return; } if (e.Item != tooltip.TargetItem) { tooltip.TargetItem = e.Item; tooltip.Refresh(); } Point pos = frmItem.PointToScreen(e.Location); pos.Offset(5, 5); tooltip.Location = pos; tooltip.Visible = true; tooltip.BringToFront(); } private void frmItem_ItemMouseLeave(object sender, EventArgs e) { tooltip.Visible = false; } private void frmEquip_MouseDown(object sender, MouseEventArgs e) { //throw new NotImplementedException(); } private void frmEquip_DragOver(object sender, DragEventArgs e) { //throw new NotImplementedException(); } private void frmEquip_DragDrop(object sender, DragEventArgs e) { //throw new NotImplementedException(); } } } ================================================ FILE: WzComparerR2/CharaSimControl/DamageSkinTooltipRender.cs ================================================ using System; using System.Drawing; using System.Linq; using WzComparerR2.CharaSim; using Resource = CharaSimResource.Resource; namespace WzComparerR2.CharaSimControl { public class DamageSkinTooltipRender : TooltipRender { public DamageSkinTooltipRender() { } private DamageSkin damageSkin; public DamageSkin DamageSkin { get { return damageSkin; } set { damageSkin = value; } } public override object TargetItem { get { return this.damageSkin; } set { this.damageSkin = value as DamageSkin; } } public bool UseMiniSize { get; set; } public bool AlwaysUseMseaFormat { get; set; } public bool DisplayUnitOnSingleLine { get; set; } public long DamageSkinNumber { get; set; } public override Bitmap Render() { if (this.damageSkin == null) { return null; } Bitmap customSampleNonCritical = GetCustomSample(DamageSkinNumber, UseMiniSize, false); Bitmap customSampleCritical = GetCustomSample(DamageSkinNumber, UseMiniSize, true); Bitmap extraBitmap = GetExtraEffect(); Bitmap unitBitmap = null; int previewWidth = Math.Max(customSampleNonCritical.Width, customSampleCritical.Width); int previewHeight = customSampleNonCritical.Height + customSampleCritical.Height; if (extraBitmap != null) { previewWidth = Math.Max(previewWidth, extraBitmap.Width); previewHeight += extraBitmap.Height; if (DisplayUnitOnSingleLine) { unitBitmap = GetUnit(); if (unitBitmap != null) { previewWidth = Math.Max(previewWidth, unitBitmap.Width); previewHeight += unitBitmap.Height; } } } int picH = 10; Bitmap tooltip = new Bitmap(previewWidth + 30, previewHeight + 30); Graphics g = Graphics.FromImage(tooltip); GearGraphics.DrawNewTooltipBack(g, 0, 0, tooltip.Width, tooltip.Height); if (this.ShowObjectID) { GearGraphics.DrawGearDetailNumber(g, 3, 3, $"{this.damageSkin.DamageSkinID.ToString()}", true); } g.DrawImage(customSampleNonCritical, 10, picH, new Rectangle(0, 0, customSampleNonCritical.Width, customSampleNonCritical.Height), GraphicsUnit.Pixel); picH += customSampleNonCritical.Height + 5; g.DrawImage(customSampleCritical, 10, picH, new Rectangle(0, 0, customSampleCritical.Width, customSampleCritical.Height), GraphicsUnit.Pixel); picH += customSampleCritical.Height + 5; if (unitBitmap != null) { g.DrawImage(unitBitmap, 10, picH, new Rectangle(0, 0, unitBitmap.Width, unitBitmap.Height), GraphicsUnit.Pixel); picH += unitBitmap.Height + 5; } if (extraBitmap != null) { g.DrawImage(extraBitmap, 10, picH, new Rectangle(0, 0, extraBitmap.Width, extraBitmap.Height), GraphicsUnit.Pixel); } customSampleNonCritical.Dispose(); customSampleCritical.Dispose(); g.Dispose(); return tooltip; } public Bitmap GetCustomSample(long inputNumber, bool useMiniSize, bool isCritical) { string numberStr = ""; if (DisplayUnitOnSingleLine) { numberStr = inputNumber.ToString(); } else { switch (damageSkin.CustomType) { case "hangul": // CJK Detailed numberStr = ItemStringHelper.ToChineseNumberExpr(inputNumber, true); break; case "hangulUnit": // CJK numberStr = ItemStringHelper.ToChineseNumberExpr(inputNumber); break; case "glUnit": // GMS numberStr = ItemStringHelper.ToThousandsNumberExpr(inputNumber, this.AlwaysUseMseaFormat); break; case "glUnit2": // MSEA numberStr = ItemStringHelper.ToThousandsNumberExpr(inputNumber, true); break; default: if (this.DamageSkin.MiniUnit.Count > 0) // Default to CJK format when units are available { numberStr = ItemStringHelper.ToChineseNumberExpr(inputNumber); } else { numberStr = inputNumber.ToString(); } break; } } Bitmap criticalSign = null; if (this.damageSkin.BigCriticalDigit.ContainsKey("effect3")) { criticalSign = this.damageSkin.BigCriticalDigit["effect3"].Bitmap; } int totalWidth = 0; int maxHeight = 0; int digitSpacing = isCritical ? (useMiniSize ? this.damageSkin.MiniCriticalDigitSpacing : this.damageSkin.BigCriticalDigitSpacing) : (useMiniSize ? this.damageSkin.MiniDigitSpacing : this.damageSkin.BigDigitSpacing); int unitSpacing = isCritical ? (useMiniSize ? this.damageSkin.MiniCriticalUnitSpacing : this.damageSkin.BigCriticalUnitSpacing) : (useMiniSize ? this.damageSkin.MiniUnitSpacing : this.damageSkin.BigUnitSpacing); if (isCritical && criticalSign != null) { totalWidth += criticalSign.Width + unitSpacing; maxHeight = Math.Max(maxHeight, criticalSign.Height); } // Calculate total width and max height foreach (char c in numberStr) { string character = c.ToString(); switch (character) { case "0": case "1": case "2": case "3": case "4": case "5": case "6": case "7": case "8": case "9": totalWidth += isCritical ? (useMiniSize ? this.damageSkin.MiniCriticalDigit[character].Bitmap.Width : this.damageSkin.BigCriticalDigit[character].Bitmap.Width) : (useMiniSize ? this.damageSkin.MiniDigit[character].Bitmap.Width : this.damageSkin.BigDigit[character].Bitmap.Width); totalWidth += digitSpacing; maxHeight = Math.Max(maxHeight, isCritical ? (useMiniSize ? this.damageSkin.MiniCriticalDigit[character].Bitmap.Height : this.damageSkin.BigCriticalDigit[character].Bitmap.Height) : (useMiniSize ? this.damageSkin.MiniDigit[character].Bitmap.Height : this.damageSkin.BigDigit[character].Bitmap.Height)); break; case "十": case ".": if (this.damageSkin.BigUnit.ContainsKey("0")) { totalWidth += isCritical ? (useMiniSize ? this.damageSkin.MiniCriticalUnit["0"].Bitmap.Width : this.damageSkin.BigCriticalUnit["0"].Bitmap.Width) : (useMiniSize ? this.damageSkin.MiniUnit["0"].Bitmap.Width : this.damageSkin.BigUnit["0"].Bitmap.Width); totalWidth += unitSpacing; maxHeight = Math.Max(maxHeight, isCritical ? (useMiniSize ? this.damageSkin.MiniCriticalUnit["0"].Bitmap.Height : this.damageSkin.BigCriticalUnit["0"].Bitmap.Height) : (useMiniSize ? this.damageSkin.MiniUnit["0"].Bitmap.Height : this.damageSkin.BigUnit["0"].Bitmap.Height)); } break; case "百": case "K": if (this.damageSkin.BigUnit.ContainsKey("1")) { totalWidth += isCritical ? (useMiniSize ? this.damageSkin.MiniCriticalUnit["1"].Bitmap.Width : this.damageSkin.BigCriticalUnit["1"].Bitmap.Width) : (useMiniSize ? this.damageSkin.MiniUnit["1"].Bitmap.Width : this.damageSkin.BigUnit["1"].Bitmap.Width); totalWidth += unitSpacing; maxHeight = Math.Max(maxHeight, isCritical ? (useMiniSize ? this.damageSkin.MiniCriticalUnit["1"].Bitmap.Height : this.damageSkin.BigCriticalUnit["1"].Bitmap.Height) : (useMiniSize ? this.damageSkin.MiniUnit["1"].Bitmap.Height : this.damageSkin.BigUnit["1"].Bitmap.Height)); } break; case "千": case "M": if (this.damageSkin.BigUnit.ContainsKey("2")) { totalWidth += isCritical ? (useMiniSize ? this.damageSkin.MiniCriticalUnit["2"].Bitmap.Width : this.damageSkin.BigCriticalUnit["2"].Bitmap.Width) : (useMiniSize ? this.damageSkin.MiniUnit["2"].Bitmap.Width : this.damageSkin.BigUnit["2"].Bitmap.Width); totalWidth += unitSpacing; maxHeight = Math.Max(maxHeight, isCritical ? (useMiniSize ? this.damageSkin.MiniCriticalUnit["2"].Bitmap.Height : this.damageSkin.BigCriticalUnit["2"].Bitmap.Height) : (useMiniSize ? this.damageSkin.MiniUnit["2"].Bitmap.Height : this.damageSkin.BigUnit["2"].Bitmap.Height)); } break; case "万": case "B": if (this.damageSkin.BigUnit.ContainsKey("3")) { totalWidth += isCritical ? (useMiniSize ? this.damageSkin.MiniCriticalUnit["3"].Bitmap.Width : this.damageSkin.BigCriticalUnit["3"].Bitmap.Width) : (useMiniSize ? this.damageSkin.MiniUnit["3"].Bitmap.Width : this.damageSkin.BigUnit["3"].Bitmap.Width); totalWidth += unitSpacing; maxHeight = Math.Max(maxHeight, isCritical ? (useMiniSize ? this.damageSkin.MiniCriticalUnit["3"].Bitmap.Height : this.damageSkin.BigCriticalUnit["3"].Bitmap.Height) : (useMiniSize ? this.damageSkin.MiniUnit["3"].Bitmap.Height : this.damageSkin.BigUnit["3"].Bitmap.Height)); } break; case "亿": case "T": if (this.damageSkin.BigUnit.ContainsKey("4")) { totalWidth += isCritical ? (useMiniSize ? this.damageSkin.MiniCriticalUnit["4"].Bitmap.Width : this.damageSkin.BigCriticalUnit["4"].Bitmap.Width) : (useMiniSize ? this.damageSkin.MiniUnit["4"].Bitmap.Width : this.damageSkin.BigUnit["4"].Bitmap.Width); totalWidth += unitSpacing; maxHeight = Math.Max(maxHeight, isCritical ? (useMiniSize ? this.damageSkin.MiniCriticalUnit["4"].Bitmap.Height : this.damageSkin.BigCriticalUnit["4"].Bitmap.Height) : (useMiniSize ? this.damageSkin.MiniUnit["4"].Bitmap.Height : this.damageSkin.BigUnit["4"].Bitmap.Height)); } break; case "兆": case "Q": if (this.damageSkin.BigUnit.ContainsKey("5")) { totalWidth += isCritical ? (useMiniSize ? this.damageSkin.MiniCriticalUnit["5"].Bitmap.Width : this.damageSkin.BigCriticalUnit["5"].Bitmap.Width) : (useMiniSize ? this.damageSkin.MiniUnit["5"].Bitmap.Width : this.damageSkin.BigUnit["5"].Bitmap.Width); totalWidth += unitSpacing; maxHeight = Math.Max(maxHeight, isCritical ? (useMiniSize ? this.damageSkin.MiniCriticalUnit["5"].Bitmap.Height : this.damageSkin.BigCriticalUnit["5"].Bitmap.Height) : (useMiniSize ? this.damageSkin.MiniUnit["5"].Bitmap.Height : this.damageSkin.BigUnit["5"].Bitmap.Height)); } break; case "京": if (this.damageSkin.BigUnit.ContainsKey("6")) { totalWidth += isCritical ? (useMiniSize ? this.damageSkin.MiniCriticalUnit["6"].Bitmap.Width : this.damageSkin.BigCriticalUnit["6"].Bitmap.Width) : (useMiniSize ? this.damageSkin.MiniUnit["6"].Bitmap.Width : this.damageSkin.BigUnit["6"].Bitmap.Width); totalWidth += unitSpacing; maxHeight = Math.Max(maxHeight, isCritical ? (useMiniSize ? this.damageSkin.MiniCriticalUnit["6"].Bitmap.Height : this.damageSkin.BigCriticalUnit["6"].Bitmap.Height) : (useMiniSize ? this.damageSkin.MiniUnit["6"].Bitmap.Height : this.damageSkin.BigUnit["6"].Bitmap.Height)); } break; } } Bitmap finalBitmap = new Bitmap(totalWidth, maxHeight); using (Graphics g = Graphics.FromImage(finalBitmap)) { g.Clear(Color.Transparent); int offsetX = 0; if (isCritical && criticalSign != null) { g.DrawImage(criticalSign, offsetX, 0); offsetX += criticalSign.Width; } foreach (char c in numberStr) { string character = c.ToString(); Bitmap charBitmap = null; switch (character) { case "0": case "1": case "2": case "3": case "4": case "5": case "6": case "7": case "8": case "9": charBitmap = isCritical ? (useMiniSize ? this.damageSkin.MiniCriticalDigit[character].Bitmap : this.damageSkin.BigCriticalDigit[character].Bitmap) : (useMiniSize ? this.damageSkin.MiniDigit[character].Bitmap : this.damageSkin.BigDigit[character].Bitmap); g.DrawImage(charBitmap, offsetX, maxHeight - charBitmap.Height); offsetX += charBitmap.Width + digitSpacing; break; case "十": case ".": if (this.damageSkin.BigUnit.ContainsKey("0")) { charBitmap = isCritical ? (useMiniSize ? this.damageSkin.MiniCriticalUnit["0"].Bitmap : this.damageSkin.BigCriticalUnit["0"].Bitmap) : (useMiniSize ? this.damageSkin.MiniUnit["0"].Bitmap : this.damageSkin.BigUnit["0"].Bitmap); g.DrawImage(charBitmap, offsetX, maxHeight - charBitmap.Height); offsetX += charBitmap.Width + unitSpacing; } break; case "百": case "K": if (this.damageSkin.BigUnit.ContainsKey("1")) { charBitmap = isCritical ? (useMiniSize ? this.damageSkin.MiniCriticalUnit["1"].Bitmap : this.damageSkin.BigCriticalUnit["1"].Bitmap) : (useMiniSize ? this.damageSkin.MiniUnit["1"].Bitmap : this.damageSkin.BigUnit["1"].Bitmap); g.DrawImage(charBitmap, offsetX, maxHeight - charBitmap.Height); offsetX += charBitmap.Width + unitSpacing; } break; case "千": case "M": if (this.damageSkin.BigUnit.ContainsKey("2")) { charBitmap = isCritical ? (useMiniSize ? this.damageSkin.MiniCriticalUnit["2"].Bitmap : this.damageSkin.BigCriticalUnit["2"].Bitmap) : (useMiniSize ? this.damageSkin.MiniUnit["2"].Bitmap : this.damageSkin.BigUnit["2"].Bitmap); g.DrawImage(charBitmap, offsetX, maxHeight - charBitmap.Height); offsetX += charBitmap.Width + unitSpacing; } break; case "万": case "B": if (this.damageSkin.BigUnit.ContainsKey("3")) { charBitmap = isCritical ? (useMiniSize ? this.damageSkin.MiniCriticalUnit["3"].Bitmap : this.damageSkin.BigCriticalUnit["3"].Bitmap) : (useMiniSize ? this.damageSkin.MiniUnit["3"].Bitmap : this.damageSkin.BigUnit["3"].Bitmap); g.DrawImage(charBitmap, offsetX, maxHeight - charBitmap.Height); offsetX += charBitmap.Width + unitSpacing; } break; case "亿": case "T": if (this.damageSkin.BigUnit.ContainsKey("4")) { charBitmap = isCritical ? (useMiniSize ? this.damageSkin.MiniCriticalUnit["4"].Bitmap : this.damageSkin.BigCriticalUnit["4"].Bitmap) : (useMiniSize ? this.damageSkin.MiniUnit["4"].Bitmap : this.damageSkin.BigUnit["4"].Bitmap); g.DrawImage(charBitmap, offsetX, maxHeight - charBitmap.Height); offsetX += charBitmap.Width + unitSpacing; } break; case "兆": case "Q": if (this.damageSkin.BigUnit.ContainsKey("5")) { charBitmap = isCritical ? (useMiniSize ? this.damageSkin.MiniCriticalUnit["5"].Bitmap : this.damageSkin.BigCriticalUnit["5"].Bitmap) : (useMiniSize ? this.damageSkin.MiniUnit["5"].Bitmap : this.damageSkin.BigUnit["5"].Bitmap); g.DrawImage(charBitmap, offsetX, maxHeight - charBitmap.Height); offsetX += charBitmap.Width + unitSpacing; } break; case "京": if (this.damageSkin.BigUnit.ContainsKey("6")) { charBitmap = isCritical ? (useMiniSize ? this.damageSkin.MiniCriticalUnit["6"].Bitmap : this.damageSkin.BigCriticalUnit["6"].Bitmap) : (useMiniSize ? this.damageSkin.MiniUnit["6"].Bitmap : this.damageSkin.BigUnit["6"].Bitmap); g.DrawImage(charBitmap, offsetX, maxHeight - charBitmap.Height); offsetX += charBitmap.Width + unitSpacing; } break; } } } return finalBitmap; } public Bitmap GetUnit() { Bitmap unitBitmap = null; int width = 0; int height = 0; if (damageSkin.BigUnit.Count > 0) { if (UseMiniSize) { foreach (var unit in damageSkin.MiniUnit.Values) { width += unit.Bitmap.Width; height = Math.Max(height, unit.Bitmap.Height); width += this.damageSkin.MiniUnitSpacing; } foreach (var unit in damageSkin.MiniCriticalUnit.Values) { width += unit.Bitmap.Width; height = Math.Max(height, unit.Bitmap.Height); width += this.damageSkin.MiniCriticalUnitSpacing; } unitBitmap = new Bitmap(width, height); using (Graphics g = Graphics.FromImage(unitBitmap)) { g.Clear(Color.Transparent); int offsetX = 0; foreach (var unit in damageSkin.MiniUnit.Values) { g.DrawImage(unit.Bitmap, offsetX, height - unit.Bitmap.Height); offsetX += unit.Bitmap.Width; offsetX += this.damageSkin.MiniUnitSpacing; } foreach (var unit in damageSkin.MiniCriticalUnit.Values) { g.DrawImage(unit.Bitmap, offsetX, height - unit.Bitmap.Height); offsetX += unit.Bitmap.Width; offsetX += this.damageSkin.MiniCriticalUnitSpacing; } } } else { foreach (var unit in damageSkin.BigUnit.Values) { width += unit.Bitmap.Width; height = Math.Max(height, unit.Bitmap.Height); width += this.damageSkin.BigUnitSpacing; } foreach (var unit in damageSkin.BigCriticalUnit.Values) { width += unit.Bitmap.Width; height = Math.Max(height, unit.Bitmap.Height); width += this.damageSkin.BigCriticalUnitSpacing; } unitBitmap = new Bitmap(width, height); using (Graphics g = Graphics.FromImage(unitBitmap)) { g.Clear(Color.Transparent); int offsetX = 0; foreach (var unit in damageSkin.BigUnit.Values) { g.DrawImage(unit.Bitmap, offsetX, height - unit.Bitmap.Height); offsetX += unit.Bitmap.Width; offsetX += this.damageSkin.BigUnitSpacing; } foreach (var unit in damageSkin.BigCriticalUnit.Values) { g.DrawImage(unit.Bitmap, offsetX, height - unit.Bitmap.Height); offsetX += unit.Bitmap.Width; offsetX += this.damageSkin.BigCriticalUnitSpacing; } } } } return unitBitmap; } public Bitmap GetExtraEffect() { Bitmap[] originalBitmaps = new Bitmap[5] { this.damageSkin.MiniDigit.ContainsKey("Miss") ? this.damageSkin.MiniDigit?["Miss"].Bitmap : null, this.damageSkin.MiniDigit.ContainsKey("guard") ? this.damageSkin.MiniDigit?["guard"].Bitmap : null, this.damageSkin.MiniDigit.ContainsKey("resist") ? this.damageSkin.MiniDigit?["resist"].Bitmap : null, this.damageSkin.MiniDigit.ContainsKey("shot") ? this.damageSkin.MiniDigit?["shot"].Bitmap : null, this.damageSkin.MiniDigit.ContainsKey("counter") ? this.damageSkin.MiniDigit?["counter"].Bitmap : null }; int width = 0; int height = 0; foreach (var bo in originalBitmaps) { if (bo == null) continue; width += bo.Width; height = Math.Max(height, bo.Height); } Bitmap bitmap = new Bitmap(width, height); using (Graphics g = Graphics.FromImage(bitmap)) { g.Clear(Color.Transparent); int offsetX = 0; for (int j = 0; j < originalBitmaps.Count(); j++) { if (originalBitmaps[j] == null) continue; g.DrawImage(originalBitmaps[j], offsetX, height - originalBitmaps[j].Height); offsetX += originalBitmaps[j].Width; } } return bitmap; } } } ================================================ FILE: WzComparerR2/CharaSimControl/FamiliarTooltipRender.cs ================================================ using System.Collections.Generic; using System.Drawing; using WzComparerR2.CharaSim; using WzComparerR2.Common; using WzComparerR2.PluginBase; using static WzComparerR2.CharaSimControl.RenderHelper; using Resource = CharaSimResource.Resource; namespace WzComparerR2.CharaSimControl { public class FamiliarTooltipRender : TooltipRender { public FamiliarTooltipRender() { } private Familiar familiar; public Familiar Familiar { get { return familiar; } set { familiar = value; } } public override object TargetItem { get { return this.familiar; } set { this.familiar = value as Familiar; } } public int? ItemID { get; set; } public int FamiliarTier { get; set; } public bool AllowOutOfBounds { get; set; } public bool UseAssembleUI { get; set; } public override Bitmap Render() { if (this.familiar == null) { return null; } return UseAssembleUI ? GeneratePostAssembleFamiliarCard() : GeneratePreAssembleFamiliarCard(); } private Bitmap GeneratePreAssembleFamiliarCard() { Bitmap baseTooltip = Resource.UIFamiliar_img_familiarCard_backgrnd; // Get Mob image and name Mob mob = Mob.CreateFromNode(PluginManager.FindWz($@"Mob\{familiar.MobID.ToString().PadLeft(7, '0')}.img"), PluginManager.FindWz); Point alignOrigin = new Point(161, 200); Point mobOrigin = new Point(0, 0); int mobXoffset = 0; int mobYoffset = 0; int tDelta = 0; int bDelta = 0; int lDelta = 0; int rDelta = 0; if (familiar.FamiliarCover.Bitmap != null) { mobOrigin = familiar.FamiliarCover.Origin; } else { mobOrigin = mob.Default.Origin; } Bitmap mobImg = Crop(familiar.FamiliarCover.Bitmap ?? mob.Default.Bitmap, alignOrigin, mobOrigin, out mobXoffset, out mobYoffset, out tDelta, out bDelta, out lDelta, out rDelta); Bitmap tooltip = new Bitmap(baseTooltip.Width + lDelta + rDelta, baseTooltip.Height + tDelta + bDelta); using (Graphics g = Graphics.FromImage(tooltip)) { g.Clear(Color.Transparent); g.DrawImage(baseTooltip, lDelta, tDelta, new Rectangle(0, 0, baseTooltip.Width, baseTooltip.Height), GraphicsUnit.Pixel); // Draw Familiar Card basic background g.DrawImage(Resource.UIFamiliar_img_familiarCard_base, 45 + lDelta, 37 + tDelta, new Rectangle(0, 0, Resource.UIFamiliar_img_familiarCard_base.Width, Resource.UIFamiliar_img_familiarCard_base.Height), GraphicsUnit.Pixel); g.DrawImage(Resource.UIFamiliar_img_familiarCard_name, 31 + lDelta, 222 + tDelta, new Rectangle(0, 0, Resource.UIFamiliar_img_familiarCard_name.Width, Resource.UIFamiliar_img_familiarCard_name.Height), GraphicsUnit.Pixel); g.DrawImage(mobImg, mobXoffset + lDelta, mobYoffset + tDelta, new Rectangle(0, 0, mobImg.Width, mobImg.Height), GraphicsUnit.Pixel); if (this.FamiliarTier == 4) { // draw Legendary bezel g.DrawImage(Resource.UIFamiliar_img_familiarCard_legendary, 35 + lDelta, 24 + tDelta, new Rectangle(0, 0, Resource.UIFamiliar_img_familiarCard_legendary.Width, Resource.UIFamiliar_img_familiarCard_legendary.Height), GraphicsUnit.Pixel); } g.DrawImage(Resource.UIFamiliar_img_jewel_backgrnd, 25 + lDelta, 21 + tDelta, new Rectangle(0, 0, Resource.UIFamiliar_img_jewel_backgrnd.Width, Resource.UIFamiliar_img_jewel_backgrnd.Height), GraphicsUnit.Pixel); switch (this.FamiliarTier) { default: case 0: g.DrawImage(Resource.UIFamiliar_img_jewel_normal_5, 30 + lDelta, 27 + tDelta, new Rectangle(0, 0, Resource.UIFamiliar_img_jewel_normal_5.Width, Resource.UIFamiliar_img_jewel_normal_5.Height), GraphicsUnit.Pixel); g.DrawImage(Resource.UIFamiliar_img_jewel_normal_0, 38 + lDelta, 25 + tDelta, new Rectangle(0, 0, Resource.UIFamiliar_img_jewel_normal_0.Width, Resource.UIFamiliar_img_jewel_normal_0.Height), GraphicsUnit.Pixel); break; case 1: g.DrawImage(Resource.UIFamiliar_img_jewel_rare_5, 30 + lDelta, 27 + tDelta, new Rectangle(0, 0, Resource.UIFamiliar_img_jewel_rare_5.Width, Resource.UIFamiliar_img_jewel_rare_5.Height), GraphicsUnit.Pixel); g.DrawImage(Resource.UIFamiliar_img_jewel_rare_0, 38 + lDelta, 25 + tDelta, new Rectangle(0, 0, Resource.UIFamiliar_img_jewel_rare_0.Width, Resource.UIFamiliar_img_jewel_rare_0.Height), GraphicsUnit.Pixel); break; case 2: g.DrawImage(Resource.UIFamiliar_img_jewel_epic_5, 30 + lDelta, 27 + tDelta, new Rectangle(0, 0, Resource.UIFamiliar_img_jewel_epic_5.Width, Resource.UIFamiliar_img_jewel_epic_5.Height), GraphicsUnit.Pixel); g.DrawImage(Resource.UIFamiliar_img_jewel_epic_0, 38 + lDelta, 25 + tDelta, new Rectangle(0, 0, Resource.UIFamiliar_img_jewel_epic_0.Width, Resource.UIFamiliar_img_jewel_epic_0.Height), GraphicsUnit.Pixel); break; case 3: g.DrawImage(Resource.UIFamiliar_img_jewel_unique_5, 30 + lDelta, 27 + tDelta, new Rectangle(0, 0, Resource.UIFamiliar_img_jewel_unique_5.Width, Resource.UIFamiliar_img_jewel_unique_5.Height), GraphicsUnit.Pixel); g.DrawImage(Resource.UIFamiliar_img_jewel_unique_0, 38 + lDelta, 25 + tDelta, new Rectangle(0, 0, Resource.UIFamiliar_img_jewel_unique_0.Width, Resource.UIFamiliar_img_jewel_unique_0.Height), GraphicsUnit.Pixel); break; case 4: g.DrawImage(Resource.UIFamiliar_img_jewel_legendary_5, 30 + lDelta, 27 + tDelta, new Rectangle(0, 0, Resource.UIFamiliar_img_jewel_legendary_5.Width, Resource.UIFamiliar_img_jewel_legendary_5.Height), GraphicsUnit.Pixel); g.DrawImage(Resource.UIFamiliar_img_jewel_legendary_0, 38 + lDelta, 25 + tDelta, new Rectangle(0, 0, Resource.UIFamiliar_img_jewel_legendary_0.Width, Resource.UIFamiliar_img_jewel_legendary_0.Height), GraphicsUnit.Pixel); break; } // Pre-Drawing List titleBlocks = new List(); string mobName = GetMobName(mob.ID); var block = PrepareText(g, mobName ?? "(null)", GearGraphics.EpicGearDetailFont, Brushes.White, 0, 0); titleBlocks.Add(block); Rectangle titleRect = Measure(titleBlocks); int titleXoffset = Resource.UIFamiliar_img_familiarCard_name.Width >= Resource.UIFamiliar_img_familiarCard_name.Width ? (Resource.UIFamiliar_img_familiarCard_name.Width - titleRect.Width) / 2 : 0; int titleYoffset = (Resource.UIFamiliar_img_familiarCard_name.Height - titleRect.Height) / 2; foreach (var item in titleBlocks) { DrawText(g, item, new Point(31 + lDelta + titleXoffset, 222 + tDelta + titleYoffset)); } // Draw Skill string skillName = GetFamiliarSkillName(this.familiar.SkillID); if (!string.IsNullOrEmpty(skillName)) { List skillBlocks = new List(); block = PrepareText(g, skillName, GearGraphics.EpicGearDetailFont, Brushes.White, 0, 0); skillBlocks.Add(block); Rectangle skillRect = Measure(skillBlocks); int skillXoffset = Resource.UIFamiliar_img_familiarCard_name.Width >= Resource.UIFamiliar_img_familiarCard_name.Width ? (Resource.UIFamiliar_img_familiarCard_name.Width - skillRect.Width) / 2 : 0; int skillYoffset = Resource.UIFamiliar_img_familiarCard_name.Height + 1; foreach (var item in skillBlocks) { DrawText(g, item, new Point(31 + lDelta + skillXoffset, 222 + tDelta + skillYoffset)); } } // Layout if (this.ShowObjectID) { GearGraphics.DrawGearDetailNumber(g, 24 + lDelta, 24 + tDelta, this.ItemID != null ? $"{((int)this.ItemID).ToString("d8")}" : $"{this.familiar.FamiliarID.ToString()}", true); } } return tooltip; } private Bitmap GeneratePostAssembleFamiliarCard() { // To be implemented return GeneratePreAssembleFamiliarCard(); } private string GetMobName(int mobID) { StringResult sr; if (this.StringLinker == null || !this.StringLinker.StringMob.TryGetValue(mobID, out sr)) { return null; } else { return sr.Name; } } private string GetFamiliarSkillName(int skillID) { StringResult sr; if (this.StringLinker == null || !this.StringLinker.StringFamiliarSkill.TryGetValue(skillID, out sr)) { return null; } else { return sr.Name; } } private Bitmap Crop(Bitmap sourceBmp, Point alignOrigin, Point mobOrigin, out int xOffset, out int yOffset, out int tDelta, out int bDelta, out int lDelta, out int rDelta) { tDelta = 0; bDelta = 0; lDelta = 0; rDelta = 0; xOffset = 0; yOffset = 0; if (sourceBmp == null) { return null; } xOffset = alignOrigin.X - mobOrigin.X; yOffset = alignOrigin.Y - mobOrigin.Y; if (this.AllowOutOfBounds) { lDelta = mobOrigin.X > alignOrigin.X ? mobOrigin.X - alignOrigin.X : 0; rDelta = sourceBmp.Width - mobOrigin.X > 154 ? sourceBmp.Width - mobOrigin.X - 154 : 0; tDelta = mobOrigin.Y > alignOrigin.Y ? mobOrigin.Y - alignOrigin.Y : 0; bDelta = sourceBmp.Height - mobOrigin.Y > 228 ? sourceBmp.Height - mobOrigin.Y - 228 : 0; return sourceBmp; } int rectangXoffset = 0; int rectangYoffset = 0; int rectangWidth = 0; int rectangHeight = 0; if (mobOrigin.X > 108) // Define Left Border { rectangXoffset = mobOrigin.X - 108; rectangWidth += 108; } else { rectangWidth += mobOrigin.X; } if (sourceBmp.Width - mobOrigin.X > 104) // Define Right Border { rectangWidth += 104; } else { rectangWidth += sourceBmp.Width - mobOrigin.X; } if (mobOrigin.Y > 154) // Define Top Border { rectangYoffset = mobOrigin.Y - 154; rectangHeight += 154; } else { rectangHeight += mobOrigin.Y; } if (sourceBmp.Height - mobOrigin.Y > 18) { rectangHeight += 18; } else { rectangHeight += sourceBmp.Height - mobOrigin.Y; } xOffset = rectangXoffset == 0 ? xOffset : 53; yOffset = rectangYoffset == 0 ? yOffset : 46; return sourceBmp.Clone(new Rectangle(rectangXoffset, rectangYoffset, rectangWidth, rectangHeight), sourceBmp.PixelFormat); } } } ================================================ FILE: WzComparerR2/CharaSimControl/GearGraphics.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Drawing; using System.Drawing.Imaging; using System.Drawing.Drawing2D; using System.Runtime.InteropServices; using CharaSimResource; using WzComparerR2.CharaSim; using TR = System.Windows.Forms.TextRenderer; using TextFormatFlags = System.Windows.Forms.TextFormatFlags; using WzComparerR2.Text; using WzComparerR2.WzLib; namespace WzComparerR2.CharaSimControl { /// /// 提供一系列的静态Graphics工具,用来绘制物品tooltip。 /// public static class GearGraphics { static GearGraphics() { TBrushes = new Dictionary(); TBrushes["n"] = new TextureBrush(Resource.UIToolTip_img_Item_Frame2_n, WrapMode.Tile); TBrushes["ne"] = new TextureBrush(Resource.UIToolTip_img_Item_Frame2_ne, WrapMode.Clamp); TBrushes["e"] = new TextureBrush(Resource.UIToolTip_img_Item_Frame2_e, WrapMode.Tile); TBrushes["se"] = new TextureBrush(Resource.UIToolTip_img_Item_Frame2_se, WrapMode.Clamp); TBrushes["s"] = new TextureBrush(Resource.UIToolTip_img_Item_Frame2_s, WrapMode.Tile); TBrushes["sw"] = new TextureBrush(Resource.UIToolTip_img_Item_Frame2_sw, WrapMode.Clamp); TBrushes["w"] = new TextureBrush(Resource.UIToolTip_img_Item_Frame2_w, WrapMode.Tile); TBrushes["nw"] = new TextureBrush(Resource.UIToolTip_img_Item_Frame2_nw, WrapMode.Clamp); TBrushes["c"] = new TextureBrush(Resource.UIToolTip_img_Item_Frame2_c, WrapMode.Tile); SetFontFamily("宋体"); } public static readonly Dictionary TBrushes; public static readonly Font ItemNameFont = new Font("宋体", 14f, FontStyle.Bold, GraphicsUnit.Pixel); public static readonly Font ItemDetailFont = new Font("宋体", 12f, GraphicsUnit.Pixel); public static readonly Font EpicGearDetailFont = new Font("宋体", 12f, FontStyle.Bold, GraphicsUnit.Pixel); public static readonly Font TahomaFont = new Font("Tahoma", 12f, GraphicsUnit.Pixel); public static readonly Font SetItemPropFont = new Font("宋体", 12f, GraphicsUnit.Pixel); public static readonly Font ItemReqLevelFont = new Font("宋体", 11f, GraphicsUnit.Pixel); public static Font ItemNameFont2 { get; private set; } public static Font ItemDetailFont2 { get; private set; } public static void SetFontFamily(string fontName) { if (ItemNameFont2 != null) { ItemNameFont2.Dispose(); ItemNameFont2 = null; } ItemNameFont2 = new Font(fontName, 14f, FontStyle.Bold, GraphicsUnit.Pixel); if (ItemDetailFont2 != null) { ItemDetailFont2.Dispose(); ItemDetailFont2 = null; } ItemDetailFont2 = new Font(fontName, 12f, GraphicsUnit.Pixel); } public static readonly Color GearBackColor = Color.FromArgb(204, 0, 51, 85); public static readonly Color EpicGearBackColor = Color.FromArgb(170, 68, 0, 0); public static readonly Color GearIconBackColor = Color.FromArgb(238, 187, 204, 221); public static readonly Color EpicGearIconBackColor = Color.FromArgb(221, 204, 187, 187); public static readonly Brush GearBackBrush = new SolidBrush(GearBackColor); public static readonly Brush EpicGearBackBrush = new SolidBrush(EpicGearBackColor); public static readonly Pen GearBackPen = new Pen(GearBackColor); public static readonly Pen EpicGearBackPen = new Pen(EpicGearBackColor); public static readonly Brush GearIconBackBrush = new SolidBrush(GearIconBackColor); public static readonly Brush GearIconBackBrush2 = new SolidBrush(Color.FromArgb(187, 238, 238, 238)); public static readonly Brush EpicGearIconBackBrush = new SolidBrush(EpicGearIconBackColor); public static readonly Brush StatDetailGrayBrush = new SolidBrush(Color.FromArgb(85, 85, 85)); public static readonly Color OrangeBrushColor = Color.FromArgb(255, 153, 0); /// /// 表示物品说明中带有#c标识的橙色字体画刷。 /// public static readonly Brush OrangeBrush = new SolidBrush(OrangeBrushColor); /// /// 表示物品附加属性中橙色字体画刷。 /// public static readonly Brush OrangeBrush2 = new SolidBrush(Color.FromArgb(255, 170, 0)); public static readonly Color OrangeBrush3Color = Color.FromArgb(255, 204, 0); /// /// 表示装备职业额外说明中使用的橙黄色画刷。 /// public static readonly Brush OrangeBrush3 = new SolidBrush(OrangeBrush3Color); /// /// 表示装备属性额外说明中使用的绿色画刷。 /// public static readonly Brush GreenBrush2 = new SolidBrush(Color.FromArgb(204, 255, 0)); public static readonly Color GrayColor2 = Color.FromArgb(153, 153, 153); /// /// 表示装备属性额外说明中使用的卷轴强化数值画刷。 /// public static readonly Color ScrollEnhancementColor = Color.FromArgb(175, 173, 255); public static readonly Brush ScrollEnhancementBrush = new SolidBrush(ScrollEnhancementColor); /// /// 表示用于绘制“攻击力提升”文字的灰色画刷。 /// public static readonly Brush GrayBrush2 = new SolidBrush(GrayColor2); /// /// 表示套装名字的绿色画刷。 /// public static readonly Brush SetItemNameBrush = new SolidBrush(Color.FromArgb(119, 255, 0)); /// /// 表示套装属性不可用的灰色画刷。 /// public static readonly Brush SetItemGrayBrush = new SolidBrush(Color.FromArgb(119, 136, 153)); /// /// 表示效果不可用的红色画刷。 /// public static readonly Brush BlockRedBrush = new SolidBrush(Color.FromArgb(255, 0, 102)); /// /// 表示装备tooltip中金锤子描述文字的颜色画刷。 /// public static readonly Brush GoldHammerBrush = new SolidBrush(Color.FromArgb(255, 238, 204)); /// /// 表示灰色品质的装备名字画刷,额外属性小于0。 /// public static readonly Brush GearNameBrushA = new SolidBrush(Color.FromArgb(187, 187, 187)); /// /// 表示白色品质的装备名字画刷,额外属性为0~5。 /// public static readonly Brush GearNameBrushB = new SolidBrush(Color.FromArgb(255, 255, 255)); /// /// 表示橙色品质的装备名字画刷,额外属性为0~5,并且已经附加卷轴。 /// public static readonly Brush GearNameBrushC = new SolidBrush(Color.FromArgb(255, 136, 17)); private static Color gearBlueColor = Color.FromArgb(85, 170, 255); /// /// 表示蓝色品质的装备名字画刷,额外属性为6~22。 /// public static readonly Brush GearNameBrushD = new SolidBrush(gearBlueColor); private static Color gearPurpleColor = Color.FromArgb(204, 102, 255); /// /// 表示紫色品质的装备名字画刷,额外属性为23~39。 /// public static readonly Brush GearNameBrushE = new SolidBrush(gearPurpleColor); private static Color gearGoldColor = Color.FromArgb(255, 255, 17); /// /// 表示金色品质的装备名字画刷,额外属性为40~54。 /// public static readonly Brush GearNameBrushF = new SolidBrush(gearGoldColor); private static Color gearGreenColor = Color.FromArgb(51, 255, 0); /// /// 表示绿色品质的装备名字画刷,额外属性为55~69。 /// public static readonly Brush GearNameBrushG = new SolidBrush(gearGreenColor); /// /// 表示红色品质的装备名字画刷,额外属性为70以上。 /// public static readonly Brush GearNameBrushH = new SolidBrush(Color.FromArgb(255, 0, 119)); public static readonly Color gearCyanColor = Color.FromArgb(102, 255, 255); /// /// 表示装备属性变化的青色画刷。 /// public static readonly Brush GearPropChangeBrush = new SolidBrush(gearCyanColor); public static readonly Color SkillSummaryOrangeTextColor = Color.FromArgb(255, 204, 0); public static readonly Brush SkillSummaryOrangeTextBrush = new SolidBrush(SkillSummaryOrangeTextColor); public static Brush GetGearNameBrush(int diff, bool up) { if (diff < 0) return GearNameBrushA; if (diff < 6) { if (!up) return GearNameBrushB; else return GearNameBrushC; } if (diff < 23) return GearNameBrushD; if (diff < 40) return GearNameBrushE; if (diff < 55) return GearNameBrushF; if (diff < 70) return GearNameBrushG; return GearNameBrushH; } public static readonly Pen GearItemBorderPenC = new Pen(Color.FromArgb(255, 0, 102)); public static readonly Pen GearItemBorderPenB = new Pen(gearBlueColor); public static readonly Pen GearItemBorderPenA = new Pen(gearPurpleColor); public static readonly Pen GearItemBorderPenS = new Pen(gearGoldColor); public static readonly Pen GearItemBorderPenSS = new Pen(gearGreenColor); public static Pen GetGearItemBorderPen(GearGrade grade) { switch (grade) { case GearGrade.B: return GearItemBorderPenB; case GearGrade.A: return GearItemBorderPenA; case GearGrade.S: return GearItemBorderPenS; case GearGrade.SS: return GearItemBorderPenSS; default: return null; } } public static Brush GetPotentialTextBrush(GearGrade grade) { switch (grade) { default: case GearGrade.B: return GearPropChangeBrush; case GearGrade.A: return GearNameBrushE; case GearGrade.S: return GearNameBrushF; case GearGrade.SS: return GreenBrush2; } } /// /// 在指定区域绘制包含宏代码的字符串。 /// /// 绘图所关联的graphics。 /// 要绘制的string。 /// 要绘制string的字体。 /// 起始的x坐标。 /// 每行终止的x坐标。 /// 起始行的y坐标。 public static void DrawString(Graphics g, string s, Font font, int x, int x1, ref int y, int height, TextAlignment alignment = TextAlignment.Left) { DrawString(g, s, font, null, x, x1, ref y, height, alignment); } public static void DrawString(Graphics g, string s, Font font, IDictionary fontColorTable, int x, int x1, ref int y, int height, TextAlignment alignment = TextAlignment.Left) { if (s == null) return; using (var r = new FormattedTextRenderer()) { r.WordWrapEnabled = false; r.UseGDIRenderer = false; r.FontColorTable = fontColorTable; r.DrawString(g, s, font, x, x1, ref y, height, alignment); } } public static void DrawPlainText(Graphics g, string s, Font font, Color color, int x, int x1, ref int y, int height, TextAlignment alignment = TextAlignment.Left) { if (s == null) return; using (var r = new FormattedTextRenderer()) { r.WordWrapEnabled = false; r.UseGDIRenderer = false; r.DrawPlainText(g, s, font, color, x, x1, ref y, height, alignment); } } public static Bitmap EnlargeBitmap(Bitmap bitmap) { BitmapData data = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb); IntPtr p = data.Scan0; byte[] origin = new byte[bitmap.Width * bitmap.Height * 4]; Marshal.Copy(p, origin, 0, origin.Length); bitmap.UnlockBits(data); byte[] newByte = new byte[origin.Length * 4]; byte[] buffer = new byte[4]; for (int i = 0; i < bitmap.Height; i++) { for (int j = 0; j < bitmap.Width; j++) { Array.Copy(origin, getOffset(j, i, bitmap.Width), buffer, 0, 4); Array.Copy(buffer, 0, newByte, getOffset(2 * j, 2 * i, bitmap.Width * 2), 4); Array.Copy(buffer, 0, newByte, getOffset(2 * j + 1, 2 * i, bitmap.Width * 2), 4); } Array.Copy(newByte, getOffset(0, 2 * i, bitmap.Width * 2), newByte, getOffset(0, 2 * i + 1, bitmap.Width * 2), bitmap.Width * 8); } Bitmap newBitmap = new Bitmap(bitmap.Width * 2, bitmap.Height * 2); data = newBitmap.LockBits(new Rectangle(0, 0, newBitmap.Width, newBitmap.Height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb); Marshal.Copy(newByte, 0, data.Scan0, newByte.Length); newBitmap.UnlockBits(data); return newBitmap; } private static int getOffset(int x, int y, int width, int unit = 4) { return (y * width + x) * unit; } public static Point[] GetBorderPath(int dx, int width, int height) { List pointList = new List(13); pointList.Add(new Point(dx + 1, 0)); pointList.Add(new Point(dx + 1, 1)); pointList.Add(new Point(dx + 0, 1)); pointList.Add(new Point(dx + 0, height - 2)); pointList.Add(new Point(dx + 1, height - 2)); pointList.Add(new Point(dx + 1, height - 1)); pointList.Add(new Point(dx + width - 2, height - 1)); pointList.Add(new Point(dx + width - 2, height - 2)); pointList.Add(new Point(dx + width - 1, height - 2)); pointList.Add(new Point(dx + width - 1, 1)); pointList.Add(new Point(dx + width - 2, 1)); pointList.Add(new Point(dx + width - 2, 0)); pointList.Add(new Point(dx + 1, 0)); return pointList.ToArray(); } public static Point[] GetIconBorderPath(int x, int y) { Point[] pointList = new Point[5]; pointList[0] = new Point(x + 32, y + 31); pointList[1] = new Point(x + 32, y); pointList[2] = new Point(x, y); pointList[3] = new Point(x, y + 32); pointList[4] = new Point(x + 31, y + 32); return pointList; } public static void DrawGearDetailNumber(Graphics g, int x, int y, string num, bool can) { Bitmap bitmap; for (int i = 0; i < num.Length; i++) { switch (num[i]) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': string resourceName = (can ? "ToolTip_Equip_Can_" : "ToolTip_Equip_Cannot_") + num[i]; bitmap = (Bitmap)Resource.ResourceManager.GetObject(resourceName); g.DrawImage(bitmap, x, y); x += bitmap.Width + 1; break; case '-': bitmap = can ? Resource.ToolTip_Equip_Can_none : Resource.ToolTip_Equip_Cannot_none; g.DrawImage(bitmap, x, y + 3); x += bitmap.Width + 1; break; case '%': bitmap = can ? Resource.ToolTip_Equip_Can_percent : Resource.ToolTip_Equip_Cannot_percent; g.DrawImage(bitmap, x + 1, y); x += bitmap.Width + 2; break; } } } public static void DrawGearGrowthNumber(Graphics g, int x, int y, string num, bool can) { Bitmap bitmap; for (int i = 0; i < num.Length; i++) { switch (num[i]) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': string resourceName = (can ? "ToolTip_Equip_GrowthEnabled_" : "ToolTip_Equip_Cannot_") + num[i]; bitmap = (Bitmap)Resource.ResourceManager.GetObject(resourceName); g.DrawImage(bitmap, x, y); x += bitmap.Width + 1; break; case '-': bitmap = can ? Resource.ToolTip_Equip_GrowthDisabled_none : Resource.ToolTip_Equip_GrowthDisabled_none; g.DrawImage(bitmap, x, y); x += bitmap.Width + 1; break; case '%': bitmap = can ? Resource.ToolTip_Equip_GrowthEnabled_percent : Resource.ToolTip_Equip_GrowthEnabled_percent; g.DrawImage(bitmap, x + 7, y - 4); x += bitmap.Width + 1; break; case 'm': bitmap = can ? Resource.ToolTip_Equip_GrowthEnabled_max : Resource.ToolTip_Equip_GrowthEnabled_max; g.DrawImage(bitmap, x, y); x += bitmap.Width + 1; break; } } } public static void DrawNewTooltipBack(Graphics g, int x, int y, int width, int height) { Dictionary res = TBrushes; //测算准线 int[] guideX = new int[4] { 0, res["w"].Image.Width, width - res["e"].Image.Width, width }; int[] guideY = new int[4] { 0, res["n"].Image.Height, height - res["s"].Image.Height, height }; for (int i = 0; i < guideX.Length; i++) guideX[i] += x; for (int i = 0; i < guideY.Length; i++) guideY[i] += y; //绘制四角 FillRect(g, res["nw"], guideX, guideY, 0, 0, 1, 1); FillRect(g, res["ne"], guideX, guideY, 2, 0, 3, 1); FillRect(g, res["sw"], guideX, guideY, 0, 2, 1, 3); FillRect(g, res["se"], guideX, guideY, 2, 2, 3, 3); //填充上下区域 if (guideX[2] > guideX[1]) { FillRect(g, res["n"], guideX, guideY, 1, 0, 2, 1); FillRect(g, res["s"], guideX, guideY, 1, 2, 2, 3); } //填充左右区域 if (guideY[2] > guideY[1]) { FillRect(g, res["w"], guideX, guideY, 0, 1, 1, 2); FillRect(g, res["e"], guideX, guideY, 2, 1, 3, 2); } //填充中心 if (guideX[2] > guideX[1] && guideY[2] > guideY[1]) { FillRect(g, res["c"], guideX, guideY, 1, 1, 2, 2); } } private static void FillRect(Graphics g, TextureBrush brush, int[] guideX, int[] guideY, int x0, int y0, int x1, int y1) { brush.ResetTransform(); brush.TranslateTransform(guideX[x0], guideY[y0]); g.FillRectangle(brush, guideX[x0], guideY[y0], guideX[x1] - guideX[x0], guideY[y1] - guideY[y0]); } public static void DrawNameTag(Graphics g, Wz_Node resNode, string tagName, int picW, ref int picH) { if (g == null || resNode == null) return; //加载资源和文本颜色 var wce = new[] { "w", "c", "e" }.Select(n => { var node = resNode.FindNodeByPath(n); if (node == null) { return new BitmapOrigin(); } return BitmapOrigin.CreateFromNode(node, PluginBase.PluginManager.FindWz); }).ToArray(); Color color = Color.FromArgb(resNode.FindNodeByPath("clr").GetValueEx(-1)); BitmapOrigin ani0 = default; Wz_Node ani0Node = resNode.FindNodeByPath(false, "ani", "0"); if (ani0Node != null) { ani0 = BitmapOrigin.CreateFromNode(ani0Node, PluginBase.PluginManager.FindWz); } //测试y轴大小 int offsetY = wce.Min(bmp => bmp.OpOrigin.Y); int height = wce.Max(bmp => bmp.Rectangle.Bottom); //测试宽度 var font = GearGraphics.ItemDetailFont2; var fmt = StringFormat.GenericTypographic; int nameWidth = string.IsNullOrEmpty(tagName) ? 0 : (int)Math.Ceiling(g.MeasureString(tagName, font, 261, fmt).Width); int center = picW / 2; if (ani0.Bitmap == null) // legacy mode { int left = center - nameWidth / 2; int right = left + nameWidth; //开始绘制背景 picH -= offsetY; if (wce[0].Bitmap != null) { g.DrawImage(wce[0].Bitmap, left - wce[0].Origin.X, picH - wce[0].Origin.Y); } if (wce[1].Bitmap != null) //不用拉伸 用纹理平铺 看运气 { var brush = new TextureBrush(wce[1].Bitmap); Rectangle rect = new Rectangle(left, picH - wce[1].Origin.Y, right - left, brush.Image.Height); brush.TranslateTransform(rect.X, rect.Y); g.FillRectangle(brush, rect); brush.Dispose(); } if (wce[2].Bitmap != null) { g.DrawImage(wce[2].Bitmap, right - wce[2].Origin.X, picH - wce[2].Origin.Y); } //绘制文字 if (!string.IsNullOrEmpty(tagName)) { using var brush = new SolidBrush(color); g.DrawString(tagName, font, brush, left, picH, fmt); } } else // ani mode { bool mixedAniMode = wce[1].Bitmap != null && (wce[1].Bitmap.Width > 1 || wce[1].Bitmap.Height > 1); offsetY = Math.Min(offsetY, ani0.OpOrigin.Y); height = Math.Max(height, ani0.Rectangle.Bottom); int bgWidth = mixedAniMode ? wce[1].Bitmap.Width : nameWidth; int left = center - bgWidth / 2; int right = left + bgWidth; int nameLeft = center - nameWidth / 2; picH -= offsetY; if (mixedAniMode) { // draw legay center // Note: item 1143360 (MILESTONE) does not render well, ignore it. g.DrawImage(wce[1].Bitmap, left - wce[1].Origin.X, picH - wce[1].Origin.Y); // draw ani0 based on bg center position g.DrawImage(ani0.Bitmap, left - wce[1].Origin.X - ani0.Origin.X, picH - wce[1].Origin.Y - ani0.Origin.Y); if (!string.IsNullOrEmpty(tagName)) // draw name { using var brush = new SolidBrush(color); // offsetX with bg for better alignment g.DrawString(tagName, font, brush, nameLeft - wce[1].Origin.X, picH, fmt); } } else { // draw ani0 only g.DrawImage(ani0.Bitmap, left - ani0.Origin.X, picH - ani0.Origin.Y); } } picH += height; } [DllImport("user32.dll")] private static extern IntPtr SendMessage(IntPtr hwnd, UInt32 wMsg, IntPtr wParam, IntPtr lParam); private const int WM_SETREDRAW = 0xB; public static void SetRedraw(System.Windows.Forms.Control control, bool enable) { if (control != null) { SendMessage(control.Handle, WM_SETREDRAW, new IntPtr(enable ? 1 : 0), IntPtr.Zero); } } private class FormattedTextRenderer : WzComparerR2.Text.TextRenderer, IDisposable { public FormattedTextRenderer() { fmt = (StringFormat)StringFormat.GenericTypographic.Clone(); } public bool UseGDIRenderer { get; set; } public IDictionary FontColorTable { get; set; } const int MAX_RANGES = 32; StringFormat fmt; Graphics g; RectangleF infinityRect; int drawX; Color defaultColor; public void DrawString(Graphics g, string s, Font font, int x, int x1, ref int y, int height, TextAlignment alignment = TextAlignment.Left) { //初始化环境 this.g = g; this.drawX = x; this.defaultColor = Color.White; float fontLineHeight = GetFontLineHeight(font); this.infinityRect = new RectangleF(0, 0, ushort.MaxValue, fontLineHeight); base.DrawFormatString(s, font, x1 - x, ref y, height, alignment); } public void DrawPlainText(Graphics g, string s, Font font, Color color, int x, int x1, ref int y, int height, TextAlignment alignment = TextAlignment.Left) { //初始化环境 this.g = g; this.drawX = x; this.defaultColor = color; float fontLineHeight = GetFontLineHeight(font); this.infinityRect = new RectangleF(0, 0, ushort.MaxValue, fontLineHeight); base.DrawPlainText(s, font, x1 - x, ref y, height, alignment); } private float GetFontLineHeight(Font font) { var ff = font.FontFamily; return (float)Math.Ceiling(1.0 * font.Height * ff.GetLineSpacing(font.Style) / ff.GetEmHeight(font.Style)); } protected override void MeasureRuns(List runs) { List tempRuns = new List(MAX_RANGES); foreach (var run in runs) { tempRuns.Add(run); if (tempRuns.Count >= MAX_RANGES) { MeasureBatch(tempRuns); tempRuns.Clear(); } } MeasureBatch(tempRuns); //failed if (runs.Where(run => !run.IsBreakLine && run.Length > 0) .All(run => run.Width == 0)) { float x = 0; foreach (var run in runs.Where(r => !r.IsBreakLine)) { run.X = (int)Math.Round(x); float width = 0; for (int i = 0; i < run.Length; i++) { var chr = this.sb[run.StartIndex + i]; width += chr > 0xff ? this.font.Size : (this.font.Size / 2); } run.Width = (int)Math.Round(x); x += width; } } } private void MeasureBatch(List runs) { string text = sb.ToString(); if (runs.Count > 0 && !runs.All(run => run.IsBreakLine)) { fmt.SetMeasurableCharacterRanges(runs.Select(r => new CharacterRange(r.StartIndex, r.Length)).ToArray()); var regions = g.MeasureCharacterRanges(text, font, infinityRect, fmt); for (int i = 0; i < runs.Count; i++) { var layout = regions[i].GetBounds(g); runs[i].X = (int)Math.Round(layout.Left); runs[i].Width = (int)Math.Round(layout.Width); regions[i].Dispose(); } } } protected override Rectangle[] MeasureChars(int startIndex, int length) { string word = sb.ToString(startIndex, length); Rectangle[] rects = new Rectangle[length]; for (int i = 0; i < length; i += MAX_RANGES) { //批次 int chrCount = Math.Min(length - i, MAX_RANGES); fmt.SetMeasurableCharacterRanges( Enumerable.Range(i, chrCount) .Select(start => new CharacterRange(start, 1)) .ToArray()); var regions = g.MeasureCharacterRanges(word, font, infinityRect, fmt); for (int i1 = 0; i1 < regions.Length; i1++) { var rect = regions[i1].GetBounds(g); rects[i + i1] = new Rectangle( (int)Math.Round(rect.Left), (int)Math.Round(rect.Top), (int)Math.Round(rect.Width), (int)Math.Round(rect.Height) ); } } //failed if (rects.All(rect => rect.Width == 0)) { float x = 0; for (int i = 0; i < rects.Length; i++) { var chr = this.sb[startIndex + i]; var width = chr > 0xff ? this.font.Size : (this.font.Size / 2); rects[i] = new Rectangle( (int)Math.Round(x), 0, (int)Math.Round(width), font.Height ); } } return rects; } protected override void Flush(StringBuilder sb, int startIndex, int length, int x, int y, string colorID) { string content = sb.ToString(startIndex, length); colorID = colorID ?? string.Empty; Color color; if (!(this.FontColorTable?.TryGetValue(colorID, out color) ?? false)) { switch (colorID) { case "c": color = GearGraphics.OrangeBrushColor; break; default: color = this.defaultColor; break; } } if (this.UseGDIRenderer) { TR.DrawText(g, content, font, new Point(this.drawX + x, y), color, TextFormatFlags.NoPrefix | TextFormatFlags.NoPadding); } else { using (var brush = new SolidBrush(color)) { g.DrawString(content, font, brush, this.drawX + x, y, fmt); } } } public void Dispose() { if (fmt != null) fmt.Dispose(); } } } } ================================================ FILE: WzComparerR2/CharaSimControl/GearTooltipRender.cs ================================================ using System; using System.Drawing; using System.Drawing.Drawing2D; using System.Collections.Generic; using System.Text; using CharaSimResource; using WzComparerR2.Common; using WzComparerR2.CharaSim; namespace WzComparerR2.CharaSimControl { public class GearTooltipRender : TooltipRender { public GearTooltipRender() { } private Gear gear; private CharacterStatus charStat; public Gear Gear { get { return gear; } set { gear = value; } } public CharacterStatus CharacterStatus { get { return charStat; } set { charStat = value; } } public override Bitmap Render() { if (this.gear == null) { return null; } int picHeight, iconY, picHeight2, picHeight3; Bitmap left = renderBase(out picHeight, out iconY); Bitmap add = renderAddition(out picHeight2); Bitmap set = renderSetItem(out picHeight3); //整合图像 int width = 252; if (add != null) width += 252; if (set != null) width += 252; Bitmap tooltip = new Bitmap(width, Math.Max(Math.Max(picHeight, picHeight2), picHeight3)); Graphics g = Graphics.FromImage(tooltip); bool epic = gear.Epic; width = 0; //绘制主图 if (left != null) { g.FillRectangle(epic ? GearGraphics.EpicGearBackBrush : GearGraphics.GearBackBrush, 2, 2, 248, picHeight - 4); g.CompositingMode = CompositingMode.SourceCopy; g.FillRectangle(epic ? GearGraphics.EpicGearIconBackBrush : GearGraphics.GearIconBackBrush, 14, iconY, 68, 68); g.CompositingMode = CompositingMode.SourceOver; g.DrawImage(left, 0, 0, new Rectangle(0, 0, 252, picHeight - 2), GraphicsUnit.Pixel); //绘制外边框 g.DrawLines(epic ? GearGraphics.EpicGearBackPen : GearGraphics.GearBackPen, GearGraphics.GetBorderPath(0, 252, picHeight)); //绘制等级边框 Pen pen = GearGraphics.GetGearItemBorderPen(gear.Grade); if (pen != null) { g.DrawLines(pen, getRankBorderPath(picHeight)); } width += 252; } //绘制addition if (add != null) { //底色和边框 g.FillRectangle(epic ? GearGraphics.EpicGearBackBrush : GearGraphics.GearBackBrush, width + 2, 2, 248, picHeight - 4); g.DrawLines(epic ? GearGraphics.EpicGearBackPen : GearGraphics.GearBackPen, GearGraphics.GetBorderPath(width, 252, picHeight)); //复制原图 g.DrawImage(add, width, 0, new Rectangle(0, 0, 252, picHeight2), GraphicsUnit.Pixel); add.Dispose(); width += 252; } //绘制setitem if (set != null) { //底色和边框 g.FillRectangle(GearGraphics.GearBackBrush, width + 2, 2, 248, picHeight3 - 4); g.DrawLines(GearGraphics.GearBackPen, GearGraphics.GetBorderPath(width, 252, picHeight3)); //复制原图 g.DrawImage(set, width, 0, new Rectangle(0, 0, 252, picHeight3), GraphicsUnit.Pixel); set.Dispose(); width += 252; } // GearGraphics.DrawGearDetailNumber(g, 2, 2, gear.ItemID.ToString("d8"), true); g.Dispose(); return tooltip; } private Bitmap renderBase(out int picHeight, out int iconY) { //绘制左侧部分 Bitmap leftPart = new Bitmap(252, DefaultPicHeight); Graphics g = Graphics.FromImage(leftPart); StringFormat format = new StringFormat(); int value; picHeight = 10; if (gear.Star > 0) //绘制星星 { if (gear.Star < 5) { for (int i = 0; i < gear.Star; i++) { g.DrawImage(Resource.ToolTip_Equip_Star_Star, 126 - gear.Star * 13 / 2 + 13 * i, picHeight); } picHeight += 18; } else { int star = gear.Star % 5, star2 = gear.Star / 5; int dx = 126 - (13 * star + 26 * star2) / 2; for (int i = 0; i < 1; i++, dx += 26) { g.DrawImage(Resource.ToolTip_Equip_Star_Star2, dx, picHeight); } for (int i = 0; i < star; i++, dx += 13) { g.DrawImage(Resource.ToolTip_Equip_Star_Star, dx, picHeight + 5); } for (int i = 1; i < star2; i++, dx += 26) { g.DrawImage(Resource.ToolTip_Equip_Star_Star2, dx, picHeight); } picHeight += 28; } } //装备标题 StringResult sr; if (StringLinker == null || !StringLinker.StringEqp.TryGetValue(gear.ItemID, out sr)) { sr = new StringResult(); sr.Name = "(null)"; } string gearName = sr.Name; string nameAdd = gear.ScrollUp > 0 ? ("+" + gear.ScrollUp) : null; switch (Gear.GetGender(gear.ItemID)) { case 0: nameAdd += "男"; break; case 1: nameAdd += "女"; break; } if (!string.IsNullOrEmpty(nameAdd)) { gearName += " (" + nameAdd + ")"; } format.Alignment = StringAlignment.Center; g.DrawString(gearName, GearGraphics.ItemNameFont, GearGraphics.GetGearNameBrush(gear.diff, gear.ScrollUp > 0), 124, picHeight, format);//绘制装备名称 picHeight += 19; //装备rank string rankStr; if (gear.GetBooleanValue(GearPropType.specialGrade)) rankStr = ItemStringHelper.GetGearGradeString(GearGrade.Special); else rankStr = ItemStringHelper.GetGearGradeString(gear.Grade); g.DrawString(rankStr, GearGraphics.ItemDetailFont, Brushes.White, 127, picHeight, format); picHeight += 21; //额外属性 for (int i = 0; i < 2; i++) { string attrStr = GetGearAttributeString(i); if (!string.IsNullOrEmpty(attrStr)) { g.DrawString(attrStr, GearGraphics.ItemDetailFont, GearGraphics.GearNameBrushC, 126, picHeight, format); picHeight += 19; } } //装备限时 if (gear.TimeLimited) { DateTime time = DateTime.Now.AddDays(7d); string expireStr = time.ToString("到yyyy年 M月 d日 H时 m分可以用"); g.DrawString(expireStr, GearGraphics.ItemDetailFont, Brushes.White, 126, picHeight, format); picHeight += 16; } picHeight += 1; iconY = picHeight + 1; bool epic = gear.Epic; //绘制图标 if (gear.Icon.Bitmap != null) { g.DrawImage(GearGraphics.EnlargeBitmap(gear.Icon.Bitmap), 14 + (1 - gear.Icon.Origin.X) * 2, iconY + (33 - gear.Icon.Origin.Y) * 2); } if (gear.Cash) { g.DrawImage(GearGraphics.EnlargeBitmap(Resource.CashItem_0), 14 + 68 - 26, iconY + 68 - 26); } //绘制属性要求 drawGearReq(g, ref picHeight); //绘制装备等级 if (gear.Props.TryGetValue(GearPropType.level, out value)) { g.DrawImage(Resource.ToolTip_Equip_GrowthEnabled_itemLEV, 96, picHeight); GearGraphics.DrawGearGrowthNumber(g, 160, picHeight + 4, (value == -1) ? "m" : value.ToString(), true); picHeight += 12; g.DrawImage(Resource.ToolTip_Equip_GrowthEnabled_itemEXP, 96, picHeight); GearGraphics.DrawGearGrowthNumber(g, 160, picHeight + 4, (value == -1) ? "m" : "0%", true); } else { g.DrawImage(Resource.ToolTip_Equip_GrowthDisabled_itemLEV, 96, picHeight); g.DrawImage(Resource.ToolTip_Equip_GrowthDisabled_none, 160, picHeight + 4 + 3); picHeight += 12; g.DrawImage(Resource.ToolTip_Equip_GrowthDisabled_itemEXP, 96, picHeight); g.DrawImage(Resource.ToolTip_Equip_GrowthDisabled_none, 160, picHeight + 4 + 3); } picHeight += 12; if (gear.Props.TryGetValue(GearPropType.durability, out value)) { if (value > 100) value = 100; g.DrawImage(value > 0 ? Resource.ToolTip_Equip_Can_durability : Resource.ToolTip_Equip_Cannot_durability, 96, picHeight); GearGraphics.DrawGearDetailNumber(g, 173, picHeight, value.ToString() + "%", value > 0); } picHeight += 13; //绘制职业要求 int reqJob; gear.Props.TryGetValue(GearPropType.reqJob, out reqJob); g.DrawString("新手", GearGraphics.ItemDetailFont, reqJob > 0 ? Brushes.Red : Brushes.White, 10, picHeight); if (reqJob == 0) reqJob = 0x1f;//0001 1111 if (reqJob == -1) reqJob = 0; //0000 0000 g.DrawString("战士", GearGraphics.ItemDetailFont, (reqJob & 1) == 0 ? Brushes.Red : Brushes.White, 46, picHeight); g.DrawString("魔法师", GearGraphics.ItemDetailFont, (reqJob & 2) == 0 ? Brushes.Red : Brushes.White, 82, picHeight); g.DrawString("弓箭手", GearGraphics.ItemDetailFont, (reqJob & 4) == 0 ? Brushes.Red : Brushes.White, 130, picHeight); g.DrawString("飞侠", GearGraphics.ItemDetailFont, (reqJob & 8) == 0 ? Brushes.Red : Brushes.White, 178, picHeight); g.DrawString("海盗", GearGraphics.ItemDetailFont, (reqJob & 16) == 0 ? Brushes.Red : Brushes.White, 214, picHeight); picHeight += 19; //额外职业要求 string extraReq = ItemStringHelper.GetExtraJobReqString(gear.type) ?? (gear.Props.TryGetValue(GearPropType.reqSpecJob, out value) ? ItemStringHelper.GetExtraJobReqString(value) : null); if (!string.IsNullOrEmpty(extraReq)) { g.DrawString(extraReq, GearGraphics.ItemDetailFont, GearGraphics.GearNameBrushC, 124, picHeight, format); picHeight += 18; } //分割线1号 g.DrawLine(Pens.White, 6, picHeight, 245, picHeight); picHeight += 9; bool hasPart2 = false; //绘制属性 if (gear.Props.TryGetValue(GearPropType.superiorEqp, out value) && value > 0) { g.DrawString("极真", GearGraphics.ItemNameFont, GearGraphics.SetItemNameBrush, 126, picHeight, format); picHeight += 18; } if (gear.Props.TryGetValue(GearPropType.limitBreak, out value) && value > 0) { g.DrawString("突破极限", GearGraphics.ItemNameFont, GearGraphics.SetItemNameBrush, 126, picHeight, format); picHeight += 18; } bool isWeapon = Gear.IsLeftWeapon(gear.type) || Gear.IsDoubleHandWeapon(gear.type); string typeStr = ItemStringHelper.GetGearTypeString(gear.type); if (!string.IsNullOrEmpty(typeStr)) { g.DrawString("·", GearGraphics.ItemDetailFont, Brushes.White, 8, picHeight); g.DrawString((isWeapon ? "武器" : "装备") + "分类 : " + typeStr, GearGraphics.ItemDetailFont, Brushes.White, 20, picHeight); picHeight += 16; hasPart2 = true; } if (gear.Props.TryGetValue(GearPropType.attackSpeed, out value)) { g.DrawString("·", GearGraphics.ItemDetailFont, Brushes.White, 8, picHeight); g.DrawString("攻击速度 : " + ItemStringHelper.GetAttackSpeedString(value), GearGraphics.ItemDetailFont, Brushes.White, 20, picHeight); picHeight += 16; hasPart2 = true; } List props = new List(); foreach (KeyValuePair p in gear.Props) { if ((int)p.Key < 100 && p.Value != 0) props.Add(p.Key); } props.Sort(); foreach (GearPropType type in props) { g.DrawString("·", GearGraphics.ItemDetailFont, Brushes.White, 8, picHeight); g.DrawString(ItemStringHelper.GetGearPropString(type, gear.Props[type]), (epic && Gear.IsEpicPropType(type)) ? GearGraphics.EpicGearDetailFont : GearGraphics.ItemDetailFont, Brushes.White, 20, picHeight); picHeight += 16; hasPart2 = true; } bool hasTuc = gear.HasTuc && gear.Props.TryGetValue(GearPropType.tuc, out value); if (hasTuc) { g.DrawString("·可升级次数 : " + value + "回", GearGraphics.ItemDetailFont, Brushes.White, 8, picHeight); picHeight += 16; hasPart2 = true; } if (gear.Props.TryGetValue(GearPropType.limitBreak, out value) && value > 0) { g.DrawString(ItemStringHelper.GetGearPropString(GearPropType.limitBreak, value), GearGraphics.ItemDetailFont, GearGraphics.SetItemNameBrush, 8, picHeight); picHeight += 16; hasPart2 = true; } if (hasTuc && gear.Hammer > -1) { if (gear.Hammer == 2) { g.DrawString("黄金锤提炼完成", GearGraphics.ItemDetailFont, Brushes.White, 8, picHeight); picHeight += 16; } if (gear.Props.TryGetValue(GearPropType.superiorEqp, out value) && value > 0) { g.DrawString(ItemStringHelper.GetGearPropString(GearPropType.superiorEqp, value), GearGraphics.ItemDetailFont, GearGraphics.SetItemNameBrush, 8, picHeight); picHeight += 16; } if (gear.Star > 0) { g.DrawString("·应用" + gear.Star + "星强化", GearGraphics.ItemDetailFont, GearGraphics.OrangeBrush, 8, picHeight); picHeight += 16; } picHeight += 2; g.DrawString("金锤子已提高的强化次数", GearGraphics.ItemDetailFont, GearGraphics.GoldHammerBrush, 8, picHeight); g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias; g.DrawString(": " + gear.Hammer.ToString() + (gear.Hammer == 2 ? "(MAX)" : null), GearGraphics.TahomaFont, GearGraphics.GoldHammerBrush, 140, picHeight - 2); g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.SystemDefault; picHeight += 14; hasPart2 = true; } //分割线2号 if (hasPart2) { g.DrawLine(Pens.White, 6, picHeight, 245, picHeight); picHeight += 9; } //绘制潜能 int optionCount = 0; foreach (Potential potential in gear.Options) { if (potential != null) { g.DrawString("·", GearGraphics.ItemDetailFont, Brushes.White, 8, picHeight); g.DrawString(potential.ConvertSummary(), GearGraphics.ItemDetailFont, Brushes.White, 20, picHeight); picHeight += 16; optionCount++; } } if (optionCount>0){ picHeight += 4 * optionCount; } else if (gear.CanPotential) { GearGraphics.DrawString(g, " #c潜能卷轴# 可增加 #cC级物品# 潜力,但需鉴定。\n #c放大镜# 可解除 #c未鉴定物品# 潜能的封印。", GearGraphics.ItemDetailFont, 8, 236, ref picHeight, 16); picHeight += 4; } //绘制附加潜能 int adOptionCount = 0; foreach (Potential potential in gear.AdditionalOptions) { if (potential != null) { adOptionCount++; } } if (adOptionCount > 0) { //分割线3号 picHeight -= 3; g.DrawLine(Pens.White, 6, picHeight, 245, picHeight); g.DrawImage(GetAdditionalOptionIcon(gear.AdditionGrade), 8, picHeight+1); g.DrawString("附加潜能", GearGraphics.ItemDetailFont, GearGraphics.SetItemNameBrush, 26, picHeight+2); picHeight += 24; foreach (Potential potential in gear.AdditionalOptions) { if (potential != null) { g.DrawString("+", GearGraphics.ItemDetailFont, Brushes.White, 8, picHeight); g.DrawString(potential.ConvertSummary(), GearGraphics.ItemDetailFont, Brushes.White, 20, picHeight); picHeight += 18; adOptionCount++; } } picHeight += 5; } //绘制desc if (!string.IsNullOrEmpty(sr.Desc)) { if (optionCount > 0) picHeight -= 2; picHeight -= 3; GearGraphics.DrawString(g, sr.Desc, GearGraphics.ItemDetailFont, 8, 236, ref picHeight, 16); picHeight += 5; } if (gear.Props.TryGetValue(GearPropType.tradeAvailable, out value) && value != 0) { g.DrawString(ItemStringHelper.GetGearPropString(GearPropType.tradeAvailable, value), GearGraphics.ItemDetailFont, GearGraphics.OrangeBrush, 14, picHeight - 5); picHeight += 16; } if (gear.Props.TryGetValue(GearPropType.accountShareTag, out value) && value != 0) { GearGraphics.DrawString(g, " #c" + ItemStringHelper.GetGearPropString(GearPropType.accountShareTag, 1) + "#", GearGraphics.ItemDetailFont, 8, 236, ref picHeight, 16); picHeight += 16; } //绘制倾向 if (gear.State == GearState.itemList) { string incline = null; GearPropType[] inclineTypes = new GearPropType[]{ GearPropType.charismaEXP, GearPropType.senseEXP, GearPropType.insightEXP, GearPropType.willEXP, GearPropType.craftEXP, GearPropType.charmEXP }; string[] inclineString = new string[]{ "领导力","感性","洞察力","意志","手技","魅力"}; for (int i = 0; i < inclineTypes.Length; i++) { if (gear.Props.TryGetValue(inclineTypes[i], out value) && value > 0) { incline += "," + inclineString[i] + value; } } if (!string.IsNullOrEmpty(incline)) { picHeight -= 5; GearGraphics.DrawString(g, "\n #c装备时可以获得" + incline.Substring(1) + "的经验值,仅限1次。#", GearGraphics.ItemDetailFont, 8, 236, ref picHeight, 16); picHeight += 8; } } format.Dispose(); g.Dispose(); return leftPart; } private Bitmap renderAddition(out int picHeight) { Bitmap addBitmap = null; picHeight = 0; if (gear.Additions.Count > 0) { addBitmap = new Bitmap(252, DefaultPicHeight); Graphics g = Graphics.FromImage(addBitmap); StringBuilder sb = new StringBuilder(); foreach (Addition addition in gear.Additions) { string conString = addition.GetConString(), propString = addition.GetPropString(); if (!string.IsNullOrEmpty(conString) || !string.IsNullOrEmpty(propString)) { sb.Append("- "); if (!string.IsNullOrEmpty(conString)) sb.AppendLine(conString); if (!string.IsNullOrEmpty(propString)) sb.AppendLine(propString); sb.AppendLine(); } } if (sb.Length > 0) { picHeight = 10; GearGraphics.DrawString(g, sb.ToString(), GearGraphics.ItemDetailFont, 8, 236, ref picHeight, 16); } g.Dispose(); } return addBitmap; } private Bitmap renderSetItem(out int picHeight) { Bitmap setBitmap = null; int setID; picHeight = 0; if (gear.Props.TryGetValue(GearPropType.setItemID, out setID)) { SetItem setItem; if (!CharaSimLoader.LoadedSetItems.TryGetValue(setID, out setItem)) return null; setBitmap = new Bitmap(252, DefaultPicHeight); Graphics g = Graphics.FromImage(setBitmap); StringFormat format = new StringFormat(); format.Alignment = StringAlignment.Center; picHeight = 10; g.DrawString(setItem.SetItemName, GearGraphics.ItemDetailFont, GearGraphics.SetItemNameBrush, 126, 10, format); picHeight += 25; format.Alignment=StringAlignment.Far; foreach (var setItemPart in setItem.ItemIDs.Parts) { string itemName = setItemPart.Value.RepresentName; string typeName = setItemPart.Value.TypeName; if (string.IsNullOrEmpty(itemName) || string.IsNullOrEmpty(typeName)) { foreach (var itemID in setItemPart.Value.ItemIDs) { StringResult sr; if (!StringLinker.StringEqp.TryGetValue(itemID.Key, out sr)) { sr = new StringResult(); sr.Name = "(null)"; } itemName = sr.Name; typeName = ItemStringHelper.GetSetItemGearTypeString(Gear.GetGearType(itemID.Key)); break; } } itemName = itemName ?? string.Empty; typeName = typeName ?? "装备"; Brush brush = setItemPart.Value.Enabled ? Brushes.White : GearGraphics.SetItemGrayBrush; g.DrawString(itemName, GearGraphics.ItemDetailFont, brush, 8, picHeight); g.DrawString("(" + typeName + ")", GearGraphics.ItemDetailFont, brush, 246, picHeight, format); picHeight += 18; } picHeight += 5; g.DrawLine(Pens.White, 6, picHeight, 245, picHeight);//分割线 picHeight += 9; foreach (KeyValuePair effect in setItem.Effects) { g.DrawString(effect.Key + "套装效果", GearGraphics.ItemDetailFont, GearGraphics.SetItemNameBrush, 8, picHeight); picHeight += 16; Brush brush = effect.Value.Enabled ? Brushes.White : GearGraphics.SetItemGrayBrush; foreach (KeyValuePair prop in effect.Value.Props) { if (prop.Key == GearPropType.Option) { List ops = (List)prop.Value; foreach (Potential p in ops) { g.DrawString(p.ConvertSummary(), GearGraphics.SetItemPropFont, brush, 8, picHeight); picHeight += 16; } } else { g.DrawString(ItemStringHelper.GetGearPropString(prop.Key, Convert.ToInt32(prop.Value)), GearGraphics.SetItemPropFont, brush, 8, picHeight); picHeight += 16; } } } picHeight += 11; format.Dispose(); g.Dispose(); } return setBitmap; } private string GetGearAttributeString(int line) { int value; List tags = new List(); switch (line) { case 0: if (gear.Props.TryGetValue(GearPropType.only, out value) && value != 0) { tags.Add(ItemStringHelper.GetGearPropString(GearPropType.only, value)); } if (gear.Props.TryGetValue(GearPropType.tradeBlock, out value) && value != 0) { tags.Add(ItemStringHelper.GetGearPropString(GearPropType.tradeBlock, value)); } if (gear.Props.TryGetValue(GearPropType.accountSharable, out value) && value != 0) { tags.Add(ItemStringHelper.GetGearPropString(GearPropType.accountSharable, value)); } if (gear.Props.TryGetValue(GearPropType.equipTradeBlock, out value) && value != 0) { if (gear.State == GearState.itemList) { tags.Add(ItemStringHelper.GetGearPropString(GearPropType.equipTradeBlock, value)); } else { string tradeBlock = ItemStringHelper.GetGearPropString(GearPropType.tradeBlock, 1); if (!tags.Contains(tradeBlock)) tags.Add(tradeBlock); } } if (gear.Props.TryGetValue(GearPropType.noPotential, out value) && value != 0) { tags.Add(ItemStringHelper.GetGearPropString(GearPropType.noPotential, value)); } if (gear.Props.TryGetValue(GearPropType.fixedPotential, out value) && value != 0) { tags.Add(ItemStringHelper.GetGearPropString(GearPropType.fixedPotential, value)); } break; case 1: if (gear.Props.TryGetValue(GearPropType.onlyEquip, out value) && value != 0) { tags.Add(ItemStringHelper.GetGearPropString(GearPropType.onlyEquip, value)); } if (gear.Props.TryGetValue(GearPropType.notExtend, out value) && value != 0) { tags.Add(ItemStringHelper.GetGearPropString(GearPropType.notExtend, value)); } break; } return tags.Count > 0 ? string.Join(", ", tags.ToArray()) : null; } private void drawGearReq(Graphics g, ref int picHeight) { int value; bool isGetProp; bool can; //等级要求 isGetProp = gear.Props.TryGetValue(GearPropType.reqLevel, out value); can = (charStat == null || charStat.Level >= value); g.DrawImage(can ? Resource.ToolTip_Equip_Can_reqLEV : Resource.ToolTip_Equip_Cannot_reqLEV, 96, picHeight); GearGraphics.DrawGearDetailNumber(g, 156, picHeight + 4, isGetProp ? value.ToString() : "-", can); picHeight += 12; //力量要求 isGetProp = gear.Props.TryGetValue(GearPropType.reqSTR, out value); can = (charStat == null || charStat.Strength.GetSum() >= value); g.DrawImage(can ? Resource.ToolTip_Equip_Can_reqSTR : Resource.ToolTip_Equip_Cannot_reqSTR, 96, picHeight); GearGraphics.DrawGearDetailNumber(g, 156, picHeight + 4, isGetProp ? value.ToString() : "-", can); picHeight += 12; //敏捷要求 isGetProp = gear.Props.TryGetValue(GearPropType.reqDEX, out value); can = (charStat == null || charStat.Dexterity.GetSum() >= value); g.DrawImage(can ? Resource.ToolTip_Equip_Can_reqDEX : Resource.ToolTip_Equip_Cannot_reqDEX, 96, picHeight); GearGraphics.DrawGearDetailNumber(g, 156, picHeight + 4, isGetProp ? value.ToString() : "-", can); picHeight += 12; //智力要求 isGetProp = gear.Props.TryGetValue(GearPropType.reqINT, out value); can = (charStat == null || charStat.Intelligence.GetSum() >= value); g.DrawImage(can ? Resource.ToolTip_Equip_Can_reqINT : Resource.ToolTip_Equip_Cannot_reqINT, 96, picHeight); GearGraphics.DrawGearDetailNumber(g, 156, picHeight + 4, isGetProp ? value.ToString() : "-", can); picHeight += 12; //运气要求 isGetProp = gear.Props.TryGetValue(GearPropType.reqLUK, out value); can = (charStat == null || charStat.Luck.GetSum() >= value); g.DrawImage(can ? Resource.ToolTip_Equip_Can_reqLUK : Resource.ToolTip_Equip_Cannot_reqLUK, 96, picHeight); GearGraphics.DrawGearDetailNumber(g, 156, picHeight + 4, isGetProp ? value.ToString() : "-", can); picHeight += 12; //人气要求 isGetProp = gear.Props.TryGetValue(GearPropType.reqPOP, out value); can = (charStat == null || charStat.Pop >= value); g.DrawImage(can ? Resource.ToolTip_Equip_Can_reqPOP : Resource.ToolTip_Equip_Cannot_reqPOP, 96, picHeight); GearGraphics.DrawGearDetailNumber(g, 156, picHeight + 4, isGetProp ? value.ToString() : "-", can); picHeight += 12; } private Image GetAdditionalOptionIcon(GearGrade grade) { switch (grade) { case GearGrade.B: return Resource.AdditionalOptionTooltip_rare; case GearGrade.A: return Resource.AdditionalOptionTooltip_epic; case GearGrade.S: return Resource.AdditionalOptionTooltip_unique; case GearGrade.SS: return Resource.AdditionalOptionTooltip_legendary; } return null; } private Point[] getRankBorderPath(int height) { List pointList = new List(5); pointList.Add(new Point(252 - 4, height - 5)); pointList.Add(new Point(252 - 4, 4)); pointList.Add(new Point(4, 4)); pointList.Add(new Point(4, height - 4)); pointList.Add(new Point(252 - 5, height - 4)); return pointList.ToArray(); } } } ================================================ FILE: WzComparerR2/CharaSimControl/GearTooltipRender2.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Drawing; using System.Drawing.Drawing2D; using System.Text; using System.Text.RegularExpressions; using Resource = CharaSimResource.Resource; using WzComparerR2.Common; using WzComparerR2.CharaSim; using WzComparerR2.WzLib; namespace WzComparerR2.CharaSimControl { public class GearTooltipRender2 : TooltipRender { static GearTooltipRender2() { res = new Dictionary(); res["t"] = new TextureBrush(Resource.UIToolTip_img_Item_Frame_top, WrapMode.Clamp); res["line"] = new TextureBrush(Resource.UIToolTip_img_Item_Frame_line, WrapMode.Tile); res["dotline"] = new TextureBrush(Resource.UIToolTip_img_Item_Frame_dotline, WrapMode.Clamp); res["b"] = new TextureBrush(Resource.UIToolTip_img_Item_Frame_bottom, WrapMode.Clamp); res["cover"] = new TextureBrush(Resource.UIToolTip_img_Item_Frame_cover, WrapMode.Clamp); } private static Dictionary res; public GearTooltipRender2() { } private CharacterStatus charStat; public Gear Gear { get; set; } public override object TargetItem { get { return this.Gear; } set { this.Gear = value as Gear; } } public CharacterStatus CharacterStatus { get { return charStat; } set { charStat = value; } } public bool ShowSpeed { get; set; } public bool ShowLevelOrSealed { get; set; } public bool ShowMedalTag { get; set; } = true; public bool IsCombineProperties { get; set; } = true; public TooltipRender SetItemRender { get; set; } public override Bitmap Render() { if (this.Gear == null) { return null; } int[] picH = new int[4]; Bitmap left = RenderBase(out picH[0]); Bitmap add = RenderAddition(out picH[1]); Bitmap genesis = RenderGenesisSkills(out int genesisHeight); Bitmap set = RenderSetItem(out int setHeight); picH[2] = genesisHeight + setHeight; Bitmap levelOrSealed = null; if (this.ShowLevelOrSealed) { levelOrSealed = RenderLevelOrSealed(out picH[3]); } int width = 261; if (add != null) width += add.Width; if (set != null) width += set.Width; else if (genesis != null) width += genesis.Width; // ideally genesisWeapons always have setitem if (levelOrSealed != null) width += levelOrSealed.Width; int height = 0; for (int i = 0; i < picH.Length; i++) { height = Math.Max(height, picH[i]); } Bitmap tooltip = new Bitmap(width, height); Graphics g = Graphics.FromImage(tooltip); //绘制主图 width = 0; if (left != null) { //绘制背景 g.DrawImage(res["t"].Image, width, 0); FillRect(g, res["line"], width, 13, picH[0] - 13); g.DrawImage(res["b"].Image, width, picH[0] - 13); //复制图像 g.DrawImage(left, width, 0, new Rectangle(0, 0, left.Width, picH[0]), GraphicsUnit.Pixel); //cover g.DrawImage(res["cover"].Image, 3, 3); width += left.Width; left.Dispose(); } //绘制addition if (add != null) { //绘制背景 g.DrawImage(res["t"].Image, width, 0); FillRect(g, res["line"], width, 13, tooltip.Height - 13); g.DrawImage(res["b"].Image, width, tooltip.Height - 13); //复制原图 g.DrawImage(add, width, 0, new Rectangle(0, 0, add.Width, picH[1]), GraphicsUnit.Pixel); width += add.Width; add.Dispose(); } //绘制setitem if (genesis != null || set != null) { int y = 0; int partWidth = 0; if (genesis != null) { // draw background g.DrawImage(res["t"].Image, width, 0); FillRect(g, res["line"], width, 13, genesisHeight - 13); g.DrawImage(res["b"].Image, width, genesisHeight - 13); // copy text layer g.DrawImage(genesis, width, 0, new Rectangle(0, 0, genesis.Width, genesisHeight), GraphicsUnit.Pixel); y += genesisHeight; partWidth = Math.Max(partWidth, genesis.Width); genesis.Dispose(); } //复制原图 if (set != null) { g.DrawImage(set, width, y, new Rectangle(0, 0, set.Width, setHeight), GraphicsUnit.Pixel); partWidth = Math.Max(partWidth, set.Width); set.Dispose(); } width += partWidth; } //绘制levelOrSealed if (levelOrSealed != null) { //绘制背景 g.DrawImage(res["t"].Image, width, 0); FillRect(g, res["line"], width, 13, picH[3] - 13); g.DrawImage(res["b"].Image, width, picH[3] - 13); //复制原图 g.DrawImage(levelOrSealed, width, 0, new Rectangle(0, 0, levelOrSealed.Width, picH[3]), GraphicsUnit.Pixel); width += levelOrSealed.Width; levelOrSealed.Dispose(); } if (this.ShowObjectID) { GearGraphics.DrawGearDetailNumber(g, 3, 3, Gear.ItemID.ToString("d8"), true); } g.Dispose(); return tooltip; } private Bitmap RenderBase(out int picH) { Bitmap bitmap = new Bitmap(261, DefaultPicHeight); Graphics g = Graphics.FromImage(bitmap); g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit; StringFormat format = (StringFormat)StringFormat.GenericTypographic.Clone(); var itemPropColorTable = new Dictionary() { { "$y", GearGraphics.gearCyanColor }, { "$e", GearGraphics.ScrollEnhancementColor }, }; int value; picH = 13; if (!Gear.GetBooleanValue(GearPropType.blockUpgradeStarforce)) { DrawStar2(g, ref picH); //绘制星星 } //绘制装备名称 StringResult sr; if (StringLinker == null || !StringLinker.StringEqp.TryGetValue(Gear.ItemID, out sr)) { sr = new StringResult(); sr.Name = "(null)"; } string gearName = sr.Name; string nameAdd = Gear.ScrollUp > 0 ? ("+" + Gear.ScrollUp) : null; switch (Gear.GetGender(Gear.ItemID)) { case 0: nameAdd += "男"; break; case 1: nameAdd += "女"; break; } if (!string.IsNullOrEmpty(nameAdd)) { gearName += " (" + nameAdd + ")"; } format.Alignment = StringAlignment.Center; g.DrawString(gearName, GearGraphics.ItemNameFont2, GearGraphics.GetGearNameBrush(Gear.diff, Gear.ScrollUp > 0), 130, picH, format); picH += 23; //装备rank string rankStr = null; if (Gear.GetBooleanValue(GearPropType.specialGrade)) { rankStr = ItemStringHelper.GetGearGradeString(GearGrade.Special); } else if (!Gear.Cash) //T98后C级物品依然显示 { rankStr = ItemStringHelper.GetGearGradeString(Gear.Grade); } if (rankStr != null) { g.DrawString(rankStr, GearGraphics.ItemDetailFont, Brushes.White, 130, picH, format); picH += 15; } //额外属性 var attrList = GetGearAttributeString(); if (attrList.Count > 0) { var font = GearGraphics.ItemDetailFont; string attrStr = null; for (int i = 0; i < attrList.Count; i++) { var newStr = (attrStr != null ? (attrStr + ", ") : null) + attrList[i]; if (g.MeasureString(newStr, font, short.MaxValue, format).Width > 257) { g.DrawString(attrStr, GearGraphics.ItemDetailFont, GearGraphics.OrangeBrush2, 130, picH, format); picH += 15; attrStr = attrList[i]; } else { attrStr = newStr; } } if (!string.IsNullOrEmpty(attrStr)) { g.DrawString(attrStr, GearGraphics.ItemDetailFont, GearGraphics.OrangeBrush2, 130, picH, format); picH += 15; } } //装备限时 if (Gear.TimeLimited) { DateTime time = DateTime.Now.AddDays(7d); string expireStr = time.ToString("到yyyy年 M月 d日 H时 m分可以用"); g.DrawString(expireStr, GearGraphics.ItemDetailFont, Brushes.White, 130, picH, format); picH += 15; } else if (Gear.GetBooleanValue(GearPropType.abilityTimeLimited)) { DateTime time = DateTime.Now.AddDays(7d); string expireStr = time.ToString("效果持续到yyyy年M月d日H点m分"); g.DrawString(expireStr, GearGraphics.ItemDetailFont, Brushes.White, 130, picH, format); picH += 15; } //分割线1号 picH += 7; g.DrawImage(res["dotline"].Image, 0, picH); //绘制装备图标 if (Gear.Grade > 0 && (int)Gear.Grade <= 4) //绘制外框 { Image border = Resource.ResourceManager.GetObject("UIToolTip_img_Item_ItemIcon_" + (int)Gear.Grade) as Image; if (border != null) { g.DrawImage(border, 13, picH + 11); } } g.DrawImage(Resource.UIToolTip_img_Item_ItemIcon_base, 12, picH + 10); //绘制背景 if (Gear.IconRaw.Bitmap != null) //绘制icon { var attr = new System.Drawing.Imaging.ImageAttributes(); var matrix = new System.Drawing.Imaging.ColorMatrix( new[] { new float[] { 1, 0, 0, 0, 0 }, new float[] { 0, 1, 0, 0, 0 }, new float[] { 0, 0, 1, 0, 0 }, new float[] { 0, 0, 0, 0.5f, 0 }, new float[] { 0, 0, 0, 0, 1 }, }); attr.SetColorMatrix(matrix); //绘制阴影 var shade = Resource.UIToolTip_img_Item_ItemIcon_shade; g.DrawImage(shade, new Rectangle(18 + 9, picH + 15 + 54, shade.Width, shade.Height), 0, 0, shade.Width, shade.Height, GraphicsUnit.Pixel, attr); //绘制图标 g.DrawImage(GearGraphics.EnlargeBitmap(Gear.IconRaw.Bitmap), 18 + (1 - Gear.IconRaw.Origin.X) * 2, picH + 15 + (33 - Gear.IconRaw.Origin.Y) * 2); attr.Dispose(); } if (Gear.Cash) //绘制cash标识 { /* not installed since CMST136 * if (Gear.Props.TryGetValue(GearPropType.royalSpecial, out value) && value > 0) g.DrawImage(GearGraphics.EnlargeBitmap(Resource.CashItem_label_0), 18 + 68 - 26, picH + 15 + 68 - 26); else if (Gear.Props.TryGetValue(GearPropType.masterSpecial, out value) && value > 0) g.DrawImage(GearGraphics.EnlargeBitmap(Resource.CashItem_label_3), 18 + 68 - 26, picH + 15 + 68 - 26); else */ g.DrawImage(GearGraphics.EnlargeBitmap(Resource.CashItem_0), 18 + 68 - 26, picH + 15 + 68 - 26); } //检查星岩 bool hasSocket = Gear.GetBooleanValue(GearPropType.nActivatedSocket); if (hasSocket) { Bitmap socketBmp = GetAlienStoneIcon(); if (socketBmp != null) { g.DrawImage(GearGraphics.EnlargeBitmap(socketBmp), 18 + 2, picH + 15 + 3); } } g.DrawImage(Resource.UIToolTip_img_Item_ItemIcon_cover, 16, picH + 14); //绘制左上角cover //绘制攻击力变化 format.Alignment = StringAlignment.Far; g.DrawString("攻击力增加量", GearGraphics.ItemDetailFont, GearGraphics.GrayBrush2, 251, picH + 10, format); g.DrawImage(Resource.UIToolTip_img_Item_Equip_Summary_incline_0, 249 - 19, picH + 27); //暂时画个0 //绘制属性需求 DrawGearReq(g, 97, picH + 58); picH += 93; //绘制属性变化 DrawPropDiffEx(g, 12, picH); picH += 20; //绘制职业需求 DrawJobReq(g, ref picH); //分割线2号 g.DrawImage(res["dotline"].Image, 0, picH); picH += 8; bool hasPart2 = false; format.Alignment = StringAlignment.Center; //绘制属性 if (Gear.Props.TryGetValue(GearPropType.superiorEqp, out value) && value > 0) { g.DrawString("极真", GearGraphics.ItemDetailFont, GearGraphics.GreenBrush2, 130, picH, format); picH += 16; } if (Gear.Props.TryGetValue(GearPropType.limitBreak, out value) && value > 0) { g.DrawString("突破上限武器", GearGraphics.ItemDetailFont, GearGraphics.GreenBrush2, 130, picH, format); picH += 16; } //绘制装备升级 if (Gear.Props.TryGetValue(GearPropType.level, out value) && !Gear.FixLevel) { bool max = (Gear.Levels != null && value >= Gear.Levels.Count); g.DrawString("成长等级: " + (max ? "MAX" : value.ToString()), GearGraphics.ItemDetailFont, GearGraphics.OrangeBrush3, 11, picH); picH += 16; g.DrawString("成长经验值: " + (max ? "MAX" : "0%"), GearGraphics.ItemDetailFont, GearGraphics.OrangeBrush3, 11, picH); picH += 16; } if (Gear.Props.TryGetValue(GearPropType.@sealed, out value)) { bool max = (Gear.Seals != null && value >= Gear.Seals.Count); g.DrawString("封印解除阶段 : " + (max ? "MAX" : value.ToString()), GearGraphics.ItemDetailFont, GearGraphics.OrangeBrush3, 11, picH); picH += 16; g.DrawString("封印解除经验值 : " + (max ? "MAX" : "0%"), GearGraphics.ItemDetailFont, GearGraphics.OrangeBrush3, 11, picH); picH += 16; } //绘制耐久度 if (Gear.Props.TryGetValue(GearPropType.durability, out value)) { g.DrawString("耐久度 : " + "100%", GearGraphics.ItemDetailFont, GearGraphics.GreenBrush2, 11, picH); picH += 16; } //装备类型 bool isWeapon = Gear.IsWeapon(Gear.type); string typeStr = ItemStringHelper.GetGearTypeString(Gear.type); if (!string.IsNullOrEmpty(typeStr)) { if (isWeapon) { typeStr = "武器分类 : " + typeStr; } else { typeStr = "装备分类 : " + typeStr; } if (Gear.IsLeftWeapon(Gear.type) || Gear.type == GearType.katara) { typeStr += " (单手武器)"; } else if (Gear.IsDoubleHandWeapon(Gear.type)) { typeStr += " (双手武器)"; } g.DrawString(typeStr, GearGraphics.ItemDetailFont, Brushes.White, 11, picH); picH += 16; hasPart2 = true; } if (!Gear.Props.TryGetValue(GearPropType.attackSpeed, out value) && (Gear.IsWeapon(Gear.type) || Gear.type == GearType.katara)) //找不到攻速的武器 { value = 6; //给予默认速度 } // if (gear.Props.TryGetValue(GearPropType.attackSpeed, out value) && value > 0) if (value > 0) { bool isValidSpeed = (2 <= value && value <= 9); string speedStr = string.Format("攻击速度 : {0}{1}{2}", ItemStringHelper.GetAttackSpeedString(value), isValidSpeed ? $"(第{10 - value}阶段)" : null, ShowSpeed ? $"({value})" : null ); g.DrawString(speedStr, GearGraphics.ItemDetailFont, Brushes.White, 11, picH); picH += 16; hasPart2 = true; } //机器人等级 if (Gear.Props.TryGetValue(GearPropType.grade, out value) && value > 0) { g.DrawString("等级 : " + value, GearGraphics.ItemDetailFont, Brushes.White, 11, picH); picH += 16; hasPart2 = true; } //一般属性 List props = new List(); foreach (KeyValuePair p in Gear.PropsV5) //5转过滤 { if ((int)p.Key < 100 && p.Value != 0) props.Add(p.Key); } props.Sort(); //bool epic = Gear.Props.TryGetValue(GearPropType.epicItem, out value) && value > 0; foreach (GearPropType type in props) { //var font = (epic && Gear.IsEpicPropType(type)) ? GearGraphics.EpicGearDetailFont : GearGraphics.ItemDetailFont; //g.DrawString(ItemStringHelper.GetGearPropString(type, Gear.Props[type]), font, Brushes.White, 11, picH); //picH += 16; //绘制属性变化 Gear.StandardProps.TryGetValue(type, out value); //standard value var propStr = ItemStringHelper.GetGearPropDiffString(type, Gear.Props[type], value); GearGraphics.DrawString(g, propStr, GearGraphics.ItemDetailFont, itemPropColorTable, 13, 256, ref picH, 16); hasPart2 = true; } //戒指特殊潜能 int ringOpt, ringOptLv; if (Gear.Props.TryGetValue(GearPropType.ringOptionSkill, out ringOpt) && Gear.Props.TryGetValue(GearPropType.ringOptionSkillLv, out ringOptLv)) { var opt = Potential.LoadFromWz(ringOpt, ringOptLv, PluginBase.PluginManager.FindWz); if (opt != null) { g.DrawString(opt.ConvertSummary(), GearGraphics.ItemDetailFont2, Brushes.White, 11, picH); picH += 16; hasPart2 = true; } } bool hasReduce = Gear.Props.TryGetValue(GearPropType.reduceReq, out value); if (hasReduce && value > 0) { g.DrawString(ItemStringHelper.GetGearPropString(GearPropType.reduceReq, value), GearGraphics.ItemDetailFont, GearGraphics.GreenBrush2, 11, picH); picH += 16; hasPart2 = true; } bool hasTuc = Gear.HasTuc && Gear.Props.TryGetValue(GearPropType.tuc, out value); if (Gear.GetBooleanValue(GearPropType.exceptUpgrade)) { g.DrawString("无法强化", GearGraphics.ItemDetailFont, Brushes.White, 11, picH); picH += 16; } else if (Gear.GetBooleanValue(GearPropType.blockUpgradeStarforce)) { g.DrawString("无法进行星之力强化", GearGraphics.ItemDetailFont, GearGraphics.BlockRedBrush, 11, picH); picH += 16; } else if (hasTuc && !Gear.GetBooleanValue(GearPropType.blockUpgradeStarforce)) { var colorTable = new Dictionary { { "c", GearGraphics.OrangeBrush3Color } }; GearGraphics.DrawString(g, "可升级次数 : " + value + "回 #c(可修复次数:0)#", GearGraphics.ItemDetailFont, colorTable, 13, 256, ref picH, 16); hasPart2 = true; } //星星锤子 if (hasTuc && Gear.Hammer > -1 && !Gear.GetBooleanValue(GearPropType.blockUpgradeStarforce)) { if (Gear.Hammer == 2) { g.DrawString("应用黄金锤精炼", GearGraphics.ItemDetailFont, Brushes.White, 11, picH); picH += 16; } if (Gear.Props.TryGetValue(GearPropType.superiorEqp, out value) && value > 0) //极真 { g.DrawString(ItemStringHelper.GetGearPropString(GearPropType.superiorEqp, value), GearGraphics.ItemDetailFont, GearGraphics.GreenBrush2, 11, picH); picH += 16; } } if (Gear.Props.TryGetValue(GearPropType.CuttableCount, out value) && value > 0) //可使用剪刀 { g.DrawString(ItemStringHelper.GetGearPropString(GearPropType.CuttableCount, value), GearGraphics.ItemDetailFont, GearGraphics.OrangeBrush3, 11, picH); picH += 16; hasPart2 = true; } if (Gear.Props.TryGetValue(GearPropType.limitBreak, out value) && value > 0) //突破上限 { g.DrawString(ItemStringHelper.GetGearPropString(GearPropType.limitBreak, value), GearGraphics.ItemDetailFont, GearGraphics.GreenBrush2, 11, picH); picH += 16; hasPart2 = true; } if (hasTuc && Gear.Hammer > -1 && !Gear.GetBooleanValue(GearPropType.blockUpgradeStarforce)) { g.DrawString("金锤子已提高的强化次数", GearGraphics.ItemDetailFont, GearGraphics.GoldHammerBrush, 11, picH + 2); g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias; g.DrawString(": " + Gear.Hammer.ToString() + (Gear.Hammer == 2 ? "(MAX)" : null), GearGraphics.TahomaFont, GearGraphics.GoldHammerBrush, 145, picH); g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.SystemDefault; picH += 16; hasPart2 = true; } else if (Gear.GetBooleanValue(GearPropType.blockUpgradeExtraOption)) { g.DrawString("无法设置/重设额外属性", GearGraphics.ItemDetailFont, GearGraphics.BlockRedBrush, 11, picH); picH += 16; } if (hasTuc && Gear.PlatinumHammer > -1 && !Gear.GetBooleanValue(GearPropType.blockUpgradeStarforce)) { g.DrawString("白金锤强化次数:" + Gear.PlatinumHammer, GearGraphics.ItemDetailFont, Brushes.White, 11, picH); picH += 16; hasPart2 = true; } picH += 5; //绘制浮动属性 if ((Gear.VariableStat != null && Gear.VariableStat.Count > 0) || hasReduce) { if (hasPart2) //分割线... { picH -= 1; g.DrawImage(res["dotline"].Image, 0, picH); picH += 8; } if (Gear.VariableStat != null && Gear.VariableStat.Count > 0) { int reqLvl; Gear.Props.TryGetValue(GearPropType.reqLevel, out reqLvl); g.DrawString("增加各角色等级能力值(" + reqLvl + "Lv为止)", GearGraphics.ItemDetailFont, GearGraphics.OrangeBrush3, 130, picH, format); picH += 20; int reduceLvl; Gear.Props.TryGetValue(GearPropType.reduceReq, out reduceLvl); int curLevel = charStat == null ? reqLvl : Math.Min(charStat.Level, reqLvl); foreach (var kv in Gear.VariableStat) { int dLevel = curLevel - reqLvl + reduceLvl; //int addVal = (int)Math.Floor(kv.Value * dLevel); //这里有一个计算上的错误 换方式执行 int addVal = (int)Math.Floor(new decimal(kv.Value) * dLevel); string text = ItemStringHelper.GetGearPropString(kv.Key, addVal, 1); text += string.Format(" ({0:f1} x {1})", kv.Value, dLevel); g.DrawString(text, GearGraphics.ItemDetailFont, GearGraphics.OrangeBrush3, 10, picH, StringFormat.GenericTypographic); picH += 20; } if (hasReduce) { g.DrawString("升级及强化时,视做" + reqLvl + "Lv武器", GearGraphics.ItemDetailFont, GearGraphics.GrayBrush2, 12, picH, StringFormat.GenericTypographic); picH += 16; } } } //绘制潜能 int optionCount = 0; foreach (Potential potential in Gear.Options) { if (potential != null) { optionCount++; } } if (optionCount > 0) { //分割线3号 if (hasPart2) { g.DrawImage(res["dotline"].Image, 0, picH); picH += 8; } g.DrawImage(GetAdditionalOptionIcon(Gear.Grade), 9, picH - 1); g.DrawString("潜在属性", GearGraphics.ItemDetailFont, GearGraphics.GetPotentialTextBrush(Gear.Grade), 25, picH); picH += 17; foreach (Potential potential in Gear.Options) { if (potential != null) { g.DrawString(potential.ConvertSummary(), GearGraphics.ItemDetailFont2, Brushes.White, 11, picH); picH += 16; } } picH += 5; } if (hasSocket) { g.DrawLine(Pens.White, 6, picH, 254, picH); picH += 8; GearGraphics.DrawString(g, ItemStringHelper.GetGearPropString(GearPropType.nActivatedSocket, 1), GearGraphics.ItemDetailFont, 11, 247, ref picH, 16); picH += 3; } //绘制附加潜能 int adOptionCount = 0; foreach (Potential potential in Gear.AdditionalOptions) { if (potential != null) { adOptionCount++; } } if (adOptionCount > 0) { //分割线4号 if (hasPart2) { g.DrawImage(res["dotline"].Image, 0, picH); picH += 8; } g.DrawImage(GetAdditionalOptionIcon(Gear.AdditionGrade), 8, picH - 1); g.DrawString("附加潜能", GearGraphics.ItemDetailFont, GearGraphics.GetPotentialTextBrush(Gear.AdditionGrade), 26, picH); picH += 17; foreach (Potential potential in Gear.AdditionalOptions) { if (potential != null) { g.DrawString("+ " + potential.ConvertSummary(), GearGraphics.ItemDetailFont2, Brushes.White, 11, picH); picH += 15; } } picH += 5; } if (Gear.Props.TryGetValue(GearPropType.Etuc, out value) && value > 0) { //分割线5号 if (hasPart2) { g.DrawImage(res["dotline"].Image, 0, picH); picH += 8; } g.DrawString(ItemStringHelper.GetGearPropString(GearPropType.Etuc, value), GearGraphics.ItemDetailFont, Brushes.White, 11, picH); picH += 23; } //绘制desc List desc = new List(); GearPropType[] descTypes = new GearPropType[]{ GearPropType.tradeAvailable, GearPropType.accountShareTag, GearPropType.jokerToSetItem, GearPropType.colorvar, }; foreach (GearPropType type in descTypes) { if (Gear.Props.TryGetValue(type, out value) && value != 0) { desc.Add(ItemStringHelper.GetGearPropString(type, value)); } } //绘制倾向 if (Gear.State == GearState.itemList) { StringBuilder incline = new StringBuilder(); GearPropType[] inclineTypes = new GearPropType[]{ GearPropType.charismaEXP, GearPropType.senseEXP, GearPropType.insightEXP, GearPropType.willEXP, GearPropType.craftEXP, GearPropType.charmEXP }; string[] inclineString = new string[]{ "领导力","感性","洞察力","意志","手技","魅力"}; for (int i = 0; i < inclineTypes.Length; i++) { if (Gear.Props.TryGetValue(inclineTypes[i], out value) && value > 0) { if (incline.Length > 0) { incline.Append(", "); } incline.Append(inclineString[i]).Append(value); } } if (incline.Length > 0) { desc.Add($"\n #c装备时限1次获得{incline}的经验值.(每天限制,超过最大值时除外)#"); } } //判断是否绘制徽章 Wz_Node medalResNode = null; bool willDrawMedalTag = this.ShowMedalTag && this.Gear.Sample.Bitmap == null && this.Gear.Props.TryGetValue(GearPropType.medalTag, out value) && this.TryGetMedalResource(value, out medalResNode); //判断是否绘制技能desc string levelDesc = null; if (Gear.FixLevel && Gear.Props.TryGetValue(GearPropType.level, out value)) { var levelInfo = Gear.Levels.FirstOrDefault(info => info.Level == value); if (levelInfo != null && levelInfo.Prob == levelInfo.ProbTotal && !string.IsNullOrEmpty(levelInfo.HS)) { levelDesc = sr[levelInfo.HS]; } } if (!string.IsNullOrEmpty(sr.Desc) || !string.IsNullOrEmpty(levelDesc) || desc.Count > 0 || Gear.Sample.Bitmap != null || willDrawMedalTag) { //分割线4号 if (hasPart2) { g.DrawImage(res["dotline"].Image, 0, picH); picH += 8; } if (Gear.Sample.Bitmap != null) { g.DrawImage(Gear.Sample.Bitmap, (bitmap.Width - Gear.Sample.Bitmap.Width) / 2, picH); picH += Gear.Sample.Bitmap.Height; picH += 4; } if (medalResNode != null) { GearGraphics.DrawNameTag(g, medalResNode, sr.Name, bitmap.Width, ref picH); picH += 4; } if (!string.IsNullOrEmpty(sr.Desc)) { GearGraphics.DrawString(g, sr.Desc, GearGraphics.ItemDetailFont2, 11, 245, ref picH, 16); } if (!string.IsNullOrEmpty(levelDesc)) { GearGraphics.DrawString(g, " " + levelDesc, GearGraphics.ItemDetailFont2, 11, 245, ref picH, 16); } foreach (string str in desc) { GearGraphics.DrawString(g, str, GearGraphics.ItemDetailFont, 11, 245, ref picH, 16); } picH += 5; } foreach (KeyValuePair kv in CharaSimLoader.LoadedExclusiveEquips) { if (kv.Value.Items.Contains(Gear.ItemID)) { if (hasPart2) { g.DrawImage(res["dotline"].Image, 0, picH); picH += 8; } string exclusiveEquip; if (!string.IsNullOrEmpty(kv.Value.Info)) { exclusiveEquip = "#c" + kv.Value.Info + "类道具无法重复使用。#"; } else { List itemNames = new List(); foreach (int itemID in kv.Value.Items) { StringResult sr2; if (this.StringLinker == null || !this.StringLinker.StringEqp.TryGetValue(itemID, out sr2)) { sr2 = new StringResult(); sr2.Name = "(null)"; } itemNames.Add(sr2.Name); } exclusiveEquip = "#c无法重复装备" + string.Join(", ", itemNames) + "。#"; } GearGraphics.DrawString(g, exclusiveEquip, GearGraphics.ItemDetailFont, 11, 246, ref picH, 16); break; } } picH += 2; format.Dispose(); g.Dispose(); return bitmap; } private Bitmap RenderAddition(out int picHeight) { Bitmap addBitmap = null; picHeight = 0; if (Gear.Additions.Count > 0 && !Gear.AdditionHideDesc) { addBitmap = new Bitmap(261, DefaultPicHeight); Graphics g = Graphics.FromImage(addBitmap); StringBuilder sb = new StringBuilder(); foreach (Addition addition in Gear.Additions) { string conString = addition.GetConString(), propString = addition.GetPropString(); if (!string.IsNullOrEmpty(conString) || !string.IsNullOrEmpty(propString)) { sb.Append("- "); if (!string.IsNullOrEmpty(conString)) sb.AppendLine(conString); if (!string.IsNullOrEmpty(propString)) sb.AppendLine(propString); sb.AppendLine(); } } if (sb.Length > 0) { picHeight = 10; GearGraphics.DrawString(g, sb.ToString(), GearGraphics.ItemDetailFont, 10, 250, ref picHeight, 16); } g.Dispose(); } return addBitmap; } private Bitmap RenderSetItem(out int picHeight) { Bitmap setBitmap = null; int setID; picHeight = 0; if (Gear.Props.TryGetValue(GearPropType.setItemID, out setID)) { SetItem setItem; if (!CharaSimLoader.LoadedSetItems.TryGetValue(setID, out setItem)) return null; TooltipRender renderer = this.SetItemRender; if (renderer == null) { var defaultRenderer = new SetItemTooltipRender(); defaultRenderer.StringLinker = this.StringLinker; defaultRenderer.ShowObjectID = false; renderer = defaultRenderer; } renderer.TargetItem = setItem; setBitmap = renderer.Render(); if (setBitmap != null) picHeight = setBitmap.Height; } return setBitmap; } private Bitmap RenderLevelOrSealed(out int picHeight) { Bitmap levelOrSealed = null; Graphics g = null; StringFormat format = new StringFormat(); format.Alignment = StringAlignment.Center; picHeight = 0; if (Gear.Levels != null) { if (levelOrSealed == null) { levelOrSealed = new Bitmap(261, DefaultPicHeight); g = Graphics.FromImage(levelOrSealed); } picHeight += 13; g.DrawString("装备成长属性", GearGraphics.ItemDetailFont, GearGraphics.GreenBrush2, 130, picHeight, format); picHeight += 16; if (Gear.FixLevel) { g.DrawString("[装备获取时固定等级]", GearGraphics.ItemDetailFont, GearGraphics.OrangeBrush, 130, picHeight, format); picHeight += 16; } for (int i = 0; i < Gear.Levels.Count; i++) { var info = Gear.Levels[i]; g.DrawString("等级 " + info.Level + (i >= Gear.Levels.Count - 1 ? "(MAX)" : null), GearGraphics.ItemDetailFont, GearGraphics.GreenBrush2, 10, picHeight); picHeight += 16; foreach (var kv in info.BonusProps) { GearLevelInfo.Range range = kv.Value; string propString = ItemStringHelper.GetGearPropString(kv.Key, kv.Value.Min); if (range.Max != range.Min) { propString += " ~ " + kv.Value.Max + (propString.EndsWith("%") ? "%" : null); } g.DrawString(propString, GearGraphics.ItemDetailFont, Brushes.White, 10, picHeight); picHeight += 16; } if (info.Skills.Count > 0) { string title = string.Format("有 {2:P2}({0}/{1}) 的几率获得技能 :", info.Prob, info.ProbTotal, info.Prob * 1.0 / info.ProbTotal); g.DrawString(title, GearGraphics.ItemDetailFont, Brushes.White, 10, picHeight); picHeight += 16; foreach (var kv in info.Skills) { StringResult sr = null; if (this.StringLinker != null) { this.StringLinker.StringSkill.TryGetValue(kv.Key, out sr); } string text = string.Format("{0}({1}) +{2}", sr == null ? null : sr.Name, kv.Key, kv.Value); g.DrawString(text, GearGraphics.ItemDetailFont, GearGraphics.OrangeBrush, 16, picHeight); picHeight += 16; } } if (info.EquipmentSkills.Count > 0) { string title; if (info.Prob < info.ProbTotal) { title = string.Format("有 {2:P2}({0}/{1}) 的几率装备时获得技能 :", info.Prob, info.ProbTotal, info.Prob * 1.0 / info.ProbTotal); } else { title = "装备时获得技能 :"; } g.DrawString(title, GearGraphics.ItemDetailFont, Brushes.White, 10, picHeight); picHeight += 16; foreach (var kv in info.EquipmentSkills) { StringResult sr = null; if (this.StringLinker != null) { this.StringLinker.StringSkill.TryGetValue(kv.Key, out sr); } string text = string.Format("{0}({1}) Lv.{2}", sr == null ? null : sr.Name, kv.Key, kv.Value); g.DrawString(text, GearGraphics.ItemDetailFont, GearGraphics.OrangeBrush, 16, picHeight); picHeight += 16; } } if (info.Exp > 0) { g.DrawString("经验成长率 : " + info.Exp + "%", GearGraphics.ItemDetailFont, Brushes.White, 10, picHeight); picHeight += 16; } picHeight += 2; } } if (Gear.Seals != null) { if (levelOrSealed == null) { levelOrSealed = new Bitmap(261, DefaultPicHeight); g = Graphics.FromImage(levelOrSealed); } picHeight += 13; g.DrawString("封印解除属性", GearGraphics.ItemDetailFont, GearGraphics.GreenBrush2, 130, picHeight, format); picHeight += 16; for (int i = 0; i < Gear.Seals.Count; i++) { var info = Gear.Seals[i]; g.DrawString("等级 " + info.Level + (i >= Gear.Seals.Count - 1 ? "(MAX)" : null), GearGraphics.ItemDetailFont, GearGraphics.GreenBrush2, 10, picHeight); picHeight += 16; var props = this.IsCombineProperties ? Gear.CombineProperties(info.BonusProps) : info.BonusProps; foreach (var kv in props) { string propString = ItemStringHelper.GetGearPropString(kv.Key, kv.Value); g.DrawString(propString, GearGraphics.ItemDetailFont, Brushes.White, 10, picHeight); picHeight += 16; } if (info.HasIcon) { Bitmap icon = info.Icon.Bitmap ?? info.IconRaw.Bitmap; if (icon != null) { g.DrawString("图标 : ", GearGraphics.ItemDetailFont, Brushes.White, 10, picHeight + icon.Height / 2 - 6); g.DrawImage(icon, 52, picHeight); picHeight += icon.Height; } } if (info.Exp > 0) { g.DrawString("经验成长率 : " + info.Exp + "%", GearGraphics.ItemDetailFont, Brushes.White, 10, picHeight); picHeight += 16; } picHeight += 2; } } format.Dispose(); if (g != null) { g.Dispose(); picHeight += 13; } return levelOrSealed; } private Bitmap RenderGenesisSkills(out int picHeight) { Bitmap genesisBitmap = null; picHeight = 0; if (Gear.IsGenesisWeapon) { genesisBitmap = new Bitmap(261, DefaultPicHeight); Graphics g = Graphics.FromImage(genesisBitmap); picHeight = 13; foreach (var skillID in new[] { 80002632, 80002633 }) { string skillName; if (this.StringLinker?.StringSkill.TryGetValue(skillID, out var sr) ?? false && sr.Name != null) { skillName = sr.Name; } else { skillName = skillID.ToString(); } g.DrawString($"可使用<{skillName}>", GearGraphics.ItemDetailFont, GearGraphics.GreenBrush2, 10, picHeight); picHeight += 16; } picHeight += 9; g.Dispose(); } return genesisBitmap; } private void FillRect(Graphics g, TextureBrush brush, int x, int y0, int y1) { brush.ResetTransform(); brush.TranslateTransform(x, y0); g.FillRectangle(brush, x, y0, brush.Image.Width, y1 - y0); } private List GetGearAttributeString() { int value; List tags = new List(); if (Gear.Props.TryGetValue(GearPropType.only, out value) && value != 0) { tags.Add(ItemStringHelper.GetGearPropString(GearPropType.only, value)); } if (Gear.Props.TryGetValue(GearPropType.tradeBlock, out value) && value != 0) { tags.Add(ItemStringHelper.GetGearPropString(GearPropType.tradeBlock, value)); } if (Gear.Props.TryGetValue(GearPropType.mintable, out value) && value != 0) { tags.Add(ItemStringHelper.GetGearPropString(GearPropType.mintable, value)); } if (Gear.Props.TryGetValue(GearPropType.abilityTimeLimited, out value) && value != 0) { tags.Add(ItemStringHelper.GetGearPropString(GearPropType.abilityTimeLimited, value)); } if (Gear.Props.TryGetValue(GearPropType.equipTradeBlock, out value) && value != 0) { if (Gear.State == GearState.itemList) { tags.Add(ItemStringHelper.GetGearPropString(GearPropType.equipTradeBlock, value)); } else { string tradeBlock = ItemStringHelper.GetGearPropString(GearPropType.tradeBlock, 1); if (!tags.Contains(tradeBlock)) tags.Add(tradeBlock); } } if (Gear.Props.TryGetValue(GearPropType.accountSharable, out value) && value != 0) { tags.Add(ItemStringHelper.GetGearPropString(GearPropType.accountSharable, value)); } if (Gear.Props.TryGetValue(GearPropType.blockGoldHammer, out value) && value != 0) { tags.Add(ItemStringHelper.GetGearPropString(GearPropType.blockGoldHammer, value)); } if (Gear.Props.TryGetValue(GearPropType.noPotential, out value) && value != 0) { tags.Add(ItemStringHelper.GetGearPropString(GearPropType.noPotential, value)); } if (Gear.Props.TryGetValue(GearPropType.fixedPotential, out value) && value != 0) { tags.Add(ItemStringHelper.GetGearPropString(GearPropType.fixedPotential, value)); } if (Gear.Props.TryGetValue(GearPropType.onlyEquip, out value) && value != 0) { tags.Add(ItemStringHelper.GetGearPropString(GearPropType.onlyEquip, value)); } if (Gear.Props.TryGetValue(GearPropType.notExtend, out value) && value != 0) { tags.Add(ItemStringHelper.GetGearPropString(GearPropType.notExtend, value)); } return tags; } private Bitmap GetAlienStoneIcon() { if (Gear.AlienStoneSlot == null) { return Resource.ToolTip_Equip_AlienStone_Empty; } else { switch (Gear.AlienStoneSlot.Grade) { case AlienStoneGrade.Normal: return Resource.ToolTip_Equip_AlienStone_Normal; case AlienStoneGrade.Rare: return Resource.ToolTip_Equip_AlienStone_Rare; case AlienStoneGrade.Epic: return Resource.ToolTip_Equip_AlienStone_Epic; case AlienStoneGrade.Unique: return Resource.ToolTip_Equip_AlienStone_Unique; case AlienStoneGrade.Legendary: return Resource.ToolTip_Equip_AlienStone_Legendary; default: return null; } } } private void DrawGearReq(Graphics g, int x, int y) { int value; bool can; NumberType type; Size size; //需求等级 this.Gear.Props.TryGetValue(GearPropType.reqLevel, out value); { int reduceReq; if (this.Gear.Props.TryGetValue(GearPropType.reduceReq, out reduceReq)) { value = Math.Max(0, value - reduceReq); } } can = this.charStat == null || this.charStat.Level >= value; type = GetReqType(can, value); g.DrawImage(FindReqImage(type, "reqLEV", out size), x, y); DrawReqNum(g, value.ToString().PadLeft(3), (type == NumberType.Can ? NumberType.YellowNumber : type), x + 54, y, StringAlignment.Near); //需求人气 this.Gear.Props.TryGetValue(GearPropType.reqPOP, out value); can = this.charStat == null || this.charStat.Pop >= value; type = GetReqType(can, value); if (value > 0) { g.DrawImage(FindReqImage(type, "reqPOP", out size), x + 80, y); DrawReqNum(g, value.ToString("D3"), type, x + 80 + 54, y, StringAlignment.Near); } y += 15; //需求力量 this.Gear.Props.TryGetValue(GearPropType.reqSTR, out value); can = this.charStat == null || this.charStat.Strength.GetSum() >= value; type = GetReqType(can, value); g.DrawImage(FindReqImage(type, "reqSTR", out size), x, y); DrawReqNum(g, value.ToString("D3"), type, x + 54, y, StringAlignment.Near); //需求运气 this.Gear.Props.TryGetValue(GearPropType.reqLUK, out value); can = this.charStat == null || this.charStat.Luck.GetSum() >= value; type = GetReqType(can, value); g.DrawImage(FindReqImage(type, "reqLUK", out size), x + 80, y); DrawReqNum(g, value.ToString("D3"), type, x + 80 + 54, y, StringAlignment.Near); y += 9; //需求敏捷 this.Gear.Props.TryGetValue(GearPropType.reqDEX, out value); can = this.charStat == null || this.charStat.Dexterity.GetSum() >= value; type = GetReqType(can, value); g.DrawImage(FindReqImage(type, "reqDEX", out size), x, y); DrawReqNum(g, value.ToString("D3"), type, x + 54, y, StringAlignment.Near); //需求智力 this.Gear.Props.TryGetValue(GearPropType.reqINT, out value); can = this.charStat == null || this.charStat.Intelligence.GetSum() >= value; type = GetReqType(can, value); g.DrawImage(FindReqImage(type, "reqINT", out size), x + 80, y); DrawReqNum(g, value.ToString("D3"), type, x + 80 + 54, y, StringAlignment.Near); } private void DrawPropDiffEx(Graphics g, int x, int y) { int value; string numValue; //防御 g.DrawImage(Resource.UIToolTip_img_Item_Equip_Summary_icon_pdd, x, y); x += 62; DrawReqNum(g, "0", NumberType.LookAhead, x - 5, y + 6, StringAlignment.Far); ////魔防 //g.DrawImage(Resource.UIToolTip_img_Item_Equip_Summary_icon_mdd, x, y); //x += 62; //DrawReqNum(g, "0", NumberType.LookAhead, x - 5, y + 6, StringAlignment.Far); //boss伤 g.DrawImage(Resource.UIToolTip_img_Item_Equip_Summary_icon_bdr, x, y); x += 62; this.Gear.Props.TryGetValue(GearPropType.bdR, out value); numValue = (value > 0 ? "+ " : null) + value + " % "; DrawReqNum(g, numValue, NumberType.LookAhead, x - 5 + 3, y + 6, StringAlignment.Far); //无视防御 g.DrawImage(Resource.UIToolTip_img_Item_Equip_Summary_icon_igpddr, x, y); x += 62; this.Gear.Props.TryGetValue(GearPropType.imdR, out value); numValue = (value > 0 ? "+ " : null) + value + " % "; DrawReqNum(g, numValue, NumberType.LookAhead, x - 5 - 1, y + 6, StringAlignment.Far); } private void DrawJobReq(Graphics g, ref int picH) { int value; string extraReq = ItemStringHelper.GetExtraJobReqString(Gear.type); if (extraReq == null && Gear.Props.TryGetValue(GearPropType.reqSpecJob, out value)) { extraReq = ItemStringHelper.GetExtraJobReqString(value); } if (extraReq == null && Gear.ReqSpecJobs.Count > 0) { // apply req order fix for CMS only int[] specJobsList1 = new[] { 2, 22, 12, 32, 172 }; if (new HashSet(specJobsList1).SetEquals(Gear.ReqSpecJobs)) { extraReq = ItemStringHelper.GetExtraJobReqString(specJobsList1); } else { extraReq = ItemStringHelper.GetExtraJobReqString(Gear.ReqSpecJobs); } } Image jobImage = null; int extraReqWidth = 216; if (extraReq == null) { jobImage = Resource.UIToolTip_img_Item_Equip_Job_normal; } else { // measure jobReq desc // Actually we use GearGraphics.DrawPlainText to render extraReq, the meatured lines may not accurate. using var extraReqFmt = new StringFormat(); extraReqFmt.Alignment = StringAlignment.Center; g.MeasureString(extraReq, GearGraphics.ItemDetailFont, new SizeF(extraReqWidth, short.MaxValue), extraReqFmt, out _, out var lines); jobImage = lines == 1 ? Resource.UIToolTip_img_Item_Equip_Job_expand : Resource.UIToolTip_img_Item_Equip_Job_expand2; } g.DrawImage(jobImage, 12, picH); int reqJob; Gear.Props.TryGetValue(GearPropType.reqJob, out reqJob); int[] origin = new int[] { 9, 4, 42, 4, 78, 5, 124, 4, 165, 5, 200, 5 }; int[] origin2 = new int[] { 10, 6, 44, 6, 79, 6, 126, 6, 166, 6, 201, 6 }; for (int i = 0; i <= 5; i++) { bool enable; if (i == 0) { enable = reqJob <= 0; if (reqJob == 0) reqJob = 0b11111; if (reqJob == -1) reqJob = 0b00000; } else { enable = (reqJob & (1 << (i - 1))) != 0; } if (enable) { enable = this.charStat == null || Character.CheckJobReq(this.charStat.Job, i); Image jobImage2 = Resource.ResourceManager.GetObject("UIToolTip_img_Item_Equip_Job_" + (enable ? "enable" : "disable") + "_" + i.ToString()) as Image; if (jobImage != null) { if (enable) g.DrawImage(jobImage2, 12 + origin[i * 2], picH + origin[i * 2 + 1]); else g.DrawImage(jobImage2, 12 + origin2[i * 2], picH + origin2[i * 2 + 1]); } } } if (extraReq != null) { // ignore yaxis. int tempY = picH + 24; GearGraphics.DrawPlainText(g, extraReq, GearGraphics.ItemDetailFont, GearGraphics.OrangeBrush3Color, 130 - extraReqWidth / 2, 130 + extraReqWidth / 2, ref tempY, 16, Text.TextAlignment.Center); } picH += jobImage.Height + 9; } private Image FindReqImage(NumberType type, string req, out Size size) { Image image = Resource.ResourceManager.GetObject("UIToolTip_img_Item_Equip_" + type.ToString() + "_" + req) as Image; if (image != null) size = image.Size; else size = Size.Empty; return image; } private void DrawStar(Graphics g, ref int picH) { if (Gear.Star > 0) { int totalWidth = Gear.Star * 10 + (Gear.Star / 5 - 1) * 6; int dx = 130 - totalWidth / 2; for (int i = 0; i < Gear.Star; i++) { g.DrawImage(Resource.UIToolTip_img_Item_Equip_Star_Star, dx, picH); dx += 10; if (i > 0 && i % 5 == 4) { dx += 6; } } picH += 18; } } private void DrawStar2(Graphics g, ref int picH) { int maxStar = Gear.GetMaxStar(); if (maxStar > 0) { for (int i = 0; i < maxStar; i += 15) { int starLine = Math.Min(maxStar - i, 15); int totalWidth = starLine * 10 + (starLine / 5 - 1) * 6; int dx = 130 - totalWidth / 2; for (int j = 0; j < starLine; j++) { g.DrawImage((i + j < Gear.Star) ? Resource.UIToolTip_img_Item_Equip_Star_Star : Resource.UIToolTip_img_Item_Equip_Star_Star0, dx, picH); dx += 10; if (j > 0 && j % 5 == 4) { dx += 6; } } picH += 18; } picH -= 1; } } private NumberType GetReqType(bool can, int reqValue) { if (reqValue <= 0) return NumberType.Disabled; if (can) return NumberType.Can; else return NumberType.Cannot; } private void DrawReqNum(Graphics g, string numString, NumberType type, int x, int y, StringAlignment align) { if (g == null || numString == null || align == StringAlignment.Center) return; int spaceWidth = type == NumberType.LookAhead ? 3 : 6; bool near = align == StringAlignment.Near; for (int i = 0; i < numString.Length; i++) { char c = near ? numString[i] : numString[numString.Length - i - 1]; Image image = null; Point origin = Point.Empty; switch (c) { case ' ': break; case '+': image = Resource.ResourceManager.GetObject("UIToolTip_img_Item_Equip_" + type.ToString() + "_" + "plus") as Image; break; case '-': image = Resource.ResourceManager.GetObject("UIToolTip_img_Item_Equip_" + type.ToString() + "_" + "minus") as Image; origin.Y = 3; break; case '%': image = Resource.ResourceManager.GetObject("UIToolTip_img_Item_Equip_" + type.ToString() + "_" + "percent") as Image; break; default: if ('0' <= c && c <= '9') { image = Resource.ResourceManager.GetObject("UIToolTip_img_Item_Equip_" + type.ToString() + "_" + c) as Image; if (c == '1' && type == NumberType.LookAhead) { origin.X = 1; } } break; } if (image != null) { if (near) { g.DrawImage(image, x + origin.X, y + origin.Y); x += image.Width + origin.X + 1; } else { x -= image.Width + origin.X; g.DrawImage(image, x + origin.X, y + origin.Y); x -= 1; } } else //空格补位 { x += spaceWidth * (near ? 1 : -1); } } } private Image GetAdditionalOptionIcon(GearGrade grade) { switch (grade) { default: case GearGrade.B: return Resource.AdditionalOptionTooltip_rare; case GearGrade.A: return Resource.AdditionalOptionTooltip_epic; case GearGrade.S: return Resource.AdditionalOptionTooltip_unique; case GearGrade.SS: return Resource.AdditionalOptionTooltip_legendary; } } private bool TryGetMedalResource(int medalTag, out Wz_Node resNode) { resNode = PluginBase.PluginManager.FindWz("UI/NameTag.img/medal/" + medalTag); return resNode != null; } private enum NumberType { Can, Cannot, Disabled, LookAhead, YellowNumber, } } } ================================================ FILE: WzComparerR2/CharaSimControl/ItemMouseEventArgs.cs ================================================ using System; using System.Collections.Generic; using System.Text; using System.Windows.Forms; using WzComparerR2.CharaSim; namespace WzComparerR2.CharaSimControl { public class ItemMouseEventArgs : MouseEventArgs { public ItemMouseEventArgs(MouseEventArgs e, ItemBase item) : this(e.Button, e.Clicks, e.X, e.Y, e.Delta, item) { } public ItemMouseEventArgs(MouseButtons button, int clicks, int x, int y, int delta, ItemBase item) : base(button, clicks, x, y, delta) { this.item = item; } private ItemBase item; public ItemBase Item { get { return item; } set { item = value; } } } } ================================================ FILE: WzComparerR2/CharaSimControl/ItemMouseEventHandler.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace WzComparerR2.CharaSimControl { public delegate void ItemMouseEventHandler(object sender, ItemMouseEventArgs e); } ================================================ FILE: WzComparerR2/CharaSimControl/ItemTooltipRender.cs ================================================ using System; using System.Collections.Generic; using System.Text; using System.Drawing; using System.Drawing.Drawing2D; using CharaSimResource; using WzComparerR2.Common; using WzComparerR2.CharaSim; namespace WzComparerR2.CharaSimControl { public class ItemTooltipRender:TooltipRender { public ItemTooltipRender() { } private Item item; public Item Item { get { return item; } set { item = value; } } public override Bitmap Render() { if (this.item == null) { return null; } int picHeight, iconY; Bitmap originBmp = renderItem(out picHeight, out iconY); Bitmap tooltip = new Bitmap(290, picHeight); Graphics g = Graphics.FromImage(tooltip); //复制图像 g.FillRectangle(GearGraphics.GearBackBrush, 2, 2, 286, picHeight - 4); g.CompositingMode = CompositingMode.SourceCopy; g.FillRectangle(GearGraphics.GearIconBackBrush, 14, iconY, 68, 68); g.CompositingMode = CompositingMode.SourceOver; g.DrawImage(originBmp, 0, 0, new Rectangle(0, 0, 290, picHeight - 2), GraphicsUnit.Pixel); //绘制外边框 g.DrawLines(GearGraphics.GearBackPen, GearGraphics.GetBorderPath(0, 290, picHeight)); //GearGraphics.DrawGearDetailNumber(g, 2, 2, item.ItemID.ToString("d8"), true); g.Dispose(); return tooltip; } private Bitmap renderItem(out int picHeight, out int iconY) { Bitmap tooltip = new Bitmap(290, DefaultPicHeight); Graphics g = Graphics.FromImage(tooltip); StringFormat format = new StringFormat(); long value; format.Alignment = StringAlignment.Center; picHeight = 10; iconY = 32; //物品标题 StringResult sr; if (StringLinker == null || !StringLinker.StringItem.TryGetValue(item.ItemID, out sr)) { sr = new StringResult(); sr.Name = "(null)"; } string gearName = sr.Name; g.DrawString(gearName, GearGraphics.ItemNameFont, Brushes.White, 145, picHeight, format);//绘制装备名称 picHeight += 21; string attr = GetItemAttributeString(); if (!string.IsNullOrEmpty(attr)) { g.DrawString(attr, GearGraphics.ItemDetailFont, GearGraphics.GearNameBrushC, 145, picHeight, format); iconY += 19; } //绘制图标 if (item.Icon.Bitmap != null) { g.DrawImage(GearGraphics.EnlargeBitmap(item.Icon.Bitmap), 14 + (1 - item.Icon.Origin.X) * 2, iconY + (33 - item.Icon.Origin.Y) * 2); } if (item.Cash) { g.DrawImage(GearGraphics.EnlargeBitmap(Resource.CashItem_0), 14 + 68 - 26, iconY + 68 - 26); } picHeight = iconY; if (item.Props.TryGetValue(ItemPropType.reqLevel, out value)) { g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit; g.PixelOffsetMode = PixelOffsetMode.HighQuality; g.DrawString("要求等级 : " + value, GearGraphics.ItemReqLevelFont, Brushes.White, 92, picHeight); picHeight += 15; g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.SystemDefault; } else { picHeight += 3; } if (!string.IsNullOrEmpty(sr.Desc)) { GearGraphics.DrawString(g, sr.Desc + sr.AutoDesc, GearGraphics.ItemDetailFont, 92, 272, ref picHeight, 16); } if (item.Props.TryGetValue(ItemPropType.tradeAvailable, out value) && value > 0) { attr = ItemStringHelper.GetItemPropString(ItemPropType.tradeAvailable, value); if (!string.IsNullOrEmpty(attr)) GearGraphics.DrawString(g, "#c" + attr + "#", GearGraphics.ItemDetailFont, 92, 272, ref picHeight, 16); } picHeight = Math.Max(iconY + 84, iconY + 16 * (int)Math.Ceiling((picHeight - iconY) / 16.0)); return tooltip; } private string GetItemAttributeString() { long value; List tags = new List(); if (item.Props.TryGetValue(ItemPropType.quest, out value) && value != 0) { tags.Add(ItemStringHelper.GetItemPropString(ItemPropType.quest, value)); } if (item.Props.TryGetValue(ItemPropType.pquest, out value) && value != 0) { tags.Add(ItemStringHelper.GetItemPropString(ItemPropType.pquest, value)); } if (item.Props.TryGetValue(ItemPropType.only, out value) && value != 0) { tags.Add(ItemStringHelper.GetItemPropString(ItemPropType.only, value)); } if (item.Props.TryGetValue(ItemPropType.tradeBlock, out value) && value != 0) { tags.Add(ItemStringHelper.GetItemPropString(ItemPropType.tradeBlock, value)); } if (item.Props.TryGetValue(ItemPropType.accountSharable, out value) && value != 0) { tags.Add(ItemStringHelper.GetItemPropString(ItemPropType.accountSharable, value)); } return tags.Count > 0 ? string.Join(", ", tags.ToArray()) : null; } } } ================================================ FILE: WzComparerR2/CharaSimControl/ItemTooltipRender2.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Drawing; using System.Drawing.Drawing2D; using Resource = CharaSimResource.Resource; using WzComparerR2.PluginBase; using WzComparerR2.WzLib; using WzComparerR2.Common; using WzComparerR2.CharaSim; namespace WzComparerR2.CharaSimControl { public class ItemTooltipRender2 : TooltipRender { public ItemTooltipRender2() { } private Item item; public Item Item { get { return item; } set { item = value; } } public override object TargetItem { get { return this.item; } set { this.item = value as Item; } } public bool LinkRecipeInfo { get; set; } public bool LinkRecipeItem { get; set; } public bool ShowNickTag { get; set; } public bool ShowDamageSkin { get; set; } public bool ShowDamageSkinID { get; set; } public bool UseMiniSizeDamageSkin { get; set; } public bool AlwaysUseMseaFormatDamageSkin { get; set; } public bool DisplayUnitOnSingleLine { get; set; } public long DamageSkinNumber { get; set; } public TooltipRender LinkRecipeInfoRender { get; set; } public TooltipRender LinkRecipeGearRender { get; set; } public TooltipRender LinkRecipeItemRender { get; set; } public TooltipRender FamiliarRender { get; set; } public TooltipRender SetItemRender { get; set; } public TooltipRender DamageSkinRender { get; set; } public override Bitmap Render() { if (this.item == null) { return null; } //绘制道具 int picHeight; Bitmap itemBmp = RenderItem(out picHeight); List recipeInfoBmps = new(); List recipeItemBmps = new(); Bitmap setItemBmp = null; //图纸相关 if (this.item.Recipes.Count > 0) { foreach (int recipeID in this.item.Recipes) { int recipeSkillID = recipeID / 10000; Recipe recipe = null; //寻找配方 Wz_Node recipeNode = PluginBase.PluginManager.FindWz(string.Format(@"Skill\Recipe_{0}.img\{1}", recipeSkillID, recipeID)); if (recipeNode != null) { recipe = Recipe.CreateFromNode(recipeNode); } //生成配方图像 if (recipe != null) { if (this.LinkRecipeInfo) { recipeInfoBmps.Add(RenderLinkRecipeInfo(recipe)); } if (this.LinkRecipeItem) { int itemID = recipe.MainTargetItemID; int itemIDClass = itemID / 1000000; if (itemIDClass == 1) //通过ID寻找装备 { Wz_Node charaWz = PluginManager.FindWz(Wz_Type.Character); if (charaWz != null) { string imgName = itemID.ToString("d8") + ".img"; foreach (Wz_Node node0 in charaWz.Nodes) { Wz_Node imgNode = node0.FindNodeByPath(imgName, true); if (imgNode != null) { Gear gear = Gear.CreateFromNode(imgNode, path => PluginManager.FindWz(path)); if (gear != null) { recipeItemBmps.Add(RenderLinkRecipeGear(gear)); } break; } } } } else if (itemIDClass >= 2 && itemIDClass <= 5) //通过ID寻找道具 { Wz_Node itemWz = PluginManager.FindWz(Wz_Type.Item); if (itemWz != null) { string imgClass = (itemID / 10000).ToString("d4") + ".img\\" + itemID.ToString("d8"); foreach (Wz_Node node0 in itemWz.Nodes) { Wz_Node imgNode = node0.FindNodeByPath(imgClass, true); if (imgNode != null) { Item item = Item.CreateFromNode(imgNode, PluginManager.FindWz); if (item != null) { recipeItemBmps.Add(RenderLinkRecipeItem(item)); } break; } } } } } } } } if (this.item.Props.TryGetValue(ItemPropType.setItemID, out long setID)) { SetItem setItem; if (CharaSimLoader.LoadedSetItems.TryGetValue((int)setID, out setItem)) { setItemBmp = RenderSetItem(setItem); } } if (this.item.DamageSkinID != null && ShowDamageSkin) { DamageSkin damageSkin = DamageSkin.CreateFromNode(PluginManager.FindWz($@"Etc\DamageSkin.img\{item.DamageSkinID}"), PluginManager.FindWz) ?? DamageSkin.CreateFromNode(PluginManager.FindWz($@"Effect\DamageSkin.img\{item.DamageSkinID}"), PluginManager.FindWz); if (damageSkin != null) { setItemBmp = RenderDamageSkin(damageSkin); } } if (this.item.FamiliarID != null) { Familiar familiar = Familiar.CreateFromNode(PluginManager.FindWz($@"Character\Familiar\{item.FamiliarID}.img"), PluginManager.FindWz); if (familiar != null) { return RenderFamiliar(familiar); } } //计算布局 Size totalSize = new Size(itemBmp.Width, picHeight); Point recipeInfoOrigin = Point.Empty; Point recipeItemOrigin = Point.Empty; Point setItemOrigin = Point.Empty; if (recipeItemBmps.Count > 0) { // layout: // item | recipeItem // recipeInfo | recipeItemOrigin.X = totalSize.Width; totalSize.Width += recipeItemBmps.Max(bmp => bmp.Width); if (recipeInfoBmps.Count > 0) { recipeInfoOrigin.X = itemBmp.Width - recipeInfoBmps.Max(bmp => bmp.Width); recipeInfoOrigin.Y = picHeight; totalSize.Height = Math.Max(picHeight + recipeInfoBmps.Sum(bmp => bmp.Height), recipeItemBmps.Sum(bmp => bmp.Height)); } else { totalSize.Height = Math.Max(picHeight, recipeItemBmps.Sum(bmp => bmp.Height)); } } else if (recipeInfoBmps.Count > 0) { // layout: // item | recipeInfo totalSize.Width += recipeInfoBmps.Max(bmp => bmp.Width); totalSize.Height = Math.Max(picHeight, recipeInfoBmps.Sum(bmp => bmp.Height)); recipeInfoOrigin.X = itemBmp.Width; } if (setItemBmp != null) { setItemOrigin = new Point(totalSize.Width, 0); totalSize.Width += setItemBmp.Width; totalSize.Height = Math.Max(totalSize.Height, setItemBmp.Height); } //开始绘制 Bitmap tooltip = new Bitmap(totalSize.Width, totalSize.Height); Graphics g = Graphics.FromImage(tooltip); if (itemBmp != null) { //绘制背景区域 GearGraphics.DrawNewTooltipBack(g, 0, 0, itemBmp.Width, picHeight); //复制图像 g.DrawImage(itemBmp, 0, 0, new Rectangle(0, 0, itemBmp.Width, picHeight), GraphicsUnit.Pixel); //左上角 g.DrawImage(Resource.UIToolTip_img_Item_Frame2_cover, 3, 3); if (this.ShowObjectID) { GearGraphics.DrawGearDetailNumber(g, 3, 3, item.ItemID.ToString("d8"), true); } } //绘制配方 if (recipeInfoBmps.Count > 0) { for (int i = 0, y = recipeInfoOrigin.Y; i < recipeInfoBmps.Count; i++) { g.DrawImage(recipeInfoBmps[i], recipeInfoOrigin.X, y, new Rectangle(Point.Empty, recipeInfoBmps[i].Size), GraphicsUnit.Pixel); y += recipeInfoBmps[i].Height; } } //绘制产出道具 if (recipeItemBmps.Count > 0) { for (int i = 0, y = recipeItemOrigin.Y; i < recipeItemBmps.Count; i++) { g.DrawImage(recipeItemBmps[i], recipeItemOrigin.X, y, new Rectangle(Point.Empty, recipeItemBmps[i].Size), GraphicsUnit.Pixel); y += recipeItemBmps[i].Height; } } //绘制套装 if (setItemBmp != null) { g.DrawImage(setItemBmp, setItemOrigin.X, setItemOrigin.Y, new Rectangle(Point.Empty, setItemBmp.Size), GraphicsUnit.Pixel); } if (itemBmp != null) itemBmp.Dispose(); if (recipeInfoBmps.Count > 0) recipeInfoBmps.ForEach(bmp => bmp.Dispose()); if (recipeItemBmps.Count > 0) recipeItemBmps.ForEach(bmp => bmp.Dispose()); if (setItemBmp != null) setItemBmp.Dispose(); g.Dispose(); return tooltip; } private Bitmap RenderItem(out int picH) { StringFormat format = (StringFormat)StringFormat.GenericDefault.Clone(); long value; //物品标题 StringResult sr; if (StringLinker == null || !StringLinker.StringItem.TryGetValue(item.ItemID, out sr)) { sr = new StringResult(); sr.Name = "(null)"; } // calculate image width const int DefualtWidth = 290; int tooltipWidth = DefualtWidth; if (int.TryParse(sr["fixWidth"], out int fixWidth) && fixWidth > 0) { tooltipWidth = fixWidth; } else { using (Bitmap dummyImg = new Bitmap(1, 1)) using (Graphics tempG = Graphics.FromImage(dummyImg)) { SizeF titleSize = tempG.MeasureString(sr.Name, GearGraphics.ItemNameFont2, short.MaxValue, format); titleSize.Width += 12 * 2; if (titleSize.Width > DefualtWidth) { tooltipWidth = (int)Math.Ceiling(titleSize.Width); } } } Bitmap tooltip = new Bitmap(tooltipWidth, DefaultPicHeight); Graphics g = Graphics.FromImage(tooltip); picH = 10; //绘制标题 bool hasPart2 = false; format.Alignment = StringAlignment.Center; g.DrawString(sr.Name, GearGraphics.ItemNameFont2, Brushes.White, tooltip.Width / 2, picH, format); picH += 22; //额外特性 string attr = GetItemAttributeString(); if (!string.IsNullOrEmpty(attr)) { g.DrawString(attr, GearGraphics.ItemDetailFont, GearGraphics.GearNameBrushC, tooltip.Width / 2, picH, format); if (attr.Contains("\n")) { picH += 31; } else { picH += 19; } hasPart2 = true; } string expireTime = null; if (item.Props.TryGetValue(ItemPropType.permanent, out value) && value != 0) { expireTime = ItemStringHelper.GetItemPropString(ItemPropType.permanent, value); } else if (item.Props.TryGetValue(ItemPropType.life, out value) && value > 0) { DateTime time = DateTime.Now.AddDays(value); expireTime = time.ToString("魔法时间:到 yyyy年 M月 d日 H时"); } if (!string.IsNullOrEmpty(expireTime)) { g.DrawString(expireTime, GearGraphics.ItemDetailFont, Brushes.White, tooltip.Width / 2, picH, format); picH += 16; hasPart2 = true; } if (hasPart2) { picH += 1; } //装备限时 if (item.TimeLimited) { DateTime time = DateTime.Now.AddDays(7d); string expireStr = time.ToString("到yyyy年 M月 d日 H时 m分可以用"); g.DrawString(expireStr, GearGraphics.ItemDetailFont, Brushes.White, tooltip.Width / 2, picH, format); picH += 21; } //绘制图标 int iconY = picH; int iconX = 14; g.DrawImage(Resource.UIToolTip_img_Item_ItemIcon_base, iconX, picH); if (item.Icon.Bitmap != null) { g.DrawImage(GearGraphics.EnlargeBitmap(item.Icon.Bitmap), iconX + 6 + (1 - item.Icon.Origin.X) * 2, picH + 6 + (33 - item.Icon.Origin.Y) * 2); } if (item.Cash) { Bitmap cashImg = null; if (item.Props.TryGetValue(ItemPropType.wonderGrade, out value) && value > 0) { string resKey = $"CashShop_img_CashItem_label_{value + 3}"; cashImg = Resource.ResourceManager.GetObject(resKey) as Bitmap; } if (cashImg == null) //default cashImg { cashImg = Resource.CashItem_0; } g.DrawImage(GearGraphics.EnlargeBitmap(cashImg), iconX + 6 + 68 - 26, picH + 6 + 68 - 26); } g.DrawImage(Resource.UIToolTip_img_Item_ItemIcon_cover, iconX + 4, picH + 4); //绘制左上角cover if (item.Props.TryGetValue(ItemPropType.reqLevel, out value)) { g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit; g.PixelOffsetMode = PixelOffsetMode.HighQuality; g.DrawString("要求等级 :" + value, GearGraphics.ItemReqLevelFont, Brushes.White, 97, picH); picH += 15; g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.SystemDefault; } else { picH += 3; } int right = tooltip.Width - 18; string desc = null; if (item.Level > 0) { desc += $"[LV.{item.Level}] "; } desc += sr.Desc; if (!string.IsNullOrEmpty(desc)) { GearGraphics.DrawString(g, desc, GearGraphics.ItemDetailFont2, 100, right, ref picH, 16); } if (!string.IsNullOrEmpty(sr.AutoDesc)) { GearGraphics.DrawString(g, sr.AutoDesc, GearGraphics.ItemDetailFont2, 100, right, ref picH, 16); } if (item.Props.TryGetValue(ItemPropType.tradeAvailable, out value) && value > 0) { attr = ItemStringHelper.GetItemPropString(ItemPropType.tradeAvailable, value); if (!string.IsNullOrEmpty(attr)) GearGraphics.DrawString(g, "#c" + attr + "#", GearGraphics.ItemDetailFont2, 100, right, ref picH, 16); } if (item.Specs.TryGetValue(ItemSpecType.recipeValidDay, out value) && value > 0) { GearGraphics.DrawString(g, "(可制作时间:" + value + "天)", GearGraphics.ItemDetailFont, 100, right, ref picH, 16); } if (item.Specs.TryGetValue(ItemSpecType.recipeUseCount, out value) && value > 0) { GearGraphics.DrawString(g, "(可制作次数:" + value + "次)", GearGraphics.ItemDetailFont, 100, right, ref picH, 16); } picH += 3; Wz_Node nickResNode = null; bool willDrawNickTag = this.ShowNickTag && this.Item.Props.TryGetValue(ItemPropType.nickTag, out value) && this.TryGetNickResource(value, out nickResNode); string descLeftAlign = sr["desc_leftalign"]; if (!string.IsNullOrEmpty(descLeftAlign) || item.Sample.Bitmap != null || willDrawNickTag) { if (picH < iconY + 84) { picH = iconY + 84; } if (!string.IsNullOrEmpty(descLeftAlign)) { picH += 12; GearGraphics.DrawString(g, descLeftAlign, GearGraphics.ItemDetailFont, 14, right, ref picH, 16); } if (item.Sample.Bitmap != null) { g.DrawImage(item.Sample.Bitmap, (tooltip.Width - item.Sample.Bitmap.Width) / 2, picH); picH += item.Sample.Bitmap.Height; picH += 2; } if (nickResNode != null) { //获取称号名称 string nickName; string nickWithQR = sr["nickWithQR"]; if (nickWithQR != null) { string qrDefault = sr["qrDefault"] ?? string.Empty; nickName = Regex.Replace(nickWithQR, "#qr.*?#", qrDefault); } else { nickName = sr.Name; } GearGraphics.DrawNameTag(g, nickResNode, nickName, tooltip.Width, ref picH); picH += 4; } } //绘制配方需求 if (item.Recipes.Count > 0) { long reqSkill, reqSkillLevel; if (!item.Specs.TryGetValue(ItemSpecType.reqSkill, out reqSkill)) { reqSkill = item.Recipes[0] / 10000 * 10000; } if (!item.Specs.TryGetValue(ItemSpecType.reqSkillLevel, out reqSkillLevel)) { reqSkillLevel = 1; } picH = Math.Max(picH, iconY + 107); g.DrawLine(Pens.White, 6, picH, 283, picH);//分割线 picH += 10; g.DrawString("<使用限制条件>", GearGraphics.ItemDetailFont, GearGraphics.SetItemNameBrush, 8, picH); picH += 17; //技能标题 if (StringLinker == null || !StringLinker.StringSkill.TryGetValue((int)reqSkill, out sr)) { sr = new StringResult(); sr.Name = "(null)"; } g.DrawString(string.Format("· {0}{1}级以上", sr.Name, reqSkillLevel), GearGraphics.ItemDetailFont, GearGraphics.SetItemNameBrush, 13, picH); picH += 16; picH += 6; } picH = Math.Max(iconY + 94, picH + 6); return tooltip; } private string GetItemAttributeString() { long value; List tags = new List(); if (item.Props.TryGetValue(ItemPropType.quest, out value) && value != 0) { tags.Add(ItemStringHelper.GetItemPropString(ItemPropType.quest, value)); } if (item.Props.TryGetValue(ItemPropType.pquest, out value) && value != 0) { tags.Add(ItemStringHelper.GetItemPropString(ItemPropType.pquest, value)); } if (item.Props.TryGetValue(ItemPropType.only, out value) && value != 0) { tags.Add(ItemStringHelper.GetItemPropString(ItemPropType.only, value)); } if (item.Props.TryGetValue(ItemPropType.tradeBlock, out value) && value != 0) { tags.Add(ItemStringHelper.GetItemPropString(ItemPropType.tradeBlock, value)); } if (item.Props.TryGetValue(ItemPropType.accountSharable, out value) && value != 0) { tags.Add(ItemStringHelper.GetItemPropString(ItemPropType.accountSharable, value)); } if (item.Props.TryGetValue(ItemPropType.accountSharableAfterExchange, out value) && value != 0) { tags.Add(ItemStringHelper.GetItemPropString(ItemPropType.accountSharableAfterExchange, value)); } if (item.Props.TryGetValue(ItemPropType.mintable, out value)) { tags.Add(ItemStringHelper.GetItemPropString(ItemPropType.mintable, value)); } return tags.Count > 0 ? string.Join(", ", tags.ToArray()) : null; } private Bitmap RenderDamageSkin(DamageSkin damageSkin) { TooltipRender renderer = this.DamageSkinRender; if (renderer == null) { DamageSkinTooltipRender defaultRenderer = new DamageSkinTooltipRender(); defaultRenderer.StringLinker = this.StringLinker; defaultRenderer.ShowObjectID = this.ShowDamageSkinID; defaultRenderer.UseMiniSize = this.UseMiniSizeDamageSkin; defaultRenderer.AlwaysUseMseaFormat = this.AlwaysUseMseaFormatDamageSkin; defaultRenderer.DisplayUnitOnSingleLine = this.DisplayUnitOnSingleLine; defaultRenderer.DamageSkinNumber = this.DamageSkinNumber; renderer = defaultRenderer; defaultRenderer.DamageSkin = damageSkin; } renderer.TargetItem = damageSkin; return renderer.Render(); } private Bitmap RenderFamiliar(Familiar familiar) { TooltipRender renderer = this.FamiliarRender; if (renderer == null) { FamiliarTooltipRender defaultRenderer = new FamiliarTooltipRender(); defaultRenderer.StringLinker = this.StringLinker; defaultRenderer.ShowObjectID = this.ShowObjectID; defaultRenderer.AllowOutOfBounds = false; defaultRenderer.ItemID = this.item.ItemID; defaultRenderer.FamiliarTier = this.item.Grade; defaultRenderer.UseAssembleUI = false; renderer = defaultRenderer; } renderer.TargetItem = familiar; return renderer.Render(); } private Bitmap RenderLinkRecipeInfo(Recipe recipe) { TooltipRender renderer = this.LinkRecipeInfoRender; if (renderer == null) { RecipeTooltipRender defaultRenderer = new RecipeTooltipRender(); defaultRenderer.StringLinker = this.StringLinker; defaultRenderer.ShowObjectID = false; renderer = defaultRenderer; } renderer.TargetItem = recipe; return renderer.Render(); } private Bitmap RenderLinkRecipeGear(Gear gear) { TooltipRender renderer = this.LinkRecipeGearRender; if (renderer == null) { GearTooltipRender2 defaultRenderer = new GearTooltipRender2(); defaultRenderer.StringLinker = this.StringLinker; defaultRenderer.ShowObjectID = false; renderer = defaultRenderer; } renderer.TargetItem = gear; return renderer.Render(); } private Bitmap RenderLinkRecipeItem(Item item) { TooltipRender renderer = this.LinkRecipeItemRender; if (renderer == null) { ItemTooltipRender2 defaultRenderer = new ItemTooltipRender2(); defaultRenderer.StringLinker = this.StringLinker; defaultRenderer.ShowObjectID = false; renderer = defaultRenderer; } renderer.TargetItem = item; return renderer.Render(); } private Bitmap RenderSetItem(SetItem setItem) { TooltipRender renderer = this.SetItemRender; if (renderer == null) { var defaultRenderer = new SetItemTooltipRender(); defaultRenderer.StringLinker = this.StringLinker; defaultRenderer.ShowObjectID = false; renderer = defaultRenderer; } renderer.TargetItem = setItem; return renderer.Render(); } private bool TryGetNickResource(long nickTag, out Wz_Node resNode) { resNode = PluginBase.PluginManager.FindWz("UI/NameTag.img/nick/" + nickTag); return resNode != null; } } } ================================================ FILE: WzComparerR2/CharaSimControl/MobTooltipRenderer.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Drawing; using System.Drawing.Imaging; using System.Text.RegularExpressions; using WzComparerR2.CharaSim; using WzComparerR2.Common; using static WzComparerR2.CharaSimControl.RenderHelper; namespace WzComparerR2.CharaSimControl { public class MobTooltipRenderer : TooltipRender { public MobTooltipRenderer() { } public override object TargetItem { get { return this.MobInfo; } set { this.MobInfo = value as Mob; } } public Mob MobInfo { get; set; } public override Bitmap Render() { if (MobInfo == null) { return null; } Bitmap bmp = new Bitmap(1, 1, PixelFormat.Format32bppArgb); Graphics g = Graphics.FromImage(bmp); //预绘制 List titleBlocks = new List(); if (MobInfo.ID > -1) { string mobName = GetMobName(MobInfo.ID); var block = PrepareText(g, mobName ?? "(null)", GearGraphics.ItemNameFont2, Brushes.White, 0, 0); titleBlocks.Add(block); block = PrepareText(g, "ID:" + MobInfo.ID, GearGraphics.ItemDetailFont, Brushes.White, block.Size.Width + 4, 4); titleBlocks.Add(block); } List propBlocks = new List(); int picY = 0; StringBuilder sbExt = new StringBuilder(); if (MobInfo.Boss) { sbExt.Append("Boss "); } if (MobInfo.Undead) { sbExt.Append("不死系 "); } if (MobInfo.FirstAttack) { sbExt.Append("主动攻击 "); } if (!MobInfo.BodyAttack) { sbExt.Append("无触碰伤害 "); } if (MobInfo.DamagedByMob) { sbExt.Append("只受怪物伤害 "); } if (MobInfo.Invincible) { sbExt.Append("无敌 "); } if (MobInfo.NotAttack) { sbExt.Append("无法攻击 "); } if (MobInfo.FixedDamage > 0) { sbExt.Append("固定伤害" + MobInfo.FixedDamage + " "); } if (sbExt.Length > 1) { sbExt.Remove(sbExt.Length - 1, 1); propBlocks.Add(PrepareText(g, sbExt.ToString(), GearGraphics.ItemDetailFont, Brushes.GreenYellow, 0, picY)); picY += 16; } if (MobInfo.RemoveAfter > 0) { propBlocks.Add(PrepareText(g, "出生" + MobInfo.RemoveAfter + "秒后自动消失", GearGraphics.ItemDetailFont, Brushes.GreenYellow, 0, picY)); picY += 16; } propBlocks.Add(PrepareText(g, "Level: " + MobInfo.Level, GearGraphics.ItemDetailFont, Brushes.White, 0, picY)); string hpNum = !string.IsNullOrEmpty(MobInfo.FinalMaxHP) ? this.AddCommaSeparators(MobInfo.FinalMaxHP) : MobInfo.MaxHP.ToString("N0"); propBlocks.Add(PrepareText(g, "HP: " + hpNum, GearGraphics.ItemDetailFont, Brushes.White, 0, picY += 16)); string mpNum = !string.IsNullOrEmpty(MobInfo.FinalMaxMP) ? this.AddCommaSeparators(MobInfo.FinalMaxMP) : MobInfo.MaxMP.ToString("N0"); propBlocks.Add(PrepareText(g, "MP: " + mpNum, GearGraphics.ItemDetailFont, Brushes.White, 0, picY += 16)); propBlocks.Add(PrepareText(g, "PAD: " + MobInfo.PADamage, GearGraphics.ItemDetailFont, Brushes.White, 0, picY += 16)); propBlocks.Add(PrepareText(g, "MAD: " + MobInfo.MADamage, GearGraphics.ItemDetailFont, Brushes.White, 0, picY += 16)); propBlocks.Add(PrepareText(g, "PDr: " + MobInfo.PDRate + "%", GearGraphics.ItemDetailFont, Brushes.White, 0, picY += 16)); propBlocks.Add(PrepareText(g, "MDr: " + MobInfo.MDRate + "%", GearGraphics.ItemDetailFont, Brushes.White, 0, picY += 16)); propBlocks.Add(PrepareText(g, "Acc: " + MobInfo.Acc, GearGraphics.ItemDetailFont, Brushes.White, 0, picY += 16)); propBlocks.Add(PrepareText(g, "Eva: " + MobInfo.Eva, GearGraphics.ItemDetailFont, Brushes.White, 0, picY += 16)); propBlocks.Add(PrepareText(g, "KB: " + MobInfo.Pushed.ToString("N0"), GearGraphics.ItemDetailFont, Brushes.White, 0, picY += 16)); propBlocks.Add(PrepareText(g, "Exp: " + MobInfo.Exp.ToString("N0"), GearGraphics.ItemDetailFont, Brushes.White, 0, picY += 16)); propBlocks.Add(PrepareText(g, GetElemAttrString(MobInfo.ElemAttr), GearGraphics.ItemDetailFont, Brushes.White, 0, picY += 16)); picY += 28; if (MobInfo.Revive.Count > 0) { Dictionary reviveCounts = new Dictionary(); foreach (var reviveID in MobInfo.Revive) { int count = 0; reviveCounts.TryGetValue(reviveID, out count); reviveCounts[reviveID] = count + 1; } StringBuilder sb = new StringBuilder(); sb.Append("死后召唤 "); int rowCount = 0; foreach (var kv in reviveCounts) { if (rowCount++ > 0) { sb.AppendLine().Append(" "); } string mobName = GetMobName(kv.Key); sb.AppendFormat("{0}({1:D7})", mobName, kv.Key); if (kv.Value > 1) { sb.Append("*" + kv.Value); } } propBlocks.Add(PrepareText(g, sb.ToString(), GearGraphics.ItemDetailFont, Brushes.GreenYellow, 0, picY)); } g.Dispose(); bmp.Dispose(); //计算大小 Rectangle titleRect = Measure(titleBlocks); Rectangle imgRect = Rectangle.Empty; Rectangle textRect = Measure(propBlocks); Bitmap mobImg = MobInfo.Default.Bitmap; if (mobImg != null) { if (mobImg.Width > 250 || mobImg.Height > 300) //进行缩放 { double scale = Math.Min((double)250 / mobImg.Width, (double)300 / mobImg.Height); imgRect = new Rectangle(0, 0, (int)(mobImg.Width * scale), (int)(mobImg.Height * scale)); } else { imgRect = new Rectangle(0, 0, mobImg.Width, mobImg.Height); } } //布局 //水平排列 int width = 0; if (!imgRect.IsEmpty) { textRect.X = imgRect.Width + 4; } width = Math.Max(titleRect.Width, Math.Max(imgRect.Right, textRect.Right)); titleRect.X = (width - titleRect.Width) / 2; //垂直居中 int height = Math.Max(imgRect.Height, textRect.Height); imgRect.Y = (height - imgRect.Height) / 2; textRect.Y = (height - textRect.Height) / 2; if (!titleRect.IsEmpty) { height += titleRect.Height + 4; imgRect.Y += titleRect.Bottom + 4; textRect.Y += titleRect.Bottom + 4; } //绘制 bmp = new Bitmap(width + 20, height + 20); titleRect.Offset(10, 10); imgRect.Offset(10, 10); textRect.Offset(10, 10); g = Graphics.FromImage(bmp); //绘制背景 GearGraphics.DrawNewTooltipBack(g, 0, 0, bmp.Width, bmp.Height); //绘制标题 foreach (var item in titleBlocks) { DrawText(g, item, titleRect.Location); } //绘制图像 if (mobImg != null && !imgRect.IsEmpty) { g.DrawImage(mobImg, imgRect); } //绘制文本 foreach (var item in propBlocks) { DrawText(g, item, textRect.Location); } g.Dispose(); return bmp; } private string GetMobName(int mobID) { StringResult sr; if (this.StringLinker == null || !this.StringLinker.StringMob.TryGetValue(mobID, out sr)) { return null; } return sr.Name; } private string GetElemAttrString(MobElemAttr elemAttr) { StringBuilder sb1 = new StringBuilder(), sb2 = new StringBuilder(); sb1.Append("冰雷火毒圣暗物"); sb2.Append(GetElemAttrResistString(elemAttr.I)); sb2.Append(GetElemAttrResistString(elemAttr.L)); sb2.Append(GetElemAttrResistString(elemAttr.F)); sb2.Append(GetElemAttrResistString(elemAttr.S)); sb2.Append(GetElemAttrResistString(elemAttr.H)); sb2.Append(GetElemAttrResistString(elemAttr.D)); sb2.Append(GetElemAttrResistString(elemAttr.P)); sb1.AppendLine().Append(sb2.ToString()); return sb1.ToString(); } private string GetElemAttrResistString(ElemResistance resist) { string e = null; switch (resist) { case ElemResistance.Immune: e = "×"; break; case ElemResistance.Resist: e = "△"; break; case ElemResistance.Normal: e = "○"; break; case ElemResistance.Weak: e = "◎"; break; } return e ?? " "; } private string AddCommaSeparators(string number) { return Regex.Replace(number, @"^(\d+?)(\d{3})+$", m => { var sb = new StringBuilder(); sb.Append(m.Result("$1")); foreach (Capture cap in m.Groups[2].Captures) { sb.Append(","); sb.Append(cap.ToString()); } return sb.ToString(); }); } } } ================================================ FILE: WzComparerR2/CharaSimControl/NpcTooltipRenderer.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Drawing; using System.Drawing.Imaging; using WzComparerR2.CharaSim; using WzComparerR2.Common; using static WzComparerR2.CharaSimControl.RenderHelper; namespace WzComparerR2.CharaSimControl { public class NpcTooltipRenderer : TooltipRender { public NpcTooltipRenderer() { } public override object TargetItem { get { return this.NpcInfo; } set { this.NpcInfo = value as Npc; } } public Npc NpcInfo { get; set; } public override Bitmap Render() { if (NpcInfo == null) { return null; } Bitmap bmp = new Bitmap(1, 1, PixelFormat.Format32bppArgb); Graphics g = Graphics.FromImage(bmp); //预绘制 List titleBlocks = new List(); List propBlocks = new List(); int picY = 0; if (NpcInfo.ID > -1) { string mobName = GetNpcName(NpcInfo.ID); var block = PrepareText(g, mobName ?? "(null)", GearGraphics.ItemNameFont2, Brushes.White, 0, 0); titleBlocks.Add(block); block = PrepareText(g, "ID:" + NpcInfo.ID, GearGraphics.ItemDetailFont, Brushes.White, block.Size.Width + 4, 4); titleBlocks.Add(block); } propBlocks.Add(PrepareText(g, "出没地区:", GearGraphics.ItemDetailFont, GearGraphics.GearNameBrushG, 0, 0)); if (NpcInfo?.ID != null) { var locNode = PluginBase.PluginManager.FindWz("Etc\\NpcLocation.img\\" + NpcInfo.ID.ToString()); if (locNode != null) { foreach (var locMapNode in locNode.Nodes) { int mapID; string mapName = null; if (int.TryParse(locMapNode.Text, out mapID)) { mapName = GetMapName(mapID); } string npcLoc = string.Format(" {0}({1})", mapName ?? "null", locMapNode.Text); propBlocks.Add(PrepareText(g, npcLoc, GearGraphics.ItemDetailFont, Brushes.White, 0, picY += 16)); } } } if (propBlocks.Count == 1) //获取地区失败 { propBlocks.Add(PrepareText(g, " 不明", GearGraphics.ItemDetailFont, Brushes.White, 0, picY += 16)); } //计算大小 Rectangle titleRect = Measure(titleBlocks); Rectangle imgRect = Rectangle.Empty; Rectangle textRect = Measure(propBlocks); Bitmap npcImg = NpcInfo.Default.Bitmap; if (npcImg != null) { if (npcImg.Width > 250 || npcImg.Height > 300) //进行缩放 { double scale = Math.Min((double)250 / npcImg.Width, (double)300 / npcImg.Height); imgRect = new Rectangle(0, 0, (int)(npcImg.Width * scale), (int)(npcImg.Height * scale)); } else { imgRect = new Rectangle(0, 0, npcImg.Width, npcImg.Height); } } //布局 //水平排列 int width = 0; if (!imgRect.IsEmpty) { textRect.X = imgRect.Width + 4; } width = Math.Max(titleRect.Width, Math.Max(imgRect.Right, textRect.Right)); titleRect.X = (width - titleRect.Width) / 2; //垂直居中 int height = Math.Max(imgRect.Height, textRect.Height); imgRect.Y = (height - imgRect.Height) / 2; textRect.Y = (height - textRect.Height) / 2; if (!titleRect.IsEmpty) { height += titleRect.Height + 4; imgRect.Y += titleRect.Bottom + 4; textRect.Y += titleRect.Bottom + 4; } //绘制 bmp = new Bitmap(width + 20, height + 20); titleRect.Offset(10, 10); imgRect.Offset(10, 10); textRect.Offset(10, 10); g = Graphics.FromImage(bmp); //绘制背景 GearGraphics.DrawNewTooltipBack(g, 0, 0, bmp.Width, bmp.Height); //绘制标题 foreach (var item in titleBlocks) { DrawText(g, item, titleRect.Location); } //绘制图像 if (npcImg != null && !imgRect.IsEmpty) { g.DrawImage(npcImg, imgRect); } //绘制文本 foreach (var item in propBlocks) { DrawText(g, item, textRect.Location); } g.Dispose(); return bmp; } private string GetNpcName(int npcID) { StringResult sr; if (this.StringLinker == null || !this.StringLinker.StringNpc.TryGetValue(npcID, out sr)) { return null; } return sr.Name; } private string GetMapName(int mapID) { StringResult sr; if (this.StringLinker == null || !this.StringLinker.StringMap.TryGetValue(mapID, out sr)) { return null; } return sr.Name; } } } ================================================ FILE: WzComparerR2/CharaSimControl/RecipeTooltipRender.cs ================================================ using System; using System.Collections.Generic; using System.Text; using System.Drawing; using System.Drawing.Drawing2D; using Resource = CharaSimResource.Resource; using WzComparerR2.Common; using WzComparerR2.CharaSim; namespace WzComparerR2.CharaSimControl { public class RecipeTooltipRender : TooltipRender { public RecipeTooltipRender() { } public Recipe Recipe { get; set; } public override object TargetItem { get { return this.Recipe; } set { this.Recipe = value as Recipe; } } public override Bitmap Render() { if (this.Recipe == null) { return null; } int picHeight; Bitmap originBmp = RenderRecipe(out picHeight); Bitmap tooltip = new Bitmap(originBmp.Width, picHeight); Graphics g = Graphics.FromImage(tooltip); //绘制背景区域 GearGraphics.DrawNewTooltipBack(g, 0, 0, tooltip.Width, tooltip.Height); //复制图像 g.DrawImage(originBmp, 0, 0, new Rectangle(0, 0, originBmp.Width, picHeight), GraphicsUnit.Pixel); //左上角 g.DrawImage(Resource.UIToolTip_img_Item_Frame2_cover, 3, 3); if (this.ShowObjectID) { GearGraphics.DrawGearDetailNumber(g, 3, 3, Recipe.RecipeID.ToString("d8"), true); } if (originBmp != null) originBmp.Dispose(); g.Dispose(); return tooltip; } private Bitmap RenderRecipe(out int picH) { Bitmap tooltip = new Bitmap(290, DefaultPicHeight); Graphics g = Graphics.FromImage(tooltip); StringFormat fmt = (StringFormat)StringFormat.GenericTypographic.Clone(); fmt.Alignment = StringAlignment.Center; picH = 10; StringResult sr; string title = "制作配方"; if (this.Recipe.MainTargetItemID != 0) { sr = GetSRByItemID(this.Recipe.MainTargetItemID); if (sr == null) { title += " - " + this.Recipe.MainTargetItemID; } else { title += " - " + sr.Name; } } g.DrawString(title, GearGraphics.ItemDetailFont, GearGraphics.GreenBrush2, 145, picH, fmt); picH += 16; g.DrawString("生成物品", GearGraphics.ItemDetailFont, GearGraphics.GreenBrush2, 13, picH); picH += 16; fmt.Alignment = StringAlignment.Far; foreach (RecipeItemInfo itemInfo in this.Recipe.TargetItems) { sr = GetSRByItemID(itemInfo.ItemID); string text = sr != null ? sr.Name : itemInfo.ItemID.ToString(); text += " x " + itemInfo.Count; g.DrawString(text, GearGraphics.ItemDetailFont2, Brushes.White, 13, picH, StringFormat.GenericTypographic); g.DrawString(itemInfo.ProbWeight + "%", GearGraphics.ItemDetailFont2, Brushes.White, 278, picH, fmt); picH += 16; } picH += 4; g.DrawString("消耗物品", GearGraphics.ItemDetailFont, GearGraphics.GreenBrush2, 13, picH); picH += 16; foreach (RecipeItemInfo itemInfo in this.Recipe.RecipeItems) { sr = GetSRByItemID(itemInfo.ItemID); string text = sr != null ? sr.Name : itemInfo.ItemID.ToString(); text += " x " + itemInfo.Count; g.DrawString(text, GearGraphics.ItemDetailFont2, Brushes.White, 13, picH, StringFormat.GenericTypographic); picH += 16; } picH += 5; fmt.Dispose(); g.Dispose(); return tooltip; } private StringResult GetSRByItemID(int itemID) { if (StringLinker == null) { return null; } StringResult sr = null; int itemIDClass = itemID / 1000000; if (itemIDClass == 1) { StringLinker.StringEqp.TryGetValue(itemID, out sr); } else if (itemIDClass >= 2 && itemIDClass <= 5) { StringLinker.StringItem.TryGetValue(itemID, out sr); } return sr; } } } ================================================ FILE: WzComparerR2/CharaSimControl/RenderHelper.cs ================================================ using System; using System.Collections.Generic; using System.Text; using System.Drawing; namespace WzComparerR2.CharaSimControl { class RenderHelper { private RenderHelper() { } public static TextBlock PrepareText(Graphics g, string text, Font font, Brush brush, int x, int y) { SizeF size = g.MeasureString(text, font, Int32.MaxValue, StringFormat.GenericTypographic); TextBlock block = new TextBlock() { Text = text, Font = font, Brush = brush, Position = new Point(x, y), Size = new Size((int)Math.Round(size.Width, MidpointRounding.AwayFromZero), (int)Math.Round(size.Height, MidpointRounding.AwayFromZero)) }; return block; } public static void DrawText(Graphics g, TextBlock block, Point offset) { g.DrawString(block.Text, block.Font, block.Brush, block.Position.X + offset.X, block.Position.Y + offset.Y, StringFormat.GenericTypographic); } public static Rectangle Measure(IEnumerable blocks) { Rectangle rect = Rectangle.Empty; foreach (var block in blocks) { var blockRect = block.Rectangle; if (!blockRect.IsEmpty) { rect = Rectangle.Union(rect, blockRect); } } return rect; } } } ================================================ FILE: WzComparerR2/CharaSimControl/SetItemTooltipRender.cs ================================================ using System; using System.Collections.Generic; using System.Text; using System.Drawing; using System.Drawing.Drawing2D; using System.Linq; using Resource = CharaSimResource.Resource; using WzComparerR2.PluginBase; using WzComparerR2.WzLib; using WzComparerR2.Common; using System.Text.RegularExpressions; using WzComparerR2.CharaSim; namespace WzComparerR2.CharaSimControl { public class SetItemTooltipRender : TooltipRender { public SetItemTooltipRender() { } public SetItem SetItem { get; set; } public override object TargetItem { get { return this.SetItem; } set { this.SetItem = value as SetItem; } } public bool IsCombineProperties { get; set; } = true; public override Bitmap Render() { if (this.SetItem == null) { return null; } int width = 261; int picHeight1; Bitmap originBmp = RenderSetItem(out picHeight1); int picHeight2 = 0; Bitmap effectBmp = null; if (this.SetItem.ExpandToolTip) { effectBmp = RenderEffectPart(out picHeight2); width += 261; } Bitmap tooltip = new Bitmap(width, Math.Max(picHeight1, picHeight2)); Graphics g = Graphics.FromImage(tooltip); //绘制左侧 GearGraphics.DrawNewTooltipBack(g, 0, 0, originBmp.Width, picHeight1); g.DrawImage(originBmp, 0, 0, new Rectangle(0, 0, originBmp.Width, picHeight1), GraphicsUnit.Pixel); //绘制右侧 if(effectBmp != null) { GearGraphics.DrawNewTooltipBack(g, originBmp.Width, 0, effectBmp.Width, picHeight2); g.DrawImage(effectBmp, originBmp.Width, 0, new Rectangle(0, 0, effectBmp.Width, picHeight2), GraphicsUnit.Pixel); } originBmp?.Dispose(); effectBmp?.Dispose(); g.Dispose(); return tooltip; } private Bitmap RenderSetItem(out int picHeight) { Bitmap setBitmap = new Bitmap(261, DefaultPicHeight); Graphics g = Graphics.FromImage(setBitmap); StringFormat format = new StringFormat(); format.Alignment = StringAlignment.Center; picHeight = 10; g.DrawString(this.SetItem.SetItemName, GearGraphics.ItemDetailFont2, GearGraphics.GreenBrush2, 130, 10, format); picHeight += 25; format.Alignment = StringAlignment.Far; Wz_Node characterWz = PluginManager.FindWz(Wz_Type.Character); foreach (var setItemPart in this.SetItem.ItemIDs.Parts) { string itemName = setItemPart.Value.RepresentName; string typeName = setItemPart.Value.TypeName; // for 'parts' setitem, detect typeName by the first itemID per part //if (string.IsNullOrEmpty(typeName) && SetItem.Parts) //{ // typeName = "特殊"; //} ItemBase itemBase = null; bool cash = false; long wonderGrade = 0; if (setItemPart.Value.ItemIDs.Count > 0) { var itemID = setItemPart.Value.ItemIDs.First().Key; switch (itemID / 1000000) { case 0: //avatar case 1: //gear if (characterWz != null) { foreach (Wz_Node typeNode in characterWz.Nodes) { Wz_Node itemNode = typeNode.FindNodeByPath(string.Format("{0:D8}.img", itemID), true); if (itemNode != null) { var gear = Gear.CreateFromNode(itemNode, PluginManager.FindWz); cash = gear.Cash; itemBase = gear; break; } } } break; case 5: //Pet { Wz_Node itemNode = PluginBase.PluginManager.FindWz(string.Format(@"Item\Pet\{0:D7}.img", itemID)); if (itemNode != null) { var item = Item.CreateFromNode(itemNode, PluginManager.FindWz); cash = item.Cash; item.Props.TryGetValue(ItemPropType.wonderGrade, out wonderGrade); itemBase = item; } } break; } } if (string.IsNullOrEmpty(itemName) || string.IsNullOrEmpty(typeName)) { if (setItemPart.Value.ItemIDs.Count > 0) { var itemID = setItemPart.Value.ItemIDs.First().Key; StringResult sr = null; ; if (this.StringLinker != null) { if (this.StringLinker.StringEqp.TryGetValue(itemID, out sr)) { itemName = sr.Name; if (typeName == null) { typeName = ItemStringHelper.GetSetItemGearTypeString(Gear.GetGearType(itemID)); } } else if (this.StringLinker.StringItem.TryGetValue(itemID, out sr)) //兼容宠物 { itemName = sr.Name; if (typeName == null) { if (itemID / 10000 == 500) { typeName = "特殊"; } else { typeName = ""; } } } } if (sr == null) { itemName = "(null)"; } } } itemName = itemName ?? string.Empty; typeName = typeName ?? "装备"; if (!Regex.IsMatch(typeName, @"^(\(.*\)|(.*))$")) { typeName = "(" + typeName + ")"; } Brush brush = setItemPart.Value.Enabled ? Brushes.White : GearGraphics.GrayBrush2; if (!cash) { g.DrawString(itemName, GearGraphics.ItemDetailFont2, brush, 8, picHeight); g.DrawString(typeName, GearGraphics.ItemDetailFont2, brush, 254, picHeight, format); picHeight += 18; } else { g.FillRectangle(GearGraphics.GearIconBackBrush2, 10, picHeight, 36, 36); g.DrawImage(Resource.Item_shadow, 10 + 2 + 3, picHeight + 2 + 32 - 6); if (itemBase?.IconRaw.Bitmap != null) { var icon = itemBase.IconRaw; g.DrawImage(icon.Bitmap, 10 + 2 - icon.Origin.X, picHeight + 2 + 32 - icon.Origin.Y); } Bitmap cashImg = null; if (wonderGrade > 0) { string resKey = $"CashShop_img_CashItem_label_{wonderGrade + 3}"; cashImg = Resource.ResourceManager.GetObject(resKey) as Bitmap; } if (cashImg == null) //default cashImg { cashImg = Resource.CashItem_0; } g.DrawImage(cashImg, 10 + 2 + 20, picHeight + 2 + 32 - 12); g.DrawString(itemName, GearGraphics.ItemDetailFont2, brush, 50, picHeight); g.DrawString(typeName, GearGraphics.ItemDetailFont2, brush, 254, picHeight, format); picHeight += 40; } } if (!this.SetItem.ExpandToolTip) { picHeight += 5; g.DrawLine(Pens.White, 6, picHeight, 254, picHeight);//分割线 picHeight += 9; RenderEffect(g, ref picHeight); } picHeight += 11; format.Dispose(); g.Dispose(); return setBitmap; } private Bitmap RenderEffectPart(out int picHeight) { Bitmap effBitmap = new Bitmap(261, DefaultPicHeight); Graphics g = Graphics.FromImage(effBitmap); picHeight = 9; RenderEffect(g, ref picHeight); picHeight += 11; g.Dispose(); return effBitmap; } /// /// 绘制套装属性。 /// private void RenderEffect(Graphics g, ref int picHeight) { foreach (KeyValuePair effect in this.SetItem.Effects) { string effTitle; if (this.SetItem.SetItemID < 0) { effTitle = $"服务器内重复装备效果({effect.Key} / {this.SetItem.CompleteCount})"; } else { effTitle = effect.Key + "套装效果"; } g.DrawString(effTitle, GearGraphics.ItemDetailFont, GearGraphics.GreenBrush2, 8, picHeight); picHeight += 16; //Brush brush = effect.Value.Enabled ? Brushes.White : GearGraphics.GrayBrush2; var color = effect.Value.Enabled ? Color.White : GearGraphics.GrayColor2; //T116 合并套装 var props = IsCombineProperties ? Gear.CombineProperties(effect.Value.PropsV5) : effect.Value.PropsV5; foreach (KeyValuePair prop in props) { if (prop.Key == GearPropType.Option) { List ops = (List)prop.Value; foreach (Potential p in ops) { GearGraphics.DrawPlainText(g, p.ConvertSummary(), GearGraphics.ItemDetailFont2, color, 10, 244, ref picHeight, 16); } } else if (prop.Key == GearPropType.OptionToMob) { List ops = (List)prop.Value; foreach (SetItemOptionToMob p in ops) { GearGraphics.DrawPlainText(g, p.ConvertSummary(), GearGraphics.ItemDetailFont2, color, 10, 244, ref picHeight, 16); } } else if (prop.Key == GearPropType.activeSkill) { List ops = (List)prop.Value; foreach (SetItemActiveSkill p in ops) { StringResult sr; if (StringLinker == null || !StringLinker.StringSkill.TryGetValue(p.SkillID, out sr)) { sr = new StringResult(); sr.Name = p.SkillID.ToString(); } string summary = $"可以使用<{sr.Name}>技能"; GearGraphics.DrawPlainText(g, summary, GearGraphics.ItemDetailFont2, color, 10, 244, ref picHeight, 16); } } else if (prop.Key == GearPropType.bonusByTime) { var ops = (List)prop.Value; foreach (SetItemBonusByTime p in ops) { GearGraphics.DrawPlainText(g, $"{p.TermStart}小时后", GearGraphics.ItemDetailFont2, color, 10, 244, ref picHeight, 16); foreach (var bonusProp in p.Props) { var summary = ItemStringHelper.GetGearPropString(bonusProp.Key, Convert.ToInt32(bonusProp.Value)); GearGraphics.DrawPlainText(g, summary, GearGraphics.ItemDetailFont2, color, 10, 244, ref picHeight, 16); } } } else { var summary = ItemStringHelper.GetGearPropString(prop.Key, Convert.ToInt32(prop.Value)); GearGraphics.DrawPlainText(g, summary, GearGraphics.ItemDetailFont2, color, 10, 244, ref picHeight, 16); } } } } } } ================================================ FILE: WzComparerR2/CharaSimControl/SkillTooltipRender.cs ================================================ using System; using System.Collections.Generic; using System.Text; using System.Drawing; using System.Drawing.Drawing2D; using WzComparerR2.Common; using WzComparerR2.CharaSim; namespace WzComparerR2.CharaSimControl { public class SkillTooltipRender : TooltipRender { public SkillTooltipRender() { } Skill skill; public Skill Skill { get { return skill; } set { skill = value; } } public override Bitmap Render() { if (this.skill == null) { return null; } int picHeight; Bitmap originBmp = RenderSkill(out picHeight); Bitmap tooltip = new Bitmap(290, picHeight); Graphics g = Graphics.FromImage(tooltip); int iconY = 33; //复制图像 g.FillRectangle(GearGraphics.GearBackBrush, 2, 2, 286, picHeight - 4); g.CompositingMode = CompositingMode.SourceCopy; g.FillRectangle(GearGraphics.GearIconBackBrush, 14, iconY, 68, 68); g.CompositingMode = CompositingMode.SourceOver; g.DrawImage(originBmp, 0, 0, new Rectangle(0, 0, 290, picHeight - 2), GraphicsUnit.Pixel); //边框 g.DrawLines(GearGraphics.GearBackPen, GearGraphics.GetBorderPath(0, 290, picHeight)); g.Dispose(); return tooltip; } private Bitmap RenderSkill(out int picH) { //int h = 128; Bitmap bitmap = new Bitmap(290, DefaultPicHeight); Graphics g = Graphics.FromImage(bitmap); picH = 33; //iconY if (!(StringLinker.StringSkill.TryGetValue(skill.SkillID, out var _sr) && _sr is StringResultSkill sr)) { sr = new StringResultSkill(); sr.Name = "(null)"; } StringFormat format = new StringFormat(); format.Alignment = StringAlignment.Center; g.DrawString(sr.Name, GearGraphics.ItemNameFont, Brushes.White, 143, 10, format);//绘制标题 if (skill.Icon.Bitmap != null) { g.DrawImage(GearGraphics.EnlargeBitmap(skill.Icon.Bitmap), 14 + (1 - skill.Icon.Origin.X) * 2, picH + (33 - skill.Icon.Bitmap.Height) * 2);//绘制图标 } //绘制desc picH = 35; GearGraphics.DrawString(g, "[最高等级:" + skill.MaxLevel + "]", GearGraphics.ItemDetailFont, 90, 270, ref picH, 16); if (sr.Desc != null) { GearGraphics.DrawString(g, sr.Desc, GearGraphics.ItemDetailFont, 90, 270, ref picH, 16); } picH = Math.Max(picH, 114); g.DrawLine(Pens.White, 6, picH, 283, picH); //分割线 picH += 5; if (skill.Level > 0) { string hStr = null; if (skill.PreBBSkill) { if (sr.SkillH.Count >= skill.Level) { hStr = sr.SkillH[skill.Level - 1]; } } else { if (sr.SkillH.Count > 0) { hStr = SummaryParser.GetSkillSummary(skill,skill.Level, sr, SummaryParams.Default); } } picH += 4; GearGraphics.DrawString(g, "[现在等级 " + skill.Level + "]", GearGraphics.ItemDetailFont, 8, 272, ref picH, 16); GearGraphics.DrawString(g, hStr, GearGraphics.ItemDetailFont, 8, 272, ref picH, 16); } if (skill.Level < skill.MaxLevel) { string hStr = null; if (skill.PreBBSkill) { if (sr.SkillH.Count >= skill.Level + 1) { hStr = sr.SkillH[skill.Level]; } } else { if (sr.SkillH.Count > 0) { hStr = SummaryParser.GetSkillSummary(skill, skill.Level+1, sr, SummaryParams.Default); } } picH += 4; GearGraphics.DrawString(g, "[下次等级 " + (skill.Level + 1) + "]", GearGraphics.ItemDetailFont, 8, 272, ref picH, 16); GearGraphics.DrawString(g, hStr, GearGraphics.ItemDetailFont, 8, 272, ref picH, 16); } picH += 9; g.Dispose(); return bitmap; } } } ================================================ FILE: WzComparerR2/CharaSimControl/SkillTooltipRender2.cs ================================================ using System; using System.Collections.Generic; using System.Text; using System.Drawing; using System.Linq; using Resource = CharaSimResource.Resource; using WzComparerR2.Common; using WzComparerR2.CharaSim; namespace WzComparerR2.CharaSimControl { public class SkillTooltipRender2 : TooltipRender { public SkillTooltipRender2() { } public Skill Skill { get; set; } public override object TargetItem { get { return this.Skill; } set { this.Skill = value as Skill; } } public bool ShowProperties { get; set; } = true; public bool ShowDelay { get; set; } public bool ShowReqSkill { get; set; } = true; public bool DisplayCooltimeMSAsSec { get; set; } = true; public bool DisplayPermyriadAsPercent { get; set; } = true; public bool IgnoreEvalError { get; set; } = false; public bool IsWideMode { get; set; } = true; public override Bitmap Render() { if (this.Skill == null) { return null; } CanvasRegion region = this.IsWideMode ? CanvasRegion.Wide : CanvasRegion.Original; int picHeight; List splitterH; Bitmap originBmp = RenderSkill(region, out picHeight, out splitterH); Bitmap tooltip = new Bitmap(originBmp.Width, picHeight); Graphics g = Graphics.FromImage(tooltip); //绘制背景区域 GearGraphics.DrawNewTooltipBack(g, 0, 0, tooltip.Width, tooltip.Height); if (splitterH != null && splitterH.Count > 0) { g.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy; foreach (var y in splitterH) { DrawV6SkillDotline(g, region.SplitterX1, region.SplitterX2, y); } g.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceOver; } //复制图像 g.DrawImage(originBmp, 0, 0, new Rectangle(0, 0, originBmp.Width, picHeight), GraphicsUnit.Pixel); //左上角 g.DrawImage(Resource.UIToolTip_img_Skill_Frame_cover, 3, 3); if (this.ShowObjectID) { GearGraphics.DrawGearDetailNumber(g, 3, 3, Skill.SkillID.ToString("d7"), true); } if (originBmp != null) originBmp.Dispose(); g.Dispose(); return tooltip; } private Bitmap RenderSkill(CanvasRegion region, out int picH, out List splitterH) { Bitmap bitmap = new Bitmap(region.Width, DefaultPicHeight); Graphics g = Graphics.FromImage(bitmap); StringFormat format = (StringFormat)StringFormat.GenericDefault.Clone(); var v6SkillSummaryFontColorTable = new Dictionary() { { "c", GearGraphics.SkillSummaryOrangeTextColor }, }; //初始化 skillCommon Dictionary skillCommon = new Dictionary(Skill.Common); if (Skill.PerJobAttackInfo.Count > 0) { var perJobInfo = Skill.PerJobAttackInfo.ElementAt(Skill.PerJobIndex).Value; foreach (var i in perJobInfo.Keys) { skillCommon[i] = perJobInfo[i]; } } picH = 0; splitterH = new List(); //获取文字 if (StringLinker == null || !(StringLinker.StringSkill.TryGetValue(Skill.SkillID, out var _sr) && _sr is StringResultSkill sr)) { sr = new StringResultSkill(); sr.Name = "(null)"; } //绘制技能名称 format.Alignment = StringAlignment.Center; g.DrawString(sr.Name, GearGraphics.ItemNameFont2, Brushes.White, region.TitleCenterX, 10, format); //绘制图标 picH = 33; g.DrawImage(Resource.UIToolTip_img_Skill_Frame_iconBackgrnd, 13, picH - 2); if (Skill.Icon.Bitmap != null) { g.DrawImage(GearGraphics.EnlargeBitmap(Skill.Icon.Bitmap), 15 + (1 - Skill.Icon.Origin.X) * 2, picH + (33 - Skill.Icon.Bitmap.Height) * 2); } // for 6th job skills if (Skill.Origin) { g.DrawImage(Resource.UIWindow2_img_Skill_skillTypeIcon_origin, 16, 11); } //绘制desc picH = 35; if (!Skill.PreBBSkill) GearGraphics.DrawString(g, "[最高等级:" + Skill.MaxLevel + "]", GearGraphics.ItemDetailFont2, region.SkillDescLeft, region.TextRight, ref picH, 16); if (sr.Desc != null) { string hdesc = SummaryParser.GetSkillSummary(sr.Desc, Skill.Level, skillCommon, SummaryParams.Default); //string hStr = SummaryParser.GetSkillSummary(skill, skill.Level, sr, SummaryParams.Default); GearGraphics.DrawString(g, hdesc, GearGraphics.ItemDetailFont2, v6SkillSummaryFontColorTable, region.SkillDescLeft, region.TextRight, ref picH, 16); } if (Skill.ReqLevel > 0) { GearGraphics.DrawString(g, "#c[要求等级:" + Skill.ReqLevel.ToString() + "]#", GearGraphics.ItemDetailFont2, region.SkillDescLeft, region.TextRight, ref picH, 16); } if (Skill.ReqAmount > 0) { GearGraphics.DrawString(g, "#c" + ItemStringHelper.GetSkillReqAmount(Skill.SkillID, Skill.ReqAmount) + "#", GearGraphics.ItemDetailFont2, region.SkillDescLeft, region.TextRight, ref picH, 16); } picH += 13; //delay rendering v6 splitter picH = Math.Max(picH, 114); splitterH.Add(picH); picH += 15; var skillSummaryOptions = new SkillSummaryOptions { ConvertCooltimeMS = this.DisplayCooltimeMSAsSec, ConvertPerM = this.DisplayPermyriadAsPercent, IgnoreEvalError = this.IgnoreEvalError, EndColorOnNewLine = true, }; if (Skill.Level > 0) { string hStr = SummaryParser.GetSkillSummary(Skill, Skill.Level, sr, SummaryParams.Default, skillSummaryOptions, skillCommon); GearGraphics.DrawString(g, "[现在等级 " + Skill.Level + "]", GearGraphics.ItemDetailFont, region.LevelDescLeft, region.TextRight, ref picH, 16); if (hStr != null) { GearGraphics.DrawString(g, hStr, GearGraphics.ItemDetailFont2, v6SkillSummaryFontColorTable, region.LevelDescLeft, region.TextRight, ref picH, 16); } } if (Skill.Level < Skill.MaxLevel) { string hStr = SummaryParser.GetSkillSummary(Skill, Skill.Level + 1, sr, SummaryParams.Default, skillSummaryOptions); GearGraphics.DrawString(g, "[下次等级 " + (Skill.Level + 1) + "]", GearGraphics.ItemDetailFont, region.LevelDescLeft, region.TextRight, ref picH, 16); if (hStr != null) { GearGraphics.DrawString(g, hStr, GearGraphics.ItemDetailFont2, v6SkillSummaryFontColorTable, region.LevelDescLeft, region.TextRight, ref picH, 16); } } picH += 9; List skillDescEx = new List(); if (ShowProperties) { List attr = new List(); if (Skill.Invisible) { attr.Add("隐藏技能"); } if (Skill.Hyper != HyperSkillType.None) { attr.Add("超级技能:" + Skill.Hyper); } if (Skill.CombatOrders) { attr.Add("战斗命令加成"); } if (Skill.NotRemoved) { attr.Add("无法被移除"); } if (Skill.MasterLevel > 0 && Skill.MasterLevel < Skill.MaxLevel) { attr.Add("初始掌握:Lv." + Skill.MasterLevel); } if (attr.Count > 0) { skillDescEx.Add("#c" + string.Join(", ", attr.ToArray()) + "#"); } } if (ShowDelay && Skill.Action.Count > 0) { foreach (string action in Skill.Action) { skillDescEx.Add("#c[技能延时] " + action + ": " + CharaSimLoader.GetActionDelay(action) + " ms#"); } } if (ShowReqSkill && Skill.ReqSkill.Count > 0) { foreach (var kv in Skill.ReqSkill) { string skillName; if (this.StringLinker != null && this.StringLinker.StringSkill.TryGetValue(kv.Key, out var sr2)) { skillName = sr2.Name; } else { skillName = kv.Key.ToString(); } skillDescEx.Add("#c[前置技能] " + skillName + ": " + kv.Value + " 级#"); } } if (Skill.PerJobAttackInfo.Count > 0) { int jobID = Skill.PerJobAttackInfo.ElementAt(Skill.PerJobIndex).Key; skillDescEx.Add($"#c[适用职业] {ItemStringHelper.GetJobName(jobID)}({jobID})#"); } if (skillDescEx.Count > 0) { //delay rendering v6 splitter splitterH.Add(picH); picH += 9; foreach (var descEx in skillDescEx) { GearGraphics.DrawString(g, descEx, GearGraphics.ItemDetailFont, region.LevelDescLeft, region.TextRight, ref picH, 16); } picH += 9; } format.Dispose(); g.Dispose(); return bitmap; } private void DrawV6SkillDotline(Graphics g, int x1, int x2, int y) { // here's a trick that we won't draw left and right part because it looks the same as background border. var picCenter = Resource.UIToolTip_img_Skill_Frame_dotline_c; using (var brush = new TextureBrush(picCenter)) { brush.TranslateTransform(x1, y); g.FillRectangle(brush, new Rectangle(x1, y, x2 - x1, picCenter.Height)); } } private class CanvasRegion { public int Width { get; private set; } public int TitleCenterX { get; private set; } public int SplitterX1 { get; private set; } public int SplitterX2 { get; private set; } public int SkillDescLeft { get; private set; } public int LevelDescLeft { get; private set; } public int TextRight { get; private set; } public static CanvasRegion Original { get; } = new CanvasRegion() { Width = 290, TitleCenterX = 144, SplitterX1 = 4, SplitterX2 = 284, SkillDescLeft = 90, LevelDescLeft = 8, TextRight = 272, }; public static CanvasRegion Wide { get; } = new CanvasRegion() { Width = 430, TitleCenterX = 215, SplitterX1 = 4, SplitterX2 = 424, SkillDescLeft = 92, LevelDescLeft = 10, TextRight = 412, }; } } } ================================================ FILE: WzComparerR2/CharaSimControl/TextBlock.cs ================================================ using System; using System.Collections.Generic; using System.Text; using System.Drawing; namespace WzComparerR2.CharaSimControl { public class TextBlock { public string Text { get; set; } public Brush Brush { get; set; } public Font Font { get; set; } public Point Position { get; set; } public Size Size { get; set; } public Rectangle Rectangle { get { return new Rectangle(Position, Size); } } } } ================================================ FILE: WzComparerR2/CharaSimControl/TooltipRender.cs ================================================ using System; using System.Collections.Generic; using System.Text; using System.Drawing; using WzComparerR2.Common; namespace WzComparerR2.CharaSimControl { public abstract class TooltipRender { static TooltipRender() { Rectangle screenRect = System.Windows.Forms.Screen.PrimaryScreen.Bounds; DefaultPicHeight = screenRect.Height * 2; } public TooltipRender() { } public static readonly int DefaultPicHeight; public StringLinker StringLinker { get; set; } public bool ShowObjectID { get; set; } public abstract Bitmap Render(); public virtual object TargetItem { get; set; } } } ================================================ FILE: WzComparerR2/Comparer/CompareDifference.cs ================================================ using System; using System.Collections.Generic; using System.Text; using WzComparerR2.WzLib; namespace WzComparerR2.Comparer { public class CompareDifference { public CompareDifference(Wz_Node nodeNew, Wz_Node nodeOld, DifferenceType type) { this.NodeNew = nodeNew; this.NodeOld = nodeOld; this.DifferenceType = type; } public Wz_Node NodeNew { get; protected set; } public Wz_Node NodeOld { get; protected set; } public virtual object ValueNew { get { return NodeNew?.Value; } } public virtual object ValueOld { get { return NodeOld?.Value; } } public DifferenceType DifferenceType { get; protected set; } public override string ToString() { switch (this.DifferenceType) { case DifferenceType.Append: return string.Format("{0} {1}({2})", this.DifferenceType, this.NodeNew.Text, this.ValueNew); case DifferenceType.Changed: return string.Format("{0} {1}({2}<-{3})", this.DifferenceType, this.NodeNew.Text, this.ValueNew, this.ValueOld); case DifferenceType.Remove: return string.Format("{0} {1}({2})", this.DifferenceType, this.NodeOld.Text, this.ValueOld); } return base.ToString(); } } } ================================================ FILE: WzComparerR2/Comparer/DifferenceType.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace WzComparerR2.Comparer { public enum DifferenceType { NotChanged = 0, Append, Remove, Changed, } } ================================================ FILE: WzComparerR2/Comparer/EasyComparer.cs ================================================ using System; using System.Collections.Generic; using System.Text; using System.IO; using System.Net; using System.Drawing; using System.Linq; using System.Security.Cryptography; using WzComparerR2.WzLib; namespace WzComparerR2.Comparer { public class EasyComparer { public EasyComparer() { this.Comparer = new WzFileComparer(); } public WzFileComparer Comparer { get; protected set; } private string stateInfo; private string stateDetail; public bool OutputPng { get; set; } public bool OutputAddedImg { get; set; } public bool OutputRemovedImg { get; set; } public List ColorTable { get; set; } public bool HashPngFileName { get; set; } public string StateInfo { get { return stateInfo; } set { stateInfo = value; this.OnStateInfoChanged(EventArgs.Empty); } } public string StateDetail { get { return stateDetail; } set { stateDetail = value; this.OnStateDetailChanged(EventArgs.Empty); } } public event EventHandler StateInfoChanged; public event EventHandler StateDetailChanged; protected virtual void OnStateInfoChanged(EventArgs e) { if (this.StateInfoChanged != null) this.StateInfoChanged(this, e); } protected virtual void OnStateDetailChanged(EventArgs e) { if (this.StateDetailChanged != null) this.StateDetailChanged(this, e); } public void EasyCompareWzFiles(Wz_File fileNew, Wz_File fileOld, string outputDir) { StateInfo = "正在对比wz概况..."; if (fileNew.Type == Wz_Type.Base || fileOld.Type == Wz_Type.Base) //至少有一个base 拆分对比 { var virtualNodeNew = RebuildWzFile(fileNew); var virtualNodeOld = RebuildWzFile(fileOld); WzFileComparer comparer = new WzFileComparer(); comparer.IgnoreWzFile = true; var dictNew = SplitVirtualNode(virtualNodeNew); var dictOld = SplitVirtualNode(virtualNodeOld); //寻找共同wzType var wzTypeList = dictNew.Select(kv => kv.Key) .Where(wzType => dictOld.ContainsKey(wzType)); CreateStyleSheet(outputDir); foreach (var wzType in wzTypeList) { var vNodeNew = dictNew[wzType]; var vNodeOld = dictOld[wzType]; var cmp = comparer.Compare(vNodeNew, vNodeOld); OutputFile(vNodeNew.LinkNodes.Select(node => node.Value).OfType().ToList(), vNodeOld.LinkNodes.Select(node => node.Value).OfType().ToList(), wzType, cmp.ToList(), outputDir); } } else //执行传统对比 { WzFileComparer comparer = new WzFileComparer(); comparer.IgnoreWzFile = false; var cmp = comparer.Compare(fileNew.Node, fileOld.Node); CreateStyleSheet(outputDir); OutputFile(fileNew, fileOld, fileNew.Type, cmp.ToList(), outputDir); } GC.Collect(); } private WzVirtualNode RebuildWzFile(Wz_File wzFile) { //分组 List subFiles = new List(); WzVirtualNode topNode = new WzVirtualNode(wzFile.Node); foreach (var childNode in wzFile.Node.Nodes) { var subFile = childNode.GetValue(); if (subFile != null && !subFile.IsSubDir) //wz子文件 { subFiles.Add(subFile); } else //其他 { topNode.AddChild(childNode, true); } } if (wzFile.Type == Wz_Type.Base) { foreach (var grp in subFiles.GroupBy(f => f.Type)) { WzVirtualNode fileNode = new WzVirtualNode(); fileNode.Name = grp.Key.ToString(); foreach (var file in grp) { fileNode.Combine(file.Node); } topNode.AddChild(fileNode); } } return topNode; } private Dictionary SplitVirtualNode(WzVirtualNode node) { var dict = new Dictionary(); Wz_File wzFile = node.LinkNodes[0].Value as Wz_File; dict[wzFile.Type] = node; if (wzFile.Type == Wz_Type.Base) //额外处理 { var wzFileList = node.ChildNodes .Select(child => new { Node = child, WzFile = child.LinkNodes[0].Value as Wz_File }) .Where(item => item.WzFile != null); foreach (var item in wzFileList) { dict[item.WzFile.Type] = item.Node; } } return dict; } private void OutputFile(Wz_File fileNew, Wz_File fileOld, Wz_Type type, List diffLst, string outputDir) { OutputFile(new List() { fileNew }, new List() { fileOld }, type, diffLst, outputDir); } private void OutputFile(List fileNew, List fileOld, Wz_Type type, List diffLst, string outputDir) { string htmlFilePath = Path.Combine(outputDir, type.ToString() + ".html"); for (int i = 1; File.Exists(htmlFilePath); i++) { htmlFilePath = Path.Combine(outputDir, string.Format("{0}_{1}.html", type, i)); } string srcDirPath = Path.Combine(outputDir, Path.GetFileNameWithoutExtension(htmlFilePath) + "_files"); if (OutputPng && !Directory.Exists(srcDirPath)) { Directory.CreateDirectory(srcDirPath); } FileStream htmlFile = null; StreamWriter sw = null; StateInfo = "正在努力对比文件..." + type; StateDetail = "正在构造输出文件"; try { htmlFile = new FileStream(htmlFilePath, FileMode.Create, FileAccess.Write); sw = new StreamWriter(htmlFile, Encoding.UTF8); sw.WriteLine(""); sw.WriteLine(""); sw.WriteLine(""); sw.WriteLine(""); sw.WriteLine("{0} {1}←{2}", type, fileNew[0].GetMergedVersion(), fileOld[0].GetMergedVersion()); sw.WriteLine(""); sw.WriteLine(""); sw.WriteLine(""); //输出概况 sw.WriteLine("

"); sw.WriteLine(""); sw.WriteLine(""); sw.WriteLine("", string.Join("
", fileNew.Select(wzf => wzf.Header.FileName)), string.Join("
", fileNew.Select(wzf => wzf.Header.FileSize.ToString("N0"))), string.Join("
", fileNew.Select(wzf => wzf.GetMergedVersion())) ); sw.WriteLine("", string.Join("
", fileOld.Select(wzf => wzf.Header.FileName)), string.Join("
", fileOld.Select(wzf => wzf.Header.FileSize.ToString("N0"))), string.Join("
", fileOld.Select(wzf => wzf.GetMergedVersion())) ); sw.WriteLine("", DateTime.Now); sw.WriteLine("", string.Join("
", new[] { this.OutputPng ? "-OutputPng" : null, this.OutputAddedImg ? "-OutputAddedImg" : null, this.OutputRemovedImg ? "-OutputRemovedImg" : null, "-PngComparison " + this.Comparer.PngComparison, this.Comparer.ResolvePngLink ? "-ResolvePngLink" : null, }.Where(p => p != null))); sw.WriteLine("
 文件名文件大小文件版本
新文件{0}{1}{2}
旧文件{0}{1}{2}
对比时间{0:yyyy-MM-dd HH:mm:ss.fff}
参数{0}
"); sw.WriteLine("

"); //输出目录 StringBuilder[] sb = { new StringBuilder(), new StringBuilder(), new StringBuilder() }; int[] count = new int[6]; string[] diffStr = { "修改", "新增", "移除" }; foreach (CompareDifference diff in diffLst) { int idx = -1; string detail = null; switch (diff.DifferenceType) { case DifferenceType.Changed: idx = 0; detail = string.Format("{0}", diff.NodeNew.FullPathToFile, idx, count[idx]); break; case DifferenceType.Append: idx = 1; if (this.OutputAddedImg) { detail = string.Format("{0}", diff.NodeNew.FullPathToFile, idx, count[idx]); } else { detail = diff.NodeNew.FullPathToFile; } break; case DifferenceType.Remove: idx = 2; if (this.OutputRemovedImg) { detail = string.Format("{0}", diff.NodeOld.FullPathToFile, idx, count[idx]); } else { detail = diff.NodeOld.FullPathToFile; } break; default: continue; } sb[idx].Append(""); sb[idx].Append(detail); sb[idx].AppendLine(""); count[idx]++; } StateDetail = "正在输出目录"; Array.Copy(count, 0, count, 3, 3); for (int i = 0; i < sb.Length; i++) { sw.WriteLine("", i); sw.WriteLine("", diffStr[i], count[i]); sw.Write(sb[i].ToString()); sw.WriteLine("
{0}共{1}项
"); sb[i] = null; count[i] = 0; } foreach (CompareDifference diff in diffLst) { switch (diff.DifferenceType) { case DifferenceType.Changed: { StateInfo = string.Format("{0}/{1}正在对比{2}", count[0], count[3], diff.NodeNew.FullPath); Wz_Image imgNew, imgOld; if ((imgNew = diff.ValueNew as Wz_Image) != null && ((imgOld = diff.ValueOld as Wz_Image) != null)) { string anchorName = "a_0_" + count[0]; string menuAnchorName = "m_0_" + count[0]; CompareImg(imgNew, imgOld, diff.NodeNew.FullPathToFile, anchorName, menuAnchorName, srcDirPath, sw); } count[0]++; } break; case DifferenceType.Append: if (this.OutputAddedImg) { StateInfo = string.Format("{0}/{1}正在输出新增{2}", count[1], count[4], diff.NodeNew.FullPath); Wz_Image imgNew = diff.ValueNew as Wz_Image; if (imgNew != null) { string anchorName = "a_1_" + count[1]; string menuAnchorName = "m_1_" + count[1]; OutputImg(imgNew, diff.DifferenceType, diff.NodeNew.FullPathToFile, anchorName, menuAnchorName, srcDirPath, sw); } count[1]++; } break; case DifferenceType.Remove: if (this.OutputRemovedImg) { StateInfo = string.Format("{0}/{1}正在输出删除{2}", count[2], count[5], diff.NodeOld.FullPath); Wz_Image imgOld = diff.ValueOld as Wz_Image; if (imgOld != null) { string anchorName = "a_2_" + count[2]; string menuAnchorName = "m_2_" + count[2]; OutputImg(imgOld, diff.DifferenceType, diff.NodeOld.FullPathToFile, anchorName, menuAnchorName, srcDirPath, sw); } count[2]++; } break; case DifferenceType.NotChanged: break; } } //html结束 sw.WriteLine(""); sw.WriteLine(""); } finally { try { if (sw != null) { sw.Flush(); sw.Close(); } } catch { } } } private void CompareImg(Wz_Image imgNew, Wz_Image imgOld, string imgName, string anchorName, string menuAnchorName, string outputDir, StreamWriter sw) { StateDetail = "正在解压img"; if (!imgNew.TryExtract() || !imgOld.TryExtract()) return; StateDetail = "正在对比img"; List diffList = new List(Comparer.Compare(imgNew.Node, imgOld.Node)); StringBuilder sb = new StringBuilder(); int[] count = new int[3]; StateDetail = "正在统计概况并输出资源文件...变动项共" + diffList.Count; foreach (var diff in diffList) { int idx = -1; string fullPath = null; string fullPathToFile = null; switch (diff.DifferenceType) { case DifferenceType.Changed: idx = 0; fullPath = diff.NodeNew.FullPath; fullPathToFile = diff.NodeNew.FullPathToFile; break; case DifferenceType.Append: idx = 1; fullPath = diff.NodeNew.FullPath; fullPathToFile = diff.NodeNew.FullPathToFile; break; case DifferenceType.Remove: idx = 2; fullPath = diff.NodeOld.FullPath; fullPathToFile = diff.NodeOld.FullPathToFile; break; } sb.AppendFormat("", idx); sb.AppendFormat("{0}", fullPath ?? " "); sb.AppendFormat("{0}", OutputNodeValue(fullPathToFile, diff.ValueNew, 0, outputDir) ?? " "); sb.AppendFormat("{0}", OutputNodeValue(fullPathToFile, diff.ValueOld, 1, outputDir) ?? " "); sb.AppendLine(""); count[idx]++; } StateDetail = "正在输出对比报告"; bool noChange = diffList.Count <= 0; sw.WriteLine("", noChange ? " noChange" : ""); sw.WriteLine("", imgName, anchorName, count[0], count[1], count[2]); sw.WriteLine(sb.ToString()); sw.WriteLine("", "回到目录", menuAnchorName); sw.WriteLine("
{0} 修改:{2} 新增:{3} 移除:{4}
{0}
"); imgNew.Unextract(); imgOld.Unextract(); sb = null; } private void OutputImg(Wz_Image img, DifferenceType diffType, string imgName, string anchorName, string menuAnchorName, string outputDir, StreamWriter sw) { StateDetail = "正在解压img"; if (!img.TryExtract()) return; int idx = 0; ; switch (diffType) { case DifferenceType.Changed: idx = 0; break; case DifferenceType.Append: idx = 1; break; case DifferenceType.Remove: idx = 2; break; } Action fnOutput = null; fnOutput = node => { if (node != null) { string fullPath = node.FullPath; string fullPathToFile = node.FullPathToFile; sw.Write("", idx); sw.Write("{0}", fullPath ?? " "); sw.Write("{0}", OutputNodeValue(fullPathToFile, node.Value, 0, outputDir) ?? " "); sw.WriteLine(""); if (node.Nodes.Count > 0) { foreach (Wz_Node child in node.Nodes) { fnOutput(child); } } } }; StateDetail = "正在输出完整img结构"; sw.WriteLine(""); sw.WriteLine("", imgName, anchorName); fnOutput(img.Node); sw.WriteLine("", "回到目录", menuAnchorName); sw.WriteLine("
wz_image: {0}
{0}
"); img.Unextract(); } protected virtual string OutputNodeValue(string fullPath, object value, int col, string outputDir) { if (value == null) return null; switch (value) { case Wz_Png png: if (OutputPng) { char[] invalidChars = Path.GetInvalidFileNameChars(); string colName = col == 0 ? "new" : (col == 1 ? "old" : col.ToString()); string fileName = fullPath.Replace('\\', '.'); string suffix = "_" + colName + ".png"; if (this.HashPngFileName) { fileName = ToHexString(MD5Hash(fileName)); // TODO: save file name mapping to another file? } else if (Environment.OSVersion.Platform == PlatformID.Win32NT && fileName.Length + suffix.Length > 255) { // force hashing if the file name too long. // TODO: also need to check full file path, we have tested that all existing browsers on Windows cannot load // local files with excessively long path. fileName = ToHexString(MD5Hash(fileName)); } else { for (int i = 0; i < invalidChars.Length; i++) { fileName = fileName.Replace(invalidChars[i], '_'); } } fileName = fileName + suffix; using (Bitmap bmp = png.ExtractPng()) { bmp.Save(Path.Combine(outputDir, fileName), System.Drawing.Imaging.ImageFormat.Png); } return string.Format("", new DirectoryInfo(outputDir).Name, WebUtility.UrlEncode(fileName)); } else { return string.Format("png {0}*{1} ({2} bytes)", png.Width, png.Height, png.DataLength); } case Wz_Uol uol: return uol.Uol; case Wz_Vector vector: return string.Format("({0}, {1})", vector.X, vector.Y); case Wz_Sound sound: return string.Format("sound {0}ms", sound.Ms); case Wz_Convex convex: return string.Format("convex {0}", string.Join(" ", convex.Points.Select(vec => $"({vec.X},{vec.Y})"))); case Wz_RawData rawData: return string.Format("rawdata {0} bytes", rawData.Length); case Wz_Video video: return string.Format("video {0} bytes", video.Length); case Wz_Image _: return "{ img }"; default: return string.Format("{1}", value.GetType().Name, WebUtility.HtmlEncode(Convert.ToString(value))); } } public virtual void CreateStyleSheet(string outputDir) { string path = Path.Combine(outputDir, "style.css"); if (File.Exists(path)) return; StringBuilder css = new StringBuilder(); css.AppendLine($"body {{ font-size:12px; background-color:{ColorToHex(ColorTable[0])}; color:{ColorToHex(ColorTable[1])}; }}"); css.AppendLine($"a {{ color:{ColorToHex(ColorTable[8])}; }}"); css.AppendLine($"p.wzf {{ }}"); css.AppendLine($"table, tr, th, td {{ border:1px solid #ff8000; border-collapse:collapse; }}"); css.AppendLine($"table {{ margin-bottom:16px; }}"); css.AppendLine($"th {{ text-align:left; }}"); css.AppendLine($"table.lst0 {{ }}"); css.AppendLine($"table.lst1 {{ }}"); css.AppendLine($"table.lst2 {{ }}"); css.AppendLine($"table.img {{ }}"); css.AppendLine($"table.img tr.r0 {{ background-color:{ColorToHex(ColorTable[2])}; color:{ColorToHex(ColorTable[5])}; }}"); css.AppendLine($"table.img tr.r1 {{ background-color:{ColorToHex(ColorTable[3])}; color:{ColorToHex(ColorTable[6])}; }}"); css.AppendLine($"table.img tr.r2 {{ background-color:{ColorToHex(ColorTable[4])}; color:{ColorToHex(ColorTable[7])}; }}"); css.AppendLine($"table.img.noChange {{ display:none; }}"); FileStream fs = new FileStream(path, FileMode.Create, FileAccess.Write); StreamWriter sw = new StreamWriter(fs, Encoding.UTF8); sw.Write(css.ToString()); sw.Flush(); sw.Close(); } private static byte[] MD5Hash(string text) { using (var md5 = MD5.Create()) { return md5.ComputeHash(Encoding.UTF8.GetBytes(text)); } } private static string ToHexString(byte[] inArray) { StringBuilder hex = new StringBuilder(inArray.Length * 2); foreach (byte b in inArray) { hex.AppendFormat("{0:x2}", b); } return hex.ToString(); } private static string ColorToHex(Color color) { return $"#{color.R:X2}{color.G:X2}{color.B:X2}"; } } } ================================================ FILE: WzComparerR2/Comparer/WzFileComparer.cs ================================================ using System; using System.Collections.Generic; using System.Text; using System.Linq; using WzComparerR2.WzLib; using WzComparerR2.Common; namespace WzComparerR2.Comparer { public class WzFileComparer { public WzFileComparer() { this.PngComparison = WzPngComparison.SizeAndDataLength; ResolvePngLink = true; } public WzPngComparison PngComparison { get; set; } public bool IgnoreWzFile { get; set; } public bool ResolvePngLink { get; set; } private DisposeQueue _disposeQueue; private List _currentWzImg = new List(); public IEnumerable Compare(Wz_Node nodeNew, Wz_Node nodeOld) { _currentWzImg.Clear(); AppendContext(nodeNew); AppendContext(nodeOld); var cmp = Compare( nodeNew == null ? null : new WzNodeAgent(nodeNew).Children, nodeOld == null ? null : new WzNodeAgent(nodeOld).Children); foreach (var diff in cmp) { yield return diff; } } public IEnumerable Compare(WzVirtualNode nodeNew, WzVirtualNode nodeOld) { _currentWzImg.Clear(); foreach (var node in nodeNew.LinkNodes) AppendContext(node); foreach (var node in nodeOld.LinkNodes) AppendContext(node); var cmp = Compare( nodeNew == null ? null : new WzVirtualNodeAgent(nodeNew).Children, nodeOld == null ? null : new WzVirtualNodeAgent(nodeOld).Children); foreach (var diff in cmp) { yield return diff; } } private void AppendContext(Wz_Node node) { Wz_Image wzImg = node.GetNodeWzImage(); if (wzImg != null) { _currentWzImg.Add(wzImg); } } private IEnumerable Compare(ComparableNode nodeNew, ComparableNode nodeOld) { var cmp = Compare( nodeNew == null ? null : nodeNew.Children, nodeOld == null ? null : nodeOld.Children); foreach (var diff in cmp) { yield return diff; } } private IEnumerable Compare(IEnumerable nodeNew, IEnumerable nodeOld) { if (nodeNew == null && nodeOld == null) //do nothing { yield break; } //初始化 排序 var arrayNew = new List(); var arrayOld = new List(); if (nodeNew != null) { arrayNew.AddRange(nodeNew); arrayNew.Sort(); } if (nodeOld != null) { arrayOld.AddRange(nodeOld); arrayOld.Sort(); } foreach (var diff in CompareSortedNodes(arrayNew, arrayOld)) { yield return diff; } //移除引用 arrayNew = null; arrayOld = null; } private IEnumerable CompareSortedNodes(IList arrayNew, IList arrayOld, Comparison compFunc = null) { //逐层对比 int l = 0, r = 0; while (l < arrayNew.Count || r < arrayOld.Count) { int? comp = null; if (r == arrayOld.Count) //输出左边 { comp = -1; } else if (l == arrayNew.Count) //输出右边 { comp = 1; } else { comp = compFunc != null ? compFunc(arrayNew[l], arrayOld[r]) : arrayNew[l].CompareTo(arrayOld[r]); } switch (comp) { case -1: yield return new CompareDifference(arrayNew[l].LinkNode, null, DifferenceType.Append); if (CompareChild(arrayNew[l], null)) { foreach (CompareDifference diff in Compare(arrayNew[l], null)) { yield return diff; } } l++; break; case 0: //TODO: 试着比较多linkNode的场合。。 if ((arrayNew[l].HasMultiValues || arrayOld[r].HasMultiValues) && !(arrayNew[l].Value is Wz_File wzf1 && !wzf1.IsSubDir || arrayOld[r].Value is Wz_File wzf2 && !wzf2.IsSubDir) //file跳过 ) { //对比node的绝对路径 var left = (arrayNew[l] as WzVirtualNodeAgent).Target.LinkNodes; var right = (arrayOld[r] as WzVirtualNodeAgent).Target.LinkNodes; var compFunc2 = new Comparison((a, b) => string.Compare(a.FullPathToFile, b.FullPathToFile)); left.Sort(compFunc2); right.Sort(compFunc2); foreach (var diff in CompareSortedNodes( left.Select(n => (ComparableNode)new WzNodeAgent(n)).ToList(), right.Select(n => (ComparableNode)new WzNodeAgent(n)).ToList(), (a, b) => Math.Sign(string.CompareOrdinal(a.LinkNode.FullPath, b.LinkNode.FullPath))) ) { yield return diff; } } else { bool compared = false; bool linkFilter = false; //同是png 检测link if (ResolvePngLink) { ComparableNode nodeNew = arrayNew[l], nodeOld = arrayOld[r]; PngLinkInfo linkInfoNew, linkInfoOld; bool linkNew = TryGetLink(nodeNew, out linkInfoNew), linkOld = TryGetLink(nodeOld, out linkInfoOld); if (linkNew && !linkOld && nodeOld.Value is Wz_Png) //图片转化为link { var newPng = GetLinkedPng(nodeNew.LinkNode); if (newPng != null) { if (!CompareData(newPng, nodeOld.Value)) { yield return new CompareDifference(nodeNew.LinkNode, nodeOld.LinkNode, DifferenceType.Changed); } else //链接后图片一致 过滤link标记 { linkFilter = true; } compared = true; } } else if (!linkNew && linkOld && nodeNew.Value is Wz_Png) //link恢复为图片 { var oldPng = GetLinkedPng(nodeOld.LinkNode); if (oldPng != null) { if (!CompareData(nodeNew.Value, oldPng)) { yield return new CompareDifference(nodeNew.LinkNode, nodeOld.LinkNode, DifferenceType.Changed); } else //链接后图片一致 过滤link标记 { linkFilter = true; } compared = true; } } else if (linkNew && linkOld) //两边都是link { if (linkInfoNew.LinkType == linkInfoOld.LinkType && linkInfoNew.LinkUrl == linkInfoOld.LinkUrl) //link没有变动 { compared = true; } else { var newPng = GetLinkedPng(nodeNew.LinkNode); var oldPng = GetLinkedPng(nodeOld.LinkNode); if (newPng != null && oldPng != null) { if (newPng != oldPng && !CompareData(newPng, oldPng)) //对比有差异 不输出dummy { //yield return new CompareDifference(nodeNew.LinkNode, nodeOld.LinkNode, DifferenceType.Changed); } else { linkFilter = true; } compared = true; } } } } //正常对比 if (!compared && !CompareData(arrayNew[l].Value, arrayOld[r].Value)) { yield return new CompareDifference(arrayNew[l].LinkNode, arrayOld[r].LinkNode, DifferenceType.Changed); } //对比子集 if (CompareChild(arrayNew[l], arrayOld[r])) { foreach (CompareDifference diff in Compare(arrayNew[l], arrayOld[r])) { if (linkFilter) // && diff.DifferenceType != DifferenceType.Changed) [s]过滤新增或删除[/s] 全部过滤 { if ((diff.NodeNew?.ParentNode == arrayNew[l].LinkNode || diff.NodeOld?.ParentNode == arrayOld[r].LinkNode)) //差异节点为当前的子级 { var nodeText = diff.NodeNew?.Text ?? diff.NodeOld?.Text; if (nodeText == "_inlink" || nodeText == "_outlink") { continue; } } } yield return diff; } } } l++; r++; break; case 1: yield return new CompareDifference(null, arrayOld[r].LinkNode, DifferenceType.Remove); if (CompareChild(null, arrayOld[r])) { foreach (CompareDifference diff in Compare(null, arrayOld[r])) { yield return diff; } } r++; break; default: throw new Exception("什么鬼"); } } } private bool CompareChild(ComparableNode node1, ComparableNode node2) { if ((node1 != null && node1.Value is Wz_File wzf1 && !wzf1.IsSubDir) || (node2 != null && node2.Value is Wz_File wzf2 && !wzf2.IsSubDir)) { return !IgnoreWzFile; } return true; } private bool TryGetLink(ComparableNode node, out PngLinkInfo linkInfo) { linkInfo = new PngLinkInfo(); var png = node.Value as Wz_Png; if (png != null && png.Width == 1 && png.Height == 1) { var node1 = node.LinkNode; WzLib.Wz_Node linkNode; if ((linkNode = node1.Nodes["_inlink"]) != null) { linkInfo.LinkType = PngLinkType.Inlink; linkInfo.LinkUrl = linkNode.GetValue(); return true; } else if ((linkNode = node1.Nodes["_outlink"]) != null) { linkInfo.LinkType = PngLinkType.Outlink; linkInfo.LinkUrl = linkNode.GetValue(); return true; } } return false; } private Wz_Png GetLinkedPng(Wz_Node node) { var wzFile = node.GetNodeWzFile(); if (wzFile != null) { var linkNode = node.GetLinkedSourceNode(path => PluginBase.PluginManager.FindWz(path, wzFile)); //添加回收池机制... if (linkNode != null) { var linkImg = linkNode.GetNodeWzImage(); if (linkImg != null && !_currentWzImg.Contains(linkImg)) { if (_disposeQueue == null) { _disposeQueue = new DisposeQueue(32); } _disposeQueue.Add(linkImg, _currentWzImg); } } return linkNode.GetValueEx(null); } return null; } /// /// 比较两个节点绑定的值是否相同。 /// /// 新的值。 /// 旧的值。 /// public virtual bool CompareData(object dataNew, object dataOld) { // skip virtual dir { if (dataNew is Wz_File fileNew && fileNew.IsSubDir) dataNew = null; if (dataOld is Wz_File fileOld && fileOld.IsSubDir) dataOld = null; } if (dataNew == null && dataOld == null) return true; if (dataNew == null ^ dataOld == null) return false; Type type = dataNew.GetType(); if (type != dataOld.GetType()) return false; if (type.IsClass) { switch (dataNew) { case string str: return str == (string)dataOld; case Wz_Image img: Wz_Image imgOld = (Wz_Image)dataOld; return img.Size == imgOld.Size && img.Checksum == imgOld.Checksum; case Wz_File file: Wz_File fileOld = (Wz_File)dataOld; return file.Type == fileOld.Type; case Wz_Png png: Wz_Png pngOld = (Wz_Png)dataOld; switch (this.PngComparison) { case WzPngComparison.SizeOnly: return png.Width == pngOld.Width && png.Height == pngOld.Height; case WzPngComparison.SizeAndDataLength: return png.Width == pngOld.Width && png.Height == pngOld.Height && png.DataLength == pngOld.DataLength; case WzPngComparison.Pixel: if (!(png.Width == pngOld.Width && png.Height == pngOld.Height && png.Format == pngOld.Format && png.Scale == pngOld.Scale)) { // we don't compare 'Pages' because KMST set pages to 1 for all PNGs. return false; } byte[] pixelNew = png.GetRawData(); byte[] pixelOld = pngOld.GetRawData(); if (pixelNew == null || pixelOld == null || pixelNew.Length != pixelOld.Length) { return false; } return pixelNew.SequenceEqual(pixelOld); default: goto case WzPngComparison.SizeAndDataLength; } break; case Wz_Vector vector: Wz_Vector vectorOld = (Wz_Vector)dataOld; return vector.X == vectorOld.X && vector.Y == vectorOld.Y; case Wz_Uol uol: return uol.Uol == ((Wz_Uol)dataOld).Uol; case Wz_Sound sound: Wz_Sound soundOld = (Wz_Sound)dataOld; return sound.Ms == soundOld.Ms && sound.DataLength == soundOld.DataLength; case Wz_Convex convex: Wz_Convex convexOld = (Wz_Convex)dataOld; if (convex.Points.Length != convexOld.Points.Length) { return false; } for (int i = 0; i < convex.Points.Length; i++) { var vectorNew = convex.Points[i]; vectorOld = convexOld.Points[i]; if (vectorNew.X != vectorOld.X || vectorNew.Y != vectorOld.Y) return false; } return true; case Wz_RawData rawData: Wz_RawData rawDataOld = (Wz_RawData)dataOld; return rawData.Length == rawDataOld.Length; case Wz_Video video: Wz_Video videoOld = (Wz_Video)dataOld; return video.Length == videoOld.Length; } } return object.Equals(dataNew, dataOld); } private abstract class ComparableNode : IComparable { public abstract string Name { get; } public abstract object Value { get; } public abstract bool HasMultiValues { get; } public virtual IEnumerable Values { get { if (this.Value == null) return Enumerable.Empty(); return new[] { this.Value }; } } public abstract IEnumerable Children { get; } public virtual Wz_Node LinkNode { get { return null; } } public int CompareTo(ComparableNode other) { return Math.Sign(string.CompareOrdinal(this.Name, other.Name)); } } private class WzNodeAgent : ComparableNode { public WzNodeAgent(Wz_Node target) { this.Target = target; } public Wz_Node Target { get; private set; } public override string Name { get { return this.Target.Text; } } public override object Value { get { return this.Target.Value; } } public override bool HasMultiValues { get { return false; } } public override IEnumerable Children { get { foreach (var node in this.Target.Nodes) { yield return new WzNodeAgent(node); } } } public override Wz_Node LinkNode { get { return this.Target; } } } private class WzVirtualNodeAgent : ComparableNode { public WzVirtualNodeAgent(WzVirtualNode target) { this.Target = target; } public WzVirtualNode Target { get; private set; } public override string Name { get { return this.Target.Name; } } public override object Value { get { return this.Target.LinkNodes.Select(n => n.Value) .Where(v => v != null) .FirstOrDefault(); } } public override bool HasMultiValues { get { return this.Values.Count() > 1; } } public override IEnumerable Values { get { return this.Target.LinkNodes.Select(n => n.Value) .Where(v => v != null); } } public override IEnumerable Children { get { foreach (var node in this.Target.ChildNodes) { yield return new WzVirtualNodeAgent(node); } } } public override Wz_Node LinkNode { get { if (this.Target.LinkNodes.Count <= 0) { return null; } else if (this.Target.LinkNodes.Count == 1) { return this.Target.LinkNodes[0]; } else { foreach (var node in this.Target.LinkNodes) { if (node.Value != null) { return node; } } return this.Target.LinkNodes[0]; } } } } private class DisposeQueue { public DisposeQueue(int maxCount) { this.MaxCount = maxCount; _list = new LinkedList(); _dict = new Dictionary>(); } public int MaxCount { get; set; } private LinkedList _list; private Dictionary> _dict; public void Add(Wz_Image wzImage) { Add(wzImage, null); } public void Add(Wz_Image wzImage, List currentImages) { LinkedListNode node; if (_dict.TryGetValue(wzImage, out node)) { //提升位置 if (node.Previous != null) { _list.Remove(node); _list.AddFirst(node); } } else { //添加item while (_list.Count >= MaxCount && _list.Count > 0) { DisposeLast(currentImages); } node = _list.AddFirst(wzImage); _dict.Add(wzImage, node); } } public void DisposeAll() { while(_list.Count > 0) { DisposeLast(); } } private void DisposeLast() { this.DisposeLast(null); } private void DisposeLast(List currentImages) { var last = _list.Last; if (currentImages == null || !currentImages.Contains(last.Value)) { last.Value.Unextract(); } _dict.Remove(last.Value); _list.Remove(last); } } private struct PngLinkInfo { public PngLinkType LinkType { get; set; } public string LinkUrl { get; set; } } private enum PngLinkType { None = 0, Inlink = 1, Outlink = 2 } } } ================================================ FILE: WzComparerR2/Comparer/WzPngComparison.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace WzComparerR2.Comparer { public enum WzPngComparison { SizeOnly, SizeAndDataLength, Pixel } } ================================================ FILE: WzComparerR2/Comparer/WzVirtualNode.cs ================================================ using System; using System.Collections.Generic; using System.Text; using System.Linq; using WzComparerR2.WzLib; namespace WzComparerR2.Comparer { public class WzVirtualNode { public WzVirtualNode() { this.LinkNodes = new List(4); this.ChildNodes = new List(); } public WzVirtualNode(Wz_Node wzNode) : this() { this.Name = wzNode.Text; this.LinkNodes.Add(wzNode); } public string Name { get; set; } public List LinkNodes { get; private set; } public List ChildNodes { get; private set; } public void AddChild(WzVirtualNode childNode) { this.ChildNodes.Add(childNode); } public void AddChild(Wz_Node wzNode) { this.AddChild(wzNode, false); } public void AddChild(Wz_Node wzNode, bool addAllChildren) { var childNode = new WzVirtualNode(wzNode); this.AddChild(childNode); if (addAllChildren && wzNode.Nodes.Count > 0) { foreach (var node in wzNode.Nodes) { childNode.AddChild(node, addAllChildren); } } } public void Combine(Wz_Node wzNode) { this.LinkNodes.Add(wzNode); bool needCheck = this.ChildNodes.Count > 0; foreach (var fromChild in wzNode.Nodes) { //如果当前本身为空 省去检查合并 因为wz本身并不重复... WzVirtualNode toChild = null; if (needCheck) { toChild = FindChild(fromChild.Text); } if (toChild == null) //没有找到 新增 { this.AddChild(fromChild, true); } else if (fromChild.Value == null && toChild.HasNoValue()) //同为目录 { toChild.Combine(fromChild); } else if (fromChild.Nodes.Count <= 0 && !toChild.HasDirectory()) //没有子集 合并测试 { toChild.Combine(fromChild); } else { throw new Exception(string.Format("WZ合并失败,{0}已存在并且存在子级。", fromChild.FullPathToFile)); } } } private WzVirtualNode FindChild(string name) { foreach (var child in this.ChildNodes) { if (child.Name == name) { return child; } } return null; } private bool HasNoValue() { foreach (var linkNode in this.LinkNodes) { if (linkNode.Value != null) { return false; } } return true; } private bool HasDirectory() { foreach(var linkNode in this.LinkNodes) { if (linkNode.Nodes.Count > 0) { return true; } } return false; } public override string ToString() { return string.Format("{0} link:{1} child:{2}", this.Name, this.LinkNodes.Count, this.ChildNodes.Count); } } } ================================================ FILE: WzComparerR2/Config/CharaSimConfig.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Configuration; namespace WzComparerR2.Config { [SectionName("WcR2.CharaSim")] public sealed class CharaSimConfig : ConfigSectionBase { [ConfigurationProperty("selectedFontIndex")] public ConfigItem SelectedFontIndex { get { return (ConfigItem)this["selectedFontIndex"]; } set { this["selectedFontIndex"] = value; } } [ConfigurationProperty("autoQuickView")] public ConfigItem AutoQuickView { get { return (ConfigItem)this["autoQuickView"]; } set { this["autoQuickView"] = value; } } [ConfigurationProperty("skill")] public CharaSimSkillConfig Skill { get { return (CharaSimSkillConfig)this["skill"]; } } [ConfigurationProperty("damageSkin")] public CharaSimDamageSkinConfig DamageSkin { get { return (CharaSimDamageSkinConfig)this["damageSkin"]; } } [ConfigurationProperty("gear")] public CharaSimGearConfig Gear { get { return (CharaSimGearConfig)this["gear"]; } } [ConfigurationProperty("item")] public CharaSimItemConfig Item { get { return (CharaSimItemConfig)this["item"]; } } [ConfigurationProperty("recipe")] public CharaSimRecipeConfig Recipe { get { return (CharaSimRecipeConfig)this["recipe"]; } } [ConfigurationProperty("mob")] public CharaSimMobConfig Mob { get { return (CharaSimMobConfig)this["mob"]; } } [ConfigurationProperty("npc")] public CharaSimNpcConfig Npc { get { return (CharaSimNpcConfig)this["npc"]; } } } } ================================================ FILE: WzComparerR2/Config/CharaSimDamageSkinConfig.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Configuration; namespace WzComparerR2.Config { public class CharaSimDamageSkinConfig : ConfigurationElement { [ConfigurationProperty("showDamageSkinID", DefaultValue = true)] public bool ShowDamageSkinID { get { return (bool)this["showDamageSkinID"]; } set { this["showDamageSkinID"] = value; } } [ConfigurationProperty("showDamageSkin", DefaultValue = true)] public bool ShowDamageSkin { get { return (bool)this["showDamageSkin"]; } set { this["showDamageSkin"] = value; } } [ConfigurationProperty("useMiniSize", DefaultValue = false)] public bool UseMiniSize { get { return (bool)this["useMiniSize"]; } set { this["useMiniSize"] = value; } } [ConfigurationProperty("alwaysUseMseaFormat", DefaultValue = false)] public bool AlwaysUseMseaFormat { get { return (bool)this["alwaysUseMseaFormat"]; } set { this["alwaysUseMseaFormat"] = value; } } [ConfigurationProperty("displayUnitOnSingleLine", DefaultValue = false)] public bool DisplayUnitOnSingleLine { get { return (bool)this["displayUnitOnSingleLine"]; } set { this["displayUnitOnSingleLine"] = value; } } [ConfigurationProperty("damageSkinNumber", DefaultValue = (long)1234567890)] public long DamageSkinNumber { get { return (long)this["damageSkinNumber"]; } set { this["damageSkinNumber"] = value; } } } } ================================================ FILE: WzComparerR2/Config/CharaSimGearConfig.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Configuration; namespace WzComparerR2.Config { public class CharaSimGearConfig : ConfigurationElement { [ConfigurationProperty("showID", DefaultValue = true)] public bool ShowID { get { return (bool)this["showID"]; } set { this["showID"] = value; } } [ConfigurationProperty("showWeaponSpeed", DefaultValue = true)] public bool ShowWeaponSpeed { get { return (bool)this["showWeaponSpeed"]; } set { this["showWeaponSpeed"] = value; } } [ConfigurationProperty("showLevelOrSealed", DefaultValue = true)] public bool ShowLevelOrSealed { get { return (bool)this["showLevelOrSealed"]; } set { this["showLevelOrSealed"] = value; } } [ConfigurationProperty("showMedalTag", DefaultValue = false)] public bool ShowMedalTag { get { return (bool)this["showMedalTag"]; } set { this["showMedalTag"] = value; } } } } ================================================ FILE: WzComparerR2/Config/CharaSimItemConfig.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Configuration; namespace WzComparerR2.Config { public class CharaSimItemConfig : ConfigurationElement { [ConfigurationProperty("showID", DefaultValue = true)] public bool ShowID { get { return (bool)this["showID"]; } set { this["showID"] = value; } } [ConfigurationProperty("linkRecipeInfo", DefaultValue = true)] public bool LinkRecipeInfo { get { return (bool)this["linkRecipeInfo"]; } set { this["linkRecipeInfo"] = value; } } [ConfigurationProperty("linkRecipeItem", DefaultValue = true)] public bool LinkRecipeItem { get { return (bool)this["linkRecipeItem"]; } set { this["linkRecipeItem"] = value; } } [ConfigurationProperty("showNickTag", DefaultValue = false)] public bool ShowNickTag { get { return (bool)this["showNickTag"]; } set { this["showNickTag"] = value; } } } } ================================================ FILE: WzComparerR2/Config/CharaSimMobConfig.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Configuration; namespace WzComparerR2.Config { public class CharaSimMobConfig : ConfigurationElement { [ConfigurationProperty("showID", DefaultValue = true)] public bool ShowID { get { return (bool)this["showID"]; } set { this["showID"] = value; } } } } ================================================ FILE: WzComparerR2/Config/CharaSimNpcConfig.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Configuration; namespace WzComparerR2.Config { public class CharaSimNpcConfig : ConfigurationElement { [ConfigurationProperty("showID", DefaultValue = true)] public bool ShowID { get { return (bool)this["showID"]; } set { this["showID"] = value; } } } } ================================================ FILE: WzComparerR2/Config/CharaSimRecipeConfig.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Configuration; namespace WzComparerR2.Config { public class CharaSimRecipeConfig : ConfigurationElement { [ConfigurationProperty("showID", DefaultValue = true)] public bool ShowID { get { return (bool)this["showID"]; } set { this["showID"] = value; } } } } ================================================ FILE: WzComparerR2/Config/CharaSimSkillConfig.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Configuration; namespace WzComparerR2.Config { public class CharaSimSkillConfig : ConfigurationElement { [ConfigurationProperty("showID", DefaultValue = true)] public bool ShowID { get { return (bool)this["showID"]; } set { this["showID"] = value; } } [ConfigurationProperty("showProperties", DefaultValue = true)] public bool ShowProperties { get { return (bool)this["showProperties"]; } set { this["showProperties"] = value; } } [ConfigurationProperty("showDelay", DefaultValue = true)] public bool ShowDelay { get { return (bool)this["showDelay"]; } set { this["showDelay"] = value; } } [ConfigurationProperty("showReqSkill", DefaultValue = true)] public bool ShowReqSkill { get { return (bool)this["showReqSkill"]; } set { this["showReqSkill"] = value; } } [ConfigurationProperty("displayCooltimeMSAsSec", DefaultValue = true)] public bool DisplayCooltimeMSAsSec { get { return (bool)this["displayCooltimeMSAsSec"]; } set { this["displayCooltimeMSAsSec"] = value; } } [ConfigurationProperty("displayPermyriadAsPercent", DefaultValue = true)] public bool DisplayPermyriadAsPercent { get { return (bool)this["displayPermyriadAsPercent"]; } set { this["displayPermyriadAsPercent"] = value; } } [ConfigurationProperty("ignoreEvalError", DefaultValue = false)] public bool IgnoreEvalError { get { return (bool)this["ignoreEvalError"]; } set { this["ignoreEvalError"] = value; } } [ConfigurationProperty("defaultLevel", DefaultValue = DefaultLevel.LevelMax)] public DefaultLevel DefaultLevel { get { return (DefaultLevel)this["defaultLevel"]; } set { this["defaultLevel"] = value; } } [ConfigurationProperty("intervalLevel", DefaultValue = 10)] public int IntervalLevel { get { return (int)this["intervalLevel"]; } set { this["intervalLevel"] = value; } } } } ================================================ FILE: WzComparerR2/Config/CustomCSSConfig.cs ================================================ using System; using System.Collections.Generic; using System.Configuration; using System.Drawing; using System.Globalization; using System.Linq; using System.Text; using System.Windows.Forms.VisualStyles; namespace WzComparerR2.Config { [SectionName("WcR2.CustomCSS")] public class CustomCSSConfig : ConfigSectionBase { public CustomCSSConfig() { BackgroundColor = Color.FromArgb(Int32.Parse("ffffffff", NumberStyles.HexNumber)); NormalTextColor = Color.FromArgb(Int32.Parse("ff000000", NumberStyles.HexNumber)); ChangedBackgroundColor = Color.FromArgb(Int32.Parse("fffff4c4", NumberStyles.HexNumber)); AddedBackgroundColor = Color.FromArgb(Int32.Parse("ffebf2f8", NumberStyles.HexNumber)); RemovedBackgroundColor = Color.FromArgb(Int32.Parse("ffffffff", NumberStyles.HexNumber)); ChangedTextColor = Color.FromArgb(Int32.Parse("ff000000", NumberStyles.HexNumber)); AddedTextColor = Color.FromArgb(Int32.Parse("ff000000", NumberStyles.HexNumber)); RemovedTextColor = Color.FromArgb(Int32.Parse("ff000000", NumberStyles.HexNumber)); HyperlinkColor = Color.FromArgb(Int32.Parse("ff0000ff", NumberStyles.HexNumber)); } [ConfigurationProperty("backgroundColor")] public Color BackgroundColor { get { return (Color)this["backgroundColor"]; } set { this["backgroundColor"] = value; } } [ConfigurationProperty("normalTextColor")] public Color NormalTextColor { get { return (Color)this["normalTextColor"]; } set { this["normalTextColor"] = value; } } [ConfigurationProperty("changedBackgroundColor")] public Color ChangedBackgroundColor { get { return (Color)this["changedBackgroundColor"]; } set { this["changedBackgroundColor"] = value; } } [ConfigurationProperty("addedBackgroundColor")] public Color AddedBackgroundColor { get { return (Color)this["addedBackgroundColor"]; } set { this["addedBackgroundColor"] = value; } } [ConfigurationProperty("removedBackgroundColor")] public Color RemovedBackgroundColor { get { return (Color)this["removedBackgroundColor"]; } set { this["removedBackgroundColor"] = value; } } [ConfigurationProperty("changedTextColor")] public Color ChangedTextColor { get { return (Color)this["changedTextColor"]; } set { this["changedTextColor"] = value; } } [ConfigurationProperty("addedTextColor")] public Color AddedTextColor { get { return (Color)this["addedTextColor"]; } set { this["addedTextColor"] = value; } } [ConfigurationProperty("removedTextColor")] public Color RemovedTextColor { get { return (Color)this["removedTextColor"]; } set { this["removedTextColor"] = value; } } [ConfigurationProperty("hyperlinkColor")] public Color HyperlinkColor { get { return (Color)this["hyperlinkColor"]; } set { this["hyperlinkColor"] = value; } } } } ================================================ FILE: WzComparerR2/Config/ImageHandlerConfig.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Configuration; using System.Drawing; namespace WzComparerR2.Config { [SectionName("WcR2.ImageHandler")] public sealed class ImageHandlerConfig : ConfigSectionBase { public ImageHandlerConfig() { BackgroundColor = Color.White; BackgroundType = ImageBackgroundType.Transparent; MinMixedAlpha = 0; MinDelay = 30; } [ConfigurationProperty("autoSavePictureFolder")] public ConfigItem AutoSavePictureFolder { get { return (ConfigItem)this["autoSavePictureFolder"]; } set { this["autoSavePictureFolder"] = value; } } [ConfigurationProperty("autoSaveEnabled")] public ConfigItem AutoSaveEnabled { get { return (ConfigItem)this["autoSaveEnabled"]; } set { this["autoSaveEnabled"] = value; } } [ConfigurationProperty("savePngFramesEnabled")] public ConfigItem SavePngFramesEnabled { get { return (ConfigItem)this["savePngFramesEnabled"]; } set { this["savePngFramesEnabled"] = value; } } [ConfigurationProperty("gifEncoder")] public ConfigItem GifEncoder { get { return (ConfigItem)this["gifEncoder"]; } set { this["gifEncoder"] = value; } } [ConfigurationProperty("backgroundType")] public ConfigItem BackgroundType { get { return (ConfigItem)this["backgroundType"]; } set { this["backgroundType"] = value; } } [ConfigurationProperty("backgroundColor")] public ConfigItem BackgroundColor { get { return (ConfigItem)this["backgroundColor"]; } set { this["backgroundColor"] = value; } } [ConfigurationProperty("minMixedAlpha")] public ConfigItem MinMixedAlpha { get { return (ConfigItem)this["minMixedAlpha"]; } set { this["minMixedAlpha"] = value; } } [ConfigurationProperty("minDelay")] public ConfigItem MinDelay { get { return (ConfigItem)this["minDelay"]; } set { this["minDelay"] = value; } } [ConfigurationProperty("mosaicInfo")] public MosaicInfo MosaicInfo { get { return (MosaicInfo)this["mosaicInfo"]; } set { this["mosaicInfo"] = value; } } [ConfigurationProperty("imageNameMethod")] public ConfigItem ImageNameMethod { get { return (ConfigItem)this["imageNameMethod"]; } set { this["imageNameMethod"] = value; } } [ConfigurationProperty("paletteOptimized")] public ConfigItem PaletteOptimized { get { return (ConfigItem)this["paletteOptimized"]; } set { this["paletteOptimized"] = value; } } [ConfigurationProperty("ffmpegBinPath")] public ConfigItem FFmpegBinPath { get { return (ConfigItem)this["ffmpegBinPath"]; } set { this["ffmpegBinPath"] = value; } } [ConfigurationProperty("ffmpegArgument")] public ConfigItem FFmpegArgument { get { return (ConfigItem)this["ffmpegArgument"]; } set { this["ffmpegArgument"] = value; } } [ConfigurationProperty("ffmpegOutputFileExtension")] public ConfigItem FFmpegOutputFileExtension { get { return (ConfigItem)this["ffmpegOutputFileExtension"]; } set { this["ffmpegOutputFileExtension"] = value; } } } public enum ImageBackgroundType { Transparent = 0, Color = 1, Mosaic = 2, } public enum ImageNameMethod { Default = 0, PathToImage = 1, PathToWz = 2 } } ================================================ FILE: WzComparerR2/Config/MosaicInfo.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Configuration; using System.Drawing; namespace WzComparerR2.Config { public class MosaicInfo : ConfigurationElement { [ConfigurationProperty("color0")] public Color Color0 { get { return (Color)this["color0"]; } set { this["color0"] = value; } } [ConfigurationProperty("color1")] public Color Color1 { get { return (Color)this["color1"]; } set { this["color1"] = value; } } [ConfigurationProperty("blockSize")] public int BlockSize { get { return (int)this["blockSize"]; } set { this["blockSize"] = value; } } } } ================================================ FILE: WzComparerR2/Config/WcR2Config.cs ================================================ using System; using System.Collections.Generic; using System.Configuration; using System.Drawing; using System.Linq; using System.Text; using System.Xml; using WzComparerR2.Patcher; namespace WzComparerR2.Config { [SectionName("WcR2")] public sealed class WcR2Config : ConfigSectionBase { public WcR2Config() { this.MainStyle = DevComponents.DotNetBar.eStyle.Office2007VistaGlass; this.MainStyleColor = Color.DimGray; this.SortWzOnOpened = true; this.AutoDetectExtFiles = true; this.EnableAutoUpdate = true; } /// /// 获取最近打开的文档列表。 /// [ConfigurationProperty("recentDocuments")] [ConfigurationCollection(typeof(ConfigArrayList.ItemElement))] public ConfigArrayList RecentDocuments { get { return (ConfigArrayList)this["recentDocuments"]; } } /// /// 获取或设置主窗体界面样式。 /// [ConfigurationProperty("mainStyle")] public ConfigItem MainStyle { get { return (ConfigItem)this["mainStyle"]; } set { this["mainStyle"] = value; } } /// /// 获取或设置主窗体界面主题色。 /// [ConfigurationProperty("mainStyleColor")] public ConfigItem MainStyleColor { get { return (ConfigItem)this["mainStyleColor"]; } set { this["mainStyleColor"] = value; } } /// /// 获取或设置Wz对比报告默认输出文件夹。 /// [ConfigurationProperty("comparerOutputFolder")] public ConfigItem ComparerOutputFolder { get { return (ConfigItem)this["comparerOutputFolder"]; } set { this["comparerOutputFolder"] = value; } } /// /// 获取或设置一个值,指示Wz文件加载后是否自动排序。 /// [ConfigurationProperty("sortWzOnOpened")] public ConfigItem SortWzOnOpened { get { return (ConfigItem)this["sortWzOnOpened"]; } set { this["sortWzOnOpened"] = value; } } /// /// 获取或设置一个值,指示Wz文件加载后是否自动排序。 /// [ConfigurationProperty("sortWzByImgID")] public ConfigItem SortWzByImgID { get { return (ConfigItem)this["sortWzByImgID"]; } set { this["sortWzByImgID"] = value; } } /// /// 获取或设置一个值,指示Wz加载中对于ansi字符串的编码。 /// [ConfigurationProperty("wzEncoding")] public ConfigItem WzEncoding { get { return (ConfigItem)this["wzEncoding"]; } set { this["wzEncoding"] = value; } } /// /// 获取或设置一个值,指示加载Base.wz时是否自动检测扩展wz文件(如Map2、Mob2)。 /// [ConfigurationProperty("autoDetectExtFiles")] public ConfigItem AutoDetectExtFiles { get { return (ConfigItem)this["autoDetectExtFiles"]; } set { this["autoDetectExtFiles"] = value; } } /// /// 获取或设置一个值,指示读取wz是否跳过img检测。 /// [ConfigurationProperty("imgCheckDisabled")] public ConfigItem ImgCheckDisabled { get { return (ConfigItem)this["imgCheckDisabled"]; } set { this["imgCheckDisabled"] = value; } } [ConfigurationProperty("patcherSettings")] [ConfigurationCollection(typeof(PatcherSetting), CollectionType = ConfigurationElementCollectionType.AddRemoveClearMap)] public PatcherSettingCollection PatcherSettings { get { return (PatcherSettingCollection)this["patcherSettings"]; } } /// /// 获取或设置一个值,指示Release版本下是否需要自动检查更新。 /// [ConfigurationProperty("EnableAutoUpdate")] public ConfigItem EnableAutoUpdate { get { return (ConfigItem)this["EnableAutoUpdate"]; } set { this["EnableAutoUpdate"] = value; } } private static readonly HashSet obsoleteElements = new HashSet(StringComparer.OrdinalIgnoreCase) { "wzVersionVerifyMode", }; protected override bool OnDeserializeUnrecognizedElement(string elementName, XmlReader reader) { if (obsoleteElements.Contains(elementName)) { reader.Skip(); return true; } return base.OnDeserializeUnrecognizedElement(elementName, reader); } } } ================================================ FILE: WzComparerR2/DBConnection.cs ================================================ using System; using System.Collections.Generic; using System.Text; using System.IO; using System.Data; using System.Text.RegularExpressions; using System.Drawing; using WzComparerR2.WzLib; using WzComparerR2.Common; using WzComparerR2.PluginBase; using WzComparerR2.CharaSimControl; using WzComparerR2.CharaSim; namespace WzComparerR2 { public class DBConnection { public DBConnection(StringLinker sl) { this.sl = sl; } private StringLinker sl; public DataSet GenerateSkillTable() { Wz_Node skillWz = PluginManager.FindWz(Wz_Type.Skill); if (skillWz == null) return null; Regex r = new Regex(@"^(\d+)\.img", RegexOptions.Compiled); DataSet ds = new DataSet(); DataTable jobTable = new DataTable("ms_job"); jobTable.Columns.Add("jobID", typeof(string)); jobTable.Columns.Add("jobName", typeof(string)); DataTable skillTable = new DataTable("ms_skill"); skillTable.Columns.Add("jobID", typeof(string)); skillTable.Columns.Add("skillID", typeof(string)); skillTable.Columns.Add("skillName", typeof(string)); skillTable.Columns.Add("skillDesc", typeof(string)); skillTable.Columns.Add("maxLevel", typeof(int)); skillTable.Columns.Add("invisible", typeof(bool)); skillTable.Columns.Add("hyper", typeof(int)); skillTable.Columns.Add("reqSkill", typeof(string)); skillTable.Columns.Add("reqSkillLevel", typeof(int)); skillTable.Columns.Add("reqLevel", typeof(int)); DataTable skillLevelTable = new DataTable("ms_skillLevel"); skillLevelTable.Columns.Add("skillID", typeof(string)); skillLevelTable.Columns.Add("level", typeof(int)); skillLevelTable.Columns.Add("levelDesc", typeof(string)); DataTable skillCommonTable = new DataTable("ms_skillCommon"); skillCommonTable.Columns.Add("skillID", typeof(string)); skillCommonTable.Columns.Add("commonName", typeof(string)); skillCommonTable.Columns.Add("commonValue", typeof(string)); DataTable skillPVPCommonTable = new DataTable("ms_skillPVPCommon"); skillPVPCommonTable.Columns.Add("skillID", typeof(string)); skillPVPCommonTable.Columns.Add("commonName", typeof(string)); skillPVPCommonTable.Columns.Add("commonValue", typeof(string)); DataTable skillHTable = new DataTable("ms_skillH"); skillHTable.Columns.Add("skillID", typeof(string)); skillHTable.Columns.Add("desc", typeof(string)); skillHTable.Columns.Add("pdesc", typeof(string)); skillHTable.Columns.Add("h", typeof(string)); skillHTable.Columns.Add("ph", typeof(string)); skillHTable.Columns.Add("hch", typeof(string)); StringResultSkill sr; foreach (Wz_Node node in skillWz.Nodes) { //获取职业 Match m = r.Match(node.Text); Wz_Image img = node.GetValue(null); if (!m.Success) { continue; } if (img == null || !img.TryExtract()) { continue; } //导入职业 string jobID = m.Result("$1"); sl.StringSkill2.TryGetValue(jobID, out var _sr); jobTable.Rows.Add(jobID, (_sr != null ? _sr["bookName"] : null)); //获取技能 Wz_Node skillListNode = img.Node.FindNodeByPath("skill"); if (skillListNode == null || skillListNode.Nodes.Count <= 0) { continue; } foreach (Wz_Node skillNode in skillListNode.Nodes) { Skill skill = Skill.CreateFromNode(skillNode, PluginManager.FindWz); if (skill == null) continue; // if (skill.Invisible) //过滤不可见技能 // continue; //导入技能 string skillID = skillNode.Text; sl.StringSkill2.TryGetValue(skillID, out _sr); sr = _sr as StringResultSkill; string reqSkill = null; int reqSkillLevel = 0; if (skill.ReqSkill.Count > 0) { foreach (var kv in skill.ReqSkill) { reqSkill = kv.Key.ToString(); reqSkillLevel = kv.Value; } } skillTable.Rows.Add( jobID, skillID, sr != null ? sr.Name : null, sr != null ? sr.Desc : null, skill.MaxLevel, skill.Invisible, skill.Hyper, reqSkill, reqSkillLevel, skill.ReqLevel ); if (!skill.PreBBSkill) { //导入技能common foreach (var kv in skill.Common) { skillCommonTable.Rows.Add( skillID, kv.Key, kv.Value ); } foreach (var kv in skill.PVPcommon) { skillPVPCommonTable.Rows.Add( skillID, kv.Key, kv.Value ); } //导入技能说明 skillHTable.Rows.Add( skillID, sr != null ? sr["desc"] : null, sr != null ? sr["pdesc"] : null, sr != null ? sr["h"] : null, sr != null ? sr["ph"] : null, sr != null ? sr["hch"] : null ); } //导入技能等级 for (int i = 1, j = skill.MaxLevel + (skill.CombatOrders ? 2 : 0); i <= j; i++) { skill.Level = i; string levelDesc; try { levelDesc = SummaryParser.GetSkillSummary(skill, sr, SummaryParams.Default); } catch(Exception ex) { levelDesc = "错误:" + ex.Message; } skillLevelTable.Rows.Add( skillID, i, levelDesc); } } img.Unextract(); } ds.Tables.Add(jobTable); ds.Tables.Add(skillTable); ds.Tables.Add(skillLevelTable); ds.Tables.Add(skillCommonTable); ds.Tables.Add(skillPVPCommonTable); ds.Tables.Add(skillHTable); return ds; } public void OutputCsv(StreamWriter sw, DataTable dt) { for (int i = 0; i < dt.Columns.Count; i++) { DataColumn col = dt.Columns[i]; sw.Write(ConvertCell(col.ColumnName)); if (i < dt.Columns.Count - 1) sw.Write(","); else sw.WriteLine(); } foreach (DataRow row in dt.Rows) { for (int i = 0; i < dt.Columns.Count; i++) { sw.Write(ConvertCell(Convert.ToString(row[i]))); if (i < dt.Columns.Count - 1) sw.Write(","); else sw.WriteLine(); } } } private string ConvertCell(string input) { if (input != null) { input = ReplaceQoute(input); if (input.IndexOfAny(",\"\r\n".ToCharArray()) > -1) { input = "\"" + input + "\""; } } return input; } private string ReplaceQoute(string input) { if (input == null) return null; if (!input.Contains("\"")) return input; return input.Replace("\"", "\"\""); } public void ExportSkillOption(string outputDir) { Wz_Node skillOption = PluginBase.PluginManager.FindWz("Item/SkillOption.img"); Wz_Node itemOption = PluginBase.PluginManager.FindWz("Item/ItemOption.img"); Wz_Node item0259 = PluginBase.PluginManager.FindWz("Item/Consume/0259.img"); Wz_Node skill8000 = PluginBase.PluginManager.FindWz("Skill/8000.img/skill"); if (skillOption == null || itemOption == null || item0259 == null || skill8000 == null) return; ItemTooltipRender2 itemRender = new ItemTooltipRender2(); itemRender.StringLinker = this.sl; itemRender.ShowObjectID = true; SkillTooltipRender2 skillRender = new SkillTooltipRender2(); skillRender.StringLinker = this.sl; skillRender.ShowObjectID = true; skillRender.ShowDelay = true; string skillImageDir = Path.Combine(outputDir, "skills"); string itemImageDir = Path.Combine(outputDir, "items"); if (!Directory.Exists(outputDir)) Directory.CreateDirectory(outputDir); if (!Directory.Exists(skillImageDir)) Directory.CreateDirectory(skillImageDir); if (!Directory.Exists(itemImageDir)) Directory.CreateDirectory(itemImageDir); StringResult sr; FileStream fs = new FileStream(Path.Combine(outputDir, "SkillOption.html"), FileMode.Create); StreamWriter sw = new StreamWriter(fs, Encoding.UTF8); try { sw.WriteLine(""); sw.WriteLine(""); sw.WriteLine(""); sw.WriteLine(""); sw.WriteLine("魂武器系统"); sw.WriteLine(@""); sw.WriteLine(""); sw.WriteLine(""); foreach (Wz_Node node in item0259.Nodes) { int itemID; if (Int32.TryParse(node.Text, out itemID) && itemID / 1000 == 2591) //02591___ { sw.WriteLine(@""); sl.StringItem.TryGetValue(itemID, out sr); if (sr != null) { sw.WriteLine(@"", sr == null ? "null" : sr.Name, itemID); } Item item = Item.CreateFromNode(node, PluginManager.FindWz); if (item != null) { itemRender.Item = item; string imageName = Path.Combine(itemImageDir, item.ItemID + ".png"); if (!File.Exists(imageName)) { Bitmap itemImage = itemRender.Render(); itemImage.Save(imageName, System.Drawing.Imaging.ImageFormat.Png); itemImage.Dispose(); } sw.WriteLine(@"", item.ItemID); } Wz_Node skillOptionNode = skillOption.FindNodeByPath("skill\\" + (itemID % 1000 + 1)); if (skillOptionNode != null) { int skillId = skillOptionNode.Nodes["skillId"].GetValueEx(-1); int reqLevel = skillOptionNode.Nodes["reqLevel"].GetValueEx(-1); int incTableId = skillOptionNode.Nodes["incTableID"].GetValueEx(-1); int incRTableId = skillOptionNode.Nodes["incRTableID"].GetValueEx(-1); Wz_Node incNode = null; string per = null; if (incTableId >= 0) { incNode = skillOption.FindNodeByPath("inc\\" + incTableId); } else if (incRTableId >= 0) { incNode = skillOption.FindNodeByPath("incR\\" + incRTableId); per = "%"; } if (incNode != null) { sw.WriteLine(@"", incNode.Nodes[0].Text, incNode.Nodes[0].Value, per); sw.WriteLine(@""); sw.WriteLine(@"", incNode.Nodes[incNode.Nodes.Count - 1].Text, incNode.Nodes[incNode.Nodes.Count - 1].Value, per); } sw.WriteLine("", reqLevel); sl.StringSkill.TryGetValue(skillId, out sr); if (sr != null) { sw.WriteLine("", sr == null ? "null" : sr.Name, skillId); } Skill skill = Skill.CreateFromNode(skill8000.Nodes[skillId.ToString("D7")], PluginManager.FindWz); if (skill != null) { skill.Level = skill.MaxLevel; skillRender.Skill = skill; string imageName = Path.Combine(skillImageDir, skill.SkillID + ".png"); if (!File.Exists(imageName)) { Bitmap skillImage = skillRender.Render(); skillImage.Save(Path.Combine(skillImageDir, skill.SkillID + ".png"), System.Drawing.Imaging.ImageFormat.Png); skillImage.Dispose(); } sw.WriteLine(@"", skill.SkillID); } List> tempOptions = new List>(); int totalProb = 0; Wz_Node tempOptionNode = skillOptionNode.Nodes["tempOption"]; if (tempOptionNode != null) { foreach (Wz_Node optionNode in tempOptionNode.Nodes) { int id = optionNode.Nodes["id"].GetValueEx(-1); int prob = optionNode.Nodes["prob"].GetValueEx(-1); if (id >= 0 && prob >= 0) { Potential optionItem = Potential.CreateFromNode(itemOption.Nodes[id.ToString("000000")], (reqLevel + 9) / 10); if (optionItem != null) { totalProb += prob; tempOptions.Add(new KeyValuePair(prob, optionItem)); } } } } if (tempOptions.Count > 0) { for (int i = 0; i < tempOptions.Count; i++) { KeyValuePair opt = tempOptions[i]; sw.Write(""); if (i == 0) { sw.Write(@"", tempOptions.Count); } sw.WriteLine("", opt.Value.ConvertSummary(), opt.Value.code, opt.Key, totalProb, (1.0 * opt.Key / totalProb)); } } } sw.WriteLine("
道具名称{0} (id:{1})
道具图片
魂珠属性阶段{0}: 提升物攻/魔攻 + {1}{2}
...
阶段{0}: 提升物攻/魔攻 + {1}{2}
需求等级{0}
技能名称{0} (id:{1})
技能图片
附加属性{0}    [潜能代码:{1:D6}, 获得几率:{2}/{3}({4:P2})])

"); } } sw.WriteLine(""); sw.WriteLine(""); } finally { sw.Close(); } } } } ================================================ FILE: WzComparerR2/Dotnet4Polyfill.cs ================================================ using System; #if NET462 namespace System.Runtime.InteropServices { internal static class RuntimeInformation { public static Architecture ProcessArchitecture { get { if (string.Equals(Environment.GetEnvironmentVariable("PROCESSOR_ARCHITECTURE"), "arm64", StringComparison.OrdinalIgnoreCase)) { return Architecture.Arm64; } return Environment.Is64BitProcess ? Architecture.X64 : Architecture.X86; } } } internal enum Architecture { X86 = 0, X64 = 1, Arm = 2, Arm64 = 3, } } #endif ================================================ FILE: WzComparerR2/Dotnet6Patches.cs ================================================ #if NET6_0_OR_GREATER using System; using System.Collections; using System.Collections.Generic; using System.Drawing; using System.Reflection; using System.Reflection.Emit; using System.Runtime.InteropServices; using System.Windows.Forms; using DevComponents.AdvTree; using DevComponents.AdvTree.Display; using DevComponents.DotNetBar; using DevComponents.DotNetBar.Controls; using HarmonyLib; namespace WzComparerR2 { internal static class Dotnet6Patch { static Dotnet6Patch() { harmony = new Harmony("WzComparerR2-Dotnet6Patch"); } private static readonly Harmony harmony; public static void Patch() { harmony.PatchAll(); } public static void Unpatch() { harmony.UnpatchAll(); } } [HarmonyPatch(typeof(ComboBoxEx))] internal class ComboBoxExPatch { [HarmonyPatch("ᑧ"), HarmonyPrefix] public static bool ᑧ(ComboBoxEx __instance, IntPtr ळ) { bool flag = false; if (Environment.Version.Major > 5) { flag = true; } else if (Environment.Version.Major == 5 && Environment.Version.Minor >= 1) { flag = true; } if (flag) { SetWindowTheme(ळ, " ", " "); } return false; } [DllImport("UxTheme.dll", CharSet = CharSet.Auto)] internal static extern int SetWindowTheme(IntPtr hwnd, string pszSubAppName, string pszSubIdList); } [HarmonyPatch(typeof(AdvTree))] internal class AdvTreePatch { // this function is decompiled by ilspy public static void ٹ(AdvTree __instance, Node _0652, MouseEventArgs ؾ, Point _٧) { #region private members var GetLayoutPosition = AccessTools.MethodDelegate>( AccessTools.Method(typeof(AdvTree), "GetLayoutPosition", new[] { typeof(MouseEventArgs) }), __instance); var InvokeNodeMouseDown = AccessTools.MethodDelegate>("DevComponents.AdvTree.AdvTree:InvokeNodeMouseDown", __instance); var ա = AccessTools.FieldRefAccess(__instance, "ա"); var node_CommandButton = AccessTools.Property(typeof(Node), "CommandButton"); var ڳ = AccessTools.MethodDelegate>("DevComponents.AdvTree.AdvTree:ڳ", __instance); var _ײ = AccessTools.FieldRefAccess(__instance, "_ײ"); var _ת = AccessTools.FieldRefAccess(__instance, "_ת"); var _055E = AccessTools.FieldRefAccess(__instance, "՞"); var __0603 = AccessTools.FieldRefAccess(__instance, "_\u0603"); var __05EB = AccessTools.FieldRefAccess(__instance, "_\u05EB"); var selectedNodesCollection_ۺ = AccessTools.Property(typeof(SelectedNodesCollection), "ۺ "); var _2599__25AA = AccessTools.MethodDelegate>("DevComponents.AdvTree.\u2599:\u25AA"); var _2599__25A4 = AccessTools.MethodDelegate>("DevComponents.AdvTree.\u2599:\u25A4"); var ٮ = AccessTools.MethodDelegate>("DevComponents.AdvTree.AdvTree:ٮ", __instance); var ڊ = AccessTools.MethodDelegate>("DevComponents.AdvTree.AdvTree:ڊ", __instance); var cell_GetEnabled = AccessTools.Method("DevComponents.AdvTree.Cell:GetEnabled"); var cell_CheckBoxBoundsRelative = AccessTools.Property(typeof(Cell), "CheckBoxBoundsRelative"); var cell_SetMouseDown = AccessTools.Method("DevComponents.AdvTree.Cell:SetMouseDown"); var _0602 = AccessTools.StaticFieldRefAccess("\u0602"); var _059B = AccessTools.FieldRefAccess(__instance, "\u059B"); #endregion #region method body #if true Point layoutPosition = GetLayoutPosition(ؾ); InvokeNodeMouseDown(new TreeNodeMouseEventArgs(_0652, ؾ.Button, ؾ.Clicks, ؾ.Delta, layoutPosition.X, layoutPosition.Y)); if (ؾ.Button == MouseButtons.Left) { if (NodeDisplay.GetNodeRectangle(eNodeRectanglePart.ExpandHitTestBounds, _0652, _٧).Contains(layoutPosition) && ؾ.Clicks == 1 && _0652.ExpandVisibility != eNodeExpandVisibility.Hidden) { ա = 0; _0652.Toggle(eTreeAction.Mouse); return; } if (/*_0652.CommandButton*/(bool)node_CommandButton.GetValue(_0652)) { ա = 0; if (NodeDisplay.GetNodeRectangle(eNodeRectanglePart.CommandBounds, _0652, _٧).Contains(layoutPosition)) { ڳ(_0652, new CommandButtonEventArgs(eTreeAction.Mouse, _0652)); return; } } Rectangle nodeRectangle = NodeDisplay.GetNodeRectangle(eNodeRectanglePart.NodeContentBounds, _0652, _٧); if ((nodeRectangle.Contains(layoutPosition) || (_ײ && layoutPosition.Y >= nodeRectangle.Y && layoutPosition.Y <= nodeRectangle.Bottom)) && _0652.TreeControl != null) { if (_0652.TreeControl.SelectedNode != _0652) { ա = 0; } if (_0652.Selectable) { if (_ת && _055E.Count > 0 && Control.ModifierKeys == Keys.None && ؾ.Button == MouseButtons.Left) { __0603 = true; } else { __0603 = false; } if (_ת && _055E.Count > 0 && (Control.ModifierKeys == Keys.Shift || Control.ModifierKeys == Keys.Control)) { ա = 0; if (__05EB == eMultiSelectRule.SameParent && _055E[0].Parent != _0652.Parent) { return; } if (Control.ModifierKeys == Keys.Shift && _055E.Count > 0) { Node node = _055E[0]; Node node2 = _0652; bool flag = false; /*_055E.ۺ = true;*/ selectedNodesCollection_ۺ.SetValue(_055E, true); try { while (_055E.Count > 1) { _055E.Remove(_055E[_055E.Count - 1], eTreeAction.Mouse); flag = true; } } finally { /*_055E.ۺ = false;*/ selectedNodesCollection_ۺ.SetValue(_055E, false); } if (node2 != node) { if (node2.Bounds.Y > node.Bounds.Y) { /*_055E.ۺ = true;*/ selectedNodesCollection_ۺ.SetValue(_055E, true); try { do { if (!node2.IsSelected && node2.Selectable && (__05EB == eMultiSelectRule.AnyNode || (__05EB == eMultiSelectRule.SameParent && _055E.Count > 0 && _055E[0].Parent == node2.Parent))) { _055E.Add(node2, eTreeAction.Mouse); } node2 = _2599__25AA(node2); } while (node != node2 && node2 != null); return; } finally { /*_055E.ۺ = false;*/ selectedNodesCollection_ۺ.SetValue(node, false); ٮ(EventArgs.Empty); } } /*_055E.ۺ = true;*/ selectedNodesCollection_ۺ.SetValue(node, true); try { do { if (!node2.IsSelected && node2.Selectable && (__05EB == eMultiSelectRule.AnyNode || (__05EB == eMultiSelectRule.SameParent && _055E.Count > 0 && _055E[0].Parent == node2.Parent))) { _055E.Add(node2, eTreeAction.Mouse); } node2 = _2599__25A4(node2); } while (node != node2 && node2 != null); return; } finally { /*_055E.ۺ = false;*/ selectedNodesCollection_ۺ.SetValue(node, false); ٮ(EventArgs.Empty); } } if (flag) { ٮ(EventArgs.Empty); } } else if (_0652.IsSelected) { _055E.Remove(_0652, eTreeAction.Mouse); } else { _055E.Add(_0652, eTreeAction.Mouse); } return; } if (!_0652.IsSelected) { __instance.SelectNode(_0652, eTreeAction.Mouse); if (_0652.TreeControl == null || _0652.TreeControl.SelectedNode != _0652) { return; } } } Cell cell = ڊ(_0652, layoutPosition.X, layoutPosition.Y, _٧); if (cell == null) { return; } bool flag2 = false; if (cell.CheckBoxVisible && /*cell.GetEnabled()*/AccessTools.MethodDelegate>(cell_GetEnabled, cell)()) { Rectangle checkBoxBoundsRelative = /*cell.CheckBoxBoundsRelative*/(Rectangle)cell_CheckBoxBoundsRelative.GetValue(cell); checkBoxBoundsRelative.Offset(NodeDisplay.GetNodeRectangle(eNodeRectanglePart.NodeBounds, _0652, _٧).Location); if (checkBoxBoundsRelative.Contains(layoutPosition)) { if (cell.CheckBoxThreeState) { if (cell.CheckState == CheckState.Checked) { cell.SetChecked(CheckState.Indeterminate, eTreeAction.Mouse); } else if (cell.CheckState == CheckState.Unchecked) { cell.SetChecked(CheckState.Checked, eTreeAction.Mouse); } else if (cell.CheckState == CheckState.Indeterminate) { cell.SetChecked(CheckState.Unchecked, eTreeAction.Mouse); } } else { cell.SetChecked(!cell.Checked, eTreeAction.Mouse); } flag2 = true; ա = 0; } } if (_0652.SelectedCell != cell) { ա = 1; } else if (!flag2) { ա++; } _0652.SetSelectedCell(cell, eTreeAction.Mouse); /*cell.SetMouseDown(over: true);*/ AccessTools.MethodDelegate>(cell_SetMouseDown, cell)(true); } else { ա = 0; } } else { if (ؾ.Button != MouseButtons.Right || _0652.TreeControl == null) { return; } if (!_0652.IsSelected) { __instance.SelectNode(_0652, eTreeAction.Mouse); } if ((!__instance.MultiSelect && _0652.TreeControl.SelectedNode != _0652) || _0652.ContextMenu == null) { return; } // the main purpose is to remove these lines, ContextMenu is obsoleted and has been removed from netcore. /* if (_0652.ContextMenu is ContextMenu) { ContextMenu contextMenu = _0652.ContextMenu as ContextMenu; contextMenu.Show(this, new Point(ؾ.X, ؾ.Y)); } */ else if (_0652.ContextMenu.GetType().FullName == "System.Windows.Forms.ContextMenuStrip") { _0652.ContextMenu.GetType().InvokeMember("Show", BindingFlags.InvokeMethod, null, _0652.ContextMenu, new object[2] { __instance, new Point(ؾ.X, ؾ.Y) }); } else if (_0652.ContextMenu.GetType().FullName == "DevComponents.DotNetBar.ButtonItem") { Point point = __instance.PointToScreen(new Point(ؾ.X, ؾ.Y)); ((PopupItem)_0652.ContextMenu).SetSourceControl(__instance); _0652.ContextMenu.GetType().InvokeMember("Popup", BindingFlags.InvokeMethod, null, _0652.ContextMenu, new object[1] { point }); } else { if (!_0652.ContextMenu.ToString().StartsWith(_0602) || _059B == null) { return; } string text = _0652.ContextMenu.ToString().Substring(_0602.Length); object obj = _059B.GetType().InvokeMember("ContextMenus", BindingFlags.GetProperty, null, _059B, null); int num = (int)obj.GetType().InvokeMember("IndexOf", BindingFlags.InvokeMethod, null, obj, new string[1] { text }); if (num >= 0) { IList list = obj as IList; object obj2 = list[num]; try { obj2.GetType().InvokeMember("SetSourceControl", BindingFlags.InvokeMethod, null, obj2, new object[1] { __instance }); } catch { } Point point2 = __instance.PointToScreen(new Point(ؾ.X, ؾ.Y)); obj2.GetType().InvokeMember("Popup", BindingFlags.InvokeMethod, null, obj2, new object[1] { point2 }); } } } #endif #endregion } [HarmonyPatch("OnMouseDown"), HarmonyTranspiler] public static IEnumerable OnMouseDown(IEnumerable instructions) { List modifiedIL = new(); var searchMethodInfo = AccessTools.Method(typeof(AdvTree), "ٹ"); foreach (var instruction in instructions) { if (instruction.Calls(searchMethodInfo)) { var replaceMethodInfo = AccessTools.Method(typeof(AdvTreePatch), "ٹ"); var inst = new CodeInstruction(OpCodes.Call, replaceMethodInfo); modifiedIL.Add(inst); } else { modifiedIL.Add(instruction); } } return modifiedIL; } } } #endif ================================================ FILE: WzComparerR2/DownloadingItem.cs ================================================ using System; using System.Collections.Generic; using System.Text; using System.Threading; using System.Net; using System.IO; namespace WzComparerR2 { public class DownloadingItem { public DownloadingItem(string url, string path) { this.url = url; this.path = path; } string url; string path; DateTime lastModified; long fileLength; Thread thread; WebResponse response; Stream responseStream; public string Url { get { return url; } } public string Path { get { return path; } set { path = value; } } public DateTime LastModified { get { return lastModified; } } public long FileLength { get { return fileLength; } } public void GetFileLength() { var uri = new Uri(this.url); switch (uri.Scheme.ToLower()) { case "http": case "https": ServicePointManager.SecurityProtocol = (SecurityProtocolType)(3072 | 12288); //TLS1.2/TLS1.3 GetFileLengthHttp(); break; case "ftp": GetFileLengthFtp(); break; } } private void GetFileLengthHttp() { try { var req = WebRequest.Create(url) as HttpWebRequest; req.Timeout = 15000; using (var resp = req.GetResponse() as HttpWebResponse) { this.lastModified = resp.LastModified; this.fileLength = resp.ContentLength; } } catch (Exception ex) { this.fileLength = 0; throw; } } private void GetFileLengthFtp() { try { var req = WebRequest.Create(url) as FtpWebRequest; req.Method = WebRequestMethods.Ftp.GetFileSize; req.Timeout = 15000; using (var resp = req.GetResponse() as FtpWebResponse) { this.fileLength = resp.ContentLength; } } catch (Exception ex) { throw; } try { var req = WebRequest.Create(url) as FtpWebRequest; req.Method = WebRequestMethods.Ftp.GetDateTimestamp; req.Timeout = 15000; using (var resp = req.GetResponse() as FtpWebResponse) { this.lastModified = resp.LastModified; } } catch (Exception ex) { throw; } } public void StartDownload() { if (thread == null) { thread = new Thread(tryStartDownload); thread.Start(); } } private void tryStartDownload() { try { WebRequest request = WebRequest.Create(url); request.Timeout = 15000; response = request.GetResponse(); responseStream = response.GetResponseStream(); response.Close(); } catch (Exception) { } finally { } } public void StopDownload() { if (response != null) { try { response.Close(); response = null; thread.Abort(); thread = null; } catch (Exception) { } finally { } } } } } ================================================ FILE: WzComparerR2/FrmAbout.Designer.cs ================================================ namespace WzComparerR2 { partial class FrmAbout { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FrmAbout)); this.labelX1 = new DevComponents.DotNetBar.LabelX(); this.labelX2 = new DevComponents.DotNetBar.LabelX(); this.labelX3 = new DevComponents.DotNetBar.LabelX(); this.lblAsmVer = new DevComponents.DotNetBar.LabelX(); this.lblFileVer = new DevComponents.DotNetBar.LabelX(); this.lblCopyright = new DevComponents.DotNetBar.LabelX(); this.buttonX1 = new DevComponents.DotNetBar.ButtonX(); this.advTree1 = new DevComponents.AdvTree.AdvTree(); this.elementStyle1 = new DevComponents.DotNetBar.ElementStyle(); this.lblClrVer = new DevComponents.DotNetBar.LabelX(); this.labelX4 = new DevComponents.DotNetBar.LabelX(); ((System.ComponentModel.ISupportInitialize)(this.advTree1)).BeginInit(); this.SuspendLayout(); // // labelX1 // this.labelX1.AutoSize = true; // // // this.labelX1.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.labelX1.Location = new System.Drawing.Point(12, 33); this.labelX1.Name = "labelX1"; this.labelX1.Size = new System.Drawing.Size(68, 18); this.labelX1.TabIndex = 0; this.labelX1.Text = "程序版本:"; // // labelX2 // this.labelX2.AutoSize = true; // // // this.labelX2.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.labelX2.Location = new System.Drawing.Point(11, 57); this.labelX2.Name = "labelX2"; this.labelX2.Size = new System.Drawing.Size(68, 18); this.labelX2.TabIndex = 1; this.labelX2.Text = "文件版本:"; // // labelX3 // this.labelX3.AutoSize = true; // // // this.labelX3.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.labelX3.Location = new System.Drawing.Point(11, 81); this.labelX3.Name = "labelX3"; this.labelX3.Size = new System.Drawing.Size(68, 18); this.labelX3.TabIndex = 2; this.labelX3.Text = "版权所有:"; // // lblAsmVer // this.lblAsmVer.AutoSize = true; // // // this.lblAsmVer.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.lblAsmVer.Location = new System.Drawing.Point(73, 33); this.lblAsmVer.Name = "lblAsmVer"; this.lblAsmVer.Size = new System.Drawing.Size(13, 16); this.lblAsmVer.TabIndex = 4; this.lblAsmVer.Text = "-"; // // lblFileVer // this.lblFileVer.AutoSize = true; // // // this.lblFileVer.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.lblFileVer.Location = new System.Drawing.Point(73, 57); this.lblFileVer.Name = "lblFileVer"; this.lblFileVer.Size = new System.Drawing.Size(13, 16); this.lblFileVer.TabIndex = 5; this.lblFileVer.Text = "-"; // // lblCopyright // this.lblCopyright.AutoSize = true; // // // this.lblCopyright.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.lblCopyright.Location = new System.Drawing.Point(73, 81); this.lblCopyright.Name = "lblCopyright"; this.lblCopyright.Size = new System.Drawing.Size(13, 16); this.lblCopyright.TabIndex = 6; this.lblCopyright.Text = "-"; // // buttonX1 // this.buttonX1.AccessibleRole = System.Windows.Forms.AccessibleRole.PushButton; this.buttonX1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.buttonX1.ColorTable = DevComponents.DotNetBar.eButtonColor.OrangeWithBackground; this.buttonX1.DialogResult = System.Windows.Forms.DialogResult.OK; this.buttonX1.Location = new System.Drawing.Point(115, 187); this.buttonX1.Name = "buttonX1"; this.buttonX1.Size = new System.Drawing.Size(75, 23); this.buttonX1.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.buttonX1.TabIndex = 8; this.buttonX1.Text = "关掉我"; // // advTree1 // this.advTree1.AccessibleRole = System.Windows.Forms.AccessibleRole.Outline; this.advTree1.AllowDrop = true; this.advTree1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.advTree1.BackColor = System.Drawing.SystemColors.Window; // // // this.advTree1.BackgroundStyle.Class = "TreeBorderKey"; this.advTree1.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.advTree1.DoubleClickTogglesNode = false; this.advTree1.DragDropEnabled = false; this.advTree1.DragDropNodeCopyEnabled = false; this.advTree1.ExpandWidth = 4; this.advTree1.HideSelection = true; this.advTree1.Location = new System.Drawing.Point(12, 103); this.advTree1.Name = "advTree1"; this.advTree1.NodeStyle = this.elementStyle1; this.advTree1.PathSeparator = ";"; this.advTree1.Size = new System.Drawing.Size(280, 78); this.advTree1.Styles.Add(this.elementStyle1); this.advTree1.TabIndex = 9; this.advTree1.Text = "advTree1"; // // elementStyle1 // this.elementStyle1.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.elementStyle1.Name = "elementStyle1"; this.elementStyle1.TextColor = System.Drawing.SystemColors.ControlText; // // lblClrVer // this.lblClrVer.AutoSize = true; // // // this.labelX4.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.lblClrVer.Location = new System.Drawing.Point(73, 9); this.lblClrVer.Name = "lblClrVer"; this.lblClrVer.Size = new System.Drawing.Size(13, 16); this.lblClrVer.TabIndex = 11; this.lblClrVer.Text = "-"; // // labelX4 // this.labelX4.AutoSize = true; // // // this.labelX4.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.labelX4.Location = new System.Drawing.Point(12, 9); this.labelX4.Name = "labelX4"; this.labelX4.Size = new System.Drawing.Size(62, 18); this.labelX4.TabIndex = 10; this.labelX4.Text = "CLR版本:"; // // FrmAbout // this.AcceptButton = this.buttonX1; this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.CancelButton = this.buttonX1; this.ClientSize = new System.Drawing.Size(304, 221); this.Controls.Add(this.lblClrVer); this.Controls.Add(this.labelX4); this.Controls.Add(this.advTree1); this.Controls.Add(this.buttonX1); this.Controls.Add(this.lblCopyright); this.Controls.Add(this.lblFileVer); this.Controls.Add(this.lblAsmVer); this.Controls.Add(this.labelX3); this.Controls.Add(this.labelX2); this.Controls.Add(this.labelX1); this.DoubleBuffered = true; this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); this.MaximizeBox = false; this.MinimizeBox = false; this.Name = "FrmAbout"; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; this.Text = "关于"; ((System.ComponentModel.ISupportInitialize)(this.advTree1)).EndInit(); this.ResumeLayout(false); this.PerformLayout(); } #endregion private DevComponents.DotNetBar.LabelX labelX1; private DevComponents.DotNetBar.LabelX labelX2; private DevComponents.DotNetBar.LabelX labelX3; private DevComponents.DotNetBar.LabelX lblAsmVer; private DevComponents.DotNetBar.LabelX lblFileVer; private DevComponents.DotNetBar.LabelX lblCopyright; private DevComponents.DotNetBar.ButtonX buttonX1; private DevComponents.AdvTree.AdvTree advTree1; private DevComponents.DotNetBar.ElementStyle elementStyle1; private DevComponents.DotNetBar.LabelX lblClrVer; private DevComponents.DotNetBar.LabelX labelX4; } } ================================================ FILE: WzComparerR2/FrmAbout.cs ================================================ using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using DevComponents.AdvTree; namespace WzComparerR2 { public partial class FrmAbout : DevComponents.DotNetBar.Office2007Form { public FrmAbout() { InitializeComponent(); #if NET6_0_OR_GREATER // https://learn.microsoft.com/en-us/dotnet/core/compatibility/fx-core#controldefaultfont-changed-to-segoe-ui-9pt this.Font = new Font(new FontFamily("Microsoft Sans Serif"), 8f); #endif this.lblClrVer.Text = string.Format("{0} ({1})", Environment.Version, RuntimeInformation.ProcessArchitecture); this.lblAsmVer.Text = GetAsmVersion().ToString(); this.lblFileVer.Text = GetFileVersion().ToString(); this.lblCopyright.Text = GetAsmCopyright().ToString(); GetPluginInfo(); } private Version GetAsmVersion() { return this.GetType().Assembly.GetName().Version; } private string GetFileVersion() { return this.GetAsmAttr()?.InformationalVersion ?? this.GetAsmAttr()?.Version; } private string GetAsmCopyright() { return this.GetAsmAttr()?.Copyright; } private void GetPluginInfo() { this.advTree1.Nodes.Clear(); if (PluginBase.PluginManager.LoadedPlugins.Count > 0) { foreach (var plugin in PluginBase.PluginManager.LoadedPlugins) { string nodeTxt = string.Format("{0} {1} ({2})", plugin.Instance.Name, plugin.Instance.Version, plugin.Instance.FileVersion); Node node = new Node(nodeTxt); this.advTree1.Nodes.Add(node); } } else { string nodeTxt = "没有加载插件_(:з」∠)_"; Node node = new Node(nodeTxt); this.advTree1.Nodes.Add(node); } } private T GetAsmAttr() { object[] attr = this.GetType().Assembly.GetCustomAttributes(typeof(T), true); if (attr != null && attr.Length > 0) { return (T)attr[0]; } return default(T); } } } ================================================ FILE: WzComparerR2/FrmAbout.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 AAABAAEAQEAAAAEAIAAoQgAAFgAAACgAAABAAAAAgAAAAAEAIAAAAAAAAAAAABMLAAATCwAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAKAIPFJ0aQlz0EjRN8RtCXfATOlbwDjBK8BY+W/AWPl3wDStD8AIPG/IAFSfsAQUJhAEA AUoAAAACAAAAAAAAAAAAAAAAAAAAAAEJDWMLNFXzCi9P8govTvALMlHwCjJQ8As1U/MGJTvkAAEDcAAA AGgAAQJqAAAAIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAALxxPcuEkdbH/QJze/y2Cxf9Epun/MpTf/yN9vf84n+r/OaDp/yV9 wf8aX47/Ay5T/wU3VP8FMlf6BhckwAABACcAAAAAAAAAAAAAABYKMU3MGoDH/xVqqf8RaKb/EWmq/x+F zv8bgtH/GHa7/w9Vh/8JPWX/AB83/wAVJ+MADxS/AAAAVAABAgMAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMHC4Mzj9T/KofI/zuPzv8re7X/QJvc/y6L 0f8hdrH/NJTb/zWW2/8hdbP/MY/U/ws4Xf8BM1j/BkBo/wlDcf8CCRGQAAAAAAAAAAABCQ5tElqU/xlu sf8ff8b/GXvB/xZ1uf8ca6b/IIHE/xp8xv8Vb6//Gni5/xNbkP8DLkv/ADBS/wAmQP8AChCDAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4TN03MNpjg/yd/ v/87kM//K326/0Cb3P8ui9H/IXaz/zWV2/8tiMz/MIG+/zmb5v8gaJz/ASlL/wIyWf8GRXD/BSM76QAA AAsAAAAACic8yBhvtP8ge7z/FnW8/xh3v/8bfcT/FHK1/xZno/8de8T/GHS6/xNjnv8eg9L/EViQ/wAp Qv8ANFf/AB43+wAAADkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAArHVWB9zmf7P8nfr3/O5DQ/yt8uf9Am9z/LovP/yF2tP82l93/KILC/ziKxv87n+T/JXq5/wAn Q/8ENV3/Bjxj/wcwU/4AAABIAAAALx9Wgewnf7z/MpPY/yiJz/8fdrb/HG+v/yCAx/8SZqT/GnGy/xh6 vv8VaKL/HHq8/xl6xP8LQ27/ACpI/wEzVP8FGinWAAAAJwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAIHjiJ0rv84nOX/Jny+/zqMy/8rf7X/QJng/y6N0f8hda//Npfd/yqC w/8nf73/NZXa/yZ8vP8RSG7/ADBS/wE2XP8EOl7/AAMEdAMFCFohbqX/M4zM/zaW3f8uicn/OIvH/zCJ x/8kfb7/G3Oz/xZmo/8ef8v/E2al/yB5uP8agMr/EF6Y/wQzVf8ALEr/ATde/wEaMNgAAAAoAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACQoiM7oke7v/NZfe/ymAu/8xjc7/KXm2/z+c 3P8tjM//I3i5/zSZ3v8pgcP/I3u8/zGP1v8kfbv/KHSs/wAkQ/8EOF//BTld/wAJDrIUNFDNKYbI/yiC wf8vjNL/Knq2/zuZ3/8zktj/I3qz/y6Jz/8kd7L/IoHE/xVxtv8RZJz/GnjC/xRqqP8OW5T/ACdC/wAv U/8FO2P/ARwx2wAAACoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAlkcYpL/LInI/zCR 2f8sfrb/QJ3i/yh3tf9Dmt3/NZPZ/yiHzv89mNz/LoC6/z+Z2P8rh8r/Kn++/zGN1v8KOFr/ADFV/wQ5 X/8CLUz/G1mH/y6FxP87ltv/KobE/yt+vv8zltz/NZbb/ymCxP8ogsL/KXe5/zCO0f8ng8j/Gmmf/yGD yv8WbrP/E2qn/w9Rgf8AJkH/ADVc/wY+Z/8BGSXSAAAABwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAADRiwKoPH/y5/vP9Gm9z/L3u3/1Gs7f83gbn/WLDo/06o5/8xjtH/Vq3t/ziEvP9huPD/LXm2/0eb 2/9PrO//FUZr/wAsUf8CNVn/BjNT/y2Jy/8terv/QaLk/yBxr/82ldj/M5HV/zea4v8th8v/KYLD/yZ8 uf8ti8z/N5Xe/yl/vv8bcbb/FnCz/xZnpv8cf8P/BjJX/wIuUf8FO2L/BjdV/wEIDHQAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAuEj1e4UKY1/9HnNr/Lnu2/0GSz/9Uru//OYnI/2Ky7P9Amt3/RZ/f/1Ss 6f86hbz/Ybbw/zOLyP9IoeP/WbT1/x9gkf8AKkn/ACpM/x9fkP84l+P/LH22/0Cc4v8ec7D/L47R/zaV 3/8oerT/E0Zx/yuExP8wjdL/Ina0/zeZ3v8pgML/H3S0/xp3vv8VZqT/G3vD/xRspf8AJ0H/AzFX/wdA av8FN17/AQgPcQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwMFVyxun/8+ktH/VrLz/0uj4v9Sq+r/U6zs/zaL yP9etOz/M5HU/0yi4/9Xs/X/O5fX/2O08P8zktX/SaPk/1Gr7f81gsD/HEVu/wYpRv9Pot//T6rq/zN+ t/87n+P/H3Ox/zGP0/82mdz/CzZZ/wAiO/8lcaj/MZDY/yN4tP83l97/KILA/yuCwf8qjdf/EV+Z/x13 u/8afcn/DE17/wIrSP8EOmP/CEh0/wk+a/8CCA90AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhsyQblBm9f/PZDQ/1Wv 7f9Rqur/U6fr/0yl5f8+ltv/Vq3u/zmU2P9PqOr/O36v/zCDw/9jt/P/NZHT/12s7P9Oqej/N4O9/0iS yf83e63/U7Dz/1Gp6P8xfbf/Ua3t/zF/uf88m9//Kn++/wU0U/8BLVT/D0Jk/zCN0/8nd7X/N5jj/yZ/ u/86jcz/OJvf/yR4uf8eerj/GnrC/xl3vP8FNFX/ATNW/wlAav8JTID/CT1m/wAJDHUAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA ACMfSmndTazu/0KZ3f9Uq+j/Uarq/1St6/88mNj/UqHh/1qw8P83mN7/I2eX/wAZMv8nb6T/Zr79/zmW 1/9So+T/Tqnm/zeDtv9iuvL/Uq7w/1Kq6v9Qpef/L327/1iv7/8zfrj/SKfq/x5ik/8AKEX/AzZe/wIw TP8eaZ//JX/A/zaX3/8rhcT/M4bD/zWV3P8yjtP/Gmyn/xp6v/8YgMf/E2Wj/wEoR/8EPGD/CUR0/wpS iP8FJD3gAAAAJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAFDhaFJny6/1qz9P8sjtH/U6jn/06p5f9Sq+r/Qp/f/02g4P9kvvv/Mnqr/wAi Qf8ALFD/IlR4/2K98/9HouX/L47S/1av8P9Ajsr/QpbQ/1Ou7v9Pqur/UKXl/y98tv9Tru//NYXC/0qc 2/8RRGn/ADBU/wU5YP8CKUn/BiI3/yJ5uf81ltr/NJHZ/yFysf81lNj/LozP/yd8t/8ritP/FnW6/xuA yP8MS3r/Ai5Q/whAbP8ISXn/CEx//wMgNtAAAAAdAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdJVFx6Tia4v9Xru3/RZze/0+m4/9quvT/bLrz/1Go 4/9YrOb/e8P7/x5HZf8ALlb/ATdb/wswTf9aoMf/Y7vy/z6W2f9etPH/PpPR/zmKyP9Vr/D/Uqrn/1Cp 6/8zicj/Uq7u/0me3v8scqr/CjVS/wMzWv8DOmD/AB0x5QAAAHAcVoX0N57m/zKV1v8qerb/QZze/yyJ z/8wf7v/OJvb/xp5wf8YecD/G3u//wc7Yf8DOlz/CUJz/wlOgv8KTH7/Bh0xywACAjsAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgYIdkeYz/9AneP/XrHs/2Sx 7f9Hn9v/fcX6/3TB+P9Al9j/fMj//02LtP8AJkb/ATZd/wQ4XP8AKlH/Llx4/3rI+/9Kod3/XK/u/0Cd 2/9Ind7/Vq7r/1Cq6f9QqOj/MZDT/1et7v9KpeT/CUNq/wAvU/8GOmX/BTxm/wALEJEAAAAADCQ1sTOS 1/8yk9r/KXe3/0Cd3P8zk9r/JHe5/yyJyv8phcz/Fne8/xl/yP8PW5T/AS1O/wdDav8JRnn/CU6A/wlH dv8EGCW8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARAn N69ot+//U6nl/2Cv5f9ywPb/Rp3e/3K89v9ywfb/P5zY/3C87/8cRWX/AC5U/wM2W/8FOWD/ACE16AIF CZ9mocb5Tqno/2Sy7v9cr+n/TaHf/1at7P9Rqev/T6Tn/zCO0f9TrvD/S5/c/wY6Yv8GPGL/B0hy/wcn Q+gAAAAwAAAAAAAAAEgkZpj/NZzj/yl6tf9Amdz/M5Ta/yR7tv82js//MZHX/xt5wP8aesL/EmWg/wUw Vf8EO2D/CUV5/wpNfv8JSXr/BBkrvwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAABY2XHrygNH//1+w6f9SpOD/fcb7/0Cb2v9ote7/dsD5/0Sd2v9mp9P/AyZC/wA3 XP8GNl3/AjVY/wAJE5gAAAAILUhYyW7C+f9Fnt3/dsH5/0Ob2v9bru3/Tqno/1Gp5/81kNT/W7Hw/0ia 2P8HNFb/BUNx/wtEdv8BChORAAAAAAAAAAAAAAABDSU5uTKU3P8qfLn/QJzd/zKS2/8qg8H/NYjI/zWV 2v8mg8n/GHjB/xZqqP8ZY5f/BTZb/wdDcP8MTIH/Bzlh/wIMF5MAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADBwtwYaLQ/3XH//90wPj/PJfV/3jA9v9fsev/TqPh/1+v 5v9Xr+f/O2iK/wAsTP8ENV3/BDxg/wMnRv8AAABOAAAAAAMDBmFmoMz/UKrn/2S06/9MoeD/YrXq/1eu 7f9LpuT/PZTU/2e6+/8nZJb/Ajha/wpOg/8EK0XnAAAALAAAAAAAAAAAAAAAAAAAAEwjZZf6LYHD/zua 3P8xkdb/MpLV/x90rf81lNv/MJTX/xlzs/8Za6j/IIDF/wpIdv8DPWH/CUp//wQkOfAAAABCAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHSkvo3S+8/9yv/r/dMX4/1an 4/9vuOr/eMP6/0yj4f9Bmdf/ecr8/yxSc/8AKk7/BTZb/wE3X/8AEyLJAAAAFwAAAAAAAAAIMklh1Vuz 7P9Xqef/Y7Hr/0ui3f93xPr/Uqvs/zuY1/9dsfH/G099/wVDc/8JRnX/AwgOjQAAAAAAAAAAAAAAAAAA AAAAAAAACyQ3sip8t/9AneL/MZDW/zGP0/8gdrL/M5PY/zaW2/8ieb//HG2o/yCEy/8Wb7L/BDxf/wlH dv8EJDvyAAAASgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALDNn gvZ/zP//db/4/3K/9/9tvvX/Vafh/3vC9v9vwPf/d8P8/2qw3P8RN1j/ADFW/wI2XP8DOWH/Ag4TrgAA AAAAAAAAAAAAAAMDCGVgm8T/RaTg/2268v9Gnt3/db72/2u79f8+oOL/NH+8/wk9Yf8LU4v/AClB7QAA ACQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEYcTXP6RKLr/zGT1/8xjdL/InSz/zWV2v80k9r/LIbJ/xxu rP8fgMf/GnzG/w1Qhf8EPmb/ARky5QAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAACY0YYX2WbTu/3m/8/9xvvf/dcH4/1Kp5P+Ex/n/gsf8/5rd//93q8P/ACBD/wE4 X/8ENFr/B0Nv/wMPHLUAAAAAAAAAAAAAAAAAAAALLERd0kei5v96xfr/Qp3a/2m28f95w/r/SaXn/xpe kv8JR3X/BTJX+wECA1YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEyk1sD6e4f8yk9n/L4zT/yx9 tP89md//MpPY/y6Ky/8hdLD/Hn/F/xp8wv8Tb6//BTRW/wAEBX0AAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAICglcYpe4/2i++v9WquP/f8X7/2++9/9qtvH/bLns/5zZ //+f3P//Woae/wApTP8DMlr/BDtj/wc4XP8ACA6AAAAAAAAAAAAAAAAAAAAAAAkKDWdAiLv/YLLw/2W1 7v9In+D/hNT//1uw3/8AOmj/AzVV/AADBlsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AEMkZpX8Nprm/zSW2f8nfrz/K4bH/zWV3f8uh83/Ini0/x9/xv8ad8H/G3/I/wdCcv8AAAB4AAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADR4qql+06P96wfr/WK7q/3q/ 9P+Nzf//iMz4/2i06/+V0f//nN3+/ydHYv8ALVT/BThf/wZDb/8GKEP6AAAAMgAAAAAAAAAAAAAAAAAA AAAAAAASFzdSzVm48/9wwvb/R6Td/2Sev/89Y3foAAgUsgAFCV0AAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAABDCEyrDSW2v82l9//J3u9/yiAw/82mN3/LovM/yJ5tP8ghc//GH7L/xVo pv8HKD/IAAAAGQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABkm L6dLot3/abfv/2y78/9ir+f/n9f//5jV//9kse3/h8z9/5PR7P8eRWD/ACtV/wg+Yv8HRXP/AyhE+wAA ADcAAAAAAAAAAAAAAAAAAAAAAAAAAAkLDlImZJDzOWOG5xEmNbkEBQZYAAAALAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD0iYpL7OJ/o/yd/vf8pgsT/Npjc/zCM 1P8kfL//Fm2p/wYwS9MBCAtuAAAAFgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAWJS2naLns/1Cl6P+Lzv7/abns/3/E8/+e2f//Ya/p/5TY//9pmLL/ACBE/wM4 X/8FQGv/CENw/wUnQ/sAAAA3AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBQAAAAC8AAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABADMFAgF+LT9KwE5wifdKa4HwPGB++jVj hv87ZYb/OWSD/wwwX/8DKFn/ATNZ/AEJDr4AAAJvAAAACgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEh0mom6+8P9csev/lNL8/3a+8v9yvPD/m9j//3rA 8v97yPz/a5ev/wAlSP8EPmj/B0Bp/wZBcP8ELET7AAAANwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMBMfI5lLdI7zUZ3W/3q8 7P+n4///od///6bh//+t5v//iNX//3zJ//9vvv3/Xa7x/yd0yP8CWqn/AFua/gAlP9IAAgJdAAAAEQAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGjJOYs17yvz/Wq3s/4vL +P92v/H/dL7w/5vU//+Mzvz/YbLl/ydKZv8AK1P/CD5k/wdBa/8HQXD/AyxE+wAAADcAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAaEBwjpEh0 h+5osN7/OJjq/yaT7P8ikvT/I5Pu/x2N8v9FpPH/YrT1/6fW+v+h1v//dcD//4LF//+DzP//So7V/wBp wP8Aes//AFmc/wAhN7EAAAARAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AEVSjaj/hNL//4jM+P9os+r/ldL9/2Ox6f+W1P3/isz9/2i45/8iQlz/ADFZ/wg/af8IQGr/B0Rx/wUp QvsAAAA3AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAABWJTpPyXXA9P+E0P//MpDg/w11z/8pmPL/LJbw/yiX8v8rlfD/JJPy/xqM8f84nO//m9T7/3/E //92w///ecL//4LN/v9Eis//AGG0/wB/1f8Abbv/AB0usgAAAAkAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAABCTYSl/pHZ//+Rz/v/Xq/q/5zY/v9drun/ldP9/43P//9Xos7/GDxY/wA4 Xv8GPmj/B0Bq/wVDbv8EKET7AAAANwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAANFBt6WZOx/XjG9P+Cyv//Wqzy/wluyP8lj+j/LZfz/yqY8P8rlPH/K5jw/yuV 8v8tl+7/F47y/4fF9/+Qz///db3//3vC//96wP//g8f//0uQ0v8CZLj/AHnN/wBntf8ACA6LAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQlSJpv6Y3v//jM37/2Ky6f+a1f7/Xq7n/5XP /P+T2v//JWKU/wAkRv8IPmn/B0Fq/wc/Z/8IQXH/BjFL+wAAADcAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJERZ0X53F/4HT//9+wv//gMf//zOY7P8GaMP/FoHY/y6b 9P8rlfP/K5Xx/y+V8f8qmPP/J5Xv/yGR8f+Ty/n/isr//3a+/v93wv3/ecD//3vG//9qrur/EFep/wVg sP8EacL/ADBY4gAAAC0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEJUiKb+n9///4vM +v9isOj/mdf+/2Kw6v+T0v3/k9f//yVmlP8AKkz/B0Jq/wc+aP8GP2j/B0h1/wcwUvsAAAA3AAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALFBV1bKDB/4TT//94vf//eML//33E //80l+z/BGvF/w9xx/8bgd7/KJTu/yua8v8rk/D/J5Dv/ymY8v99vPj/lNH//3O8//98xPz/eL/8/4HF //9Fn+T/E4LU/x6P5P8trPz/HY/p/xVyvP8EGCKOAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAA9Yo6n/Z3j//+My/r/X7Hp/5rT/v9dsOj/lM/8/5TZ//8lZpn/AC1N/wg/av8GP2j/BD9p/wlK f/8GMVH7AAAANwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABOf6Gj/qbk //+g0vz/hsz//4nR//+Y2f//Tqrs/wRmwf8LccT/CG/E/w1uxf8Mb8r/Bm7L/zGV6P+o1v7/k8///3G+ //95wvv/eL77/3rB//97xvz/IozU/ySm9f8Xi+P/Kaj//yuq//8vuf//DDRM0gAAAAcAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAcm2Wp/+f3///jcv7/2Gy6f+a1P7/XrDo/5XS/f+U2P//JWuc/wEx Vv8IQWv/Bj9m/whBbP8KUIX/BjBR+wAAADcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAEDSpBu0up6/8ekvX/MZjw/7De+/+g5///nOb//5vh/v8ujtr/AmvJ/wdvyf8ahtj/LJDi/0yk 6/92v/7/esT//3zG//+Byf7/gsf//4HI//+Dxv//iMr+/yiK0f8Lgtb/HZrw/yyr//8qpP//KbX//w4z TNIAAAAHAAAAAAAAAAAAAAAAAAAAAAADAE4AAAAAAAcBRwBGDftQhKX/oeL//4zN+v9gsOf/ndf+/1+u 6/+U0P3/lNj//yZrov8AM1T/BkFt/wY8af8IRXP/ClGF/wUtUPsAAAA3AAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAASQxPjP4hmPf/Mpv2/yCO8P+i1/r/pen//5zg//+d5P//kNr8/2bA +f9WsPX/abj7/3/E//99x///gsf//3vB+/9Pjtb/PoHJ/zuZ3P88lt3/O5rd/0KY2/8ahtX/JaP5/yqn //8opv//KrH//yeQz/8HEyGKAAAAAAAAAAAAAAAAAAAAAAAAAAABMwXcBSkQhwEHBXgJbz//LF6G/4nQ //+KzPf/U6TY/47L+f9VqN7/lNH8/5TX//8hYpT/ADNa/wdAaf8HQWr/Ckh6/wlQhf8JL0/7AAAANwAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBYpMq4Mb8b/EHjT/yCK6f8OeNf/esfs/6Lp //+b4f//m+T//5zk//+m6f//ktj//3nB//96wf//hcf//2ar6v8ZY7T/A2q5/wBow/8AcMX/AHjP/wB3 0v8AddX/E5ju/zCy//8prv//LrL//yOP0/8IFB2FAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAQUCUQ5X NeARhz7/D5Yo/wc8C/8lbJv/QJri/yt0sP9Vruz/V6Ta/5bV/f+Mx/L/DT5f/wU+aP8GPmn/Bj9r/wlM ff8KToL/BjFQ+wAAADcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAElcip79OJnm/wBj v/8DacD/OZLV/5bd+f+g5P//l+H//5zh//+c4v//lt3//37G//92vv//fsf//2Sp6P8RZrf/AHfM/wB6 0P8AftL/AIDW/wB3zP8ARHXrACNB0wEmPtQQXZTuI5jj/x6Iwf8FGieLAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAMzHdIELhjNC24d/w6fJf8LjxT/DGxN/wxAc/8FQy3/JXq+/0Kb3f+Kzvr/b7jw/ws9 Yv8GPmf/BUBo/wlEcf8JSn3/C0t6/wYxUfoAAAAxAAAAAgAAAAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAWJy2ykdn3/4rV/f9Vq+L/Z7jp/5/k//+g6v//meH//57k//+X5P//n+T//5Xd//94wf//e8D//37E /f8jbLf/AHHK/wB8z/8AetD/AH3U/wBstf4AER+pAAAANwAAAAAAAAABAAEDSQIECYwAAgRoAAAABAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABFAtaDGk6/A+hOP8Mkgr/CWYA/wyRIf8HPxr/Dmww/xJT lf8ne73/W7Ly/1au7f8KOF3/Azpg/wc8Zv8LS33/BTpn/wc5av8BJUP8AQgAkAEiANEAAwBiAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAeRlVb0Jng/v+e6P//pez//6bp//+b5P//meD//5nh//+b4v//neL//5ni //+GzP//dbz9/4DK//9cotn/AmC8/wB+0f8AeM//AHzS/wB5y/8AFiauAAAAAQAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACsHLBTZBU8L/whm Bv8IXQD/BEMC/wdWCf8EHhv/BhwY/w0+Yv8QSoX/AiVA/wAuUf8BM1r/AyhM/wEdGP8DHwv/BjoI/wpM AP8DHwCnAAAADwAAAAAAAAAAAAAAAAAAAAAAAAAAAAABZpi1vv+g6P//nOL//5zh//+X3///neL//5vi //+e4v//muP//5zi//+c4f//g83//3jB//96vfj/IHHA/wByyv8Aes7/AHzQ/wN90f8Fc8n/AAwWoAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAe Ar0ITCnKBEoY9AGFFf8PlT//B38C/wVaAP8ISxf/EJot/wNdAP8BMAD/BUUA/wMoB/8CDhH/AhUQ/wU3 BP8FQQD/BEgA/wZKAP8DQgD/AykAzwIkALgAAwAoAAAAAAAAAAAAAAAAAAAAAAAAACk5WWzbxfH//5jh //+b5P//neD//5nh//+Z5P//muH//53i//+b4f//nOX//4TL//94w///crf2/wtftf8AfdP/AXvP/wV4 zv8Ae9L/AHfI/wAOGqMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAJAKXBlUt2g9MIfRbRAf/OS8K/weWLv8SpDb/E5o//wp4C/8GVwD/CXsA/wls AP8JcgD/BDwA/wQxAP8FPQD/BkIA/wU+AP8EOQD/AzIA/wMiAPMHVwD0AS4A1AAAAA0AAAAAAAAAAAAA AAAAAAAVMEFNyc7y/v+b4///mOD//5rl//+c4v//m+D//53k//+c4///md///53k//+HzP//fMH7/3e8 9/8QZLj/AHnO/wB70P8Dfc3/AHvV/wB2w/8ADROeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAEOqThTzemMq/w97Mv8JciH/DpQb/wVL Av8Fagf/CEkS/wZbAP8GWwD/BEUA/wZEAP8DLAD/AygA/wMnAP8DIwD/BEUA/wg6AP8BFADEAAIARgAG AG0AAAAMAAAAAAAAAAAAAAAADB03xJzK6//S9f//muD//53k//+a4f//muH//5rk//+a4f//muL//5nj //+d5P//ldv//3zJ//92uvb/DF21/wB/0v8AfNL/AHfN/wB90v8CftX/ATtr4QAAAC0AAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAADQAAACsAAAASAAAAAAAAAAAAAAAAAAAAAAAAAAAMOkCvToBt/wdb S/8OcXb/Ah8F/wVNAP8FPQD/C20S/xKcQP8OjjT/CGwC/wRVAP8FIgD/B0gA/wdrAP8GUAD/BDAA/wQ0 AP8GUAD/B1QA/wACAF0AAAAAAAAAAAAAAAAAAAAAAAAAAGBeX/D5////xO///5ri//+Y3v//m+T//53h //+a4///nOH//5vj//+c3v//meL//5vj//+P0v//dr36/zN+yf8AWLH/A2/D/wF+0v8Ad87/BIDY/wFu uv8AEBuPAAAABAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAQgAfL7UAWIj/ACU5sgAAAAkAAAAAAAAAAAAA AAAAAAAPB2R58ALd//8S0+v/QP///0KXnf8HWQP/BD8A/wZfAP8HcAL/C3sT/wM6Ef8Ljxv/A0QA/wdp AP8KiQD/BWQA/whMAP8DNQD/BUoA/wQ8AP8DEwCcAAAAAAAAAAAAAAAAAAAAAAAAAABhXlzw9////8Ty //+X3v//muP//53i//+X4f//nOP//5rg//+b4f//muH//53j//+j5///aLfn/xyB1P8wlen/KIDa/wZh uv8AeM7/AHzR/wB3zf8Ag9z/AGWs/gAmQNIAAgNeAAAATwAAAEsADReCACZD2ABIfe4Adsf/Ap78/wOM 2/8ACRGLAAAAAAAAAAAAAAAAAAAABEI7I8jQ3rL/xv/0/7P///9wxcX/CVgb/wAvLv8COBT/CYIT/xOE KP9CZV//FH5K/wVLNv8ARkD/BlEA/wg9AP8DOQD/BUoA/wdJAPwEJgDGASoA5wAGAGwAAAAAAAAAAAAA AAAAAAAAYV5d8PP////T8P7/ruj+/5Xg//+a4f//nuD//5zk//+Z4v//nOL//5nj//+i6f//gs3s/xFx yf8klfH/KZjx/zCh/P8Wccn/AGvB/wF80v8Ces7/AHjN/wOB2f8Agtn/AFyb/wBWlP8AVZD/AGWu/wCF 4P8AhN3/AnHI/wOM4P8Dofb/AlWD5AABAUIAAAAAAAAAAAAAAAAFAABaxoo7///ttv/b////RpeO/wFI Iv8EYH7/AKe4/wNiT/8wl5T//7JP/21eHP8AVTD/AJjI/xCDtv9Qamn/BjAG/wIzAP8AAAB6AAAABAAB ACsAAAArAAAAAAAAAAAAAAAAAAAAAGBcW+vw/v//3fT9/8ju/v+T3///muX//5ri//+Y4f//neH//5ni //+c4v//n+b//zuW1/8IbMn/KJPu/yuX8/8rlfD/L5jx/xBsxP8BccX/BHzT/wB6z/8Fecz/AXnR/wCC 2f8Bgd7/AILb/wCB2/8AfND/AGjE/wNqwf8FnfT/BJbs/wSh+P8AV4TuAAgOcwAAAAAAAAAAAAAAACYU AXmqtpH/Ov///0fq6v96oIz/UWFH/6rWqP9M4t//Tvz//8XktP/Zp1b/N0MZ/wiTtf8Xve7/C7To/wAW EO4ANAD/ADMA4QADAEIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQDg5yvsHD/+f8///R9Pv/nuP//5ze //+b4v//nuL//5vi//+Z5P//n9///5jg/f8Uds3/CWzD/xuF2/8unPb/LZbx/yqX9f8wmvP/DV24/wB4 zv8AfND/AH3T/wB/zv8AetL/Bn7P/wF50f8Accb/B2O6/zF3uv8ZbcD/AJXm/wOW7f8Cnff/AIXJ/wAb J78AAAAAAAAAAAAAAAAAAAAAABkbdACAkPaAx7D/8r5e/4JHEv//qT//r9Gp/3L///99////zOjA//q+ aP9qtKj/r3M0/wil0v8ACg+CAAIAVQBEAOkAIwDJAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAYLm6 uv/i/f//3/P8/7zt//+U4P//m+H//5rj//+Z4P//nuD//57m//+X4f//KY3f/wZrw/8Nccr/H4fi/yqY 7/8umfX/K5v1/x9muv8IZrv/AGnB/wBqxP8AZ8D/AGvD/wBowv8IZ7z/M3bE/2aq4P+Ezv//erjt/xls wP8Aluz/BZ72/wKGyf8AGie/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIkGBUMk4xMFPJSOhf4a6ON/kzy 8v+F////nf///3L//f+M6dr/J7rZ/1OhpP8GZX7xAAAAFwAAAAAAAwA7AAQAPwAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAC53dXbf6Pn//9r0/P/U9P//nuH//5ni//+c4f//meH//5nl//+Z4f//o+j//2S7 8f8Aasr/CGrA/whvw/8VeNT/GYDd/w1yz/9gsu3/WqHj/ziAzP9Dis7/QIfQ/0CIzf89gc3/ZKfm/3/K //99xv//esD//4LL//9His//AH7W/wOl/v8DV4nzAAgNdwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAIAAAAhAAAAKgBcatIo/v//vfz1/8j78P9J+v7/C+b//y7J4/9Nrr//AzxJwwAAAAMAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANDEwt+b0+f/Y9f//2/L8/8nv+v+c5f//lt///5zj //+X4f//nuD//5zi//+h5f//U7Pv/w50zf8BacT/AWTA/wlswv9WquD/nuL//63d//+c2f//e8b//4DJ //+Ayf//gMj//3rF//90vP//f8X//33C//93wP//g8n+/0iP2f8Gd8X/ABorpAAAABgAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACg5VgJd87//ur//m++n/Iez//zfU4P/zvV3/vmEL/wIA AFYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgHB16trq//5////9jy /P/c8/3/ze7+/6Pm/P+b4f//nuD//5Lk//+W4P//meP//6Lm//+D0fj/c8Du/3TD7P+H0PT/nef//1Og 2/9XpOL/f8b6/5HO//96v///fcr//4XL//+Jzf//i9T//5Ta//+W4f//gcr//3jC//99x///ETFf8QAA ADkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8FAE6acCzu5M+T/xTp //9OxL//umsb/BsIAHEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAOSkhJxO/8/v/a+v//2fH7/9v1/P/Y8fr/zfH+/8fv+/+76vz/uOj//6Xm//+V4f//neX//6Pq //+i6///qe///2q66v8AW73/BnfU/xOC5P+j0fP/nN///5LY//+b5v//m+L//5ri//+n6P//rer//6bp //+R0f//b7jx/w4YH6EAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAANjMmE7gQXmr/ADxGxgMAAEoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUEBF+9vLv/5Pz//9fz+//Z8fr/2fb//9zz+v/b8fv/3/b+/9zy /v/Y9Pr/wu///6nn//+e6f//M4bQ/2Sx6f+F0vH/C2rG/wBiwP8id8r/qNjw/6Dp//+b4f//neD//53k //+f5v//hs/x/5vU+P+c0/r/rvD//1x9i/gAAAAxAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHUk5O0vb+/v/l/v//2fL9/9rx /f/Z9P3/2fP+/9ry+f/Y8/7/2vL9/+Dx/P/Y9Pr/tej//3jF7/+Gzfb/o+v//4HM8/9sv+j/kdLz/6Po //+c4P//neX//5rh//+e6P//dcHr/xF0y/8fke7/Ipj9/ymCzP8kIyWMAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEB ATFYWFfawMjJ/977///Z9f3/2O/7/9r2/P/Z8v7/2fL9/9j1+v/Z9f7/2vP+/+D0///S9///ouf//5Tf //+i5///oez//5zk//+V3///mOH//5fh//+b4///md/+/xqA0/8Gacb/Jpb3/yiGz/8IFB6LAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAADiIfH6Hq7e3/5P///+b9///l/v//6f///+P5///d9f//2fP5/9ry /f/Z8vr/3PT8/8vv+v+i5P//n+j//6Hk//+m5v//o+f//6Xl//+c5P//nOL//5Xg/f8cfsz/BHHS/wxl sP8FERyFAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAue3p64tve3P+FtNP/SprP/0qV zf+dz+v/3vv//9/0/f/Z8v3/2fT6/9ny/P/a9P//1/L9/9Tw/v/X8f3/1vD+/9Xx/f/X8f3/ye3+/7Pr //+88P//ltz6/xhosf8ADRmLAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEB ADEJCAiEAgMHgQAABoAAAAB6ASI9wjp5rf/a9///3fb8/9fz/f/a8/7/2fL+/9ry+v/e9///4/7//9z8 ///e/P//5Pz8/9r1/v/o/P//6v3//73Lz/8YICKLAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAdeUn2X7uz9///Z8///1vP5/9j0 ///d9/7/vOby/0aCrf8sdaj/OHql/5LC2f/o////vcvO/1phZd4NDQx2AAAABAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgm JaDl6ej/9f///+/////x/////P///1Noc+AAAABWAAAAUAAAAEoDGSiaR1BX3xUWFHsAAAAeAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAzSklJ7IqKifOBgYHwg4KC8YOBgfYjISGFAAAAAAAAAAAAAAAAAAAABAAA AAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAA////////////8AAeAB/////gAAwAA////+AADAAD////wAAEAAH////AAAAAAP///8AAAAAA f///gAAAAAA///+AAAAAAB///4AAAAAAH///AAAAAAAP//8AAAAAAAf//gAAAAAAA//+AAAAAAAB//4A AAAAAAD//AAAAAAAAH/8AAAAAgAAf/gAAAACAAB/+AAAAAYAAH/4AAQABwAAf/gABAAPgAB/8AAOAA+A AH/wAA4AH8AA//AADwA/wAD/8AAPAH/AAP/wAA+B/+AB//AAD8/+AAP/8AAP//gAAP/gAA//4AAAf+AA D//AAAA/4AAP/4AAAD/gAA//AAAAH+AAD/4AAAAf4AAP/AAAAA/gAA/4AAAAD0AAD/gAAAAfAAAP8AAA AB8AAA/wAAAAfwAAA/AAABB/AAAD4AAAP/+AAAPgAAB//wAAAeAAAH//AAAA4AAAf/+AAADgAAA/j4AA A+AAAB4HAAAD4AAAAAcAAAHgAAAAA4AAAeAAAAABwAAH4AAAAAHgAAPgAAAAAfAAJ+AAAAAB+AA/8AAA AAH/AH/wAAAAA/+A//AAAAAH/8H/+AAAAAf////4AAAAD/////wAAAAf/////gAAAB//////AAAAf/// //+AAAD///////wAAP///////wAD////////Ac////////////8= ================================================ FILE: WzComparerR2/FrmCustomCSS.Designer.cs ================================================ namespace WzComparerR2 { partial class FrmCustomCSS { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FrmGifSetting)); this.lblBackgroundColor = new DevComponents.DotNetBar.LabelX(); this.lblNormalTextColor = new DevComponents.DotNetBar.LabelX(); this.lblChangedBackgroundColor = new DevComponents.DotNetBar.LabelX(); this.lblAddedBackgroundColor = new DevComponents.DotNetBar.LabelX(); this.lblRemovedBackgroundColor = new DevComponents.DotNetBar.LabelX(); this.lblChangedTextColor = new DevComponents.DotNetBar.LabelX(); this.lblAddedTextColor = new DevComponents.DotNetBar.LabelX(); this.lblRemovedTextColor = new DevComponents.DotNetBar.LabelX(); this.lblHyperlinkColor = new DevComponents.DotNetBar.LabelX(); this.btnOk = new DevComponents.DotNetBar.ButtonX(); this.btnCancel = new DevComponents.DotNetBar.ButtonX(); this.btnDefault = new DevComponents.DotNetBar.ButtonX(); this.richTextBoxEx1 = new DevComponents.DotNetBar.Controls.RichTextBoxEx(); this.colorPickerBackgroundColor = new DevComponents.DotNetBar.ColorPickerButton(); this.colorPickerNormalTextColor = new DevComponents.DotNetBar.ColorPickerButton(); this.colorPickerChangedBackgroundColor = new DevComponents.DotNetBar.ColorPickerButton(); this.colorPickerAddedBackgroundColor = new DevComponents.DotNetBar.ColorPickerButton(); this.colorPickerRemovedBackgroundColor = new DevComponents.DotNetBar.ColorPickerButton(); this.colorPickerChangedTextColor = new DevComponents.DotNetBar.ColorPickerButton(); this.colorPickerAddedTextColor = new DevComponents.DotNetBar.ColorPickerButton(); this.colorPickerRemovedTextColor = new DevComponents.DotNetBar.ColorPickerButton(); this.colorPickerHyperlinkColor = new DevComponents.DotNetBar.ColorPickerButton(); this.SuspendLayout(); // // lblBackgroundColor // this.lblBackgroundColor.AutoSize = true; // // // this.lblBackgroundColor.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.lblBackgroundColor.Location = new System.Drawing.Point(10, 10); this.lblBackgroundColor.Name = "lblBackgroundColor"; this.lblBackgroundColor.Size = new System.Drawing.Size(222, 16); this.lblBackgroundColor.TabIndex = 0; this.lblBackgroundColor.Text = "backColor"; // // lblNormalTextColor // this.lblNormalTextColor.AutoSize = true; // // // this.lblNormalTextColor.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.lblNormalTextColor.Location = new System.Drawing.Point(165, 10); this.lblNormalTextColor.Name = "lblNormalTextColor"; this.lblNormalTextColor.Size = new System.Drawing.Size(222, 16); this.lblNormalTextColor.TabIndex = 0; this.lblNormalTextColor.Text = "foreColor"; // // lblChangedBackgroundColor // this.lblChangedBackgroundColor.AutoSize = true; // // // this.lblChangedBackgroundColor.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.lblChangedBackgroundColor.Location = new System.Drawing.Point(10, 38); this.lblChangedBackgroundColor.Name = "lblChangedBackgroundColor"; this.lblChangedBackgroundColor.Size = new System.Drawing.Size(222, 16); this.lblChangedBackgroundColor.TabIndex = 0; this.lblChangedBackgroundColor.Text = "[Changed] backColor"; // // lblChangedTextColor // this.lblChangedTextColor.AutoSize = true; // // // this.lblChangedTextColor.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.lblChangedTextColor.Location = new System.Drawing.Point(165, 38); this.lblChangedTextColor.Name = "lblChangedTextColor"; this.lblChangedTextColor.Size = new System.Drawing.Size(222, 16); this.lblChangedTextColor.TabIndex = 0; this.lblChangedTextColor.Text = "[Changed] foreColor"; // // lblAddedBackgroundColor // this.lblAddedBackgroundColor.AutoSize = true; // // // this.lblAddedBackgroundColor.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.lblAddedBackgroundColor.Location = new System.Drawing.Point(10, 66); this.lblAddedBackgroundColor.Name = "lblAddedBackgroundColor"; this.lblAddedBackgroundColor.Size = new System.Drawing.Size(222, 16); this.lblAddedBackgroundColor.TabIndex = 0; this.lblAddedBackgroundColor.Text = "[Added] backColor"; // // lblAddedTextColor // this.lblAddedTextColor.AutoSize = true; // // // this.lblAddedTextColor.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.lblAddedTextColor.Location = new System.Drawing.Point(165, 66); this.lblAddedTextColor.Name = "lblAddedTextColor"; this.lblAddedTextColor.Size = new System.Drawing.Size(222, 16); this.lblAddedTextColor.TabIndex = 0; this.lblAddedTextColor.Text = "[Added] foreColor"; // // lblRemovedBackgroundColor // this.lblRemovedBackgroundColor.AutoSize = true; // // // this.lblRemovedBackgroundColor.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.lblRemovedBackgroundColor.Location = new System.Drawing.Point(10, 94); this.lblRemovedBackgroundColor.Name = "lblRemovedBackgroundColor"; this.lblRemovedBackgroundColor.Size = new System.Drawing.Size(222, 16); this.lblRemovedBackgroundColor.TabIndex = 0; this.lblRemovedBackgroundColor.Text = "[Removed] backColor"; // // lblRemovedTextColor // this.lblRemovedTextColor.AutoSize = true; // // // this.lblRemovedTextColor.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.lblRemovedTextColor.Location = new System.Drawing.Point(165, 94); this.lblRemovedTextColor.Name = "lblRemovedTextColor"; this.lblRemovedTextColor.Size = new System.Drawing.Size(222, 16); this.lblRemovedTextColor.TabIndex = 0; this.lblRemovedTextColor.Text = "[Removed] foreColor"; // // lblHyperlinkColor // this.lblHyperlinkColor.AutoSize = true; // // // this.lblHyperlinkColor.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.lblHyperlinkColor.Location = new System.Drawing.Point(10, 122); this.lblHyperlinkColor.Name = "lblHyperlinkColor"; this.lblHyperlinkColor.Size = new System.Drawing.Size(222, 16); this.lblHyperlinkColor.TabIndex = 0; this.lblHyperlinkColor.Text = "[Hyperlink] Color"; // // colorPickerBackgroundColor // this.colorPickerBackgroundColor.AccessibleRole = System.Windows.Forms.AccessibleRole.PushButton; this.colorPickerBackgroundColor.AutoExpandOnClick = true; this.colorPickerBackgroundColor.ColorTable = DevComponents.DotNetBar.eButtonColor.OrangeWithBackground; this.colorPickerBackgroundColor.Image = ((System.Drawing.Image)(resources.GetObject("colorPickerButton1.Image"))); this.colorPickerBackgroundColor.Location = new System.Drawing.Point(120, 7); this.colorPickerBackgroundColor.Name = "colorPickerBackgroundColor"; this.colorPickerBackgroundColor.SelectedColorImageRectangle = new System.Drawing.Rectangle(2, 2, 12, 12); this.colorPickerBackgroundColor.Size = new System.Drawing.Size(37, 23); this.colorPickerBackgroundColor.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.colorPickerBackgroundColor.TabIndex = 1; this.colorPickerBackgroundColor.SelectedColorChanged += new System.EventHandler(this.colorPickers_SelectedColorChanged); // // colorPickerNormalTextColor // this.colorPickerNormalTextColor.AccessibleRole = System.Windows.Forms.AccessibleRole.PushButton; this.colorPickerNormalTextColor.AutoExpandOnClick = true; this.colorPickerNormalTextColor.ColorTable = DevComponents.DotNetBar.eButtonColor.OrangeWithBackground; this.colorPickerNormalTextColor.Image = ((System.Drawing.Image)(resources.GetObject("colorPickerButton1.Image"))); this.colorPickerNormalTextColor.Location = new System.Drawing.Point(280, 7); this.colorPickerNormalTextColor.Name = "colorPickerNormalTextColor"; this.colorPickerNormalTextColor.SelectedColorImageRectangle = new System.Drawing.Rectangle(2, 2, 12, 12); this.colorPickerNormalTextColor.Size = new System.Drawing.Size(37, 23); this.colorPickerNormalTextColor.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.colorPickerNormalTextColor.TabIndex = 2; this.colorPickerNormalTextColor.SelectedColorChanged += new System.EventHandler(this.colorPickers_SelectedColorChanged); // // colorPickerChangedBackgroundColor // this.colorPickerChangedBackgroundColor.AccessibleRole = System.Windows.Forms.AccessibleRole.PushButton; this.colorPickerChangedBackgroundColor.AutoExpandOnClick = true; this.colorPickerChangedBackgroundColor.ColorTable = DevComponents.DotNetBar.eButtonColor.OrangeWithBackground; this.colorPickerChangedBackgroundColor.Image = ((System.Drawing.Image)(resources.GetObject("colorPickerButton1.Image"))); this.colorPickerChangedBackgroundColor.Location = new System.Drawing.Point(120, 35); this.colorPickerChangedBackgroundColor.Name = "colorPickerChangedBackgroundColor"; this.colorPickerChangedBackgroundColor.SelectedColorImageRectangle = new System.Drawing.Rectangle(2, 2, 12, 12); this.colorPickerChangedBackgroundColor.Size = new System.Drawing.Size(37, 23); this.colorPickerChangedBackgroundColor.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.colorPickerChangedBackgroundColor.TabIndex = 3; this.colorPickerChangedBackgroundColor.SelectedColorChanged += new System.EventHandler(this.colorPickers_SelectedColorChanged); // // colorPickerAddedBackgroundColor // this.colorPickerAddedBackgroundColor.AccessibleRole = System.Windows.Forms.AccessibleRole.PushButton; this.colorPickerAddedBackgroundColor.AutoExpandOnClick = true; this.colorPickerAddedBackgroundColor.ColorTable = DevComponents.DotNetBar.eButtonColor.OrangeWithBackground; this.colorPickerAddedBackgroundColor.Image = ((System.Drawing.Image)(resources.GetObject("colorPickerButton1.Image"))); this.colorPickerAddedBackgroundColor.Location = new System.Drawing.Point(120, 63); this.colorPickerAddedBackgroundColor.Name = "colorPickerAddedBackgroundColor"; this.colorPickerAddedBackgroundColor.SelectedColorImageRectangle = new System.Drawing.Rectangle(2, 2, 12, 12); this.colorPickerAddedBackgroundColor.Size = new System.Drawing.Size(37, 23); this.colorPickerAddedBackgroundColor.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.colorPickerAddedBackgroundColor.TabIndex = 4; this.colorPickerAddedBackgroundColor.SelectedColorChanged += new System.EventHandler(this.colorPickers_SelectedColorChanged); // // colorPickerRemovedBackgroundColor // this.colorPickerRemovedBackgroundColor.AccessibleRole = System.Windows.Forms.AccessibleRole.PushButton; this.colorPickerRemovedBackgroundColor.AutoExpandOnClick = true; this.colorPickerRemovedBackgroundColor.ColorTable = DevComponents.DotNetBar.eButtonColor.OrangeWithBackground; this.colorPickerRemovedBackgroundColor.Image = ((System.Drawing.Image)(resources.GetObject("colorPickerButton1.Image"))); this.colorPickerRemovedBackgroundColor.Location = new System.Drawing.Point(120, 91); this.colorPickerRemovedBackgroundColor.Name = "colorPickerRemovedBackgroundColor"; this.colorPickerRemovedBackgroundColor.SelectedColorImageRectangle = new System.Drawing.Rectangle(2, 2, 12, 12); this.colorPickerRemovedBackgroundColor.Size = new System.Drawing.Size(37, 23); this.colorPickerRemovedBackgroundColor.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.colorPickerRemovedBackgroundColor.TabIndex = 5; this.colorPickerRemovedBackgroundColor.SelectedColorChanged += new System.EventHandler(this.colorPickers_SelectedColorChanged); // // colorPickerChangedTextColor // this.colorPickerChangedTextColor.AccessibleRole = System.Windows.Forms.AccessibleRole.PushButton; this.colorPickerChangedTextColor.AutoExpandOnClick = true; this.colorPickerChangedTextColor.ColorTable = DevComponents.DotNetBar.eButtonColor.OrangeWithBackground; this.colorPickerChangedTextColor.Image = ((System.Drawing.Image)(resources.GetObject("colorPickerButton1.Image"))); this.colorPickerChangedTextColor.Location = new System.Drawing.Point(280, 35); this.colorPickerChangedTextColor.Name = "colorPickerChangedTextColor"; this.colorPickerChangedTextColor.SelectedColorImageRectangle = new System.Drawing.Rectangle(2, 2, 12, 12); this.colorPickerChangedTextColor.Size = new System.Drawing.Size(37, 23); this.colorPickerChangedTextColor.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.colorPickerChangedTextColor.TabIndex = 6; this.colorPickerChangedTextColor.SelectedColorChanged += new System.EventHandler(this.colorPickers_SelectedColorChanged); // // colorPickerAddedTextColor // this.colorPickerAddedTextColor.AccessibleRole = System.Windows.Forms.AccessibleRole.PushButton; this.colorPickerAddedTextColor.AutoExpandOnClick = true; this.colorPickerAddedTextColor.ColorTable = DevComponents.DotNetBar.eButtonColor.OrangeWithBackground; this.colorPickerAddedTextColor.Image = ((System.Drawing.Image)(resources.GetObject("colorPickerButton1.Image"))); this.colorPickerAddedTextColor.Location = new System.Drawing.Point(280, 63); this.colorPickerAddedTextColor.Name = "colorPickerAddedTextColor"; this.colorPickerAddedTextColor.SelectedColorImageRectangle = new System.Drawing.Rectangle(2, 2, 12, 12); this.colorPickerAddedTextColor.Size = new System.Drawing.Size(37, 23); this.colorPickerAddedTextColor.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.colorPickerAddedTextColor.TabIndex = 7; this.colorPickerAddedTextColor.SelectedColorChanged += new System.EventHandler(this.colorPickers_SelectedColorChanged); // // colorPickerRemovedTextColor // this.colorPickerRemovedTextColor.AccessibleRole = System.Windows.Forms.AccessibleRole.PushButton; this.colorPickerRemovedTextColor.AutoExpandOnClick = true; this.colorPickerRemovedTextColor.ColorTable = DevComponents.DotNetBar.eButtonColor.OrangeWithBackground; this.colorPickerRemovedTextColor.Image = ((System.Drawing.Image)(resources.GetObject("colorPickerButton1.Image"))); this.colorPickerRemovedTextColor.Location = new System.Drawing.Point(280, 91); this.colorPickerRemovedTextColor.Name = "colorPickerRemovedTextColor"; this.colorPickerRemovedTextColor.SelectedColorImageRectangle = new System.Drawing.Rectangle(2, 2, 12, 12); this.colorPickerRemovedTextColor.Size = new System.Drawing.Size(37, 23); this.colorPickerRemovedTextColor.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.colorPickerRemovedTextColor.TabIndex = 8; this.colorPickerRemovedTextColor.SelectedColorChanged += new System.EventHandler(this.colorPickers_SelectedColorChanged); // // colorPickerHyperlinkColor // this.colorPickerHyperlinkColor.AccessibleRole = System.Windows.Forms.AccessibleRole.PushButton; this.colorPickerHyperlinkColor.AutoExpandOnClick = true; this.colorPickerHyperlinkColor.ColorTable = DevComponents.DotNetBar.eButtonColor.OrangeWithBackground; this.colorPickerHyperlinkColor.Image = ((System.Drawing.Image)(resources.GetObject("colorPickerButton1.Image"))); this.colorPickerHyperlinkColor.Location = new System.Drawing.Point(120, 119); this.colorPickerHyperlinkColor.Name = "colorPickerHyperlinkColor"; this.colorPickerHyperlinkColor.SelectedColorImageRectangle = new System.Drawing.Rectangle(2, 2, 12, 12); this.colorPickerHyperlinkColor.Size = new System.Drawing.Size(37, 23); this.colorPickerHyperlinkColor.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.colorPickerHyperlinkColor.TabIndex = 9; this.colorPickerHyperlinkColor.SelectedColorChanged += new System.EventHandler(this.colorPickers_SelectedColorChanged); // // richTextBoxEx1 // // // // this.richTextBoxEx1.BackgroundStyle.Class = "RichTextBoxBorder"; this.richTextBoxEx1.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.richTextBoxEx1.Location = new System.Drawing.Point(12, 144); this.richTextBoxEx1.Name = "richTextBoxEx1"; this.richTextBoxEx1.Rtf = "{\\rtf1\\ansi\\ansicpg936\\deff0\\deflang1033\\deflangfe1042{\\fonttbl{\\f0\\fnil\\fcharset" + "129 \\\'b5\\\'b8\\\'bf\\\'f2;}}\r\n\\viewkind4\\uc1\\pard\\lang1042\\f0\\fs18\\par\r\n}\r\n"; this.richTextBoxEx1.Size = new System.Drawing.Size(305, 83); this.richTextBoxEx1.TabIndex = 10; this.richTextBoxEx1.Font = new System.Drawing.Font("SimSun", 9f); // // // btnOk // this.btnOk.Location = new System.Drawing.Point(12, 231); this.btnOk.Name = "btnOk"; this.btnOk.Size = new System.Drawing.Size(100, 35); this.btnOk.TabIndex = 11; this.btnOk.Text = "OK"; this.btnOk.ColorTable = DevComponents.DotNetBar.eButtonColor.OrangeWithBackground; this.btnOk.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.btnOk.DialogResult = System.Windows.Forms.DialogResult.OK; // // btnCancel // this.btnCancel.Location = new System.Drawing.Point(117, 231); this.btnCancel.Name = "btnCancel"; this.btnCancel.Size = new System.Drawing.Size(100, 35); this.btnCancel.TabIndex = 12; this.btnCancel.Text = "Cancel"; this.btnCancel.ColorTable = DevComponents.DotNetBar.eButtonColor.OrangeWithBackground; this.btnCancel.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; // // btnDefault // this.btnDefault.Location = new System.Drawing.Point(222, 231); this.btnDefault.Name = "btnDefault"; this.btnDefault.Size = new System.Drawing.Size(100, 35); this.btnDefault.TabIndex = 13; this.btnDefault.Text = "Default"; this.btnDefault.ColorTable = DevComponents.DotNetBar.eButtonColor.OrangeWithBackground; this.btnDefault.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.btnDefault.Click += new System.EventHandler(this.btnDefault_Click); // // FrmCustomCSS // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(329, 280); this.Controls.Add(this.btnOk); this.Controls.Add(this.btnCancel); this.Controls.Add(this.btnDefault); this.Controls.Add(this.richTextBoxEx1); this.Controls.Add(this.colorPickerBackgroundColor); this.Controls.Add(this.colorPickerNormalTextColor); this.Controls.Add(this.colorPickerChangedBackgroundColor); this.Controls.Add(this.colorPickerAddedBackgroundColor); this.Controls.Add(this.colorPickerRemovedBackgroundColor); this.Controls.Add(this.colorPickerChangedTextColor); this.Controls.Add(this.colorPickerAddedTextColor); this.Controls.Add(this.colorPickerRemovedTextColor); this.Controls.Add(this.colorPickerHyperlinkColor); this.Controls.Add(this.lblBackgroundColor); this.Controls.Add(this.lblNormalTextColor); this.Controls.Add(this.lblChangedBackgroundColor); this.Controls.Add(this.lblChangedTextColor); this.Controls.Add(this.lblAddedBackgroundColor); this.Controls.Add(this.lblAddedTextColor); this.Controls.Add(this.lblRemovedBackgroundColor); this.Controls.Add(this.lblRemovedTextColor); this.Controls.Add(this.lblHyperlinkColor); this.DoubleBuffered = true; this.Font = new System.Drawing.Font("SimSun", 9F); this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; this.MaximizeBox = false; this.MinimizeBox = false; this.Name = "FrmCustomCSS"; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; this.Text = "CustomCSS"; this.ResumeLayout(false); this.PerformLayout(); } #endregion private DevComponents.DotNetBar.LabelX lblBackgroundColor; private DevComponents.DotNetBar.LabelX lblNormalTextColor; private DevComponents.DotNetBar.LabelX lblChangedBackgroundColor; private DevComponents.DotNetBar.LabelX lblAddedBackgroundColor; private DevComponents.DotNetBar.LabelX lblRemovedBackgroundColor; private DevComponents.DotNetBar.LabelX lblChangedTextColor; private DevComponents.DotNetBar.LabelX lblAddedTextColor; private DevComponents.DotNetBar.LabelX lblRemovedTextColor; private DevComponents.DotNetBar.LabelX lblHyperlinkColor; private DevComponents.DotNetBar.ButtonX btnOk; private DevComponents.DotNetBar.ButtonX btnCancel; private DevComponents.DotNetBar.ButtonX btnDefault; private DevComponents.DotNetBar.ColorPickerButton colorPickerBackgroundColor; private DevComponents.DotNetBar.ColorPickerButton colorPickerNormalTextColor; private DevComponents.DotNetBar.ColorPickerButton colorPickerChangedBackgroundColor; private DevComponents.DotNetBar.ColorPickerButton colorPickerAddedBackgroundColor; private DevComponents.DotNetBar.ColorPickerButton colorPickerRemovedBackgroundColor; private DevComponents.DotNetBar.ColorPickerButton colorPickerChangedTextColor; private DevComponents.DotNetBar.ColorPickerButton colorPickerAddedTextColor; private DevComponents.DotNetBar.ColorPickerButton colorPickerRemovedTextColor; private DevComponents.DotNetBar.ColorPickerButton colorPickerHyperlinkColor; private DevComponents.DotNetBar.Controls.RichTextBoxEx richTextBoxEx1; } } ================================================ FILE: WzComparerR2/FrmCustomCSS.cs ================================================ using System; using System.Drawing; using System.Globalization; using System.Windows.Forms; using WzComparerR2.Config; namespace WzComparerR2 { public partial class FrmCustomCSS : DevComponents.DotNetBar.Office2007Form { public FrmCustomCSS() { InitializeComponent(); #if NET6_0_OR_GREATER // https://learn.microsoft.com/en-us/dotnet/core/compatibility/fx-core#controldefaultfont-changed-to-segoe-ui-9pt this.Font = new Font(new FontFamily("Microsoft Sans Serif"), 8f); #endif } private bool DarkMode; public Color backgroundColor { get { return colorPickerBackgroundColor.SelectedColor; } set { colorPickerBackgroundColor.SelectedColor = value; } } public Color normalTextColor { get { return colorPickerNormalTextColor.SelectedColor; } set { colorPickerNormalTextColor.SelectedColor = value; } } public Color changedBackgroundColor { get { return colorPickerChangedBackgroundColor.SelectedColor; } set { colorPickerChangedBackgroundColor.SelectedColor = value; } } public Color addedBackgroundColor { get { return colorPickerAddedBackgroundColor.SelectedColor; } set { colorPickerAddedBackgroundColor.SelectedColor = value; } } public Color removedBackgroundColor { get { return colorPickerRemovedBackgroundColor.SelectedColor; } set { colorPickerRemovedBackgroundColor.SelectedColor = value; } } public Color changedTextColor { get { return colorPickerChangedTextColor.SelectedColor; } set { colorPickerChangedTextColor.SelectedColor = value; } } public Color addedTextColor { get { return colorPickerAddedTextColor.SelectedColor; } set { colorPickerAddedTextColor.SelectedColor = value; } } public Color removedTextColor { get { return colorPickerRemovedTextColor.SelectedColor; } set { colorPickerRemovedTextColor.SelectedColor = value; } } public Color hyperlinkColor { get { return colorPickerHyperlinkColor.SelectedColor; } set { colorPickerHyperlinkColor.SelectedColor = value; } } private void btnDefault_Click(object sender, EventArgs e) { if (!DarkMode) { this.backgroundColor = Color.FromArgb(Int32.Parse("ff000000", NumberStyles.HexNumber)); this.normalTextColor = Color.FromArgb(Int32.Parse("ffffffff", NumberStyles.HexNumber)); this.changedBackgroundColor = Color.FromArgb(Int32.Parse("ff003049", NumberStyles.HexNumber)); this.addedBackgroundColor = Color.FromArgb(Int32.Parse("ff000000", NumberStyles.HexNumber)); this.removedBackgroundColor = Color.FromArgb(Int32.Parse("ff462306", NumberStyles.HexNumber)); this.changedTextColor = Color.FromArgb(Int32.Parse("ffffffff", NumberStyles.HexNumber)); this.addedTextColor = Color.FromArgb(Int32.Parse("ffffffff", NumberStyles.HexNumber)); this.removedTextColor = Color.FromArgb(Int32.Parse("ffffffff", NumberStyles.HexNumber)); this.hyperlinkColor = Color.FromArgb(Int32.Parse("ffffffff", NumberStyles.HexNumber)); } else { this.backgroundColor = Color.FromArgb(Int32.Parse("ffffffff", NumberStyles.HexNumber)); this.normalTextColor = Color.FromArgb(Int32.Parse("ff000000", NumberStyles.HexNumber)); this.changedBackgroundColor = Color.FromArgb(Int32.Parse("fffff4c4", NumberStyles.HexNumber)); this.addedBackgroundColor = Color.FromArgb(Int32.Parse("ffebf2f8", NumberStyles.HexNumber)); this.removedBackgroundColor = Color.FromArgb(Int32.Parse("ffffffff", NumberStyles.HexNumber)); this.changedTextColor = Color.FromArgb(Int32.Parse("ff000000", NumberStyles.HexNumber)); this.addedTextColor = Color.FromArgb(Int32.Parse("ff000000", NumberStyles.HexNumber)); this.removedTextColor = Color.FromArgb(Int32.Parse("ff000000", NumberStyles.HexNumber)); this.hyperlinkColor = Color.FromArgb(Int32.Parse("ff0000ff", NumberStyles.HexNumber)); } this.DarkMode = !this.DarkMode; } private void colorPickers_SelectedColorChanged(object sender, EventArgs e) { this.richTextBoxEx1.ReadOnly = false; this.richTextBoxEx1.Clear(); this.richTextBoxEx1.BackColorRichTextBox = backgroundColor; this.richTextBoxEx1.AppendText(" "); var contents = new[] { new {Text = "Hyperlink" + Environment.NewLine, Fore = hyperlinkColor, Back = backgroundColor, Underline = true}, new {Text = " Normal Content Text " + Environment.NewLine, Fore = normalTextColor, Back = backgroundColor, Underline = false}, new {Text = " Changed Content Text " + Environment.NewLine, Fore = changedTextColor, Back = changedBackgroundColor, Underline = false}, new {Text = " Added Content Text " + Environment.NewLine, Fore = addedTextColor, Back = addedBackgroundColor, Underline = false}, new {Text = " Removed Content Text ", Fore = removedTextColor, Back = removedBackgroundColor, Underline = false} }; foreach (var line in contents) { int start = richTextBoxEx1.TextLength; richTextBoxEx1.AppendText(line.Text); richTextBoxEx1.Select(start, line.Text.Length); richTextBoxEx1.SelectionColor = line.Fore; richTextBoxEx1.SelectionBackColor = line.Back; if (line.Underline) { richTextBoxEx1.SelectionFont = new Font(richTextBoxEx1.Font, FontStyle.Underline); } } this.richTextBoxEx1.ReadOnly = true; } public void LoadConfig(CustomCSSConfig config) { this.backgroundColor = config.BackgroundColor; this.normalTextColor = config.NormalTextColor; this.changedBackgroundColor = config.ChangedBackgroundColor; this.addedBackgroundColor = config.AddedBackgroundColor; this.removedBackgroundColor = config.RemovedBackgroundColor; this.changedTextColor = config.ChangedTextColor; this.addedTextColor = config.AddedTextColor; this.removedTextColor = config.RemovedTextColor; this.changedBackgroundColor = config.ChangedBackgroundColor; this.hyperlinkColor = config.HyperlinkColor; } public void SaveConfig(CustomCSSConfig config) { config.BackgroundColor = this.backgroundColor; config.NormalTextColor = this.normalTextColor; config.ChangedBackgroundColor = this.changedBackgroundColor; config.AddedBackgroundColor = this.addedBackgroundColor; config.RemovedBackgroundColor = this.removedBackgroundColor; config.ChangedTextColor = this.changedTextColor; config.AddedTextColor = this.addedTextColor; config.RemovedTextColor = this.removedTextColor; config.ChangedBackgroundColor = this.changedBackgroundColor; config.HyperlinkColor = this.hyperlinkColor; } } } ================================================ FILE: WzComparerR2/FrmCustomCSS.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 17, 17 ================================================ FILE: WzComparerR2/FrmGifClipOptions.Designer.cs ================================================  namespace WzComparerR2 { partial class FrmGifClipOptions { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { this.buttonOK = new DevComponents.DotNetBar.ButtonX(); this.buttonCancel = new DevComponents.DotNetBar.ButtonX(); this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); this.labelX13 = new DevComponents.DotNetBar.LabelX(); this.line2 = new DevComponents.DotNetBar.Controls.Line(); this.labelX1 = new DevComponents.DotNetBar.LabelX(); this.line1 = new DevComponents.DotNetBar.Controls.Line(); this.labelX3 = new DevComponents.DotNetBar.LabelX(); this.labelX4 = new DevComponents.DotNetBar.LabelX(); this.labelX2 = new DevComponents.DotNetBar.LabelX(); this.labelX5 = new DevComponents.DotNetBar.LabelX(); this.labelX6 = new DevComponents.DotNetBar.LabelX(); this.labelX7 = new DevComponents.DotNetBar.LabelX(); this.labelX8 = new DevComponents.DotNetBar.LabelX(); this.labelX9 = new DevComponents.DotNetBar.LabelX(); this.labelX10 = new DevComponents.DotNetBar.LabelX(); this.line3 = new DevComponents.DotNetBar.Controls.Line(); this.labelX11 = new DevComponents.DotNetBar.LabelX(); this.labelX12 = new DevComponents.DotNetBar.LabelX(); this.labelX14 = new DevComponents.DotNetBar.LabelX(); this.txtStartTime = new DevComponents.Editors.IntegerInput(); this.txtStopTime = new DevComponents.Editors.IntegerInput(); this.lblDuration = new DevComponents.DotNetBar.LabelX(); this.txtClipLeft = new DevComponents.Editors.IntegerInput(); this.txtClipTop = new DevComponents.Editors.IntegerInput(); this.txtClipRight = new DevComponents.Editors.IntegerInput(); this.txtClipBottom = new DevComponents.Editors.IntegerInput(); this.lblClipSize = new DevComponents.DotNetBar.LabelX(); this.txtWidth = new DevComponents.Editors.IntegerInput(); this.txtHeight = new DevComponents.Editors.IntegerInput(); this.txtScale = new DevComponents.Editors.IntegerInput(); this.txtStartTimeNew = new DevComponents.Editors.IntegerInput(); this.txtStopTimeNew = new DevComponents.Editors.IntegerInput(); this.lblDurationNew = new DevComponents.DotNetBar.LabelX(); this.txtClipLeftNew = new DevComponents.Editors.IntegerInput(); this.txtClipTopNew = new DevComponents.Editors.IntegerInput(); this.txtClipRightNew = new DevComponents.Editors.IntegerInput(); this.txtClipBottomNew = new DevComponents.Editors.IntegerInput(); this.lblClipSizeNew = new DevComponents.DotNetBar.LabelX(); this.txtWidthNew = new DevComponents.Editors.IntegerInput(); this.txtHeightNew = new DevComponents.Editors.IntegerInput(); this.txtScaleNew = new DevComponents.Editors.IntegerInput(); this.tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel(); this.tableLayoutPanel1.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.txtStartTime)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.txtStopTime)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.txtClipLeft)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.txtClipTop)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.txtClipRight)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.txtClipBottom)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.txtWidth)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.txtHeight)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.txtScale)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.txtStartTimeNew)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.txtStopTimeNew)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.txtClipLeftNew)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.txtClipTopNew)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.txtClipRightNew)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.txtClipBottomNew)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.txtWidthNew)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.txtHeightNew)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.txtScaleNew)).BeginInit(); this.tableLayoutPanel2.SuspendLayout(); this.SuspendLayout(); // // buttonOK // this.buttonOK.AccessibleRole = System.Windows.Forms.AccessibleRole.PushButton; this.buttonOK.Anchor = System.Windows.Forms.AnchorStyles.None; this.buttonOK.ColorTable = DevComponents.DotNetBar.eButtonColor.OrangeWithBackground; this.buttonOK.DialogResult = System.Windows.Forms.DialogResult.OK; this.buttonOK.Location = new System.Drawing.Point(58, 3); this.buttonOK.Margin = new System.Windows.Forms.Padding(30, 3, 3, 3); this.buttonOK.Name = "buttonOK"; this.buttonOK.Size = new System.Drawing.Size(75, 23); this.buttonOK.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.buttonOK.Symbol = ""; this.buttonOK.SymbolSize = 1F; this.buttonOK.TabIndex = 0; this.buttonOK.Text = "OK"; // // buttonCancel // this.buttonCancel.AccessibleRole = System.Windows.Forms.AccessibleRole.PushButton; this.buttonCancel.Anchor = System.Windows.Forms.AnchorStyles.None; this.buttonCancel.ColorTable = DevComponents.DotNetBar.eButtonColor.OrangeWithBackground; this.buttonCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; this.buttonCancel.Location = new System.Drawing.Point(195, 3); this.buttonCancel.Margin = new System.Windows.Forms.Padding(3, 3, 30, 3); this.buttonCancel.Name = "buttonCancel"; this.buttonCancel.Size = new System.Drawing.Size(75, 23); this.buttonCancel.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.buttonCancel.TabIndex = 1; this.buttonCancel.Text = "Cancel"; // // tableLayoutPanel1 // this.tableLayoutPanel1.ColumnCount = 4; this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 80F)); this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 100F)); this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); this.tableLayoutPanel1.Controls.Add(this.labelX13, 1, 11); this.tableLayoutPanel1.Controls.Add(this.line2, 0, 9); this.tableLayoutPanel1.Controls.Add(this.labelX1, 0, 0); this.tableLayoutPanel1.Controls.Add(this.line1, 0, 3); this.tableLayoutPanel1.Controls.Add(this.labelX3, 1, 0); this.tableLayoutPanel1.Controls.Add(this.labelX4, 1, 1); this.tableLayoutPanel1.Controls.Add(this.labelX2, 0, 4); this.tableLayoutPanel1.Controls.Add(this.labelX5, 1, 2); this.tableLayoutPanel1.Controls.Add(this.labelX6, 1, 4); this.tableLayoutPanel1.Controls.Add(this.labelX7, 1, 5); this.tableLayoutPanel1.Controls.Add(this.labelX8, 1, 6); this.tableLayoutPanel1.Controls.Add(this.labelX9, 1, 7); this.tableLayoutPanel1.Controls.Add(this.labelX10, 1, 8); this.tableLayoutPanel1.Controls.Add(this.line3, 0, 13); this.tableLayoutPanel1.Controls.Add(this.labelX11, 0, 10); this.tableLayoutPanel1.Controls.Add(this.labelX12, 1, 10); this.tableLayoutPanel1.Controls.Add(this.labelX14, 1, 12); this.tableLayoutPanel1.Controls.Add(this.txtStartTime, 2, 0); this.tableLayoutPanel1.Controls.Add(this.txtStopTime, 2, 1); this.tableLayoutPanel1.Controls.Add(this.lblDuration, 2, 2); this.tableLayoutPanel1.Controls.Add(this.txtClipLeft, 2, 4); this.tableLayoutPanel1.Controls.Add(this.txtClipTop, 2, 5); this.tableLayoutPanel1.Controls.Add(this.txtClipRight, 2, 6); this.tableLayoutPanel1.Controls.Add(this.txtClipBottom, 2, 7); this.tableLayoutPanel1.Controls.Add(this.lblClipSize, 2, 8); this.tableLayoutPanel1.Controls.Add(this.txtWidth, 2, 10); this.tableLayoutPanel1.Controls.Add(this.txtHeight, 2, 11); this.tableLayoutPanel1.Controls.Add(this.txtScale, 2, 12); this.tableLayoutPanel1.Controls.Add(this.txtStartTimeNew, 3, 0); this.tableLayoutPanel1.Controls.Add(this.txtStopTimeNew, 3, 1); this.tableLayoutPanel1.Controls.Add(this.lblDurationNew, 3, 2); this.tableLayoutPanel1.Controls.Add(this.txtClipLeftNew, 3, 4); this.tableLayoutPanel1.Controls.Add(this.txtClipTopNew, 3, 5); this.tableLayoutPanel1.Controls.Add(this.txtClipRightNew, 3, 6); this.tableLayoutPanel1.Controls.Add(this.txtClipBottomNew, 3, 7); this.tableLayoutPanel1.Controls.Add(this.lblClipSizeNew, 3, 8); this.tableLayoutPanel1.Controls.Add(this.txtWidthNew, 3, 10); this.tableLayoutPanel1.Controls.Add(this.txtHeightNew, 3, 11); this.tableLayoutPanel1.Controls.Add(this.txtScaleNew, 3, 12); this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; this.tableLayoutPanel1.Location = new System.Drawing.Point(8, 8); this.tableLayoutPanel1.Name = "tableLayoutPanel1"; this.tableLayoutPanel1.RowCount = 14; this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 9.090908F)); this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 9.090908F)); this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 9.090908F)); this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 6F)); this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 9.090908F)); this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 9.090908F)); this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 9.090908F)); this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 9.090908F)); this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 9.090908F)); this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 6F)); this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 9.090908F)); this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 9.090908F)); this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 9.090908F)); this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 6F)); this.tableLayoutPanel1.Size = new System.Drawing.Size(328, 305); this.tableLayoutPanel1.TabIndex = 2; // // labelX13 // this.labelX13.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); // // // this.labelX13.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.labelX13.Location = new System.Drawing.Point(83, 249); this.labelX13.Name = "labelX13"; this.labelX13.Size = new System.Drawing.Size(94, 20); this.labelX13.TabIndex = 38; this.labelX13.Text = "Height (px)"; // // line2 // this.line2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); this.tableLayoutPanel1.SetColumnSpan(this.line2, 4); this.line2.ForeColor = System.Drawing.Color.Gray; this.line2.Location = new System.Drawing.Point(3, 217); this.line2.Name = "line2"; this.line2.Size = new System.Drawing.Size(322, 1); this.line2.TabIndex = 34; this.line2.TabStop = false; this.line2.Text = "line2"; // // labelX1 // this.labelX1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); // // // this.labelX1.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.labelX1.Location = new System.Drawing.Point(3, 3); this.labelX1.Name = "labelX1"; this.labelX1.Size = new System.Drawing.Size(74, 20); this.labelX1.TabIndex = 0; this.labelX1.Text = "Clip Length"; // // line1 // this.line1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); this.tableLayoutPanel1.SetColumnSpan(this.line1, 4); this.line1.ForeColor = System.Drawing.Color.Gray; this.line1.Location = new System.Drawing.Point(3, 81); this.line1.Name = "line1"; this.line1.Size = new System.Drawing.Size(322, 1); this.line1.TabIndex = 7; this.line1.TabStop = false; this.line1.Text = "line1"; // // labelX3 // this.labelX3.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); // // // this.labelX3.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.labelX3.Location = new System.Drawing.Point(83, 3); this.labelX3.Name = "labelX3"; this.labelX3.Size = new System.Drawing.Size(94, 20); this.labelX3.TabIndex = 8; this.labelX3.Text = "StartTime (ms)"; // // labelX4 // this.labelX4.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); // // // this.labelX4.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.labelX4.Location = new System.Drawing.Point(83, 29); this.labelX4.Name = "labelX4"; this.labelX4.Size = new System.Drawing.Size(94, 20); this.labelX4.TabIndex = 9; this.labelX4.Text = "StopTime (ms)"; // // labelX2 // this.labelX2.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); // // // this.labelX2.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.labelX2.Location = new System.Drawing.Point(3, 87); this.labelX2.Name = "labelX2"; this.labelX2.Size = new System.Drawing.Size(74, 20); this.labelX2.TabIndex = 1; this.labelX2.Text = "Clip Bounds"; // // labelX5 // this.labelX5.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); // // // this.labelX5.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.labelX5.Location = new System.Drawing.Point(83, 55); this.labelX5.Name = "labelX5"; this.labelX5.Size = new System.Drawing.Size(94, 20); this.labelX5.TabIndex = 10; this.labelX5.Text = "Duration"; // // labelX6 // this.labelX6.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); // // // this.labelX6.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.labelX6.Location = new System.Drawing.Point(83, 87); this.labelX6.Name = "labelX6"; this.labelX6.Size = new System.Drawing.Size(94, 20); this.labelX6.TabIndex = 25; this.labelX6.Text = "Left (px)"; // // labelX7 // this.labelX7.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); // // // this.labelX7.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.labelX7.Location = new System.Drawing.Point(83, 113); this.labelX7.Name = "labelX7"; this.labelX7.Size = new System.Drawing.Size(94, 20); this.labelX7.TabIndex = 26; this.labelX7.Text = "Top (px)"; // // labelX8 // this.labelX8.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); // // // this.labelX8.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.labelX8.Location = new System.Drawing.Point(83, 139); this.labelX8.Name = "labelX8"; this.labelX8.Size = new System.Drawing.Size(94, 20); this.labelX8.TabIndex = 27; this.labelX8.Text = "Right (px)"; // // labelX9 // this.labelX9.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); // // // this.labelX9.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.labelX9.Location = new System.Drawing.Point(83, 165); this.labelX9.Name = "labelX9"; this.labelX9.Size = new System.Drawing.Size(94, 20); this.labelX9.TabIndex = 28; this.labelX9.Text = "Bottom (px)"; // // labelX10 // this.labelX10.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); // // // this.labelX10.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.labelX10.Location = new System.Drawing.Point(83, 191); this.labelX10.Name = "labelX10"; this.labelX10.Size = new System.Drawing.Size(94, 20); this.labelX10.TabIndex = 29; this.labelX10.Text = "Expected Size"; // // line3 // this.line3.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); this.tableLayoutPanel1.SetColumnSpan(this.line3, 4); this.line3.ForeColor = System.Drawing.Color.Gray; this.line3.Location = new System.Drawing.Point(3, 301); this.line3.Name = "line3"; this.line3.Size = new System.Drawing.Size(322, 1); this.line3.TabIndex = 35; this.line3.TabStop = false; this.line3.Text = "line3"; // // labelX11 // this.labelX11.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); // // // this.labelX11.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.labelX11.Location = new System.Drawing.Point(3, 223); this.labelX11.Name = "labelX11"; this.labelX11.Size = new System.Drawing.Size(74, 20); this.labelX11.TabIndex = 36; this.labelX11.Text = "Scale"; // // labelX12 // this.labelX12.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); // // // this.labelX12.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.labelX12.Location = new System.Drawing.Point(83, 223); this.labelX12.Name = "labelX12"; this.labelX12.Size = new System.Drawing.Size(94, 20); this.labelX12.TabIndex = 37; this.labelX12.Text = "Width (px)"; // // labelX14 // this.labelX14.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); // // // this.labelX14.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.labelX14.Location = new System.Drawing.Point(83, 275); this.labelX14.Name = "labelX14"; this.labelX14.Size = new System.Drawing.Size(94, 20); this.labelX14.TabIndex = 38; this.labelX14.Text = "Scale (%)"; // // txtStartTime // this.txtStartTime.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); // // // this.txtStartTime.BackgroundStyle.Class = "DateTimeInputBackground"; this.txtStartTime.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.txtStartTime.ButtonFreeText.Shortcut = DevComponents.DotNetBar.eShortcut.F2; this.txtStartTime.ForeColor = System.Drawing.SystemColors.ControlDark; this.txtStartTime.IsInputReadOnly = true; this.txtStartTime.Location = new System.Drawing.Point(183, 3); this.txtStartTime.Name = "txtStartTime"; this.txtStartTime.Size = new System.Drawing.Size(68, 21); this.txtStartTime.TabIndex = 0; this.txtStartTime.TabStop = false; this.txtStartTime.ValueObjectChanged += new System.EventHandler(this.txtTime_ValueObjectChanged); // // txtStopTime // this.txtStopTime.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); // // // this.txtStopTime.BackgroundStyle.Class = "DateTimeInputBackground"; this.txtStopTime.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.txtStopTime.ButtonFreeText.Shortcut = DevComponents.DotNetBar.eShortcut.F2; this.txtStopTime.ForeColor = System.Drawing.SystemColors.ControlDark; this.txtStopTime.IsInputReadOnly = true; this.txtStopTime.Location = new System.Drawing.Point(183, 29); this.txtStopTime.Name = "txtStopTime"; this.txtStopTime.Size = new System.Drawing.Size(68, 21); this.txtStopTime.TabIndex = 2; this.txtStopTime.TabStop = false; this.txtStopTime.ValueObjectChanged += new System.EventHandler(this.txtTime_ValueObjectChanged); // // lblDuration // this.lblDuration.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); // // // this.lblDuration.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.lblDuration.Location = new System.Drawing.Point(183, 55); this.lblDuration.Name = "lblDuration"; this.lblDuration.Size = new System.Drawing.Size(68, 20); this.lblDuration.TabIndex = 4; this.lblDuration.Text = "0 ms"; // // txtClipLeft // this.txtClipLeft.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); // // // this.txtClipLeft.BackgroundStyle.Class = "DateTimeInputBackground"; this.txtClipLeft.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.txtClipLeft.ButtonFreeText.Shortcut = DevComponents.DotNetBar.eShortcut.F2; this.txtClipLeft.ForeColor = System.Drawing.SystemColors.ControlDark; this.txtClipLeft.IsInputReadOnly = true; this.txtClipLeft.Location = new System.Drawing.Point(183, 87); this.txtClipLeft.Name = "txtClipLeft"; this.txtClipLeft.Size = new System.Drawing.Size(68, 21); this.txtClipLeft.TabIndex = 6; this.txtClipLeft.TabStop = false; this.txtClipLeft.ValueObjectChanged += new System.EventHandler(this.txtBound_ValueObjectChanged); // // txtClipTop // this.txtClipTop.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); // // // this.txtClipTop.BackgroundStyle.Class = "DateTimeInputBackground"; this.txtClipTop.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.txtClipTop.ButtonFreeText.Shortcut = DevComponents.DotNetBar.eShortcut.F2; this.txtClipTop.ForeColor = System.Drawing.SystemColors.ControlDark; this.txtClipTop.IsInputReadOnly = true; this.txtClipTop.Location = new System.Drawing.Point(183, 113); this.txtClipTop.Name = "txtClipTop"; this.txtClipTop.Size = new System.Drawing.Size(68, 21); this.txtClipTop.TabIndex = 8; this.txtClipTop.TabStop = false; this.txtClipTop.ValueObjectChanged += new System.EventHandler(this.txtBound_ValueObjectChanged); // // txtClipRight // this.txtClipRight.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); // // // this.txtClipRight.BackgroundStyle.Class = "DateTimeInputBackground"; this.txtClipRight.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.txtClipRight.ButtonFreeText.Shortcut = DevComponents.DotNetBar.eShortcut.F2; this.txtClipRight.ForeColor = System.Drawing.SystemColors.ControlDark; this.txtClipRight.IsInputReadOnly = true; this.txtClipRight.Location = new System.Drawing.Point(183, 139); this.txtClipRight.Name = "txtClipRight"; this.txtClipRight.Size = new System.Drawing.Size(68, 21); this.txtClipRight.TabIndex = 10; this.txtClipRight.TabStop = false; this.txtClipRight.ValueObjectChanged += new System.EventHandler(this.txtBound_ValueObjectChanged); // // txtClipBottom // this.txtClipBottom.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); // // // this.txtClipBottom.BackgroundStyle.Class = "DateTimeInputBackground"; this.txtClipBottom.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.txtClipBottom.ButtonFreeText.Shortcut = DevComponents.DotNetBar.eShortcut.F2; this.txtClipBottom.ForeColor = System.Drawing.SystemColors.ControlDark; this.txtClipBottom.IsInputReadOnly = true; this.txtClipBottom.Location = new System.Drawing.Point(183, 165); this.txtClipBottom.Name = "txtClipBottom"; this.txtClipBottom.Size = new System.Drawing.Size(68, 21); this.txtClipBottom.TabIndex = 12; this.txtClipBottom.TabStop = false; this.txtClipBottom.ValueObjectChanged += new System.EventHandler(this.txtBound_ValueObjectChanged); // // lblClipSize // this.lblClipSize.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); // // // this.lblClipSize.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.lblClipSize.Location = new System.Drawing.Point(183, 191); this.lblClipSize.Name = "lblClipSize"; this.lblClipSize.Size = new System.Drawing.Size(68, 20); this.lblClipSize.TabIndex = 14; this.lblClipSize.Text = "1280x720"; // // txtWidth // this.txtWidth.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); // // // this.txtWidth.BackgroundStyle.Class = "DateTimeInputBackground"; this.txtWidth.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.txtWidth.ButtonFreeText.Shortcut = DevComponents.DotNetBar.eShortcut.F2; this.txtWidth.ForeColor = System.Drawing.SystemColors.ControlDark; this.txtWidth.IsInputReadOnly = true; this.txtWidth.Location = new System.Drawing.Point(183, 223); this.txtWidth.Name = "txtWidth"; this.txtWidth.Size = new System.Drawing.Size(68, 21); this.txtWidth.TabIndex = 17; this.txtWidth.TabStop = false; this.txtWidth.ValueObjectChanged += new System.EventHandler(this.txtSize_ValueObjectChanged); // // txtHeight // this.txtHeight.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); // // // this.txtHeight.BackgroundStyle.Class = "DateTimeInputBackground"; this.txtHeight.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.txtHeight.ButtonFreeText.Shortcut = DevComponents.DotNetBar.eShortcut.F2; this.txtHeight.ForeColor = System.Drawing.SystemColors.ControlDark; this.txtHeight.IsInputReadOnly = true; this.txtHeight.Location = new System.Drawing.Point(183, 249); this.txtHeight.Name = "txtHeight"; this.txtHeight.Size = new System.Drawing.Size(68, 21); this.txtHeight.TabIndex = 19; this.txtHeight.TabStop = false; this.txtHeight.ValueObjectChanged += new System.EventHandler(this.txtSize_ValueObjectChanged); // // txtScale // this.txtScale.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); // // // this.txtScale.BackgroundStyle.Class = "DateTimeInputBackground"; this.txtScale.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.txtScale.ButtonFreeText.Shortcut = DevComponents.DotNetBar.eShortcut.F2; this.txtScale.ForeColor = System.Drawing.SystemColors.ControlDark; this.txtScale.IsInputReadOnly = true; this.txtScale.Location = new System.Drawing.Point(183, 275); this.txtScale.Name = "txtScale"; this.txtScale.Size = new System.Drawing.Size(68, 21); this.txtScale.TabIndex = 21; this.txtScale.TabStop = false; // // txtStartTimeNew // this.txtStartTimeNew.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); // // // this.txtStartTimeNew.BackgroundStyle.Class = "DateTimeInputBackground"; this.txtStartTimeNew.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.txtStartTimeNew.ButtonFreeText.Shortcut = DevComponents.DotNetBar.eShortcut.F2; this.txtStartTimeNew.Increment = 10; this.txtStartTimeNew.Location = new System.Drawing.Point(257, 3); this.txtStartTimeNew.MinValue = 0; this.txtStartTimeNew.Name = "txtStartTimeNew"; this.txtStartTimeNew.ShowUpDown = true; this.txtStartTimeNew.Size = new System.Drawing.Size(68, 21); this.txtStartTimeNew.TabIndex = 1; this.txtStartTimeNew.ValueObjectChanged += new System.EventHandler(this.txtTimeNew_ValueObjectChanged); // // txtStopTimeNew // this.txtStopTimeNew.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); // // // this.txtStopTimeNew.BackgroundStyle.Class = "DateTimeInputBackground"; this.txtStopTimeNew.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.txtStopTimeNew.ButtonFreeText.Shortcut = DevComponents.DotNetBar.eShortcut.F2; this.txtStopTimeNew.Increment = 10; this.txtStopTimeNew.Location = new System.Drawing.Point(257, 29); this.txtStopTimeNew.MinValue = 0; this.txtStopTimeNew.Name = "txtStopTimeNew"; this.txtStopTimeNew.ShowUpDown = true; this.txtStopTimeNew.Size = new System.Drawing.Size(68, 21); this.txtStopTimeNew.TabIndex = 3; this.txtStopTimeNew.ValueObjectChanged += new System.EventHandler(this.txtTimeNew_ValueObjectChanged); // // lblDurationNew // this.lblDurationNew.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); // // // this.lblDurationNew.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.lblDurationNew.Location = new System.Drawing.Point(257, 55); this.lblDurationNew.Name = "lblDurationNew"; this.lblDurationNew.Size = new System.Drawing.Size(68, 20); this.lblDurationNew.TabIndex = 5; this.lblDurationNew.Text = "0 ms"; // // txtClipLeftNew // this.txtClipLeftNew.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); // // // this.txtClipLeftNew.BackgroundStyle.Class = "DateTimeInputBackground"; this.txtClipLeftNew.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.txtClipLeftNew.ButtonFreeText.Shortcut = DevComponents.DotNetBar.eShortcut.F2; this.txtClipLeftNew.Location = new System.Drawing.Point(257, 87); this.txtClipLeftNew.MaxValue = 16384; this.txtClipLeftNew.MinValue = -16384; this.txtClipLeftNew.Name = "txtClipLeftNew"; this.txtClipLeftNew.ShowUpDown = true; this.txtClipLeftNew.Size = new System.Drawing.Size(68, 21); this.txtClipLeftNew.TabIndex = 7; this.txtClipLeftNew.ValueObjectChanged += new System.EventHandler(this.txtBoundNew_ValueObjectChanged); // // txtClipTopNew // this.txtClipTopNew.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); // // // this.txtClipTopNew.BackgroundStyle.Class = "DateTimeInputBackground"; this.txtClipTopNew.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.txtClipTopNew.ButtonFreeText.Shortcut = DevComponents.DotNetBar.eShortcut.F2; this.txtClipTopNew.Location = new System.Drawing.Point(257, 113); this.txtClipTopNew.MaxValue = 16384; this.txtClipTopNew.MinValue = -16384; this.txtClipTopNew.Name = "txtClipTopNew"; this.txtClipTopNew.ShowUpDown = true; this.txtClipTopNew.Size = new System.Drawing.Size(68, 21); this.txtClipTopNew.TabIndex = 9; this.txtClipTopNew.ValueObjectChanged += new System.EventHandler(this.txtBoundNew_ValueObjectChanged); // // txtClipRightNew // this.txtClipRightNew.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); // // // this.txtClipRightNew.BackgroundStyle.Class = "DateTimeInputBackground"; this.txtClipRightNew.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.txtClipRightNew.ButtonFreeText.Shortcut = DevComponents.DotNetBar.eShortcut.F2; this.txtClipRightNew.Location = new System.Drawing.Point(257, 139); this.txtClipRightNew.MaxValue = 16384; this.txtClipRightNew.MinValue = -16384; this.txtClipRightNew.Name = "txtClipRightNew"; this.txtClipRightNew.ShowUpDown = true; this.txtClipRightNew.Size = new System.Drawing.Size(68, 21); this.txtClipRightNew.TabIndex = 11; this.txtClipRightNew.ValueObjectChanged += new System.EventHandler(this.txtBoundNew_ValueObjectChanged); // // txtClipBottomNew // this.txtClipBottomNew.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); // // // this.txtClipBottomNew.BackgroundStyle.Class = "DateTimeInputBackground"; this.txtClipBottomNew.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.txtClipBottomNew.ButtonFreeText.Shortcut = DevComponents.DotNetBar.eShortcut.F2; this.txtClipBottomNew.Location = new System.Drawing.Point(257, 165); this.txtClipBottomNew.MaxValue = 16384; this.txtClipBottomNew.MinValue = -16384; this.txtClipBottomNew.Name = "txtClipBottomNew"; this.txtClipBottomNew.ShowUpDown = true; this.txtClipBottomNew.Size = new System.Drawing.Size(68, 21); this.txtClipBottomNew.TabIndex = 13; this.txtClipBottomNew.ValueObjectChanged += new System.EventHandler(this.txtBoundNew_ValueObjectChanged); // // lblClipSizeNew // this.lblClipSizeNew.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); // // // this.lblClipSizeNew.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.lblClipSizeNew.Location = new System.Drawing.Point(257, 191); this.lblClipSizeNew.Name = "lblClipSizeNew"; this.lblClipSizeNew.Size = new System.Drawing.Size(68, 20); this.lblClipSizeNew.TabIndex = 15; this.lblClipSizeNew.Text = "1280x720"; // // txtWidthNew // this.txtWidthNew.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); // // // this.txtWidthNew.BackgroundStyle.Class = "DateTimeInputBackground"; this.txtWidthNew.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.txtWidthNew.ButtonFreeText.Shortcut = DevComponents.DotNetBar.eShortcut.F2; this.txtWidthNew.Location = new System.Drawing.Point(257, 223); this.txtWidthNew.MaxValue = 16384; this.txtWidthNew.MinValue = -16384; this.txtWidthNew.Name = "txtWidthNew"; this.txtWidthNew.ShowUpDown = true; this.txtWidthNew.Size = new System.Drawing.Size(68, 21); this.txtWidthNew.TabIndex = 18; this.txtWidthNew.ValueObjectChanged += new System.EventHandler(this.txtSizeNew_ValueObjectChanged); // // txtHeightNew // this.txtHeightNew.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); // // // this.txtHeightNew.BackgroundStyle.Class = "DateTimeInputBackground"; this.txtHeightNew.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.txtHeightNew.ButtonFreeText.Shortcut = DevComponents.DotNetBar.eShortcut.F2; this.txtHeightNew.Location = new System.Drawing.Point(257, 249); this.txtHeightNew.MaxValue = 16384; this.txtHeightNew.MinValue = -16384; this.txtHeightNew.Name = "txtHeightNew"; this.txtHeightNew.ShowUpDown = true; this.txtHeightNew.Size = new System.Drawing.Size(68, 21); this.txtHeightNew.TabIndex = 20; this.txtHeightNew.ValueObjectChanged += new System.EventHandler(this.txtSizeNew_ValueObjectChanged); // // txtScaleNew // this.txtScaleNew.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); // // // this.txtScaleNew.BackgroundStyle.Class = "DateTimeInputBackground"; this.txtScaleNew.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.txtScaleNew.ButtonFreeText.Shortcut = DevComponents.DotNetBar.eShortcut.F2; this.txtScaleNew.Location = new System.Drawing.Point(257, 275); this.txtScaleNew.MaxValue = 65535; this.txtScaleNew.MinValue = 0; this.txtScaleNew.Name = "txtScaleNew"; this.txtScaleNew.ShowUpDown = true; this.txtScaleNew.Size = new System.Drawing.Size(68, 21); this.txtScaleNew.TabIndex = 22; this.txtScaleNew.ValueObjectChanged += new System.EventHandler(this.txtScaleNew_ValueObjectChanged); // // tableLayoutPanel2 // this.tableLayoutPanel2.ColumnCount = 2; this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); this.tableLayoutPanel2.Controls.Add(this.buttonCancel, 1, 0); this.tableLayoutPanel2.Controls.Add(this.buttonOK, 0, 0); this.tableLayoutPanel2.Dock = System.Windows.Forms.DockStyle.Bottom; this.tableLayoutPanel2.Location = new System.Drawing.Point(8, 313); this.tableLayoutPanel2.Name = "tableLayoutPanel2"; this.tableLayoutPanel2.RowCount = 1; this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 30F)); this.tableLayoutPanel2.Size = new System.Drawing.Size(328, 30); this.tableLayoutPanel2.TabIndex = 3; // // FrmGifClipOptions // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.CancelButton = this.buttonCancel; this.ClientSize = new System.Drawing.Size(344, 351); this.Controls.Add(this.tableLayoutPanel1); this.Controls.Add(this.tableLayoutPanel2); this.DoubleBuffered = true; this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; this.MaximizeBox = false; this.MinimizeBox = false; this.Name = "FrmGifClipOptions"; this.Padding = new System.Windows.Forms.Padding(8); this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; this.Text = "GifClipOptions"; this.tableLayoutPanel1.ResumeLayout(false); ((System.ComponentModel.ISupportInitialize)(this.txtStartTime)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.txtStopTime)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.txtClipLeft)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.txtClipTop)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.txtClipRight)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.txtClipBottom)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.txtWidth)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.txtHeight)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.txtScale)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.txtStartTimeNew)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.txtStopTimeNew)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.txtClipLeftNew)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.txtClipTopNew)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.txtClipRightNew)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.txtClipBottomNew)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.txtWidthNew)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.txtHeightNew)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.txtScaleNew)).EndInit(); this.tableLayoutPanel2.ResumeLayout(false); this.ResumeLayout(false); } #endregion private DevComponents.DotNetBar.ButtonX buttonOK; private DevComponents.DotNetBar.ButtonX buttonCancel; private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; private DevComponents.DotNetBar.LabelX labelX1; private DevComponents.DotNetBar.Controls.Line line1; private DevComponents.DotNetBar.LabelX labelX2; private System.Windows.Forms.TableLayoutPanel tableLayoutPanel2; private DevComponents.DotNetBar.LabelX labelX3; private DevComponents.Editors.IntegerInput txtStartTimeNew; private DevComponents.DotNetBar.LabelX labelX4; private DevComponents.DotNetBar.LabelX labelX5; private DevComponents.Editors.IntegerInput txtStartTime; private DevComponents.Editors.IntegerInput txtStopTime; private DevComponents.Editors.IntegerInput txtStopTimeNew; private DevComponents.Editors.IntegerInput txtClipLeft; private DevComponents.Editors.IntegerInput txtClipLeftNew; private DevComponents.Editors.IntegerInput txtClipTop; private DevComponents.Editors.IntegerInput txtClipTopNew; private DevComponents.Editors.IntegerInput txtClipRight; private DevComponents.Editors.IntegerInput txtClipRightNew; private DevComponents.Editors.IntegerInput txtClipBottom; private DevComponents.Editors.IntegerInput txtClipBottomNew; private DevComponents.DotNetBar.LabelX labelX6; private DevComponents.DotNetBar.LabelX labelX7; private DevComponents.DotNetBar.LabelX labelX8; private DevComponents.DotNetBar.LabelX labelX9; private DevComponents.DotNetBar.LabelX labelX10; private DevComponents.DotNetBar.LabelX lblDuration; private DevComponents.DotNetBar.LabelX lblDurationNew; private DevComponents.DotNetBar.LabelX lblClipSize; private DevComponents.DotNetBar.LabelX lblClipSizeNew; private DevComponents.DotNetBar.Controls.Line line2; private DevComponents.DotNetBar.Controls.Line line3; private DevComponents.DotNetBar.LabelX labelX13; private DevComponents.DotNetBar.LabelX labelX11; private DevComponents.DotNetBar.LabelX labelX12; private DevComponents.DotNetBar.LabelX labelX14; private DevComponents.Editors.IntegerInput txtWidth; private DevComponents.Editors.IntegerInput txtHeight; private DevComponents.Editors.IntegerInput txtScale; private DevComponents.Editors.IntegerInput txtWidthNew; private DevComponents.Editors.IntegerInput txtHeightNew; private DevComponents.Editors.IntegerInput txtScaleNew; } } ================================================ FILE: WzComparerR2/FrmGifClipOptions.cs ================================================ using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using DevComponents.Editors; using WzComparerR2.Controls; namespace WzComparerR2 { public partial class FrmGifClipOptions : DevComponents.DotNetBar.Office2007Form { public FrmGifClipOptions() { #if NET6_0_OR_GREATER // https://learn.microsoft.com/en-us/dotnet/core/compatibility/fx-core#controldefaultfont-changed-to-segoe-ui-9pt this.Font = new Font(new FontFamily("Microsoft Sans Serif"), 8f); #endif InitializeComponent(); } public AnimationClipOptions ClipOptions { get { return this.CollectControlValues(false); } set { this.ApplyControlValues(value, false); } } public AnimationClipOptions ClipOptionsNew { get { return this.CollectControlValues(true); } set { this.ApplyControlValues(value, true); } } private bool isUpdating; private IntegerInput[] GetInputGroup(bool isNew) { return isNew ? new[] { this.txtStartTimeNew, this.txtStopTimeNew, this.txtClipLeftNew, this.txtClipTopNew, this.txtClipRightNew, this.txtClipBottomNew, this.txtWidthNew, this.txtHeightNew, this.txtScaleNew } : new[] { this.txtStartTime, this.txtStopTime, this.txtClipLeft, this.txtClipTop, this.txtClipRight, this.txtClipBottom, this.txtWidth, this.txtHeight, this.txtScale }; } private void ApplyControlValues(AnimationClipOptions value, bool isNew) { var controls = GetInputGroup(isNew); if (value != null) { controls[0].ValueObject = value.StartTime; controls[1].ValueObject = value.StopTime; controls[2].ValueObject = value.Left; controls[3].ValueObject = value.Top; controls[4].ValueObject = value.Right; controls[5].ValueObject = value.Bottom; controls[6].ValueObject = value.OutputWidth; controls[7].ValueObject = value.OutputHeight; } else { foreach (var txt in controls) { txt.ValueObject = null; } } } private AnimationClipOptions CollectControlValues(bool isNew) { var controls = GetInputGroup(isNew); return new AnimationClipOptions() { StartTime = controls[0].ValueObject as int?, StopTime = controls[1].ValueObject as int?, Left = controls[2].ValueObject as int?, Top = controls[3].ValueObject as int?, Right = controls[4].ValueObject as int?, Bottom = controls[5].ValueObject as int?, OutputWidth = controls[6].ValueObject as int?, OutputHeight = controls[7].ValueObject as int?, }; } private void LockEvent(Action action) { if (this.isUpdating) { return; } this.isUpdating = true; try { action?.Invoke(); } finally { this.isUpdating = false; } } private void txtTime_ValueObjectChanged(object sender, EventArgs e) { LockEvent(()=>this.onUpdateDuration(false)); } private void txtTimeNew_ValueObjectChanged(object sender, EventArgs e) { LockEvent(() => this.onUpdateDuration(true)); } private void txtBound_ValueObjectChanged(object sender, EventArgs e) { LockEvent(() => this.onUpdateSizeAndScale(false, isBoundChanging: true)); } private void txtBoundNew_ValueObjectChanged(object sender, EventArgs e) { LockEvent(() => this.onUpdateSizeAndScale(true, isBoundChanging: true)); } private void txtSize_ValueObjectChanged(object sender, EventArgs e) { LockEvent(() => this.onUpdateSizeAndScale(false, isOutSizeChanging: true)); } private void txtSizeNew_ValueObjectChanged(object sender, EventArgs e) { LockEvent(() => this.onUpdateSizeAndScale(true, isOutSizeChanging: true)); } private void txtScaleNew_ValueObjectChanged(object sender, EventArgs e) { LockEvent(() => this.onUpdateSizeAndScale(true, isScaleChanging: true)); } private void onUpdateDuration(bool isNew) { var controls = GetInputGroup(isNew); var lbl = isNew ? this.lblDurationNew : this.lblDuration; int? duration = (controls[1].ValueObject as int?) - (controls[0].ValueObject as int?); lbl.Text = $"{duration?.ToString() ?? "-"} ms"; } private void onUpdateSizeAndScale(bool isNew, bool isBoundChanging = false, bool isOutSizeChanging = false, bool isScaleChanging = false) { var controls = GetInputGroup(isNew); int? width = (controls[4].ValueObject as int?) - (controls[2].ValueObject as int?); int? height = (controls[5].ValueObject as int?) - (controls[3].ValueObject as int?); int? outWidth = (controls[6].ValueObject as int?); int? outHeight = (controls[7].ValueObject as int?); var lblSize = isNew ? this.lblClipSizeNew : this.lblClipSize; var txtScale = isNew ? this.txtScaleNew : this.txtScale; if (isBoundChanging) { lblSize.Text = $"{width?.ToString() ?? "?"}x{height?.ToString() ?? "?"}"; isOutSizeChanging = true; } if (isOutSizeChanging) { if (new[] { width, height, outWidth, outHeight }.All(v => v != null)) { var scaleX = 1.0 * outWidth / width; var scaleY = 1.0 * outHeight / height; txtScale.ValueObject = (int)Math.Round(Math.Min(scaleX.Value, scaleY.Value) * 100); } else { txtScale.ValueObject = null; } } if (isScaleChanging) { if (new[] { width, height, txtScale.ValueObject }.All(v => v != null)) { controls[6].ValueObject = (int)Math.Round(0.01 * width.Value * (int)txtScale.ValueObject); controls[7].ValueObject = (int)Math.Round(0.01 * height.Value * (int)txtScale.ValueObject); } } } } } ================================================ FILE: WzComparerR2/FrmGifClipOptions.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ================================================ FILE: WzComparerR2/FrmGifMaker.Designer.cs ================================================ namespace WzComparerR2 { partial class FrmGifMaker { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { this.SuspendLayout(); // // FrmGifMaker // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(284, 261); this.DoubleBuffered = true; this.Name = "FrmGifMaker"; this.Text = "FrmGifMaker"; this.ResumeLayout(false); } #endregion } } ================================================ FILE: WzComparerR2/FrmGifMaker.cs ================================================ using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; using DevComponents.DotNetBar; using DevComponents.AdvTree; namespace WzComparerR2 { public partial class FrmGifMaker : DevComponents.DotNetBar.Office2007Form { public FrmGifMaker() { InitializeComponent(); } private class GifFragment { } private class TimeLine { } private class ImageLayer { } } } ================================================ FILE: WzComparerR2/FrmGifMaker.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ================================================ FILE: WzComparerR2/FrmGifSetting.Designer.cs ================================================ namespace WzComparerR2 { partial class FrmGifSetting { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FrmGifSetting)); this.colorPickerButton1 = new DevComponents.DotNetBar.ColorPickerButton(); this.labelX1 = new DevComponents.DotNetBar.LabelX(); this.checkBoxX1 = new DevComponents.DotNetBar.Controls.CheckBoxX(); this.labelX2 = new DevComponents.DotNetBar.LabelX(); this.comboBoxEx1 = new DevComponents.DotNetBar.Controls.ComboBoxEx(); this.comboItem1 = new DevComponents.Editors.ComboItem(); this.comboItem2 = new DevComponents.Editors.ComboItem(); this.comboItem6 = new DevComponents.Editors.ComboItem(); this.comboItem7 = new DevComponents.Editors.ComboItem(); this.labelX3 = new DevComponents.DotNetBar.LabelX(); this.slider1 = new DevComponents.DotNetBar.Controls.Slider(); this.labelX4 = new DevComponents.DotNetBar.LabelX(); this.rdoMosaic = new DevComponents.DotNetBar.Controls.CheckBoxX(); this.panelExMosaic = new DevComponents.DotNetBar.PanelEx(); this.labelX7 = new DevComponents.DotNetBar.LabelX(); this.labelX6 = new DevComponents.DotNetBar.LabelX(); this.labelX5 = new DevComponents.DotNetBar.LabelX(); this.slider2 = new DevComponents.DotNetBar.Controls.Slider(); this.colorPickerButton3 = new DevComponents.DotNetBar.ColorPickerButton(); this.colorPickerButton2 = new DevComponents.DotNetBar.ColorPickerButton(); this.panelExColor = new DevComponents.DotNetBar.PanelEx(); this.rdoColor = new DevComponents.DotNetBar.Controls.CheckBoxX(); this.superTooltip1 = new DevComponents.DotNetBar.SuperTooltip(); this.checkBoxX2 = new DevComponents.DotNetBar.Controls.CheckBoxX(); this.labelX8 = new DevComponents.DotNetBar.LabelX(); this.integerInput1 = new DevComponents.Editors.IntegerInput(); this.labelX9 = new DevComponents.DotNetBar.LabelX(); this.comboBoxEx2 = new DevComponents.DotNetBar.Controls.ComboBoxEx(); this.comboItem3 = new DevComponents.Editors.ComboItem(); this.comboItem4 = new DevComponents.Editors.ComboItem(); this.comboItem5 = new DevComponents.Editors.ComboItem(); this.superTabControl1 = new DevComponents.DotNetBar.SuperTabControl(); this.superTabControlPanel1 = new DevComponents.DotNetBar.SuperTabControlPanel(); this.superTabItem1 = new DevComponents.DotNetBar.SuperTabItem(); this.superTabControlPanel3 = new DevComponents.DotNetBar.SuperTabControlPanel(); this.buttonX3 = new DevComponents.DotNetBar.ButtonX(); this.textBoxX2 = new DevComponents.DotNetBar.Controls.TextBoxX(); this.labelX11 = new DevComponents.DotNetBar.LabelX(); this.textBoxX1 = new DevComponents.DotNetBar.Controls.TextBoxX(); this.labelX10 = new DevComponents.DotNetBar.LabelX(); this.superTabItem3 = new DevComponents.DotNetBar.SuperTabItem(); this.superTabControlPanel2 = new DevComponents.DotNetBar.SuperTabControlPanel(); this.checkBoxX3 = new DevComponents.DotNetBar.Controls.CheckBoxX(); this.superTabItem2 = new DevComponents.DotNetBar.SuperTabItem(); this.panelEx1 = new DevComponents.DotNetBar.PanelEx(); this.buttonX2 = new DevComponents.DotNetBar.ButtonX(); this.buttonX1 = new DevComponents.DotNetBar.ButtonX(); this.labelX12 = new DevComponents.DotNetBar.LabelX(); this.textBoxX3 = new DevComponents.DotNetBar.Controls.TextBoxX(); this.labelX13 = new DevComponents.DotNetBar.LabelX(); this.btnPreset = new DevComponents.DotNetBar.ButtonX(); this.btnNonTransparentMP4Preset = new DevComponents.DotNetBar.ButtonItem(); this.btnGreenBackdropMP4Preset = new DevComponents.DotNetBar.ButtonItem(); this.btnBlueBackdropMP4Preset = new DevComponents.DotNetBar.ButtonItem(); this.btnTransparentMOVPreset = new DevComponents.DotNetBar.ButtonItem(); this.btnTransparentWebMPreset = new DevComponents.DotNetBar.ButtonItem(); this.btnDefaultPreset = new DevComponents.DotNetBar.ButtonItem(); this.panelExMosaic.SuspendLayout(); this.panelExColor.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.integerInput1)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.superTabControl1)).BeginInit(); this.superTabControl1.SuspendLayout(); this.superTabControlPanel1.SuspendLayout(); this.superTabControlPanel3.SuspendLayout(); this.superTabControlPanel2.SuspendLayout(); this.panelEx1.SuspendLayout(); this.SuspendLayout(); // // colorPickerButton1 // this.colorPickerButton1.AccessibleRole = System.Windows.Forms.AccessibleRole.PushButton; this.colorPickerButton1.AutoExpandOnClick = true; this.colorPickerButton1.ColorTable = DevComponents.DotNetBar.eButtonColor.OrangeWithBackground; this.colorPickerButton1.Image = ((System.Drawing.Image)(resources.GetObject("colorPickerButton1.Image"))); this.colorPickerButton1.Location = new System.Drawing.Point(116, 6); this.colorPickerButton1.Name = "colorPickerButton1"; this.colorPickerButton1.SelectedColorImageRectangle = new System.Drawing.Rectangle(2, 2, 12, 12); this.colorPickerButton1.Size = new System.Drawing.Size(37, 23); this.colorPickerButton1.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.colorPickerButton1.TabIndex = 1; // // labelX1 // this.labelX1.AutoSize = true; // // // this.labelX1.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.labelX1.Location = new System.Drawing.Point(11, 13); this.labelX1.Name = "labelX1"; this.labelX1.Size = new System.Drawing.Size(99, 16); this.labelX1.TabIndex = 0; this.labelX1.Text = "BackGroundColor"; // // btnPreset // this.btnPreset.Location = new System.Drawing.Point(160, 6); this.btnPreset.ColorTable = DevComponents.DotNetBar.eButtonColor.OrangeWithBackground; this.btnPreset.Size = new System.Drawing.Size(56, 23); this.btnPreset.AutoExpandOnClick = true; this.btnPreset.Name = "btnPreset"; this.btnPreset.TabIndex = 4; this.btnPreset.Text = "Preset"; this.btnPreset.SubItems.AddRange(new DevComponents.DotNetBar.BaseItem[] { this.btnNonTransparentMP4Preset, this.btnGreenBackdropMP4Preset, this.btnBlueBackdropMP4Preset, this.btnTransparentMOVPreset, this.btnTransparentWebMPreset, this.btnDefaultPreset}); // // btnNonTransparentMP4Preset // this.btnNonTransparentMP4Preset.Name = "btnNonTransparentMP4Preset"; this.btnNonTransparentMP4Preset.Text = "NonTransparentMP4"; this.btnNonTransparentMP4Preset.Click += new System.EventHandler(this.btnNonTransparentMP4Preset_Click); // // btnGreenBackdropMP4Preset // this.btnGreenBackdropMP4Preset.Name = "btnGreenBackdropMP4Preset"; this.btnGreenBackdropMP4Preset.Text = "GreenBackdropMP4"; this.btnGreenBackdropMP4Preset.Click += new System.EventHandler(this.btnGreenBackdropMP4Preset_Click); // // btnBlueBackdropMP4Preset // this.btnBlueBackdropMP4Preset.Name = "btnBlueBackdropMP4Preset"; this.btnBlueBackdropMP4Preset.Text = "BlueBackdropMP4"; this.btnBlueBackdropMP4Preset.Click += new System.EventHandler(this.btnBlueBackdropMP4Preset_Click); // // btnTransparentMOVPreset // this.btnTransparentMOVPreset.Name = "btnTransparentMOVPreset"; this.btnTransparentMOVPreset.Text = "TransparentMOV"; this.btnTransparentMOVPreset.Click += new System.EventHandler(this.btnTransparentMOVPreset_Click); // // btnTransparentWebMPreset // this.btnTransparentWebMPreset.Name = "btnTransparentWebMPreset"; this.btnTransparentWebMPreset.Text = "TransparentWebM"; this.btnTransparentWebMPreset.Click += new System.EventHandler(this.btnTransparentWebMPreset_Click); // // btnDefaultPreset // this.btnDefaultPreset.Name = "btnDefaultPreset"; this.btnDefaultPreset.Text = "Default"; this.btnDefaultPreset.Click += new System.EventHandler(this.btnDefaultPreset_Click); // // checkBoxX1 // this.checkBoxX1.AutoSize = true; // // // this.checkBoxX1.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.checkBoxX1.Location = new System.Drawing.Point(11, 36); this.checkBoxX1.Name = "checkBoxX1"; this.checkBoxX1.Size = new System.Drawing.Size(163, 16); this.checkBoxX1.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.checkBoxX1.TabIndex = 2; this.checkBoxX1.Text = "Background &Transparent"; // // labelX2 // this.labelX2.AutoSize = true; this.labelX2.BackColor = System.Drawing.Color.Transparent; // // // this.labelX2.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.labelX2.Location = new System.Drawing.Point(16, 207); this.labelX2.Name = "labelX2"; this.labelX2.Size = new System.Drawing.Size(56, 16); this.labelX2.TabIndex = 5; this.labelX2.Text = "FileName"; // // comboBoxEx1 // this.comboBoxEx1.DisplayMember = "Text"; this.comboBoxEx1.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawFixed; this.comboBoxEx1.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.comboBoxEx1.FormattingEnabled = true; this.comboBoxEx1.ItemHeight = 15; this.comboBoxEx1.Items.AddRange(new object[] { this.comboItem1, this.comboItem2, this.comboItem6, this.comboItem7}); this.comboBoxEx1.Location = new System.Drawing.Point(80, 178); this.comboBoxEx1.Name = "comboBoxEx1"; this.comboBoxEx1.Size = new System.Drawing.Size(129, 21); this.comboBoxEx1.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.comboBoxEx1.TabIndex = 6; // // comboItem1 // this.comboItem1.Text = "BuildIn"; // // comboItem2 // this.comboItem2.Text = "Index Gif Encoder"; // // comboItem6 // this.comboItem6.Text = "Apng Encoder"; // // comboItem7 // this.comboItem7.Text = "FFmpeg Encoder"; // // labelX3 // this.labelX3.AutoSize = true; // // // this.labelX3.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.labelX3.Location = new System.Drawing.Point(11, 61); this.labelX3.Name = "labelX3"; this.labelX3.Size = new System.Drawing.Size(87, 16); this.labelX3.TabIndex = 3; this.labelX3.Text = "MinAlphaMixed"; // // slider1 // // // // this.slider1.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.slider1.LabelPosition = DevComponents.DotNetBar.eSliderLabelPosition.Right; this.slider1.LabelWidth = 25; this.slider1.Location = new System.Drawing.Point(112, 58); this.slider1.Maximum = 254; this.slider1.Name = "slider1"; this.slider1.Size = new System.Drawing.Size(136, 23); this.slider1.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.slider1.TabIndex = 4; this.slider1.Text = "0"; this.slider1.Value = 0; this.slider1.ValueChanged += new System.EventHandler(this.slider1_ValueChanged); // // labelX4 // this.labelX4.AutoSize = true; this.labelX4.BackColor = System.Drawing.Color.Transparent; // // // this.labelX4.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.labelX4.Location = new System.Drawing.Point(16, 20); this.labelX4.Name = "labelX4"; this.labelX4.Size = new System.Drawing.Size(68, 16); this.labelX4.TabIndex = 0; this.labelX4.Text = "BackGround"; // // rdoMosaic // this.rdoMosaic.AutoSize = true; this.rdoMosaic.BackColor = System.Drawing.Color.Transparent; // // // this.rdoMosaic.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.rdoMosaic.CheckBoxStyle = DevComponents.DotNetBar.eCheckBoxStyle.RadioButton; this.rdoMosaic.Location = new System.Drawing.Point(16, 130); this.rdoMosaic.Name = "rdoMosaic"; this.rdoMosaic.Size = new System.Drawing.Size(64, 16); this.rdoMosaic.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.superTooltip1.SetSuperTooltip(this.rdoMosaic, new DevComponents.DotNetBar.SuperTooltipInfo("", "", "使用马赛克晶格作为背景生成Gif动画", null, null, DevComponents.DotNetBar.eTooltipColor.System)); this.rdoMosaic.TabIndex = 3; this.rdoMosaic.Text = "Mosaic"; this.rdoMosaic.CheckedChanged += new System.EventHandler(this.rdoMosaic_CheckedChanged); // // panelExMosaic // this.panelExMosaic.CanvasColor = System.Drawing.SystemColors.Control; this.panelExMosaic.ColorSchemeStyle = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.panelExMosaic.Controls.Add(this.labelX7); this.panelExMosaic.Controls.Add(this.labelX6); this.panelExMosaic.Controls.Add(this.labelX5); this.panelExMosaic.Controls.Add(this.slider2); this.panelExMosaic.Controls.Add(this.colorPickerButton3); this.panelExMosaic.Controls.Add(this.colorPickerButton2); this.panelExMosaic.DisabledBackColor = System.Drawing.Color.Empty; this.panelExMosaic.Enabled = false; this.panelExMosaic.Location = new System.Drawing.Point(80, 105); this.panelExMosaic.Name = "panelExMosaic"; this.panelExMosaic.Size = new System.Drawing.Size(256, 66); this.panelExMosaic.Style.Alignment = System.Drawing.StringAlignment.Center; this.panelExMosaic.Style.BackColor1.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.PanelBackground; this.panelExMosaic.Style.BackColor2.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.PanelBackground2; this.panelExMosaic.Style.Border = DevComponents.DotNetBar.eBorderType.SingleLine; this.panelExMosaic.Style.BorderColor.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.PanelBorder; this.panelExMosaic.Style.ForeColor.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.PanelText; this.panelExMosaic.Style.GradientAngle = 90; this.panelExMosaic.TabIndex = 4; // // labelX7 // this.labelX7.AutoSize = true; // // // this.labelX7.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.labelX7.Location = new System.Drawing.Point(11, 42); this.labelX7.Name = "labelX7"; this.labelX7.Size = new System.Drawing.Size(62, 16); this.labelX7.TabIndex = 4; this.labelX7.Text = "BlockSize"; // // labelX6 // this.labelX6.AutoSize = true; // // // this.labelX6.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.labelX6.Location = new System.Drawing.Point(104, 14); this.labelX6.Name = "labelX6"; this.labelX6.Size = new System.Drawing.Size(44, 16); this.labelX6.TabIndex = 2; this.labelX6.Text = "Color1"; // // labelX5 // this.labelX5.AutoSize = true; // // // this.labelX5.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.labelX5.Location = new System.Drawing.Point(11, 14); this.labelX5.Name = "labelX5"; this.labelX5.Size = new System.Drawing.Size(44, 16); this.labelX5.TabIndex = 0; this.labelX5.Text = "Color0"; // // slider2 // // // // this.slider2.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.slider2.LabelPosition = DevComponents.DotNetBar.eSliderLabelPosition.Right; this.slider2.LabelWidth = 25; this.slider2.Location = new System.Drawing.Point(79, 35); this.slider2.Maximum = 256; this.slider2.Minimum = 1; this.slider2.Name = "slider2"; this.slider2.Size = new System.Drawing.Size(136, 23); this.slider2.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.slider2.TabIndex = 5; this.slider2.Text = "1"; this.slider2.Value = 1; this.slider2.ValueChanged += new System.EventHandler(this.slider1_ValueChanged); // // colorPickerButton3 // this.colorPickerButton3.AccessibleRole = System.Windows.Forms.AccessibleRole.PushButton; this.colorPickerButton3.AutoExpandOnClick = true; this.colorPickerButton3.ColorTable = DevComponents.DotNetBar.eButtonColor.OrangeWithBackground; this.colorPickerButton3.Image = ((System.Drawing.Image)(resources.GetObject("colorPickerButton3.Image"))); this.colorPickerButton3.Location = new System.Drawing.Point(154, 7); this.colorPickerButton3.Name = "colorPickerButton3"; this.colorPickerButton3.SelectedColorImageRectangle = new System.Drawing.Rectangle(2, 2, 12, 12); this.colorPickerButton3.Size = new System.Drawing.Size(37, 23); this.colorPickerButton3.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.colorPickerButton3.TabIndex = 3; // // colorPickerButton2 // this.colorPickerButton2.AccessibleRole = System.Windows.Forms.AccessibleRole.PushButton; this.colorPickerButton2.AutoExpandOnClick = true; this.colorPickerButton2.ColorTable = DevComponents.DotNetBar.eButtonColor.OrangeWithBackground; this.colorPickerButton2.Image = ((System.Drawing.Image)(resources.GetObject("colorPickerButton2.Image"))); this.colorPickerButton2.Location = new System.Drawing.Point(61, 7); this.colorPickerButton2.Name = "colorPickerButton2"; this.colorPickerButton2.SelectedColorImageRectangle = new System.Drawing.Rectangle(2, 2, 12, 12); this.colorPickerButton2.Size = new System.Drawing.Size(37, 23); this.colorPickerButton2.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.colorPickerButton2.TabIndex = 1; // // panelExColor // this.panelExColor.CanvasColor = System.Drawing.SystemColors.Control; this.panelExColor.ColorSchemeStyle = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.panelExColor.Controls.Add(this.labelX1); this.panelExColor.Controls.Add(this.colorPickerButton1); this.panelExColor.Controls.Add(this.checkBoxX1); this.panelExColor.Controls.Add(this.labelX3); this.panelExColor.Controls.Add(this.slider1); this.panelExColor.Controls.Add(this.btnPreset); this.panelExColor.DisabledBackColor = System.Drawing.Color.Empty; this.panelExColor.Enabled = false; this.panelExColor.Location = new System.Drawing.Point(80, 14); this.panelExColor.Name = "panelExColor"; this.panelExColor.Size = new System.Drawing.Size(256, 86); this.panelExColor.Style.Alignment = System.Drawing.StringAlignment.Center; this.panelExColor.Style.BackColor1.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.PanelBackground; this.panelExColor.Style.BackColor2.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.PanelBackground2; this.panelExColor.Style.Border = DevComponents.DotNetBar.eBorderType.SingleLine; this.panelExColor.Style.BorderColor.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.PanelBorder; this.panelExColor.Style.ForeColor.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.PanelText; this.panelExColor.Style.GradientAngle = 90; this.panelExColor.TabIndex = 2; // // rdoColor // this.rdoColor.AutoSize = true; this.rdoColor.BackColor = System.Drawing.Color.Transparent; // // // this.rdoColor.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.rdoColor.CheckBoxStyle = DevComponents.DotNetBar.eCheckBoxStyle.RadioButton; this.rdoColor.Location = new System.Drawing.Point(16, 59); this.rdoColor.Name = "rdoColor"; this.rdoColor.Size = new System.Drawing.Size(57, 16); this.rdoColor.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.superTooltip1.SetSuperTooltip(this.rdoColor, new DevComponents.DotNetBar.SuperTooltipInfo("", "", "使用透明或纯色背景生成Gif动画。", null, null, DevComponents.DotNetBar.eTooltipColor.System)); this.rdoColor.TabIndex = 1; this.rdoColor.Text = "Color"; this.rdoColor.CheckedChanged += new System.EventHandler(this.rdoColor_CheckedChanged); // // superTooltip1 // this.superTooltip1.DefaultTooltipSettings = new DevComponents.DotNetBar.SuperTooltipInfo("", "", "", null, null, DevComponents.DotNetBar.eTooltipColor.Gray); // // checkBoxX2 // this.checkBoxX2.AutoSize = true; this.checkBoxX2.BackColor = System.Drawing.Color.Transparent; // // // this.checkBoxX2.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.checkBoxX2.Location = new System.Drawing.Point(16, 231); this.checkBoxX2.Name = "checkBoxX2"; this.checkBoxX2.Size = new System.Drawing.Size(144, 16); this.checkBoxX2.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.checkBoxX2.TabIndex = 8; this.checkBoxX2.Text = "Save Frames As .png"; // // labelX8 // this.labelX8.AutoSize = true; this.labelX8.BackColor = System.Drawing.Color.Transparent; // // // this.labelX8.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.labelX8.Location = new System.Drawing.Point(16, 256); this.labelX8.Name = "labelX8"; this.labelX8.Size = new System.Drawing.Size(37, 16); this.labelX8.TabIndex = 8; this.labelX8.Text = "Delay"; // // integerInput1 // // // // this.integerInput1.BackgroundStyle.Class = "DateTimeInputBackground"; this.integerInput1.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.integerInput1.ButtonFreeText.Shortcut = DevComponents.DotNetBar.eShortcut.F2; this.integerInput1.Increment = 10; this.integerInput1.Location = new System.Drawing.Point(80, 253); this.integerInput1.MaxValue = 1000; this.integerInput1.MinValue = 10; this.integerInput1.Name = "integerInput1"; this.integerInput1.ShowUpDown = true; this.integerInput1.Size = new System.Drawing.Size(80, 21); this.integerInput1.TabIndex = 9; this.integerInput1.Value = 10; // // labelX9 // this.labelX9.AutoSize = true; this.labelX9.BackColor = System.Drawing.Color.Transparent; // // // this.labelX9.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.labelX9.Location = new System.Drawing.Point(16, 182); this.labelX9.Name = "labelX9"; this.labelX9.Size = new System.Drawing.Size(50, 16); this.labelX9.TabIndex = 16; this.labelX9.Text = "Encoder"; // // comboBoxEx2 // this.comboBoxEx2.DisplayMember = "Text"; this.comboBoxEx2.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawFixed; this.comboBoxEx2.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.comboBoxEx2.FormattingEnabled = true; this.comboBoxEx2.ItemHeight = 15; this.comboBoxEx2.Items.AddRange(new object[] { this.comboItem3, this.comboItem4, this.comboItem5}); this.comboBoxEx2.Location = new System.Drawing.Point(80, 205); this.comboBoxEx2.Name = "comboBoxEx2"; this.comboBoxEx2.Size = new System.Drawing.Size(129, 21); this.comboBoxEx2.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.comboBoxEx2.TabIndex = 7; // // comboItem3 // this.comboItem3.Text = "Default"; // // comboItem4 // this.comboItem4.Text = "PathToImage"; // // comboItem5 // this.comboItem5.Text = "PathToWz"; // // superTabControl1 // // // // // // // this.superTabControl1.ControlBox.CloseBox.Name = ""; // // // this.superTabControl1.ControlBox.MenuBox.Name = ""; this.superTabControl1.ControlBox.Name = ""; this.superTabControl1.ControlBox.SubItems.AddRange(new DevComponents.DotNetBar.BaseItem[] { this.superTabControl1.ControlBox.MenuBox, this.superTabControl1.ControlBox.CloseBox}); this.superTabControl1.Controls.Add(this.superTabControlPanel3); this.superTabControl1.Controls.Add(this.superTabControlPanel1); this.superTabControl1.Controls.Add(this.superTabControlPanel2); this.superTabControl1.Dock = System.Windows.Forms.DockStyle.Fill; this.superTabControl1.Location = new System.Drawing.Point(0, 0); this.superTabControl1.Name = "superTabControl1"; this.superTabControl1.ReorderTabsEnabled = true; this.superTabControl1.SelectedTabFont = new System.Drawing.Font("宋体", 9F, System.Drawing.FontStyle.Bold); this.superTabControl1.SelectedTabIndex = 0; this.superTabControl1.Size = new System.Drawing.Size(454, 311); this.superTabControl1.TabAlignment = DevComponents.DotNetBar.eTabStripAlignment.Left; this.superTabControl1.TabFont = new System.Drawing.Font("宋体", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); this.superTabControl1.TabIndex = 30; this.superTabControl1.Tabs.AddRange(new DevComponents.DotNetBar.BaseItem[] { this.superTabItem1, this.superTabItem2, this.superTabItem3}); this.superTabControl1.Text = "superTabControl1"; // // superTabControlPanel1 // this.superTabControlPanel1.Controls.Add(this.comboBoxEx2); this.superTabControlPanel1.Controls.Add(this.labelX9); this.superTabControlPanel1.Controls.Add(this.labelX2); this.superTabControlPanel1.Controls.Add(this.integerInput1); this.superTabControlPanel1.Controls.Add(this.comboBoxEx1); this.superTabControlPanel1.Controls.Add(this.labelX8); this.superTabControlPanel1.Controls.Add(this.rdoMosaic); this.superTabControlPanel1.Controls.Add(this.checkBoxX2); this.superTabControlPanel1.Controls.Add(this.panelExColor); this.superTabControlPanel1.Controls.Add(this.rdoColor); this.superTabControlPanel1.Controls.Add(this.panelExMosaic); this.superTabControlPanel1.Controls.Add(this.labelX4); this.superTabControlPanel1.Dock = System.Windows.Forms.DockStyle.Fill; this.superTabControlPanel1.Location = new System.Drawing.Point(105, 0); this.superTabControlPanel1.Name = "superTabControlPanel1"; this.superTabControlPanel1.Size = new System.Drawing.Size(349, 311); this.superTabControlPanel1.TabIndex = 1; this.superTabControlPanel1.TabItem = this.superTabItem1; // // superTabItem1 // this.superTabItem1.AttachedControl = this.superTabControlPanel1; this.superTabItem1.GlobalItem = false; this.superTabItem1.Name = "superTabItem1"; this.superTabItem1.Text = "General"; // // superTabControlPanel3 // this.superTabControlPanel3.Controls.Add(this.labelX13); this.superTabControlPanel3.Controls.Add(this.textBoxX3); this.superTabControlPanel3.Controls.Add(this.labelX12); this.superTabControlPanel3.Controls.Add(this.buttonX3); this.superTabControlPanel3.Controls.Add(this.textBoxX2); this.superTabControlPanel3.Controls.Add(this.labelX11); this.superTabControlPanel3.Controls.Add(this.textBoxX1); this.superTabControlPanel3.Controls.Add(this.labelX10); this.superTabControlPanel3.Dock = System.Windows.Forms.DockStyle.Fill; this.superTabControlPanel3.Location = new System.Drawing.Point(108, 0); this.superTabControlPanel3.Name = "superTabControlPanel3"; this.superTabControlPanel3.Size = new System.Drawing.Size(346, 311); this.superTabControlPanel3.TabIndex = 0; this.superTabControlPanel3.TabItem = this.superTabItem3; // // buttonX3 // this.buttonX3.AccessibleRole = System.Windows.Forms.AccessibleRole.PushButton; this.buttonX3.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.buttonX3.ColorTable = DevComponents.DotNetBar.eButtonColor.OrangeWithBackground; this.buttonX3.Location = new System.Drawing.Point(315, 14); this.buttonX3.Name = "buttonX3"; this.buttonX3.Size = new System.Drawing.Size(24, 24); this.buttonX3.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.buttonX3.Symbol = ""; this.buttonX3.SymbolSize = 12F; this.buttonX3.TabIndex = 15; this.buttonX3.Click += new System.EventHandler(this.buttonX3_Click); // // textBoxX2 // this.textBoxX2.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); // // // this.textBoxX2.Border.Class = "TextBoxBorder"; this.textBoxX2.Border.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.textBoxX2.Location = new System.Drawing.Point(109, 46); this.textBoxX2.Multiline = true; this.textBoxX2.Name = "textBoxX2"; this.textBoxX2.PreventEnterBeep = true; this.textBoxX2.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; this.textBoxX2.Size = new System.Drawing.Size(201, 124); this.textBoxX2.TabIndex = 14; // // labelX11 // this.labelX11.AutoSize = true; this.labelX11.BackColor = System.Drawing.Color.Transparent; // // // this.labelX11.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.labelX11.Location = new System.Drawing.Point(16, 50); this.labelX11.Name = "labelX11"; this.labelX11.Size = new System.Drawing.Size(93, 16); this.labelX11.TabIndex = 13; this.labelX11.Text = "FFmpegArgument"; // // textBoxX1 // this.textBoxX1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); // // // this.textBoxX1.Border.Class = "TextBoxBorder"; this.textBoxX1.Border.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.textBoxX1.Location = new System.Drawing.Point(109, 16); this.textBoxX1.Name = "textBoxX1"; this.textBoxX1.PreventEnterBeep = true; this.textBoxX1.Size = new System.Drawing.Size(201, 21); this.textBoxX1.TabIndex = 12; // // labelX10 // this.labelX10.AutoSize = true; this.labelX10.BackColor = System.Drawing.Color.Transparent; // // // this.labelX10.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.labelX10.Location = new System.Drawing.Point(16, 20); this.labelX10.Name = "labelX10"; this.labelX10.Size = new System.Drawing.Size(87, 16); this.labelX10.TabIndex = 10; this.labelX10.Text = "FFmpegBinPath"; // // superTabItem3 // this.superTabItem3.AttachedControl = this.superTabControlPanel3; this.superTabItem3.GlobalItem = false; this.superTabItem3.Name = "superTabItem3"; this.superTabItem3.Text = "FFmpegEncoder"; // // superTabControlPanel2 // this.superTabControlPanel2.Controls.Add(this.checkBoxX3); this.superTabControlPanel2.Dock = System.Windows.Forms.DockStyle.Fill; this.superTabControlPanel2.Location = new System.Drawing.Point(105, 0); this.superTabControlPanel2.Name = "superTabControlPanel2"; this.superTabControlPanel2.Size = new System.Drawing.Size(349, 291); this.superTabControlPanel2.TabIndex = 0; this.superTabControlPanel2.TabItem = this.superTabItem2; // // checkBoxX3 // this.checkBoxX3.AutoSize = true; this.checkBoxX3.BackColor = System.Drawing.Color.Transparent; // // // this.checkBoxX3.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.checkBoxX3.Location = new System.Drawing.Point(16, 20); this.checkBoxX3.Name = "checkBoxX3"; this.checkBoxX3.Size = new System.Drawing.Size(255, 16); this.checkBoxX3.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.checkBoxX3.TabIndex = 24; this.checkBoxX3.Text = "Optimize File Size (8bit palette png)"; // // superTabItem2 // this.superTabItem2.AttachedControl = this.superTabControlPanel2; this.superTabItem2.GlobalItem = false; this.superTabItem2.Name = "superTabItem2"; this.superTabItem2.Text = "APngEncoder"; // // panelEx1 // this.panelEx1.CanvasColor = System.Drawing.SystemColors.Control; this.panelEx1.ColorSchemeStyle = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.panelEx1.Controls.Add(this.buttonX2); this.panelEx1.Controls.Add(this.buttonX1); this.panelEx1.DisabledBackColor = System.Drawing.Color.Empty; this.panelEx1.Dock = System.Windows.Forms.DockStyle.Bottom; this.panelEx1.Location = new System.Drawing.Point(0, 311); this.panelEx1.Name = "panelEx1"; this.panelEx1.Size = new System.Drawing.Size(454, 30); this.panelEx1.Style.Alignment = System.Drawing.StringAlignment.Center; this.panelEx1.Style.BackColor1.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.PanelBackground; this.panelEx1.Style.BackColor2.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.PanelBackground2; this.panelEx1.Style.Border = DevComponents.DotNetBar.eBorderType.SingleLine; this.panelEx1.Style.BorderColor.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.PanelBorder; this.panelEx1.Style.ForeColor.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.PanelText; this.panelEx1.Style.GradientAngle = 90; this.panelEx1.TabIndex = 38; // // buttonX2 // this.buttonX2.AccessibleRole = System.Windows.Forms.AccessibleRole.PushButton; this.buttonX2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.buttonX2.ColorTable = DevComponents.DotNetBar.eButtonColor.OrangeWithBackground; this.buttonX2.DialogResult = System.Windows.Forms.DialogResult.Cancel; this.buttonX2.Location = new System.Drawing.Point(385, 4); this.buttonX2.Name = "buttonX2"; this.buttonX2.Size = new System.Drawing.Size(60, 23); this.buttonX2.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.buttonX2.TabIndex = 1; this.buttonX2.Text = "取消"; // // buttonX1 // this.buttonX1.AccessibleRole = System.Windows.Forms.AccessibleRole.PushButton; this.buttonX1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.buttonX1.ColorTable = DevComponents.DotNetBar.eButtonColor.OrangeWithBackground; this.buttonX1.DialogResult = System.Windows.Forms.DialogResult.OK; this.buttonX1.Location = new System.Drawing.Point(318, 4); this.buttonX1.Name = "buttonX1"; this.buttonX1.Size = new System.Drawing.Size(60, 23); this.buttonX1.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.buttonX1.TabIndex = 0; this.buttonX1.Text = "确定"; // // labelX12 // this.labelX12.AutoSize = true; this.labelX12.BackColor = System.Drawing.Color.Transparent; // // // this.labelX12.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.labelX12.Location = new System.Drawing.Point(16, 184); this.labelX12.Name = "labelX12"; this.labelX12.Size = new System.Drawing.Size(87, 16); this.labelX12.TabIndex = 16; this.labelX12.Text = "OutputFileExt"; // // textBoxX3 // this.textBoxX3.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); // // // this.textBoxX3.Border.Class = "TextBoxBorder"; this.textBoxX3.Border.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.textBoxX3.Location = new System.Drawing.Point(109, 180); this.textBoxX3.Name = "textBoxX3"; this.textBoxX3.PreventEnterBeep = true; this.textBoxX3.Size = new System.Drawing.Size(75, 21); this.textBoxX3.TabIndex = 17; // // labelX13 // this.labelX13.AutoSize = true; this.labelX13.BackColor = System.Drawing.Color.Transparent; // // // this.labelX13.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.labelX13.ForeColor = System.Drawing.SystemColors.ControlDarkDark; this.labelX13.Location = new System.Drawing.Point(16, 207); this.labelX13.Name = "labelX13"; this.labelX13.Size = new System.Drawing.Size(126, 86); this.labelX13.TabIndex = 18; this.labelX13.Text = "参数通配符:
\r\n  %i 输入文件名
\r\n  %o 输出文件名
\r\n  %w 输入图像宽度(px)
\r\n  %h 输出图像高度(px)
\r\n  %t 帧间隔(ms)"; // // FrmGifSetting // this.CancelButton = this.buttonX2; this.ClientSize = new System.Drawing.Size(454, 341); this.Controls.Add(this.superTabControl1); this.Controls.Add(this.panelEx1); this.DoubleBuffered = true; this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; this.MaximizeBox = false; this.MinimizeBox = false; this.Name = "FrmGifSetting"; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; this.Text = "Gif输出设置"; this.panelExMosaic.ResumeLayout(false); this.panelExMosaic.PerformLayout(); this.panelExColor.ResumeLayout(false); this.panelExColor.PerformLayout(); ((System.ComponentModel.ISupportInitialize)(this.integerInput1)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.superTabControl1)).EndInit(); this.superTabControl1.ResumeLayout(false); this.superTabControlPanel1.ResumeLayout(false); this.superTabControlPanel1.PerformLayout(); this.superTabControlPanel3.ResumeLayout(false); this.superTabControlPanel3.PerformLayout(); this.superTabControlPanel2.ResumeLayout(false); this.superTabControlPanel2.PerformLayout(); this.panelEx1.ResumeLayout(false); this.ResumeLayout(false); } #endregion private DevComponents.DotNetBar.ColorPickerButton colorPickerButton1; private DevComponents.DotNetBar.LabelX labelX1; private DevComponents.DotNetBar.Controls.CheckBoxX checkBoxX1; private DevComponents.DotNetBar.LabelX labelX2; private DevComponents.DotNetBar.Controls.ComboBoxEx comboBoxEx1; private DevComponents.Editors.ComboItem comboItem1; private DevComponents.DotNetBar.LabelX labelX3; private DevComponents.DotNetBar.Controls.Slider slider1; private DevComponents.Editors.ComboItem comboItem2; private DevComponents.DotNetBar.LabelX labelX4; private DevComponents.DotNetBar.Controls.CheckBoxX rdoMosaic; private DevComponents.DotNetBar.PanelEx panelExMosaic; private DevComponents.DotNetBar.LabelX labelX7; private DevComponents.DotNetBar.LabelX labelX6; private DevComponents.DotNetBar.LabelX labelX5; private DevComponents.DotNetBar.Controls.Slider slider2; private DevComponents.DotNetBar.ColorPickerButton colorPickerButton3; private DevComponents.DotNetBar.ColorPickerButton colorPickerButton2; private DevComponents.DotNetBar.PanelEx panelExColor; private DevComponents.DotNetBar.Controls.CheckBoxX rdoColor; private DevComponents.DotNetBar.SuperTooltip superTooltip1; private DevComponents.DotNetBar.Controls.CheckBoxX checkBoxX2; private DevComponents.DotNetBar.LabelX labelX8; private DevComponents.Editors.IntegerInput integerInput1; private DevComponents.DotNetBar.LabelX labelX9; private DevComponents.DotNetBar.Controls.ComboBoxEx comboBoxEx2; private DevComponents.Editors.ComboItem comboItem3; private DevComponents.Editors.ComboItem comboItem4; private DevComponents.Editors.ComboItem comboItem5; private DevComponents.Editors.ComboItem comboItem6; private DevComponents.DotNetBar.SuperTabControl superTabControl1; private DevComponents.DotNetBar.SuperTabControlPanel superTabControlPanel1; private DevComponents.DotNetBar.SuperTabItem superTabItem1; private DevComponents.DotNetBar.PanelEx panelEx1; private DevComponents.DotNetBar.ButtonX buttonX2; private DevComponents.DotNetBar.ButtonX buttonX1; private DevComponents.DotNetBar.SuperTabControlPanel superTabControlPanel2; private DevComponents.DotNetBar.SuperTabItem superTabItem2; private DevComponents.DotNetBar.SuperTabControlPanel superTabControlPanel3; private DevComponents.DotNetBar.SuperTabItem superTabItem3; private DevComponents.DotNetBar.Controls.CheckBoxX checkBoxX3; private DevComponents.DotNetBar.LabelX labelX10; private DevComponents.DotNetBar.ButtonX buttonX3; private DevComponents.DotNetBar.Controls.TextBoxX textBoxX2; private DevComponents.DotNetBar.LabelX labelX11; private DevComponents.DotNetBar.Controls.TextBoxX textBoxX1; private DevComponents.Editors.ComboItem comboItem7; private DevComponents.DotNetBar.LabelX labelX12; private DevComponents.DotNetBar.Controls.TextBoxX textBoxX3; private DevComponents.DotNetBar.LabelX labelX13; private DevComponents.DotNetBar.ButtonX btnPreset; private DevComponents.DotNetBar.ButtonItem btnNonTransparentMP4Preset; private DevComponents.DotNetBar.ButtonItem btnGreenBackdropMP4Preset; private DevComponents.DotNetBar.ButtonItem btnBlueBackdropMP4Preset; private DevComponents.DotNetBar.ButtonItem btnTransparentMOVPreset; private DevComponents.DotNetBar.ButtonItem btnTransparentWebMPreset; private DevComponents.DotNetBar.ButtonItem btnDefaultPreset; } } ================================================ FILE: WzComparerR2/FrmGifSetting.cs ================================================ using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Linq; using System.Windows.Forms; using System.Reflection; using DevComponents.DotNetBar; using WzComparerR2.Config; using MathHelper = Microsoft.Xna.Framework.MathHelper; namespace WzComparerR2 { public partial class FrmGifSetting : DevComponents.DotNetBar.Office2007Form { public FrmGifSetting() { InitializeComponent(); #if NET6_0_OR_GREATER // https://learn.microsoft.com/en-us/dotnet/core/compatibility/fx-core#controldefaultfont-changed-to-segoe-ui-9pt this.Font = new Font(new FontFamily("Microsoft Sans Serif"), 8f); #endif initSelection(); } private void initSelection() { comboBoxEx1.SelectedIndex = 0; comboBoxEx2.SelectedIndex = 0; } public bool SavePngFramesEnabled { get { return checkBoxX2.Checked; } set { checkBoxX2.Checked = value; } } public int GifEncoder { get { return comboBoxEx1.SelectedIndex; } set { comboBoxEx1.SelectedIndex = MathHelper.Clamp(value, 0, comboBoxEx1.Items.Count - 1); } } public ImageNameMethod ImageNameMethod { get { return (ImageNameMethod)comboBoxEx2.SelectedIndex; } set { comboBoxEx2.SelectedIndex = MathHelper.Clamp((int)value, 0, comboBoxEx2.Items.Count - 1); } } public ImageBackgroundType BackgroundType { get { if (rdoColor.Checked) { return checkBoxX1.Checked ? ImageBackgroundType.Transparent : ImageBackgroundType.Color; } else if (rdoMosaic.Checked) { return ImageBackgroundType.Mosaic; } else //默认 { return ImageBackgroundType.Transparent; } } set { switch (value) { default: case ImageBackgroundType.Transparent: rdoColor.Checked = true; checkBoxX1.Checked = true; break; case ImageBackgroundType.Color: rdoColor.Checked = true; checkBoxX1.Checked = false; break; case ImageBackgroundType.Mosaic: rdoMosaic.Checked = true; break; } } } public Color BackgroundColor { get { return colorPickerButton1.SelectedColor; } set { colorPickerButton1.SelectedColor = value; } } public int MinMixedAlpha { get { return slider1.Value; } set { slider1.Value = MathHelper.Clamp(value, slider1.Minimum, slider1.Maximum); } } public int MinDelay { get { return integerInput1.Value; } set { integerInput1.Value = MathHelper.Clamp(value, integerInput1.MinValue, integerInput1.MaxValue); } } public Color MosaicColor0 { get { return colorPickerButton2.SelectedColor; } set { colorPickerButton2.SelectedColor = value; } } public Color MosaicColor1 { get { return colorPickerButton3.SelectedColor; } set { colorPickerButton3.SelectedColor = value; } } public int MosaicBlockSize { get { return slider2.Value; } set { slider2.Value = MathHelper.Clamp(value, slider2.Minimum, slider2.Maximum); } } public bool PaletteOptimized { get { return checkBoxX3.Checked; } set { checkBoxX3.Checked = value; } } public string FFmpegBinPath { get { return textBoxX1.Text; } set { textBoxX1.Text = value; } } public string FFmpegArgument { get { return textBoxX2.Text; } set { textBoxX2.Text = value; } } public string FFmpegDefaultExtension { get { return textBoxX3.Text; } set { textBoxX3.Text = value; } } public string FFmpegBinPathHint { set { textBoxX1.WatermarkText = value; } } public string FFmpegArgumentHint { set { textBoxX2.WatermarkText = value; } } public string FFmpegDefaultExtensionHint { set { textBoxX3.WatermarkText = value; } } public void Load(ImageHandlerConfig config) { this.SavePngFramesEnabled = config.SavePngFramesEnabled; this.GifEncoder = config.GifEncoder; this.ImageNameMethod = config.ImageNameMethod; this.BackgroundType = config.BackgroundType; this.BackgroundColor = config.BackgroundColor; this.MinMixedAlpha = config.MinMixedAlpha; this.MinDelay = config.MinDelay; this.MosaicColor0 = config.MosaicInfo.Color0; this.MosaicColor1 = config.MosaicInfo.Color1; this.MosaicBlockSize = config.MosaicInfo.BlockSize; this.PaletteOptimized = config.PaletteOptimized; this.FFmpegBinPath = config.FFmpegBinPath; this.FFmpegArgument = config.FFmpegArgument; this.FFmpegDefaultExtension = config.FFmpegOutputFileExtension; } public void Save(ImageHandlerConfig config) { config.SavePngFramesEnabled = this.SavePngFramesEnabled; config.GifEncoder = this.GifEncoder; config.ImageNameMethod = this.ImageNameMethod; config.BackgroundType = this.BackgroundType; config.BackgroundColor = this.BackgroundColor; config.MinMixedAlpha = this.MinMixedAlpha; config.MinDelay = this.MinDelay; config.MosaicInfo.Color0 = this.MosaicColor0; config.MosaicInfo.Color1 = this.MosaicColor1; config.MosaicInfo.BlockSize = this.MosaicBlockSize; config.PaletteOptimized = this.PaletteOptimized; config.FFmpegBinPath = this.FFmpegBinPath; config.FFmpegArgument = this.FFmpegArgument; config.FFmpegOutputFileExtension = this.FFmpegDefaultExtension; } private void btnNonTransparentMP4Preset_Click(object sender, EventArgs e) { GifEncoder = 3; BackgroundType = ImageBackgroundType.Color; BackgroundColor = Color.White; FFmpegArgument = string.Empty; FFmpegDefaultExtension = string.Empty; } private void btnGreenBackdropMP4Preset_Click(object sender, EventArgs e) { GifEncoder = 3; BackgroundType = ImageBackgroundType.Color; BackgroundColor = Color.FromArgb(0, 255, 0); FFmpegArgument = string.Empty; FFmpegDefaultExtension = string.Empty; } private void btnBlueBackdropMP4Preset_Click(object sender, EventArgs e) { GifEncoder = 3; BackgroundType = ImageBackgroundType.Color; BackgroundColor = Color.Blue; FFmpegArgument = string.Empty; FFmpegDefaultExtension = string.Empty; } private void btnTransparentMOVPreset_Click(object sender, EventArgs e) { GifEncoder = 3; BackgroundType = ImageBackgroundType.Transparent; MinMixedAlpha = 0; BackgroundColor = Color.White; FFmpegArgument = @$"-y -f rawvideo -pixel_format bgra -s %w*%h -r 1000/%t -i ""%i"" -vf ""crop=trunc(iw/2)*2:trunc(ih/2)*2"" -vcodec qtrle -pix_fmt argb ""%o"""; FFmpegDefaultExtension = ".mov"; } private void btnTransparentWebMPreset_Click(object sender, EventArgs e) { GifEncoder = 3; BackgroundType = ImageBackgroundType.Transparent; MinMixedAlpha = 0; BackgroundColor = Color.White; FFmpegArgument = @$"-y -f rawvideo -pixel_format bgra -s %w*%h -r 1000/%t -i ""%i"" -vf ""crop=trunc(iw/2)*2:trunc(ih/2)*2"" -vcodec libvpx-vp9 -pix_fmt yuva420p ""%o"""; FFmpegDefaultExtension = ".webm"; } private void btnDefaultPreset_Click(object sender, EventArgs e) { GifEncoder = 0; BackgroundType = ImageBackgroundType.Transparent; MinMixedAlpha = 0; BackgroundColor = Color.White; FFmpegArgument = string.Empty; FFmpegDefaultExtension = string.Empty; } private void slider1_ValueChanged(object sender, EventArgs e) { var slider = sender as DevComponents.DotNetBar.Controls.Slider; slider.Text = slider.Value.ToString(); } private void rdoColor_CheckedChanged(object sender, EventArgs e) { panelExColor.Enabled = rdoColor.Checked; } private void rdoMosaic_CheckedChanged(object sender, EventArgs e) { panelExMosaic.Enabled = rdoMosaic.Checked; } private void buttonX3_Click(object sender, EventArgs e) { OpenFileDialog dlg = new(); dlg.Title = "请选择FFmpeg可执行文件路径..."; dlg.Filter = "ffmpeg.exe|*.exe|*.*|*.*"; dlg.FileName = this.FFmpegBinPath; if (dlg.ShowDialog(this) == DialogResult.OK) { this.FFmpegBinPath = dlg.FileName; } } } } ================================================ FILE: WzComparerR2/FrmGifSetting.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6 JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAKklEQVQ4T2P4//8/RRhCAClyMIoBIJoU PGrAqAEgPBwNIAfDDSAf/2cAALEslYfUgx+eAAAAAElFTkSuQmCC 17, 17 iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6 JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAKklEQVQ4T2P4//8/RRhCAClyMIoBIJoU PGrAqAEgPBwNIAfDDSAf/2cAALEslYfUgx+eAAAAAElFTkSuQmCC iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6 JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAKklEQVQ4T2P4//8/RRhCAClyMIoBIJoU PGrAqAEgPBwNIAfDDSAf/2cAALEslYfUgx+eAAAAAElFTkSuQmCC ================================================ FILE: WzComparerR2/FrmOptions.Designer.cs ================================================ namespace WzComparerR2 { partial class FrmOptions { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { this.panelEx1 = new DevComponents.DotNetBar.PanelEx(); this.buttonX2 = new DevComponents.DotNetBar.ButtonX(); this.buttonX1 = new DevComponents.DotNetBar.ButtonX(); this.superTabControl1 = new DevComponents.DotNetBar.SuperTabControl(); this.superTabControlPanel1 = new DevComponents.DotNetBar.SuperTabControlPanel(); this.chkAutoCheckExtFiles = new DevComponents.DotNetBar.Controls.CheckBoxX(); this.cmbWzEncoding = new DevComponents.DotNetBar.Controls.ComboBoxEx(); this.labelX1 = new DevComponents.DotNetBar.LabelX(); this.chkWzAutoSort = new DevComponents.DotNetBar.Controls.CheckBoxX(); this.superTabItem1 = new DevComponents.DotNetBar.SuperTabItem(); this.chkWzSortByImgID = new DevComponents.DotNetBar.Controls.CheckBoxX(); this.chkImgCheckDisabled = new DevComponents.DotNetBar.Controls.CheckBoxX(); this.panelEx1.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.superTabControl1)).BeginInit(); this.superTabControl1.SuspendLayout(); this.superTabControlPanel1.SuspendLayout(); this.SuspendLayout(); // // panelEx1 // this.panelEx1.CanvasColor = System.Drawing.SystemColors.Control; this.panelEx1.ColorSchemeStyle = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.panelEx1.Controls.Add(this.buttonX2); this.panelEx1.Controls.Add(this.buttonX1); this.panelEx1.DisabledBackColor = System.Drawing.Color.Empty; this.panelEx1.Dock = System.Windows.Forms.DockStyle.Bottom; this.panelEx1.Location = new System.Drawing.Point(0, 171); this.panelEx1.Name = "panelEx1"; this.panelEx1.Size = new System.Drawing.Size(304, 30); this.panelEx1.Style.Alignment = System.Drawing.StringAlignment.Center; this.panelEx1.Style.BackColor1.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.PanelBackground; this.panelEx1.Style.BackColor2.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.PanelBackground2; this.panelEx1.Style.Border = DevComponents.DotNetBar.eBorderType.SingleLine; this.panelEx1.Style.BorderColor.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.PanelBorder; this.panelEx1.Style.ForeColor.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.PanelText; this.panelEx1.Style.GradientAngle = 90; this.panelEx1.TabIndex = 0; // // buttonX2 // this.buttonX2.AccessibleRole = System.Windows.Forms.AccessibleRole.PushButton; this.buttonX2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.buttonX2.ColorTable = DevComponents.DotNetBar.eButtonColor.OrangeWithBackground; this.buttonX2.DialogResult = System.Windows.Forms.DialogResult.Cancel; this.buttonX2.Location = new System.Drawing.Point(235, 4); this.buttonX2.Name = "buttonX2"; this.buttonX2.Size = new System.Drawing.Size(60, 23); this.buttonX2.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.buttonX2.TabIndex = 1; this.buttonX2.Text = "取消"; // // buttonX1 // this.buttonX1.AccessibleRole = System.Windows.Forms.AccessibleRole.PushButton; this.buttonX1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.buttonX1.ColorTable = DevComponents.DotNetBar.eButtonColor.OrangeWithBackground; this.buttonX1.DialogResult = System.Windows.Forms.DialogResult.OK; this.buttonX1.Location = new System.Drawing.Point(168, 4); this.buttonX1.Name = "buttonX1"; this.buttonX1.Size = new System.Drawing.Size(60, 23); this.buttonX1.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.buttonX1.TabIndex = 0; this.buttonX1.Text = "确定"; // // superTabControl1 // this.superTabControl1.AutoCloseTabs = false; // // // // // // this.superTabControl1.ControlBox.CloseBox.Name = ""; // // // this.superTabControl1.ControlBox.MenuBox.Name = ""; this.superTabControl1.ControlBox.Name = ""; this.superTabControl1.ControlBox.SubItems.AddRange(new DevComponents.DotNetBar.BaseItem[] { this.superTabControl1.ControlBox.MenuBox, this.superTabControl1.ControlBox.CloseBox}); this.superTabControl1.Controls.Add(this.superTabControlPanel1); this.superTabControl1.Dock = System.Windows.Forms.DockStyle.Fill; this.superTabControl1.Location = new System.Drawing.Point(0, 0); this.superTabControl1.Name = "superTabControl1"; this.superTabControl1.ReorderTabsEnabled = true; this.superTabControl1.SelectedTabFont = new System.Drawing.Font("宋体", 9F, System.Drawing.FontStyle.Bold); this.superTabControl1.SelectedTabIndex = 0; this.superTabControl1.Size = new System.Drawing.Size(304, 171); this.superTabControl1.TabAlignment = DevComponents.DotNetBar.eTabStripAlignment.Left; this.superTabControl1.TabFont = new System.Drawing.Font("宋体", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); this.superTabControl1.TabIndex = 4; this.superTabControl1.Tabs.AddRange(new DevComponents.DotNetBar.BaseItem[] { this.superTabItem1}); this.superTabControl1.Text = "superTabControl1"; // // superTabControlPanel1 // this.superTabControlPanel1.Controls.Add(this.chkImgCheckDisabled); this.superTabControlPanel1.Controls.Add(this.chkWzSortByImgID); this.superTabControlPanel1.Controls.Add(this.chkAutoCheckExtFiles); this.superTabControlPanel1.Controls.Add(this.cmbWzEncoding); this.superTabControlPanel1.Controls.Add(this.labelX1); this.superTabControlPanel1.Controls.Add(this.chkWzAutoSort); this.superTabControlPanel1.Dock = System.Windows.Forms.DockStyle.Fill; this.superTabControlPanel1.Location = new System.Drawing.Point(82, 0); this.superTabControlPanel1.Name = "superTabControlPanel1"; this.superTabControlPanel1.Size = new System.Drawing.Size(222, 171); this.superTabControlPanel1.TabIndex = 1; this.superTabControlPanel1.TabItem = this.superTabItem1; // // chkAutoCheckExtFiles // this.chkAutoCheckExtFiles.AutoSize = true; this.chkAutoCheckExtFiles.BackColor = System.Drawing.Color.Transparent; // // // this.chkAutoCheckExtFiles.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.chkAutoCheckExtFiles.Location = new System.Drawing.Point(14, 86); this.chkAutoCheckExtFiles.Name = "chkAutoCheckExtFiles"; this.chkAutoCheckExtFiles.Size = new System.Drawing.Size(193, 18); this.chkAutoCheckExtFiles.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.chkAutoCheckExtFiles.TabIndex = 4; this.chkAutoCheckExtFiles.Text = "自动检测扩展wz文件(map2...)"; // // cmbWzEncoding // this.cmbWzEncoding.DisplayMember = "Text"; this.cmbWzEncoding.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawFixed; this.cmbWzEncoding.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.cmbWzEncoding.FormattingEnabled = true; this.cmbWzEncoding.ItemHeight = 15; this.cmbWzEncoding.Location = new System.Drawing.Point(86, 59); this.cmbWzEncoding.Name = "cmbWzEncoding"; this.cmbWzEncoding.Size = new System.Drawing.Size(121, 21); this.cmbWzEncoding.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.cmbWzEncoding.TabIndex = 3; // // labelX1 // this.labelX1.AutoSize = true; this.labelX1.BackColor = System.Drawing.Color.Transparent; // // // this.labelX1.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.labelX1.Location = new System.Drawing.Point(14, 61); this.labelX1.Name = "labelX1"; this.labelX1.Size = new System.Drawing.Size(68, 18); this.labelX1.TabIndex = 2; this.labelX1.Text = "wz默认编码"; // // chkWzAutoSort // this.chkWzAutoSort.AutoSize = true; this.chkWzAutoSort.BackColor = System.Drawing.Color.Transparent; // // // this.chkWzAutoSort.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.chkWzAutoSort.Location = new System.Drawing.Point(14, 13); this.chkWzAutoSort.Name = "chkWzAutoSort"; this.chkWzAutoSort.Size = new System.Drawing.Size(125, 18); this.chkWzAutoSort.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.chkWzAutoSort.TabIndex = 0; this.chkWzAutoSort.Text = "Wz加载时自动排序"; // // superTabItem1 // this.superTabItem1.AttachedControl = this.superTabControlPanel1; this.superTabItem1.GlobalItem = false; this.superTabItem1.Name = "superTabItem1"; this.superTabItem1.Text = "WzLoading"; // // chkWzSortByImgID // this.chkWzSortByImgID.AutoSize = true; this.chkWzSortByImgID.BackColor = System.Drawing.Color.Transparent; // // // this.chkWzSortByImgID.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.chkWzSortByImgID.Location = new System.Drawing.Point(31, 36); this.chkWzSortByImgID.Name = "chkWzSortByImgID"; this.chkWzSortByImgID.Size = new System.Drawing.Size(107, 18); this.chkWzSortByImgID.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.chkWzSortByImgID.TabIndex = 5; this.chkWzSortByImgID.Text = "按照ImgID排序"; // // chkImgCheckDisabled // this.chkImgCheckDisabled.AutoSize = true; this.chkImgCheckDisabled.BackColor = System.Drawing.Color.Transparent; // // // this.chkImgCheckDisabled.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.chkImgCheckDisabled.Location = new System.Drawing.Point(14, 110); this.chkImgCheckDisabled.Name = "chkImgCheckDisabled"; this.chkImgCheckDisabled.Size = new System.Drawing.Size(132, 18); this.chkImgCheckDisabled.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.chkImgCheckDisabled.TabIndex = 6; this.chkImgCheckDisabled.Text = "跳过img校验和检测"; // // FrmOptions // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(304, 201); this.Controls.Add(this.superTabControl1); this.Controls.Add(this.panelEx1); this.DoubleBuffered = true; this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; this.MaximizeBox = false; this.Name = "FrmOptions"; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; this.Text = "通用设置"; this.panelEx1.ResumeLayout(false); ((System.ComponentModel.ISupportInitialize)(this.superTabControl1)).EndInit(); this.superTabControl1.ResumeLayout(false); this.superTabControlPanel1.ResumeLayout(false); this.superTabControlPanel1.PerformLayout(); this.ResumeLayout(false); } #endregion private DevComponents.DotNetBar.PanelEx panelEx1; private DevComponents.DotNetBar.SuperTabControl superTabControl1; private DevComponents.DotNetBar.SuperTabControlPanel superTabControlPanel1; private DevComponents.DotNetBar.SuperTabItem superTabItem1; private DevComponents.DotNetBar.ButtonX buttonX2; private DevComponents.DotNetBar.ButtonX buttonX1; private DevComponents.DotNetBar.Controls.CheckBoxX chkWzAutoSort; private DevComponents.DotNetBar.LabelX labelX1; private DevComponents.DotNetBar.Controls.ComboBoxEx cmbWzEncoding; private DevComponents.DotNetBar.Controls.CheckBoxX chkAutoCheckExtFiles; private DevComponents.DotNetBar.Controls.CheckBoxX chkWzSortByImgID; private DevComponents.DotNetBar.Controls.CheckBoxX chkImgCheckDisabled; } } ================================================ FILE: WzComparerR2/FrmOptions.cs ================================================ using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using DevComponents.DotNetBar; using DevComponents.Editors; using WzComparerR2.Config; namespace WzComparerR2 { public partial class FrmOptions : DevComponents.DotNetBar.Office2007Form { public FrmOptions() { InitializeComponent(); #if NET6_0_OR_GREATER // https://learn.microsoft.com/en-us/dotnet/core/compatibility/fx-core#controldefaultfont-changed-to-segoe-ui-9pt this.Font = new Font(new FontFamily("Microsoft Sans Serif"), 8f); #endif cmbWzEncoding.Items.AddRange(new[] { new ComboItem("默认"){ Value = 0 }, new ComboItem("CMS(gb2312)"){ Value = 936 }, new ComboItem("KMS(euc-kr)"){ Value = 949 }, new ComboItem("JMS(shift-jis)"){ Value = 932 }, new ComboItem("TMS(big5)"){ Value = 950 }, new ComboItem("GMS(iso-8859-1)"){ Value = 1252 }, new ComboItem("自定义"){ Value = -1 }, }); } public bool SortWzOnOpened { get { return chkWzAutoSort.Checked; } set { chkWzAutoSort.Checked = value; } } public bool SortWzByImgID { get { return chkWzSortByImgID.Checked; } set { chkWzSortByImgID.Checked = value; } } public int DefaultWzCodePage { get { return ((cmbWzEncoding.SelectedItem as ComboItem)?.Value as int?) ?? 0; } set { var items = cmbWzEncoding.Items.Cast(); var item = items.FirstOrDefault(_item => _item.Value as int? == value) ?? items.Last(); item.Value = value; cmbWzEncoding.SelectedItem = item; } } public bool AutoDetectExtFiles { get { return chkAutoCheckExtFiles.Checked; } set { chkAutoCheckExtFiles.Checked = value; } } public bool ImgCheckDisabled { get { return chkImgCheckDisabled.Checked; } set { chkImgCheckDisabled.Checked = value; } } public void Load(WcR2Config config) { this.SortWzOnOpened = config.SortWzOnOpened; this.SortWzByImgID = config.SortWzByImgID; this.DefaultWzCodePage = config.WzEncoding; this.AutoDetectExtFiles = config.AutoDetectExtFiles; this.ImgCheckDisabled = config.ImgCheckDisabled; } public void Save(WcR2Config config) { config.SortWzOnOpened = this.SortWzOnOpened; config.SortWzByImgID = this.SortWzByImgID; config.WzEncoding = this.DefaultWzCodePage; config.AutoDetectExtFiles = this.AutoDetectExtFiles; config.ImgCheckDisabled = this.ImgCheckDisabled; } } } ================================================ FILE: WzComparerR2/FrmOptions.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ================================================ FILE: WzComparerR2/FrmPatcher.Designer.cs ================================================ namespace WzComparerR2 { partial class FrmPatcher { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { this.comboBoxEx1 = new DevComponents.DotNetBar.Controls.ComboBoxEx(); this.integerInput1 = new DevComponents.Editors.IntegerInput(); this.txtUrl = new DevComponents.DotNetBar.Controls.TextBoxX(); this.buttonXPatch = new DevComponents.DotNetBar.ButtonX(); this.chkDeadPatch = new DevComponents.DotNetBar.Controls.CheckBoxX(); this.chkPrePatch = new DevComponents.DotNetBar.Controls.CheckBoxX(); this.buttonXOpen2 = new DevComponents.DotNetBar.ButtonX(); this.txtMSFolder = new DevComponents.DotNetBar.Controls.TextBoxX(); this.labelX2 = new DevComponents.DotNetBar.LabelX(); this.buttonXOpen1 = new DevComponents.DotNetBar.ButtonX(); this.txtPatchFile = new DevComponents.DotNetBar.Controls.TextBoxX(); this.labelX1 = new DevComponents.DotNetBar.LabelX(); this.expandablePanel1 = new DevComponents.DotNetBar.ExpandablePanel(); this.flowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel(); this.buttonXCheck = new DevComponents.DotNetBar.ButtonX(); this.expandablePanel2 = new DevComponents.DotNetBar.ExpandablePanel(); this.chkEnableDarkMode = new DevComponents.DotNetBar.Controls.CheckBoxX(); this.chkOutputRemovedImg = new DevComponents.DotNetBar.Controls.CheckBoxX(); this.chkOutputAddedImg = new DevComponents.DotNetBar.Controls.CheckBoxX(); this.cmbComparePng = new DevComponents.DotNetBar.Controls.ComboBoxEx(); this.chkOutputPng = new DevComponents.DotNetBar.Controls.CheckBoxX(); this.chkCompare = new DevComponents.DotNetBar.Controls.CheckBoxX(); this.panelEx2 = new DevComponents.DotNetBar.PanelEx(); this.superTabControl1 = new DevComponents.DotNetBar.SuperTabControl(); this.superTabControlPanel1 = new DevComponents.DotNetBar.SuperTabControlPanel(); this.txtNotice = new DevComponents.DotNetBar.Controls.TextBoxX(); this.superTabItem1 = new DevComponents.DotNetBar.SuperTabItem(); this.superTabControlPanel2 = new DevComponents.DotNetBar.SuperTabControlPanel(); this.advTreePatchFiles = new DevComponents.AdvTree.AdvTree(); this.columnHeader1 = new DevComponents.AdvTree.ColumnHeader(); this.columnHeader2 = new DevComponents.AdvTree.ColumnHeader(); this.columnHeader3 = new DevComponents.AdvTree.ColumnHeader(); this.columnHeader4 = new DevComponents.AdvTree.ColumnHeader(); this.columnHeader5 = new DevComponents.AdvTree.ColumnHeader(); this.columnHeader6 = new DevComponents.AdvTree.ColumnHeader(); this.nodeConnector1 = new DevComponents.AdvTree.NodeConnector(); this.elementStyle1 = new DevComponents.DotNetBar.ElementStyle(); this.superTabItem2 = new DevComponents.DotNetBar.SuperTabItem(); this.superTabControlPanel3 = new DevComponents.DotNetBar.SuperTabControlPanel(); this.txtPatchState = new DevComponents.DotNetBar.Controls.TextBoxX(); this.progressBarX1 = new DevComponents.DotNetBar.Controls.ProgressBarX(); this.superTabItem3 = new DevComponents.DotNetBar.SuperTabItem(); this.panelEx1 = new DevComponents.DotNetBar.PanelEx(); this.expandablePanel3 = new DevComponents.DotNetBar.ExpandablePanel(); this.buttonXCreate = new DevComponents.DotNetBar.ButtonX(); this.txtPatchFile2 = new DevComponents.DotNetBar.Controls.TextBoxX(); this.buttonXOpen4 = new DevComponents.DotNetBar.ButtonX(); this.buttonXOpen3 = new DevComponents.DotNetBar.ButtonX(); this.txtMSFolder2 = new DevComponents.DotNetBar.Controls.TextBoxX(); this.labelX4 = new DevComponents.DotNetBar.LabelX(); this.labelX5 = new DevComponents.DotNetBar.LabelX(); this.labelX3 = new DevComponents.DotNetBar.LabelX(); this.superTooltip1 = new DevComponents.DotNetBar.SuperTooltip(); this.chkResolvePngLink = new DevComponents.DotNetBar.Controls.CheckBoxX(); ((System.ComponentModel.ISupportInitialize)(this.integerInput1)).BeginInit(); this.expandablePanel1.SuspendLayout(); this.flowLayoutPanel1.SuspendLayout(); this.expandablePanel2.SuspendLayout(); this.panelEx2.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.superTabControl1)).BeginInit(); this.superTabControl1.SuspendLayout(); this.superTabControlPanel1.SuspendLayout(); this.superTabControlPanel2.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.advTreePatchFiles)).BeginInit(); this.superTabControlPanel3.SuspendLayout(); this.panelEx1.SuspendLayout(); this.expandablePanel3.SuspendLayout(); this.SuspendLayout(); // // comboBoxEx1 // this.comboBoxEx1.DisplayMember = "Text"; this.comboBoxEx1.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawFixed; this.comboBoxEx1.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.comboBoxEx1.FormattingEnabled = true; this.comboBoxEx1.ItemHeight = 15; this.comboBoxEx1.Location = new System.Drawing.Point(12, 35); this.comboBoxEx1.Name = "comboBoxEx1"; this.comboBoxEx1.Size = new System.Drawing.Size(86, 21); this.comboBoxEx1.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.comboBoxEx1.TabIndex = 0; this.comboBoxEx1.SelectedIndexChanged += new System.EventHandler(this.comboBoxEx1_SelectedIndexChanged); // // integerInput1 // this.integerInput1.AllowEmptyState = false; // // // this.integerInput1.BackgroundStyle.Class = "DateTimeInputBackground"; this.integerInput1.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.integerInput1.ButtonFreeText.Shortcut = DevComponents.DotNetBar.eShortcut.F2; this.integerInput1.DisplayFormat = "00000"; this.integerInput1.Location = new System.Drawing.Point(3, 3); this.integerInput1.MaxValue = 99999; this.integerInput1.MinValue = 0; this.integerInput1.Name = "integerInput1"; this.integerInput1.ShowUpDown = true; this.integerInput1.Size = new System.Drawing.Size(60, 21); this.integerInput1.TabIndex = 1; this.integerInput1.ValueChanged += new System.EventHandler(this.integerInput_ValueChanged); // // txtUrl // this.txtUrl.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); // // // this.txtUrl.Border.Class = "TextBoxBorder"; this.txtUrl.Border.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.txtUrl.Location = new System.Drawing.Point(12, 62); this.txtUrl.Name = "txtUrl"; this.txtUrl.ReadOnly = true; this.txtUrl.Size = new System.Drawing.Size(304, 21); this.txtUrl.TabIndex = 3; // // buttonXPatch // this.buttonXPatch.AccessibleRole = System.Windows.Forms.AccessibleRole.PushButton; this.buttonXPatch.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.buttonXPatch.ColorTable = DevComponents.DotNetBar.eButtonColor.OrangeWithBackground; this.buttonXPatch.Location = new System.Drawing.Point(323, 82); this.buttonXPatch.Name = "buttonXPatch"; this.buttonXPatch.Size = new System.Drawing.Size(40, 25); this.buttonXPatch.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.buttonXPatch.TabIndex = 8; this.buttonXPatch.Text = "Patch"; this.buttonXPatch.Click += new System.EventHandler(this.buttonXPatch_Click); // // chkDeadPatch // this.chkDeadPatch.AutoSize = true; this.chkDeadPatch.BackColor = System.Drawing.Color.Transparent; // // // this.chkDeadPatch.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.chkDeadPatch.Location = new System.Drawing.Point(81, 87); this.chkDeadPatch.Name = "chkDeadPatch"; this.chkDeadPatch.Size = new System.Drawing.Size(82, 16); this.chkDeadPatch.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.superTooltip1.SetSuperTooltip(this.chkDeadPatch, new DevComponents.DotNetBar.SuperTooltipInfo("DeadPatch", "", "开启此项后,每更新完一个子文件,将立即覆盖原文件并删除临时文件。这样做会减少临时文件空间的需要,但是伴随一定风险。\r\n\r\n在KMST1125格式的补丁上,需同时启" + "用PrePatch才能生效。", null, null, DevComponents.DotNetBar.eTooltipColor.System, true, false, new System.Drawing.Size(180, 180))); this.chkDeadPatch.TabIndex = 7; this.chkDeadPatch.Text = "DeadPatch"; // // chkPrePatch // this.chkPrePatch.AutoSize = true; this.chkPrePatch.BackColor = System.Drawing.Color.Transparent; // // // this.chkPrePatch.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.chkPrePatch.Location = new System.Drawing.Point(6, 87); this.chkPrePatch.Name = "chkPrePatch"; this.chkPrePatch.Size = new System.Drawing.Size(76, 16); this.chkPrePatch.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.superTooltip1.SetSuperTooltip(this.chkPrePatch, new DevComponents.DotNetBar.SuperTooltipInfo("PrePatch", "", "开启此项则会在补丁更新前进行预加载。预加载后可以自由选择要更新的子文件,或调整子文件更新顺序。调整完毕后再次单击\"Patch\"按钮开始更新。", null, null, DevComponents.DotNetBar.eTooltipColor.Default, true, false, new System.Drawing.Size(180, 140))); this.chkPrePatch.TabIndex = 6; this.chkPrePatch.Text = "PrePatch"; // // buttonXOpen2 // this.buttonXOpen2.AccessibleRole = System.Windows.Forms.AccessibleRole.PushButton; this.buttonXOpen2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.buttonXOpen2.ColorTable = DevComponents.DotNetBar.eButtonColor.OrangeWithBackground; this.buttonXOpen2.Location = new System.Drawing.Point(330, 59); this.buttonXOpen2.Name = "buttonXOpen2"; this.buttonXOpen2.Size = new System.Drawing.Size(33, 21); this.buttonXOpen2.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.buttonXOpen2.TabIndex = 4; this.buttonXOpen2.Text = "Open"; this.buttonXOpen2.Click += new System.EventHandler(this.buttonXOpen2_Click); // // txtMSFolder // this.txtMSFolder.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); // // // this.txtMSFolder.Border.Class = "TextBoxBorder"; this.txtMSFolder.Border.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.txtMSFolder.Location = new System.Drawing.Point(68, 59); this.txtMSFolder.Name = "txtMSFolder"; this.txtMSFolder.Size = new System.Drawing.Size(259, 21); this.txtMSFolder.TabIndex = 3; // // labelX2 // this.labelX2.AutoSize = true; this.labelX2.BackColor = System.Drawing.Color.Transparent; // // // this.labelX2.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.labelX2.Location = new System.Drawing.Point(7, 61); this.labelX2.Name = "labelX2"; this.labelX2.Size = new System.Drawing.Size(62, 16); this.labelX2.TabIndex = 5; this.labelX2.Text = "MS Folder"; // // buttonXOpen1 // this.buttonXOpen1.AccessibleRole = System.Windows.Forms.AccessibleRole.PushButton; this.buttonXOpen1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.buttonXOpen1.ColorTable = DevComponents.DotNetBar.eButtonColor.OrangeWithBackground; this.buttonXOpen1.Location = new System.Drawing.Point(330, 34); this.buttonXOpen1.Name = "buttonXOpen1"; this.buttonXOpen1.Size = new System.Drawing.Size(33, 21); this.buttonXOpen1.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.buttonXOpen1.TabIndex = 1; this.buttonXOpen1.Text = "Open"; this.buttonXOpen1.Click += new System.EventHandler(this.buttonXOpen1_Click); // // txtPatchFile // this.txtPatchFile.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); // // // this.txtPatchFile.Border.Class = "TextBoxBorder"; this.txtPatchFile.Border.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.txtPatchFile.Location = new System.Drawing.Point(68, 34); this.txtPatchFile.Name = "txtPatchFile"; this.txtPatchFile.Size = new System.Drawing.Size(259, 21); this.txtPatchFile.TabIndex = 0; // // labelX1 // this.labelX1.AutoSize = true; this.labelX1.BackColor = System.Drawing.Color.Transparent; // // // this.labelX1.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.labelX1.Location = new System.Drawing.Point(7, 36); this.labelX1.Name = "labelX1"; this.labelX1.Size = new System.Drawing.Size(62, 16); this.labelX1.TabIndex = 2; this.labelX1.Text = "PatchFile"; // // expandablePanel1 // this.expandablePanel1.CanvasColor = System.Drawing.SystemColors.Control; this.expandablePanel1.ColorSchemeStyle = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.expandablePanel1.Controls.Add(this.flowLayoutPanel1); this.expandablePanel1.Controls.Add(this.buttonXCheck); this.expandablePanel1.Controls.Add(this.comboBoxEx1); this.expandablePanel1.Controls.Add(this.txtUrl); this.expandablePanel1.DisabledBackColor = System.Drawing.Color.Empty; this.expandablePanel1.Dock = System.Windows.Forms.DockStyle.Top; this.expandablePanel1.ExpandOnTitleClick = true; this.expandablePanel1.Location = new System.Drawing.Point(0, 0); this.expandablePanel1.Name = "expandablePanel1"; this.expandablePanel1.Size = new System.Drawing.Size(384, 87); this.expandablePanel1.Style.Alignment = System.Drawing.StringAlignment.Center; this.expandablePanel1.Style.BackColor1.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.PanelBackground; this.expandablePanel1.Style.BackColor2.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.PanelBackground2; this.expandablePanel1.Style.Border = DevComponents.DotNetBar.eBorderType.SingleLine; this.expandablePanel1.Style.BorderColor.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.BarDockedBorder; this.expandablePanel1.Style.ForeColor.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.ItemText; this.expandablePanel1.Style.GradientAngle = 90; this.expandablePanel1.TabIndex = 6; this.expandablePanel1.TitleStyle.Alignment = System.Drawing.StringAlignment.Center; this.expandablePanel1.TitleStyle.BackColor1.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.PanelBackground; this.expandablePanel1.TitleStyle.BackColor2.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.PanelBackground2; this.expandablePanel1.TitleStyle.Border = DevComponents.DotNetBar.eBorderType.RaisedInner; this.expandablePanel1.TitleStyle.BorderColor.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.PanelBorder; this.expandablePanel1.TitleStyle.ForeColor.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.PanelText; this.expandablePanel1.TitleStyle.GradientAngle = 90; this.expandablePanel1.TitleText = "Patch Download Url"; // // flowLayoutPanel1 // this.flowLayoutPanel1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.flowLayoutPanel1.BackColor = System.Drawing.Color.Transparent; this.flowLayoutPanel1.Controls.Add(this.integerInput1); this.flowLayoutPanel1.Location = new System.Drawing.Point(104, 33); this.flowLayoutPanel1.Margin = new System.Windows.Forms.Padding(0); this.flowLayoutPanel1.Name = "flowLayoutPanel1"; this.flowLayoutPanel1.Size = new System.Drawing.Size(270, 26); this.flowLayoutPanel1.TabIndex = 5; // // buttonXCheck // this.buttonXCheck.AccessibleRole = System.Windows.Forms.AccessibleRole.PushButton; this.buttonXCheck.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.buttonXCheck.ColorTable = DevComponents.DotNetBar.eButtonColor.OrangeWithBackground; this.buttonXCheck.Location = new System.Drawing.Point(322, 61); this.buttonXCheck.Name = "buttonXCheck"; this.buttonXCheck.Size = new System.Drawing.Size(41, 23); this.buttonXCheck.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.buttonXCheck.TabIndex = 4; this.buttonXCheck.Text = "Check"; this.buttonXCheck.Click += new System.EventHandler(this.buttonXCheck_Click); // // expandablePanel2 // this.expandablePanel2.CanvasColor = System.Drawing.SystemColors.Control; this.expandablePanel2.ColorSchemeStyle = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.expandablePanel2.Controls.Add(this.chkResolvePngLink); this.expandablePanel2.Controls.Add(this.chkEnableDarkMode); this.expandablePanel2.Controls.Add(this.chkOutputRemovedImg); this.expandablePanel2.Controls.Add(this.chkOutputAddedImg); this.expandablePanel2.Controls.Add(this.cmbComparePng); this.expandablePanel2.Controls.Add(this.chkOutputPng); this.expandablePanel2.Controls.Add(this.chkCompare); this.expandablePanel2.Controls.Add(this.panelEx2); this.expandablePanel2.Controls.Add(this.buttonXPatch); this.expandablePanel2.Controls.Add(this.chkDeadPatch); this.expandablePanel2.Controls.Add(this.chkPrePatch); this.expandablePanel2.Controls.Add(this.txtPatchFile); this.expandablePanel2.Controls.Add(this.buttonXOpen2); this.expandablePanel2.Controls.Add(this.buttonXOpen1); this.expandablePanel2.Controls.Add(this.txtMSFolder); this.expandablePanel2.Controls.Add(this.labelX2); this.expandablePanel2.Controls.Add(this.labelX1); this.expandablePanel2.DisabledBackColor = System.Drawing.Color.Empty; this.expandablePanel2.Dock = System.Windows.Forms.DockStyle.Top; this.expandablePanel2.ExpandOnTitleClick = true; this.expandablePanel2.Location = new System.Drawing.Point(0, 87); this.expandablePanel2.Name = "expandablePanel2"; this.expandablePanel2.Size = new System.Drawing.Size(384, 157); this.expandablePanel2.Style.Alignment = System.Drawing.StringAlignment.Center; this.expandablePanel2.Style.BackColor1.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.PanelBackground; this.expandablePanel2.Style.BackColor2.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.PanelBackground2; this.expandablePanel2.Style.Border = DevComponents.DotNetBar.eBorderType.SingleLine; this.expandablePanel2.Style.BorderColor.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.BarDockedBorder; this.expandablePanel2.Style.ForeColor.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.ItemText; this.expandablePanel2.Style.GradientAngle = 90; this.expandablePanel2.TabIndex = 7; this.expandablePanel2.TitleStyle.Alignment = System.Drawing.StringAlignment.Center; this.expandablePanel2.TitleStyle.BackColor1.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.PanelBackground; this.expandablePanel2.TitleStyle.BackColor2.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.PanelBackground2; this.expandablePanel2.TitleStyle.Border = DevComponents.DotNetBar.eBorderType.RaisedInner; this.expandablePanel2.TitleStyle.BorderColor.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.PanelBorder; this.expandablePanel2.TitleStyle.ForeColor.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.PanelText; this.expandablePanel2.TitleStyle.GradientAngle = 90; this.expandablePanel2.TitleText = "Manual Patcher"; // // chkEnableDarkMode // this.chkEnableDarkMode.AutoSize = true; this.chkEnableDarkMode.BackColor = System.Drawing.Color.Transparent; // // // this.chkEnableDarkMode.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.chkEnableDarkMode.Location = new System.Drawing.Point(253, 135); this.chkEnableDarkMode.Name = "chkEnableDarkMode"; this.chkEnableDarkMode.Size = new System.Drawing.Size(125, 16); this.chkEnableDarkMode.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.superTooltip1.SetSuperTooltip(this.chkEnableDarkMode, new DevComponents.DotNetBar.SuperTooltipInfo("EnableDarkMode", "", "将比对结果以暗黑模式 HTML 输出", null, null, DevComponents.DotNetBar.eTooltipColor.System, true, false, new System.Drawing.Size(180, 80))); this.chkEnableDarkMode.TabIndex = 14; this.chkEnableDarkMode.Text = "EnableDarkMode"; // // chkOutputRemovedImg // this.chkOutputRemovedImg.AutoSize = true; this.chkOutputRemovedImg.BackColor = System.Drawing.Color.Transparent; // // // this.chkOutputRemovedImg.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.chkOutputRemovedImg.Location = new System.Drawing.Point(125, 135); this.chkOutputRemovedImg.Name = "chkOutputRemovedImg"; this.chkOutputRemovedImg.Size = new System.Drawing.Size(125, 16); this.chkOutputRemovedImg.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.superTooltip1.SetSuperTooltip(this.chkOutputRemovedImg, new DevComponents.DotNetBar.SuperTooltipInfo("OutputRemovedImg", "", "对比报告中是否输出被整体移除的Image的完整结构", null, null, DevComponents.DotNetBar.eTooltipColor.System, true, false, new System.Drawing.Size(180, 80))); this.chkOutputRemovedImg.TabIndex = 14; this.chkOutputRemovedImg.Text = "OutputRemovedImg"; // // chkOutputAddedImg // this.chkOutputAddedImg.AutoSize = true; this.chkOutputAddedImg.BackColor = System.Drawing.Color.Transparent; // // // this.chkOutputAddedImg.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.chkOutputAddedImg.Location = new System.Drawing.Point(6, 135); this.chkOutputAddedImg.Name = "chkOutputAddedImg"; this.chkOutputAddedImg.Size = new System.Drawing.Size(113, 16); this.chkOutputAddedImg.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.superTooltip1.SetSuperTooltip(this.chkOutputAddedImg, new DevComponents.DotNetBar.SuperTooltipInfo("OutputAddedImg", "", "对比报告中是否输出新增Image的完整结构", null, null, DevComponents.DotNetBar.eTooltipColor.System, true, false, new System.Drawing.Size(180, 80))); this.chkOutputAddedImg.TabIndex = 13; this.chkOutputAddedImg.Text = "OutputAddedImg"; // // cmbComparePng // this.cmbComparePng.DisplayMember = "Text"; this.cmbComparePng.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawFixed; this.cmbComparePng.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.cmbComparePng.FormattingEnabled = true; this.cmbComparePng.ItemHeight = 15; this.cmbComparePng.Location = new System.Drawing.Point(79, 108); this.cmbComparePng.Name = "cmbComparePng"; this.cmbComparePng.Size = new System.Drawing.Size(120, 21); this.cmbComparePng.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.superTooltip1.SetSuperTooltip(this.cmbComparePng, new DevComponents.DotNetBar.SuperTooltipInfo("PngComparison", "", "对于对比报告中图片的对比方式。\r\nSizeOnly - 仅对比图片大小,可能会遗漏。\r\nSizeAndDataLength - 同时对比图片大小和压缩流长度,可能" + "会误判。\r\nPixel - 像素级对比,非常精确但可能略耗时。", null, null, DevComponents.DotNetBar.eTooltipColor.System, true, true, new System.Drawing.Size(300, 130))); this.cmbComparePng.TabIndex = 12; // // chkOutputPng // this.chkOutputPng.AutoSize = true; this.chkOutputPng.BackColor = System.Drawing.Color.Transparent; // // // this.chkOutputPng.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.chkOutputPng.Checked = true; this.chkOutputPng.CheckState = System.Windows.Forms.CheckState.Checked; this.chkOutputPng.CheckValue = "Y"; this.chkOutputPng.Location = new System.Drawing.Point(208, 111); this.chkOutputPng.Name = "chkOutputPng"; this.chkOutputPng.Size = new System.Drawing.Size(82, 16); this.chkOutputPng.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.superTooltip1.SetSuperTooltip(this.chkOutputPng, new DevComponents.DotNetBar.SuperTooltipInfo("OutputPng", "", "对比报告中是否输出有差异的图片文件。", null, null, DevComponents.DotNetBar.eTooltipColor.System, true, false, new System.Drawing.Size(180, 80))); this.chkOutputPng.TabIndex = 11; this.chkOutputPng.Text = "OutputPng"; // // chkCompare // this.chkCompare.AutoSize = true; this.chkCompare.BackColor = System.Drawing.Color.Transparent; // // // this.chkCompare.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.chkCompare.Location = new System.Drawing.Point(6, 111); this.chkCompare.Name = "chkCompare"; this.chkCompare.Size = new System.Drawing.Size(70, 16); this.chkCompare.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.superTooltip1.SetSuperTooltip(this.chkCompare, new DevComponents.DotNetBar.SuperTooltipInfo("Compare", "", "开启此项后,每更新完一个Wz文件,将会进行新旧文件对比并输出更新报告。有一些额外的选项可以控制对比更新的执行方式。\r\n对比过程出错不会影响补丁继续执行。", null, null, DevComponents.DotNetBar.eTooltipColor.System, true, false, new System.Drawing.Size(238, 130))); this.chkCompare.TabIndex = 10; this.chkCompare.Text = "Compare"; // // panelEx2 // this.panelEx2.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.panelEx2.CanvasColor = System.Drawing.SystemColors.Control; this.panelEx2.ColorSchemeStyle = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.panelEx2.Controls.Add(this.superTabControl1); this.panelEx2.DisabledBackColor = System.Drawing.Color.Empty; this.panelEx2.Location = new System.Drawing.Point(3, 157); this.panelEx2.Name = "panelEx2"; this.panelEx2.Size = new System.Drawing.Size(360, 180); this.panelEx2.Style.Alignment = System.Drawing.StringAlignment.Center; this.panelEx2.Style.BackColor1.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.PanelBackground; this.panelEx2.Style.BackColor2.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.PanelBackground2; this.panelEx2.Style.Border = DevComponents.DotNetBar.eBorderType.SingleLine; this.panelEx2.Style.BorderColor.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.PanelBorder; this.panelEx2.Style.ForeColor.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.PanelText; this.panelEx2.Style.GradientAngle = 90; this.panelEx2.TabIndex = 9; this.panelEx2.Text = "panelEx2"; this.panelEx2.Visible = false; // // superTabControl1 // // // // // // // this.superTabControl1.ControlBox.CloseBox.Name = ""; // // // this.superTabControl1.ControlBox.MenuBox.Name = ""; this.superTabControl1.ControlBox.Name = ""; this.superTabControl1.ControlBox.SubItems.AddRange(new DevComponents.DotNetBar.BaseItem[] { this.superTabControl1.ControlBox.MenuBox, this.superTabControl1.ControlBox.CloseBox}); this.superTabControl1.Controls.Add(this.superTabControlPanel1); this.superTabControl1.Controls.Add(this.superTabControlPanel2); this.superTabControl1.Controls.Add(this.superTabControlPanel3); this.superTabControl1.Dock = System.Windows.Forms.DockStyle.Fill; this.superTabControl1.Location = new System.Drawing.Point(0, 0); this.superTabControl1.Name = "superTabControl1"; this.superTabControl1.ReorderTabsEnabled = true; this.superTabControl1.SelectedTabFont = new System.Drawing.Font("宋体", 9F, System.Drawing.FontStyle.Bold); this.superTabControl1.SelectedTabIndex = 1; this.superTabControl1.Size = new System.Drawing.Size(360, 180); this.superTabControl1.TabAlignment = DevComponents.DotNetBar.eTabStripAlignment.Left; this.superTabControl1.TabFont = new System.Drawing.Font("宋体", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); this.superTabControl1.TabIndex = 2; this.superTabControl1.Tabs.AddRange(new DevComponents.DotNetBar.BaseItem[] { this.superTabItem1, this.superTabItem2, this.superTabItem3}); this.superTabControl1.Text = "superTabControl1"; // // superTabControlPanel1 // this.superTabControlPanel1.Controls.Add(this.txtNotice); this.superTabControlPanel1.Dock = System.Windows.Forms.DockStyle.Fill; this.superTabControlPanel1.Location = new System.Drawing.Point(63, 0); this.superTabControlPanel1.Name = "superTabControlPanel1"; this.superTabControlPanel1.Size = new System.Drawing.Size(297, 180); this.superTabControlPanel1.TabIndex = 1; this.superTabControlPanel1.TabItem = this.superTabItem1; // // txtNotice // // // // this.txtNotice.Border.Class = "TextBoxBorder"; this.txtNotice.Border.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.txtNotice.Dock = System.Windows.Forms.DockStyle.Fill; this.txtNotice.Location = new System.Drawing.Point(0, 0); this.txtNotice.Multiline = true; this.txtNotice.Name = "txtNotice"; this.txtNotice.ReadOnly = true; this.txtNotice.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; this.txtNotice.Size = new System.Drawing.Size(297, 180); this.txtNotice.TabIndex = 0; // // superTabItem1 // this.superTabItem1.AttachedControl = this.superTabControlPanel1; this.superTabItem1.GlobalItem = false; this.superTabItem1.Name = "superTabItem1"; this.superTabItem1.Text = "Notice"; // // superTabControlPanel2 // this.superTabControlPanel2.Controls.Add(this.advTreePatchFiles); this.superTabControlPanel2.Dock = System.Windows.Forms.DockStyle.Fill; this.superTabControlPanel2.Location = new System.Drawing.Point(63, 0); this.superTabControlPanel2.Name = "superTabControlPanel2"; this.superTabControlPanel2.Size = new System.Drawing.Size(257, 180); this.superTabControlPanel2.TabIndex = 0; this.superTabControlPanel2.TabItem = this.superTabItem2; // // advTreePatchFiles // this.advTreePatchFiles.AccessibleRole = System.Windows.Forms.AccessibleRole.Outline; this.advTreePatchFiles.AllowDrop = true; this.advTreePatchFiles.BackColor = System.Drawing.SystemColors.Window; // // // this.advTreePatchFiles.BackgroundStyle.Class = "TreeBorderKey"; this.advTreePatchFiles.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.advTreePatchFiles.Columns.Add(this.columnHeader1); this.advTreePatchFiles.Columns.Add(this.columnHeader2); this.advTreePatchFiles.Columns.Add(this.columnHeader3); this.advTreePatchFiles.Columns.Add(this.columnHeader4); this.advTreePatchFiles.Columns.Add(this.columnHeader5); this.advTreePatchFiles.Columns.Add(this.columnHeader6); this.advTreePatchFiles.Dock = System.Windows.Forms.DockStyle.Fill; this.advTreePatchFiles.DoubleClickTogglesNode = false; this.advTreePatchFiles.DragDropNodeCopyEnabled = false; this.advTreePatchFiles.DropAsChildOffset = 65535; this.advTreePatchFiles.Enabled = false; this.advTreePatchFiles.ExpandWidth = 0; this.advTreePatchFiles.GridRowLines = true; this.advTreePatchFiles.Location = new System.Drawing.Point(0, 0); this.advTreePatchFiles.MultiSelect = true; this.advTreePatchFiles.MultiSelectRule = DevComponents.AdvTree.eMultiSelectRule.AnyNode; this.advTreePatchFiles.Name = "advTreePatchFiles"; this.advTreePatchFiles.NodesConnector = this.nodeConnector1; this.advTreePatchFiles.NodeStyle = this.elementStyle1; this.advTreePatchFiles.PathSeparator = ";"; this.advTreePatchFiles.Size = new System.Drawing.Size(257, 180); this.advTreePatchFiles.Styles.Add(this.elementStyle1); this.advTreePatchFiles.TabIndex = 0; this.advTreePatchFiles.Text = "advTree1"; // // columnHeader1 // this.columnHeader1.Name = "columnHeader1"; this.columnHeader1.Text = "FileName"; this.columnHeader1.Width.Absolute = 100; // // columnHeader2 // this.columnHeader2.Editable = false; this.columnHeader2.Name = "columnHeader2"; this.columnHeader2.Text = "PatchType"; this.columnHeader2.Width.Absolute = 70; // // columnHeader3 // this.columnHeader3.Editable = false; this.columnHeader3.Name = "columnHeader3"; this.columnHeader3.Text = "FileLength"; this.columnHeader3.Width.Absolute = 70; // // columnHeader4 // this.columnHeader4.Editable = false; this.columnHeader4.Name = "columnHeader4"; this.columnHeader4.Text = "Checksum"; this.columnHeader4.Width.Absolute = 70; // // columnHeader5 // this.columnHeader5.Name = "columnHeader5"; this.columnHeader5.Text = "OldChecksum"; this.columnHeader5.Width.Absolute = 70; // // columnHeader6 // this.columnHeader6.Name = "columnHeader6"; this.columnHeader6.Text = "Action"; this.columnHeader6.Width.Absolute = 150; // // nodeConnector1 // this.nodeConnector1.LineColor = System.Drawing.SystemColors.ControlText; // // elementStyle1 // this.elementStyle1.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.elementStyle1.Name = "elementStyle1"; this.elementStyle1.TextColor = System.Drawing.SystemColors.ControlText; // // superTabItem2 // this.superTabItem2.AttachedControl = this.superTabControlPanel2; this.superTabItem2.GlobalItem = false; this.superTabItem2.Name = "superTabItem2"; this.superTabItem2.Text = "Files"; // // superTabControlPanel3 // this.superTabControlPanel3.Controls.Add(this.txtPatchState); this.superTabControlPanel3.Controls.Add(this.progressBarX1); this.superTabControlPanel3.Dock = System.Windows.Forms.DockStyle.Fill; this.superTabControlPanel3.Location = new System.Drawing.Point(63, 0); this.superTabControlPanel3.Name = "superTabControlPanel3"; this.superTabControlPanel3.Size = new System.Drawing.Size(257, 180); this.superTabControlPanel3.TabIndex = 0; this.superTabControlPanel3.TabItem = this.superTabItem3; // // txtPatchState // // // // this.txtPatchState.Border.Class = "TextBoxBorder"; this.txtPatchState.Border.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.txtPatchState.Dock = System.Windows.Forms.DockStyle.Fill; this.txtPatchState.Location = new System.Drawing.Point(0, 0); this.txtPatchState.Multiline = true; this.txtPatchState.Name = "txtPatchState"; this.txtPatchState.ReadOnly = true; this.txtPatchState.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; this.txtPatchState.Size = new System.Drawing.Size(257, 162); this.txtPatchState.TabIndex = 1; // // progressBarX1 // // // // this.progressBarX1.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.progressBarX1.Dock = System.Windows.Forms.DockStyle.Bottom; this.progressBarX1.Location = new System.Drawing.Point(0, 162); this.progressBarX1.Name = "progressBarX1"; this.progressBarX1.Size = new System.Drawing.Size(257, 18); this.progressBarX1.TabIndex = 2; this.progressBarX1.TextVisible = true; // // superTabItem3 // this.superTabItem3.AttachedControl = this.superTabControlPanel3; this.superTabItem3.GlobalItem = false; this.superTabItem3.Name = "superTabItem3"; this.superTabItem3.Text = "State"; // // panelEx1 // this.panelEx1.CanvasColor = System.Drawing.SystemColors.Control; this.panelEx1.ColorSchemeStyle = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.panelEx1.Controls.Add(this.expandablePanel3); this.panelEx1.Controls.Add(this.expandablePanel2); this.panelEx1.Controls.Add(this.expandablePanel1); this.panelEx1.DisabledBackColor = System.Drawing.Color.Empty; this.panelEx1.Dock = System.Windows.Forms.DockStyle.Fill; this.panelEx1.Location = new System.Drawing.Point(0, 0); this.panelEx1.Name = "panelEx1"; this.panelEx1.Size = new System.Drawing.Size(384, 361); this.panelEx1.Style.Alignment = System.Drawing.StringAlignment.Center; this.panelEx1.Style.BackColor1.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.PanelBackground; this.panelEx1.Style.BackColor2.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.PanelBackground2; this.panelEx1.Style.Border = DevComponents.DotNetBar.eBorderType.SingleLine; this.panelEx1.Style.BorderColor.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.PanelBorder; this.panelEx1.Style.ForeColor.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.PanelText; this.panelEx1.Style.GradientAngle = 90; this.panelEx1.TabIndex = 8; // // expandablePanel3 // this.expandablePanel3.CanvasColor = System.Drawing.SystemColors.Control; this.expandablePanel3.ColorSchemeStyle = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.expandablePanel3.Controls.Add(this.buttonXCreate); this.expandablePanel3.Controls.Add(this.txtPatchFile2); this.expandablePanel3.Controls.Add(this.buttonXOpen4); this.expandablePanel3.Controls.Add(this.buttonXOpen3); this.expandablePanel3.Controls.Add(this.txtMSFolder2); this.expandablePanel3.Controls.Add(this.labelX4); this.expandablePanel3.Controls.Add(this.labelX5); this.expandablePanel3.Controls.Add(this.labelX3); this.expandablePanel3.DisabledBackColor = System.Drawing.Color.Empty; this.expandablePanel3.Dock = System.Windows.Forms.DockStyle.Top; this.expandablePanel3.ExpandOnTitleClick = true; this.expandablePanel3.Location = new System.Drawing.Point(0, 244); this.expandablePanel3.Name = "expandablePanel3"; this.expandablePanel3.Size = new System.Drawing.Size(384, 110); this.expandablePanel3.Style.Alignment = System.Drawing.StringAlignment.Center; this.expandablePanel3.Style.BackColor1.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.PanelBackground; this.expandablePanel3.Style.BackColor2.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.PanelBackground2; this.expandablePanel3.Style.Border = DevComponents.DotNetBar.eBorderType.SingleLine; this.expandablePanel3.Style.BorderColor.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.BarDockedBorder; this.expandablePanel3.Style.ForeColor.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.ItemText; this.expandablePanel3.Style.GradientAngle = 90; this.expandablePanel3.TabIndex = 8; this.expandablePanel3.TitleStyle.Alignment = System.Drawing.StringAlignment.Center; this.expandablePanel3.TitleStyle.BackColor1.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.PanelBackground; this.expandablePanel3.TitleStyle.BackColor2.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.PanelBackground2; this.expandablePanel3.TitleStyle.Border = DevComponents.DotNetBar.eBorderType.RaisedInner; this.expandablePanel3.TitleStyle.BorderColor.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.PanelBorder; this.expandablePanel3.TitleStyle.ForeColor.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.PanelText; this.expandablePanel3.TitleStyle.GradientAngle = 90; this.expandablePanel3.TitleText = "Reverse Patcher"; // // buttonXCreate // this.buttonXCreate.AccessibleRole = System.Windows.Forms.AccessibleRole.PushButton; this.buttonXCreate.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.buttonXCreate.ColorTable = DevComponents.DotNetBar.eButtonColor.OrangeWithBackground; this.buttonXCreate.Location = new System.Drawing.Point(315, 82); this.buttonXCreate.Name = "buttonXCreate"; this.buttonXCreate.Size = new System.Drawing.Size(48, 21); this.buttonXCreate.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.buttonXCreate.TabIndex = 12; this.buttonXCreate.Text = "Create"; this.buttonXCreate.Click += new System.EventHandler(this.buttonXCreate_Click); // // txtPatchFile2 // this.txtPatchFile2.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); // // // this.txtPatchFile2.Border.Class = "TextBoxBorder"; this.txtPatchFile2.Border.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.txtPatchFile2.Location = new System.Drawing.Point(68, 34); this.txtPatchFile2.Name = "txtPatchFile2"; this.txtPatchFile2.Size = new System.Drawing.Size(259, 21); this.txtPatchFile2.TabIndex = 6; // // buttonXOpen4 // this.buttonXOpen4.AccessibleRole = System.Windows.Forms.AccessibleRole.PushButton; this.buttonXOpen4.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.buttonXOpen4.ColorTable = DevComponents.DotNetBar.eButtonColor.OrangeWithBackground; this.buttonXOpen4.Location = new System.Drawing.Point(330, 59); this.buttonXOpen4.Name = "buttonXOpen4"; this.buttonXOpen4.Size = new System.Drawing.Size(33, 21); this.buttonXOpen4.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.buttonXOpen4.TabIndex = 10; this.buttonXOpen4.Text = "Open"; this.buttonXOpen4.Click += new System.EventHandler(this.buttonXOpen4_Click); // // buttonXOpen3 // this.buttonXOpen3.AccessibleRole = System.Windows.Forms.AccessibleRole.PushButton; this.buttonXOpen3.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.buttonXOpen3.ColorTable = DevComponents.DotNetBar.eButtonColor.OrangeWithBackground; this.buttonXOpen3.Location = new System.Drawing.Point(330, 34); this.buttonXOpen3.Name = "buttonXOpen3"; this.buttonXOpen3.Size = new System.Drawing.Size(33, 21); this.buttonXOpen3.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.buttonXOpen3.TabIndex = 7; this.buttonXOpen3.Text = "Open"; this.buttonXOpen3.Click += new System.EventHandler(this.buttonXOpen3_Click); // // txtMSFolder2 // this.txtMSFolder2.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); // // // this.txtMSFolder2.Border.Class = "TextBoxBorder"; this.txtMSFolder2.Border.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.txtMSFolder2.Location = new System.Drawing.Point(68, 59); this.txtMSFolder2.Name = "txtMSFolder2"; this.txtMSFolder2.Size = new System.Drawing.Size(259, 21); this.txtMSFolder2.TabIndex = 9; // // labelX4 // this.labelX4.AutoSize = true; this.labelX4.BackColor = System.Drawing.Color.Transparent; // // // this.labelX4.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.labelX4.Location = new System.Drawing.Point(7, 61); this.labelX4.Name = "labelX4"; this.labelX4.Size = new System.Drawing.Size(62, 16); this.labelX4.TabIndex = 11; this.labelX4.Text = "MS Folder"; // // labelX5 // this.labelX5.AutoSize = true; this.labelX5.BackColor = System.Drawing.Color.Transparent; // // // this.labelX5.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.labelX5.Location = new System.Drawing.Point(7, 36); this.labelX5.Name = "labelX5"; this.labelX5.Size = new System.Drawing.Size(62, 16); this.labelX5.TabIndex = 8; this.labelX5.Text = "PatchFile"; // // labelX3 // this.labelX3.AutoSize = true; this.labelX3.BackColor = System.Drawing.Color.Transparent; // // // this.labelX3.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.labelX3.Location = new System.Drawing.Point(12, 86); this.labelX3.Name = "labelX3"; this.labelX3.Size = new System.Drawing.Size(285, 18); this.labelX3.TabIndex = 1; this.labelX3.Text = "这功能太危险了在考虑要不要放出来...还是算了吧"; // // superTooltip1 // this.superTooltip1.DefaultTooltipSettings = new DevComponents.DotNetBar.SuperTooltipInfo("", "", "", null, null, DevComponents.DotNetBar.eTooltipColor.Gray); // // chkResolvePngLink // this.chkResolvePngLink.AutoSize = true; this.chkResolvePngLink.BackColor = System.Drawing.Color.Transparent; // // // this.chkResolvePngLink.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.chkResolvePngLink.Location = new System.Drawing.Point(290, 111); this.chkResolvePngLink.Name = "chkResolvePngLink"; this.chkResolvePngLink.Size = new System.Drawing.Size(95, 16); this.chkResolvePngLink.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.superTooltip1.SetSuperTooltip(this.chkResolvePngLink, new DevComponents.DotNetBar.SuperTooltipInfo("ResolvePngLink", "", "对比报告中是否智能解析对比被Link的图片\r\n这会过滤掉无用的变更内容", null, null, DevComponents.DotNetBar.eTooltipColor.System, true, false, new System.Drawing.Size(180, 90))); this.chkResolvePngLink.TabIndex = 18; this.chkResolvePngLink.Text = "ResolveLink"; // // FrmPatcher // this.ClientSize = new System.Drawing.Size(384, 361); this.Controls.Add(this.panelEx1); this.DoubleBuffered = true; this.MaximizeBox = false; this.MinimizeBox = false; this.Name = "FrmPatcher"; this.Text = "更新装置"; this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.FrmPatcher_FormClosed); ((System.ComponentModel.ISupportInitialize)(this.integerInput1)).EndInit(); this.expandablePanel1.ResumeLayout(false); this.flowLayoutPanel1.ResumeLayout(false); this.expandablePanel2.ResumeLayout(false); this.expandablePanel2.PerformLayout(); this.panelEx2.ResumeLayout(false); ((System.ComponentModel.ISupportInitialize)(this.superTabControl1)).EndInit(); this.superTabControl1.ResumeLayout(false); this.superTabControlPanel1.ResumeLayout(false); this.superTabControlPanel2.ResumeLayout(false); ((System.ComponentModel.ISupportInitialize)(this.advTreePatchFiles)).EndInit(); this.superTabControlPanel3.ResumeLayout(false); this.panelEx1.ResumeLayout(false); this.expandablePanel3.ResumeLayout(false); this.expandablePanel3.PerformLayout(); this.ResumeLayout(false); } #endregion private DevComponents.DotNetBar.Controls.ComboBoxEx comboBoxEx1; private DevComponents.Editors.IntegerInput integerInput1; private DevComponents.DotNetBar.Controls.TextBoxX txtUrl; private DevComponents.DotNetBar.ButtonX buttonXOpen2; private DevComponents.DotNetBar.Controls.TextBoxX txtMSFolder; private DevComponents.DotNetBar.LabelX labelX2; private DevComponents.DotNetBar.ButtonX buttonXOpen1; private DevComponents.DotNetBar.Controls.TextBoxX txtPatchFile; private DevComponents.DotNetBar.LabelX labelX1; private DevComponents.DotNetBar.ButtonX buttonXPatch; private DevComponents.DotNetBar.Controls.CheckBoxX chkDeadPatch; private DevComponents.DotNetBar.Controls.CheckBoxX chkPrePatch; private DevComponents.DotNetBar.ExpandablePanel expandablePanel1; private DevComponents.DotNetBar.ExpandablePanel expandablePanel2; private DevComponents.DotNetBar.PanelEx panelEx1; private DevComponents.DotNetBar.PanelEx panelEx2; private DevComponents.DotNetBar.SuperTabControl superTabControl1; private DevComponents.DotNetBar.SuperTabControlPanel superTabControlPanel2; private DevComponents.AdvTree.AdvTree advTreePatchFiles; private DevComponents.AdvTree.ColumnHeader columnHeader1; private DevComponents.AdvTree.NodeConnector nodeConnector1; private DevComponents.DotNetBar.ElementStyle elementStyle1; private DevComponents.DotNetBar.SuperTabItem superTabItem2; private DevComponents.DotNetBar.SuperTabControlPanel superTabControlPanel3; private DevComponents.DotNetBar.SuperTabItem superTabItem3; private DevComponents.DotNetBar.SuperTabControlPanel superTabControlPanel1; private DevComponents.DotNetBar.Controls.TextBoxX txtNotice; private DevComponents.DotNetBar.SuperTabItem superTabItem1; private DevComponents.AdvTree.ColumnHeader columnHeader2; private DevComponents.DotNetBar.Controls.TextBoxX txtPatchState; private DevComponents.DotNetBar.Controls.CheckBoxX chkCompare; private DevComponents.AdvTree.ColumnHeader columnHeader3; private DevComponents.AdvTree.ColumnHeader columnHeader4; private DevComponents.DotNetBar.Controls.ProgressBarX progressBarX1; private DevComponents.AdvTree.ColumnHeader columnHeader5; private DevComponents.DotNetBar.ButtonX buttonXCheck; private DevComponents.AdvTree.ColumnHeader columnHeader6; private DevComponents.DotNetBar.Controls.ComboBoxEx cmbComparePng; private DevComponents.DotNetBar.Controls.CheckBoxX chkOutputPng; private DevComponents.DotNetBar.SuperTooltip superTooltip1; private DevComponents.DotNetBar.ExpandablePanel expandablePanel3; private DevComponents.DotNetBar.LabelX labelX3; private DevComponents.DotNetBar.Controls.TextBoxX txtPatchFile2; private DevComponents.DotNetBar.ButtonX buttonXOpen4; private DevComponents.DotNetBar.ButtonX buttonXOpen3; private DevComponents.DotNetBar.Controls.TextBoxX txtMSFolder2; private DevComponents.DotNetBar.LabelX labelX4; private DevComponents.DotNetBar.LabelX labelX5; private DevComponents.DotNetBar.Controls.CheckBoxX chkOutputRemovedImg; private DevComponents.DotNetBar.Controls.CheckBoxX chkOutputAddedImg; private DevComponents.DotNetBar.ButtonX buttonXCreate; private DevComponents.DotNetBar.Controls.CheckBoxX chkResolvePngLink; private DevComponents.DotNetBar.Controls.CheckBoxX chkEnableDarkMode; private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel1; } } ================================================ FILE: WzComparerR2/FrmPatcher.cs ================================================ using System; using System.Collections.Generic; using System.Data; using System.Diagnostics; using System.Drawing; using System.IO; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; using DevComponents.AdvTree; using DevComponents.DotNetBar; using DevComponents.Editors; using WzComparerR2.Comparer; using WzComparerR2.Config; using WzComparerR2.Patcher; using WzComparerR2.WzLib; namespace WzComparerR2 { public partial class FrmPatcher : DevComponents.DotNetBar.Office2007Form { public FrmPatcher() { InitializeComponent(); #if NET6_0_OR_GREATER // https://learn.microsoft.com/en-us/dotnet/core/compatibility/fx-core#controldefaultfont-changed-to-segoe-ui-9pt this.Font = new Font(new FontFamily("Microsoft Sans Serif"), 8f); #endif panelEx1.AutoScroll = true; var settings = WcR2Config.Default.PatcherSettings; if (settings.Count <= 0) { settings.Add(new PatcherSetting("KMST", "http://maplestory.dn.nexoncdn.co.kr/PatchT/{1:d5}/{0:d5}to{1:d5}.patch", 2)); settings.Add(new PatcherSetting("KMST-Minor", "http://maplestory.dn.nexoncdn.co.kr/PatchT/{0:d5}/Minor/{1:d2}to{2:d2}.patch", 3)); settings.Add(new PatcherSetting("KMS", "http://maplestory.dn.nexoncdn.co.kr/Patch/{1:d5}/{0:d5}to{1:d5}.patch", 2)); settings.Add(new PatcherSetting("KMS-Minor", "http://maplestory.dn.nexoncdn.co.kr/Patch/{0:d5}/Minor/{1:d2}to{2:d2}.patch", 3)); settings.Add(new PatcherSetting("JMS", "http://webdown2.nexon.co.jp/maple/patch/patchdir/{1:d5}/{0:d5}to{1:d5}.patch", 2)); settings.Add(new PatcherSetting("GMS", "http://download2.nexon.net/Game/MapleStory/patch/patchdir/{1:d5}/CustomPatch{0}to{1}.exe", 2)); settings.Add(new PatcherSetting("TMS", "http://tw.cdnpatch.maplestory.beanfun.com/maplestory/patch/patchdir/{1:d5}/{0:d5}to{1:d5}.patch", 2)); settings.Add(new PatcherSetting("MSEA", "http://patch.maplesea.com/sea/patch/patchdir/{1:d5}/{0:d5}to{1:d5}.patch", 2)); settings.Add(new PatcherSetting("CMS", "http://mxd.clientdown.sdo.com/maplestory/patch/patchdir/{1:d5}/{0:d5}to{1:d5}.patch", 2)); } foreach (PatcherSetting p in settings) { this.MigrateSetting(p); comboBoxEx1.Items.Add(p); } if (comboBoxEx1.Items.Count > 0) comboBoxEx1.SelectedIndex = 0; foreach (WzPngComparison comp in Enum.GetValues(typeof(WzPngComparison))) { cmbComparePng.Items.Add(comp); } cmbComparePng.SelectedItem = WzPngComparison.SizeAndDataLength; } public Encoding PatcherNoticeEncoding { get; set; } private bool isUpdating; private PatcherSession patcherSession; private PatcherSetting SelectedPatcherSetting => comboBoxEx1.SelectedItem as PatcherSetting; private void MigrateSetting(PatcherSetting patcherSetting) { if (patcherSetting.MaxVersion == 0 && patcherSetting.Versions == null) { patcherSetting.MaxVersion = 2; patcherSetting.Versions = new[] { patcherSetting.Version0 ?? 0, patcherSetting.Version1 ?? 0 }; patcherSetting.Version0 = null; patcherSetting.Version1 = null; } if (patcherSetting.Versions != null && patcherSetting.Versions.Length < patcherSetting.MaxVersion) { var newVersions = new int[patcherSetting.MaxVersion]; Array.Copy(patcherSetting.Versions, newVersions, patcherSetting.Versions.Length); patcherSetting.Versions = newVersions; } } private void ApplySetting(PatcherSetting p) { if (isUpdating) { return; } isUpdating = true; try { if (this.flowLayoutPanel1.Controls.Count < p.MaxVersion) { var inputTemplate = this.integerInput1; var preAddedControls = Enumerable.Range(0, p.MaxVersion - this.flowLayoutPanel1.Controls.Count) .Select(_ => { var input = new IntegerInput() { AllowEmptyState = inputTemplate.AllowEmptyState, Size = inputTemplate.Size, Value = 0, MinValue = inputTemplate.MinValue, MaxValue = inputTemplate.MaxValue, DisplayFormat = inputTemplate.DisplayFormat, ShowUpDown = inputTemplate.ShowUpDown, }; input.BackgroundStyle.ApplyStyle(inputTemplate.BackgroundStyle); input.ValueChanged += this.integerInput_ValueChanged; return input; }).ToArray(); this.flowLayoutPanel1.Controls.AddRange(preAddedControls); } for (int i = 0; i < this.flowLayoutPanel1.Controls.Count; i++) { var input = (IntegerInput)this.flowLayoutPanel1.Controls[i]; if (i < p.MaxVersion) { input.Show(); input.Value = (p.Versions != null && i < p.Versions.Length) ? p.Versions[i] : 0; } else { input.Hide(); input.Value = 0; } } this.txtUrl.Text = p.Url; } finally { isUpdating = false; } } private void combineUrl() { if (this.SelectedPatcherSetting is var p) { txtUrl.Text = p.Url; } } private void comboBoxEx1_SelectedIndexChanged(object sender, EventArgs e) { if (this.SelectedPatcherSetting is var p) { this.ApplySetting(p); } } private void integerInput_ValueChanged(object sender, EventArgs e) { if (this.SelectedPatcherSetting is var p && sender is IntegerInput input) { var i = this.flowLayoutPanel1.Controls.IndexOf(input); if (i > -1 && i < p.MaxVersion) { if (p.Versions == null) { p.Versions = new int[p.MaxVersion]; } p.Versions[i] = input.Value; } this.ApplySetting(p); } } private void buttonXCheck_Click(object sender, EventArgs e) { DownloadingItem item = new DownloadingItem(txtUrl.Text, null); try { item.GetFileLength(); if (item.FileLength > 0) { switch (MessageBoxEx.Show(string.Format("文件大小:{0:N0} bytes, 更新时间:{1:yyyy-MM-dd HH:mm:ss}\r\n是否立即开始下载文件?", item.FileLength, item.LastModified), "Patcher", MessageBoxButtons.YesNo)) { case DialogResult.Yes: #if NET6_0_OR_GREATER Process.Start(new ProcessStartInfo { UseShellExecute = true, FileName = txtUrl.Text, }); #else Process.Start(txtUrl.Text); #endif return; case DialogResult.No: return; } } else { MessageBoxEx.Show("文件不存在"); } } catch (Exception ex) { MessageBoxEx.Show("出现错误:" + ex.Message); } } private void FrmPatcher_FormClosed(object sender, FormClosedEventArgs e) { if (this.patcherSession != null && !this.patcherSession.IsCompleted) { this.patcherSession.Cancel(); } ConfigManager.Reload(); WcR2Config.Default.PatcherSettings.Clear(); foreach (PatcherSetting item in comboBoxEx1.Items) { WcR2Config.Default.PatcherSettings.Add(item); } ConfigManager.Save(); } private void NewFile(BinaryReader reader, string fileName, string patchDir) { string tmpFile = Path.Combine(patchDir, fileName); string dir = Path.GetDirectoryName(tmpFile); if (!Directory.Exists(dir)) Directory.CreateDirectory(dir); } private void buttonXOpen1_Click(object sender, EventArgs e) { OpenFileDialog dlg = new OpenFileDialog(); dlg.Title = "请选择补丁文件路径"; dlg.Filter = "*.patch;*.exe|*.patch;*.exe"; if (dlg.ShowDialog(this) == DialogResult.OK) { txtPatchFile.Text = dlg.FileName; } } private void buttonXOpen2_Click(object sender, EventArgs e) { FolderBrowserDialog dlg = new FolderBrowserDialog(); dlg.Description = "请选择冒险岛文件夹路径"; if (dlg.ShowDialog(this) == DialogResult.OK) { txtMSFolder.Text = dlg.SelectedPath; } } private void buttonXPatch_Click(object sender, EventArgs e) { if (this.patcherSession != null) { if (this.patcherSession.State == PatcherTaskState.WaitForContinue) { this.patcherSession.Continue(); return; } else if (!this.patcherSession.PatchExecTask.IsCompleted) { MessageBoxEx.Show("已经开始了一个补丁进程..."); return; } } string compareFolder = null; if (chkCompare.Checked) { FolderBrowserDialog dlg = new FolderBrowserDialog(); dlg.Description = "请选择对比报告输出文件夹"; if (dlg.ShowDialog(this) != DialogResult.OK) { return; } compareFolder = dlg.SelectedPath; } var session = new PatcherSession() { PatchFile = txtPatchFile.Text, MSFolder = txtMSFolder.Text, PrePatch = chkPrePatch.Checked, DeadPatch = chkDeadPatch.Checked, }; session.LoggingFileName = Path.Combine(session.MSFolder, $"wcpatcher_{DateTime.Now:yyyyMMdd_HHmmssfff}.log"); session.PatchExecTask = Task.Run(() => this.ExecutePatchAsync(session, session.CancellationToken)); this.patcherSession = session; } private async Task ExecutePatchAsync(PatcherSession session, CancellationToken cancellationToken) { void AppendStateText(string text) { this.Invoke(new Action(t => this.txtPatchState.AppendText(t)), text); if (session.LoggingFileName != null) { File.AppendAllText(session.LoggingFileName, text, Encoding.UTF8); } } this.Invoke(() => { this.advTreePatchFiles.Nodes.Clear(); this.txtNotice.Clear(); this.txtPatchState.Clear(); this.panelEx2.Visible = true; this.expandablePanel2.Height = 340; }); WzPatcher patcher = null; session.State = PatcherTaskState.Prepatch; try { patcher = new WzPatcher(session.PatchFile); patcher.NoticeEncoding = this.PatcherNoticeEncoding ?? Encoding.Default; patcher.PatchingStateChanged += (o, e) => this.patcher_PatchingStateChanged(o, e, session, AppendStateText); AppendStateText($"补丁文件:{session.PatchFile}\r\n"); AppendStateText("正在检查补丁..."); patcher.OpenDecompress(cancellationToken); AppendStateText("成功\r\n"); if (session.PrePatch) { AppendStateText("正在预读补丁...\r\n"); long decompressedSize = patcher.PrePatch(cancellationToken); if (patcher.IsKMST1125Format.Value) { AppendStateText("补丁类型:KMST1125\r\n"); if (patcher.OldFileHash != null) { AppendStateText($"获取原文件信息:{patcher.OldFileHash.Count} 个\r\n"); } } AppendStateText(string.Format("补丁大小: {0:N0} bytes...\r\n", decompressedSize)); AppendStateText(string.Format("文件变动: {0} 个...\r\n", patcher.PatchParts.Count)); this.Invoke(() => { this.advTreePatchFiles.BeginUpdate(); this.txtNotice.Text = patcher.NoticeText; foreach (PatchPartContext part in patcher.PatchParts) { this.advTreePatchFiles.Nodes.Add(CreateFileNode(part)); } this.advTreePatchFiles.Enabled = true; this.advTreePatchFiles.EndUpdate(); }); AppendStateText("等待调整更新顺序...\r\n"); session.State = PatcherTaskState.WaitForContinue; await session.WaitForContinueAsync(); this.Invoke(() => { this.advTreePatchFiles.Enabled = false; }); session.State = PatcherTaskState.Patching; patcher.PatchParts.Clear(); foreach (Node node in this.advTreePatchFiles.Nodes) { if (node.Checked && node.Tag is PatchPartContext part) { patcher.PatchParts.Add(part); } } if (patcher.IsKMST1125Format.Value && session.DeadPatch) { AppendStateText("生成deadPatch执行计划:\r\n"); session.deadPatchExecutionPlan = new(); session.deadPatchExecutionPlan.Build(patcher.PatchParts); foreach (var part in patcher.PatchParts) { if (session.deadPatchExecutionPlan.Check(part.FileName, out var filesCanInstantUpdate)) { AppendStateText($"+ 执行文件{part.FileName}\r\n"); foreach (var fileName in filesCanInstantUpdate) { AppendStateText($" - 应用文件{fileName}\r\n"); } } else { AppendStateText($"- 执行文件{part.FileName},但延迟应用\r\n"); } } // disable force validation patcher.ThrowOnValidationFailed = false; } } AppendStateText("开始更新\r\n"); var sw = Stopwatch.StartNew(); patcher.Patch(session.MSFolder, cancellationToken); sw.Stop(); AppendStateText("完成\r\n"); session.State = PatcherTaskState.Complete; MessageBoxEx.Show(this, "补丁结束,用时" + sw.Elapsed, "Patcher"); } catch (OperationCanceledException) { MessageBoxEx.Show(this.Owner, "补丁中止。", "Patcher"); } catch (UnauthorizedAccessException ex) { // File IO permission error MessageBoxEx.Show(this, ex.ToString(), "Patcher"); } catch (Exception ex) { AppendStateText(ex.ToString()); MessageBoxEx.Show(this, ex.ToString(), "Patcher"); } finally { session.State = PatcherTaskState.Complete; if (patcher != null) { patcher.Close(); patcher = null; } GC.Collect(); panelEx2.Visible = false; expandablePanel2.Height = 157; } } private void patcher_PatchingStateChanged(object sender, PatchingEventArgs e, PatcherSession session, Action logFunc) { switch (e.State) { case PatchingState.PatchStart: logFunc("开始更新" + e.Part.FileName + "\r\n"); break; case PatchingState.VerifyOldChecksumBegin: logFunc(" 检查旧文件checksum..."); break; case PatchingState.VerifyOldChecksumEnd: logFunc(" 结束\r\n"); break; case PatchingState.VerifyNewChecksumBegin: logFunc(" 检查新文件checksum..."); break; case PatchingState.VerifyNewChecksumEnd: logFunc(" 结束\r\n"); break; case PatchingState.TempFileCreated: logFunc(" 创建临时文件...\r\n"); progressBarX1.Maximum = e.Part.NewFileLength; session.TemporaryFileMapping.Add(e.Part.FileName, e.Part.TempFilePath); break; case PatchingState.TempFileBuildProcessChanged: progressBarX1.Value = (int)e.CurrentFileLength; progressBarX1.Text = string.Format("{0:N0}/{1:N0}", e.CurrentFileLength, e.Part.NewFileLength); break; case PatchingState.TempFileClosed: logFunc(" 关闭临时文件...\r\n"); progressBarX1.Value = 0; progressBarX1.Maximum = 0; progressBarX1.Text = string.Empty; if (!string.IsNullOrEmpty(session.CompareFolder) && e.Part.Type == 1 && Path.GetExtension(e.Part.FileName).Equals(".wz", StringComparison.OrdinalIgnoreCase) && !Path.GetFileName(e.Part.FileName).Equals("list.wz", StringComparison.OrdinalIgnoreCase)) { Wz_Structure wznew = new Wz_Structure(); Wz_Structure wzold = new Wz_Structure(); try { logFunc(" (comparer)正在对比文件...\r\n"); EasyComparer comparer = new EasyComparer(); comparer.OutputPng = chkOutputPng.Checked; comparer.OutputAddedImg = chkOutputAddedImg.Checked; comparer.OutputRemovedImg = chkOutputRemovedImg.Checked; comparer.Comparer.PngComparison = (WzPngComparison)cmbComparePng.SelectedItem; comparer.Comparer.ResolvePngLink = chkResolvePngLink.Checked; comparer.ColorTable = new List() { CustomCSSConfig.Default.BackgroundColor, CustomCSSConfig.Default.NormalTextColor, CustomCSSConfig.Default.ChangedBackgroundColor, CustomCSSConfig.Default.AddedBackgroundColor, CustomCSSConfig.Default.RemovedBackgroundColor, CustomCSSConfig.Default.ChangedTextColor, CustomCSSConfig.Default.AddedTextColor, CustomCSSConfig.Default.RemovedTextColor, CustomCSSConfig.Default.HyperlinkColor }; wznew.Load(e.Part.TempFilePath, false); wzold.Load(e.Part.OldFilePath, false); comparer.EasyCompareWzFiles(wznew.wz_files[0], wzold.wz_files[0], session.CompareFolder); } catch (Exception ex) { txtPatchState.AppendText(ex.ToString()); } finally { wznew.Clear(); wzold.Clear(); GC.Collect(); } } if (session.DeadPatch && e.Part.Type == 1 && sender is WzPatcher patcher) { if (patcher.IsKMST1125Format.Value) { if (session.deadPatchExecutionPlan?.Check(e.Part.FileName, out var filesCanInstantUpdate) ?? false) { foreach (string fileName in filesCanInstantUpdate) { if (session.TemporaryFileMapping.TryGetValue(fileName, out var temporaryFileName)) { logFunc($" (deadpatch)正在应用文件{fileName}...\r\n"); patcher.SafeMove(temporaryFileName, Path.Combine(session.MSFolder, fileName)); } } } else { logFunc(" (deadpatch)延迟应用文件...\r\n"); } } else { logFunc(" (deadpatch)正在应用文件...\r\n"); patcher.SafeMove(e.Part.TempFilePath, e.Part.OldFilePath); } } break; case PatchingState.PrepareVerifyOldChecksumBegin: logFunc($"预检查旧文件checksum: {e.Part.FileName}"); break; case PatchingState.PrepareVerifyOldChecksumEnd: if (e.Part.OldChecksum != e.Part.OldChecksumActual) { logFunc(" 不一致\r\n"); } else { logFunc(" 结束\r\n"); } break; case PatchingState.ApplyFile: logFunc($"应用文件: {e.Part.FileName}\r\n"); break; case PatchingState.FileSkipped: logFunc(" 跳过" + e.Part.FileName + "\r\n"); break; } } private Node CreateFileNode(PatchPartContext part) { Node node = new Node(part.FileName) { CheckBoxVisible = true, Checked = true }; ElementStyle style = new ElementStyle(); style.TextAlignment = eStyleTextAlignment.Far; node.Cells.Add(new Cell(part.Type.ToString(), style)); node.Cells.Add(new Cell(part.NewFileLength.ToString("n0"), style)); node.Cells.Add(new Cell(part.NewChecksum.ToString("x8"), style)); node.Cells.Add(new Cell(part.OldChecksum?.ToString("x8"), style)); if (part.Type == 1) { string text = string.Format("{0}|{1}|{2}|{3}", part.Action0, part.Action1, part.Action2, part.DependencyFiles.Count); node.Cells.Add(new Cell(text, style)); } node.Tag = part; return node; } private void buttonXOpen3_Click(object sender, EventArgs e) { OpenFileDialog dlg = new OpenFileDialog(); dlg.Title = "请选择补丁文件路径"; dlg.Filter = "*.patch;*.exe|*.patch;*.exe"; if (dlg.ShowDialog(this) == DialogResult.OK) { txtPatchFile2.Text = dlg.FileName; } } private void buttonXOpen4_Click(object sender, EventArgs e) { FolderBrowserDialog dlg = new FolderBrowserDialog(); dlg.Description = "请选择冒险岛文件夹路径"; if (dlg.ShowDialog(this) == DialogResult.OK) { txtMSFolder2.Text = dlg.SelectedPath; } } private void buttonXCreate_Click(object sender, EventArgs e) { MessageBoxEx.Show(@"> 这是一个测试功能... > 还没完成 所以请选择patch文件 exe补丁暂时懒得分离 > 没有检查原客户端版本 为了正确执行请预先确认 > 暂时不提供文件块的筛选或文件缺失提示 > 没优化 于是可能生成文件体积较大 但是几乎可以保证完整性 > 对于KMST1125后无法正常工作", "声明"); SaveFileDialog dlg = new SaveFileDialog(); dlg.Filter = "*.patch|*.patch"; dlg.Title = "选择输出文件"; dlg.CheckFileExists = false; dlg.InitialDirectory = Path.GetDirectoryName(txtPatchFile2.Text); dlg.FileName = Path.GetFileNameWithoutExtension(txtPatchFile2.Text) + "_reverse.patch"; if (dlg.ShowDialog(this) == DialogResult.OK) { try { ReversePatcherBuilder builder = new ReversePatcherBuilder(); builder.msDir = txtMSFolder2.Text; builder.patchFileName = txtPatchFile2.Text; builder.outputFileName = dlg.FileName; builder.Build(); } catch (Exception ex) { } } } class PatcherSession { public PatcherSession() { this.cancellationTokenSource = new CancellationTokenSource(); } public string PatchFile; public string MSFolder; public string CompareFolder; public bool PrePatch; public bool DeadPatch; public Task PatchExecTask; public string LoggingFileName; public PatcherTaskState State; public DeadPatchExecutionPlan deadPatchExecutionPlan; public Dictionary TemporaryFileMapping = new (); public CancellationToken CancellationToken => this.cancellationTokenSource.Token; private CancellationTokenSource cancellationTokenSource; private TaskCompletionSource tcsWaiting; public bool IsCompleted => this.PatchExecTask?.IsCompleted ?? true; public void Cancel() { this.cancellationTokenSource.Cancel(); } public async Task WaitForContinueAsync() { var tcs = new TaskCompletionSource(); this.tcsWaiting = tcs; this.cancellationTokenSource.Token.Register(() => tcs.TrySetCanceled()); await tcs.Task; } public void Continue() { if (this.tcsWaiting != null) { this.tcsWaiting.SetResult(true); } } } enum PatcherTaskState { NotStarted = 0, Prepatch = 1, WaitForContinue = 2, Patching = 3, Complete = 4, } class DeadPatchExecutionPlan { public DeadPatchExecutionPlan() { this.FileUpdateDependencies = new Dictionary>(); } public Dictionary> FileUpdateDependencies { get; private set; } public void Build(IEnumerable orderedParts) { /* * for examle: * fileName | type | dependencies * -----------|------|--------------- * Mob_000.wz | 1 | Mob_000.wz (self update) * Mob_001.wz | 1 | Mob_001.wz, Mob_002.wz (merge data) * Mob_002.wz | 1 | Mob_001.wz, Mob_002.wz (merge data) * Mob_003.wz | 1 | Mob_001.wz, Mob_002.wz (balance size from other file) * * fileLastDependecy: * key | value * -----------|---------------- * Mob_000.wz | Mob_000.wz * Mob_001.wz | Mob_003.wz * Mob_002.wz | Mob_003.wz * Mob_003.wz | Mob_003.wz * * FileUpdateDependencies: * key | value * -----------|---------------- * Mob_000.wz | Mob000.wz * Mob_003.wz | Mob001.wz, Mob002.wz, Mob003.wz */ // find the last dependency Dictionary fileLastDependecy = new(); foreach (var part in orderedParts) { if (part.Type == 0) { fileLastDependecy[part.FileName] = part.FileName; } else if (part.Type == 1) { fileLastDependecy[part.FileName] = part.FileName; foreach (var dep in part.DependencyFiles) { fileLastDependecy[dep] = part.FileName; } } } // reverse key and value this.FileUpdateDependencies.Clear(); foreach (var grp in fileLastDependecy.GroupBy(kv => kv.Value, kv => kv.Key)) { this.FileUpdateDependencies.Add(grp.Key, grp.ToList()); } } public bool Check(string fileName, out IReadOnlyList filesCanInstantUpdate) { if (this.FileUpdateDependencies.TryGetValue(fileName, out var value) && value != null && value.Count > 0) { filesCanInstantUpdate = value; return true; } filesCanInstantUpdate = null; return false; } } } } ================================================ FILE: WzComparerR2/FrmPatcher.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 163, 55 17, 17 146, 17 275, 17 404, 17 533, 17 17, 55 ================================================ FILE: WzComparerR2/FrmQuickViewSetting.Designer.cs ================================================ namespace WzComparerR2 { partial class FrmQuickViewSetting { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { this.superTabControl1 = new DevComponents.DotNetBar.SuperTabControl(); this.superTabControlPanel1 = new DevComponents.DotNetBar.SuperTabControlPanel(); this.checkBoxX10 = new DevComponents.DotNetBar.Controls.CheckBoxX(); this.labelX3 = new DevComponents.DotNetBar.LabelX(); this.comboBoxEx2 = new DevComponents.DotNetBar.Controls.ComboBoxEx(); this.comboItem5 = new DevComponents.Editors.ComboItem(); this.comboItem6 = new DevComponents.Editors.ComboItem(); this.comboItem7 = new DevComponents.Editors.ComboItem(); this.comboItem8 = new DevComponents.Editors.ComboItem(); this.comboItem9 = new DevComponents.Editors.ComboItem(); this.comboItem10 = new DevComponents.Editors.ComboItem(); this.labelX2 = new DevComponents.DotNetBar.LabelX(); this.labelX1 = new DevComponents.DotNetBar.LabelX(); this.comboBoxEx1 = new DevComponents.DotNetBar.Controls.ComboBoxEx(); this.comboItem1 = new DevComponents.Editors.ComboItem(); this.comboItem2 = new DevComponents.Editors.ComboItem(); this.comboItem3 = new DevComponents.Editors.ComboItem(); this.comboItem4 = new DevComponents.Editors.ComboItem(); this.checkBoxX2 = new DevComponents.DotNetBar.Controls.CheckBoxX(); this.checkBoxX1 = new DevComponents.DotNetBar.Controls.CheckBoxX(); this.superTabItem1 = new DevComponents.DotNetBar.SuperTabItem(); this.superTabControlPanel4 = new DevComponents.DotNetBar.SuperTabControlPanel(); this.checkBoxX7 = new DevComponents.DotNetBar.Controls.CheckBoxX(); this.superTabItem4 = new DevComponents.DotNetBar.SuperTabItem(); this.superTabControlPanel3 = new DevComponents.DotNetBar.SuperTabControlPanel(); this.checkBoxX9 = new DevComponents.DotNetBar.Controls.CheckBoxX(); this.checkBoxX8 = new DevComponents.DotNetBar.Controls.CheckBoxX(); this.checkBoxX5 = new DevComponents.DotNetBar.Controls.CheckBoxX(); this.superTabItem3 = new DevComponents.DotNetBar.SuperTabItem(); this.superTabControlPanel2 = new DevComponents.DotNetBar.SuperTabControlPanel(); this.checkBoxX6 = new DevComponents.DotNetBar.Controls.CheckBoxX(); this.checkBoxX4 = new DevComponents.DotNetBar.Controls.CheckBoxX(); this.checkBoxX3 = new DevComponents.DotNetBar.Controls.CheckBoxX(); this.superTabItem2 = new DevComponents.DotNetBar.SuperTabItem(); this.panelEx1 = new DevComponents.DotNetBar.PanelEx(); this.buttonX2 = new DevComponents.DotNetBar.ButtonX(); this.buttonX1 = new DevComponents.DotNetBar.ButtonX(); this.checkBoxX11 = new DevComponents.DotNetBar.Controls.CheckBoxX(); this.checkBoxX12 = new DevComponents.DotNetBar.Controls.CheckBoxX(); this.checkBoxX13 = new DevComponents.DotNetBar.Controls.CheckBoxX(); this.checkBoxX14 = new DevComponents.DotNetBar.Controls.CheckBoxX(); this.checkBoxX15 = new DevComponents.DotNetBar.Controls.CheckBoxX(); this.superTabControlPanel5 = new DevComponents.DotNetBar.SuperTabControlPanel(); this.superTabItem5 = new DevComponents.DotNetBar.SuperTabItem(); this.chkShowDamageSkinID = new DevComponents.DotNetBar.Controls.CheckBoxX(); this.chkShowDamageSkin = new DevComponents.DotNetBar.Controls.CheckBoxX(); this.chkUseMiniSize = new DevComponents.DotNetBar.Controls.CheckBoxX(); this.chkAlwaysUseMseaFormat = new DevComponents.DotNetBar.Controls.CheckBoxX(); this.chkDisplayUnitOnSingleLine = new DevComponents.DotNetBar.Controls.CheckBoxX(); this.lblDamageSkinNumber = new DevComponents.DotNetBar.LabelX(); this.txtDamageSkinNumber = new DevComponents.DotNetBar.Controls.TextBoxX(); ((System.ComponentModel.ISupportInitialize)(this.superTabControl1)).BeginInit(); this.superTabControl1.SuspendLayout(); this.superTabControlPanel1.SuspendLayout(); this.superTabControlPanel4.SuspendLayout(); this.superTabControlPanel3.SuspendLayout(); this.superTabControlPanel2.SuspendLayout(); this.superTabControlPanel5.SuspendLayout(); this.panelEx1.SuspendLayout(); this.SuspendLayout(); // // superTabControl1 // this.superTabControl1.CloseButtonOnTabsAlwaysDisplayed = false; // // // // // // this.superTabControl1.ControlBox.CloseBox.Name = ""; // // // this.superTabControl1.ControlBox.MenuBox.Name = ""; this.superTabControl1.ControlBox.Name = ""; this.superTabControl1.ControlBox.SubItems.AddRange(new DevComponents.DotNetBar.BaseItem[] { this.superTabControl1.ControlBox.MenuBox, this.superTabControl1.ControlBox.CloseBox}); this.superTabControl1.Controls.Add(this.superTabControlPanel2); this.superTabControl1.Controls.Add(this.superTabControlPanel3); this.superTabControl1.Controls.Add(this.superTabControlPanel1); this.superTabControl1.Controls.Add(this.superTabControlPanel4); this.superTabControl1.Controls.Add(this.superTabControlPanel5); this.superTabControl1.Dock = System.Windows.Forms.DockStyle.Fill; this.superTabControl1.Location = new System.Drawing.Point(0, 0); this.superTabControl1.Name = "superTabControl1"; this.superTabControl1.ReorderTabsEnabled = true; this.superTabControl1.SelectedTabFont = new System.Drawing.Font("宋体", 9F, System.Drawing.FontStyle.Bold); this.superTabControl1.SelectedTabIndex = 0; this.superTabControl1.Size = new System.Drawing.Size(304, 251); this.superTabControl1.TabAlignment = DevComponents.DotNetBar.eTabStripAlignment.Left; this.superTabControl1.TabFont = new System.Drawing.Font("宋体", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); this.superTabControl1.TabIndex = 0; this.superTabControl1.Tabs.AddRange(new DevComponents.DotNetBar.BaseItem[] { this.superTabItem1, this.superTabItem2, this.superTabItem3, this.superTabItem4, this.superTabItem5}); this.superTabControl1.Text = "superTabControl1"; // // superTabControlPanel1 // this.superTabControlPanel1.Controls.Add(this.checkBoxX15); this.superTabControlPanel1.Controls.Add(this.checkBoxX14); this.superTabControlPanel1.Controls.Add(this.checkBoxX13); this.superTabControlPanel1.Controls.Add(this.checkBoxX10); this.superTabControlPanel1.Controls.Add(this.labelX3); this.superTabControlPanel1.Controls.Add(this.comboBoxEx2); this.superTabControlPanel1.Controls.Add(this.labelX2); this.superTabControlPanel1.Controls.Add(this.labelX1); this.superTabControlPanel1.Controls.Add(this.comboBoxEx1); this.superTabControlPanel1.Controls.Add(this.checkBoxX2); this.superTabControlPanel1.Controls.Add(this.checkBoxX1); this.superTabControlPanel1.Dock = System.Windows.Forms.DockStyle.Fill; this.superTabControlPanel1.Location = new System.Drawing.Point(62, 0); this.superTabControlPanel1.Name = "superTabControlPanel1"; this.superTabControlPanel1.Size = new System.Drawing.Size(242, 251); this.superTabControlPanel1.TabIndex = 1; this.superTabControlPanel1.TabItem = this.superTabItem1; // // checkBoxX10 // this.checkBoxX10.AutoSize = true; this.checkBoxX10.BackColor = System.Drawing.Color.Transparent; // // // this.checkBoxX10.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.checkBoxX10.Location = new System.Drawing.Point(13, 9); this.checkBoxX10.Name = "checkBoxX10"; this.checkBoxX10.Size = new System.Drawing.Size(125, 18); this.checkBoxX10.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.checkBoxX10.TabIndex = 7; this.checkBoxX10.Text = "显示技能扩展属性"; // // labelX3 // this.labelX3.AutoSize = true; this.labelX3.BackColor = System.Drawing.Color.Transparent; // // // this.labelX3.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.labelX3.ForeColor = System.Drawing.SystemColors.ControlDarkDark; this.labelX3.Location = new System.Drawing.Point(13, 211); this.labelX3.Name = "labelX3"; this.labelX3.Size = new System.Drawing.Size(142, 30); this.labelX3.TabIndex = 6; this.labelX3.Text = "对tooltip窗口为焦点,
\r\n使用 - + [ ] 键调整等级
\r\n使用 PgUp / PgDn 键调整适用职业"; // // comboBoxEx2 // this.comboBoxEx2.DisplayMember = "Text"; this.comboBoxEx2.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawFixed; this.comboBoxEx2.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.comboBoxEx2.FormattingEnabled = true; this.comboBoxEx2.ItemHeight = 15; this.comboBoxEx2.Items.AddRange(new object[] { this.comboItem5, this.comboItem6, this.comboItem7, this.comboItem8, this.comboItem9, this.comboItem10}); this.comboBoxEx2.Location = new System.Drawing.Point(94, 179); this.comboBoxEx2.Name = "comboBoxEx2"; this.comboBoxEx2.Size = new System.Drawing.Size(90, 21); this.comboBoxEx2.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.comboBoxEx2.TabIndex = 5; // // comboItem5 // this.comboItem5.Text = "1"; // // comboItem6 // this.comboItem6.Text = "5"; // // comboItem7 // this.comboItem7.Text = "10"; // // comboItem8 // this.comboItem8.Text = "15"; // // comboItem9 // this.comboItem9.Text = "30"; // // comboItem10 // this.comboItem10.Text = "32"; // // labelX2 // this.labelX2.AutoSize = true; this.labelX2.BackColor = System.Drawing.Color.Transparent; // // // this.labelX2.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.labelX2.Location = new System.Drawing.Point(13, 182); this.labelX2.Name = "labelX2"; this.labelX2.Size = new System.Drawing.Size(81, 18); this.labelX2.TabIndex = 4; this.labelX2.Text = "调整等级间隔"; // // labelX1 // this.labelX1.AutoSize = true; this.labelX1.BackColor = System.Drawing.Color.Transparent; // // // this.labelX1.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.labelX1.Location = new System.Drawing.Point(13, 157); this.labelX1.Name = "labelX1"; this.labelX1.Size = new System.Drawing.Size(81, 18); this.labelX1.TabIndex = 3; this.labelX1.Text = "默认技能等级"; // // comboBoxEx1 // this.comboBoxEx1.DisplayMember = "Text"; this.comboBoxEx1.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawFixed; this.comboBoxEx1.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.comboBoxEx1.FormattingEnabled = true; this.comboBoxEx1.ItemHeight = 15; this.comboBoxEx1.Items.AddRange(new object[] { this.comboItem1, this.comboItem2, this.comboItem3, this.comboItem4}); this.comboBoxEx1.Location = new System.Drawing.Point(94, 154); this.comboBoxEx1.Name = "comboBoxEx1"; this.comboBoxEx1.Size = new System.Drawing.Size(90, 21); this.comboBoxEx1.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.comboBoxEx1.TabIndex = 2; // // comboItem1 // this.comboItem1.Text = "Lv.0"; // // comboItem2 // this.comboItem2.Text = "Lv.1"; // // comboItem3 // this.comboItem3.Text = "Lv.Max"; // // comboItem4 // this.comboItem4.Text = "Lv.Max+2"; // // checkBoxX2 // this.checkBoxX2.AutoSize = true; this.checkBoxX2.BackColor = System.Drawing.Color.Transparent; // // // this.checkBoxX2.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.checkBoxX2.Location = new System.Drawing.Point(13, 57); this.checkBoxX2.Name = "checkBoxX2"; this.checkBoxX2.Size = new System.Drawing.Size(125, 18); this.checkBoxX2.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.checkBoxX2.TabIndex = 1; this.checkBoxX2.Text = "显示技能动作延时"; // // checkBoxX1 // this.checkBoxX1.AutoSize = true; this.checkBoxX1.BackColor = System.Drawing.Color.Transparent; // // // this.checkBoxX1.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.checkBoxX1.Location = new System.Drawing.Point(13, 33); this.checkBoxX1.Name = "checkBoxX1"; this.checkBoxX1.Size = new System.Drawing.Size(101, 18); this.checkBoxX1.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.checkBoxX1.TabIndex = 0; this.checkBoxX1.Text = "显示技能代码"; // // superTabItem1 // this.superTabItem1.AttachedControl = this.superTabControlPanel1; this.superTabItem1.GlobalItem = false; this.superTabItem1.Name = "superTabItem1"; this.superTabItem1.Text = "Skill"; // // superTabControlPanel4 // this.superTabControlPanel4.Controls.Add(this.checkBoxX7); this.superTabControlPanel4.Dock = System.Windows.Forms.DockStyle.Fill; this.superTabControlPanel4.Location = new System.Drawing.Point(63, 0); this.superTabControlPanel4.Name = "superTabControlPanel4"; this.superTabControlPanel4.Size = new System.Drawing.Size(241, 231); this.superTabControlPanel4.TabIndex = 0; this.superTabControlPanel4.TabItem = this.superTabItem4; // // checkBoxX7 // this.checkBoxX7.AutoSize = true; this.checkBoxX7.BackColor = System.Drawing.Color.Transparent; // // // this.checkBoxX7.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.checkBoxX7.Location = new System.Drawing.Point(13, 12); this.checkBoxX7.Name = "checkBoxX7"; this.checkBoxX7.Size = new System.Drawing.Size(101, 18); this.checkBoxX7.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.checkBoxX7.TabIndex = 3; this.checkBoxX7.Text = "显示制造代码"; // // superTabItem4 // this.superTabItem4.AttachedControl = this.superTabControlPanel4; this.superTabItem4.GlobalItem = false; this.superTabItem4.Name = "superTabItem4"; this.superTabItem4.Text = "Recipe"; // // superTabControlPanel3 // this.superTabControlPanel3.Controls.Add(this.checkBoxX12); this.superTabControlPanel3.Controls.Add(this.checkBoxX9); this.superTabControlPanel3.Controls.Add(this.checkBoxX8); this.superTabControlPanel3.Controls.Add(this.checkBoxX5); this.superTabControlPanel3.Dock = System.Windows.Forms.DockStyle.Fill; this.superTabControlPanel3.Location = new System.Drawing.Point(62, 0); this.superTabControlPanel3.Name = "superTabControlPanel3"; this.superTabControlPanel3.Size = new System.Drawing.Size(242, 231); this.superTabControlPanel3.TabIndex = 0; this.superTabControlPanel3.TabItem = this.superTabItem3; // // checkBoxX12 // this.checkBoxX12.AutoSize = true; this.checkBoxX12.BackColor = System.Drawing.Color.Transparent; // // // this.checkBoxX12.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.checkBoxX12.Location = new System.Drawing.Point(13, 84); this.checkBoxX12.Name = "checkBoxX12"; this.checkBoxX12.Size = new System.Drawing.Size(101, 18); this.checkBoxX12.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.checkBoxX12.TabIndex = 5; this.checkBoxX12.Text = "显示称号样式"; // // checkBoxX9 // this.checkBoxX9.AutoSize = true; this.checkBoxX9.BackColor = System.Drawing.Color.Transparent; // // // this.checkBoxX9.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.checkBoxX9.Location = new System.Drawing.Point(13, 60); this.checkBoxX9.Name = "checkBoxX9"; this.checkBoxX9.Size = new System.Drawing.Size(101, 18); this.checkBoxX9.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.checkBoxX9.TabIndex = 4; this.checkBoxX9.Text = "图纸联动道具"; // // checkBoxX8 // this.checkBoxX8.AutoSize = true; this.checkBoxX8.BackColor = System.Drawing.Color.Transparent; // // // this.checkBoxX8.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.checkBoxX8.Location = new System.Drawing.Point(13, 36); this.checkBoxX8.Name = "checkBoxX8"; this.checkBoxX8.Size = new System.Drawing.Size(101, 18); this.checkBoxX8.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.checkBoxX8.TabIndex = 3; this.checkBoxX8.Text = "图纸联动配方"; // // checkBoxX5 // this.checkBoxX5.AutoSize = true; this.checkBoxX5.BackColor = System.Drawing.Color.Transparent; // // // this.checkBoxX5.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.checkBoxX5.Location = new System.Drawing.Point(13, 12); this.checkBoxX5.Name = "checkBoxX5"; this.checkBoxX5.Size = new System.Drawing.Size(101, 18); this.checkBoxX5.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.checkBoxX5.TabIndex = 2; this.checkBoxX5.Text = "显示物品代码"; // // superTabItem3 // this.superTabItem3.AttachedControl = this.superTabControlPanel3; this.superTabItem3.GlobalItem = false; this.superTabItem3.Name = "superTabItem3"; this.superTabItem3.Text = "Item"; // // superTabControlPanel2 // this.superTabControlPanel2.Controls.Add(this.checkBoxX11); this.superTabControlPanel2.Controls.Add(this.checkBoxX6); this.superTabControlPanel2.Controls.Add(this.checkBoxX4); this.superTabControlPanel2.Controls.Add(this.checkBoxX3); this.superTabControlPanel2.Dock = System.Windows.Forms.DockStyle.Fill; this.superTabControlPanel2.Location = new System.Drawing.Point(62, 0); this.superTabControlPanel2.Name = "superTabControlPanel2"; this.superTabControlPanel2.Size = new System.Drawing.Size(242, 231); this.superTabControlPanel2.TabIndex = 0; this.superTabControlPanel2.TabItem = this.superTabItem2; // // checkBoxX6 // this.checkBoxX6.AutoSize = true; this.checkBoxX6.BackColor = System.Drawing.Color.Transparent; // // // this.checkBoxX6.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.checkBoxX6.Location = new System.Drawing.Point(13, 60); this.checkBoxX6.Name = "checkBoxX6"; this.checkBoxX6.Size = new System.Drawing.Size(125, 18); this.checkBoxX6.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.checkBoxX6.TabIndex = 3; this.checkBoxX6.Text = "显示装备成长信息"; // // checkBoxX4 // this.checkBoxX4.AutoSize = true; this.checkBoxX4.BackColor = System.Drawing.Color.Transparent; // // // this.checkBoxX4.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.checkBoxX4.Location = new System.Drawing.Point(13, 36); this.checkBoxX4.Name = "checkBoxX4"; this.checkBoxX4.Size = new System.Drawing.Size(101, 18); this.checkBoxX4.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.checkBoxX4.TabIndex = 2; this.checkBoxX4.Text = "显示武器攻速"; // // checkBoxX3 // this.checkBoxX3.AutoSize = true; this.checkBoxX3.BackColor = System.Drawing.Color.Transparent; // // // this.checkBoxX3.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.checkBoxX3.Location = new System.Drawing.Point(13, 12); this.checkBoxX3.Name = "checkBoxX3"; this.checkBoxX3.Size = new System.Drawing.Size(101, 18); this.checkBoxX3.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.checkBoxX3.TabIndex = 1; this.checkBoxX3.Text = "显示装备代码"; // // superTabItem2 // this.superTabItem2.AttachedControl = this.superTabControlPanel2; this.superTabItem2.GlobalItem = false; this.superTabItem2.Name = "superTabItem2"; this.superTabItem2.Text = "Gear"; // // panelEx1 // this.panelEx1.CanvasColor = System.Drawing.SystemColors.Control; this.panelEx1.ColorSchemeStyle = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.panelEx1.Controls.Add(this.buttonX2); this.panelEx1.Controls.Add(this.buttonX1); this.panelEx1.DisabledBackColor = System.Drawing.Color.Empty; this.panelEx1.Dock = System.Windows.Forms.DockStyle.Bottom; this.panelEx1.Location = new System.Drawing.Point(0, 251); this.panelEx1.Name = "panelEx1"; this.panelEx1.Size = new System.Drawing.Size(304, 30); this.panelEx1.Style.Alignment = System.Drawing.StringAlignment.Center; this.panelEx1.Style.BackColor1.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.PanelBackground; this.panelEx1.Style.BackColor2.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.PanelBackground2; this.panelEx1.Style.Border = DevComponents.DotNetBar.eBorderType.SingleLine; this.panelEx1.Style.BorderColor.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.PanelBorder; this.panelEx1.Style.ForeColor.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.PanelText; this.panelEx1.Style.GradientAngle = 90; this.panelEx1.TabIndex = 1; // // buttonX2 // this.buttonX2.AccessibleRole = System.Windows.Forms.AccessibleRole.PushButton; this.buttonX2.ColorTable = DevComponents.DotNetBar.eButtonColor.OrangeWithBackground; this.buttonX2.DialogResult = System.Windows.Forms.DialogResult.Cancel; this.buttonX2.Location = new System.Drawing.Point(234, 4); this.buttonX2.Name = "buttonX2"; this.buttonX2.Size = new System.Drawing.Size(60, 23); this.buttonX2.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.buttonX2.TabIndex = 1; this.buttonX2.Text = "取消"; // // buttonX1 // this.buttonX1.AccessibleRole = System.Windows.Forms.AccessibleRole.PushButton; this.buttonX1.ColorTable = DevComponents.DotNetBar.eButtonColor.OrangeWithBackground; this.buttonX1.DialogResult = System.Windows.Forms.DialogResult.OK; this.buttonX1.Location = new System.Drawing.Point(167, 4); this.buttonX1.Name = "buttonX1"; this.buttonX1.Size = new System.Drawing.Size(60, 23); this.buttonX1.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.buttonX1.TabIndex = 0; this.buttonX1.Text = "确定"; // // checkBoxX11 // this.checkBoxX11.AutoSize = true; this.checkBoxX11.BackColor = System.Drawing.Color.Transparent; // // // this.checkBoxX11.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.checkBoxX11.Location = new System.Drawing.Point(13, 84); this.checkBoxX11.Name = "checkBoxX11"; this.checkBoxX11.Size = new System.Drawing.Size(101, 18); this.checkBoxX11.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.checkBoxX11.TabIndex = 4; this.checkBoxX11.Text = "显示勋章样式"; // // checkBoxX13 // this.checkBoxX13.AutoSize = true; this.checkBoxX13.BackColor = System.Drawing.Color.Transparent; // // // this.checkBoxX13.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.checkBoxX13.Location = new System.Drawing.Point(13, 81); this.checkBoxX13.Name = "checkBoxX13"; this.checkBoxX13.Size = new System.Drawing.Size(138, 18); this.checkBoxX13.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.checkBoxX13.TabIndex = 8; this.checkBoxX13.Text = "冷却时间转换为[秒]"; // // checkBoxX14 // this.checkBoxX14.AutoSize = true; this.checkBoxX14.BackColor = System.Drawing.Color.Transparent; // // // this.checkBoxX14.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.checkBoxX14.Location = new System.Drawing.Point(13, 105); this.checkBoxX14.Name = "checkBoxX14"; this.checkBoxX14.Size = new System.Drawing.Size(150, 18); this.checkBoxX14.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.checkBoxX14.TabIndex = 9; this.checkBoxX14.Text = "PerM属性转换为百分制"; // // checkBoxX15 // this.checkBoxX15.AutoSize = true; this.checkBoxX15.BackColor = System.Drawing.Color.Transparent; // // // this.checkBoxX15.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.checkBoxX15.Location = new System.Drawing.Point(13, 129); this.checkBoxX15.Name = "checkBoxX15"; this.checkBoxX15.Size = new System.Drawing.Size(125, 18); this.checkBoxX15.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.checkBoxX15.TabIndex = 10; this.checkBoxX15.Text = "忽略公式求解错误"; // // superTabControlPanel5 // this.superTabControlPanel5.Controls.Add(this.txtDamageSkinNumber); this.superTabControlPanel5.Controls.Add(this.lblDamageSkinNumber); this.superTabControlPanel5.Controls.Add(this.chkDisplayUnitOnSingleLine); this.superTabControlPanel5.Controls.Add(this.chkAlwaysUseMseaFormat); this.superTabControlPanel5.Controls.Add(this.chkUseMiniSize); this.superTabControlPanel5.Controls.Add(this.chkShowDamageSkin); this.superTabControlPanel5.Controls.Add(this.chkShowDamageSkinID); this.superTabControlPanel5.Dock = System.Windows.Forms.DockStyle.Fill; this.superTabControlPanel5.Location = new System.Drawing.Point(62, 0); this.superTabControlPanel5.Name = "superTabControlPanel5"; this.superTabControlPanel5.Size = new System.Drawing.Size(242, 211); this.superTabControlPanel5.TabIndex = 0; this.superTabControlPanel5.TabItem = this.superTabItem5; this.superTabControlPanel5.Visible = false; // // superTabItem5 // this.superTabItem5.AttachedControl = this.superTabControlPanel5; this.superTabItem5.GlobalItem = false; this.superTabItem5.Name = "superTabItem5"; this.superTabItem5.Text = "D.Skin"; // // txtDamageSkinNumber // // // // this.txtDamageSkinNumber.Border.Class = "TextBoxBorder"; this.txtDamageSkinNumber.Border.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.txtDamageSkinNumber.Location = new System.Drawing.Point(76, 130); this.txtDamageSkinNumber.Name = "txtDamageSkinNumber"; this.txtDamageSkinNumber.Size = new System.Drawing.Size(160, 23); this.txtDamageSkinNumber.WatermarkText = "1234567890"; this.txtDamageSkinNumber.MaxLength = 18; this.txtDamageSkinNumber.TextChanged += new System.EventHandler(this.txtDamageSkinNumber_TextChanged); this.txtDamageSkinNumber.TabIndex = 7; // // lblDamageSkinNumber // this.lblDamageSkinNumber.AutoSize = true; this.lblDamageSkinNumber.BackColor = System.Drawing.Color.Transparent; // // // this.lblDamageSkinNumber.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.lblDamageSkinNumber.Location = new System.Drawing.Point(13, 132); this.lblDamageSkinNumber.Name = "lblDamageSkinNumber"; this.lblDamageSkinNumber.Size = new System.Drawing.Size(87, 16); this.lblDamageSkinNumber.TabIndex = 6; this.lblDamageSkinNumber.Text = "伤害数字"; // // chkDisplayUnitOnSingleLine // this.chkDisplayUnitOnSingleLine.AutoSize = true; this.chkDisplayUnitOnSingleLine.BackColor = System.Drawing.Color.Transparent; // // // this.chkDisplayUnitOnSingleLine.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.chkDisplayUnitOnSingleLine.Location = new System.Drawing.Point(13, 108); this.chkDisplayUnitOnSingleLine.Name = "chkDisplayUnitOnSingleLine"; this.chkDisplayUnitOnSingleLine.Size = new System.Drawing.Size(133, 16); this.chkDisplayUnitOnSingleLine.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.chkDisplayUnitOnSingleLine.TabIndex = 5; this.chkDisplayUnitOnSingleLine.Text = "在单独一行显示单位"; // // chkAlwaysUseMseaFormat // this.chkAlwaysUseMseaFormat.AutoSize = true; this.chkAlwaysUseMseaFormat.BackColor = System.Drawing.Color.Transparent; // // // this.chkAlwaysUseMseaFormat.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.chkAlwaysUseMseaFormat.Location = new System.Drawing.Point(13, 84); this.chkAlwaysUseMseaFormat.Name = "chkAlwaysUseMseaFormat"; this.chkAlwaysUseMseaFormat.Size = new System.Drawing.Size(133, 16); this.chkAlwaysUseMseaFormat.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.chkAlwaysUseMseaFormat.TabIndex = 4; this.chkAlwaysUseMseaFormat.Text = "使用MSEA的千位分隔符"; // // chkUseMiniSize // this.chkUseMiniSize.AutoSize = true; this.chkUseMiniSize.BackColor = System.Drawing.Color.Transparent; // // // this.chkUseMiniSize.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.chkUseMiniSize.Location = new System.Drawing.Point(13, 60); this.chkUseMiniSize.Name = "chkUseMiniSize"; this.chkUseMiniSize.Size = new System.Drawing.Size(145, 16); this.chkUseMiniSize.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.chkUseMiniSize.TabIndex = 3; this.chkUseMiniSize.Text = "使用迷你尺寸伤害皮肤"; // // chkShowDamageSkin // this.chkShowDamageSkin.AutoSize = true; this.chkShowDamageSkin.BackColor = System.Drawing.Color.Transparent; // // // this.chkShowDamageSkin.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.chkShowDamageSkin.Location = new System.Drawing.Point(13, 36); this.chkShowDamageSkin.Name = "chkShowDamageSkin"; this.chkShowDamageSkin.Size = new System.Drawing.Size(172, 16); this.chkShowDamageSkin.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.chkShowDamageSkin.TabIndex = 2; this.chkShowDamageSkin.Text = "显示伤害皮肤"; // // chkShowDamageSkinID // this.chkShowDamageSkinID.AutoSize = true; this.chkShowDamageSkinID.BackColor = System.Drawing.Color.Transparent; // // // this.chkShowDamageSkinID.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.chkShowDamageSkinID.Location = new System.Drawing.Point(13, 12); this.chkShowDamageSkinID.Name = "chkShowDamageSkinID"; this.chkShowDamageSkinID.Size = new System.Drawing.Size(117, 16); this.chkShowDamageSkinID.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.chkShowDamageSkinID.TabIndex = 1; this.chkShowDamageSkinID.Text = "显示伤害皮肤代码"; // // FrmQuickViewSetting // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(304, 281); this.Controls.Add(this.superTabControl1); this.Controls.Add(this.panelEx1); this.DoubleBuffered = true; this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; this.MaximizeBox = false; this.Name = "FrmQuickViewSetting"; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; this.Text = "快速预览设置"; ((System.ComponentModel.ISupportInitialize)(this.superTabControl1)).EndInit(); this.superTabControl1.ResumeLayout(false); this.superTabControlPanel1.ResumeLayout(false); this.superTabControlPanel1.PerformLayout(); this.superTabControlPanel4.ResumeLayout(false); this.superTabControlPanel4.PerformLayout(); this.superTabControlPanel3.ResumeLayout(false); this.superTabControlPanel3.PerformLayout(); this.superTabControlPanel2.ResumeLayout(false); this.superTabControlPanel2.PerformLayout(); this.superTabControlPanel5.ResumeLayout(false); this.superTabControlPanel5.PerformLayout(); this.panelEx1.ResumeLayout(false); this.ResumeLayout(false); } #endregion private DevComponents.DotNetBar.SuperTabControl superTabControl1; private DevComponents.DotNetBar.SuperTabControlPanel superTabControlPanel1; private DevComponents.DotNetBar.Controls.CheckBoxX checkBoxX1; private DevComponents.DotNetBar.SuperTabItem superTabItem1; private DevComponents.DotNetBar.SuperTabControlPanel superTabControlPanel3; private DevComponents.DotNetBar.SuperTabItem superTabItem3; private DevComponents.DotNetBar.SuperTabControlPanel superTabControlPanel2; private DevComponents.DotNetBar.SuperTabItem superTabItem2; private DevComponents.DotNetBar.LabelX labelX1; private DevComponents.DotNetBar.Controls.ComboBoxEx comboBoxEx1; private DevComponents.Editors.ComboItem comboItem1; private DevComponents.Editors.ComboItem comboItem2; private DevComponents.Editors.ComboItem comboItem3; private DevComponents.Editors.ComboItem comboItem4; private DevComponents.DotNetBar.Controls.CheckBoxX checkBoxX2; private DevComponents.DotNetBar.Controls.ComboBoxEx comboBoxEx2; private DevComponents.Editors.ComboItem comboItem5; private DevComponents.Editors.ComboItem comboItem6; private DevComponents.DotNetBar.LabelX labelX2; private DevComponents.Editors.ComboItem comboItem7; private DevComponents.Editors.ComboItem comboItem8; private DevComponents.Editors.ComboItem comboItem9; private DevComponents.Editors.ComboItem comboItem10; private DevComponents.DotNetBar.PanelEx panelEx1; private DevComponents.DotNetBar.ButtonX buttonX1; private DevComponents.DotNetBar.ButtonX buttonX2; private DevComponents.DotNetBar.Controls.CheckBoxX checkBoxX3; private DevComponents.DotNetBar.Controls.CheckBoxX checkBoxX5; private DevComponents.DotNetBar.LabelX labelX3; private DevComponents.DotNetBar.Controls.CheckBoxX checkBoxX4; private DevComponents.DotNetBar.SuperTabControlPanel superTabControlPanel4; private DevComponents.DotNetBar.SuperTabItem superTabItem4; private DevComponents.DotNetBar.Controls.CheckBoxX checkBoxX6; private DevComponents.DotNetBar.Controls.CheckBoxX checkBoxX7; private DevComponents.DotNetBar.Controls.CheckBoxX checkBoxX9; private DevComponents.DotNetBar.Controls.CheckBoxX checkBoxX8; private DevComponents.DotNetBar.Controls.CheckBoxX checkBoxX10; private DevComponents.DotNetBar.Controls.CheckBoxX checkBoxX11; private DevComponents.DotNetBar.Controls.CheckBoxX checkBoxX12; private DevComponents.DotNetBar.Controls.CheckBoxX checkBoxX13; private DevComponents.DotNetBar.Controls.CheckBoxX checkBoxX14; private DevComponents.DotNetBar.Controls.CheckBoxX checkBoxX15; private DevComponents.DotNetBar.SuperTabControlPanel superTabControlPanel5; private DevComponents.DotNetBar.SuperTabItem superTabItem5; private DevComponents.DotNetBar.Controls.CheckBoxX chkShowDamageSkinID; private DevComponents.DotNetBar.Controls.CheckBoxX chkShowDamageSkin; private DevComponents.DotNetBar.Controls.CheckBoxX chkUseMiniSize; private DevComponents.DotNetBar.Controls.CheckBoxX chkAlwaysUseMseaFormat; private DevComponents.DotNetBar.Controls.CheckBoxX chkDisplayUnitOnSingleLine; private DevComponents.DotNetBar.LabelX lblDamageSkinNumber; private DevComponents.DotNetBar.Controls.TextBoxX txtDamageSkinNumber; } } ================================================ FILE: WzComparerR2/FrmQuickViewSetting.cs ================================================ using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; using System.Reflection; using DevComponents.Editors; using WzComparerR2.Config; namespace WzComparerR2 { public partial class FrmQuickViewSetting : DevComponents.DotNetBar.Office2007Form { public FrmQuickViewSetting() { InitializeComponent(); #if NET6_0_OR_GREATER // https://learn.microsoft.com/en-us/dotnet/core/compatibility/fx-core#controldefaultfont-changed-to-segoe-ui-9pt this.Font = new Font(new FontFamily("Microsoft Sans Serif"), 8f); #endif this.comboBoxEx1.SelectedIndex = 0; this.comboBoxEx2.SelectedIndex = 0; } [Link] public bool Skill_ShowProperties { get { return checkBoxX10.Checked; } set { checkBoxX10.Checked = value; } } [Link] public bool Skill_ShowID { get { return checkBoxX1.Checked; } set { checkBoxX1.Checked = value; } } [Link] public bool Skill_ShowDelay { get { return checkBoxX2.Checked; } set { checkBoxX2.Checked = value; } } [Link] public DefaultLevel Skill_DefaultLevel { get { return (DefaultLevel)comboBoxEx1.SelectedIndex; } set { comboBoxEx1.SelectedIndex = (int)value; } } [Link] public int Skill_IntervalLevel { get { return Convert.ToInt32(((ComboItem)comboBoxEx2.SelectedItem).Text); } set { for (int i = 0; i < comboBoxEx2.Items.Count; i++) { if (value <= Convert.ToInt32(((ComboItem)comboBoxEx2.Items[i]).Text)) { comboBoxEx2.SelectedIndex = i; return; } } comboBoxEx2.SelectedIndex = comboBoxEx2.Items.Count - 1; } } [Link] public bool Skill_DisplayCooltimeMSAsSec { get { return checkBoxX13.Checked; } set { checkBoxX13.Checked = value; } } [Link] public bool Skill_DisplayPermyriadAsPercent { get { return checkBoxX14.Checked; } set { checkBoxX14.Checked = value; } } [Link] public bool Skill_IgnoreEvalError { get { return checkBoxX15.Checked; } set { checkBoxX15.Checked = value; } } [Link] public bool DamageSkin_ShowDamageSkinID { get { return chkShowDamageSkinID.Checked; } set { chkShowDamageSkinID.Checked = value; } } [Link] public bool DamageSkin_ShowDamageSkin { get { return chkShowDamageSkin.Checked; } set { chkShowDamageSkin.Checked = value; } } [Link] public bool DamageSkin_UseMiniSize { get { return chkUseMiniSize.Checked; } set { chkUseMiniSize.Checked = value; } } [Link] public bool DamageSkin_AlwaysUseMseaFormat { get { return chkAlwaysUseMseaFormat.Checked; } set { chkAlwaysUseMseaFormat.Checked = value; } } [Link] public bool DamageSkin_DisplayUnitOnSingleLine { get { return chkDisplayUnitOnSingleLine.Checked; } set { chkDisplayUnitOnSingleLine.Checked = value; } } [Link] public long DamageSkin_DamageSkinNumber { get { return long.TryParse(txtDamageSkinNumber.Text, out long val) ? val : 0; } set { txtDamageSkinNumber.Text = value.ToString(); } } [Link] public bool Gear_ShowID { get { return checkBoxX3.Checked; } set { checkBoxX3.Checked = value; } } [Link] public bool Gear_ShowWeaponSpeed { get { return checkBoxX4.Checked; } set { checkBoxX4.Checked = value; } } [Link] public bool Item_ShowID { get { return checkBoxX5.Checked; } set { checkBoxX5.Checked = value; } } [Link] public bool Gear_ShowLevelOrSealed { get { return checkBoxX6.Checked; } set { checkBoxX6.Checked = value; } } [Link] public bool Gear_ShowMedalTag { get { return checkBoxX11.Checked; } set { checkBoxX11.Checked = value; } } [Link] public bool Recipe_ShowID { get { return checkBoxX7.Checked; } set { checkBoxX7.Checked = value; } } [Link] public bool Item_LinkRecipeInfo { get { return checkBoxX8.Checked; } set { checkBoxX8.Checked = value; } } [Link] public bool Item_LinkRecipeItem { get { return checkBoxX9.Checked; } set { checkBoxX9.Checked = value; } } [Link] public bool Item_ShowNickTag { get { return checkBoxX12.Checked; } set { checkBoxX12.Checked = value; } } private void txtDamageSkinNumber_TextChanged(object sender, EventArgs e) { this.buttonX1.Enabled = !(string.IsNullOrEmpty(txtDamageSkinNumber.Text) || txtDamageSkinNumber.Text == "0"); string digitsOnly = new string(txtDamageSkinNumber.Text.Where(char.IsDigit).ToArray()); if (txtDamageSkinNumber.Text != digitsOnly) { int cursorPos = txtDamageSkinNumber.SelectionStart; txtDamageSkinNumber.Text = digitsOnly; txtDamageSkinNumber.SelectionStart = Math.Min(cursorPos, txtDamageSkinNumber.Text.Length); } } public void Load(CharaSimConfig config) { var linkProp = this.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance) .Where(prop => prop.GetCustomAttributes(typeof(LinkAttribute), false).Length > 0); foreach (var prop in linkProp) { string[] path = prop.Name.Split('_'); try { var configGroup = config.GetType().GetProperty(path[0]).GetValue(config, null); var configPropInfo = configGroup.GetType().GetProperty(path[1]); var value = configPropInfo.GetGetMethod().Invoke(configGroup, null); prop.GetSetMethod().Invoke(this, new object[] { value }); } catch { } } } public void Save(CharaSimConfig config) { var linkProp = this.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance) .Where(prop => prop.GetCustomAttributes(typeof(LinkAttribute), false).Length > 0); foreach (var prop in linkProp) { string[] path = prop.Name.Split('_'); try { var configGroup = config.GetType().GetProperty(path[0]).GetValue(config, null); var configPropInfo = configGroup.GetType().GetProperty(path[1]); var value = prop.GetGetMethod().Invoke(this, null); configPropInfo.GetSetMethod().Invoke(configGroup, new object[] { value }); } catch { } } } private sealed class LinkAttribute : Attribute { } } public enum DefaultLevel { Level0 = 0, Level1 = 1, LevelMax = 2, LevelMaxWithCO = 3, } } ================================================ FILE: WzComparerR2/FrmQuickViewSetting.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ================================================ FILE: WzComparerR2/FrmUpdater.Designer.cs ================================================ namespace WzComparerR2 { partial class FrmUpdater { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FrmUpdater)); this.labelX1 = new DevComponents.DotNetBar.LabelX(); this.labelX2 = new DevComponents.DotNetBar.LabelX(); this.labelX3 = new DevComponents.DotNetBar.LabelX(); this.lblCurrentVer = new DevComponents.DotNetBar.LabelX(); this.lblLatestVer = new DevComponents.DotNetBar.LabelX(); this.lblUpdateContent = new DevComponents.DotNetBar.LabelX(); this.buttonX1 = new DevComponents.DotNetBar.ButtonX(); this.richTextBoxEx1 = new DevComponents.DotNetBar.Controls.RichTextBoxEx(); this.chkEnableAutoUpdate = new DevComponents.DotNetBar.Controls.CheckBoxX(); this.elementStyle1 = new DevComponents.DotNetBar.ElementStyle(); this.SuspendLayout(); // // labelX1 // this.labelX1.AutoSize = true; // // // this.labelX1.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.labelX1.Location = new System.Drawing.Point(12, 9); this.labelX1.Name = "labelX1"; this.labelX1.Size = new System.Drawing.Size(56, 16); this.labelX1.TabIndex = 0; this.labelX1.Text = "当前版本:"; // // labelX2 // this.labelX2.AutoSize = true; // // // this.labelX2.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.labelX2.Location = new System.Drawing.Point(11, 33); this.labelX2.Name = "labelX2"; this.labelX2.Size = new System.Drawing.Size(81, 16); this.labelX2.TabIndex = 1; this.labelX2.Text = "最新版本:"; // // labelX3 // this.labelX3.AutoSize = true; // // // this.labelX3.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.labelX3.Location = new System.Drawing.Point(11, 57); this.labelX3.Name = "labelX3"; this.labelX3.Size = new System.Drawing.Size(50, 16); this.labelX3.TabIndex = 2; this.labelX3.Text = "版本信息:"; // // lblCurrentVer // this.lblCurrentVer.AutoSize = true; // // // this.lblCurrentVer.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.lblCurrentVer.Location = new System.Drawing.Point(110, 9); this.lblCurrentVer.Name = "lblCurrentVer"; this.lblCurrentVer.Size = new System.Drawing.Size(13, 16); this.lblCurrentVer.TabIndex = 4; this.lblCurrentVer.Text = "-"; // // lblLatestVer // this.lblLatestVer.AutoSize = true; // // // this.lblLatestVer.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.lblLatestVer.Location = new System.Drawing.Point(110, 33); this.lblLatestVer.Name = "lblLatestVer"; this.lblLatestVer.Size = new System.Drawing.Size(13, 16); this.lblLatestVer.TabIndex = 5; this.lblLatestVer.Text = "-"; // // lblUpdateContent // this.lblUpdateContent.AutoSize = true; // // // this.lblUpdateContent.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.lblUpdateContent.Location = new System.Drawing.Point(110, 57); this.lblUpdateContent.Name = "lblUpdateContent"; this.lblUpdateContent.Size = new System.Drawing.Size(13, 16); this.lblUpdateContent.TabIndex = 6; this.lblUpdateContent.Text = "正在检查更新..."; // // buttonX1 // this.buttonX1.AccessibleRole = System.Windows.Forms.AccessibleRole.PushButton; this.buttonX1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.buttonX1.ColorTable = DevComponents.DotNetBar.eButtonColor.OrangeWithBackground; // this.buttonX1.DialogResult = System.Windows.Forms.DialogResult.OK; this.buttonX1.Location = new System.Drawing.Point(110, 187); this.buttonX1.Name = "buttonX1"; this.buttonX1.Size = new System.Drawing.Size(85, 23); this.buttonX1.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.buttonX1.TabIndex = 8; this.buttonX1.Enabled = false; this.buttonX1.Text = "更新"; this.buttonX1.Click += new System.EventHandler(this.buttonX1_Click); // // richTextBoxEx1 // // // // this.richTextBoxEx1.BackgroundStyle.Class = "RichTextBoxBorder"; this.richTextBoxEx1.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.richTextBoxEx1.Location = new System.Drawing.Point(12, 81); this.richTextBoxEx1.Name = "richTextBoxEx1"; this.richTextBoxEx1.Rtf = "{\\rtf1\\ansi\\ansicpg936\\deff0\\deflang1033\\deflangfe1042{\\fonttbl{\\f0\\fnil\\fcharset" + "129 \\\'b5\\\'b8\\\'bf\\\'f2;}}\r\n\\viewkind4\\uc1\\pard\\lang1042\\f0\\fs18\\par\r\n}\r\n"; this.richTextBoxEx1.Size = new System.Drawing.Size(280, 100); this.richTextBoxEx1.ReadOnly = true; this.richTextBoxEx1.TabIndex = 9; // // elementStyle1 // this.elementStyle1.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.elementStyle1.Name = "elementStyle1"; this.elementStyle1.TextColor = System.Drawing.SystemColors.ControlText; // // chkEnableAutoUpdate // this.chkEnableAutoUpdate.AutoSize = true; this.chkEnableAutoUpdate.BackColor = System.Drawing.Color.Transparent; // // // this.chkEnableAutoUpdate.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.chkEnableAutoUpdate.Location = new System.Drawing.Point(12, 189); this.chkEnableAutoUpdate.Name = "chkEnableAutoUpdate"; this.chkEnableAutoUpdate.Size = new System.Drawing.Size(170, 23); this.chkEnableAutoUpdate.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.chkEnableAutoUpdate.TabIndex = 10; this.chkEnableAutoUpdate.Text = "自动检查更新"; this.chkEnableAutoUpdate.CheckedChanged += new System.EventHandler(this.chkEnableAutoUpdate_CheckedChanged); // // FrmUpdater // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(304, 221); this.Controls.Add(this.richTextBoxEx1); this.Controls.Add(this.buttonX1); this.Controls.Add(this.lblUpdateContent); this.Controls.Add(this.lblLatestVer); this.Controls.Add(this.lblCurrentVer); this.Controls.Add(this.labelX3); this.Controls.Add(this.labelX2); this.Controls.Add(this.labelX1); this.Controls.Add(this.chkEnableAutoUpdate); this.DoubleBuffered = true; this.Font = new System.Drawing.Font("Microsoft Sans Serif", 8F); this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); this.MaximizeBox = false; this.MinimizeBox = false; this.Name = "FrmUpdater"; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; this.Text = "更新程序"; this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.FrmUpdater_FormClosed); this.Load += new System.EventHandler(this.FrmUpdater_Load); this.ResumeLayout(false); this.PerformLayout(); } #endregion private DevComponents.DotNetBar.LabelX labelX1; private DevComponents.DotNetBar.LabelX labelX2; private DevComponents.DotNetBar.LabelX labelX3; private DevComponents.DotNetBar.LabelX lblCurrentVer; private DevComponents.DotNetBar.LabelX lblLatestVer; private DevComponents.DotNetBar.LabelX lblUpdateContent; private DevComponents.DotNetBar.ButtonX buttonX1; private DevComponents.DotNetBar.Controls.CheckBoxX chkEnableAutoUpdate; private DevComponents.DotNetBar.Controls.RichTextBoxEx richTextBoxEx1; private DevComponents.DotNetBar.ElementStyle elementStyle1; } } ================================================ FILE: WzComparerR2/FrmUpdater.cs ================================================ using System; using System.Diagnostics; using System.Drawing; using System.IO; using System.Reflection; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; using DevComponents.DotNetBar; using WzComparerR2.Config; using WzComparerR2.Controls; namespace WzComparerR2 { public partial class FrmUpdater : DevComponents.DotNetBar.Office2007Form { public FrmUpdater() : this(new Updater()) { } public FrmUpdater(Updater updater) { InitializeComponent(); #if NET6_0_OR_GREATER // https://learn.microsoft.com/en-us/dotnet/core/compatibility/fx-core#controldefaultfont-changed-to-segoe-ui-9pt this.Font = new Font(new FontFamily("Microsoft Sans Serif"), 8f); #endif this.Updater = updater; this.lblCurrentVer.Text = updater.CurrentVersionString; } public Updater Updater { get; set; } public bool EnableAutoUpdate { get { return chkEnableAutoUpdate.Checked; } set { chkEnableAutoUpdate.Checked = value; } } private CancellationTokenSource cts; private async void FrmUpdater_Load(object sender, EventArgs e) { var updater = this.Updater; if (!updater.LatestReleaseFetched) { using var cts = new CancellationTokenSource(); this.cts = cts; try { await updater.QueryUpdateAsync(cts.Token); } catch (TaskCanceledException) { return; } catch (Exception ex) { this.lblUpdateContent.Text = "更新检查失败"; this.AppendText(ex.Message + "\r\n" + ex.StackTrace, Color.Red); return; } finally { this.cts = null; } } if (updater.LatestReleaseFetched) { this.lblLatestVer.Text = updater.LatestVersionString; this.AppendText(updater.Release?.CreatedAt.ToLocalTime().ToString() + "\r\n" + updater.Release?.Body, Color.Black); this.richTextBoxEx1.SelectionStart = 0; if (updater.UpdateAvailable) { this.buttonX1.Enabled = true; this.lblUpdateContent.Text = "更新可用"; } else { this.lblUpdateContent.Text = "已是最新版本"; } } } private void buttonX1_Click(object sender, EventArgs e) { var updater = this.Updater; if (!updater.UpdateAvailable) { MessageBoxEx.Show(this, "没有获取到更新,请重试。", "提示", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } var runtimeVer = Environment.Version.Major; var asset = runtimeVer switch { 4 => updater.Net462Asset, 6 => updater.Net6Asset, 8 => updater.Net8Asset, 10 => updater.Net10Asset, _ => null, }; if (asset == null) { MessageBoxEx.Show(this, $"没有找到.Net {runtimeVer}的对应版本,请手动下载最新版。", "提示", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } if (this.cts != null) { MessageBoxEx.Show(this, "已有另一个任务正在运行,请稍后再试。", "提示", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } using var cts = new CancellationTokenSource(); this.cts = cts; this.buttonX1.Enabled = false; this.lblUpdateContent.Text = "正在下载更新..."; try { string savePath = Path.Combine(Application.StartupPath, "update.zip"); var result = ProgressDialog.Show(this, "下载更新中..", "Updater", true, true, async (ctx, cancellationToken) => { cancellationToken.Register(() => cts.Cancel()); try { await updater.DownloadAssetAsync(asset, savePath, (downloaded, total) => { if (total > 0) { if (ctx.Progress == 0) { ctx.ProgressMin = 0; ctx.ProgressMax = (int)total; } ctx.Progress = (int)downloaded; ctx.Message = $"已下载 {(1.0 * downloaded / total):P1}"; } else { ctx.Message = $"已下载 {downloaded:N0}"; } }, cts.Token); } catch (TaskCanceledException) { this.lblUpdateContent.Text = "更新取消"; throw; } catch (Exception ex) { this.lblUpdateContent.Text = "更新下载失败"; this.AppendText(ex.Message + "\r\n" + ex.StackTrace, Color.Red); throw; } }); if (result == DialogResult.OK) { if (DialogResult.OK == MessageBoxEx.Show(this, "新版本已下载完成,点击确认更新。", "提示", MessageBoxButtons.OKCancel, MessageBoxIcon.Information)) { this.ExecuteUpdater(savePath); } } } catch (Exception ex) { this.lblUpdateContent.Text = "更新失败"; AppendText(ex.Message + "\r\n" + ex.StackTrace, Color.Red); } finally { this.cts = null; if (!this.IsDisposed) { this.buttonX1.Enabled = true; } } } private void ExecuteUpdater(string assetFileName) { string wcR2Folder = Application.StartupPath; ExtractResource("WzComparerR2.WzComparerR2.Updater.exe", Path.Combine(wcR2Folder, "WzComparerR2.Updater.exe")); #if NET6_0_OR_GREATER ExtractResource("WzComparerR2.WzComparerR2.Updater.deps.json", Path.Combine(wcR2Folder, "WzComparerR2.Updater.deps.json")); ExtractResource("WzComparerR2.WzComparerR2.Updater.dll", Path.Combine(wcR2Folder, "WzComparerR2.Updater.dll")); ExtractResource("WzComparerR2.WzComparerR2.Updater.dll.config", Path.Combine(wcR2Folder, "WzComparerR2.Updater.dll.config")); ExtractResource("WzComparerR2.WzComparerR2.Updater.runtimeconfig.json", Path.Combine(wcR2Folder, "WzComparerR2.Updater.runtimeconfig.json")); #else ExtractResource("WzComparerR2.WzComparerR2.Updater.exe.config", Path.Combine(wcR2Folder, "WzComparerR2.Updater.exe.config")); #endif RunProgram("WzComparerR2.Updater.exe", "\"" + assetFileName + "\""); } private void RunProgram(string url, string argument) { #if NET6_0_OR_GREATER Process.Start(new ProcessStartInfo { UseShellExecute = true, FileName = url, Arguments = argument, }); #else Process.Start(url, argument); #endif } private void AppendText(string text, Color color) { this.richTextBoxEx1.SelectionStart = this.richTextBoxEx1.TextLength; this.richTextBoxEx1.SelectionLength = 0; this.richTextBoxEx1.SelectionColor = color; this.richTextBoxEx1.AppendText(text); this.richTextBoxEx1.SelectionColor = this.richTextBoxEx1.ForeColor; } private void ExtractResource(string resourceName, string outputPath) { var assembly = Assembly.GetExecutingAssembly(); using Stream? resourceStream = assembly.GetManifestResourceStream(resourceName); if (resourceStream == null) throw new InvalidOperationException($"Resource not found: {resourceName}"); Directory.CreateDirectory(Path.GetDirectoryName(outputPath)!); using FileStream fileStream = new FileStream(outputPath, FileMode.Create, FileAccess.Write); resourceStream.CopyTo(fileStream); } private void chkEnableAutoUpdate_CheckedChanged(object sender, EventArgs e) { var config = WcR2Config.Default; config.EnableAutoUpdate = chkEnableAutoUpdate.Checked; ConfigManager.Save(); } public void LoadConfig(WcR2Config config) { this.EnableAutoUpdate = config.EnableAutoUpdate; } private void FrmUpdater_FormClosed(object sender, System.Windows.Forms.FormClosedEventArgs e) { if (this.cts != null) { this.cts.Cancel(); } } } } ================================================ FILE: WzComparerR2/FrmUpdater.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 17, 17 112, 17 207, 17 302, 17 414, 17 520, 17 645, 17 752, 17 858, 17 960, 17 AAABAAEAQEAAAAEAIAAoQgAAFgAAACgAAABAAAAAgAAAAAEAIAAAAAAAAAAAABMLAAATCwAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAKAIPFJ0aQlz0EjRN8RtCXfATOlbwDjBK8BY+W/AWPl3wDStD8AIPG/IAFSfsAQUJhAEA AUoAAAACAAAAAAAAAAAAAAAAAAAAAAEJDWMLNFXzCi9P8govTvALMlHwCjJQ8As1U/MGJTvkAAEDcAAA AGgAAQJqAAAAIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAALxxPcuEkdbH/QJze/y2Cxf9Epun/MpTf/yN9vf84n+r/OaDp/yV9 wf8aX47/Ay5T/wU3VP8FMlf6BhckwAABACcAAAAAAAAAAAAAABYKMU3MGoDH/xVqqf8RaKb/EWmq/x+F zv8bgtH/GHa7/w9Vh/8JPWX/AB83/wAVJ+MADxS/AAAAVAABAgMAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMHC4Mzj9T/KofI/zuPzv8re7X/QJvc/y6L 0f8hdrH/NJTb/zWW2/8hdbP/MY/U/ws4Xf8BM1j/BkBo/wlDcf8CCRGQAAAAAAAAAAABCQ5tElqU/xlu sf8ff8b/GXvB/xZ1uf8ca6b/IIHE/xp8xv8Vb6//Gni5/xNbkP8DLkv/ADBS/wAmQP8AChCDAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4TN03MNpjg/yd/ v/87kM//K326/0Cb3P8ui9H/IXaz/zWV2/8tiMz/MIG+/zmb5v8gaJz/ASlL/wIyWf8GRXD/BSM76QAA AAsAAAAACic8yBhvtP8ge7z/FnW8/xh3v/8bfcT/FHK1/xZno/8de8T/GHS6/xNjnv8eg9L/EViQ/wAp Qv8ANFf/AB43+wAAADkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAArHVWB9zmf7P8nfr3/O5DQ/yt8uf9Am9z/LovP/yF2tP82l93/KILC/ziKxv87n+T/JXq5/wAn Q/8ENV3/Bjxj/wcwU/4AAABIAAAALx9Wgewnf7z/MpPY/yiJz/8fdrb/HG+v/yCAx/8SZqT/GnGy/xh6 vv8VaKL/HHq8/xl6xP8LQ27/ACpI/wEzVP8FGinWAAAAJwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAIHjiJ0rv84nOX/Jny+/zqMy/8rf7X/QJng/y6N0f8hda//Npfd/yqC w/8nf73/NZXa/yZ8vP8RSG7/ADBS/wE2XP8EOl7/AAMEdAMFCFohbqX/M4zM/zaW3f8uicn/OIvH/zCJ x/8kfb7/G3Oz/xZmo/8ef8v/E2al/yB5uP8agMr/EF6Y/wQzVf8ALEr/ATde/wEaMNgAAAAoAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACQoiM7oke7v/NZfe/ymAu/8xjc7/KXm2/z+c 3P8tjM//I3i5/zSZ3v8pgcP/I3u8/zGP1v8kfbv/KHSs/wAkQ/8EOF//BTld/wAJDrIUNFDNKYbI/yiC wf8vjNL/Knq2/zuZ3/8zktj/I3qz/y6Jz/8kd7L/IoHE/xVxtv8RZJz/GnjC/xRqqP8OW5T/ACdC/wAv U/8FO2P/ARwx2wAAACoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAlkcYpL/LInI/zCR 2f8sfrb/QJ3i/yh3tf9Dmt3/NZPZ/yiHzv89mNz/LoC6/z+Z2P8rh8r/Kn++/zGN1v8KOFr/ADFV/wQ5 X/8CLUz/G1mH/y6FxP87ltv/KobE/yt+vv8zltz/NZbb/ymCxP8ogsL/KXe5/zCO0f8ng8j/Gmmf/yGD yv8WbrP/E2qn/w9Rgf8AJkH/ADVc/wY+Z/8BGSXSAAAABwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAADRiwKoPH/y5/vP9Gm9z/L3u3/1Gs7f83gbn/WLDo/06o5/8xjtH/Vq3t/ziEvP9huPD/LXm2/0eb 2/9PrO//FUZr/wAsUf8CNVn/BjNT/y2Jy/8terv/QaLk/yBxr/82ldj/M5HV/zea4v8th8v/KYLD/yZ8 uf8ti8z/N5Xe/yl/vv8bcbb/FnCz/xZnpv8cf8P/BjJX/wIuUf8FO2L/BjdV/wEIDHQAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAuEj1e4UKY1/9HnNr/Lnu2/0GSz/9Uru//OYnI/2Ky7P9Amt3/RZ/f/1Ss 6f86hbz/Ybbw/zOLyP9IoeP/WbT1/x9gkf8AKkn/ACpM/x9fkP84l+P/LH22/0Cc4v8ec7D/L47R/zaV 3/8oerT/E0Zx/yuExP8wjdL/Ina0/zeZ3v8pgML/H3S0/xp3vv8VZqT/G3vD/xRspf8AJ0H/AzFX/wdA av8FN17/AQgPcQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwMFVyxun/8+ktH/VrLz/0uj4v9Sq+r/U6zs/zaL yP9etOz/M5HU/0yi4/9Xs/X/O5fX/2O08P8zktX/SaPk/1Gr7f81gsD/HEVu/wYpRv9Pot//T6rq/zN+ t/87n+P/H3Ox/zGP0/82mdz/CzZZ/wAiO/8lcaj/MZDY/yN4tP83l97/KILA/yuCwf8qjdf/EV+Z/x13 u/8afcn/DE17/wIrSP8EOmP/CEh0/wk+a/8CCA90AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhsyQblBm9f/PZDQ/1Wv 7f9Rqur/U6fr/0yl5f8+ltv/Vq3u/zmU2P9PqOr/O36v/zCDw/9jt/P/NZHT/12s7P9Oqej/N4O9/0iS yf83e63/U7Dz/1Gp6P8xfbf/Ua3t/zF/uf88m9//Kn++/wU0U/8BLVT/D0Jk/zCN0/8nd7X/N5jj/yZ/ u/86jcz/OJvf/yR4uf8eerj/GnrC/xl3vP8FNFX/ATNW/wlAav8JTID/CT1m/wAJDHUAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA ACMfSmndTazu/0KZ3f9Uq+j/Uarq/1St6/88mNj/UqHh/1qw8P83mN7/I2eX/wAZMv8nb6T/Zr79/zmW 1/9So+T/Tqnm/zeDtv9iuvL/Uq7w/1Kq6v9Qpef/L327/1iv7/8zfrj/SKfq/x5ik/8AKEX/AzZe/wIw TP8eaZ//JX/A/zaX3/8rhcT/M4bD/zWV3P8yjtP/Gmyn/xp6v/8YgMf/E2Wj/wEoR/8EPGD/CUR0/wpS iP8FJD3gAAAAJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAFDhaFJny6/1qz9P8sjtH/U6jn/06p5f9Sq+r/Qp/f/02g4P9kvvv/Mnqr/wAi Qf8ALFD/IlR4/2K98/9HouX/L47S/1av8P9Ajsr/QpbQ/1Ou7v9Pqur/UKXl/y98tv9Tru//NYXC/0qc 2/8RRGn/ADBU/wU5YP8CKUn/BiI3/yJ5uf81ltr/NJHZ/yFysf81lNj/LozP/yd8t/8ritP/FnW6/xuA yP8MS3r/Ai5Q/whAbP8ISXn/CEx//wMgNtAAAAAdAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdJVFx6Tia4v9Xru3/RZze/0+m4/9quvT/bLrz/1Go 4/9YrOb/e8P7/x5HZf8ALlb/ATdb/wswTf9aoMf/Y7vy/z6W2f9etPH/PpPR/zmKyP9Vr/D/Uqrn/1Cp 6/8zicj/Uq7u/0me3v8scqr/CjVS/wMzWv8DOmD/AB0x5QAAAHAcVoX0N57m/zKV1v8qerb/QZze/yyJ z/8wf7v/OJvb/xp5wf8YecD/G3u//wc7Yf8DOlz/CUJz/wlOgv8KTH7/Bh0xywACAjsAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgYIdkeYz/9AneP/XrHs/2Sx 7f9Hn9v/fcX6/3TB+P9Al9j/fMj//02LtP8AJkb/ATZd/wQ4XP8AKlH/Llx4/3rI+/9Kod3/XK/u/0Cd 2/9Ind7/Vq7r/1Cq6f9QqOj/MZDT/1et7v9KpeT/CUNq/wAvU/8GOmX/BTxm/wALEJEAAAAADCQ1sTOS 1/8yk9r/KXe3/0Cd3P8zk9r/JHe5/yyJyv8phcz/Fne8/xl/yP8PW5T/AS1O/wdDav8JRnn/CU6A/wlH dv8EGCW8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARAn N69ot+//U6nl/2Cv5f9ywPb/Rp3e/3K89v9ywfb/P5zY/3C87/8cRWX/AC5U/wM2W/8FOWD/ACE16AIF CZ9mocb5Tqno/2Sy7v9cr+n/TaHf/1at7P9Rqev/T6Tn/zCO0f9TrvD/S5/c/wY6Yv8GPGL/B0hy/wcn Q+gAAAAwAAAAAAAAAEgkZpj/NZzj/yl6tf9Amdz/M5Ta/yR7tv82js//MZHX/xt5wP8aesL/EmWg/wUw Vf8EO2D/CUV5/wpNfv8JSXr/BBkrvwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAABY2XHrygNH//1+w6f9SpOD/fcb7/0Cb2v9ote7/dsD5/0Sd2v9mp9P/AyZC/wA3 XP8GNl3/AjVY/wAJE5gAAAAILUhYyW7C+f9Fnt3/dsH5/0Ob2v9bru3/Tqno/1Gp5/81kNT/W7Hw/0ia 2P8HNFb/BUNx/wtEdv8BChORAAAAAAAAAAAAAAABDSU5uTKU3P8qfLn/QJzd/zKS2/8qg8H/NYjI/zWV 2v8mg8n/GHjB/xZqqP8ZY5f/BTZb/wdDcP8MTIH/Bzlh/wIMF5MAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADBwtwYaLQ/3XH//90wPj/PJfV/3jA9v9fsev/TqPh/1+v 5v9Xr+f/O2iK/wAsTP8ENV3/BDxg/wMnRv8AAABOAAAAAAMDBmFmoMz/UKrn/2S06/9MoeD/YrXq/1eu 7f9LpuT/PZTU/2e6+/8nZJb/Ajha/wpOg/8EK0XnAAAALAAAAAAAAAAAAAAAAAAAAEwjZZf6LYHD/zua 3P8xkdb/MpLV/x90rf81lNv/MJTX/xlzs/8Za6j/IIDF/wpIdv8DPWH/CUp//wQkOfAAAABCAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHSkvo3S+8/9yv/r/dMX4/1an 4/9vuOr/eMP6/0yj4f9Bmdf/ecr8/yxSc/8AKk7/BTZb/wE3X/8AEyLJAAAAFwAAAAAAAAAIMklh1Vuz 7P9Xqef/Y7Hr/0ui3f93xPr/Uqvs/zuY1/9dsfH/G099/wVDc/8JRnX/AwgOjQAAAAAAAAAAAAAAAAAA AAAAAAAACyQ3sip8t/9AneL/MZDW/zGP0/8gdrL/M5PY/zaW2/8ieb//HG2o/yCEy/8Wb7L/BDxf/wlH dv8EJDvyAAAASgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALDNn gvZ/zP//db/4/3K/9/9tvvX/Vafh/3vC9v9vwPf/d8P8/2qw3P8RN1j/ADFW/wI2XP8DOWH/Ag4TrgAA AAAAAAAAAAAAAAMDCGVgm8T/RaTg/2268v9Gnt3/db72/2u79f8+oOL/NH+8/wk9Yf8LU4v/AClB7QAA ACQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEYcTXP6RKLr/zGT1/8xjdL/InSz/zWV2v80k9r/LIbJ/xxu rP8fgMf/GnzG/w1Qhf8EPmb/ARky5QAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAACY0YYX2WbTu/3m/8/9xvvf/dcH4/1Kp5P+Ex/n/gsf8/5rd//93q8P/ACBD/wE4 X/8ENFr/B0Nv/wMPHLUAAAAAAAAAAAAAAAAAAAALLERd0kei5v96xfr/Qp3a/2m28f95w/r/SaXn/xpe kv8JR3X/BTJX+wECA1YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEyk1sD6e4f8yk9n/L4zT/yx9 tP89md//MpPY/y6Ky/8hdLD/Hn/F/xp8wv8Tb6//BTRW/wAEBX0AAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAICglcYpe4/2i++v9WquP/f8X7/2++9/9qtvH/bLns/5zZ //+f3P//Woae/wApTP8DMlr/BDtj/wc4XP8ACA6AAAAAAAAAAAAAAAAAAAAAAAkKDWdAiLv/YLLw/2W1 7v9In+D/hNT//1uw3/8AOmj/AzVV/AADBlsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AEMkZpX8Nprm/zSW2f8nfrz/K4bH/zWV3f8uh83/Ini0/x9/xv8ad8H/G3/I/wdCcv8AAAB4AAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADR4qql+06P96wfr/WK7q/3q/ 9P+Nzf//iMz4/2i06/+V0f//nN3+/ydHYv8ALVT/BThf/wZDb/8GKEP6AAAAMgAAAAAAAAAAAAAAAAAA AAAAAAASFzdSzVm48/9wwvb/R6Td/2Sev/89Y3foAAgUsgAFCV0AAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAABDCEyrDSW2v82l9//J3u9/yiAw/82mN3/LovM/yJ5tP8ghc//GH7L/xVo pv8HKD/IAAAAGQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABkm L6dLot3/abfv/2y78/9ir+f/n9f//5jV//9kse3/h8z9/5PR7P8eRWD/ACtV/wg+Yv8HRXP/AyhE+wAA ADcAAAAAAAAAAAAAAAAAAAAAAAAAAAkLDlImZJDzOWOG5xEmNbkEBQZYAAAALAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD0iYpL7OJ/o/yd/vf8pgsT/Npjc/zCM 1P8kfL//Fm2p/wYwS9MBCAtuAAAAFgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAWJS2naLns/1Cl6P+Lzv7/abns/3/E8/+e2f//Ya/p/5TY//9pmLL/ACBE/wM4 X/8FQGv/CENw/wUnQ/sAAAA3AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBQAAAAC8AAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABADMFAgF+LT9KwE5wifdKa4HwPGB++jVj hv87ZYb/OWSD/wwwX/8DKFn/ATNZ/AEJDr4AAAJvAAAACgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEh0mom6+8P9csev/lNL8/3a+8v9yvPD/m9j//3rA 8v97yPz/a5ev/wAlSP8EPmj/B0Bp/wZBcP8ELET7AAAANwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMBMfI5lLdI7zUZ3W/3q8 7P+n4///od///6bh//+t5v//iNX//3zJ//9vvv3/Xa7x/yd0yP8CWqn/AFua/gAlP9IAAgJdAAAAEQAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGjJOYs17yvz/Wq3s/4vL +P92v/H/dL7w/5vU//+Mzvz/YbLl/ydKZv8AK1P/CD5k/wdBa/8HQXD/AyxE+wAAADcAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAaEBwjpEh0 h+5osN7/OJjq/yaT7P8ikvT/I5Pu/x2N8v9FpPH/YrT1/6fW+v+h1v//dcD//4LF//+DzP//So7V/wBp wP8Aes//AFmc/wAhN7EAAAARAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AEVSjaj/hNL//4jM+P9os+r/ldL9/2Ox6f+W1P3/isz9/2i45/8iQlz/ADFZ/wg/af8IQGr/B0Rx/wUp QvsAAAA3AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAABWJTpPyXXA9P+E0P//MpDg/w11z/8pmPL/LJbw/yiX8v8rlfD/JJPy/xqM8f84nO//m9T7/3/E //92w///ecL//4LN/v9Eis//AGG0/wB/1f8Abbv/AB0usgAAAAkAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAABCTYSl/pHZ//+Rz/v/Xq/q/5zY/v9drun/ldP9/43P//9Xos7/GDxY/wA4 Xv8GPmj/B0Bq/wVDbv8EKET7AAAANwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAANFBt6WZOx/XjG9P+Cyv//Wqzy/wluyP8lj+j/LZfz/yqY8P8rlPH/K5jw/yuV 8v8tl+7/F47y/4fF9/+Qz///db3//3vC//96wP//g8f//0uQ0v8CZLj/AHnN/wBntf8ACA6LAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQlSJpv6Y3v//jM37/2Ky6f+a1f7/Xq7n/5XP /P+T2v//JWKU/wAkRv8IPmn/B0Fq/wc/Z/8IQXH/BjFL+wAAADcAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJERZ0X53F/4HT//9+wv//gMf//zOY7P8GaMP/FoHY/y6b 9P8rlfP/K5Xx/y+V8f8qmPP/J5Xv/yGR8f+Ty/n/isr//3a+/v93wv3/ecD//3vG//9qrur/EFep/wVg sP8EacL/ADBY4gAAAC0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEJUiKb+n9///4vM +v9isOj/mdf+/2Kw6v+T0v3/k9f//yVmlP8AKkz/B0Jq/wc+aP8GP2j/B0h1/wcwUvsAAAA3AAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALFBV1bKDB/4TT//94vf//eML//33E //80l+z/BGvF/w9xx/8bgd7/KJTu/yua8v8rk/D/J5Dv/ymY8v99vPj/lNH//3O8//98xPz/eL/8/4HF //9Fn+T/E4LU/x6P5P8trPz/HY/p/xVyvP8EGCKOAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAA9Yo6n/Z3j//+My/r/X7Hp/5rT/v9dsOj/lM/8/5TZ//8lZpn/AC1N/wg/av8GP2j/BD9p/wlK f/8GMVH7AAAANwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABOf6Gj/qbk //+g0vz/hsz//4nR//+Y2f//Tqrs/wRmwf8LccT/CG/E/w1uxf8Mb8r/Bm7L/zGV6P+o1v7/k8///3G+ //95wvv/eL77/3rB//97xvz/IozU/ySm9f8Xi+P/Kaj//yuq//8vuf//DDRM0gAAAAcAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAcm2Wp/+f3///jcv7/2Gy6f+a1P7/XrDo/5XS/f+U2P//JWuc/wEx Vv8IQWv/Bj9m/whBbP8KUIX/BjBR+wAAADcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAEDSpBu0up6/8ekvX/MZjw/7De+/+g5///nOb//5vh/v8ujtr/AmvJ/wdvyf8ahtj/LJDi/0yk 6/92v/7/esT//3zG//+Byf7/gsf//4HI//+Dxv//iMr+/yiK0f8Lgtb/HZrw/yyr//8qpP//KbX//w4z TNIAAAAHAAAAAAAAAAAAAAAAAAAAAAADAE4AAAAAAAcBRwBGDftQhKX/oeL//4zN+v9gsOf/ndf+/1+u 6/+U0P3/lNj//yZrov8AM1T/BkFt/wY8af8IRXP/ClGF/wUtUPsAAAA3AAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAASQxPjP4hmPf/Mpv2/yCO8P+i1/r/pen//5zg//+d5P//kNr8/2bA +f9WsPX/abj7/3/E//99x///gsf//3vB+/9Pjtb/PoHJ/zuZ3P88lt3/O5rd/0KY2/8ahtX/JaP5/yqn //8opv//KrH//yeQz/8HEyGKAAAAAAAAAAAAAAAAAAAAAAAAAAABMwXcBSkQhwEHBXgJbz//LF6G/4nQ //+KzPf/U6TY/47L+f9VqN7/lNH8/5TX//8hYpT/ADNa/wdAaf8HQWr/Ckh6/wlQhf8JL0/7AAAANwAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBYpMq4Mb8b/EHjT/yCK6f8OeNf/esfs/6Lp //+b4f//m+T//5zk//+m6f//ktj//3nB//96wf//hcf//2ar6v8ZY7T/A2q5/wBow/8AcMX/AHjP/wB3 0v8AddX/E5ju/zCy//8prv//LrL//yOP0/8IFB2FAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAQUCUQ5X NeARhz7/D5Yo/wc8C/8lbJv/QJri/yt0sP9Vruz/V6Ta/5bV/f+Mx/L/DT5f/wU+aP8GPmn/Bj9r/wlM ff8KToL/BjFQ+wAAADcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAElcip79OJnm/wBj v/8DacD/OZLV/5bd+f+g5P//l+H//5zh//+c4v//lt3//37G//92vv//fsf//2Sp6P8RZrf/AHfM/wB6 0P8AftL/AIDW/wB3zP8ARHXrACNB0wEmPtQQXZTuI5jj/x6Iwf8FGieLAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAMzHdIELhjNC24d/w6fJf8LjxT/DGxN/wxAc/8FQy3/JXq+/0Kb3f+Kzvr/b7jw/ws9 Yv8GPmf/BUBo/wlEcf8JSn3/C0t6/wYxUfoAAAAxAAAAAgAAAAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAWJy2ykdn3/4rV/f9Vq+L/Z7jp/5/k//+g6v//meH//57k//+X5P//n+T//5Xd//94wf//e8D//37E /f8jbLf/AHHK/wB8z/8AetD/AH3U/wBstf4AER+pAAAANwAAAAAAAAABAAEDSQIECYwAAgRoAAAABAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABFAtaDGk6/A+hOP8Mkgr/CWYA/wyRIf8HPxr/Dmww/xJT lf8ne73/W7Ly/1au7f8KOF3/Azpg/wc8Zv8LS33/BTpn/wc5av8BJUP8AQgAkAEiANEAAwBiAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAeRlVb0Jng/v+e6P//pez//6bp//+b5P//meD//5nh//+b4v//neL//5ni //+GzP//dbz9/4DK//9cotn/AmC8/wB+0f8AeM//AHzS/wB5y/8AFiauAAAAAQAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACsHLBTZBU8L/whm Bv8IXQD/BEMC/wdWCf8EHhv/BhwY/w0+Yv8QSoX/AiVA/wAuUf8BM1r/AyhM/wEdGP8DHwv/BjoI/wpM AP8DHwCnAAAADwAAAAAAAAAAAAAAAAAAAAAAAAAAAAABZpi1vv+g6P//nOL//5zh//+X3///neL//5vi //+e4v//muP//5zi//+c4f//g83//3jB//96vfj/IHHA/wByyv8Aes7/AHzQ/wN90f8Fc8n/AAwWoAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAe Ar0ITCnKBEoY9AGFFf8PlT//B38C/wVaAP8ISxf/EJot/wNdAP8BMAD/BUUA/wMoB/8CDhH/AhUQ/wU3 BP8FQQD/BEgA/wZKAP8DQgD/AykAzwIkALgAAwAoAAAAAAAAAAAAAAAAAAAAAAAAACk5WWzbxfH//5jh //+b5P//neD//5nh//+Z5P//muH//53i//+b4f//nOX//4TL//94w///crf2/wtftf8AfdP/AXvP/wV4 zv8Ae9L/AHfI/wAOGqMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAJAKXBlUt2g9MIfRbRAf/OS8K/weWLv8SpDb/E5o//wp4C/8GVwD/CXsA/wls AP8JcgD/BDwA/wQxAP8FPQD/BkIA/wU+AP8EOQD/AzIA/wMiAPMHVwD0AS4A1AAAAA0AAAAAAAAAAAAA AAAAAAAVMEFNyc7y/v+b4///mOD//5rl//+c4v//m+D//53k//+c4///md///53k//+HzP//fMH7/3e8 9/8QZLj/AHnO/wB70P8Dfc3/AHvV/wB2w/8ADROeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAEOqThTzemMq/w97Mv8JciH/DpQb/wVL Av8Fagf/CEkS/wZbAP8GWwD/BEUA/wZEAP8DLAD/AygA/wMnAP8DIwD/BEUA/wg6AP8BFADEAAIARgAG AG0AAAAMAAAAAAAAAAAAAAAADB03xJzK6//S9f//muD//53k//+a4f//muH//5rk//+a4f//muL//5nj //+d5P//ldv//3zJ//92uvb/DF21/wB/0v8AfNL/AHfN/wB90v8CftX/ATtr4QAAAC0AAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAADQAAACsAAAASAAAAAAAAAAAAAAAAAAAAAAAAAAAMOkCvToBt/wdb S/8OcXb/Ah8F/wVNAP8FPQD/C20S/xKcQP8OjjT/CGwC/wRVAP8FIgD/B0gA/wdrAP8GUAD/BDAA/wQ0 AP8GUAD/B1QA/wACAF0AAAAAAAAAAAAAAAAAAAAAAAAAAGBeX/D5////xO///5ri//+Y3v//m+T//53h //+a4///nOH//5vj//+c3v//meL//5vj//+P0v//dr36/zN+yf8AWLH/A2/D/wF+0v8Ad87/BIDY/wFu uv8AEBuPAAAABAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAQgAfL7UAWIj/ACU5sgAAAAkAAAAAAAAAAAAA AAAAAAAPB2R58ALd//8S0+v/QP///0KXnf8HWQP/BD8A/wZfAP8HcAL/C3sT/wM6Ef8Ljxv/A0QA/wdp AP8KiQD/BWQA/whMAP8DNQD/BUoA/wQ8AP8DEwCcAAAAAAAAAAAAAAAAAAAAAAAAAABhXlzw9////8Ty //+X3v//muP//53i//+X4f//nOP//5rg//+b4f//muH//53j//+j5///aLfn/xyB1P8wlen/KIDa/wZh uv8AeM7/AHzR/wB3zf8Ag9z/AGWs/gAmQNIAAgNeAAAATwAAAEsADReCACZD2ABIfe4Adsf/Ap78/wOM 2/8ACRGLAAAAAAAAAAAAAAAAAAAABEI7I8jQ3rL/xv/0/7P///9wxcX/CVgb/wAvLv8COBT/CYIT/xOE KP9CZV//FH5K/wVLNv8ARkD/BlEA/wg9AP8DOQD/BUoA/wdJAPwEJgDGASoA5wAGAGwAAAAAAAAAAAAA AAAAAAAAYV5d8PP////T8P7/ruj+/5Xg//+a4f//nuD//5zk//+Z4v//nOL//5nj//+i6f//gs3s/xFx yf8klfH/KZjx/zCh/P8Wccn/AGvB/wF80v8Ces7/AHjN/wOB2f8Agtn/AFyb/wBWlP8AVZD/AGWu/wCF 4P8AhN3/AnHI/wOM4P8Dofb/AlWD5AABAUIAAAAAAAAAAAAAAAAFAABaxoo7///ttv/b////RpeO/wFI Iv8EYH7/AKe4/wNiT/8wl5T//7JP/21eHP8AVTD/AJjI/xCDtv9Qamn/BjAG/wIzAP8AAAB6AAAABAAB ACsAAAArAAAAAAAAAAAAAAAAAAAAAGBcW+vw/v//3fT9/8ju/v+T3///muX//5ri//+Y4f//neH//5ni //+c4v//n+b//zuW1/8IbMn/KJPu/yuX8/8rlfD/L5jx/xBsxP8BccX/BHzT/wB6z/8Fecz/AXnR/wCC 2f8Bgd7/AILb/wCB2/8AfND/AGjE/wNqwf8FnfT/BJbs/wSh+P8AV4TuAAgOcwAAAAAAAAAAAAAAACYU AXmqtpH/Ov///0fq6v96oIz/UWFH/6rWqP9M4t//Tvz//8XktP/Zp1b/N0MZ/wiTtf8Xve7/C7To/wAW EO4ANAD/ADMA4QADAEIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQDg5yvsHD/+f8///R9Pv/nuP//5ze //+b4v//nuL//5vi//+Z5P//n9///5jg/f8Uds3/CWzD/xuF2/8unPb/LZbx/yqX9f8wmvP/DV24/wB4 zv8AfND/AH3T/wB/zv8AetL/Bn7P/wF50f8Accb/B2O6/zF3uv8ZbcD/AJXm/wOW7f8Cnff/AIXJ/wAb J78AAAAAAAAAAAAAAAAAAAAAABkbdACAkPaAx7D/8r5e/4JHEv//qT//r9Gp/3L///99////zOjA//q+ aP9qtKj/r3M0/wil0v8ACg+CAAIAVQBEAOkAIwDJAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAYLm6 uv/i/f//3/P8/7zt//+U4P//m+H//5rj//+Z4P//nuD//57m//+X4f//KY3f/wZrw/8Nccr/H4fi/yqY 7/8umfX/K5v1/x9muv8IZrv/AGnB/wBqxP8AZ8D/AGvD/wBowv8IZ7z/M3bE/2aq4P+Ezv//erjt/xls wP8Aluz/BZ72/wKGyf8AGie/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIkGBUMk4xMFPJSOhf4a6ON/kzy 8v+F////nf///3L//f+M6dr/J7rZ/1OhpP8GZX7xAAAAFwAAAAAAAwA7AAQAPwAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAC53dXbf6Pn//9r0/P/U9P//nuH//5ni//+c4f//meH//5nl//+Z4f//o+j//2S7 8f8Aasr/CGrA/whvw/8VeNT/GYDd/w1yz/9gsu3/WqHj/ziAzP9Dis7/QIfQ/0CIzf89gc3/ZKfm/3/K //99xv//esD//4LL//9His//AH7W/wOl/v8DV4nzAAgNdwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAIAAAAhAAAAKgBcatIo/v//vfz1/8j78P9J+v7/C+b//y7J4/9Nrr//AzxJwwAAAAMAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANDEwt+b0+f/Y9f//2/L8/8nv+v+c5f//lt///5zj //+X4f//nuD//5zi//+h5f//U7Pv/w50zf8BacT/AWTA/wlswv9WquD/nuL//63d//+c2f//e8b//4DJ //+Ayf//gMj//3rF//90vP//f8X//33C//93wP//g8n+/0iP2f8Gd8X/ABorpAAAABgAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACg5VgJd87//ur//m++n/Iez//zfU4P/zvV3/vmEL/wIA AFYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgHB16trq//5////9jy /P/c8/3/ze7+/6Pm/P+b4f//nuD//5Lk//+W4P//meP//6Lm//+D0fj/c8Du/3TD7P+H0PT/nef//1Og 2/9XpOL/f8b6/5HO//96v///fcr//4XL//+Jzf//i9T//5Ta//+W4f//gcr//3jC//99x///ETFf8QAA ADkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8FAE6acCzu5M+T/xTp //9OxL//umsb/BsIAHEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAOSkhJxO/8/v/a+v//2fH7/9v1/P/Y8fr/zfH+/8fv+/+76vz/uOj//6Xm//+V4f//neX//6Pq //+i6///qe///2q66v8AW73/BnfU/xOC5P+j0fP/nN///5LY//+b5v//m+L//5ri//+n6P//rer//6bp //+R0f//b7jx/w4YH6EAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAANjMmE7gQXmr/ADxGxgMAAEoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUEBF+9vLv/5Pz//9fz+//Z8fr/2fb//9zz+v/b8fv/3/b+/9zy /v/Y9Pr/wu///6nn//+e6f//M4bQ/2Sx6f+F0vH/C2rG/wBiwP8id8r/qNjw/6Dp//+b4f//neD//53k //+f5v//hs/x/5vU+P+c0/r/rvD//1x9i/gAAAAxAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHUk5O0vb+/v/l/v//2fL9/9rx /f/Z9P3/2fP+/9ry+f/Y8/7/2vL9/+Dx/P/Y9Pr/tej//3jF7/+Gzfb/o+v//4HM8/9sv+j/kdLz/6Po //+c4P//neX//5rh//+e6P//dcHr/xF0y/8fke7/Ipj9/ymCzP8kIyWMAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEB ATFYWFfawMjJ/977///Z9f3/2O/7/9r2/P/Z8v7/2fL9/9j1+v/Z9f7/2vP+/+D0///S9///ouf//5Tf //+i5///oez//5zk//+V3///mOH//5fh//+b4///md/+/xqA0/8Gacb/Jpb3/yiGz/8IFB6LAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAADiIfH6Hq7e3/5P///+b9///l/v//6f///+P5///d9f//2fP5/9ry /f/Z8vr/3PT8/8vv+v+i5P//n+j//6Hk//+m5v//o+f//6Xl//+c5P//nOL//5Xg/f8cfsz/BHHS/wxl sP8FERyFAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAue3p64tve3P+FtNP/SprP/0qV zf+dz+v/3vv//9/0/f/Z8v3/2fT6/9ny/P/a9P//1/L9/9Tw/v/X8f3/1vD+/9Xx/f/X8f3/ye3+/7Pr //+88P//ltz6/xhosf8ADRmLAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEB ADEJCAiEAgMHgQAABoAAAAB6ASI9wjp5rf/a9///3fb8/9fz/f/a8/7/2fL+/9ry+v/e9///4/7//9z8 ///e/P//5Pz8/9r1/v/o/P//6v3//73Lz/8YICKLAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAdeUn2X7uz9///Z8///1vP5/9j0 ///d9/7/vOby/0aCrf8sdaj/OHql/5LC2f/o////vcvO/1phZd4NDQx2AAAABAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgm JaDl6ej/9f///+/////x/////P///1Noc+AAAABWAAAAUAAAAEoDGSiaR1BX3xUWFHsAAAAeAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAzSklJ7IqKifOBgYHwg4KC8YOBgfYjISGFAAAAAAAAAAAAAAAAAAAABAAA AAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAA////////////8AAeAB/////gAAwAA////+AADAAD////wAAEAAH////AAAAAAP///8AAAAAA f///gAAAAAA///+AAAAAAB///4AAAAAAH///AAAAAAAP//8AAAAAAAf//gAAAAAAA//+AAAAAAAB//4A AAAAAAD//AAAAAAAAH/8AAAAAgAAf/gAAAACAAB/+AAAAAYAAH/4AAQABwAAf/gABAAPgAB/8AAOAA+A AH/wAA4AH8AA//AADwA/wAD/8AAPAH/AAP/wAA+B/+AB//AAD8/+AAP/8AAP//gAAP/gAA//4AAAf+AA D//AAAA/4AAP/4AAAD/gAA//AAAAH+AAD/4AAAAf4AAP/AAAAA/gAA/4AAAAD0AAD/gAAAAfAAAP8AAA AB8AAA/wAAAAfwAAA/AAABB/AAAD4AAAP/+AAAPgAAB//wAAAeAAAH//AAAA4AAAf/+AAADgAAA/j4AA A+AAAB4HAAAD4AAAAAcAAAHgAAAAA4AAAeAAAAABwAAH4AAAAAHgAAPgAAAAAfAAJ+AAAAAB+AA/8AAA AAH/AH/wAAAAA/+A//AAAAAH/8H/+AAAAAf////4AAAAD/////wAAAAf/////gAAAB//////AAAAf/// //+AAAD///////wAAP///////wAD////////Ac////////////8= ================================================ FILE: WzComparerR2/HistoryList.cs ================================================ using System; using System.Collections; using System.Collections.Generic; using System.Text; namespace WzComparerR2 { public class HistoryList : IEnumerable { public HistoryList() { this.stackPrev = new Stack(); this.stackNext = new Stack(); } private Stack stackPrev; private Stack stackNext; /// /// 在历史列表中添加一个新项,这会舍弃全部next列表中的项。 /// /// 要添加的新项。 public void Add(T item) { this.stackPrev.Push(item); this.stackNext.Clear(); } public void AddRange(IEnumerable collection) { foreach (T item in collection) { this.stackPrev.Push(item); } this.stackNext.Clear(); } /// /// 返回历史列表的上一项。 /// /// /// public T MovePrev() { stackNext.Push(stackPrev.Pop()); return this.Current; } /// /// 返回历史列表的下一项。 /// /// /// public T MoveNext() { stackPrev.Push(stackNext.Pop()); return this.Current; } /// /// 获取历史列表的当前项。 /// /// /// public T Current { get { if (stackPrev.Count > 0) return stackPrev.Peek(); else throw new InvalidOperationException("当前列表中没有添加项。"); } } public int PrevCount { get { return stackPrev.Count == 0 ? 0 : stackPrev.Count - 1; } } public int NextCount { get { return stackNext.Count; } } public int Count { get { return stackPrev.Count + stackNext.Count; } } public void Clear() { this.stackPrev.Clear(); this.stackNext.Clear(); } public IEnumerator GetEnumerator() { List lst = new List(this.stackPrev.Count + this.stackNext.Count); lst.AddRange(stackNext); lst.Reverse(0, lst.Count); lst.AddRange(stackPrev); return lst.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return this.GetEnumerator(); } } } ================================================ FILE: WzComparerR2/ImageDragHandler.cs ================================================ using System; using System.Collections.Generic; using System.Text; using System.Windows.Forms; using System.Drawing; using System.IO; using WzComparerR2.Common; namespace WzComparerR2 { public class ImageDragHandler { public ImageDragHandler(PictureBox owner) { this.OwnerControl = owner; this.dragBox = Rectangle.Empty; } public PictureBox OwnerControl { get; private set; } private Rectangle dragBox; public void AttachEvents() { this.OwnerControl.MouseDown += OwnerControl_MouseDown; this.OwnerControl.MouseMove += OwnerControl_MouseMove; this.OwnerControl.MouseUp += OwnerControl_MouseUp; } void OwnerControl_MouseDown(object sender, MouseEventArgs e) { if (e.Button == MouseButtons.Left && OwnerControl.Image != null) { this.dragBox = new Rectangle(e.Location, SystemInformation.DragSize); } } void OwnerControl_MouseMove(object sender, MouseEventArgs e) { if (e.Button == MouseButtons.Left && OwnerControl.Image != null && this.dragBox != Rectangle.Empty && !this.dragBox.Contains(e.Location)) { string fileName = Convert.ToString(OwnerControl.Tag); ImageDataObject dataObj = new ImageDataObject(OwnerControl.Image, fileName); var dragEff = this.OwnerControl.DoDragDrop(dataObj, DragDropEffects.Copy); } } void OwnerControl_MouseUp(object sender, MouseEventArgs e) { this.dragBox = Rectangle.Empty; } } } ================================================ FILE: WzComparerR2/MainForm.Designer.cs ================================================ namespace WzComparerR2 { partial class MainForm { /// /// 必需的设计器变量。 /// private System.ComponentModel.IContainer components = null; /// /// 清理所有正在使用的资源。 /// /// 如果应释放托管资源,为 true;否则为 false。 protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows 窗体设计器生成的代码 /// /// 设计器支持所需的方法 - 不要 /// 使用代码编辑器修改此方法的内容。 /// private void InitializeComponent() { this.components = new System.ComponentModel.Container(); System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainForm)); this.ribbonControl1 = new DevComponents.DotNetBar.RibbonControl(); this.ribbonPanel2 = new DevComponents.DotNetBar.RibbonPanel(); this.ribbonBar8 = new DevComponents.DotNetBar.RibbonBar(); this.itemContainer37 = new DevComponents.DotNetBar.ItemContainer(); this.itemContainer38 = new DevComponents.DotNetBar.ItemContainer(); this.comboBoxItemCharacter = new DevComponents.DotNetBar.ComboBoxItem(); this.itemContainer39 = new DevComponents.DotNetBar.ItemContainer(); this.itemContainer40 = new DevComponents.DotNetBar.ItemContainer(); this.buttonItemCreateChara = new DevComponents.DotNetBar.ButtonItem(); this.buttonItemEdit = new DevComponents.DotNetBar.ButtonItem(); this.itemContainer41 = new DevComponents.DotNetBar.ItemContainer(); this.buttonItemLoadChara = new DevComponents.DotNetBar.ButtonItem(); this.buttonItemSaveChara = new DevComponents.DotNetBar.ButtonItem(); this.itemContainer23 = new DevComponents.DotNetBar.ItemContainer(); this.itemContainer24 = new DevComponents.DotNetBar.ItemContainer(); this.comboBoxItemLanguage = new DevComponents.DotNetBar.ComboBoxItem(); this.comboItem13 = new DevComponents.Editors.ComboItem(); this.comboItem14 = new DevComponents.Editors.ComboItem(); this.comboItem15 = new DevComponents.Editors.ComboItem(); this.comboItem16 = new DevComponents.Editors.ComboItem(); this.comboItem17 = new DevComponents.Editors.ComboItem(); this.comboItem18 = new DevComponents.Editors.ComboItem(); this.itemContainer25 = new DevComponents.DotNetBar.ItemContainer(); this.buttonItemQuickView = new DevComponents.DotNetBar.ButtonItem(); this.itemContainer42 = new DevComponents.DotNetBar.ItemContainer(); this.buttonItemAutoQuickView = new DevComponents.DotNetBar.ButtonItem(); this.buttonItemQuickViewSetting = new DevComponents.DotNetBar.ButtonItem(); this.itemContainer26 = new DevComponents.DotNetBar.ItemContainer(); this.buttonItemSetItems = new DevComponents.DotNetBar.ButtonItem(); this.itemContainer43 = new DevComponents.DotNetBar.ItemContainer(); this.buttonItemClearSetItems = new DevComponents.DotNetBar.ButtonItem(); this.itemContainer28 = new DevComponents.DotNetBar.ItemContainer(); this.itemContainer29 = new DevComponents.DotNetBar.ItemContainer(); this.buttonItemCharItem = new DevComponents.DotNetBar.ButtonItem(); this.itemContainer30 = new DevComponents.DotNetBar.ItemContainer(); this.buttonItemCharaStat = new DevComponents.DotNetBar.ButtonItem(); this.itemContainer31 = new DevComponents.DotNetBar.ItemContainer(); this.buttonItemCharaEquip = new DevComponents.DotNetBar.ButtonItem(); this.itemContainer32 = new DevComponents.DotNetBar.ItemContainer(); this.itemContainer33 = new DevComponents.DotNetBar.ItemContainer(); this.buttonItemAddItem = new DevComponents.DotNetBar.ButtonItem(); this.itemContainer34 = new DevComponents.DotNetBar.ItemContainer(); this.itemContainer35 = new DevComponents.DotNetBar.ItemContainer(); this.ribbonBar3 = new DevComponents.DotNetBar.RibbonBar(); this.itemContainer6 = new DevComponents.DotNetBar.ItemContainer(); this.itemContainer7 = new DevComponents.DotNetBar.ItemContainer(); this.labelItemSoundTitle = new DevComponents.DotNetBar.LabelItem(); this.itemContainer9 = new DevComponents.DotNetBar.ItemContainer(); this.sliderItemSoundTime = new DevComponents.DotNetBar.SliderItem(); this.checkBoxItemSoundLoop = new DevComponents.DotNetBar.CheckBoxItem(); this.itemContainer18 = new DevComponents.DotNetBar.ItemContainer(); this.labelItemSoundTime = new DevComponents.DotNetBar.LabelItem(); this.itemContainer13 = new DevComponents.DotNetBar.ItemContainer(); this.buttonItemLoadSound = new DevComponents.DotNetBar.ButtonItem(); this.buttonItemSoundPlay = new DevComponents.DotNetBar.ButtonItem(); this.buttonItemSoundStop = new DevComponents.DotNetBar.ButtonItem(); this.buttonItemSoundSave = new DevComponents.DotNetBar.ButtonItem(); this.sliderItemSoundVol = new DevComponents.DotNetBar.SliderItem(); this.ribbonPanel1 = new DevComponents.DotNetBar.RibbonPanel(); this.ribbonBar9 = new DevComponents.DotNetBar.RibbonBar(); this.buttonItemPatcher = new DevComponents.DotNetBar.ButtonItem(); this.ribbonBar4 = new DevComponents.DotNetBar.RibbonBar(); this.itemContainer10 = new DevComponents.DotNetBar.ItemContainer(); this.itemContainer8 = new DevComponents.DotNetBar.ItemContainer(); this.labelItem2 = new DevComponents.DotNetBar.LabelItem(); this.textBoxItemSearchString = new DevComponents.DotNetBar.TextBoxItem(); this.itemContainer11 = new DevComponents.DotNetBar.ItemContainer(); this.checkBoxItemExact2 = new DevComponents.DotNetBar.CheckBoxItem(); this.comboBoxItem2 = new DevComponents.DotNetBar.ComboBoxItem(); this.comboItem3 = new DevComponents.Editors.ComboItem(); this.comboItem4 = new DevComponents.Editors.ComboItem(); this.comboItem5 = new DevComponents.Editors.ComboItem(); this.comboItem6 = new DevComponents.Editors.ComboItem(); this.comboItem7 = new DevComponents.Editors.ComboItem(); this.comboItem8 = new DevComponents.Editors.ComboItem(); this.comboItem9 = new DevComponents.Editors.ComboItem(); this.itemContainer12 = new DevComponents.DotNetBar.ItemContainer(); this.checkBoxItemRegex2 = new DevComponents.DotNetBar.CheckBoxItem(); this.buttonItemSearchString = new DevComponents.DotNetBar.ButtonItem(); this.buttonItemSelectStringWz = new DevComponents.DotNetBar.ButtonItem(); this.buttonItemClearStringWz = new DevComponents.DotNetBar.ButtonItem(); this.ribbonBar1 = new DevComponents.DotNetBar.RibbonBar(); this.itemContainer14 = new DevComponents.DotNetBar.ItemContainer(); this.itemContainer15 = new DevComponents.DotNetBar.ItemContainer(); this.labelItem3 = new DevComponents.DotNetBar.LabelItem(); this.textBoxItemSearchWz = new DevComponents.DotNetBar.TextBoxItem(); this.itemContainer16 = new DevComponents.DotNetBar.ItemContainer(); this.checkBoxItemExact1 = new DevComponents.DotNetBar.CheckBoxItem(); this.comboBoxItem1 = new DevComponents.DotNetBar.ComboBoxItem(); this.comboItem10 = new DevComponents.Editors.ComboItem(); this.comboItem11 = new DevComponents.Editors.ComboItem(); this.comboItem12 = new DevComponents.Editors.ComboItem(); this.itemContainer17 = new DevComponents.DotNetBar.ItemContainer(); this.checkBoxItemRegex1 = new DevComponents.DotNetBar.CheckBoxItem(); this.buttonItemSearchWz = new DevComponents.DotNetBar.ButtonItem(); this.ribbonPanel3 = new DevComponents.DotNetBar.RibbonPanel(); this.ribbonBar11 = new DevComponents.DotNetBar.RibbonBar(); this.buttonItem1 = new DevComponents.DotNetBar.ButtonItem(); this.ribbonBar7 = new DevComponents.DotNetBar.RibbonBar(); this.buttonItemUpdate = new DevComponents.DotNetBar.ButtonItem(); this.ribbonBar6 = new DevComponents.DotNetBar.RibbonBar(); this.buttonItemAbout = new DevComponents.DotNetBar.ButtonItem(); this.ribbonTabItem1 = new DevComponents.DotNetBar.RibbonTabItem(); this.ribbonTabItem2 = new DevComponents.DotNetBar.RibbonTabItem(); this.ribbonTabItem3 = new DevComponents.DotNetBar.RibbonTabItem(); this.buttonItemStyle = new DevComponents.DotNetBar.ButtonItem(); this.office2007StartButton1 = new DevComponents.DotNetBar.Office2007StartButton(); this.itemContainer1 = new DevComponents.DotNetBar.ItemContainer(); this.itemContainer2 = new DevComponents.DotNetBar.ItemContainer(); this.itemContainer3 = new DevComponents.DotNetBar.ItemContainer(); this.btnItemOpenWz = new DevComponents.DotNetBar.ButtonItem(); this.buttonItemClose = new DevComponents.DotNetBar.ButtonItem(); this.buttonItemCloseAll = new DevComponents.DotNetBar.ButtonItem(); this.galleryContainerRecent = new DevComponents.DotNetBar.GalleryContainer(); this.labelItem8 = new DevComponents.DotNetBar.LabelItem(); this.itemContainer4 = new DevComponents.DotNetBar.ItemContainer(); this.btnItemOptions = new DevComponents.DotNetBar.ButtonItem(); this.buttonItem13 = new DevComponents.DotNetBar.ButtonItem(); this.styleManager1 = new DevComponents.DotNetBar.StyleManager(this.components); this.colorPickerDropDown1 = new DevComponents.DotNetBar.ColorPickerDropDown(); this.ribbonBar2 = new DevComponents.DotNetBar.RibbonBar(); this.labelItemStatus = new DevComponents.DotNetBar.LabelItem(); this.progressBarItem1 = new DevComponents.DotNetBar.ProgressBarItem(); this.panelExMain = new DevComponents.DotNetBar.PanelEx(); this.panelExRight = new DevComponents.DotNetBar.PanelEx(); this.superTabControl1 = new DevComponents.DotNetBar.SuperTabControl(); this.superTabControlPanel1 = new DevComponents.DotNetBar.SuperTabControlPanel(); this.panelEx2 = new DevComponents.DotNetBar.PanelEx(); this.pictureBoxEx1 = new WzComparerR2.PictureBoxEx(); this.ribbonBar5 = new DevComponents.DotNetBar.RibbonBar(); this.cmbItemAniNames = new DevComponents.DotNetBar.ComboBoxItem(); this.cmbItemSkins = new DevComponents.DotNetBar.ComboBoxItem(); this.buttonItemSaveImage = new DevComponents.DotNetBar.ButtonItem(); this.itemContainer27 = new DevComponents.DotNetBar.ItemContainer(); this.buttonItemAutoSave = new DevComponents.DotNetBar.ButtonItem(); this.buttonItemAutoSaveFolder = new DevComponents.DotNetBar.ButtonItem(); this.labelItemAutoSaveFolder = new DevComponents.DotNetBar.LabelItem(); this.buttonItemGif = new DevComponents.DotNetBar.ButtonItem(); this.itemContainer36 = new DevComponents.DotNetBar.ItemContainer(); this.buttonItemExtractGifEx = new DevComponents.DotNetBar.ButtonItem(); this.buttonItemGifSetting = new DevComponents.DotNetBar.ButtonItem(); this.colorPickerPicBoxBgColor = new DevComponents.DotNetBar.ColorPickerDropDown(); this.textBoxX1 = new DevComponents.DotNetBar.Controls.TextBoxX(); this.expandableSplitter1 = new DevComponents.DotNetBar.ExpandableSplitter(); this.panelEx1 = new DevComponents.DotNetBar.PanelEx(); this.advTree3 = new DevComponents.AdvTree.AdvTree(); this.columnHeader3 = new DevComponents.AdvTree.ColumnHeader(); this.columnHeader4 = new DevComponents.AdvTree.ColumnHeader(); this.columnHeader5 = new DevComponents.AdvTree.ColumnHeader(); this.contextMenuStrip2 = new System.Windows.Forms.ContextMenuStrip(this.components); this.tsmi2SaveAs = new System.Windows.Forms.ToolStripMenuItem(); this.tsmi2HandleUol = new System.Windows.Forms.ToolStripMenuItem(); this.tsmi2Splitter1 = new System.Windows.Forms.ToolStripSeparator(); this.tsmi2ExpandAll = new System.Windows.Forms.ToolStripMenuItem(); this.tsmi2CollapseAll = new System.Windows.Forms.ToolStripMenuItem(); this.toolStripMenuItem1 = new System.Windows.Forms.ToolStripSeparator(); this.tsmi2ExpandLevel = new System.Windows.Forms.ToolStripMenuItem(); this.tsmi2CollapseLevel = new System.Windows.Forms.ToolStripMenuItem(); this.toolStripMenuItem2 = new System.Windows.Forms.ToolStripSeparator(); this.tsmi2Prev = new System.Windows.Forms.ToolStripMenuItem(); this.tsmi2Next = new System.Windows.Forms.ToolStripMenuItem(); this.imageList1 = new System.Windows.Forms.ImageList(this.components); this.nodeConnector3 = new DevComponents.AdvTree.NodeConnector(); this.elementStyle3 = new DevComponents.DotNetBar.ElementStyle(); this.listViewExWzDetail = new DevComponents.DotNetBar.Controls.ListViewEx(); this.columnHeader1 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); this.columnHeader2 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); this.superTabItem1 = new DevComponents.DotNetBar.SuperTabItem(); this.superTabControlPanel2 = new DevComponents.DotNetBar.SuperTabControlPanel(); this.chkResolvePngLink = new DevComponents.DotNetBar.Controls.CheckBoxX(); this.btnCustomCSS = new DevComponents.DotNetBar.ButtonX(); this.superTooltip1 = new DevComponents.DotNetBar.SuperTooltip(); this.chkOutputRemovedImg = new DevComponents.DotNetBar.Controls.CheckBoxX(); this.chkOutputAddedImg = new DevComponents.DotNetBar.Controls.CheckBoxX(); this.labelX1 = new DevComponents.DotNetBar.LabelX(); this.chkOutputPng = new DevComponents.DotNetBar.Controls.CheckBoxX(); this.cmbComparePng = new DevComponents.DotNetBar.Controls.ComboBoxEx(); this.labelXComp2 = new DevComponents.DotNetBar.LabelX(); this.labelXComp1 = new DevComponents.DotNetBar.LabelX(); this.btnEasyCompare = new DevComponents.DotNetBar.ButtonX(); this.superTabItem2 = new DevComponents.DotNetBar.SuperTabItem(); this.superTabControlPanel3 = new DevComponents.DotNetBar.SuperTabControlPanel(); this.btnExportSkillOption = new DevComponents.DotNetBar.ButtonX(); this.btnExportSkill = new DevComponents.DotNetBar.ButtonX(); this.superTabItem3 = new DevComponents.DotNetBar.SuperTabItem(); this.btnNodeBack = new DevComponents.DotNetBar.ButtonItem(); this.btnNodeForward = new DevComponents.DotNetBar.ButtonItem(); this.panelExLeft = new DevComponents.DotNetBar.PanelEx(); this.advTree2 = new DevComponents.AdvTree.AdvTree(); this.nodeConnector2 = new DevComponents.AdvTree.NodeConnector(); this.elementStyle2 = new DevComponents.DotNetBar.ElementStyle(); this.expandableSplitter2 = new DevComponents.DotNetBar.ExpandableSplitter(); this.advTree1 = new DevComponents.AdvTree.AdvTree(); this.contextMenuStrip1 = new System.Windows.Forms.ContextMenuStrip(this.components); this.tsmi1Sort = new System.Windows.Forms.ToolStripMenuItem(); this.toolStripMenuItem3 = new System.Windows.Forms.ToolStripSeparator(); this.tsmi1Export = new System.Windows.Forms.ToolStripMenuItem(); this.tsmi1DumpAsXml = new System.Windows.Forms.ToolStripMenuItem(); this.nodeConnector1 = new DevComponents.AdvTree.NodeConnector(); this.elementStyle1 = new DevComponents.DotNetBar.ElementStyle(); this.listViewExString = new DevComponents.DotNetBar.Controls.ListViewEx(); this.columnHeader6 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); this.columnHeader7 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); this.columnHeader8 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); this.columnHeader9 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); this.comboItem1 = new DevComponents.Editors.ComboItem(); this.comboItem2 = new DevComponents.Editors.ComboItem(); this.dotNetBarManager1 = new DevComponents.DotNetBar.DotNetBarManager(this.components); this.dockSite4 = new DevComponents.DotNetBar.DockSite(); this.bar1 = new DevComponents.DotNetBar.Bar(); this.panelDockContainer1 = new DevComponents.DotNetBar.PanelDockContainer(); this.dockContainerItem1 = new DevComponents.DotNetBar.DockContainerItem(); this.dockSite1 = new DevComponents.DotNetBar.DockSite(); this.dockSite2 = new DevComponents.DotNetBar.DockSite(); this.dockSite8 = new DevComponents.DotNetBar.DockSite(); this.dockSite5 = new DevComponents.DotNetBar.DockSite(); this.dockSite6 = new DevComponents.DotNetBar.DockSite(); this.dockSite7 = new DevComponents.DotNetBar.DockSite(); this.dockSite3 = new DevComponents.DotNetBar.DockSite(); this.dockContainerItem2 = new DevComponents.DotNetBar.DockContainerItem(); this.panelDockContainer2 = new DevComponents.DotNetBar.PanelDockContainer(); this.chkHashPngFileName = new DevComponents.DotNetBar.Controls.CheckBoxX(); this.btnItemOpenImg = new DevComponents.DotNetBar.ButtonItem(); this.buttonItemSaveWithOptions = new DevComponents.DotNetBar.ButtonItem(); this.toolStripMenuItem4 = new System.Windows.Forms.ToolStripSeparator(); this.tsmi2CopyFullPath = new System.Windows.Forms.ToolStripMenuItem(); this.comboItem19 = new DevComponents.Editors.ComboItem(); this.ribbonControl1.SuspendLayout(); this.ribbonPanel1.SuspendLayout(); this.ribbonPanel2.SuspendLayout(); this.ribbonPanel3.SuspendLayout(); this.panelExMain.SuspendLayout(); this.panelExRight.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.superTabControl1)).BeginInit(); this.superTabControl1.SuspendLayout(); this.superTabControlPanel1.SuspendLayout(); this.panelEx2.SuspendLayout(); this.panelEx1.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.advTree3)).BeginInit(); this.contextMenuStrip2.SuspendLayout(); this.superTabControlPanel2.SuspendLayout(); this.superTabControlPanel3.SuspendLayout(); this.panelExLeft.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.advTree2)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.advTree1)).BeginInit(); this.contextMenuStrip1.SuspendLayout(); this.dockSite4.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.bar1)).BeginInit(); this.bar1.SuspendLayout(); this.panelDockContainer1.SuspendLayout(); this.SuspendLayout(); // // ribbonControl1 // // // // this.ribbonControl1.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.ribbonControl1.CanCustomize = false; this.ribbonControl1.CaptionVisible = true; this.ribbonControl1.Controls.Add(this.ribbonPanel1); this.ribbonControl1.Controls.Add(this.ribbonPanel2); this.ribbonControl1.Controls.Add(this.ribbonPanel3); this.ribbonControl1.Dock = System.Windows.Forms.DockStyle.Top; this.ribbonControl1.Expanded = false; this.ribbonControl1.Items.AddRange(new DevComponents.DotNetBar.BaseItem[] { this.ribbonTabItem1, this.ribbonTabItem2, this.ribbonTabItem3, this.buttonItemStyle}); this.ribbonControl1.KeyTipsFont = new System.Drawing.Font("Tahoma", 7F); this.ribbonControl1.Location = new System.Drawing.Point(5, 1); this.ribbonControl1.Name = "ribbonControl1"; this.ribbonControl1.Padding = new System.Windows.Forms.Padding(0, 0, 0, 3); this.ribbonControl1.QuickToolbarItems.AddRange(new DevComponents.DotNetBar.BaseItem[] { this.office2007StartButton1}); this.ribbonControl1.Size = new System.Drawing.Size(740, 153); this.ribbonControl1.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.ribbonControl1.SystemText.MaximizeRibbonText = "&Maximize the Ribbon"; this.ribbonControl1.SystemText.MinimizeRibbonText = "Mi&nimize the Ribbon"; this.ribbonControl1.SystemText.QatAddItemText = "&Add to Quick Access Toolbar"; this.ribbonControl1.SystemText.QatCustomizeMenuLabel = "Customize Quick Access Toolbar"; this.ribbonControl1.SystemText.QatCustomizeText = "&Customize Quick Access Toolbar..."; this.ribbonControl1.SystemText.QatDialogAddButton = "&Add >>"; this.ribbonControl1.SystemText.QatDialogCancelButton = "Cancel"; this.ribbonControl1.SystemText.QatDialogCaption = "Customize Quick Access Toolbar"; this.ribbonControl1.SystemText.QatDialogCategoriesLabel = "&Choose commands from:"; this.ribbonControl1.SystemText.QatDialogOkButton = "OK"; this.ribbonControl1.SystemText.QatDialogPlacementCheckbox = "&Place Quick Access Toolbar below the Ribbon"; this.ribbonControl1.SystemText.QatDialogRemoveButton = "&Remove"; this.ribbonControl1.SystemText.QatPlaceAboveRibbonText = "&Place Quick Access Toolbar above the Ribbon"; this.ribbonControl1.SystemText.QatPlaceBelowRibbonText = "&Place Quick Access Toolbar below the Ribbon"; this.ribbonControl1.SystemText.QatRemoveItemText = "&Remove from Quick Access Toolbar"; this.ribbonControl1.TabGroupHeight = 14; this.ribbonControl1.TabIndex = 0; this.ribbonControl1.Text = "ribbonControl1"; this.ribbonControl1.UseCustomizeDialog = false; // // ribbonPanel2 // this.ribbonPanel2.ColorSchemeStyle = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.ribbonPanel2.Controls.Add(this.ribbonBar8); this.ribbonPanel2.Controls.Add(this.ribbonBar3); this.ribbonPanel2.Dock = System.Windows.Forms.DockStyle.Fill; this.ribbonPanel2.Location = new System.Drawing.Point(0, 56); this.ribbonPanel2.Name = "ribbonPanel2"; this.ribbonPanel2.Padding = new System.Windows.Forms.Padding(3, 0, 3, 3); this.ribbonPanel2.Size = new System.Drawing.Size(740, 94); // // // this.ribbonPanel2.Style.CornerType = DevComponents.DotNetBar.eCornerType.Square; // // // this.ribbonPanel2.StyleMouseDown.CornerType = DevComponents.DotNetBar.eCornerType.Square; // // // this.ribbonPanel2.StyleMouseOver.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.ribbonPanel2.TabIndex = 2; this.ribbonPanel2.Visible = false; // // ribbonBar8 // this.ribbonBar8.AutoOverflowEnabled = true; // // // this.ribbonBar8.BackgroundMouseOverStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; // // // this.ribbonBar8.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.ribbonBar8.ContainerControlProcessDialogKey = true; this.ribbonBar8.Dock = System.Windows.Forms.DockStyle.Left; this.ribbonBar8.DragDropSupport = true; this.ribbonBar8.Items.AddRange(new DevComponents.DotNetBar.BaseItem[] { this.itemContainer37, this.itemContainer23, this.itemContainer28, this.itemContainer32}); this.ribbonBar8.Location = new System.Drawing.Point(265, 0); this.ribbonBar8.Name = "ribbonBar8"; this.ribbonBar8.Size = new System.Drawing.Size(270, 91); this.ribbonBar8.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.ribbonBar8.TabIndex = 1; this.ribbonBar8.Text = "CharaSim"; // // // this.ribbonBar8.TitleStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; // // // this.ribbonBar8.TitleStyleMouseOver.CornerType = DevComponents.DotNetBar.eCornerType.Square; // // itemContainer37 // // // // this.itemContainer37.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.itemContainer37.LayoutOrientation = DevComponents.DotNetBar.eOrientation.Vertical; this.itemContainer37.Name = "itemContainer37"; this.itemContainer37.SubItems.AddRange(new DevComponents.DotNetBar.BaseItem[] { this.itemContainer38, this.itemContainer39}); // // // this.itemContainer37.TitleStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.itemContainer37.Visible = false; // // itemContainer38 // // // // this.itemContainer38.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.itemContainer38.Name = "itemContainer38"; this.itemContainer38.SubItems.AddRange(new DevComponents.DotNetBar.BaseItem[] { this.comboBoxItemCharacter}); // // // this.itemContainer38.TitleStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; // // comboBoxItemCharacter // this.comboBoxItemCharacter.ComboWidth = 80; this.comboBoxItemCharacter.DropDownHeight = 106; this.comboBoxItemCharacter.ItemHeight = 16; this.comboBoxItemCharacter.Name = "comboBoxItemCharacter"; this.comboBoxItemCharacter.Text = "comboBoxItem3"; // // itemContainer39 // // // // this.itemContainer39.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.itemContainer39.Name = "itemContainer39"; this.itemContainer39.SubItems.AddRange(new DevComponents.DotNetBar.BaseItem[] { this.itemContainer40, this.itemContainer41}); // // // this.itemContainer39.TitleStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.itemContainer39.Visible = false; // // itemContainer40 // // // // this.itemContainer40.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.itemContainer40.LayoutOrientation = DevComponents.DotNetBar.eOrientation.Vertical; this.itemContainer40.Name = "itemContainer40"; this.itemContainer40.SubItems.AddRange(new DevComponents.DotNetBar.BaseItem[] { this.buttonItemCreateChara, this.buttonItemEdit}); // // // this.itemContainer40.TitleStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; // // buttonItemCreateChara // this.buttonItemCreateChara.Name = "buttonItemCreateChara"; this.buttonItemCreateChara.Text = "Create"; // // buttonItemEdit // this.buttonItemEdit.Name = "buttonItemEdit"; this.buttonItemEdit.Text = "Edit"; // // itemContainer41 // // // // this.itemContainer41.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.itemContainer41.LayoutOrientation = DevComponents.DotNetBar.eOrientation.Vertical; this.itemContainer41.Name = "itemContainer41"; this.itemContainer41.SubItems.AddRange(new DevComponents.DotNetBar.BaseItem[] { this.buttonItemLoadChara, this.buttonItemSaveChara}); // // // this.itemContainer41.TitleStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; // // buttonItemLoadChara // this.buttonItemLoadChara.Name = "buttonItemLoadChara"; this.buttonItemLoadChara.Text = "Load"; // // buttonItemSaveChara // this.buttonItemSaveChara.Name = "buttonItemSaveChara"; this.buttonItemSaveChara.Text = "Save"; // // itemContainer23 // // // // this.itemContainer23.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.itemContainer23.LayoutOrientation = DevComponents.DotNetBar.eOrientation.Vertical; this.itemContainer23.Name = "itemContainer23"; this.itemContainer23.SubItems.AddRange(new DevComponents.DotNetBar.BaseItem[] { this.itemContainer24, this.itemContainer25, this.itemContainer26}); // // // this.itemContainer23.TitleStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; // // itemContainer24 // // // // this.itemContainer24.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.itemContainer24.Name = "itemContainer24"; this.itemContainer24.SubItems.AddRange(new DevComponents.DotNetBar.BaseItem[] { this.comboBoxItemLanguage}); // // // this.itemContainer24.TitleStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; // // comboBoxItemLanguage // this.comboBoxItemLanguage.ComboWidth = 75; this.comboBoxItemLanguage.DropDownHeight = 106; this.comboBoxItemLanguage.ItemHeight = 16; this.comboBoxItemLanguage.Items.AddRange(new object[] { this.comboItem13, this.comboItem14, this.comboItem15, this.comboItem16, this.comboItem17, this.comboItem18}); this.comboBoxItemLanguage.Name = "comboBoxItemLanguage"; this.comboBoxItemLanguage.Text = "comboBoxItem3"; this.comboBoxItemLanguage.SelectedIndexChanged += new System.EventHandler(this.comboBoxItemLanguage_SelectedIndexChanged); // // comboItem14 // this.comboItem14.Text = "宋体"; // // comboItem15 // this.comboItem15.Text = "微软雅黑"; // // comboItem16 // this.comboItem16.Text = "MS Gothic"; // // comboItem17 // this.comboItem17.Text = "굴림체"; // // comboItem18 // this.comboItem18.Text = "돋움"; // // itemContainer25 // // // // this.itemContainer25.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.itemContainer25.Name = "itemContainer25"; this.itemContainer25.SubItems.AddRange(new DevComponents.DotNetBar.BaseItem[] { this.buttonItemQuickView}); // // // this.itemContainer25.TitleStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; // // buttonItemQuickView // this.buttonItemQuickView.Name = "buttonItemQuickView"; this.buttonItemQuickView.SubItems.AddRange(new DevComponents.DotNetBar.BaseItem[] { this.itemContainer42}); this.buttonItemQuickView.Text = "QuickView"; this.buttonItemQuickView.Click += new System.EventHandler(this.buttonItemQuickView_Click); // // itemContainer42 // // // // this.itemContainer42.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.itemContainer42.LayoutOrientation = DevComponents.DotNetBar.eOrientation.Vertical; this.itemContainer42.Name = "itemContainer42"; this.itemContainer42.SubItems.AddRange(new DevComponents.DotNetBar.BaseItem[] { this.buttonItemAutoQuickView, this.buttonItemQuickViewSetting}); // // // this.itemContainer42.TitleStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; // // buttonItemAutoQuickView // this.buttonItemAutoQuickView.AutoCheckOnClick = true; this.buttonItemAutoQuickView.Name = "buttonItemAutoQuickView"; this.buttonItemAutoQuickView.Text = "AutoQuickView"; this.buttonItemAutoQuickView.Tooltip = "开启/关闭自动预览机能"; this.buttonItemAutoQuickView.Click += new System.EventHandler(this.buttonItemAutoQuickView_Click); // // buttonItemQuickViewSetting // this.buttonItemQuickViewSetting.Name = "buttonItemQuickViewSetting"; this.buttonItemQuickViewSetting.Text = "Setting"; this.buttonItemQuickViewSetting.Click += new System.EventHandler(this.buttonItemQuickViewSetting_Click); // // itemContainer26 // // // // this.itemContainer26.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.itemContainer26.Name = "itemContainer26"; this.itemContainer26.SubItems.AddRange(new DevComponents.DotNetBar.BaseItem[] { this.buttonItemSetItems}); // // // this.itemContainer26.TitleStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; // // buttonItemSetItems // this.buttonItemSetItems.Name = "buttonItemSetItems"; this.buttonItemSetItems.SubItems.AddRange(new DevComponents.DotNetBar.BaseItem[] { this.itemContainer43}); this.buttonItemSetItems.Text = "SetItems"; // // itemContainer43 // // // // this.itemContainer43.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.itemContainer43.LayoutOrientation = DevComponents.DotNetBar.eOrientation.Vertical; this.itemContainer43.Name = "itemContainer43"; this.itemContainer43.SubItems.AddRange(new DevComponents.DotNetBar.BaseItem[] { this.buttonItemClearSetItems}); // // // this.itemContainer43.TitleStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; // // buttonItemClearSetItems // this.buttonItemClearSetItems.Name = "buttonItemClearSetItems"; this.buttonItemClearSetItems.Text = "Clear SetItems"; this.buttonItemClearSetItems.Click += new System.EventHandler(this.buttonItemClearSetItems_Click); // // itemContainer28 // // // // this.itemContainer28.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.itemContainer28.LayoutOrientation = DevComponents.DotNetBar.eOrientation.Vertical; this.itemContainer28.Name = "itemContainer28"; this.itemContainer28.SubItems.AddRange(new DevComponents.DotNetBar.BaseItem[] { this.itemContainer29, this.itemContainer30, this.itemContainer31}); // // // this.itemContainer28.TitleStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; // // itemContainer29 // // // // this.itemContainer29.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.itemContainer29.Name = "itemContainer29"; this.itemContainer29.SubItems.AddRange(new DevComponents.DotNetBar.BaseItem[] { this.buttonItemCharItem}); // // // this.itemContainer29.TitleStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; // // buttonItemCharItem // this.buttonItemCharItem.AutoCheckOnClick = true; this.buttonItemCharItem.Name = "buttonItemCharItem"; this.buttonItemCharItem.Text = "Item"; this.buttonItemCharItem.Tooltip = "开启/关闭道具栏"; this.buttonItemCharItem.CheckedChanged += new System.EventHandler(this.buttonItemCharItem_CheckedChanged); // // itemContainer30 // // // // this.itemContainer30.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.itemContainer30.Name = "itemContainer30"; this.itemContainer30.SubItems.AddRange(new DevComponents.DotNetBar.BaseItem[] { this.buttonItemCharaStat}); // // // this.itemContainer30.TitleStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; // // buttonItemCharaStat // this.buttonItemCharaStat.AutoCheckOnClick = true; this.buttonItemCharaStat.Name = "buttonItemCharaStat"; this.buttonItemCharaStat.Text = "Stat"; this.buttonItemCharaStat.Tooltip = "开启/关闭状态栏"; this.buttonItemCharaStat.CheckedChanged += new System.EventHandler(this.buttonItemCharaStat_CheckedChanged); // // itemContainer31 // // // // this.itemContainer31.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.itemContainer31.Name = "itemContainer31"; this.itemContainer31.SubItems.AddRange(new DevComponents.DotNetBar.BaseItem[] { this.buttonItemCharaEquip}); // // // this.itemContainer31.TitleStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; // // buttonItemCharaEquip // this.buttonItemCharaEquip.AutoCheckOnClick = true; this.buttonItemCharaEquip.Name = "buttonItemCharaEquip"; this.buttonItemCharaEquip.Text = "Equip"; this.buttonItemCharaEquip.Tooltip = "开启/关闭装备栏"; this.buttonItemCharaEquip.CheckedChanged += new System.EventHandler(this.buttonItemCharaEquip_CheckedChanged); // // itemContainer32 // // // // this.itemContainer32.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.itemContainer32.LayoutOrientation = DevComponents.DotNetBar.eOrientation.Vertical; this.itemContainer32.Name = "itemContainer32"; this.itemContainer32.SubItems.AddRange(new DevComponents.DotNetBar.BaseItem[] { this.itemContainer33, this.itemContainer34, this.itemContainer35}); // // // this.itemContainer32.TitleStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; // // itemContainer33 // // // // this.itemContainer33.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.itemContainer33.Name = "itemContainer33"; this.itemContainer33.SubItems.AddRange(new DevComponents.DotNetBar.BaseItem[] { this.buttonItemAddItem}); // // // this.itemContainer33.TitleStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; // // buttonItemAddItem // this.buttonItemAddItem.Name = "buttonItemAddItem"; this.buttonItemAddItem.Text = "AddItem"; this.buttonItemAddItem.Tooltip = "把最后预览的装备或道具添加至背包"; this.buttonItemAddItem.Click += new System.EventHandler(this.buttonItemAddItem_Click); // // itemContainer34 // // // // this.itemContainer34.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.itemContainer34.Name = "itemContainer34"; // // // this.itemContainer34.TitleStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; // // itemContainer35 // // // // this.itemContainer35.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.itemContainer35.Name = "itemContainer35"; // // // this.itemContainer35.TitleStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; // // ribbonBar3 // this.ribbonBar3.AllowDrop = true; this.ribbonBar3.AutoOverflowEnabled = false; // // // this.ribbonBar3.BackgroundMouseOverStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; // // // this.ribbonBar3.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.ribbonBar3.ContainerControlProcessDialogKey = true; this.ribbonBar3.Dock = System.Windows.Forms.DockStyle.Left; this.ribbonBar3.DragDropSupport = true; this.ribbonBar3.Items.AddRange(new DevComponents.DotNetBar.BaseItem[] { this.itemContainer6}); this.ribbonBar3.Location = new System.Drawing.Point(3, 0); this.ribbonBar3.Name = "ribbonBar3"; this.ribbonBar3.Size = new System.Drawing.Size(262, 91); this.ribbonBar3.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.ribbonBar3.TabIndex = 0; this.ribbonBar3.Text = "SoundPlayer"; // // // this.ribbonBar3.TitleStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; // // // this.ribbonBar3.TitleStyleMouseOver.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.ribbonBar3.DragDrop += new System.Windows.Forms.DragEventHandler(this.ribbonBar3_DragDrop); this.ribbonBar3.DragEnter += new System.Windows.Forms.DragEventHandler(this.ribbonBar3_DragEnter); // // itemContainer6 // // // // this.itemContainer6.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.itemContainer6.LayoutOrientation = DevComponents.DotNetBar.eOrientation.Vertical; this.itemContainer6.Name = "itemContainer6"; this.itemContainer6.SubItems.AddRange(new DevComponents.DotNetBar.BaseItem[] { this.itemContainer7, this.itemContainer9, this.itemContainer18, this.itemContainer13}); // // // this.itemContainer6.TitleStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; // // itemContainer7 // // // // this.itemContainer7.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.itemContainer7.Name = "itemContainer7"; this.itemContainer7.SubItems.AddRange(new DevComponents.DotNetBar.BaseItem[] { this.labelItemSoundTitle}); // // // this.itemContainer7.TitleStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; // // labelItemSoundTitle // this.labelItemSoundTitle.Name = "labelItemSoundTitle"; this.labelItemSoundTitle.Text = "player"; this.labelItemSoundTitle.Width = 254; // // itemContainer9 // // // // this.itemContainer9.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.itemContainer9.Name = "itemContainer9"; this.itemContainer9.SubItems.AddRange(new DevComponents.DotNetBar.BaseItem[] { this.sliderItemSoundTime, this.checkBoxItemSoundLoop}); // // // this.itemContainer9.TitleStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; // // sliderItemSoundTime // this.sliderItemSoundTime.LabelWidth = 6; this.sliderItemSoundTime.Name = "sliderItemSoundTime"; this.sliderItemSoundTime.Step = 10; this.sliderItemSoundTime.Value = 0; this.sliderItemSoundTime.Width = 195; this.sliderItemSoundTime.ValueChanged += new System.EventHandler(this.sliderItemSoundTime_ValueChanged); // // checkBoxItemSoundLoop // this.checkBoxItemSoundLoop.Name = "checkBoxItemSoundLoop"; this.checkBoxItemSoundLoop.Text = "loop"; this.checkBoxItemSoundLoop.CheckedChanged += new DevComponents.DotNetBar.CheckBoxChangeEventHandler(this.checkBoxItemSoundLoop_CheckedChanged); // // itemContainer18 // // // // this.itemContainer18.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.itemContainer18.Name = "itemContainer18"; this.itemContainer18.SubItems.AddRange(new DevComponents.DotNetBar.BaseItem[] { this.labelItemSoundTime}); // // // this.itemContainer18.TitleStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; // // labelItemSoundTime // this.labelItemSoundTime.Name = "labelItemSoundTime"; this.labelItemSoundTime.Text = "00:00 / 00:00"; this.labelItemSoundTime.TextAlignment = System.Drawing.StringAlignment.Center; this.labelItemSoundTime.Width = 222; // // itemContainer13 // // // // this.itemContainer13.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.itemContainer13.Name = "itemContainer13"; this.itemContainer13.SubItems.AddRange(new DevComponents.DotNetBar.BaseItem[] { this.buttonItemLoadSound, this.buttonItemSoundPlay, this.buttonItemSoundStop, this.buttonItemSoundSave, this.sliderItemSoundVol}); // // // this.itemContainer13.TitleStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; // // buttonItemLoadSound // this.buttonItemLoadSound.Image = global::WzComparerR2.Properties.Resources.Open; this.buttonItemLoadSound.Name = "buttonItemLoadSound"; this.buttonItemLoadSound.Text = "Load"; this.buttonItemLoadSound.Click += new System.EventHandler(this.buttonItemLoadSound_Click); // // buttonItemSoundPlay // this.buttonItemSoundPlay.Image = global::WzComparerR2.Properties.Resources.Play; this.buttonItemSoundPlay.Name = "buttonItemSoundPlay"; this.buttonItemSoundPlay.Text = " Play"; this.buttonItemSoundPlay.Click += new System.EventHandler(this.buttonItemSoundPlay_Click); // // buttonItemSoundStop // this.buttonItemSoundStop.Image = global::WzComparerR2.Properties.Resources.Stop; this.buttonItemSoundStop.Name = "buttonItemSoundStop"; this.buttonItemSoundStop.Text = "Stop"; this.buttonItemSoundStop.Click += new System.EventHandler(this.buttonItemSoundStop_Click); // // buttonItemSoundSave // this.buttonItemSoundSave.Image = global::WzComparerR2.Properties.Resources.Save; this.buttonItemSoundSave.Name = "buttonItemSoundSave"; this.buttonItemSoundSave.Text = "Save"; this.buttonItemSoundSave.Click += new System.EventHandler(this.buttonItemSoundSave_Click); // // sliderItemSoundVol // this.sliderItemSoundVol.LabelWidth = 25; this.sliderItemSoundVol.Name = "sliderItemSoundVol"; this.sliderItemSoundVol.Text = "vol"; this.sliderItemSoundVol.Value = 100; this.sliderItemSoundVol.Width = 110; this.sliderItemSoundVol.ValueChanged += new System.EventHandler(this.sliderItemSoundVol_ValueChanged); // // ribbonPanel1 // this.ribbonPanel1.ColorSchemeStyle = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.ribbonPanel1.Controls.Add(this.ribbonBar9); this.ribbonPanel1.Controls.Add(this.ribbonBar4); this.ribbonPanel1.Controls.Add(this.ribbonBar1); this.ribbonPanel1.Dock = System.Windows.Forms.DockStyle.Fill; this.ribbonPanel1.Location = new System.Drawing.Point(0, 56); this.ribbonPanel1.Name = "ribbonPanel1"; this.ribbonPanel1.Padding = new System.Windows.Forms.Padding(3, 0, 3, 3); this.ribbonPanel1.Size = new System.Drawing.Size(740, 94); // // // this.ribbonPanel1.Style.CornerType = DevComponents.DotNetBar.eCornerType.Square; // // // this.ribbonPanel1.StyleMouseDown.CornerType = DevComponents.DotNetBar.eCornerType.Square; // // // this.ribbonPanel1.StyleMouseOver.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.ribbonPanel1.TabIndex = 1; // // ribbonBar9 // this.ribbonBar9.AutoOverflowEnabled = true; // // // this.ribbonBar9.BackgroundMouseOverStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; // // // this.ribbonBar9.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.ribbonBar9.ContainerControlProcessDialogKey = true; this.ribbonBar9.Dock = System.Windows.Forms.DockStyle.Left; this.ribbonBar9.DragDropSupport = true; this.ribbonBar9.Items.AddRange(new DevComponents.DotNetBar.BaseItem[] { this.buttonItemPatcher}); this.ribbonBar9.Location = new System.Drawing.Point(339, 0); this.ribbonBar9.Name = "ribbonBar9"; this.ribbonBar9.Size = new System.Drawing.Size(63, 91); this.ribbonBar9.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.ribbonBar9.TabIndex = 2; this.ribbonBar9.Text = "Patcher"; // // // this.ribbonBar9.TitleStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; // // // this.ribbonBar9.TitleStyleMouseOver.CornerType = DevComponents.DotNetBar.eCornerType.Square; // // buttonItemPatcher // this.buttonItemPatcher.Name = "buttonItemPatcher"; this.buttonItemPatcher.SubItemsExpandWidth = 14; this.buttonItemPatcher.Text = "Patcher"; this.buttonItemPatcher.Click += new System.EventHandler(this.buttonItemPatcher_Click); // // ribbonBar4 // this.ribbonBar4.AutoOverflowEnabled = true; // // // this.ribbonBar4.BackgroundMouseOverStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; // // // this.ribbonBar4.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.ribbonBar4.ContainerControlProcessDialogKey = true; this.ribbonBar4.Dock = System.Windows.Forms.DockStyle.Left; this.ribbonBar4.DragDropSupport = true; this.ribbonBar4.Items.AddRange(new DevComponents.DotNetBar.BaseItem[] { this.itemContainer10}); this.ribbonBar4.Location = new System.Drawing.Point(171, 0); this.ribbonBar4.Name = "ribbonBar4"; this.ribbonBar4.Size = new System.Drawing.Size(168, 91); this.ribbonBar4.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.ribbonBar4.TabIndex = 1; this.ribbonBar4.Text = "SearchString"; // // // this.ribbonBar4.TitleStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; // // // this.ribbonBar4.TitleStyleMouseOver.CornerType = DevComponents.DotNetBar.eCornerType.Square; // // itemContainer10 // // // // this.itemContainer10.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.itemContainer10.LayoutOrientation = DevComponents.DotNetBar.eOrientation.Vertical; this.itemContainer10.Name = "itemContainer10"; this.itemContainer10.SubItems.AddRange(new DevComponents.DotNetBar.BaseItem[] { this.itemContainer8, this.itemContainer11, this.itemContainer12}); // // // this.itemContainer10.TitleStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; // // itemContainer8 // // // // this.itemContainer8.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.itemContainer8.Name = "itemContainer8"; this.itemContainer8.SubItems.AddRange(new DevComponents.DotNetBar.BaseItem[] { this.labelItem2, this.textBoxItemSearchString}); // // // this.itemContainer8.TitleStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; // // labelItem2 // this.labelItem2.Name = "labelItem2"; this.labelItem2.Text = "String"; // // textBoxItemSearchString // this.textBoxItemSearchString.Name = "textBoxItemSearchString"; this.textBoxItemSearchString.TextBoxWidth = 110; this.textBoxItemSearchString.WatermarkColor = System.Drawing.SystemColors.GrayText; this.textBoxItemSearchString.KeyDown += new System.Windows.Forms.KeyEventHandler(this.textBoxItemSearchString_KeyDown); // // itemContainer11 // // // // this.itemContainer11.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.itemContainer11.Name = "itemContainer11"; this.itemContainer11.SubItems.AddRange(new DevComponents.DotNetBar.BaseItem[] { this.checkBoxItemExact2, this.comboBoxItem2}); // // // this.itemContainer11.TitleStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; // // checkBoxItemExact2 // this.checkBoxItemExact2.Name = "checkBoxItemExact2"; this.checkBoxItemExact2.Text = "IsExact"; // // comboBoxItem2 // this.comboBoxItem2.ComboWidth = 85; this.comboBoxItem2.DropDownHeight = 106; this.comboBoxItem2.ItemHeight = 16; this.comboBoxItem2.Items.AddRange(new object[] { this.comboItem3, this.comboItem4, this.comboItem5, this.comboItem6, this.comboItem7, this.comboItem8, this.comboItem9}); this.comboBoxItem2.Name = "comboBoxItem2"; // // comboItem3 // this.comboItem3.Text = "All"; // // comboItem4 // this.comboItem4.Text = "Eqp"; // // comboItem5 // this.comboItem5.Text = "Item"; // // comboItem6 // this.comboItem6.Text = "Map"; // // comboItem7 // this.comboItem7.Text = "Mob"; // // comboItem8 // this.comboItem8.Text = "Npc"; // // comboItem9 // this.comboItem9.Text = "Skill"; // // itemContainer12 // // // // this.itemContainer12.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.itemContainer12.ItemSpacing = 40; this.itemContainer12.Name = "itemContainer12"; this.itemContainer12.SubItems.AddRange(new DevComponents.DotNetBar.BaseItem[] { this.checkBoxItemRegex2, this.buttonItemSearchString}); // // // this.itemContainer12.TitleStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; // // checkBoxItemRegex2 // this.checkBoxItemRegex2.Name = "checkBoxItemRegex2"; this.checkBoxItemRegex2.Text = "Regex"; // // buttonItemSearchString // this.buttonItemSearchString.Name = "buttonItemSearchString"; this.buttonItemSearchString.SubItems.AddRange(new DevComponents.DotNetBar.BaseItem[] { this.buttonItemSelectStringWz, this.buttonItemClearStringWz}); this.buttonItemSearchString.Text = "Search"; this.buttonItemSearchString.Click += new System.EventHandler(this.buttonItemSearchString_Click); // // buttonItemSelectStringWz // this.buttonItemSelectStringWz.Name = "buttonItemSelectStringWz"; this.buttonItemSelectStringWz.Text = "&Select String.wz"; this.buttonItemSelectStringWz.Click += new System.EventHandler(this.buttonItemSelectStringWz_Click); // // buttonItemClearStringWz // this.buttonItemClearStringWz.Name = "buttonItemClearStringWz"; this.buttonItemClearStringWz.Text = "Clear StringLinker"; this.buttonItemClearStringWz.Click += new System.EventHandler(this.buttonItemClearStringWz_Click); // // ribbonBar1 // this.ribbonBar1.AutoOverflowEnabled = true; // // // this.ribbonBar1.BackgroundMouseOverStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; // // // this.ribbonBar1.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.ribbonBar1.ContainerControlProcessDialogKey = true; this.ribbonBar1.Dock = System.Windows.Forms.DockStyle.Left; this.ribbonBar1.DragDropSupport = true; this.ribbonBar1.Items.AddRange(new DevComponents.DotNetBar.BaseItem[] { this.itemContainer14}); this.ribbonBar1.Location = new System.Drawing.Point(3, 0); this.ribbonBar1.Name = "ribbonBar1"; this.ribbonBar1.Size = new System.Drawing.Size(168, 91); this.ribbonBar1.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.ribbonBar1.TabIndex = 0; this.ribbonBar1.Text = "SearchWzNode"; // // // this.ribbonBar1.TitleStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; // // // this.ribbonBar1.TitleStyleMouseOver.CornerType = DevComponents.DotNetBar.eCornerType.Square; // // itemContainer14 // // // // this.itemContainer14.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.itemContainer14.LayoutOrientation = DevComponents.DotNetBar.eOrientation.Vertical; this.itemContainer14.Name = "itemContainer14"; this.itemContainer14.SubItems.AddRange(new DevComponents.DotNetBar.BaseItem[] { this.itemContainer15, this.itemContainer16, this.itemContainer17}); // // // this.itemContainer14.TitleStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; // // itemContainer15 // // // // this.itemContainer15.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.itemContainer15.Name = "itemContainer15"; this.itemContainer15.SubItems.AddRange(new DevComponents.DotNetBar.BaseItem[] { this.labelItem3, this.textBoxItemSearchWz}); // // // this.itemContainer15.TitleStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; // // labelItem3 // this.labelItem3.Name = "labelItem3"; this.labelItem3.Text = "NodeText"; // // textBoxItemSearchWz // this.textBoxItemSearchWz.MaxLength = 50; this.textBoxItemSearchWz.Name = "textBoxItemSearchWz"; this.textBoxItemSearchWz.TextBoxWidth = 98; this.textBoxItemSearchWz.WatermarkColor = System.Drawing.SystemColors.GrayText; this.textBoxItemSearchWz.KeyDown += new System.Windows.Forms.KeyEventHandler(this.textBoxItemSearchWz_KeyDown); // // itemContainer16 // // // // this.itemContainer16.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.itemContainer16.Name = "itemContainer16"; this.itemContainer16.SubItems.AddRange(new DevComponents.DotNetBar.BaseItem[] { this.checkBoxItemExact1, this.comboBoxItem1}); // // // this.itemContainer16.TitleStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; // // checkBoxItemExact1 // this.checkBoxItemExact1.Name = "checkBoxItemExact1"; this.checkBoxItemExact1.Text = "IsExact"; // // comboBoxItem1 // this.comboBoxItem1.ComboWidth = 85; this.comboBoxItem1.DropDownHeight = 106; this.comboBoxItem1.ItemHeight = 16; this.comboBoxItem1.Items.AddRange(new object[] { this.comboItem10, this.comboItem11, this.comboItem12, this.comboItem19}); this.comboBoxItem1.Name = "comboBoxItem1"; // // comboItem10 // this.comboItem10.Text = "wzNode"; // // comboItem11 // this.comboItem11.Text = "imageNode"; // // comboItem12 // this.comboItem12.Text = "imageValue"; // // itemContainer17 // // // // this.itemContainer17.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.itemContainer17.ItemSpacing = 30; this.itemContainer17.Name = "itemContainer17"; this.itemContainer17.SubItems.AddRange(new DevComponents.DotNetBar.BaseItem[] { this.checkBoxItemRegex1, this.buttonItemSearchWz}); // // // this.itemContainer17.TitleStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; // // checkBoxItemRegex1 // this.checkBoxItemRegex1.Name = "checkBoxItemRegex1"; this.checkBoxItemRegex1.Text = "Regex"; // // buttonItemSearchWz // this.buttonItemSearchWz.Name = "buttonItemSearchWz"; this.buttonItemSearchWz.Text = "SearchNext"; this.buttonItemSearchWz.Click += new System.EventHandler(this.buttonItemSearchWz_Click); // // ribbonPanel3 // this.ribbonPanel3.ColorSchemeStyle = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.ribbonPanel3.Controls.Add(this.ribbonBar11); this.ribbonPanel3.Controls.Add(this.ribbonBar7); this.ribbonPanel3.Controls.Add(this.ribbonBar6); this.ribbonPanel3.Dock = System.Windows.Forms.DockStyle.Fill; this.ribbonPanel3.Location = new System.Drawing.Point(0, 56); this.ribbonPanel3.Name = "ribbonPanel3"; this.ribbonPanel3.Padding = new System.Windows.Forms.Padding(3, 0, 3, 3); this.ribbonPanel3.Size = new System.Drawing.Size(740, 94); // // // this.ribbonPanel3.Style.CornerType = DevComponents.DotNetBar.eCornerType.Square; // // // this.ribbonPanel3.StyleMouseDown.CornerType = DevComponents.DotNetBar.eCornerType.Square; // // // this.ribbonPanel3.StyleMouseOver.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.ribbonPanel3.TabIndex = 3; this.ribbonPanel3.Visible = false; // // ribbonBar11 // this.ribbonBar11.AutoOverflowEnabled = true; // // // this.ribbonBar11.BackgroundMouseOverStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; // // // this.ribbonBar11.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.ribbonBar11.ContainerControlProcessDialogKey = true; this.ribbonBar11.Dock = System.Windows.Forms.DockStyle.Left; this.ribbonBar11.DragDropSupport = true; this.ribbonBar11.Items.AddRange(new DevComponents.DotNetBar.BaseItem[] { this.buttonItem1}); this.ribbonBar11.Location = new System.Drawing.Point(110, 0); this.ribbonBar11.Name = "ribbonBar11"; this.ribbonBar11.Size = new System.Drawing.Size(69, 91); this.ribbonBar11.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.ribbonBar11.TabIndex = 2; this.ribbonBar11.Text = "测试用"; // // // this.ribbonBar11.TitleStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; // // // this.ribbonBar11.TitleStyleMouseOver.CornerType = DevComponents.DotNetBar.eCornerType.Square; // // buttonItem1 // this.buttonItem1.Name = "buttonItem1"; this.buttonItem1.SubItemsExpandWidth = 14; this.buttonItem1.Text = "不要按我"; this.buttonItem1.Click += new System.EventHandler(this.buttonItem1_Click); // // ribbonBar7 // this.ribbonBar7.AutoOverflowEnabled = true; // // // this.ribbonBar7.BackgroundMouseOverStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; // // // this.ribbonBar7.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.ribbonBar7.ContainerControlProcessDialogKey = true; this.ribbonBar7.Dock = System.Windows.Forms.DockStyle.Left; this.ribbonBar7.DragDropSupport = true; this.ribbonBar7.Items.AddRange(new DevComponents.DotNetBar.BaseItem[] { this.buttonItemUpdate}); this.ribbonBar7.Location = new System.Drawing.Point(53, 0); this.ribbonBar7.Name = "ribbonBar7"; this.ribbonBar7.Size = new System.Drawing.Size(57, 91); this.ribbonBar7.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.ribbonBar7.TabIndex = 1; this.ribbonBar7.Text = "Update"; // // // this.ribbonBar7.TitleStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; // // // this.ribbonBar7.TitleStyleMouseOver.CornerType = DevComponents.DotNetBar.eCornerType.Square; // // buttonItemUpdate // this.buttonItemUpdate.Name = "buttonItemUpdate"; this.buttonItemUpdate.SubItemsExpandWidth = 14; this.buttonItemUpdate.Text = "Update"; this.buttonItemUpdate.Click += new System.EventHandler(this.buttonItemUpdate_Click); // // ribbonBar6 // this.ribbonBar6.AutoOverflowEnabled = true; // // // this.ribbonBar6.BackgroundMouseOverStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; // // // this.ribbonBar6.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.ribbonBar6.ContainerControlProcessDialogKey = true; this.ribbonBar6.Dock = System.Windows.Forms.DockStyle.Left; this.ribbonBar6.DragDropSupport = true; this.ribbonBar6.Items.AddRange(new DevComponents.DotNetBar.BaseItem[] { this.buttonItemAbout}); this.ribbonBar6.Location = new System.Drawing.Point(3, 0); this.ribbonBar6.Name = "ribbonBar6"; this.ribbonBar6.Size = new System.Drawing.Size(50, 91); this.ribbonBar6.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.ribbonBar6.TabIndex = 0; this.ribbonBar6.Text = "About"; // // // this.ribbonBar6.TitleStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; // // // this.ribbonBar6.TitleStyleMouseOver.CornerType = DevComponents.DotNetBar.eCornerType.Square; // // buttonItemAbout // this.buttonItemAbout.Name = "buttonItemAbout"; this.buttonItemAbout.SubItemsExpandWidth = 14; this.buttonItemAbout.Text = "About"; this.buttonItemAbout.Click += new System.EventHandler(this.buttonItemAbout_Click); // // ribbonTabItem1 // this.ribbonTabItem1.Checked = true; this.ribbonTabItem1.Name = "ribbonTabItem1"; this.ribbonTabItem1.Panel = this.ribbonPanel1; this.ribbonTabItem1.Tag = "Tools"; this.ribbonTabItem1.Text = "&Tools"; // // ribbonTabItem2 // this.ribbonTabItem2.Name = "ribbonTabItem2"; this.ribbonTabItem2.Panel = this.ribbonPanel2; this.ribbonTabItem2.Tag = "Modules"; this.ribbonTabItem2.Text = "&Modules"; // // ribbonTabItem3 // this.ribbonTabItem3.Name = "ribbonTabItem3"; this.ribbonTabItem3.Panel = this.ribbonPanel3; this.ribbonTabItem3.Tag = "Help"; this.ribbonTabItem3.Text = "&Help"; // // buttonItemStyle // this.buttonItemStyle.AutoExpandOnClick = true; this.buttonItemStyle.ItemAlignment = DevComponents.DotNetBar.eItemAlignment.Far; this.buttonItemStyle.Name = "buttonItemStyle"; this.buttonItemStyle.Text = "&Style"; // // office2007StartButton1 // this.office2007StartButton1.AutoExpandOnClick = true; this.office2007StartButton1.CanCustomize = false; this.office2007StartButton1.HotFontUnderline = true; this.office2007StartButton1.HotTrackingStyle = DevComponents.DotNetBar.eHotTrackingStyle.Image; this.office2007StartButton1.ImagePaddingHorizontal = 2; this.office2007StartButton1.ImagePaddingVertical = 2; this.office2007StartButton1.Name = "office2007StartButton1"; this.office2007StartButton1.ShowSubItems = false; this.office2007StartButton1.SubItems.AddRange(new DevComponents.DotNetBar.BaseItem[] { this.itemContainer1}); this.office2007StartButton1.Text = "&File"; // // itemContainer1 // // // // this.itemContainer1.BackgroundStyle.Class = "RibbonFileMenuContainer"; this.itemContainer1.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.itemContainer1.LayoutOrientation = DevComponents.DotNetBar.eOrientation.Vertical; this.itemContainer1.Name = "itemContainer1"; this.itemContainer1.SubItems.AddRange(new DevComponents.DotNetBar.BaseItem[] { this.itemContainer2, this.itemContainer4}); // // // this.itemContainer1.TitleStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; // // itemContainer2 // // // // this.itemContainer2.BackgroundStyle.Class = "RibbonFileMenuTwoColumnContainer"; this.itemContainer2.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.itemContainer2.ItemSpacing = 0; this.itemContainer2.Name = "itemContainer2"; this.itemContainer2.SubItems.AddRange(new DevComponents.DotNetBar.BaseItem[] { this.itemContainer3, this.galleryContainerRecent}); // // // this.itemContainer2.TitleStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; // // itemContainer3 // // // // this.itemContainer3.BackgroundStyle.Class = "RibbonFileMenuColumnOneContainer"; this.itemContainer3.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.itemContainer3.LayoutOrientation = DevComponents.DotNetBar.eOrientation.Vertical; this.itemContainer3.MinimumSize = new System.Drawing.Size(120, 0); this.itemContainer3.Name = "itemContainer3"; this.itemContainer3.SubItems.AddRange(new DevComponents.DotNetBar.BaseItem[] { this.btnItemOpenWz, this.btnItemOpenImg, this.buttonItemClose, this.buttonItemCloseAll}); // // // this.itemContainer3.TitleStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; // // btnItemOpenWz // this.btnItemOpenWz.ButtonStyle = DevComponents.DotNetBar.eButtonStyle.ImageAndText; this.btnItemOpenWz.Name = "btnItemOpenWz"; this.btnItemOpenWz.SubItemsExpandWidth = 24; this.btnItemOpenWz.Text = "&Open Wz..."; this.btnItemOpenWz.Click += new System.EventHandler(this.btnItemOpenWz_Click); // // btnItemOpenImg // this.btnItemOpenImg.Name = "btnItemOpenImg"; this.btnItemOpenImg.Text = "Open Img..."; this.btnItemOpenImg.Click += new System.EventHandler(this.btnItemOpenImg_Click); // // buttonItemClose // this.buttonItemClose.ButtonStyle = DevComponents.DotNetBar.eButtonStyle.ImageAndText; this.buttonItemClose.Name = "buttonItemClose"; this.buttonItemClose.SubItemsExpandWidth = 24; this.buttonItemClose.Text = "&Close..."; this.buttonItemClose.Click += new System.EventHandler(this.buttonItemClose_Click); // // buttonItemCloseAll // this.buttonItemCloseAll.Name = "buttonItemCloseAll"; this.buttonItemCloseAll.Text = "Close All..."; this.buttonItemCloseAll.Click += new System.EventHandler(this.buttonItemCloseAll_Click); // // galleryContainerRecent // // // // this.galleryContainerRecent.BackgroundStyle.Class = "RibbonFileMenuColumnTwoContainer"; this.galleryContainerRecent.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.galleryContainerRecent.EnableGalleryPopup = false; this.galleryContainerRecent.LayoutOrientation = DevComponents.DotNetBar.eOrientation.Vertical; this.galleryContainerRecent.MinimumSize = new System.Drawing.Size(180, 140); this.galleryContainerRecent.MultiLine = false; this.galleryContainerRecent.Name = "galleryContainerRecent"; this.galleryContainerRecent.SubItems.AddRange(new DevComponents.DotNetBar.BaseItem[] { this.labelItem8}); // // // this.galleryContainerRecent.TitleStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; // // labelItem8 // this.labelItem8.BorderSide = DevComponents.DotNetBar.eBorderSide.Bottom; this.labelItem8.BorderType = DevComponents.DotNetBar.eBorderType.Etched; this.labelItem8.CanCustomize = false; this.labelItem8.ForeColor = System.Drawing.SystemColors.ControlText; this.labelItem8.Name = "labelItem8"; this.labelItem8.PaddingBottom = 2; this.labelItem8.PaddingTop = 2; this.labelItem8.Stretch = true; this.labelItem8.Text = "Recent Documents"; // // itemContainer4 // // // // this.itemContainer4.BackgroundStyle.Class = "RibbonFileMenuBottomContainer"; this.itemContainer4.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.itemContainer4.CanCustomize = false; this.itemContainer4.HorizontalItemAlignment = DevComponents.DotNetBar.eHorizontalItemsAlignment.Right; this.itemContainer4.Name = "itemContainer4"; this.itemContainer4.SubItems.AddRange(new DevComponents.DotNetBar.BaseItem[] { this.btnItemOptions, this.buttonItem13}); // // // this.itemContainer4.TitleStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; // // btnItemOptions // this.btnItemOptions.ButtonStyle = DevComponents.DotNetBar.eButtonStyle.ImageAndText; this.btnItemOptions.ColorTable = DevComponents.DotNetBar.eButtonColor.OrangeWithBackground; this.btnItemOptions.Name = "btnItemOptions"; this.btnItemOptions.SubItemsExpandWidth = 24; this.btnItemOptions.Text = "Opt&ions"; this.btnItemOptions.Click += new System.EventHandler(this.btnItemOptions_Click); // // buttonItem13 // this.buttonItem13.ButtonStyle = DevComponents.DotNetBar.eButtonStyle.ImageAndText; this.buttonItem13.ColorTable = DevComponents.DotNetBar.eButtonColor.OrangeWithBackground; this.buttonItem13.Enabled = false; this.buttonItem13.Name = "buttonItem13"; this.buttonItem13.SubItemsExpandWidth = 24; this.buttonItem13.Text = "E&xit"; // // styleManager1 // this.styleManager1.ManagerStyle = DevComponents.DotNetBar.eStyle.Office2007VistaGlass; this.styleManager1.MetroColorParameters = new DevComponents.DotNetBar.Metro.ColorTables.MetroColorGeneratorParameters(System.Drawing.Color.White, System.Drawing.Color.FromArgb(((int)(((byte)(43)))), ((int)(((byte)(87)))), ((int)(((byte)(154)))))); // // colorPickerDropDown1 // this.colorPickerDropDown1.Name = "colorPickerDropDown1"; this.colorPickerDropDown1.SubItemsExpandWidth = 14; this.colorPickerDropDown1.Text = "colorPickerDropDown1"; // // ribbonBar2 // this.ribbonBar2.AutoOverflowEnabled = false; // // // this.ribbonBar2.BackgroundMouseOverStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; // // // this.ribbonBar2.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.ribbonBar2.CanCustomize = false; this.ribbonBar2.ContainerControlProcessDialogKey = true; this.ribbonBar2.Dock = System.Windows.Forms.DockStyle.Bottom; this.ribbonBar2.DragDropSupport = true; this.ribbonBar2.Items.AddRange(new DevComponents.DotNetBar.BaseItem[] { this.labelItemStatus, this.progressBarItem1}); this.ribbonBar2.Location = new System.Drawing.Point(5, 486); this.ribbonBar2.Name = "ribbonBar2"; this.ribbonBar2.Size = new System.Drawing.Size(740, 24); this.ribbonBar2.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.ribbonBar2.TabIndex = 1; this.ribbonBar2.Text = "ribbonBar2"; // // // this.ribbonBar2.TitleStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; // // // this.ribbonBar2.TitleStyleMouseOver.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.ribbonBar2.TitleVisible = false; // // labelItemStatus // this.labelItemStatus.Name = "labelItemStatus"; this.labelItemStatus.Text = "kira~"; this.labelItemStatus.TextChanged += new System.EventHandler(this.labelItemStatus_TextChanged); // // progressBarItem1 // // // // this.progressBarItem1.BackStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.progressBarItem1.CanCustomize = false; this.progressBarItem1.ChunkGradientAngle = 0F; this.progressBarItem1.ItemAlignment = DevComponents.DotNetBar.eItemAlignment.Far; this.progressBarItem1.MenuVisibility = DevComponents.DotNetBar.eMenuVisibility.VisibleAlways; this.progressBarItem1.Name = "progressBarItem1"; this.progressBarItem1.RecentlyUsed = false; this.progressBarItem1.ShowSubItems = false; this.progressBarItem1.Text = "progressBarItem1"; this.progressBarItem1.Width = 100; // // panelExMain // this.panelExMain.CanvasColor = System.Drawing.SystemColors.Control; this.panelExMain.ColorSchemeStyle = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.panelExMain.Controls.Add(this.panelExRight); this.panelExMain.Controls.Add(this.panelExLeft); this.panelExMain.DisabledBackColor = System.Drawing.Color.Empty; this.panelExMain.Dock = System.Windows.Forms.DockStyle.Fill; this.panelExMain.Location = new System.Drawing.Point(5, 154); this.panelExMain.Name = "panelExMain"; this.panelExMain.Size = new System.Drawing.Size(740, 234); this.panelExMain.Style.Alignment = System.Drawing.StringAlignment.Center; this.panelExMain.Style.BackColor1.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.PanelBackground; this.panelExMain.Style.BackColor2.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.PanelBackground2; this.panelExMain.Style.Border = DevComponents.DotNetBar.eBorderType.SingleLine; this.panelExMain.Style.BorderColor.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.PanelBorder; this.panelExMain.Style.ForeColor.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.PanelText; this.panelExMain.Style.GradientAngle = 90; this.panelExMain.TabIndex = 2; // // panelExRight // this.panelExRight.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.panelExRight.CanvasColor = System.Drawing.SystemColors.Control; this.panelExRight.ColorSchemeStyle = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.panelExRight.Controls.Add(this.superTabControl1); this.panelExRight.DisabledBackColor = System.Drawing.Color.Empty; this.panelExRight.Location = new System.Drawing.Point(207, 3); this.panelExRight.Name = "panelExRight"; this.panelExRight.Size = new System.Drawing.Size(530, 228); this.panelExRight.Style.Alignment = System.Drawing.StringAlignment.Center; this.panelExRight.Style.BackColor1.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.PanelBackground; this.panelExRight.Style.BackColor2.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.PanelBackground2; this.panelExRight.Style.Border = DevComponents.DotNetBar.eBorderType.SingleLine; this.panelExRight.Style.BorderColor.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.PanelBorder; this.panelExRight.Style.ForeColor.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.PanelText; this.panelExRight.Style.GradientAngle = 90; this.panelExRight.TabIndex = 1; // // superTabControl1 // // // // // // // this.superTabControl1.ControlBox.CloseBox.Name = ""; // // // this.superTabControl1.ControlBox.MenuBox.Name = ""; this.superTabControl1.ControlBox.Name = ""; this.superTabControl1.ControlBox.SubItems.AddRange(new DevComponents.DotNetBar.BaseItem[] { this.superTabControl1.ControlBox.MenuBox, this.superTabControl1.ControlBox.CloseBox}); this.superTabControl1.Controls.Add(this.superTabControlPanel1); this.superTabControl1.Controls.Add(this.superTabControlPanel2); this.superTabControl1.Controls.Add(this.superTabControlPanel3); this.superTabControl1.Dock = System.Windows.Forms.DockStyle.Fill; this.superTabControl1.Location = new System.Drawing.Point(0, 0); this.superTabControl1.Name = "superTabControl1"; this.superTabControl1.ReorderTabsEnabled = true; this.superTabControl1.SelectedTabFont = new System.Drawing.Font("宋体", 9F, System.Drawing.FontStyle.Bold); this.superTabControl1.SelectedTabIndex = 0; this.superTabControl1.Size = new System.Drawing.Size(530, 228); this.superTabControl1.TabFont = new System.Drawing.Font("宋体", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); this.superTabControl1.TabIndex = 0; this.superTabControl1.Tabs.AddRange(new DevComponents.DotNetBar.BaseItem[] { this.btnNodeBack, this.btnNodeForward, this.superTabItem1, this.superTabItem2, this.superTabItem3}); this.superTabControl1.Text = "superTabControl1"; // // superTabControlPanel1 // this.superTabControlPanel1.ColorSchemeStyle = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.superTabControlPanel1.Controls.Add(this.panelEx2); this.superTabControlPanel1.Controls.Add(this.expandableSplitter1); this.superTabControlPanel1.Controls.Add(this.panelEx1); this.superTabControlPanel1.Dock = System.Windows.Forms.DockStyle.Fill; this.superTabControlPanel1.Location = new System.Drawing.Point(0, 0); this.superTabControlPanel1.Name = "superTabControlPanel1"; this.superTabControlPanel1.Size = new System.Drawing.Size(530, 228); this.superTabControlPanel1.TabIndex = 1; this.superTabControlPanel1.TabItem = this.superTabItem1; // // panelEx2 // this.panelEx2.CanvasColor = System.Drawing.SystemColors.Control; this.panelEx2.ColorSchemeStyle = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.panelEx2.Controls.Add(this.pictureBoxEx1); this.panelEx2.Controls.Add(this.ribbonBar5); this.panelEx2.Controls.Add(this.textBoxX1); this.panelEx2.DisabledBackColor = System.Drawing.Color.Empty; this.panelEx2.Dock = System.Windows.Forms.DockStyle.Fill; this.panelEx2.Location = new System.Drawing.Point(238, 0); this.panelEx2.Name = "panelEx2"; this.panelEx2.Size = new System.Drawing.Size(292, 228); this.panelEx2.Style.Alignment = System.Drawing.StringAlignment.Center; this.panelEx2.Style.BackColor1.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.PanelBackground; this.panelEx2.Style.BackColor2.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.PanelBackground2; this.panelEx2.Style.Border = DevComponents.DotNetBar.eBorderType.SingleLine; this.panelEx2.Style.BorderColor.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.PanelBorder; this.panelEx2.Style.ForeColor.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.PanelText; this.panelEx2.Style.GradientAngle = 90; this.panelEx2.TabIndex = 2; // // pictureBoxEx1 // this.pictureBoxEx1.AutoAdjustPosition = true; this.pictureBoxEx1.Dock = System.Windows.Forms.DockStyle.Fill; this.pictureBoxEx1.FrameInterval = 30; this.pictureBoxEx1.GlobalScale = 1F; this.pictureBoxEx1.IsPlaying = true; this.pictureBoxEx1.Location = new System.Drawing.Point(0, 79); this.pictureBoxEx1.MouseDragEnabled = true; this.pictureBoxEx1.MouseDragSaveEnabled = true; this.pictureBoxEx1.Name = "pictureBoxEx1"; this.pictureBoxEx1.Padding = new System.Windows.Forms.Padding(0, 14, 0, 0); this.pictureBoxEx1.PictureName = null; this.pictureBoxEx1.ShowInfo = true; this.pictureBoxEx1.ShowPositionGridOnDrag = true; this.pictureBoxEx1.Size = new System.Drawing.Size(292, 123); this.pictureBoxEx1.TabIndex = 7; this.pictureBoxEx1.Text = "pictureBoxEx1"; // // ribbonBar5 // this.ribbonBar5.AutoOverflowEnabled = false; // // // this.ribbonBar5.BackgroundMouseOverStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; // // // this.ribbonBar5.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.ribbonBar5.ContainerControlProcessDialogKey = true; this.ribbonBar5.Dock = System.Windows.Forms.DockStyle.Bottom; this.ribbonBar5.DragDropSupport = true; this.ribbonBar5.Items.AddRange(new DevComponents.DotNetBar.BaseItem[] { this.cmbItemAniNames, this.cmbItemSkins, this.buttonItemSaveImage, this.buttonItemGif, this.colorPickerPicBoxBgColor}); this.ribbonBar5.Location = new System.Drawing.Point(0, 202); this.ribbonBar5.Name = "ribbonBar5"; this.ribbonBar5.Size = new System.Drawing.Size(292, 26); this.ribbonBar5.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.ribbonBar5.TabIndex = 2; this.ribbonBar5.Text = "ribbonBar5"; // // // this.ribbonBar5.TitleStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; // // // this.ribbonBar5.TitleStyleMouseOver.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.ribbonBar5.TitleVisible = false; // // cmbItemAniNames // this.cmbItemAniNames.ComboWidth = 80; this.cmbItemAniNames.DropDownHeight = 106; this.cmbItemAniNames.DropDownWidth = 180; this.cmbItemAniNames.ItemHeight = 14; this.cmbItemAniNames.Name = "cmbItemAniNames"; this.cmbItemAniNames.SelectedIndexChanged += new System.EventHandler(this.cmbItemAniNames_SelectedIndexChanged); // // cmbItemSkins // this.cmbItemSkins.ComboWidth = 80; this.cmbItemSkins.DropDownHeight = 106; this.cmbItemSkins.DropDownWidth = 180; this.cmbItemSkins.ItemHeight = 14; this.cmbItemSkins.Name = "cmbItemSkins"; this.cmbItemSkins.Visible = false; this.cmbItemSkins.SelectedIndexChanged += new System.EventHandler(this.cmbItemSkins_SelectedIndexChanged); // // buttonItemSaveImage // this.buttonItemSaveImage.Name = "buttonItemSaveImage"; this.buttonItemSaveImage.SubItems.AddRange(new DevComponents.DotNetBar.BaseItem[] { this.itemContainer27}); this.buttonItemSaveImage.SubItemsExpandWidth = 14; this.buttonItemSaveImage.Text = "SavePicture"; this.buttonItemSaveImage.Click += new System.EventHandler(this.buttonItemSaveImage_Click); // // itemContainer27 // // // // this.itemContainer27.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.itemContainer27.LayoutOrientation = DevComponents.DotNetBar.eOrientation.Vertical; this.itemContainer27.Name = "itemContainer27"; this.itemContainer27.SubItems.AddRange(new DevComponents.DotNetBar.BaseItem[] { this.buttonItemAutoSave, this.buttonItemAutoSaveFolder, this.buttonItemSaveWithOptions}); // // // this.itemContainer27.TitleStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; // // buttonItemAutoSave // this.buttonItemAutoSave.AutoCheckOnClick = true; this.buttonItemAutoSave.Name = "buttonItemAutoSave"; this.buttonItemAutoSave.Text = "Auto Save"; this.buttonItemAutoSave.Click += new System.EventHandler(this.buttonItemAutoSave_Click); // // buttonItemAutoSaveFolder // this.buttonItemAutoSaveFolder.Name = "buttonItemAutoSaveFolder"; this.buttonItemAutoSaveFolder.SubItems.AddRange(new DevComponents.DotNetBar.BaseItem[] { this.labelItemAutoSaveFolder}); this.buttonItemAutoSaveFolder.Text = "Select Folder"; this.buttonItemAutoSaveFolder.Click += new System.EventHandler(this.buttonItemAutoSaveFolder_Click); // // labelItemAutoSaveFolder // this.labelItemAutoSaveFolder.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(221)))), ((int)(((byte)(231)))), ((int)(((byte)(238))))); this.labelItemAutoSaveFolder.BorderSide = DevComponents.DotNetBar.eBorderSide.Bottom; this.labelItemAutoSaveFolder.BorderType = DevComponents.DotNetBar.eBorderType.SingleLine; this.labelItemAutoSaveFolder.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(0)))), ((int)(((byte)(21)))), ((int)(((byte)(110))))); this.labelItemAutoSaveFolder.Name = "labelItemAutoSaveFolder"; this.labelItemAutoSaveFolder.PaddingBottom = 1; this.labelItemAutoSaveFolder.PaddingLeft = 10; this.labelItemAutoSaveFolder.PaddingTop = 1; this.labelItemAutoSaveFolder.SingleLineColor = System.Drawing.Color.FromArgb(((int)(((byte)(197)))), ((int)(((byte)(197)))), ((int)(((byte)(197))))); this.labelItemAutoSaveFolder.Text = "labelItem1"; this.labelItemAutoSaveFolder.Click += new System.EventHandler(this.labelItemAutoSaveFolder_Click); // // buttonItemSaveWithOptions // this.buttonItemSaveWithOptions.Name = "buttonItemSaveWithOptions"; this.buttonItemSaveWithOptions.Text = "Save with options"; this.buttonItemSaveWithOptions.Click += new System.EventHandler(this.buttonItemSaveWithOptions_Click); // // buttonItemGif // this.buttonItemGif.Name = "buttonItemGif"; this.buttonItemGif.SubItems.AddRange(new DevComponents.DotNetBar.BaseItem[] { this.itemContainer36}); this.buttonItemGif.SubItemsExpandWidth = 14; this.buttonItemGif.Text = "ExtractGif"; this.buttonItemGif.Click += new System.EventHandler(this.buttonItemGif_Click); // // itemContainer36 // // // // this.itemContainer36.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.itemContainer36.LayoutOrientation = DevComponents.DotNetBar.eOrientation.Vertical; this.itemContainer36.Name = "itemContainer36"; this.itemContainer36.SubItems.AddRange(new DevComponents.DotNetBar.BaseItem[] { this.buttonItemExtractGifEx, this.buttonItemGifSetting}); // // // this.itemContainer36.TitleStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; // // buttonItemExtractGifEx // this.buttonItemExtractGifEx.Name = "buttonItemExtractGifEx"; this.buttonItemExtractGifEx.Text = "ExtractGifEx"; this.buttonItemExtractGifEx.Tooltip = "对所选节点的全部子节点提取帧动画,而不使用序数节点名称。"; this.buttonItemExtractGifEx.Click += new System.EventHandler(this.buttonItemGif_Click); // // buttonItemGifSetting // this.buttonItemGifSetting.Name = "buttonItemGifSetting"; this.buttonItemGifSetting.Text = "GifSettings"; this.buttonItemGifSetting.Click += new System.EventHandler(this.buttonItemGifSetting_Click); // // colorPickerPicBoxBgColor // this.colorPickerPicBoxBgColor.AutoExpandOnClick = true; this.colorPickerPicBoxBgColor.BeginGroup = true; this.colorPickerPicBoxBgColor.ImagePaddingHorizontal = 6; this.colorPickerPicBoxBgColor.ImagePaddingVertical = 0; this.colorPickerPicBoxBgColor.ImagePosition = DevComponents.DotNetBar.eImagePosition.Bottom; this.colorPickerPicBoxBgColor.Name = "colorPickerPicBoxBgColor"; this.colorPickerPicBoxBgColor.SelectedColorImageRectangle = new System.Drawing.Rectangle(0, 0, 100, 100); this.colorPickerPicBoxBgColor.ShowSubItems = false; this.colorPickerPicBoxBgColor.SubItemsExpandWidth = 14; this.colorPickerPicBoxBgColor.Symbol = "57914"; this.colorPickerPicBoxBgColor.SymbolSet = DevComponents.DotNetBar.eSymbolSet.Material; this.colorPickerPicBoxBgColor.SymbolSize = 12F; this.colorPickerPicBoxBgColor.Tooltip = "Set Background Color for Image Viewer"; this.colorPickerPicBoxBgColor.SelectedColorChanged += new System.EventHandler(this.colorPickerPicBoxBgColor_SelectedColorChanged); // // textBoxX1 // // // // this.textBoxX1.Border.Class = "TextBoxBorder"; this.textBoxX1.Border.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.textBoxX1.DisabledBackColor = System.Drawing.Color.White; this.textBoxX1.Dock = System.Windows.Forms.DockStyle.Top; this.textBoxX1.Location = new System.Drawing.Point(0, 0); this.textBoxX1.Multiline = true; this.textBoxX1.Name = "textBoxX1"; this.textBoxX1.ReadOnly = true; this.textBoxX1.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; this.textBoxX1.Size = new System.Drawing.Size(292, 79); this.textBoxX1.TabIndex = 0; // // expandableSplitter1 // this.expandableSplitter1.BackColor2 = System.Drawing.Color.FromArgb(((int)(((byte)(100)))), ((int)(((byte)(110)))), ((int)(((byte)(121))))); this.expandableSplitter1.BackColor2SchemePart = DevComponents.DotNetBar.eColorSchemePart.PanelBorder; this.expandableSplitter1.BackColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.PanelBackground; this.expandableSplitter1.ExpandActionClick = false; this.expandableSplitter1.ExpandFillColor = System.Drawing.Color.FromArgb(((int)(((byte)(100)))), ((int)(((byte)(110)))), ((int)(((byte)(121))))); this.expandableSplitter1.ExpandFillColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.PanelBorder; this.expandableSplitter1.ExpandLineColor = System.Drawing.Color.FromArgb(((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0))))); this.expandableSplitter1.ExpandLineColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.ItemText; this.expandableSplitter1.GripDarkColor = System.Drawing.Color.FromArgb(((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0))))); this.expandableSplitter1.GripDarkColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.ItemText; this.expandableSplitter1.GripLightColor = System.Drawing.Color.FromArgb(((int)(((byte)(254)))), ((int)(((byte)(254)))), ((int)(((byte)(255))))); this.expandableSplitter1.GripLightColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.BarBackground; this.expandableSplitter1.HotBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(229)))), ((int)(((byte)(244)))), ((int)(((byte)(252))))); this.expandableSplitter1.HotBackColor2 = System.Drawing.Color.FromArgb(((int)(((byte)(104)))), ((int)(((byte)(179)))), ((int)(((byte)(219))))); this.expandableSplitter1.HotBackColor2SchemePart = DevComponents.DotNetBar.eColorSchemePart.ItemPressedBackground2; this.expandableSplitter1.HotBackColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.ItemPressedBackground; this.expandableSplitter1.HotExpandFillColor = System.Drawing.Color.FromArgb(((int)(((byte)(100)))), ((int)(((byte)(110)))), ((int)(((byte)(121))))); this.expandableSplitter1.HotExpandFillColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.PanelBorder; this.expandableSplitter1.HotExpandLineColor = System.Drawing.Color.FromArgb(((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0))))); this.expandableSplitter1.HotExpandLineColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.ItemText; this.expandableSplitter1.HotGripDarkColor = System.Drawing.Color.FromArgb(((int)(((byte)(100)))), ((int)(((byte)(110)))), ((int)(((byte)(121))))); this.expandableSplitter1.HotGripDarkColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.PanelBorder; this.expandableSplitter1.HotGripLightColor = System.Drawing.Color.FromArgb(((int)(((byte)(254)))), ((int)(((byte)(254)))), ((int)(((byte)(255))))); this.expandableSplitter1.HotGripLightColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.BarBackground; this.expandableSplitter1.Location = new System.Drawing.Point(233, 0); this.expandableSplitter1.Name = "expandableSplitter1"; this.expandableSplitter1.Size = new System.Drawing.Size(5, 228); this.expandableSplitter1.Style = DevComponents.DotNetBar.eSplitterStyle.Office2007; this.expandableSplitter1.TabIndex = 1; this.expandableSplitter1.TabStop = false; // // panelEx1 // this.panelEx1.CanvasColor = System.Drawing.SystemColors.Control; this.panelEx1.ColorSchemeStyle = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.panelEx1.Controls.Add(this.advTree3); this.panelEx1.Controls.Add(this.listViewExWzDetail); this.panelEx1.DisabledBackColor = System.Drawing.Color.Empty; this.panelEx1.Dock = System.Windows.Forms.DockStyle.Left; this.panelEx1.Location = new System.Drawing.Point(0, 0); this.panelEx1.Name = "panelEx1"; this.panelEx1.Size = new System.Drawing.Size(233, 228); this.panelEx1.Style.Alignment = System.Drawing.StringAlignment.Center; this.panelEx1.Style.BackColor1.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.PanelBackground; this.panelEx1.Style.BackColor2.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.PanelBackground2; this.panelEx1.Style.Border = DevComponents.DotNetBar.eBorderType.SingleLine; this.panelEx1.Style.BorderColor.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.PanelBorder; this.panelEx1.Style.ForeColor.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.PanelText; this.panelEx1.Style.GradientAngle = 90; this.panelEx1.TabIndex = 0; // // advTree3 // this.advTree3.AccessibleRole = System.Windows.Forms.AccessibleRole.Outline; this.advTree3.AllowDrop = true; this.advTree3.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.advTree3.BackColor = System.Drawing.SystemColors.Window; // // // this.advTree3.BackgroundStyle.Class = "TreeBorderKey"; this.advTree3.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.advTree3.Columns.Add(this.columnHeader3); this.advTree3.Columns.Add(this.columnHeader4); this.advTree3.Columns.Add(this.columnHeader5); this.advTree3.ContextMenuStrip = this.contextMenuStrip2; this.advTree3.DragDropEnabled = false; this.advTree3.ImageList = this.imageList1; this.advTree3.Location = new System.Drawing.Point(3, 83); this.advTree3.Name = "advTree3"; this.advTree3.NodesConnector = this.nodeConnector3; this.advTree3.NodeStyle = this.elementStyle3; this.advTree3.PathSeparator = ";"; this.advTree3.Size = new System.Drawing.Size(227, 142); this.advTree3.Styles.Add(this.elementStyle3); this.advTree3.TabIndex = 1; this.advTree3.Text = "advTree3"; this.advTree3.AfterNodeSelect += new DevComponents.AdvTree.AdvTreeNodeEventHandler(this.advTree3_AfterNodeSelect); // // columnHeader3 // this.columnHeader3.Name = "columnHeader3"; this.columnHeader3.Text = "ImageNode"; this.columnHeader3.Width.Absolute = 150; // // columnHeader4 // this.columnHeader4.Name = "columnHeader4"; this.columnHeader4.Text = "Value"; this.columnHeader4.Width.Absolute = 150; // // columnHeader5 // this.columnHeader5.Name = "columnHeader5"; this.columnHeader5.Text = "ValueType"; this.columnHeader5.Width.Absolute = 150; // // contextMenuStrip2 // this.contextMenuStrip2.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { this.tsmi2SaveAs, this.tsmi2HandleUol, this.tsmi2Splitter1, this.tsmi2ExpandAll, this.tsmi2CollapseAll, this.toolStripMenuItem1, this.tsmi2ExpandLevel, this.tsmi2CollapseLevel, this.toolStripMenuItem2, this.tsmi2Prev, this.tsmi2Next, this.toolStripMenuItem4, this.tsmi2CopyFullPath}); this.contextMenuStrip2.Name = "contextMenuStrip2"; this.contextMenuStrip2.Size = new System.Drawing.Size(196, 226); this.contextMenuStrip2.Opening += new System.ComponentModel.CancelEventHandler(this.contextMenuStrip2_Opening); // // tsmi2SaveAs // this.tsmi2SaveAs.Name = "tsmi2SaveAs"; this.tsmi2SaveAs.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.S))); this.tsmi2SaveAs.Size = new System.Drawing.Size(195, 22); this.tsmi2SaveAs.Text = "Save as..."; this.tsmi2SaveAs.Click += new System.EventHandler(this.tsmi2SaveAs_Click); // // tsmi2HandleUol // this.tsmi2HandleUol.Name = "tsmi2HandleUol"; this.tsmi2HandleUol.Size = new System.Drawing.Size(195, 22); this.tsmi2HandleUol.Text = "Handle Uol"; this.tsmi2HandleUol.Click += new System.EventHandler(this.tsmi2HandleUol_Click); // // tsmi2Splitter1 // this.tsmi2Splitter1.Name = "tsmi2Splitter1"; this.tsmi2Splitter1.Size = new System.Drawing.Size(192, 6); // // tsmi2ExpandAll // this.tsmi2ExpandAll.Name = "tsmi2ExpandAll"; this.tsmi2ExpandAll.Size = new System.Drawing.Size(195, 22); this.tsmi2ExpandAll.Text = "&Expand All"; this.tsmi2ExpandAll.Click += new System.EventHandler(this.tsmi2ExpandAll_Click); // // tsmi2CollapseAll // this.tsmi2CollapseAll.Name = "tsmi2CollapseAll"; this.tsmi2CollapseAll.Size = new System.Drawing.Size(195, 22); this.tsmi2CollapseAll.Text = "&Collapse All"; this.tsmi2CollapseAll.Click += new System.EventHandler(this.tsmi2CollapseAll_Click); // // toolStripMenuItem1 // this.toolStripMenuItem1.Name = "toolStripMenuItem1"; this.toolStripMenuItem1.Size = new System.Drawing.Size(192, 6); // // tsmi2ExpandLevel // this.tsmi2ExpandLevel.Name = "tsmi2ExpandLevel"; this.tsmi2ExpandLevel.Size = new System.Drawing.Size(195, 22); this.tsmi2ExpandLevel.Text = "E&xpand Equal Level"; this.tsmi2ExpandLevel.Click += new System.EventHandler(this.tsmi2ExpandLevel_Click); // // tsmi2CollapseLevel // this.tsmi2CollapseLevel.Name = "tsmi2CollapseLevel"; this.tsmi2CollapseLevel.Size = new System.Drawing.Size(195, 22); this.tsmi2CollapseLevel.Text = "C&ollapse Equal Level"; this.tsmi2CollapseLevel.Click += new System.EventHandler(this.tsmi2CollapseLevel_Click); // // toolStripMenuItem2 // this.toolStripMenuItem2.Name = "toolStripMenuItem2"; this.toolStripMenuItem2.Size = new System.Drawing.Size(192, 6); // // tsmi2Prev // this.tsmi2Prev.Name = "tsmi2Prev"; this.tsmi2Prev.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.Z))); this.tsmi2Prev.Size = new System.Drawing.Size(195, 22); this.tsmi2Prev.Text = "&Prev Select"; this.tsmi2Prev.Click += new System.EventHandler(this.tsmi2Prev_Click); // // tsmi2Next // this.tsmi2Next.Name = "tsmi2Next"; this.tsmi2Next.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.X))); this.tsmi2Next.Size = new System.Drawing.Size(195, 22); this.tsmi2Next.Text = "&Next Select"; this.tsmi2Next.Click += new System.EventHandler(this.tsmi2Next_Click); // // imageList1 // this.imageList1.ColorDepth = System.Windows.Forms.ColorDepth.Depth32Bit; this.imageList1.ImageSize = new System.Drawing.Size(16, 16); this.imageList1.TransparentColor = System.Drawing.Color.Transparent; // // nodeConnector3 // this.nodeConnector3.LineColor = System.Drawing.SystemColors.ControlText; // // elementStyle3 // this.elementStyle3.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.elementStyle3.Name = "elementStyle3"; this.elementStyle3.TextColor = System.Drawing.SystemColors.ControlText; // // listViewExWzDetail // this.listViewExWzDetail.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); // // // this.listViewExWzDetail.Border.Class = "ListViewBorder"; this.listViewExWzDetail.Border.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.listViewExWzDetail.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { this.columnHeader1, this.columnHeader2}); this.listViewExWzDetail.DisabledBackColor = System.Drawing.Color.Empty; this.listViewExWzDetail.FullRowSelect = true; this.listViewExWzDetail.GridLines = true; this.listViewExWzDetail.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.None; this.listViewExWzDetail.HideSelection = false; this.listViewExWzDetail.Location = new System.Drawing.Point(3, 3); this.listViewExWzDetail.MultiSelect = false; this.listViewExWzDetail.Name = "listViewExWzDetail"; this.listViewExWzDetail.ShowGroups = false; this.listViewExWzDetail.Size = new System.Drawing.Size(227, 78); this.listViewExWzDetail.TabIndex = 0; this.listViewExWzDetail.UseCompatibleStateImageBehavior = false; this.listViewExWzDetail.View = System.Windows.Forms.View.Details; // // columnHeader1 // this.columnHeader1.Width = 80; // // columnHeader2 // this.columnHeader2.Width = 100; // // superTabItem1 // this.superTabItem1.AttachedControl = this.superTabControlPanel1; this.superTabItem1.GlobalItem = false; this.superTabItem1.Name = "superTabItem1"; this.superTabItem1.Text = "WzView"; // // superTabControlPanel2 // this.superTabControlPanel2.Controls.Add(this.chkHashPngFileName); this.superTabControlPanel2.Controls.Add(this.btnCustomCSS); this.superTabControlPanel2.Controls.Add(this.chkResolvePngLink); this.superTabControlPanel2.Controls.Add(this.chkOutputRemovedImg); this.superTabControlPanel2.Controls.Add(this.chkOutputAddedImg); this.superTabControlPanel2.Controls.Add(this.labelX1); this.superTabControlPanel2.Controls.Add(this.chkOutputPng); this.superTabControlPanel2.Controls.Add(this.cmbComparePng); this.superTabControlPanel2.Controls.Add(this.labelXComp2); this.superTabControlPanel2.Controls.Add(this.labelXComp1); this.superTabControlPanel2.Controls.Add(this.btnEasyCompare); this.superTabControlPanel2.Dock = System.Windows.Forms.DockStyle.Fill; this.superTabControlPanel2.Location = new System.Drawing.Point(0, 0); this.superTabControlPanel2.Name = "superTabControlPanel2"; this.superTabControlPanel2.Size = new System.Drawing.Size(530, 228); this.superTabControlPanel2.TabIndex = 0; this.superTabControlPanel2.TabItem = this.superTabItem2; // // chkResolvePngLink // // // // this.chkResolvePngLink.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.chkResolvePngLink.Location = new System.Drawing.Point(318, 34); this.chkResolvePngLink.Name = "chkResolvePngLink"; this.chkResolvePngLink.Size = new System.Drawing.Size(107, 23); this.chkResolvePngLink.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.superTooltip1.SetSuperTooltip(this.chkResolvePngLink, new DevComponents.DotNetBar.SuperTooltipInfo("ResolvePngLink", "", "对比报告中是否智能解析对比被Link的图片\r\n这会过滤掉无用的变更内容", null, null, DevComponents.DotNetBar.eTooltipColor.System, true, false, new System.Drawing.Size(180, 90))); this.chkResolvePngLink.TabIndex = 9; this.chkResolvePngLink.Text = "ResolvePngLink"; // // btnCustomCSS // // // // this.btnCustomCSS.AccessibleRole = System.Windows.Forms.AccessibleRole.PushButton; this.btnCustomCSS.ColorTable = DevComponents.DotNetBar.eButtonColor.OrangeWithBackground; this.btnCustomCSS.Location = new System.Drawing.Point(400, 61); this.btnCustomCSS.Name = "btnCustomCSS"; this.btnCustomCSS.Size = new System.Drawing.Size(80, 23); this.btnCustomCSS.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.superTooltip1.SetSuperTooltip(this.btnCustomCSS, new DevComponents.DotNetBar.SuperTooltipInfo("CustomCSS", "", "修改对比报告的配色。", null, null, DevComponents.DotNetBar.eTooltipColor.System, true, false, new System.Drawing.Size(180, 90))); this.btnCustomCSS.TabIndex = 10; this.btnCustomCSS.Text = "CustomCSS"; this.btnCustomCSS.Click += new System.EventHandler(this.btnCustomCSS_Click); // // chkOutputRemovedImg // // // // this.chkOutputRemovedImg.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.chkOutputRemovedImg.Location = new System.Drawing.Point(152, 61); this.chkOutputRemovedImg.Name = "chkOutputRemovedImg"; this.chkOutputRemovedImg.Size = new System.Drawing.Size(135, 23); this.chkOutputRemovedImg.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.superTooltip1.SetSuperTooltip(this.chkOutputRemovedImg, new DevComponents.DotNetBar.SuperTooltipInfo("OutputRemovedImg", "", "对比报告中是否输出被整体移除的Image的完整结构", null, null, DevComponents.DotNetBar.eTooltipColor.System, true, false, new System.Drawing.Size(180, 80))); this.chkOutputRemovedImg.TabIndex = 8; this.chkOutputRemovedImg.Text = "OutputRemovedImg"; // // chkOutputAddedImg // // // // this.chkOutputAddedImg.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.chkOutputAddedImg.Location = new System.Drawing.Point(34, 61); this.chkOutputAddedImg.Name = "chkOutputAddedImg"; this.chkOutputAddedImg.Size = new System.Drawing.Size(135, 23); this.chkOutputAddedImg.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.superTooltip1.SetSuperTooltip(this.chkOutputAddedImg, new DevComponents.DotNetBar.SuperTooltipInfo("OutputAddedImg", "", "对比报告中是否输出新增Image的完整结构", null, null, DevComponents.DotNetBar.eTooltipColor.System, true, false, new System.Drawing.Size(180, 80))); this.chkOutputAddedImg.TabIndex = 7; this.chkOutputAddedImg.Text = "OutputAddedImg"; // // labelX1 // this.labelX1.AutoSize = true; // // // this.labelX1.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.labelX1.Location = new System.Drawing.Point(34, 39); this.labelX1.Name = "labelX1"; this.labelX1.Size = new System.Drawing.Size(44, 16); this.labelX1.TabIndex = 6; this.labelX1.Text = "wzPng:"; // // chkOutputPng // // // // this.chkOutputPng.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.chkOutputPng.Checked = true; this.chkOutputPng.CheckState = System.Windows.Forms.CheckState.Checked; this.chkOutputPng.CheckValue = "Y"; this.chkOutputPng.Location = new System.Drawing.Point(205, 34); this.chkOutputPng.Name = "chkOutputPng"; this.chkOutputPng.Size = new System.Drawing.Size(107, 23); this.chkOutputPng.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.superTooltip1.SetSuperTooltip(this.chkOutputPng, new DevComponents.DotNetBar.SuperTooltipInfo("OutputPngFile", "", "对比报告中是否输出有差异的图片文件。", null, null, DevComponents.DotNetBar.eTooltipColor.System, true, false, new System.Drawing.Size(180, 80))); this.chkOutputPng.TabIndex = 5; this.chkOutputPng.Text = "OutputPngFile"; // // cmbComparePng // this.cmbComparePng.DisplayMember = "Text"; this.cmbComparePng.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawFixed; this.cmbComparePng.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.cmbComparePng.FormattingEnabled = true; this.cmbComparePng.ItemHeight = 15; this.cmbComparePng.Location = new System.Drawing.Point(83, 36); this.cmbComparePng.Name = "cmbComparePng"; this.cmbComparePng.Size = new System.Drawing.Size(120, 21); this.cmbComparePng.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.superTooltip1.SetSuperTooltip(this.cmbComparePng, new DevComponents.DotNetBar.SuperTooltipInfo("PngComparison", "", "对于对比报告中图片的对比方式。\r\nSizeOnly - 仅对比图片大小,可能会遗漏。\r\nSizeAndDataLength - 同时对比图片大小和压缩流长度,可能" + "会误判。\r\nPixel - 像素级对比,非常精确但可能略耗时。", null, null, DevComponents.DotNetBar.eTooltipColor.System, true, true, new System.Drawing.Size(300, 130))); this.cmbComparePng.TabIndex = 4; // // labelXComp2 // this.labelXComp2.AutoSize = true; // // // this.labelXComp2.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.labelXComp2.Location = new System.Drawing.Point(3, 113); this.labelXComp2.Name = "labelXComp2"; this.labelXComp2.Size = new System.Drawing.Size(44, 16); this.labelXComp2.TabIndex = 3; this.labelXComp2.Text = "detail"; this.labelXComp2.TextLineAlignment = System.Drawing.StringAlignment.Near; // // labelXComp1 // this.labelXComp1.AutoSize = true; // // // this.labelXComp1.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.labelXComp1.Location = new System.Drawing.Point(3, 91); this.labelXComp1.Name = "labelXComp1"; this.labelXComp1.Size = new System.Drawing.Size(31, 16); this.labelXComp1.TabIndex = 1; this.labelXComp1.Text = "tail"; this.labelXComp1.TextLineAlignment = System.Drawing.StringAlignment.Near; // // btnEasyCompare // this.btnEasyCompare.AccessibleRole = System.Windows.Forms.AccessibleRole.PushButton; this.btnEasyCompare.ColorTable = DevComponents.DotNetBar.eButtonColor.OrangeWithBackground; this.btnEasyCompare.Location = new System.Drawing.Point(3, 3); this.btnEasyCompare.Name = "btnEasyCompare"; this.btnEasyCompare.Size = new System.Drawing.Size(100, 30); this.btnEasyCompare.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.btnEasyCompare.TabIndex = 0; this.btnEasyCompare.Text = "Easy Compare"; this.btnEasyCompare.Click += new System.EventHandler(this.btnEasyCompare_Click); // // superTabItem2 // this.superTabItem2.AttachedControl = this.superTabControlPanel2; this.superTabItem2.GlobalItem = false; this.superTabItem2.Name = "superTabItem2"; this.superTabItem2.Text = "WzCompare"; // // superTabControlPanel3 // this.superTabControlPanel3.Controls.Add(this.btnExportSkillOption); this.superTabControlPanel3.Controls.Add(this.btnExportSkill); this.superTabControlPanel3.Dock = System.Windows.Forms.DockStyle.Fill; this.superTabControlPanel3.Location = new System.Drawing.Point(0, 0); this.superTabControlPanel3.Name = "superTabControlPanel3"; this.superTabControlPanel3.Size = new System.Drawing.Size(530, 228); this.superTabControlPanel3.TabIndex = 0; this.superTabControlPanel3.TabItem = this.superTabItem3; // // btnExportSkillOption // this.btnExportSkillOption.AccessibleRole = System.Windows.Forms.AccessibleRole.PushButton; this.btnExportSkillOption.ColorTable = DevComponents.DotNetBar.eButtonColor.OrangeWithBackground; this.btnExportSkillOption.Location = new System.Drawing.Point(99, 6); this.btnExportSkillOption.Name = "btnExportSkillOption"; this.btnExportSkillOption.Size = new System.Drawing.Size(111, 23); this.btnExportSkillOption.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.btnExportSkillOption.TabIndex = 1; this.btnExportSkillOption.Text = "ExportSkillOption"; this.btnExportSkillOption.Click += new System.EventHandler(this.btnExportSkillOption_Click); // // btnExportSkill // this.btnExportSkill.AccessibleRole = System.Windows.Forms.AccessibleRole.PushButton; this.btnExportSkill.ColorTable = DevComponents.DotNetBar.eButtonColor.OrangeWithBackground; this.btnExportSkill.Location = new System.Drawing.Point(6, 6); this.btnExportSkill.Name = "btnExportSkill"; this.btnExportSkill.Size = new System.Drawing.Size(75, 23); this.btnExportSkill.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.btnExportSkill.TabIndex = 0; this.btnExportSkill.Text = "ExportSkill"; this.btnExportSkill.Click += new System.EventHandler(this.btnExportSkill_Click); // // superTabItem3 // this.superTabItem3.AttachedControl = this.superTabControlPanel3; this.superTabItem3.GlobalItem = false; this.superTabItem3.Name = "superTabItem3"; this.superTabItem3.Text = "DataBase"; // // btnNodeBack // this.btnNodeBack.ImagePaddingHorizontal = 6; this.btnNodeBack.ImagePaddingVertical = 4; this.btnNodeBack.Name = "btnNodeBack"; this.btnNodeBack.Symbol = ""; this.btnNodeBack.SymbolColor = System.Drawing.Color.Gray; this.btnNodeBack.SymbolSize = 12F; this.btnNodeBack.Text = "back"; this.btnNodeBack.Click += new System.EventHandler(this.btnNodeBack_Click); // // btnNodeForward // this.btnNodeForward.ImagePaddingHorizontal = 6; this.btnNodeForward.ImagePaddingVertical = 4; this.btnNodeForward.Name = "btnNodeForward"; this.btnNodeForward.Symbol = ""; this.btnNodeForward.SymbolColor = System.Drawing.Color.Gray; this.btnNodeForward.SymbolSize = 12F; this.btnNodeForward.Text = "forward"; this.btnNodeForward.Click += new System.EventHandler(this.btnNodeForward_Click); // // panelExLeft // this.panelExLeft.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left))); this.panelExLeft.CanvasColor = System.Drawing.SystemColors.Control; this.panelExLeft.ColorSchemeStyle = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.panelExLeft.Controls.Add(this.advTree2); this.panelExLeft.Controls.Add(this.expandableSplitter2); this.panelExLeft.Controls.Add(this.advTree1); this.panelExLeft.DisabledBackColor = System.Drawing.Color.Empty; this.panelExLeft.Location = new System.Drawing.Point(3, 3); this.panelExLeft.Name = "panelExLeft"; this.panelExLeft.Size = new System.Drawing.Size(200, 228); this.panelExLeft.Style.Alignment = System.Drawing.StringAlignment.Center; this.panelExLeft.Style.BackColor1.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.PanelBackground; this.panelExLeft.Style.BackColor2.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.PanelBackground2; this.panelExLeft.Style.Border = DevComponents.DotNetBar.eBorderType.SingleLine; this.panelExLeft.Style.BorderColor.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.PanelBorder; this.panelExLeft.Style.ForeColor.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.PanelText; this.panelExLeft.Style.GradientAngle = 90; this.panelExLeft.TabIndex = 0; this.panelExLeft.SizeChanged += new System.EventHandler(this.panelExLeft_SizeChanged); // // advTree2 // this.advTree2.AccessibleRole = System.Windows.Forms.AccessibleRole.Outline; this.advTree2.AllowDrop = true; this.advTree2.BackColor = System.Drawing.SystemColors.Window; // // // this.advTree2.BackgroundStyle.Class = "TreeBorderKey"; this.advTree2.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.advTree2.Dock = System.Windows.Forms.DockStyle.Fill; this.advTree2.DoubleClickTogglesNode = false; this.advTree2.DragDropEnabled = false; this.advTree2.Location = new System.Drawing.Point(0, 157); this.advTree2.Name = "advTree2"; this.advTree2.NodesConnector = this.nodeConnector2; this.advTree2.NodeStyle = this.elementStyle2; this.advTree2.PathSeparator = ";"; this.advTree2.Size = new System.Drawing.Size(200, 71); this.advTree2.Styles.Add(this.elementStyle2); this.advTree2.TabIndex = 1; this.advTree2.Text = "advTree2"; this.advTree2.NodeDoubleClick += new DevComponents.AdvTree.TreeNodeMouseEventHandler(this.advTree2_NodeDoubleClick); // // nodeConnector2 // this.nodeConnector2.LineColor = System.Drawing.SystemColors.ControlText; // // elementStyle2 // this.elementStyle2.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.elementStyle2.Name = "elementStyle2"; this.elementStyle2.TextColor = System.Drawing.SystemColors.ControlText; // // expandableSplitter2 // this.expandableSplitter2.BackColor2 = System.Drawing.Color.FromArgb(((int)(((byte)(100)))), ((int)(((byte)(110)))), ((int)(((byte)(121))))); this.expandableSplitter2.BackColor2SchemePart = DevComponents.DotNetBar.eColorSchemePart.PanelBorder; this.expandableSplitter2.BackColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.PanelBackground; this.expandableSplitter2.Dock = System.Windows.Forms.DockStyle.Top; this.expandableSplitter2.ExpandActionClick = false; this.expandableSplitter2.ExpandFillColor = System.Drawing.Color.FromArgb(((int)(((byte)(100)))), ((int)(((byte)(110)))), ((int)(((byte)(121))))); this.expandableSplitter2.ExpandFillColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.PanelBorder; this.expandableSplitter2.ExpandLineColor = System.Drawing.Color.FromArgb(((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0))))); this.expandableSplitter2.ExpandLineColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.ItemText; this.expandableSplitter2.GripDarkColor = System.Drawing.Color.FromArgb(((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0))))); this.expandableSplitter2.GripDarkColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.ItemText; this.expandableSplitter2.GripLightColor = System.Drawing.Color.FromArgb(((int)(((byte)(254)))), ((int)(((byte)(254)))), ((int)(((byte)(255))))); this.expandableSplitter2.GripLightColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.BarBackground; this.expandableSplitter2.HotBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(229)))), ((int)(((byte)(244)))), ((int)(((byte)(252))))); this.expandableSplitter2.HotBackColor2 = System.Drawing.Color.FromArgb(((int)(((byte)(104)))), ((int)(((byte)(179)))), ((int)(((byte)(219))))); this.expandableSplitter2.HotBackColor2SchemePart = DevComponents.DotNetBar.eColorSchemePart.ItemPressedBackground2; this.expandableSplitter2.HotBackColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.ItemPressedBackground; this.expandableSplitter2.HotExpandFillColor = System.Drawing.Color.FromArgb(((int)(((byte)(100)))), ((int)(((byte)(110)))), ((int)(((byte)(121))))); this.expandableSplitter2.HotExpandFillColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.PanelBorder; this.expandableSplitter2.HotExpandLineColor = System.Drawing.Color.FromArgb(((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0))))); this.expandableSplitter2.HotExpandLineColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.ItemText; this.expandableSplitter2.HotGripDarkColor = System.Drawing.Color.FromArgb(((int)(((byte)(100)))), ((int)(((byte)(110)))), ((int)(((byte)(121))))); this.expandableSplitter2.HotGripDarkColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.PanelBorder; this.expandableSplitter2.HotGripLightColor = System.Drawing.Color.FromArgb(((int)(((byte)(254)))), ((int)(((byte)(254)))), ((int)(((byte)(255))))); this.expandableSplitter2.HotGripLightColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.BarBackground; this.expandableSplitter2.Location = new System.Drawing.Point(0, 150); this.expandableSplitter2.MinExtra = 0; this.expandableSplitter2.MinSize = 0; this.expandableSplitter2.Name = "expandableSplitter2"; this.expandableSplitter2.Size = new System.Drawing.Size(200, 7); this.expandableSplitter2.Style = DevComponents.DotNetBar.eSplitterStyle.Office2007; this.expandableSplitter2.TabIndex = 2; this.expandableSplitter2.TabStop = false; // // advTree1 // this.advTree1.AccessibleRole = System.Windows.Forms.AccessibleRole.Outline; this.advTree1.AllowDrop = true; this.advTree1.BackColor = System.Drawing.SystemColors.Window; // // // this.advTree1.BackgroundStyle.Class = "TreeBorderKey"; this.advTree1.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.advTree1.ContextMenuStrip = this.contextMenuStrip1; this.advTree1.Dock = System.Windows.Forms.DockStyle.Top; this.advTree1.DragDropEnabled = false; this.advTree1.Indent = 14; this.advTree1.Location = new System.Drawing.Point(0, 0); this.advTree1.Name = "advTree1"; this.advTree1.NodesConnector = this.nodeConnector1; this.advTree1.NodeStyle = this.elementStyle1; this.advTree1.PathSeparator = ";"; this.advTree1.Size = new System.Drawing.Size(200, 150); this.advTree1.Styles.Add(this.elementStyle1); this.advTree1.TabIndex = 0; this.advTree1.Text = "advTree1"; this.advTree1.AfterNodeSelect += new DevComponents.AdvTree.AdvTreeNodeEventHandler(this.advTree1_AfterNodeSelect); this.advTree1.DragDrop += new System.Windows.Forms.DragEventHandler(this.advTree1_DragDrop); this.advTree1.DragEnter += new System.Windows.Forms.DragEventHandler(this.advTree1_DragEnter); // // contextMenuStrip1 // this.contextMenuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { this.tsmi1Sort, this.toolStripMenuItem3, this.tsmi1Export, this.tsmi1DumpAsXml}); this.contextMenuStrip1.Name = "contextMenuStrip1"; this.contextMenuStrip1.Size = new System.Drawing.Size(155, 76); // // tsmi1Sort // this.tsmi1Sort.Name = "tsmi1Sort"; this.tsmi1Sort.Size = new System.Drawing.Size(154, 22); this.tsmi1Sort.Text = "&Sort"; this.tsmi1Sort.Click += new System.EventHandler(this.tsmi1Sort_Click); // // toolStripMenuItem3 // this.toolStripMenuItem3.Name = "toolStripMenuItem3"; this.toolStripMenuItem3.Size = new System.Drawing.Size(151, 6); // // tsmi1Export // this.tsmi1Export.Name = "tsmi1Export"; this.tsmi1Export.Size = new System.Drawing.Size(154, 22); this.tsmi1Export.Text = "&Export"; this.tsmi1Export.Click += new System.EventHandler(this.tsmi1Export_Click); // // tsmi1DumpAsXml // this.tsmi1DumpAsXml.Name = "tsmi1DumpAsXml"; this.tsmi1DumpAsXml.Size = new System.Drawing.Size(154, 22); this.tsmi1DumpAsXml.Text = "&Dump as Xml"; this.tsmi1DumpAsXml.Click += new System.EventHandler(this.tsmi1DumpAsXml_Click); // // elementStyle1 // this.elementStyle1.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.elementStyle1.Name = "elementStyle1"; this.elementStyle1.TextColor = System.Drawing.SystemColors.ControlText; // // listViewExString // // // // this.listViewExString.Border.Class = "ListViewBorder"; this.listViewExString.Border.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.listViewExString.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { this.columnHeader6, this.columnHeader7, this.columnHeader8, this.columnHeader9}); this.listViewExString.DisabledBackColor = System.Drawing.Color.Empty; this.listViewExString.Dock = System.Windows.Forms.DockStyle.Fill; this.listViewExString.FullRowSelect = true; this.listViewExString.GridLines = true; this.listViewExString.HideSelection = false; this.listViewExString.Location = new System.Drawing.Point(0, 0); this.listViewExString.MultiSelect = false; this.listViewExString.Name = "listViewExString"; this.listViewExString.ShowGroups = false; this.listViewExString.ShowItemToolTips = true; this.listViewExString.Size = new System.Drawing.Size(734, 69); this.listViewExString.TabIndex = 0; this.listViewExString.UseCompatibleStateImageBehavior = false; this.listViewExString.View = System.Windows.Forms.View.Details; this.listViewExString.KeyDown += new System.Windows.Forms.KeyEventHandler(this.listViewExString_KeyDown); this.listViewExString.MouseDoubleClick += new System.Windows.Forms.MouseEventHandler(this.listViewExString_MouseDoubleClick); // // columnHeader6 // this.columnHeader6.Text = "Code"; this.columnHeader6.Width = 80; // // columnHeader7 // this.columnHeader7.Text = "Name"; this.columnHeader7.Width = 100; // // columnHeader8 // this.columnHeader8.Text = "Desc"; this.columnHeader8.Width = 350; // // columnHeader9 // this.columnHeader9.Text = "StringPath"; this.columnHeader9.Width = 150; // // comboItem1 // this.comboItem1.Text = "wz"; // // comboItem2 // this.comboItem2.Text = "img"; // // dotNetBarManager1 // this.dotNetBarManager1.AutoDispatchShortcuts.Add(DevComponents.DotNetBar.eShortcut.F1); this.dotNetBarManager1.AutoDispatchShortcuts.Add(DevComponents.DotNetBar.eShortcut.CtrlC); this.dotNetBarManager1.AutoDispatchShortcuts.Add(DevComponents.DotNetBar.eShortcut.CtrlA); this.dotNetBarManager1.AutoDispatchShortcuts.Add(DevComponents.DotNetBar.eShortcut.CtrlV); this.dotNetBarManager1.AutoDispatchShortcuts.Add(DevComponents.DotNetBar.eShortcut.CtrlX); this.dotNetBarManager1.AutoDispatchShortcuts.Add(DevComponents.DotNetBar.eShortcut.CtrlZ); this.dotNetBarManager1.AutoDispatchShortcuts.Add(DevComponents.DotNetBar.eShortcut.CtrlY); this.dotNetBarManager1.AutoDispatchShortcuts.Add(DevComponents.DotNetBar.eShortcut.Del); this.dotNetBarManager1.AutoDispatchShortcuts.Add(DevComponents.DotNetBar.eShortcut.Ins); this.dotNetBarManager1.BottomDockSite = this.dockSite4; this.dotNetBarManager1.EnableFullSizeDock = false; this.dotNetBarManager1.LeftDockSite = this.dockSite1; this.dotNetBarManager1.ParentForm = this; this.dotNetBarManager1.RightDockSite = this.dockSite2; this.dotNetBarManager1.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.dotNetBarManager1.ToolbarBottomDockSite = this.dockSite8; this.dotNetBarManager1.ToolbarLeftDockSite = this.dockSite5; this.dotNetBarManager1.ToolbarRightDockSite = this.dockSite6; this.dotNetBarManager1.ToolbarTopDockSite = this.dockSite7; this.dotNetBarManager1.TopDockSite = this.dockSite3; // // dockSite4 // this.dockSite4.AccessibleRole = System.Windows.Forms.AccessibleRole.Window; this.dockSite4.Controls.Add(this.bar1); this.dockSite4.Dock = System.Windows.Forms.DockStyle.Bottom; this.dockSite4.DocumentDockContainer = new DevComponents.DotNetBar.DocumentDockContainer(new DevComponents.DotNetBar.DocumentBaseContainer[] { ((DevComponents.DotNetBar.DocumentBaseContainer)(new DevComponents.DotNetBar.DocumentBarContainer(this.bar1, 740, 95)))}, DevComponents.DotNetBar.eOrientation.Vertical); this.dockSite4.Location = new System.Drawing.Point(5, 388); this.dockSite4.Name = "dockSite4"; this.dockSite4.Size = new System.Drawing.Size(740, 98); this.dockSite4.TabIndex = 7; this.dockSite4.TabStop = false; // // bar1 // this.bar1.AccessibleDescription = "DotNetBar Bar (bar1)"; this.bar1.AccessibleName = "DotNetBar Bar"; this.bar1.AccessibleRole = System.Windows.Forms.AccessibleRole.Grouping; this.bar1.AutoSyncBarCaption = true; this.bar1.CanCustomize = false; this.bar1.CanDockLeft = false; this.bar1.CanDockRight = false; this.bar1.CanDockTab = false; this.bar1.CanDockTop = false; this.bar1.CloseSingleTab = true; this.bar1.Controls.Add(this.panelDockContainer1); this.bar1.Font = new System.Drawing.Font("宋体", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); this.bar1.GrabHandleStyle = DevComponents.DotNetBar.eGrabHandleStyle.Caption; this.bar1.IsMaximized = false; this.bar1.Items.AddRange(new DevComponents.DotNetBar.BaseItem[] { this.dockContainerItem1}); this.bar1.LayoutType = DevComponents.DotNetBar.eLayoutType.DockContainer; this.bar1.Location = new System.Drawing.Point(0, 3); this.bar1.Name = "bar1"; this.bar1.Size = new System.Drawing.Size(740, 95); this.bar1.Stretch = true; this.bar1.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.bar1.TabIndex = 0; this.bar1.TabStop = false; this.bar1.Text = "搜索结果"; // // panelDockContainer1 // this.panelDockContainer1.ColorSchemeStyle = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.panelDockContainer1.Controls.Add(this.listViewExString); this.panelDockContainer1.DisabledBackColor = System.Drawing.Color.Empty; this.panelDockContainer1.Location = new System.Drawing.Point(3, 23); this.panelDockContainer1.Name = "panelDockContainer1"; this.panelDockContainer1.Size = new System.Drawing.Size(734, 69); this.panelDockContainer1.Style.Alignment = System.Drawing.StringAlignment.Center; this.panelDockContainer1.Style.BackColor1.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.BarBackground; this.panelDockContainer1.Style.BorderColor.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.BarDockedBorder; this.panelDockContainer1.Style.ForeColor.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.ItemText; this.panelDockContainer1.Style.GradientAngle = 90; this.panelDockContainer1.TabIndex = 0; // // dockContainerItem1 // this.dockContainerItem1.Control = this.panelDockContainer1; this.dockContainerItem1.Name = "dockContainerItem1"; this.dockContainerItem1.Text = "搜索结果"; // // dockSite1 // this.dockSite1.AccessibleRole = System.Windows.Forms.AccessibleRole.Window; this.dockSite1.Dock = System.Windows.Forms.DockStyle.Left; this.dockSite1.DocumentDockContainer = new DevComponents.DotNetBar.DocumentDockContainer(); this.dockSite1.Location = new System.Drawing.Point(5, 154); this.dockSite1.Name = "dockSite1"; this.dockSite1.Size = new System.Drawing.Size(0, 234); this.dockSite1.TabIndex = 4; this.dockSite1.TabStop = false; // // dockSite2 // this.dockSite2.AccessibleRole = System.Windows.Forms.AccessibleRole.Window; this.dockSite2.Dock = System.Windows.Forms.DockStyle.Right; this.dockSite2.DocumentDockContainer = new DevComponents.DotNetBar.DocumentDockContainer(); this.dockSite2.Location = new System.Drawing.Point(745, 154); this.dockSite2.Name = "dockSite2"; this.dockSite2.Size = new System.Drawing.Size(0, 234); this.dockSite2.TabIndex = 5; this.dockSite2.TabStop = false; // // dockSite8 // this.dockSite8.AccessibleRole = System.Windows.Forms.AccessibleRole.Window; this.dockSite8.Dock = System.Windows.Forms.DockStyle.Bottom; this.dockSite8.Location = new System.Drawing.Point(5, 486); this.dockSite8.Name = "dockSite8"; this.dockSite8.Size = new System.Drawing.Size(740, 0); this.dockSite8.TabIndex = 11; this.dockSite8.TabStop = false; // // dockSite5 // this.dockSite5.AccessibleRole = System.Windows.Forms.AccessibleRole.Window; this.dockSite5.Dock = System.Windows.Forms.DockStyle.Left; this.dockSite5.Location = new System.Drawing.Point(5, 1); this.dockSite5.Name = "dockSite5"; this.dockSite5.Size = new System.Drawing.Size(0, 485); this.dockSite5.TabIndex = 8; this.dockSite5.TabStop = false; // // dockSite6 // this.dockSite6.AccessibleRole = System.Windows.Forms.AccessibleRole.Window; this.dockSite6.Dock = System.Windows.Forms.DockStyle.Right; this.dockSite6.Location = new System.Drawing.Point(745, 1); this.dockSite6.Name = "dockSite6"; this.dockSite6.Size = new System.Drawing.Size(0, 485); this.dockSite6.TabIndex = 9; this.dockSite6.TabStop = false; // // dockSite7 // this.dockSite7.AccessibleRole = System.Windows.Forms.AccessibleRole.Window; this.dockSite7.Dock = System.Windows.Forms.DockStyle.Top; this.dockSite7.Location = new System.Drawing.Point(5, 1); this.dockSite7.Name = "dockSite7"; this.dockSite7.Size = new System.Drawing.Size(740, 0); this.dockSite7.TabIndex = 10; this.dockSite7.TabStop = false; // // dockSite3 // this.dockSite3.AccessibleRole = System.Windows.Forms.AccessibleRole.Window; this.dockSite3.Dock = System.Windows.Forms.DockStyle.Top; this.dockSite3.DocumentDockContainer = new DevComponents.DotNetBar.DocumentDockContainer(); this.dockSite3.Location = new System.Drawing.Point(5, 1); this.dockSite3.Name = "dockSite3"; this.dockSite3.Size = new System.Drawing.Size(740, 0); this.dockSite3.TabIndex = 6; this.dockSite3.TabStop = false; // // dockContainerItem2 // this.dockContainerItem2.Name = "dockContainerItem2"; this.dockContainerItem2.Text = "dockContainerItem2"; // // panelDockContainer2 // this.panelDockContainer2.DisabledBackColor = System.Drawing.Color.Empty; this.panelDockContainer2.Location = new System.Drawing.Point(3, 23); this.panelDockContainer2.Name = "panelDockContainer2"; this.panelDockContainer2.Size = new System.Drawing.Size(734, 44); this.panelDockContainer2.Style.Alignment = System.Drawing.StringAlignment.Center; this.panelDockContainer2.Style.BackColor1.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.BarBackground; this.panelDockContainer2.Style.BorderColor.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.BarDockedBorder; this.panelDockContainer2.Style.ForeColor.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.ItemText; this.panelDockContainer2.Style.GradientAngle = 90; this.panelDockContainer2.TabIndex = 2; // // chkHashPngFileName // // // // this.chkHashPngFileName.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.chkHashPngFileName.Location = new System.Drawing.Point(280, 61); this.chkHashPngFileName.Name = "chkHashPngFileName"; this.chkHashPngFileName.Size = new System.Drawing.Size(115, 23); this.chkHashPngFileName.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.superTooltip1.SetSuperTooltip(this.chkHashPngFileName, new DevComponents.DotNetBar.SuperTooltipInfo("HashPngFileName", "", "以 MD5 校验值输出 PNG 文件名称", null, null, DevComponents.DotNetBar.eTooltipColor.System, true, false, new System.Drawing.Size(180, 80))); this.chkHashPngFileName.TabIndex = 9; this.chkHashPngFileName.Text = "HashPngFileName"; // // toolStripMenuItem4 // this.toolStripMenuItem4.Name = "toolStripMenuItem4"; this.toolStripMenuItem4.Size = new System.Drawing.Size(192, 6); // // tsmi2CopyFullPath // this.tsmi2CopyFullPath.Name = "tsmi2CopyFullPath"; this.tsmi2CopyFullPath.Size = new System.Drawing.Size(195, 22); this.tsmi2CopyFullPath.Text = "Copy Full Path"; this.tsmi2CopyFullPath.Click += new System.EventHandler(this.tsmi2CopyFullPath_Click); // // comboItem19 // this.comboItem19.Text = "fullPath"; // // MainForm // this.ClientSize = new System.Drawing.Size(750, 512); this.Controls.Add(this.dockSite2); this.Controls.Add(this.dockSite1); this.Controls.Add(this.panelExMain); this.Controls.Add(this.ribbonControl1); this.Controls.Add(this.dockSite3); this.Controls.Add(this.dockSite4); this.Controls.Add(this.dockSite5); this.Controls.Add(this.dockSite6); this.Controls.Add(this.dockSite7); this.Controls.Add(this.dockSite8); this.Controls.Add(this.ribbonBar2); this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); this.MinimumSize = new System.Drawing.Size(750, 513); this.Name = "MainForm"; this.Text = "WzComparerR2"; this.Shown += new System.EventHandler(MainForm_Shown); this.ribbonControl1.ResumeLayout(false); this.ribbonControl1.PerformLayout(); this.ribbonPanel1.ResumeLayout(false); this.ribbonPanel2.ResumeLayout(false); this.ribbonPanel3.ResumeLayout(false); this.panelExMain.ResumeLayout(false); this.panelExRight.ResumeLayout(false); ((System.ComponentModel.ISupportInitialize)(this.superTabControl1)).EndInit(); this.superTabControl1.ResumeLayout(false); this.superTabControlPanel1.ResumeLayout(false); this.panelEx2.ResumeLayout(false); this.panelEx1.ResumeLayout(false); ((System.ComponentModel.ISupportInitialize)(this.advTree3)).EndInit(); this.contextMenuStrip2.ResumeLayout(false); this.superTabControlPanel2.ResumeLayout(false); this.superTabControlPanel2.PerformLayout(); this.superTabControlPanel3.ResumeLayout(false); this.panelExLeft.ResumeLayout(false); ((System.ComponentModel.ISupportInitialize)(this.advTree2)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.advTree1)).EndInit(); this.contextMenuStrip1.ResumeLayout(false); this.dockSite4.ResumeLayout(false); ((System.ComponentModel.ISupportInitialize)(this.bar1)).EndInit(); this.bar1.ResumeLayout(false); this.panelDockContainer1.ResumeLayout(false); this.ResumeLayout(false); } #endregion private DevComponents.DotNetBar.RibbonControl ribbonControl1; private DevComponents.DotNetBar.RibbonPanel ribbonPanel1; private DevComponents.DotNetBar.RibbonPanel ribbonPanel2; private DevComponents.DotNetBar.Office2007StartButton office2007StartButton1; private DevComponents.DotNetBar.ItemContainer itemContainer1; private DevComponents.DotNetBar.ItemContainer itemContainer2; private DevComponents.DotNetBar.ItemContainer itemContainer3; private DevComponents.DotNetBar.ButtonItem btnItemOpenWz; private DevComponents.DotNetBar.ButtonItem buttonItemClose; private DevComponents.DotNetBar.GalleryContainer galleryContainerRecent; private DevComponents.DotNetBar.LabelItem labelItem8; private DevComponents.DotNetBar.ItemContainer itemContainer4; private DevComponents.DotNetBar.ButtonItem btnItemOptions; private DevComponents.DotNetBar.ButtonItem buttonItem13; private DevComponents.DotNetBar.RibbonTabItem ribbonTabItem1; private DevComponents.DotNetBar.RibbonTabItem ribbonTabItem2; private DevComponents.DotNetBar.StyleManager styleManager1; private DevComponents.DotNetBar.RibbonBar ribbonBar1; private DevComponents.DotNetBar.ColorPickerDropDown colorPickerDropDown1; private DevComponents.DotNetBar.RibbonBar ribbonBar2; private DevComponents.DotNetBar.LabelItem labelItemStatus; private DevComponents.DotNetBar.ProgressBarItem progressBarItem1; private DevComponents.DotNetBar.PanelEx panelExMain; private DevComponents.DotNetBar.PanelEx panelExRight; private DevComponents.DotNetBar.PanelEx panelExLeft; private DevComponents.DotNetBar.ButtonItem buttonItemStyle; private DevComponents.AdvTree.AdvTree advTree2; private DevComponents.AdvTree.NodeConnector nodeConnector2; private DevComponents.DotNetBar.ElementStyle elementStyle2; private DevComponents.AdvTree.AdvTree advTree1; private DevComponents.DotNetBar.ElementStyle elementStyle1; private DevComponents.AdvTree.NodeConnector nodeConnector1; private DevComponents.DotNetBar.SuperTabControl superTabControl1; private DevComponents.DotNetBar.SuperTabControlPanel superTabControlPanel1; private DevComponents.DotNetBar.SuperTabItem superTabItem1; private DevComponents.DotNetBar.SuperTabControlPanel superTabControlPanel2; private DevComponents.DotNetBar.SuperTabItem superTabItem2; private DevComponents.DotNetBar.SuperTabControlPanel superTabControlPanel3; private DevComponents.DotNetBar.SuperTabItem superTabItem3; private DevComponents.DotNetBar.RibbonBar ribbonBar3; private System.Windows.Forms.ContextMenuStrip contextMenuStrip1; private System.Windows.Forms.ToolStripMenuItem tsmi1Sort; private DevComponents.DotNetBar.Controls.ListViewEx listViewExString; private DevComponents.Editors.ComboItem comboItem1; private DevComponents.Editors.ComboItem comboItem2; private DevComponents.DotNetBar.RibbonBar ribbonBar4; private DevComponents.DotNetBar.ItemContainer itemContainer10; private DevComponents.DotNetBar.ItemContainer itemContainer11; private DevComponents.DotNetBar.ItemContainer itemContainer12; private DevComponents.DotNetBar.CheckBoxItem checkBoxItemExact2; private DevComponents.DotNetBar.ComboBoxItem comboBoxItem2; private DevComponents.DotNetBar.ButtonItem buttonItemSearchString; private DevComponents.DotNetBar.ItemContainer itemContainer14; private DevComponents.DotNetBar.ItemContainer itemContainer15; private DevComponents.DotNetBar.LabelItem labelItem3; private DevComponents.DotNetBar.ItemContainer itemContainer16; private DevComponents.DotNetBar.CheckBoxItem checkBoxItemExact1; private DevComponents.DotNetBar.ComboBoxItem comboBoxItem1; private DevComponents.DotNetBar.ItemContainer itemContainer17; private DevComponents.DotNetBar.ButtonItem buttonItemSearchWz; private DevComponents.DotNetBar.PanelEx panelEx2; private DevComponents.DotNetBar.Controls.TextBoxX textBoxX1; private DevComponents.DotNetBar.ExpandableSplitter expandableSplitter1; private DevComponents.DotNetBar.PanelEx panelEx1; private DevComponents.AdvTree.AdvTree advTree3; private DevComponents.AdvTree.NodeConnector nodeConnector3; private DevComponents.DotNetBar.ElementStyle elementStyle3; private DevComponents.DotNetBar.Controls.ListViewEx listViewExWzDetail; private System.Windows.Forms.ColumnHeader columnHeader1; private System.Windows.Forms.ColumnHeader columnHeader2; private DevComponents.DotNetBar.RibbonBar ribbonBar5; private DevComponents.DotNetBar.ButtonItem buttonItemSaveImage; private System.Windows.Forms.ImageList imageList1; private DevComponents.AdvTree.ColumnHeader columnHeader3; private DevComponents.AdvTree.ColumnHeader columnHeader4; private DevComponents.AdvTree.ColumnHeader columnHeader5; private DevComponents.DotNetBar.ItemContainer itemContainer6; private DevComponents.DotNetBar.ItemContainer itemContainer7; private DevComponents.DotNetBar.LabelItem labelItemSoundTitle; private DevComponents.DotNetBar.ItemContainer itemContainer9; private DevComponents.DotNetBar.SliderItem sliderItemSoundTime; private DevComponents.DotNetBar.CheckBoxItem checkBoxItemSoundLoop; private DevComponents.DotNetBar.ItemContainer itemContainer13; private DevComponents.DotNetBar.ButtonItem buttonItemSoundStop; private DevComponents.DotNetBar.SliderItem sliderItemSoundVol; private DevComponents.DotNetBar.ButtonItem buttonItemSoundSave; private DevComponents.DotNetBar.ItemContainer itemContainer18; private DevComponents.DotNetBar.LabelItem labelItemSoundTime; private DevComponents.DotNetBar.ButtonItem buttonItemSoundPlay; private DevComponents.Editors.ComboItem comboItem3; private DevComponents.Editors.ComboItem comboItem4; private DevComponents.Editors.ComboItem comboItem5; private DevComponents.Editors.ComboItem comboItem6; private DevComponents.Editors.ComboItem comboItem7; private DevComponents.Editors.ComboItem comboItem8; private DevComponents.Editors.ComboItem comboItem9; private DevComponents.DotNetBar.TextBoxItem textBoxItemSearchWz; private DevComponents.DotNetBar.ButtonItem buttonItemCloseAll; private DevComponents.Editors.ComboItem comboItem10; private DevComponents.Editors.ComboItem comboItem11; private DevComponents.Editors.ComboItem comboItem12; private DevComponents.DotNetBar.ButtonItem buttonItemSelectStringWz; private DevComponents.DotNetBar.ItemContainer itemContainer8; private DevComponents.DotNetBar.LabelItem labelItem2; private DevComponents.DotNetBar.TextBoxItem textBoxItemSearchString; private DevComponents.DotNetBar.ButtonItem buttonItemClearStringWz; private System.Windows.Forms.ColumnHeader columnHeader6; private System.Windows.Forms.ColumnHeader columnHeader7; private System.Windows.Forms.ColumnHeader columnHeader8; private System.Windows.Forms.ColumnHeader columnHeader9; private DevComponents.DotNetBar.RibbonPanel ribbonPanel3; private DevComponents.DotNetBar.RibbonBar ribbonBar7; private DevComponents.DotNetBar.RibbonBar ribbonBar6; private DevComponents.DotNetBar.ButtonItem buttonItemAbout; private DevComponents.DotNetBar.RibbonTabItem ribbonTabItem3; private DevComponents.DotNetBar.RibbonBar ribbonBar8; private DevComponents.DotNetBar.ItemContainer itemContainer23; private DevComponents.DotNetBar.ItemContainer itemContainer24; private DevComponents.DotNetBar.ItemContainer itemContainer25; private DevComponents.DotNetBar.ButtonItem buttonItemQuickView; private DevComponents.DotNetBar.ButtonItem buttonItemLoadSound; private DevComponents.DotNetBar.ItemContainer itemContainer26; private DevComponents.DotNetBar.ButtonItem buttonItemSetItems; private DevComponents.DotNetBar.ButtonItem buttonItemClearSetItems; private DevComponents.DotNetBar.ItemContainer itemContainer27; private DevComponents.DotNetBar.ButtonItem buttonItemAutoSave; private DevComponents.DotNetBar.ButtonItem buttonItemAutoSaveFolder; private DevComponents.DotNetBar.LabelItem labelItemAutoSaveFolder; private DevComponents.DotNetBar.ItemContainer itemContainer28; private DevComponents.DotNetBar.ItemContainer itemContainer29; private DevComponents.DotNetBar.ButtonItem buttonItemCharItem; private DevComponents.DotNetBar.ItemContainer itemContainer30; private DevComponents.DotNetBar.ButtonItem buttonItemCharaStat; private DevComponents.DotNetBar.ItemContainer itemContainer31; private DevComponents.DotNetBar.ButtonItem buttonItemCharaEquip; private DevComponents.DotNetBar.ItemContainer itemContainer32; private DevComponents.DotNetBar.ItemContainer itemContainer33; private DevComponents.DotNetBar.ButtonItem buttonItemAddItem; private DevComponents.DotNetBar.ItemContainer itemContainer34; private DevComponents.DotNetBar.ItemContainer itemContainer35; private DevComponents.DotNetBar.ButtonItem buttonItemGif; private DevComponents.DotNetBar.ItemContainer itemContainer36; private DevComponents.DotNetBar.ButtonItem buttonItemExtractGifEx; private DevComponents.DotNetBar.ButtonItem buttonItemGifSetting; private System.Windows.Forms.ContextMenuStrip contextMenuStrip2; private System.Windows.Forms.ToolStripMenuItem tsmi2ExpandAll; private System.Windows.Forms.ToolStripMenuItem tsmi2CollapseAll; private System.Windows.Forms.ToolStripSeparator toolStripMenuItem1; private System.Windows.Forms.ToolStripMenuItem tsmi2ExpandLevel; private System.Windows.Forms.ToolStripMenuItem tsmi2CollapseLevel; private System.Windows.Forms.ToolStripSeparator toolStripMenuItem2; private System.Windows.Forms.ToolStripMenuItem tsmi2Prev; private System.Windows.Forms.ToolStripMenuItem tsmi2Next; private DevComponents.DotNetBar.ItemContainer itemContainer37; private DevComponents.DotNetBar.ItemContainer itemContainer38; private DevComponents.DotNetBar.ComboBoxItem comboBoxItemCharacter; private DevComponents.DotNetBar.ItemContainer itemContainer39; private DevComponents.DotNetBar.ItemContainer itemContainer40; private DevComponents.DotNetBar.ButtonItem buttonItemCreateChara; private DevComponents.DotNetBar.ButtonItem buttonItemEdit; private DevComponents.DotNetBar.RibbonBar ribbonBar9; private DevComponents.DotNetBar.ButtonItem buttonItemPatcher; private DevComponents.DotNetBar.ItemContainer itemContainer41; private DevComponents.DotNetBar.ButtonItem buttonItemLoadChara; private DevComponents.DotNetBar.ButtonItem buttonItemSaveChara; private DevComponents.DotNetBar.ExpandableSplitter expandableSplitter2; private DevComponents.DotNetBar.ButtonX btnEasyCompare; private DevComponents.DotNetBar.LabelX labelXComp1; private DevComponents.DotNetBar.LabelX labelXComp2; private System.Windows.Forms.ToolStripMenuItem tsmi1Export; private System.Windows.Forms.ToolStripSeparator toolStripMenuItem3; private DevComponents.DotNetBar.ComboBoxItem comboBoxItemLanguage; private DevComponents.Editors.ComboItem comboItem13; private DevComponents.Editors.ComboItem comboItem14; private DevComponents.Editors.ComboItem comboItem15; private DevComponents.Editors.ComboItem comboItem16; private DevComponents.Editors.ComboItem comboItem17; private DevComponents.DotNetBar.ItemContainer itemContainer42; private DevComponents.DotNetBar.ButtonItem buttonItemAutoQuickView; private DevComponents.DotNetBar.ButtonItem buttonItemQuickViewSetting; private DevComponents.DotNetBar.ItemContainer itemContainer43; private DevComponents.DotNetBar.ButtonX btnExportSkillOption; private DevComponents.DotNetBar.ButtonX btnExportSkill; private DevComponents.DotNetBar.LabelX labelX1; private DevComponents.DotNetBar.Controls.CheckBoxX chkOutputPng; private DevComponents.DotNetBar.Controls.ComboBoxEx cmbComparePng; private DevComponents.DotNetBar.Controls.CheckBoxX chkOutputRemovedImg; private DevComponents.DotNetBar.Controls.CheckBoxX chkOutputAddedImg; private DevComponents.DotNetBar.DotNetBarManager dotNetBarManager1; private DevComponents.DotNetBar.DockSite dockSite4; private DevComponents.DotNetBar.Bar bar1; private DevComponents.DotNetBar.PanelDockContainer panelDockContainer1; private DevComponents.DotNetBar.DockContainerItem dockContainerItem1; private DevComponents.DotNetBar.DockSite dockSite1; private DevComponents.DotNetBar.DockSite dockSite2; private DevComponents.DotNetBar.DockSite dockSite3; private DevComponents.DotNetBar.DockSite dockSite5; private DevComponents.DotNetBar.DockSite dockSite6; private DevComponents.DotNetBar.DockSite dockSite7; private DevComponents.DotNetBar.DockSite dockSite8; private DevComponents.DotNetBar.DockContainerItem dockContainerItem2; private DevComponents.DotNetBar.PanelDockContainer panelDockContainer2; private DevComponents.DotNetBar.RibbonBar ribbonBar11; private DevComponents.DotNetBar.ButtonItem buttonItem1; private DevComponents.DotNetBar.ButtonItem btnNodeBack; private DevComponents.DotNetBar.ButtonItem btnNodeForward; private System.Windows.Forms.ToolStripMenuItem tsmi2SaveAs; private System.Windows.Forms.ToolStripSeparator tsmi2Splitter1; private System.Windows.Forms.ToolStripMenuItem tsmi2HandleUol; private PictureBoxEx pictureBoxEx1; private DevComponents.DotNetBar.ComboBoxItem cmbItemAniNames; private DevComponents.DotNetBar.ButtonItem buttonItemUpdate; private System.Windows.Forms.ToolStripMenuItem tsmi1DumpAsXml; private DevComponents.Editors.ComboItem comboItem18; private DevComponents.DotNetBar.Controls.CheckBoxX chkResolvePngLink; private DevComponents.DotNetBar.ComboBoxItem cmbItemSkins; private DevComponents.DotNetBar.ButtonItem btnItemOpenImg; private DevComponents.DotNetBar.ButtonItem buttonItemSaveWithOptions; private DevComponents.DotNetBar.CheckBoxItem checkBoxItemRegex1; private DevComponents.DotNetBar.CheckBoxItem checkBoxItemRegex2; private DevComponents.DotNetBar.SuperTooltip superTooltip1; private DevComponents.DotNetBar.ButtonX btnCustomCSS; private DevComponents.DotNetBar.Controls.CheckBoxX chkHashPngFileName; private DevComponents.Editors.ComboItem comboItem19; private System.Windows.Forms.ToolStripSeparator toolStripMenuItem4; private System.Windows.Forms.ToolStripMenuItem tsmi2CopyFullPath; private DevComponents.DotNetBar.ColorPickerDropDown colorPickerPicBoxBgColor; } } ================================================ FILE: WzComparerR2/MainForm.cs ================================================ using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Diagnostics; using System.Drawing; using System.IO; using System.Linq; using System.Reflection; using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; using System.Xml; using static Microsoft.Xna.Framework.MathHelper; using Timer = System.Timers.Timer; using DevComponents.AdvTree; using DevComponents.AdvTree.Display; using DevComponents.DotNetBar; using DevComponents.DotNetBar.Controls; using WzComparerR2.Animation; using WzComparerR2.CharaSim; using WzComparerR2.CharaSimControl; using WzComparerR2.Common; using WzComparerR2.Comparer; using WzComparerR2.Config; using WzComparerR2.Controls; using WzComparerR2.Encoders; using WzComparerR2.PluginBase; using WzComparerR2.WzLib; namespace WzComparerR2 { public partial class MainForm : Office2007RibbonForm, PluginContextProvider { public MainForm() { InitializeComponent(); #if NET6_0_OR_GREATER // https://learn.microsoft.com/en-us/dotnet/core/compatibility/fx-core#controldefaultfont-changed-to-segoe-ui-9pt this.Font = new Font(new FontFamily("Microsoft Sans Serif"), 8f); #endif Form.CheckForIllegalCrossThreadCalls = false; this.MinimumSize = new Size(600, 450); advTree1.AfterNodeSelect += new AdvTreeNodeEventHandler(advTree1_AfterNodeSelect_2); advTree2.AfterNodeSelect += new AdvTreeNodeEventHandler(advTree2_AfterNodeSelect_2); //new ImageDragHandler(this.pictureBox1).AttachEvents(); RegisterPluginEvents(); createStyleItems(); initFields(); } List openedWz; StringLinker stringLinker; HistoryList historyNodeList; bool historySelecting; //soundPlayer BassSoundPlayer soundPlayer; Timer soundTimer; bool timerChangeValue; //charaSim AfrmTooltip tooltipQuickView; CharaSimControlGroup charaSimCtrl; AdvTree lastSelectedTree; DefaultLevel skillDefaultLevel = DefaultLevel.Level0; int skillInterval = 32; //compare Thread compareThread; private void initFields() { openedWz = new List(); stringLinker = new StringLinker(); historyNodeList = new HistoryList(); tooltipQuickView = new AfrmTooltip(); tooltipQuickView.Visible = false; tooltipQuickView.StringLinker = this.stringLinker; tooltipQuickView.KeyDown += new KeyEventHandler(afrm_KeyDown); tooltipQuickView.ShowID = true; tooltipQuickView.ShowMenu = true; charaSimCtrl = new CharaSimControlGroup(); charaSimCtrl.StringLinker = this.stringLinker; charaSimCtrl.Character = new Character(); charaSimCtrl.Character.Name = "Test"; string[] images = new string[] { "dir", "mp3", "num", "png", "str", "uol", "vector", "img", "rawdata", "convex", "video" }; foreach (string img in images) { imageList1.Images.Add(img, (Image)Properties.Resources.ResourceManager.GetObject(img)); } soundPlayer = new BassSoundPlayer(); if (!soundPlayer.Init()) { ManagedBass.Errors error = soundPlayer.GetLastError(); MessageBoxEx.Show("Bass初始化失败!\r\n\r\nerrorCode : " + (int)error + "(" + error + ")", "虫子"); } soundTimer = new Timer(120d); soundTimer.Elapsed += new System.Timers.ElapsedEventHandler(soundTimer_Elapsed); soundTimer.Enabled = true; PluginBase.PluginManager.WzFileFinding += new FindWzEventHandler(CharaSimLoader_WzFileFinding); foreach (WzPngComparison comp in Enum.GetValues(typeof(WzPngComparison))) { cmbComparePng.Items.Add(comp); } cmbComparePng.SelectedItem = WzPngComparison.SizeAndDataLength; } /// /// 插件加载时执行的方法,用于初始化配置文件。 /// internal void PluginOnLoad() { ConfigManager.RegisterAllSection(this.GetType().Assembly); var conf = ImageHandlerConfig.Default; //刷新最近打开文件列表 refreshRecentDocItems(); //读取CharaSim配置 UpdateCharaSimSettings(); //wz加载配置 UpdateWzLoadingSettings(); //杂项配置 labelItemAutoSaveFolder.Text = ImageHandlerConfig.Default.AutoSavePictureFolder; buttonItemAutoSave.Checked = ImageHandlerConfig.Default.AutoSaveEnabled; comboBoxItemLanguage.SelectedIndex = Clamp(CharaSimConfig.Default.SelectedFontIndex, 0, comboBoxItemLanguage.Items.Count); //更新界面颜色 styleManager1.ManagerStyle = WcR2Config.Default.MainStyle; UpdateButtonItemStyles(); styleManager1.ManagerColorTint = WcR2Config.Default.MainStyleColor; } void UpdateCharaSimSettings() { var Setting = CharaSimConfig.Default; this.buttonItemAutoQuickView.Checked = Setting.AutoQuickView; tooltipQuickView.SkillRender.ShowProperties = Setting.Skill.ShowProperties; tooltipQuickView.SkillRender.ShowObjectID = Setting.Skill.ShowID; tooltipQuickView.SkillRender.ShowDelay = Setting.Skill.ShowDelay; tooltipQuickView.SkillRender.DisplayCooltimeMSAsSec = Setting.Skill.DisplayCooltimeMSAsSec; tooltipQuickView.SkillRender.DisplayPermyriadAsPercent = Setting.Skill.DisplayPermyriadAsPercent; tooltipQuickView.SkillRender.IgnoreEvalError = Setting.Skill.IgnoreEvalError; this.skillDefaultLevel = Setting.Skill.DefaultLevel; this.skillInterval = Setting.Skill.IntervalLevel; tooltipQuickView.FamiliarRender.ShowObjectID = Setting.Gear.ShowID; tooltipQuickView.GearRender.ShowObjectID = Setting.Gear.ShowID; tooltipQuickView.GearRender.ShowSpeed = Setting.Gear.ShowWeaponSpeed; tooltipQuickView.GearRender.ShowLevelOrSealed = Setting.Gear.ShowLevelOrSealed; tooltipQuickView.GearRender.ShowMedalTag = Setting.Gear.ShowMedalTag; tooltipQuickView.ItemRender.ShowObjectID = Setting.Item.ShowID; tooltipQuickView.ItemRender.LinkRecipeInfo = Setting.Item.LinkRecipeInfo; tooltipQuickView.ItemRender.LinkRecipeItem = Setting.Item.LinkRecipeItem; tooltipQuickView.ItemRender.ShowNickTag = Setting.Item.ShowNickTag; tooltipQuickView.ItemRender.ShowDamageSkin = Setting.DamageSkin.ShowDamageSkin; tooltipQuickView.ItemRender.ShowDamageSkinID = Setting.DamageSkin.ShowDamageSkinID; tooltipQuickView.ItemRender.UseMiniSizeDamageSkin = Setting.DamageSkin.UseMiniSize; tooltipQuickView.ItemRender.AlwaysUseMseaFormatDamageSkin = Setting.DamageSkin.AlwaysUseMseaFormat; tooltipQuickView.ItemRender.DisplayUnitOnSingleLine = Setting.DamageSkin.DisplayUnitOnSingleLine; tooltipQuickView.ItemRender.DamageSkinNumber = Setting.DamageSkin.DamageSkinNumber; tooltipQuickView.RecipeRender.ShowObjectID = Setting.Recipe.ShowID; } void UpdateWzLoadingSettings() { var config = WcR2Config.Default; Encoding enc; try { enc = Encoding.GetEncoding(config.WzEncoding); } catch { enc = null; } Wz_Structure.DefaultEncoding = enc; Wz_Structure.DefaultAutoDetectExtFiles = config.AutoDetectExtFiles; Wz_Structure.DefaultImgCheckDisabled = config.ImgCheckDisabled; } async Task AutomaticCheckUpdate() { var config = WcR2Config.Default; if (config.EnableAutoUpdate) { var updater = new Updater(); try { await updater.QueryUpdateAsync(); if (updater.UpdateAvailable) { ToastNotification.Show(this, $"检查到更新版本{updater.LatestVersionString}", 5000, eToastPosition.TopCenter); var frmUpdater = new FrmUpdater(updater); frmUpdater.LoadConfig(config); frmUpdater.ShowDialog(this); } } catch { // ignore error } } } void CharaSimLoader_WzFileFinding(object sender, FindWzEventArgs e) { string[] fullPath = null; if (!string.IsNullOrEmpty(e.FullPath)) //用fullpath作为输入参数 { fullPath = e.FullPath.Split('/', '\\'); e.WzType = Enum.TryParse(fullPath[0], true, out var wzType) ? wzType : Wz_Type.Unknown; } List preSearch = new List(); if (e.WzType != Wz_Type.Unknown) //用wztype作为输入参数 { IEnumerable preSearchWz = e.WzFile?.WzStructure != null ? Enumerable.Repeat(e.WzFile.WzStructure, 1) : this.openedWz; foreach (var wzs in preSearchWz) { Wz_File baseWz = null; bool find = false; foreach (Wz_File wz_f in wzs.wz_files) { if (wz_f.Type == e.WzType) { preSearch.Add(wz_f.Node); find = true; //e.WzFile = wz_f; } if (wz_f.Type == Wz_Type.Base) { baseWz = wz_f; } } // detect data.wz if (baseWz != null && !find) { string key = e.WzType.ToString(); foreach (Wz_Node node in baseWz.Node.Nodes) { if (node.Text == key && node.Nodes.Count > 0) { preSearch.Add(node); } } } } } if (fullPath == null || fullPath.Length <= 1) { if (e.WzType != Wz_Type.Unknown && preSearch.Count > 0) //返回wzFile { e.WzNode = preSearch[0]; e.WzFile = preSearch[0].Value as Wz_File; } return; } if (preSearch.Count <= 0) { return; } foreach (var wzFileNode in preSearch) { var searchNode = wzFileNode; for (int i = 1; i < fullPath.Length && searchNode != null; i++) { searchNode = searchNode.Nodes[fullPath[i]]; var img = searchNode.GetValueEx(null); if (img != null) { searchNode = img.TryExtract() ? img.Node : null; } } if (searchNode != null) { e.WzNode = searchNode; e.WzFile = wzFileNode.Value as Wz_File; return; } } //寻找失败 e.WzNode = null; } #region 界面主题配置 private void createStyleItems() { //添加菜单 foreach (eStyle style in Enum.GetValues(typeof(eStyle)).OfType().Distinct()) { var buttonItemStyle = new ButtonItem() { Tag = style, Text = style.ToString(), Checked = (styleManager1.ManagerStyle == style) }; buttonItemStyle.Click += new EventHandler(buttonItemStyle_Click); this.buttonItemStyle.SubItems.Add(buttonItemStyle); } var styleColorPicker = new ColorPickerDropDown() { Text = "StyleColorTint", BeginGroup = true, SelectedColor = styleManager1.ManagerColorTint }; styleColorPicker.SelectedColorChanged += new EventHandler(styleColorPicker_SelectedColorChanged); buttonItemStyle.SubItems.Add(styleColorPicker); } private void buttonItemStyle_Click(object sender, EventArgs e) { var style = (eStyle)((sender as ButtonItem).Tag); styleManager1.ManagerStyle = style; UpdateButtonItemStyles(); ConfigManager.Reload(); WcR2Config.Default.MainStyle = style; ConfigManager.Save(); } private void UpdateButtonItemStyles() { foreach (BaseItem item in buttonItemStyle.SubItems) { ButtonItem buttonItem = item as ButtonItem; if (buttonItem != null) { buttonItem.Checked = (buttonItem.Tag as eStyle?) == styleManager1.ManagerStyle; } } } private void styleColorPicker_SelectedColorChanged(object sender, EventArgs e) { var color = (sender as ColorPickerDropDown).SelectedColor; styleManager1.ManagerColorTint = color; ConfigManager.Reload(); WcR2Config.Default.MainStyleColor = color; ConfigManager.Save(); } #endregion #region 读取wz相关方法 private Node createNode(Wz_Node wzNode) { if (wzNode == null) return null; Node parentNode = new Node(wzNode.Text) { Tag = new WeakReference(wzNode) }; foreach (Wz_Node subNode in wzNode.Nodes) { Node subTreeNode = createNode(subNode); if (subTreeNode != null) parentNode.Nodes.Add(subTreeNode); } return parentNode; } private void sortWzNode(Wz_Node wzNode) { this.sortWzNode(wzNode, WcR2Config.Default.SortWzByImgID); } private void sortWzNode(Wz_Node wzNode, bool sortByImgID) { if (wzNode.Nodes.Count > 1) { if (sortByImgID) { wzNode.Nodes.SortByImgID(); } else { wzNode.Nodes.Sort(); } } foreach (Wz_Node subNode in wzNode.Nodes) { sortWzNode(subNode, sortByImgID); } } #endregion #region wz提取右侧 private void cmbItemAniNames_SelectedIndexChanged(object sender, EventArgs e) { if (this.cmbItemAniNames.SelectedIndex > -1 && this.pictureBoxEx1.Items.Count > 0) { if (this.pictureBoxEx1.Items[0] is ISpineAnimator aniItem) { string aniName = this.cmbItemAniNames.SelectedItem as string; aniItem.SelectedAnimationName = aniName; this.cmbItemAniNames.Tooltip = aniName; } else if (this.pictureBoxEx1.Items[0] is FrameAnimator frameAni && this.cmbItemAniNames.SelectedItem is int selectedpage) { if (frameAni.Data.Frames.Count == 1) { var png = frameAni.Data.Frames[0].Png; if (png != null && png.ActualPages > 1 && 0 <= selectedpage && selectedpage < png.ActualPages) { this.pictureBoxEx1.ShowImage(png, selectedpage); } } } } } private void cmbItemSkins_SelectedIndexChanged(object sender, EventArgs e) { if (this.cmbItemSkins.SelectedIndex > -1 && this.pictureBoxEx1.Items.Count > 0) { if (this.pictureBoxEx1.Items[0] is ISpineAnimator aniItem) { string skinName = this.cmbItemSkins.SelectedItem as string; aniItem.SelectedSkin = skinName; this.cmbItemSkins.Tooltip = skinName; } } } private void buttonItemSaveImage_Click(object sender, EventArgs e) { this.OnSaveImage(false); } private void buttonItemSaveWithOptions_Click(object sender, EventArgs e) { this.OnSaveImage(true); } private Node handleUol(Node currentNode, string uolString) { if (currentNode == null || currentNode.Parent == null || string.IsNullOrEmpty(uolString)) return null; string[] dirs = uolString.Split('/'); currentNode = currentNode.Parent; for (int i = 0; i < dirs.Length; i++) { string dir = dirs[i]; if (dir == "..") { currentNode = currentNode.Parent; } else { bool find = false; foreach (Node child in currentNode.Nodes) { if (child.Text == dir) { currentNode = child; find = true; break; } } if (!find) currentNode = null; } if (currentNode == null) return null; } return currentNode; } private void labelItemAutoSaveFolder_Click(object sender, EventArgs e) { string dir = ImageHandlerConfig.Default.AutoSavePictureFolder; if (!string.IsNullOrEmpty(dir)) { System.Diagnostics.Process.Start("explorer.exe", dir); } } private void buttonItemGif_Click(object sender, EventArgs e) { if (advTree3.SelectedNode == null) return; Wz_Node node = advTree3.SelectedNode.AsWzNode(); string aniName = GetSelectedNodeImageName(); //添加到动画控件 var spineDetectResult = SpineLoader.Detect(node); if (spineDetectResult.Success) { var spineData = this.pictureBoxEx1.LoadSpineAnimation(spineDetectResult); if (spineData != null) { this.pictureBoxEx1.ShowAnimation(spineData); var aniItem = this.pictureBoxEx1.Items[0] as ISpineAnimator; this.cmbItemAniNames.Items.Clear(); this.cmbItemAniNames.Items.Add(""); this.cmbItemAniNames.Items.AddRange(aniItem.Animations.ToArray()); this.cmbItemAniNames.SelectedIndex = 0; this.cmbItemSkins.Visible = true; this.cmbItemSkins.Items.Clear(); this.cmbItemSkins.Items.AddRange(aniItem.Skins.ToArray()); this.cmbItemSkins.SelectedIndex = aniItem.Skins.IndexOf(aniItem.SelectedSkin); } } else { var options = (sender == this.buttonItemExtractGifEx) ? FrameAnimationCreatingOptions.ScanAllChildrenFrames: default; var frameData = this.pictureBoxEx1.LoadFrameAnimation(node, options); if (frameData != null) { this.pictureBoxEx1.ShowAnimation(frameData); this.cmbItemAniNames.Items.Clear(); this.cmbItemSkins.Visible = false; } } this.pictureBoxEx1.PictureName = aniName; } private string GetSelectedNodeImageName() { Wz_Node node = advTree3.SelectedNode.AsWzNode(); string aniName; switch (ImageHandlerConfig.Default.ImageNameMethod.Value) { default: case ImageNameMethod.Default: advTree3.PathSeparator = "."; aniName = advTree3.SelectedNode.FullPath; break; case ImageNameMethod.PathToImage: aniName = node.FullPath.Replace('\\', '.'); break; case ImageNameMethod.PathToWz: aniName = node.FullPathToFile.Replace('\\', '.'); break; } return aniName; } private void buttonItemGifSetting_Click(object sender, EventArgs e) { FrmGifSetting frm = new FrmGifSetting(); frm.Load(ImageHandlerConfig.Default); frm.FFmpegBinPathHint = FFmpegEncoder.DefaultExecutionFileName; frm.FFmpegArgumentHint = FFmpegEncoder.DefaultArgumentFormat; frm.FFmpegDefaultExtensionHint = FFmpegEncoder.DefaultOutputFileExtension; if (frm.ShowDialog() == DialogResult.OK) { ConfigManager.Reload(); frm.Save(ImageHandlerConfig.Default); ConfigManager.Save(); } } private void buttonItemAutoSave_Click(object sender, EventArgs e) { ConfigManager.Reload(); ImageHandlerConfig.Default.AutoSaveEnabled = buttonItemAutoSave.Checked; ConfigManager.Save(); } private void buttonItemAutoSaveFolder_Click(object sender, EventArgs e) { using (FolderBrowserDialog dlg = new FolderBrowserDialog()) { dlg.Description = "请选择自动保存图片的文件夹..."; dlg.SelectedPath = ImageHandlerConfig.Default.AutoSavePictureFolder; if (DialogResult.OK == dlg.ShowDialog()) { labelItemAutoSaveFolder.Text = dlg.SelectedPath; ConfigManager.Reload(); ImageHandlerConfig.Default.AutoSavePictureFolder = dlg.SelectedPath; ConfigManager.Save(); } } } private void OnSaveImage(bool options) { if (this.pictureBoxEx1.Items.Count <= 0) { return; } var aniItem = this.pictureBoxEx1.Items[0]; var frameData = (aniItem as FrameAnimator)?.Data; if (frameData != null && frameData.Frames.Count == 1 && frameData.Frames[0].A0 == 255 && frameData.Frames[0].A1 == 255 && frameData.Frames[0].Delay == 0) { // save still picture as png this.OnSavePngFile(frameData.Frames[0]); } else { // save as gif/apng this.OnSaveGifFile(aniItem, options); } } private void OnSavePngFile(Frame frame) { if (frame.Png != null) { var config = ImageHandlerConfig.Default; int page = frame.Page; string pngFileName = pictureBoxEx1.PictureName + (frame.Png.ActualPages > 1 ? $".{page}" : null) + ".png"; if (config.AutoSaveEnabled) { pngFileName = Path.Combine(config.AutoSavePictureFolder, string.Join("_", pngFileName.Split(Path.GetInvalidFileNameChars(), StringSplitOptions.None))); } else { var dlg = new SaveFileDialog(); dlg.Filter = "Png图片(*.png)|*.png|全部文件(*.*)|*.*"; dlg.FileName = pngFileName; if (dlg.ShowDialog() != DialogResult.OK) { return; } pngFileName = dlg.FileName; } using (var bmp = frame.Png.ExtractPng(page)) { bmp.Save(pngFileName, System.Drawing.Imaging.ImageFormat.Png); } labelItemStatus.Text = "图片保存于" + pngFileName; } else { labelItemStatus.Text = "没有文件被保存。"; } } private void OnSaveGifFile(AnimationItem aniItem, bool options) { var config = ImageHandlerConfig.Default; using var encoder = AnimateEncoderFactory.CreateEncoder(config); var cap = encoder.Compatibility; string aniName = this.cmbItemAniNames.SelectedItem as string; string aniFileName = pictureBoxEx1.PictureName + (string.IsNullOrEmpty(aniName) ? "" : ("." + aniName)) + cap.DefaultExtension; if (config.AutoSaveEnabled) { var fullFileName = Path.Combine(config.AutoSavePictureFolder, string.Join("_", aniFileName.Split(Path.GetInvalidFileNameChars(), StringSplitOptions.None))); int i = 1; while (File.Exists(fullFileName)) { fullFileName = Path.Combine(config.AutoSavePictureFolder, string.Format("{0}({1}){2}", Path.GetFileNameWithoutExtension(aniFileName), i, Path.GetExtension(aniFileName))); i++; } aniFileName = fullFileName; } else { var dlg = new SaveFileDialog(); string extensionFilter = string.Join(";", cap.SupportedExtensions.Select(ext => $"*{ext}")); dlg.Filter = string.Format("{0} Supported Files ({1})|{1}|All files (*.*)|*.*", encoder.Name, extensionFilter); dlg.FileName = aniFileName; if (dlg.ShowDialog() != DialogResult.OK) { return; } aniFileName = dlg.FileName; } var clonedAniItem = (AnimationItem)aniItem.Clone(); if (this.pictureBoxEx1.SaveAsGif(clonedAniItem, aniFileName, config, encoder, options)) { labelItemStatus.Text = "图片保存于" + aniFileName; } } #endregion #region File菜单的事件 private void btnItemOpenWz_Click(object sender, EventArgs e) { using (OpenFileDialog dlg = new OpenFileDialog()) { dlg.Title = "请选择冒险岛wz文件..."; dlg.Filter = "MapleStory Data File(Base.wz, *.wz, *.ms, *.mn)|*.wz;*.ms;*.mn"; if (dlg.ShowDialog() == DialogResult.OK) { openWz(dlg.FileName); } } } private void openWz(string wzFilePath) { foreach (Wz_Structure wzs in openedWz) { foreach (Wz_File wz_f in wzs.wz_files) { if (string.Compare(wz_f.Header.FileName, wzFilePath, true) == 0) { MessageBoxEx.Show("已经打开的wz。", "喵~"); return; } } } Wz_Structure wz = new Wz_Structure(); QueryPerformance.Start(); advTree1.BeginUpdate(); try { string[] msFileExtensions = { ".ms", ".mn" }; if (msFileExtensions.Any(ext => string.Equals(Path.GetExtension(wzFilePath), ext, StringComparison.OrdinalIgnoreCase))) { wz.LoadMsFile(wzFilePath); } else if (wz.IsKMST1125WzFormat(wzFilePath)) { wz.LoadKMST1125DataWz(wzFilePath); if (string.Equals(Path.GetFileName(wzFilePath), "Base.wz", StringComparison.OrdinalIgnoreCase)) { string packsDir = Path.Combine(Path.GetDirectoryName(Path.GetDirectoryName(wzFilePath)), "Packs"); if (Directory.Exists(packsDir)) { foreach (var ext in msFileExtensions) { foreach (var msFile in Directory.GetFiles(packsDir, $"*{ext}")) { wz.LoadMsFile(msFile); } } } } } else { wz.Load(wzFilePath, true); } if (WcR2Config.Default.SortWzOnOpened) { sortWzNode(wz.WzNode); } Node node = createNode(wz.WzNode); node.Expand(); advTree1.Nodes.Add(node); this.openedWz.Add(wz); OnWzOpened(new WzStructureEventArgs(wz)); //触发事件 QueryPerformance.End(); labelItemStatus.Text = "读取成功,用时" + (Math.Round(QueryPerformance.GetLastInterval(), 4) * 1000) + "ms,共读取" + wz.img_number + "img."; ConfigManager.Reload(); WcR2Config.Default.RecentDocuments.Remove(wzFilePath); WcR2Config.Default.RecentDocuments.Insert(0, wzFilePath); ConfigManager.Save(); refreshRecentDocItems(); } catch (FileNotFoundException) { MessageBoxEx.Show("文件没有找到", "嗯?"); } catch (Exception ex) { MessageBoxEx.Show(ex.ToString(), "嗯?"); wz.Clear(); } finally { advTree1.EndUpdate(); } } private void btnItemOpenImg_Click(object sender, EventArgs e) { using (OpenFileDialog dlg = new OpenFileDialog()) { dlg.Title = "请选择冒险岛img文件..."; dlg.Filter = "*.img|*.img|*.wz|*.wz"; if (dlg.ShowDialog() == DialogResult.OK) { openImg(dlg.FileName); } } } private void openImg(string imgFileName) { foreach (Wz_Structure wzs in openedWz) { foreach (Wz_File wz_f in wzs.wz_files) { if (StringComparer.OrdinalIgnoreCase.Equals(wz_f.Header.FileName, imgFileName)) { MessageBoxEx.Show("已经打开的wz。", "喵~"); return; } } } Wz_Structure wz = new Wz_Structure(); var sw = Stopwatch.StartNew(); advTree1.BeginUpdate(); try { wz.LoadImg(imgFileName); Node node = createNode(wz.WzNode); node.Expand(); advTree1.Nodes.Add(node); this.openedWz.Add(wz); OnWzOpened(new WzStructureEventArgs(wz)); //触发事件 sw.Stop(); labelItemStatus.Text = $"读取成功,用时{sw.ElapsedMilliseconds}ms."; refreshRecentDocItems(); } catch (FileNotFoundException) { MessageBoxEx.Show("文件没有找到", "嗯?"); } catch (Exception ex) { MessageBoxEx.Show(ex.ToString(), "嗯?"); wz.Clear(); } finally { advTree1.EndUpdate(); } } private void buttonItemClose_Click(object sender, EventArgs e) { if (advTree1.SelectedNode == null) { MessageBoxEx.Show("没有选中要关闭的wz.", "喵~"); return; } Node baseWzNode = advTree1.SelectedNode; while (baseWzNode.Parent != null) baseWzNode = baseWzNode.Parent; if (baseWzNode.Text.ToLower() == "list.wz") { advTree1.Nodes.Remove(baseWzNode); labelItemStatus.Text = "已经关闭list.wz..."; return; } Wz_File wz_f = advTree1.SelectedNode.AsWzNode()?.GetNodeWzFile(); if (wz_f == null) { MessageBoxEx.Show("没有正确选择要关闭的wz。", "喵~"); return; } Wz_Structure wz = wz_f.WzStructure; advTree1.Nodes.Remove(baseWzNode); listViewExWzDetail.Items.Clear(); Wz_Image image = null; if (advTree2.Nodes.Count > 0 && (image = advTree2.Nodes[0].AsWzNode()?.GetValue()) != null && image.WzFile.WzStructure == wz) { advTree2.Nodes.Clear(); } if (advTree3.Nodes.Count > 0 && (image = advTree3.Nodes[0].AsWzNode()?.GetNodeWzImage()) != null && image.WzFile.WzStructure == wz) { advTree3.Nodes.Clear(); } OnWzClosing(new WzStructureEventArgs(wz)); wz.Clear(); if (this.openedWz.Remove(wz)) labelItemStatus.Text = "已经关闭所选wz..."; else labelItemStatus.Text = "wz已经关闭,但是发生了诡异的错误..."; } private void buttonItemCloseAll_Click(object sender, EventArgs e) { advTree1.ClearAndDisposeAllNodes(); advTree1.ClearLayoutCellInfo(); advTree2.ClearAndDisposeAllNodes(); advTree2.ClearLayoutCellInfo(); advTree3.ClearAndDisposeAllNodes(); advTree3.ClearLayoutCellInfo(); foreach (Wz_Structure wz in openedWz) { OnWzClosing(new WzStructureEventArgs(wz)); wz.Clear(); } openedWz.Clear(); CharaSimLoader.ClearAll(); stringLinker.Clear(); labelItemStatus.Text = "已经清理全部已读取资源..."; GC.Collect(); } private void refreshRecentDocItems() { List items = new List(); foreach (BaseItem item in galleryContainerRecent.SubItems) { if (item is ButtonItem) { items.Add(item); } } galleryContainerRecent.SubItems.RemoveRange(items.ToArray()); items.Clear(); foreach (var doc in WcR2Config.Default.RecentDocuments) { ButtonItem item = new ButtonItem() { Text = "&" + (items.Count + 1) + ". " + Path.GetFileName(doc), Tooltip = doc, Tag = doc }; item.Click += new EventHandler(buttonItemRecentDocument_Click); items.Add(item); } galleryContainerRecent.SubItems.AddRange(items.ToArray()); } void buttonItemRecentDocument_Click(object sender, EventArgs e) { ButtonItem btnItem = sender as ButtonItem; string path; if (btnItem == null || (path = btnItem.Tag as string) == null) return; openWz(path); } #endregion #region wzView和提取的事件和方法 private void advTree1_DragEnter(object sender, DragEventArgs e) { string[] types = e.Data.GetFormats(); if (e.Data.GetDataPresent(DataFormats.FileDrop)) { string[] files = (string[])e.Data.GetData(DataFormats.FileDrop); foreach (string file in files) { if (Path.GetExtension(file) != ".wz") { e.Effect = DragDropEffects.None; return; } } e.Effect = DragDropEffects.Move; } else { e.Effect = DragDropEffects.None; } } private void advTree1_DragDrop(object sender, DragEventArgs e) { if (e.Data.GetDataPresent(DataFormats.FileDrop)) { string[] files = (string[])e.Data.GetData(DataFormats.FileDrop); foreach (string file in files) { openWz(file); } } } private void advTree1_AfterNodeSelect(object sender, AdvTreeNodeEventArgs e) { Wz_Node selectedNode = e.Node.AsWzNode(); if (selectedNode == null) { return; } listViewExWzDetail.BeginUpdate(); listViewExWzDetail.Items.Clear(); if (selectedNode.Value == null) { listViewExWzDetail.Items.Add(new ListViewItem(new string[] { "Dir Name", Path.GetFileName(e.Node.Text) })); autoResizeColumns(listViewExWzDetail); } else if (selectedNode.Value is Wz_File wzFile) { listViewExWzDetail.Items.Add(new ListViewItem(new string[] { "File Name", wzFile.Header.FileName })); listViewExWzDetail.Items.Add(new ListViewItem(new string[] { "File Size", wzFile.Header.FileSize + " bytes" })); listViewExWzDetail.Items.Add(new ListViewItem(new string[] { "Copyright", wzFile.Header.Copyright })); listViewExWzDetail.Items.Add(new ListViewItem(new string[] { "Version", wzFile.GetMergedVersion().ToString() })); listViewExWzDetail.Items.Add(new ListViewItem(new string[] { "Wz Type", wzFile.IsSubDir ? "SubDir" : wzFile.Type.ToString() })); foreach (Wz_File subFile in wzFile.MergedWzFiles) { listViewExWzDetail.Items.Add(" "); listViewExWzDetail.Items.Add(new ListViewItem(new string[] { "File Name", subFile.Header.FileName })); listViewExWzDetail.Items.Add(new ListViewItem(new string[] { "File Size", subFile.Header.FileSize + " bytes" })); listViewExWzDetail.Items.Add(new ListViewItem(new string[] { "Copyright", subFile.Header.Copyright })); listViewExWzDetail.Items.Add(new ListViewItem(new string[] { "Version", subFile.Header.WzVersion.ToString() })); } autoResizeColumns(listViewExWzDetail); } else if (selectedNode.Value is Wz_Image wzImage) { listViewExWzDetail.Items.Add(new ListViewItem(new string[] { "Image Name", wzImage.Name })); listViewExWzDetail.Items.Add(new ListViewItem(new string[] { "Image Size", wzImage.Size + " bytes" })); listViewExWzDetail.Items.Add(new ListViewItem(new string[] { "Image Offset", wzImage.Offset + " bytes" })); listViewExWzDetail.Items.Add(new ListViewItem(new string[] { "Path", wzImage.Node.FullPathToFile })); listViewExWzDetail.Items.Add(new ListViewItem(new string[] { "Check Sum", wzImage.Checksum.ToString() })); autoResizeColumns(listViewExWzDetail); advTree2.ClearAndDisposeAllNodes(); //advTree2.Nodes.Clear(); QueryPerformance.Start(); try { Exception ex; if (wzImage.TryExtract(out ex)) { advTree2.Nodes.Add(createNode(wzImage.Node)); advTree2.Nodes[0].Expand(); QueryPerformance.End(); double ms = (Math.Round(QueryPerformance.GetLastInterval(), 4) * 1000); labelItemStatus.Text = "读取image成功~用时" + ms + "ms..."; } else { labelItemStatus.Text = "读取image失败..." + ex.Message; } } catch (Exception ex) { labelItemStatus.Text = "读取image失败:" + ex.Message; } } listViewExWzDetail.EndUpdate(); } private void autoResizeColumns(ListViewEx listView) { listView.AutoResizeColumns(ColumnHeaderAutoResizeStyle.ColumnContent); foreach (System.Windows.Forms.ColumnHeader column in listView.Columns) { column.Width += (int)(listView.Font.Size * 2); } } private void advTree2_NodeDoubleClick(object sender, TreeNodeMouseEventArgs e) { if (e.Node == null || e.Button != MouseButtons.Left) return; historyNodeList.Clear(); advTree3.Nodes.Clear(); var selectedNode = e.Node.AsWzNode(); if (selectedNode != null) { advTree3.BeginUpdate(); try { var node = createNodeDetail(e.Node); node.ExpandAll(); advTree3.Nodes.Add(node); advTree3.SelectedNode = node; } finally { advTree3.EndUpdate(); } } } private Node createNodeDetail(Node parentNode) { Node newNode = new Node(parentNode.Text); newNode.Tag = parentNode.Tag; Wz_Node wzNode = newNode.AsWzNode(); if (wzNode != null) { newNode.Cells.Add(new Cell(wzNode.Value == null ? "<" + parentNode.Nodes.Count + ">" : getValueString(wzNode.Value))); newNode.Cells.Add(new Cell(wzNode.Value == null ? null : wzNode.Value.GetType().Name)); newNode.ImageKey = wzNode.Value == null ? "dir" : (getValueImageKey(wzNode.Value) ?? "num"); } foreach (Node subNode in parentNode.Nodes) { newNode.Nodes.Add(createNodeDetail(subNode)); } return newNode; } private string getValueString(object value) { switch (value) { case Wz_Png png: return $"png {png.Width}*{png.Height} ({(int)png.Format}{(png.Scale > 0 ? $", {png.Scale}" : null)})"; case Wz_Vector vector: return $"({vector.X}, {vector.Y})"; case Wz_Uol uol: return uol.Uol; case Wz_Sound sound: return $"sound {sound.Ms}ms"; case Wz_Image img: return $"<{img.Node.Nodes.Count}>"; case Wz_RawData rawData: return $"rawdata {rawData.Length}"; case Wz_Convex convex: return $"convex [{convex.Points.Length}]"; case Wz_Video video: return $"video {video.Length}"; default: string cellVal = Convert.ToString(value); if (cellVal != null && cellVal.Length > 50) { cellVal = cellVal.Substring(0, 50); } return cellVal; } } private string getValueImageKey(object value) { return value switch { string => "str", short or int or long or float or double=> "num", Wz_Png => "png", Wz_Vector => "vector", Wz_Uol => "uol", Wz_Sound sound => sound.SoundType == Wz_SoundType.Binary ? "rawdata" : "mp3", Wz_Image => "img", Wz_RawData => "rawdata", Wz_Convex => "convex", Wz_Video => "video", _ => null }; } private void advTree3_AfterNodeSelect(object sender, AdvTreeNodeEventArgs e) { if (e.Node == null) return; if (!historySelecting && (historyNodeList.Count == 0 || e.Node != historyNodeList.Current)) { historyNodeList.Add(e.Node); } else { historySelecting = false; } Wz_Node selectedNode = e.Node.AsWzNode(); if (selectedNode == null) return; switch (selectedNode.Value) { case Wz_Png png: pictureBoxEx1.PictureName = GetSelectedNodeImageName(); pictureBoxEx1.ShowImage(png); this.cmbItemAniNames.Items.Clear(); if (png.ActualPages > 1) { for (int i = 0; i < png.ActualPages; i++) this.cmbItemAniNames.Items.Add(i); } advTree3.PathSeparator = "."; textBoxX1.Text = "dataLength: " + png.DataLength + " bytes\r\n" + "offset: " + png.Offset + "\r\n" + "size: " + png.Width + "*" + png.Height + "\r\n" + "png format: " + png.Format + "(" + (int)png.Format + ")\r\n" + "scale: " + png.Scale + "(x" + png.ActualScale + ")\r\n" + "pages: " + png.Pages + "(" + png.ActualPages + ")\r\n" + "unknown1: " + png.Unknown1; break; case Wz_Vector vector: textBoxX1.Text = "x: " + vector.X + " px\r\n" + "y: " + vector.Y + " px"; break; case Wz_Convex convex: var sb = new StringBuilder(); for (int i = 0; i < convex.Points.Length; i++) { if (i > 0) sb.AppendLine(); sb.AppendFormat("({0}, {1})", convex.Points[i].X, convex.Points[i].Y); } textBoxX1.Text = sb.ToString(); break; case Wz_Uol uol: textBoxX1.Text = "uolPath: " + uol.Uol; break; case Wz_Sound sound: preLoadSound(sound, selectedNode.Text); textBoxX1.Text = "dataLength: " + sound.DataLength + " bytes\r\n" + "offset: " + sound.Offset + "\r\n" + "duration: " + sound.Ms + " ms\r\n" + "channels: " + sound.Channels + "\r\n" + "freq: " + sound.Frequency + " Hz\r\n" + "type: " + sound.SoundType.ToString(); break; case Wz_Image: //do nothing; break; case Wz_RawData rawData: textBoxX1.Text = "dataLength: " + rawData.Length + " bytes\r\n" + "offset: " + rawData.Offset; break; case Wz_Video video: textBoxX1.Text = "dataLength: " + video.Length + " bytes\r\n" + "offset: " + video.Offset; var videoFrameData = this.pictureBoxEx1.LoadVideo(video); pictureBoxEx1.PictureName = GetSelectedNodeImageName(); this.pictureBoxEx1.ShowAnimation(videoFrameData); this.cmbItemAniNames.Items.Clear(); break; default: string valueStr = Convert.ToString(selectedNode.Value); if (valueStr != null && valueStr.Contains("\n") && !valueStr.Contains("\r\n")) { valueStr = valueStr.Replace("\n", "\r\n"); } textBoxX1.Text = Convert.ToString(valueStr); switch (selectedNode.Text) { case "source": case "_inlink": case "_outlink": { var parentNode = selectedNode.ParentNode; if (parentNode != null && parentNode.Value is Wz_Png) { var linkNode = parentNode.GetLinkedSourceNode(PluginManager.FindWz); var png = linkNode.GetValueEx(null); if (png != null) { pictureBoxEx1.PictureName = GetSelectedNodeImageName(); pictureBoxEx1.ShowImage(png); this.cmbItemAniNames.Items.Clear(); advTree3.PathSeparator = "."; textBoxX1.AppendText("\r\n\r\ndataLength: " + png.DataLength + " bytes\r\n" + "offset: " + png.Offset + "\r\n" + "size: " + png.Width + "*" + png.Height + "\r\n" + "png format: " + png.Format + "(" + (int)png.Format + ")\r\n" + "scale: " + png.Scale + "(x" + png.ActualScale + ")\r\n" + "pages: " + png.Pages + "(" + png.ActualPages + ")"); } } } break; } break; } } private void pictureBox1_MouseDoubleClick(object sender, MouseEventArgs e) { /* if (pictureBox1.Image != null && e.Button == MouseButtons.Left) { string tempFile = Path.Combine(Path.GetTempPath(), Convert.ToString(pictureBox1.Tag)); switch (Path.GetExtension(tempFile)) { case ".png": pictureBox1.Image.Save(tempFile, System.Drawing.Imaging.ImageFormat.Png); System.Diagnostics.Process.Start(tempFile); break; case ".gif": pictureBox1.Image.Save(tempFile, System.Drawing.Imaging.ImageFormat.Gif); System.Diagnostics.Process.Start(tempFile); break; default: MessageBoxEx.Show("不识别的文件名:" + tempFile, "喵~"); break; } }*/ } private void listViewExString_MouseDoubleClick(object sender, MouseEventArgs e) { if (e.Button == MouseButtons.Left) { this.listViewExStringFind(); } } private void listViewExString_KeyDown(object sender, KeyEventArgs e) { if (e.KeyCode == Keys.Enter) { this.listViewExStringFind(); } else if (e.KeyCode == Keys.C && e.Control) { this.listViewExStringCopy(); } } private void listViewExStringFind() { if (listViewExString.SelectedItems.Count == 0 || advTree1.Nodes.Count == 0) { return; } string id = listViewExString.SelectedItems[0].Text; string nodePath = listViewExString.SelectedItems[0].SubItems[3].Text; List objPathList = detectObjPathByStringPath(id, nodePath); //分离wz路径和img路径 foreach (string[] fullPath in objPathList) { //寻找所有可能的wzfile List allWzFile = new List(); Wz_Type wzType = ParseType(fullPath[0]); foreach (var wzs in this.openedWz) { foreach (var wzf in wzs.wz_files) { if (wzf.Type == wzType && wzf.OwnerWzFile == null) { allWzFile.Add(wzf.Node); } } } //开始搜索 foreach (var wzFileNode in allWzFile) { Wz_Node node = SearchNode(wzFileNode, fullPath, 1); if (node != null) { OnSelectedWzNode(node); //遇到第一个 选中 返回 return; } } } //失败 string path; if (objPathList.Count == 1) { path = string.Join("\\", objPathList[0]); } else { path = "(" + objPathList.Count + ")个节点"; } labelItemStatus.Text = "无法找到imageNode: " + path; } private Wz_Node SearchNode(Wz_Node parent, string[] path, int startIndex) { if (startIndex >= path.Length) { return null; } if (parent.Value is Wz_Image) { Wz_Image img = parent.GetValue(); if (!img.TryExtract()) { return null; } parent = img.Node; } string nodeName = path[startIndex]; if (!string.IsNullOrEmpty(nodeName)) { Wz_Node child = parent.FindNodeByPath(false, true, nodeName); if (child != null) { return (startIndex == path.Length - 1) ? child : SearchNode(child, path, startIndex + 1); } } else //遍历全部 { foreach (Wz_Node child in parent.Nodes) { if (child.Nodes.Count == 0) //只过滤文件夹 未来有需求再改 { continue; } Wz_Node find = SearchNode(child, path, startIndex + 1); if (find != null) { return (startIndex == path.Length - 1) ? null : find; } } } return null; } private bool OnSelectedWzNode(Wz_Node wzNode) { Wz_File wzFile = wzNode.GetNodeWzFile(); string[] path = wzNode.FullPathToFile.Split('\\'); if (wzFile == null) { return false; } Node treeNode = findWzFileTreeNode(wzFile); if (treeNode == null) { return false; } for (int i = 1; i < path.Length; i++) { Node find = null; foreach (Node child in treeNode.Nodes) { if (child.Text == path[i]) { find = child; break; } } if (find == null) { return false; } if (find.AsWzNode()?.Value is Wz_Image) { advTree1.SelectedNode = find; if (advTree2.Nodes.Count > 0) { treeNode = advTree2.Nodes[0]; } else { return false; } } else { treeNode = find; } } advTree2.SelectedNode = treeNode; return true; } private void listViewExStringCopy() { if (listViewExString.SelectedItems.Count == 0 || advTree1.Nodes.Count == 0) { return; } StringBuilder sb = new StringBuilder(); foreach (ListViewItem.ListViewSubItem item in listViewExString.SelectedItems[0].SubItems) { sb.Append(item.Text).Append(" "); } sb.Remove(sb.Length - 1, 1); Clipboard.SetText(sb.ToString(), TextDataFormat.UnicodeText); labelItemStatus.Text = "已复制string条目到剪切板。"; } private List detectObjPathByStringPath(string id, string stringNodePath) { List pathList = new List(); List wzPath = new List(); List imagePath = new List(); Action addPath = () => { List fullPath = new List(wzPath.Count + imagePath.Count); fullPath.AddRange(wzPath); fullPath.AddRange(imagePath); pathList.Add(fullPath.ToArray()); }; string[] pathArray = stringNodePath.Split('\\'); switch (pathArray[0]) { case "Cash.img": case "Consume.img": case "Etc.img": case "Pet.img": wzPath.Add("Item"); wzPath.Add(pathArray[0].Substring(0, pathArray[0].IndexOf(".img"))); if (pathArray[0] == "Pet.img") { wzPath.Add(id.TrimStart('0') + ".img"); } else { id = id.PadLeft(8, '0'); wzPath.Add(id.Substring(0, 4) + ".img"); imagePath.Add(id); } addPath(); break; case "Ins.img": //KMST1066 wzPath.Add("Item"); wzPath.Add("Install"); wzPath.Add(""); id = id.PadLeft(8, '0'); imagePath.Add(id); for (int len = 4; len <= 6; len++) { wzPath[2] = id.Substring(0, len) + ".img"; addPath(); } break; case "Eqp.img": wzPath.Add("Character"); if (pathArray[2] == "Taming") { wzPath.Add("TamingMob"); } else { wzPath.Add(pathArray[2]); } wzPath.Add(id.PadLeft(8, '0') + ".img"); addPath(); //往往这个不靠谱。。 加一个任意门备用 wzPath[1] = ""; addPath(); break; case "Map.img": id = id.PadLeft(9, '0'); wzPath.AddRange(new string[] { "Map", "Map", "Map" + id[0], id + ".img" }); addPath(); break; case "Mob.img": wzPath.Add("Mob"); wzPath.Add(id.PadLeft(7, '0') + ".img"); addPath(); break; case "Npc.img": wzPath.Add("Npc"); wzPath.Add(id.PadLeft(7, '0') + ".img"); addPath(); break; case "Skill.img": id = id.PadLeft(7, '0'); wzPath.Add("Skill"); //old skill wzPath.Add(id.Substring(0, id.Length - 4) + ".img"); imagePath.Add("skill"); imagePath.Add(id); addPath(); if (Regex.IsMatch(id, @"80\d{6}")) //kmst new skill { wzPath[1] = id.Substring(0, 6) + ".img"; addPath(); } break; default: break; } return pathList; } /// /// 通过给定的wz名称,在advTree1中寻找第一个对应的wz_file节点。 /// /// 要寻找的wz名称,不包含".wz"后缀。 /// private Node findWzFileTreeNode(string wzName) { Wz_Type type = ParseType(wzName); if (type == Wz_Type.Unknown) { return null; } foreach (var wzs in this.openedWz) { foreach (var wzf in wzs.wz_files) { if (wzf.Type == type) { Node node = findWzFileTreeNode(wzf); if (node != null) { return node; } } } } return null; } private Wz_Type ParseType(string wzName) { Wz_Type type; try { type = (Wz_Type)Enum.Parse(typeof(Wz_Type), wzName, true); } catch { type = Wz_Type.Unknown; } return type; } private Node findWzFileTreeNode(Wz_File wzFile) { foreach (Node baseNode in advTree1.Nodes) { Wz_File wz_f = baseNode.AsWzNode()?.Value as Wz_File; if (wz_f != null) { if (wz_f == wzFile) { return baseNode; } else if (wz_f.Type == Wz_Type.Base) { foreach (Node wzNode in baseNode.Nodes) { if ((wz_f = wzNode.AsWzNode()?.Value as Wz_File) != null && wz_f == wzFile) { return wzNode; } } } } } return null; } private Node findChildTreeNode(Node parent, string[] path) { if (parent == null || path == null) return null; for (int i = 0; i < path.Length; i++) { bool find = false; foreach (Node subNode in parent.Nodes) { if (subNode.Text == path[i]) { parent = subNode; find = true; break; } } if (!find) { return null; } } return parent; } #endregion #region contextMenuStrip1 private void tsmi1Sort_Click(object sender, EventArgs e) { if (openedWz.Count > 0) { var sw = Stopwatch.StartNew(); advTree1.BeginUpdate(); try { advTree1.ClearAndDisposeAllNodes(); foreach (Wz_Structure wz in openedWz) { sortWzNode(wz.WzNode); Node node = createNode(wz.WzNode); node.Expand(); advTree1.Nodes.Add(node); } } finally { advTree1.EndUpdate(); sw.Stop(); } GC.Collect(); labelItemStatus.Text = $"排序成功,用时{sw.ElapsedMilliseconds}ms."; } else { labelItemStatus.Text = "没有打开的wz"; } } private void tsmi1Export_Click(object sender, EventArgs e) { Wz_Image img = advTree1.SelectedNode?.AsWzNode()?.GetValue(); if (img == null) { MessageBoxEx.Show("没有选中一个用于导出的wz_img。"); return; } SaveFileDialog dlg = new SaveFileDialog(); dlg.DefaultExt = ".img"; dlg.FileName = img.Name; dlg.Filter = "*.img|*.img"; if (dlg.ShowDialog() == DialogResult.OK) { FileStream fs = null; try { fs = new FileStream(dlg.FileName, FileMode.Create, FileAccess.Write); var s = img.OpenRead(); s.Position = 0; s.CopyTo(fs); fs.Close(); labelItemStatus.Text = img.Name + "导出完毕。"; } catch (Exception ex) { fs?.Close(); MessageBoxEx.Show(ex.ToString(), "错了"); } } } private void tsmi1DumpAsXml_Click(object sender, EventArgs e) { Wz_Image img = advTree1.SelectedNode?.AsWzNode()?.GetValue(); if (img == null) { MessageBoxEx.Show("没有选中一个用于导出的wz_img。"); return; } SaveFileDialog dlg = new SaveFileDialog(); dlg.DefaultExt = ".xml"; dlg.Filter = "*.xml|*.xml"; dlg.FileName = img.Node.FullPathToFile.Replace('\\', '.') + ".xml"; if (dlg.ShowDialog() == DialogResult.OK) { FileStream fs = null; try { fs = new FileStream(dlg.FileName, FileMode.Create, FileAccess.Write); var xsetting = new XmlWriterSettings() { CloseOutput = false, Indent = true, Encoding = Encoding.UTF8, CheckCharacters = true, NewLineChars = Environment.NewLine, NewLineOnAttributes = false, }; var writer = XmlWriter.Create(fs, xsetting); writer.WriteStartDocument(true); img.Node.DumpAsXml(writer); writer.WriteEndDocument(); writer.Close(); labelItemStatus.Text = img.Name + "导出完毕。"; } catch (Exception ex) { MessageBoxEx.Show(ex.ToString(), "错了"); } finally { if (fs != null) { fs.Close(); } } } } #endregion #region Tools菜单事件和方法 private void buttonItemSearchWz_Click(object sender, EventArgs e) { if (string.IsNullOrEmpty(textBoxItemSearchWz.Text)) return; if (comboBoxItem1.SelectedIndex == -1) { comboBoxItem1.SelectedIndex = 0; } switch (comboBoxItem1.SelectedIndex) { case 0: searchAdvTree(advTree1, 0, textBoxItemSearchWz.Text, checkBoxItemExact1.Checked, checkBoxItemRegex1.Checked); break; case 1: searchAdvTree(advTree2, 0, textBoxItemSearchWz.Text, checkBoxItemExact1.Checked, checkBoxItemRegex1.Checked); break; case 2: searchAdvTree(advTree3, 1, textBoxItemSearchWz.Text, checkBoxItemExact1.Checked, checkBoxItemRegex1.Checked); break; case 3: //full path searchAdvTreeFullPath(textBoxItemSearchWz.Text); break; } } private void searchAdvTree(AdvTree advTree, int cellIndex, string searchText, bool exact, bool regex) { if (string.IsNullOrEmpty(searchText)) return; try { Node searchNode = searchAdvTree(advTree, cellIndex, searchText, exact, regex, true); advTree.SelectedNode = searchNode; if (searchNode == null) MessageBoxEx.Show("已经搜索到末尾。", "喵呜~"); } catch (Exception ex) { MessageBoxEx.Show(this, ex.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); } } private Node searchAdvTree(AdvTree advTree, int cellIndex, string searchText, bool exact, bool isRegex, bool ignoreCase) { if (advTree.Nodes.Count == 0) return null; if (isRegex) { Regex r = new Regex(searchText, ignoreCase ? RegexOptions.IgnoreCase : RegexOptions.None); foreach (var node in findNextNode(advTree)) { if (node != null && node.Cells.Count > cellIndex && r.IsMatch(node.Cells[cellIndex].Text)) { return node; } } } else { string[] pattern = searchText.Split('\\'); foreach (var node in findNextNode(advTree)) { if (checkSearchNodeText(node, cellIndex, pattern, exact, ignoreCase)) { return node; } } } return null; } private void searchAdvTreeFullPath(string fullPath) { string[] pathSegments = fullPath.Split('/'); bool isNodePathMatches(string pathSegment, string nodeName, StringComparison stringComparison) { if (string.Equals(pathSegment, nodeName, stringComparison)) { return true; } int pathExtIndex = pathSegment.LastIndexOf('.'); int nodeExtIndex = nodeName.LastIndexOf("."); if (pathExtIndex != -1 || nodeExtIndex != -1) { ReadOnlySpan pathWithoutExt = pathExtIndex == -1 ? pathSegment.AsSpan() : pathSegment.AsSpan(0, pathExtIndex); ReadOnlySpan nodeWithoutExt = nodeExtIndex == -1 ? nodeName.AsSpan() : nodeName.AsSpan(0, nodeExtIndex); return pathWithoutExt.Equals(nodeWithoutExt, stringComparison); } return false; } IEnumerable<(Node result, int resultPathLevel)> searchNode(Node treeNode, int pathLevel) { string pathSegment = pathSegments[pathLevel]; if (isNodePathMatches(pathSegment, treeNode.Text, StringComparison.OrdinalIgnoreCase)) { if (treeNode.Nodes.Count == 0 || pathLevel == pathSegments.Length - 1) { yield return (treeNode, pathLevel); } foreach (Node childNode in treeNode.Nodes) { foreach (var resultTuple in searchNode(childNode, pathLevel + 1)) { yield return resultTuple; } } } } foreach (var node in findNextNode(this.advTree1)) { foreach ((Node result, int resultPathLevel) in searchNode(node, 0)) { if (resultPathLevel == pathSegments.Length - 1) { this.advTree1.SelectedNode = node; return; } var wzNode = result.AsWzNode(); if (wzNode != null && wzNode.Value is Wz_Image wzImg && wzImg.TryExtract(out _)) { // find remaining path in wzImg wzNode = wzImg.Node; for (int i = resultPathLevel + 1; i < pathSegments.Length; i++) { string pathSegment = pathSegments[i]; wzNode = wzNode.Nodes.FirstOrDefault(child => isNodePathMatches(pathSegment, child.Text, StringComparison.OrdinalIgnoreCase)); if (wzNode == null) { break; } } if (wzNode != null && this.OnSelectedWzNode(wzNode)) { return; } } } } this.advTree1.SelectedNode = null; MessageBoxEx.Show(this, "已经搜索到末尾。"); } private IEnumerable findNextNode(AdvTree advTree) { var node = advTree.SelectedNode; if (node == null) { node = advTree.Nodes[0]; yield return node; } var levelStack = new Stack(); int index = node.Index + 1; while (true) { if (node.Nodes.Count > 0) { levelStack.Push(index); index = 0; yield return node = node.Nodes[index++]; continue; } NodeCollection owner; while (index >= (owner = (node.Parent?.Nodes ?? advTree.Nodes)).Count) { node = node.Parent; if (node == null) { yield break; } if (levelStack.Count > 0) { index = levelStack.Pop(); } else { index = node.Index + 1; } } yield return node = owner[index++]; } } private bool checkSearchNodeText(Node node, int cellIndex, string[] searchTextArray, bool exact, bool ignoreCase) { if (node == null || searchTextArray == null || searchTextArray.Length == 0) return false; for (int i = searchTextArray.Length - 1; i >= 0; i--) { if (node == null || node.Cells.Count <= cellIndex) return false; if (exact) { if (string.Compare(node.Cells[cellIndex].Text, searchTextArray[i], ignoreCase) != 0) return false; } else { if (ignoreCase ? node.Cells[cellIndex].Text.IndexOf(searchTextArray[i], StringComparison.CurrentCultureIgnoreCase) < 0 : !node.Text.Contains(searchTextArray[i])) return false; } node = node.Parent; } return true; } private void textBoxItemSearchWz_KeyDown(object sender, KeyEventArgs e) { if (e.KeyCode == Keys.Enter) { buttonItemSearchWz_Click(buttonItemSearchWz, EventArgs.Empty); } } private void buttonItemSearchString_Click(object sender, EventArgs e) { if (string.IsNullOrEmpty(textBoxItemSearchString.Text)) return; QueryPerformance.Start(); if (!this.stringLinker.HasValues) { if (!this.TryLoadStringWz()) { MessageBoxEx.Show("没有初始化string链接,请手动指定一个String.wz。", "喵~~"); return; } QueryPerformance.End(); double ms = (Math.Round(QueryPerformance.GetLastInterval(), 4) * 1000); labelItemStatus.Text = "初始化StringLinker成功, 用时" + ms + "ms."; } if (comboBoxItem2.SelectedIndex < 0) comboBoxItem2.SelectedIndex = 0; List> dicts = new List>(); switch (comboBoxItem2.SelectedIndex) { case 0: dicts.Add(stringLinker.StringEqp); dicts.Add(stringLinker.StringItem); dicts.Add(stringLinker.StringMap); dicts.Add(stringLinker.StringMob); dicts.Add(stringLinker.StringNpc); dicts.Add(stringLinker.StringSkill); break; case 1: dicts.Add(stringLinker.StringEqp); break; case 2: dicts.Add(stringLinker.StringItem); break; case 3: dicts.Add(stringLinker.StringMap); break; case 4: dicts.Add(stringLinker.StringMob); break; case 5: dicts.Add(stringLinker.StringNpc); break; case 6: dicts.Add(stringLinker.StringSkill); break; } listViewExString.BeginUpdate(); try { listViewExString.Items.Clear(); IEnumerable> results = searchStringLinker(dicts, textBoxItemSearchString.Text, checkBoxItemExact2.Checked, checkBoxItemRegex2.Checked); foreach (KeyValuePair kv in results) { string[] item = new string[] { kv.Key.ToString(), kv.Value.Name, kv.Value.Desc, kv.Value.FullPath }; listViewExString.Items.Add(new ListViewItem(item)); } } catch (Exception ex) { MessageBoxEx.Show(ex.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); } finally { listViewExString.EndUpdate(); } } private bool TryLoadStringWz() { foreach (Wz_Structure wz in openedWz) { foreach (Wz_File file in wz.wz_files) { if (file.Type == Wz_Type.String && this.stringLinker.Load(file)) { return true; } } } return false; } private IEnumerable> searchStringLinker(IEnumerable> dicts, string key, bool exact, bool isRegex) { string[] match = key.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); Regex re = null; if (isRegex) { re = new Regex(key, RegexOptions.IgnoreCase); } foreach (Dictionary dict in dicts) { foreach (KeyValuePair kv in dict) { if (exact) { if (kv.Key.ToString() == key || kv.Value.Name == key) yield return kv; } else if (isRegex) { if (re.IsMatch(kv.Key.ToString()) || (!string.IsNullOrEmpty(kv.Value.Name) && re.IsMatch(kv.Value.Name))) { yield return kv; } } else { string id = kv.Key.ToString(); bool r = true; foreach (string str in match) { if (!(id.Contains(str) || (!string.IsNullOrEmpty(kv.Value.Name) && kv.Value.Name.Contains(str)))) { r = false; break; } } if (r) { yield return kv; } } } } } private void textBoxItemSearchString_KeyDown(object sender, KeyEventArgs e) { if (e.KeyCode == Keys.Enter) { buttonItemSearchString_Click(buttonItemSearchString, EventArgs.Empty); } } private void buttonItemSelectStringWz_Click(object sender, EventArgs e) { Wz_File stringWzFile = advTree1.SelectedNode?.AsWzNode()?.GetNodeWzFile(); if (stringWzFile == null) { MessageBoxEx.Show("没有选择一个用于初始化StringLinker的WzFile。", "喵..."); return; } QueryPerformance.Start(); bool r = stringLinker.Load(stringWzFile); QueryPerformance.End(); if (r) { double ms = (Math.Round(QueryPerformance.GetLastInterval(), 4) * 1000); labelItemStatus.Text = "初始化StringLinker成功, 用时" + ms + "ms."; } else { MessageBoxEx.Show("初始化StringLinker失败。", "喵.."); } } private void buttonItemClearStringWz_Click(object sender, EventArgs e) { stringLinker.Clear(); labelItemStatus.Text = "StringLinker已经清空..."; } private void buttonItemPatcher_Click(object sender, EventArgs e) { foreach (Form form in Application.OpenForms) { if (form is FrmPatcher && !form.IsDisposed) { form.Show(); form.BringToFront(); return; } } FrmPatcher patcher = new FrmPatcher(); var config = WcR2Config.Default; var defaultEnc = config?.WzEncoding?.Value ?? 0; if (defaultEnc != 0) { patcher.PatcherNoticeEncoding = Encoding.GetEncoding(defaultEnc); } patcher.Owner = this; patcher.Show(); } #endregion #region soundPlayer相关事件 private void preLoadSound(Wz_Sound sound, string soundName) { byte[] data = sound.ExtractSound(); if (data == null || data.Length <= 0) { return; } soundPlayer.PreLoad(data); labelItemSoundTitle.Text = soundName; switch (sound.SoundType) { case Wz_SoundType.Mp3: soundName += ".mp3"; break; case Wz_SoundType.Pcm: soundName += ".wav"; break; } soundPlayer.PlayingSoundName = soundName; labelItemSoundTitle.Tooltip = soundName; } private void sliderItemSoundTime_ValueChanged(object sender, EventArgs e) { if (!timerChangeValue) soundPlayer.SoundPosition = sliderItemSoundTime.Value; } private void sliderItemSoundVol_ValueChanged(object sender, EventArgs e) { soundPlayer.Volume = sliderItemSoundVol.Value; } private void buttonItemLoadSound_Click(object sender, EventArgs e) { using (OpenFileDialog dlg = new OpenFileDialog()) { List supportExt = new List(); supportExt.Add("Sound File(*.mp3;*.ogg;*.wav)|*.mp3;*.ogg;*.wav"); foreach (string ext in this.soundPlayer.GetPluginSupportedExt()) { supportExt.Add(ext); } supportExt.Add("Any File|*.*"); dlg.Title = "请选择一个声音文件..."; dlg.Filter = string.Join("|", supportExt.ToArray()); dlg.Multiselect = false; if (DialogResult.OK == dlg.ShowDialog()) { loadCostumSoundFile(dlg.FileName); } } } private void buttonItemSoundPlay_Click(object sender, EventArgs e) { if (soundPlayer.State == PlayState.Playing) { soundPlayer.Pause(); buttonItemSoundPlay.Image = WzComparerR2.Properties.Resources.Play; //buttonItemSoundPlay.Text = " Play"; } else if (soundPlayer.State == PlayState.Paused) { soundPlayer.Resume(); //buttonItemSoundPlay.Text = "Pause"; buttonItemSoundPlay.Image = WzComparerR2.Properties.Resources.Pause; } else { soundPlayer.Play(); //buttonItemSoundPlay.Text = "Pause"; buttonItemSoundPlay.Image = WzComparerR2.Properties.Resources.Pause; } } private void buttonItemSoundStop_Click(object sender, EventArgs e) { soundPlayer.Stop(); //buttonItemSoundPlay.Text = " Play"; buttonItemSoundPlay.Image = WzComparerR2.Properties.Resources.Play; } private void buttonItemSoundSave_Click(object sender, EventArgs e) { byte[] data = soundPlayer.Data; if (data == null) return; using (SaveFileDialog dlg = new SaveFileDialog()) { dlg.AddExtension = true; dlg.Title = "请选择保存路径..."; dlg.Filter = "*.*|*.*"; dlg.AddExtension = false; dlg.FileName = soundPlayer.PlayingSoundName; if (dlg.ShowDialog() == DialogResult.OK) { FileStream fs = null; try { fs = new FileStream(dlg.FileName, FileMode.Create); fs.Write(data, 0, data.Length); MessageBoxEx.Show("保存成功!"); } catch (Exception ex) { MessageBoxEx.Show("保存失败\r\n\r\n" + ex.ToString(), "错误"); } finally { if (fs != null) { fs.Close(); } } } } } private void checkBoxItemSoundLoop_CheckedChanged(object sender, CheckBoxChangeEventArgs e) { soundPlayer.Loop = checkBoxItemSoundLoop.Checked; } private void soundTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) { TimeSpan currentTime = TimeSpan.FromSeconds(soundPlayer.SoundPosition); TimeSpan totalTime = TimeSpan.FromSeconds(soundPlayer.SoundLength); labelItemSoundTime.Text = string.Format("{0:d2}:{1:d2}:{2:d2}.{3:d3} / {4:d2}:{5:d2}:{6:d2}.{7:d3}", currentTime.Hours, currentTime.Minutes, currentTime.Seconds, currentTime.Milliseconds, totalTime.Hours, totalTime.Minutes, totalTime.Seconds, totalTime.Milliseconds); timerChangeValue = true; sliderItemSoundTime.Maximum = (int)totalTime.TotalSeconds; sliderItemSoundTime.Value = (int)currentTime.TotalSeconds; timerChangeValue = false; } private void ribbonBar3_DragEnter(object sender, DragEventArgs e) { string[] types = e.Data.GetFormats(); if (e.Data.GetDataPresent(DataFormats.FileDrop)) { e.Effect = DragDropEffects.Move; } else { e.Effect = DragDropEffects.None; } } private void ribbonBar3_DragDrop(object sender, DragEventArgs e) { if (e.Data.GetDataPresent(DataFormats.FileDrop)) { string[] files = (string[])e.Data.GetData(DataFormats.FileDrop); loadCostumSoundFile(files[0]); } } private void loadCostumSoundFile(string fileName) { CustomSoundFile soundFile = new CustomSoundFile(fileName, 0, (int)(new FileInfo(fileName).Length)); soundPlayer.PreLoad(soundFile); soundPlayer.PlayingSoundName = Path.GetFileName(fileName); labelItemSoundTitle.Text = "(载入文件)" + soundPlayer.PlayingSoundName; labelItemSoundTitle.Tooltip = fileName; } #endregion #region contextMenuStrip2 private void tsmi2SaveAs_Click(object sender, EventArgs e) { object item = advTree3.SelectedNode?.AsWzNode()?.Value; if (item == null) return; if (item is string str) { SaveFileDialog dlg = new SaveFileDialog(); dlg.FileName = advTree3.SelectedNode.Text; if (!dlg.FileName.Contains(".")) { dlg.FileName += ".txt"; } dlg.Filter = "*.*|*.*"; if (dlg.ShowDialog() == DialogResult.OK) { try { File.WriteAllText(dlg.FileName, str); this.labelItemStatus.Text = "保存成功。"; } catch (Exception ex) { MessageBoxEx.Show("文件保存失败。\r\n" + ex.ToString(), "提示"); } } } else if (item is IMapleStoryBlob blob) { SaveFileDialog dlg = new SaveFileDialog(); dlg.FileName = advTree3.SelectedNode.Text; if (!dlg.FileName.Contains(".") && blob is Wz_Sound wzSound) { switch (wzSound.SoundType) { case Wz_SoundType.Mp3: dlg.FileName += ".mp3"; break; case Wz_SoundType.Pcm: dlg.FileName += ".pcm"; break; } } dlg.Filter = "*.*|*.*"; if (dlg.ShowDialog() == DialogResult.OK) { try { byte[] data = new byte[blob.Length]; blob.CopyTo(data, 0); using (var f = File.Create(dlg.FileName)) { f.Write(data, 0, data.Length); f.Flush(); } this.labelItemStatus.Text = "保存成功。"; } catch (Exception ex) { MessageBoxEx.Show("文件保存失败。\r\n" + ex.ToString(), "提示"); } } } else if (item is Wz_Png png) { SaveFileDialog dlg = new SaveFileDialog(); dlg.Title = "保存Canvas的原始数据"; dlg.FileName = advTree3.SelectedNode.Text + ".bin"; dlg.Filter = "*.*|*.*"; if (dlg.ShowDialog() == DialogResult.OK) { try { using (var dataReader = png.UnsafeOpenRead()) using (var outputFile = dlg.OpenFile()) { dataReader.CopyTo(outputFile); } this.labelItemStatus.Text = "保存成功。"; } catch (Exception ex) { MessageBoxEx.Show("文件保存失败。\r\n" + ex.ToString(), "提示"); } } } } private void tsmi2HandleUol_Click(object sender, EventArgs e) { Wz_Uol uol = advTree3.SelectedNode?.AsWzNode()?.Value as Wz_Uol; if (uol == null) { labelItemStatus.Text = "没有选中适当的uol节点..."; return; } Node uolNode = handleUol(advTree3.SelectedNode, uol.Uol); if (uolNode == null) { labelItemStatus.Text = "没有找到uol对应的节点...请试着载入更底层的父节点重新执行.."; return; } else { advTree3.SelectedNode = uolNode; } } private void tsmi2ExpandAll_Click(object sender, EventArgs e) { if (advTree3.SelectedNode == null) return; advTree3.BeginUpdate(); advTree3.SelectedNode.ExpandAll(); advTree3.SelectedNode.Expand(); advTree3.EndUpdate(); } private void tsmi2CollapseAll_Click(object sender, EventArgs e) { if (advTree3.SelectedNode == null) return; advTree3.BeginUpdate(); advTree3.SelectedNode.Collapse(); advTree3.SelectedNode.CollapseAll(); advTree3.EndUpdate(); } private void tsmi2ExpandLevel_Click(object sender, EventArgs e) { if (advTree3.SelectedNode == null) return; advTree3.BeginUpdate(); foreach (Node node in getEqualLevelNode(advTree3.SelectedNode)) { node.Expand(); } advTree3.EndUpdate(); } private void tsmi2CollapseLevel_Click(object sender, EventArgs e) { if (advTree3.SelectedNode == null) return; advTree3.BeginUpdate(); foreach (Node node in getEqualLevelNode(advTree3.SelectedNode)) { node.Collapse(); } advTree3.EndUpdate(); } private IEnumerable getEqualLevelNode(Node currentNode) { if (currentNode == null) yield break; int level = currentNode.Level; Node parent = currentNode; while (parent != null && parent.Parent != null) { parent = parent.Parent; } Queue nodeList = new Queue(); nodeList.Enqueue(parent); for (int i = 0; i < level; i++) { int count = nodeList.Count; for (int j = 0; j < count; j++) { Node node = nodeList.Dequeue(); foreach (Node child in node.Nodes) nodeList.Enqueue(child); } } while (nodeList.Count > 0) { yield return nodeList.Dequeue(); } } private void tsmi2Prev_Click(object sender, EventArgs e) { if (historyNodeList.PrevCount > 0) { historySelecting = true; advTree3.SelectedNode = historyNodeList.MovePrev(); } } private void tsmi2Next_Click(object sender, EventArgs e) { if (historyNodeList.NextCount > 0) { historySelecting = true; advTree3.SelectedNode = historyNodeList.MoveNext(); } } private void tsmi2CopyFullPath_Click(object sender, EventArgs e) { var selectedWzNode = advTree3.SelectedNode.AsWzNode(); if (selectedWzNode != null) { string fullPath = selectedWzNode.FullPathToFile.Replace('\\', '/'); Clipboard.SetText(fullPath); ToastNotification.Show(this, "已经复制当前所选节点完整路径", 1000, eToastPosition.TopCenter); } } private void contextMenuStrip2_Opening(object sender, CancelEventArgs e) { var node = advTree3.SelectedNode.AsWzNode(); tsmi2SaveAs.Visible = false; tsmi2HandleUol.Visible = false; if (node != null) { if (node.Value is Wz_Sound || node.Value is Wz_Png || node.Value is string || node.Value is Wz_RawData || node.Value is Wz_Video) { tsmi2SaveAs.Visible = true; tsmi2SaveAs.Enabled = true; } else if (node.Value is Wz_Uol) { tsmi2HandleUol.Visible = true; } else { tsmi2SaveAs.Visible = true; tsmi2SaveAs.Enabled = false; } } } #endregion #region charaSim相关 private void buttonItemQuickView_Click(object sender, EventArgs e) { quickView(); } private void advTree1_AfterNodeSelect_2(object sender, AdvTreeNodeEventArgs e) { lastSelectedTree = advTree1; if (buttonItemAutoQuickView.Checked) { quickView(advTree1.SelectedNode); } } private void advTree2_AfterNodeSelect_2(object sender, AdvTreeNodeEventArgs e) { lastSelectedTree = advTree2; if (buttonItemAutoQuickView.Checked) { quickView(advTree2.SelectedNode); } } private void quickView() { if (lastSelectedTree != null) { quickView(lastSelectedTree.SelectedNode); } } private void quickView(Node node) { Wz_Node selectedNode = node.AsWzNode(); if (selectedNode == null) { return; } Wz_Image image; Wz_File wzf = selectedNode.GetNodeWzFile(); if (wzf == null) { labelItemStatus.Text = "没有查询到节点所属的wzfile。"; return; } if (!this.stringLinker.HasValues) { this.TryLoadStringWz(); } object obj = null; string fileName = null; switch (wzf.Type) { case Wz_Type.Character: if ((image = selectedNode.GetValue()) == null || !image.TryExtract()) return; CharaSimLoader.LoadSetItemsIfEmpty(); CharaSimLoader.LoadExclusiveEquipsIfEmpty(); if (selectedNode.FullPathToFile.Contains("Familiar")) { var familiar = Familiar.CreateFromNode(image.Node, PluginManager.FindWz); obj = familiar; if (familiar != null) { fileName = "familiar_" + familiar.FamiliarID + ".png"; } } else { var gear = Gear.CreateFromNode(image.Node, PluginManager.FindWz); obj = gear; if (gear != null) { fileName = gear.ItemID + ".png"; } } break; case Wz_Type.Item: Wz_Node itemNode = selectedNode; if (Regex.IsMatch(itemNode.FullPathToFile, @"^Item\\(Cash|Consume|Etc|Install|Cash)\\\d{4,6}.img\\\d+$")) { var item = Item.CreateFromNode(itemNode, PluginManager.FindWz); obj = item; if (item != null) { fileName = item.ItemID + ".png"; } } else if (Regex.IsMatch(itemNode.FullPathToFile, @"^Item\\Pet\\\d{7}.img")) { if (CharaSimLoader.LoadedSetItems.Count == 0) //宠物 预读套装 { CharaSimLoader.LoadSetItemsIfEmpty(); } if ((image = selectedNode.GetValue()) == null || !image.TryExtract()) return; var item = Item.CreateFromNode(image.Node, PluginManager.FindWz); obj = item; if (item != null) { fileName = item.ItemID + ".png"; } } break; case Wz_Type.Skill: Wz_Node skillNode = selectedNode; //模式路径分析 if (Regex.IsMatch(skillNode.FullPathToFile, @"^Skill\d*\\Recipe_\d+.img\\\d+$")) { Recipe recipe = Recipe.CreateFromNode(skillNode); obj = recipe; if (recipe != null) { fileName = "recipe_" + recipe.RecipeID + ".png"; } } else if (Regex.IsMatch(skillNode.FullPathToFile, @"^Skill\d*\\\d+.img\\skill\\\d+$")) { Skill skill = Skill.CreateFromNode(skillNode, PluginManager.FindWz); if (skill != null) { switch (this.skillDefaultLevel) { case DefaultLevel.Level0: skill.Level = 0; break; case DefaultLevel.Level1: skill.Level = 1; break; case DefaultLevel.LevelMax: skill.Level = skill.MaxLevel; break; case DefaultLevel.LevelMaxWithCO: skill.Level = skill.MaxLevel + 2; break; } obj = skill; fileName = "skill_" + skill.SkillID + ".png"; } } break; case Wz_Type.Mob: if ((image = selectedNode.GetValue()) == null || !image.TryExtract()) return; var mob = Mob.CreateFromNode(image.Node, PluginManager.FindWz); obj = mob; if (mob != null) { fileName = mob.ID + ".png"; } break; case Wz_Type.Npc: if ((image = selectedNode.GetValue()) == null || !image.TryExtract()) return; var npc = Npc.CreateFromNode(image.Node, PluginManager.FindWz); obj = npc; if (npc != null) { fileName = npc.ID + ".png"; } break; } if (obj != null) { tooltipQuickView.TargetItem = obj; tooltipQuickView.ImageFileName = fileName; tooltipQuickView.Refresh(); tooltipQuickView.HideOnHover = false; tooltipQuickView.Show(); } } private void comboBoxItemLanguage_SelectedIndexChanged(object sender, EventArgs e) { DevComponents.Editors.ComboItem item = comboBoxItemLanguage.SelectedItem as DevComponents.Editors.ComboItem; if (item != null) { GearGraphics.SetFontFamily(item.Text); ConfigManager.Reload(); CharaSimConfig.Default.SelectedFontIndex = comboBoxItemLanguage.SelectedIndex; ConfigManager.Save(); } } private void buttonItemClearSetItems_Click(object sender, EventArgs e) { int count = CharaSimLoader.LoadedSetItems.Count; CharaSimLoader.LoadedSetItems.Clear(); labelItemStatus.Text = "已经清空预读套装共" + count + "项。"; } private void buttonItemCharItem_CheckedChanged(object sender, EventArgs e) { if (buttonItemCharItem.Checked) this.charaSimCtrl.UIItem.Refresh(); this.charaSimCtrl.UIItem.Visible = buttonItemCharItem.Checked; } private void buttonItemAddItem_Click(object sender, EventArgs e) { bool success; success = this.charaSimCtrl.UIItem.AddItem(this.tooltipQuickView.TargetItem as ItemBase); if (!success) { labelItemStatus.Text = "背包加入物品失败..."; } } private void afrm_KeyDown(object sender, KeyEventArgs e) { AfrmTooltip frm = sender as AfrmTooltip; if (frm == null) return; switch (e.KeyCode) { case Keys.Escape: frm.Hide(); return; case Keys.Up: frm.Top -= 1; return; case Keys.Down: frm.Top += 1; return; case Keys.Left: frm.Left -= 1; return; case Keys.Right: frm.Left += 1; return; } Skill skill = frm.TargetItem as Skill; if (skill != null) { switch (e.KeyCode) { case Keys.Oemplus: case Keys.Add: skill.Level += 1; break; case Keys.OemMinus: case Keys.Subtract: skill.Level -= 1; break; case Keys.PageDown: skill.PerJobIndex += 1; break; case Keys.PageUp: skill.PerJobIndex -= 1; break; case Keys.OemOpenBrackets: skill.Level -= this.skillInterval; break; case Keys.OemCloseBrackets: skill.Level += this.skillInterval; break; default: return; } frm.Refresh(); } } private void buttonItemCharaStat_CheckedChanged(object sender, EventArgs e) { if (buttonItemCharaStat.Checked) { this.charaSimCtrl.UIStat.Refresh(); } this.charaSimCtrl.UIStat.Visible = buttonItemCharaStat.Checked; } private void buttonItemCharaEquip_CheckedChanged(object sender, EventArgs e) { if (buttonItemCharaEquip.Checked) { this.charaSimCtrl.UIEquip.Refresh(); } this.charaSimCtrl.UIEquip.Visible = buttonItemCharaEquip.Checked; } private void buttonItemQuickViewSetting_Click(object sender, EventArgs e) { using (FrmQuickViewSetting frm = new FrmQuickViewSetting()) { frm.Load(CharaSimConfig.Default); if (frm.ShowDialog() == DialogResult.OK) { ConfigManager.Reload(); frm.Save(CharaSimConfig.Default); ConfigManager.Save(); UpdateCharaSimSettings(); } } } #endregion #region 实现插件接口 Office2007RibbonForm PluginContextProvider.MainForm { get { return this; } } DotNetBarManager PluginContextProvider.DotNetBarManager { get { return this.dotNetBarManager1; } } IList PluginContextProvider.LoadedWz { get { return new System.Collections.ObjectModel.ReadOnlyCollection(this.openedWz); } } Wz_Node PluginContextProvider.SelectedNode1 { get { return advTree1.SelectedNode.AsWzNode(); } } Wz_Node PluginContextProvider.SelectedNode2 { get { return advTree2.SelectedNode.AsWzNode(); } } Wz_Node PluginContextProvider.SelectedNode3 { get { return advTree3.SelectedNode.AsWzNode(); } } private EventHandler selectedNode1Changed; private EventHandler selectedNode2Changed; private EventHandler selectedNode3Changed; private EventHandler wzOpened; private EventHandler wzClosing; event EventHandler PluginContextProvider.SelectedNode1Changed { add { selectedNode1Changed += value; } remove { selectedNode1Changed -= value; } } event EventHandler PluginContextProvider.SelectedNode2Changed { add { selectedNode2Changed += value; } remove { selectedNode2Changed -= value; } } event EventHandler PluginContextProvider.SelectedNode3Changed { add { selectedNode3Changed += value; } remove { selectedNode3Changed -= value; } } event EventHandler PluginContextProvider.WzOpened { add { wzOpened += value; } remove { wzOpened -= value; } } event EventHandler PluginContextProvider.WzClosing { add { wzClosing += value; } remove { wzClosing -= value; } } StringLinker PluginContextProvider.DefaultStringLinker { get { return this.stringLinker; } } AlphaForm PluginContextProvider.DefaultTooltipWindow { get { return this.tooltipQuickView; } } private void RegisterPluginEvents() { advTree1.AfterNodeSelect += advTree1_AfterNodeSelect_Plugin; advTree2.AfterNodeSelect += advTree2_AfterNodeSelect_Plugin; advTree3.AfterNodeSelect += advTree3_AfterNodeSelect_Plugin; } private void advTree1_AfterNodeSelect_Plugin(object sender, AdvTreeNodeEventArgs e) { if (selectedNode1Changed != null) { var wzNode = ((PluginContextProvider)(this)).SelectedNode1; var args = new WzNodeEventArgs(wzNode); selectedNode1Changed(this, args); } } private void advTree2_AfterNodeSelect_Plugin(object sender, AdvTreeNodeEventArgs e) { if (selectedNode2Changed != null) { var wzNode = ((PluginContextProvider)(this)).SelectedNode2; var args = new WzNodeEventArgs(wzNode); selectedNode2Changed(this, args); } } private void advTree3_AfterNodeSelect_Plugin(object sender, AdvTreeNodeEventArgs e) { if (selectedNode3Changed != null) { var wzNode = ((PluginContextProvider)(this)).SelectedNode3; var args = new WzNodeEventArgs(wzNode); selectedNode3Changed(this, args); } } protected virtual void OnWzOpened(WzStructureEventArgs e) { if (wzOpened != null) { wzOpened(this, e); } } protected virtual void OnWzClosing(WzStructureEventArgs e) { if (wzClosing != null) { wzClosing(this, e); } } #endregion private void btnEasyCompare_Click(object sender, EventArgs e) { if (compareThread != null) { compareThread.Suspend(); if (DialogResult.Yes == MessageBoxEx.Show("正在执行一个对比操作,要中止吗?", "Compare", MessageBoxButtons.YesNoCancel)) { compareThread.Resume(); compareThread.Abort(); } else { compareThread.Resume(); } return; } if (openedWz.Count < 2) { MessageBoxEx.Show("没有成功打开两个WZ。", "Compare"); return; } FolderBrowserDialog dlg = new FolderBrowserDialog(); dlg.Description = "请选择输出报告的文件夹"; if (dlg.ShowDialog() == DialogResult.OK) { compareThread = new Thread(() => { System.Diagnostics.Stopwatch sw = System.Diagnostics.Stopwatch.StartNew(); EasyComparer comparer = new EasyComparer(); comparer.Comparer.PngComparison = (WzPngComparison)cmbComparePng.SelectedItem; comparer.Comparer.ResolvePngLink = chkResolvePngLink.Checked; comparer.OutputPng = chkOutputPng.Checked; comparer.OutputAddedImg = chkOutputAddedImg.Checked; comparer.OutputRemovedImg = chkOutputRemovedImg.Checked; comparer.HashPngFileName = chkHashPngFileName.Checked; comparer.StateInfoChanged += new EventHandler(comparer_StateInfoChanged); comparer.StateDetailChanged += new EventHandler(comparer_StateDetailChanged); comparer.ColorTable = new List() { CustomCSSConfig.Default.BackgroundColor, CustomCSSConfig.Default.NormalTextColor, CustomCSSConfig.Default.ChangedBackgroundColor, CustomCSSConfig.Default.AddedBackgroundColor, CustomCSSConfig.Default.RemovedBackgroundColor, CustomCSSConfig.Default.ChangedTextColor, CustomCSSConfig.Default.AddedTextColor, CustomCSSConfig.Default.RemovedTextColor, CustomCSSConfig.Default.HyperlinkColor }; try { Wz_File fileNew = openedWz[0].wz_files[0]; Wz_File fileOld = openedWz[1].wz_files[0]; while (true) { string txt = string.Format("待比较wz文件:\r\n\r\n new : {0} (ver:{1})\r\n old : {2} (ver:{3})\r\n\r\n如果继续对比请选择Yes,如果想交换文件顺序请选择No。", fileNew.Header.FileName, fileNew.GetMergedVersion(), fileOld.Header.FileName, fileOld.GetMergedVersion() ); switch (MessageBoxEx.Show(txt, "Easy Compare", MessageBoxButtons.YesNoCancel)) { case DialogResult.Yes: comparer.EasyCompareWzFiles(fileNew, fileOld, dlg.SelectedPath); return; case DialogResult.No: Wz_File tmp = fileNew; fileNew = fileOld; fileOld = tmp; break; case DialogResult.Cancel: default: return; } } } catch (ThreadAbortException) { MessageBoxEx.Show(this, "比较已经中止。", "Compare"); } catch (Exception ex) { MessageBoxEx.Show(this, "出现异常\r\n" + ex.ToString(), "Compare"); } finally { sw.Stop(); compareThread = null; labelXComp1.Text = "wz对比结束...共用时" + sw.Elapsed.ToString(); labelXComp2.Text = ""; } }); compareThread.Priority = ThreadPriority.Highest; compareThread.Start(); } } void comparer_StateDetailChanged(object sender, EventArgs e) { EasyComparer comp = sender as EasyComparer; if (comp != null) { labelXComp1.Text = comp.StateInfo; } } void comparer_StateInfoChanged(object sender, EventArgs e) { EasyComparer comp = sender as EasyComparer; if (comp != null) { labelXComp2.Text = comp.StateDetail; } } private void buttonItemAbout_Click(object sender, EventArgs e) { new FrmAbout().ShowDialog(); } private void btnExportSkill_Click(object sender, EventArgs e) { FolderBrowserDialog dlg = new FolderBrowserDialog(); dlg.Description = "请选择技能列表输出文件夹"; if (dlg.ShowDialog() == DialogResult.OK) { if (!this.stringLinker.HasValues) this.TryLoadStringWz(); DBConnection conn = new DBConnection(this.stringLinker); DataSet ds = conn.GenerateSkillTable(); foreach (DataTable dt in ds.Tables) { FileStream fs = new FileStream(Path.Combine(dlg.SelectedPath, dt.TableName + ".csv"), FileMode.Create); StreamWriter sw = new StreamWriter(fs, Encoding.UTF8); conn.OutputCsv(sw, dt); sw.Close(); fs.Dispose(); } MessageBoxEx.Show("数据导出完毕。"); } } private void btnCustomCSS_Click(object sender, EventArgs e) { ConfigManager.Reload(); var Setting = CustomCSSConfig.Default; using (FrmCustomCSS frm = new FrmCustomCSS()) { frm.LoadConfig(Setting); if (frm.ShowDialog() == DialogResult.OK) { frm.SaveConfig(Setting); ConfigManager.Save(); } } } private void btnExportSkillOption_Click(object sender, EventArgs e) { FolderBrowserDialog dlg = new FolderBrowserDialog(); dlg.Description = "请选择魂宝珠系统输出文件夹"; if (dlg.ShowDialog() == DialogResult.OK) { if (!this.stringLinker.HasValues) this.TryLoadStringWz(); DBConnection conn = new DBConnection(this.stringLinker); conn.ExportSkillOption(dlg.SelectedPath); MessageBoxEx.Show("数据导出完毕。"); } } private void buttonItemAutoQuickView_Click(object sender, EventArgs e) { ConfigManager.Reload(); CharaSimConfig.Default.AutoQuickView = buttonItemAutoQuickView.Checked; ConfigManager.Save(); } private void panelExLeft_SizeChanged(object sender, EventArgs e) { if (this.WindowState != FormWindowState.Minimized) { if (panelExLeft.Tag is int) { int oldHeight = (int)panelExLeft.Tag; advTree1.Height = (int)(1.0 * advTree1.Height / oldHeight * panelExLeft.Height); } panelExLeft.Tag = panelExLeft.Height; } } private void buttonItem1_Click(object sender, EventArgs e) { } private void labelItemStatus_TextChanged(object sender, EventArgs e) { ribbonBar2.RecalcLayout(); } private void btnNodeBack_Click(object sender, EventArgs e) { } private void btnNodeForward_Click(object sender, EventArgs e) { } private void buttonItemUpdate_Click(object sender, EventArgs e) { var frm = new FrmUpdater(); frm.LoadConfig(WcR2Config.Default); frm.ShowDialog(); } private void btnItemOptions_Click(object sender, System.EventArgs e) { var frm = new FrmOptions(); frm.Load(WcR2Config.Default); if (frm.ShowDialog() == DialogResult.OK) { ConfigManager.Reload(); frm.Save(WcR2Config.Default); ConfigManager.Save(); UpdateWzLoadingSettings(); } } private void colorPickerPicBoxBgColor_SelectedColorChanged(object sender, EventArgs e) { this.pictureBoxEx1.BackColor = ((ColorPickerDropDown)sender).SelectedColor; } private async void MainForm_Shown(object sender, EventArgs e) { await this.AutomaticCheckUpdate(); } } #region 内部用扩展方法 internal static partial class Ext { public static Wz_Node AsWzNode(this Node node) { return (node?.Tag as WeakReference)?.Target as Wz_Node; } public static void ClearLayoutCellInfo(this AdvTree advTree) { var bindingPrivateField = BindingFlags.NonPublic | BindingFlags.Instance; { var field1 = advTree.GetType().GetField("Ֆ", bindingPrivateField); var obj1 = field1.GetValue(advTree); if (obj1 != null) { var field2 = obj1.GetType().BaseType.GetField("ӹ", bindingPrivateField); var obj2 = field2.GetValue(obj1); if (obj2 != null) { var field3 = obj2.GetType().GetField("ܦ", bindingPrivateField); var obj3 = field3.GetValue(obj2); if (obj3 != null) { field3.SetValue(obj2, null); } } } } { var display = advTree.NodeDisplay as NodeTreeDisplay; if (display != null) { var field4 = display.GetType().GetField("☼", bindingPrivateField); var obj4 = field4.GetValue(display) as NodeCellRendererEventArgs; if (obj4 != null) { obj4.Node = null; obj4.Cell = null; } } } } } #endregion } ================================================ FILE: WzComparerR2/MainForm.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 455, 17 17, 17 309, 17 163, 17 132, 54 17, 54 594, 17 297, 54 96 AAABAAQAICAAAAEACACoCAAARgAAABAQAAABAAgAaAUAAO4IAAAgIAAAAQAgAKgQAABWDgAAEBAAAAEA IABoBAAA/h4AACgAAAAgAAAAQAAAAAEACAAAAAAAgAQAAAAAAAAAAAAAAAEAAAAAAAAAAAAABTgDAAQz OAAFRgIABlkGAAtNGgAnXBkACGETAAltEgAKcBMADHgYAA5TPgAlUSMAKE40ABFmKwATei0AC2A7ADVr NwAENFAABDZaAAU5XQAOOVkABTxkAAk+ZAAGP2kAD0JeAAZlRgA/eFIABkBrAAtCawAUSG0AGUlrAAlE cQAKSXYAC0x7ABdOdwAUT3oAE1J/ACZLZgAuT2cANVJnADRTawAqVncAIlh/ACxZegA/X3YAT21OAFZw VgBbe14ARGJ4AFJqewBPf3oALIRFADWIdABhjm8Ad4t2AIKXcwAZV4MAEFmOABxejQAQXZUAIFiAACxe gwAbYpUAH2aZACdogAAoYowAKXeBADR5gQAka54ALm2dADZyngAUZaAAGmihABZtrQAbbasADHS9ABpy swAUdrwAGne6AB96vwAobaEALXGkACp0qgA1c6AAP3unAD57qAAldrAAKnq1ACN7vAAsf7wANH2yADJ+ uQBeeIkAQnqjAEd/qAACd80ADHfLAAN4zQAae8AAEnzNAA550QAjf8MAOoe0ADSCugA5hb0AWYCdAEWY mwBriJ0AdoiWAHmKlwB4jJoAb6GJAEOCrwBbia4AR4a1AEqMvABRiLoAUpe9AHyarwBmk7MAfpuwAHqe uQBUr7EAf6GiAHuhvwAaiswAHYLTAB2F3gAngMIALIPDAC2GyAAticsAMIfHADiDwQAziMcAMYrMADqP zgA+kc0AJYXTACiJ1wAmiNgAMo7QADCN2QA+ldQANpLaADuV3QAIkeQAJYzlAC2U4gAnkewANpPiADmW 5QA7mOEAIp30ADOa8QA/oOkAPaDzAECKwQBPlMYAQ5LLAFeXxABfmcUAQpXSAEWY1QBDldgARJrbAEme 3ABRm9YAWJ7SAGyeygBIoN8AXaPVAFGg2gBoocsAe6PCAH+oxgBgo9UAaajSAGGn2QB6rNIAcLDfAEaf 4ABIneEASqLhAFGm5ABVq+cAWqrkAFGp6ABcq+sAXrHrAGCs6QBisuwAbbXqAG2+7QBxtukAeLrqAGOx 9ABrt/UAa7nyAG+5+QByvPQAdL36AFzAxAB53s4AfsfuAHvB9QB8yfIAd8D7AHvD/AB/y/wAcu7uAICQ mwCftosAg6WZAKWkiQCAkqAAgJyxAIuluQCNqr8AnbC/AKW2rADBvaIAk8O8AIKnxQCXtMcAk7POAJm2 yACOttcAobXEAKC4xQCqvckAm8DXAJ3e0ACqwc8AuMXLAKnD0wCBw/MAhM/1AIHF+ACDyPoAhdH2AI3V +wCQ1voAlNz9AI7r5ACb4f4AqeX+ALjo/QDEycsAxNPZAM3j6wDG6/oA0ejyANby/QD///8AAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAB4W2haUx8tAABGSUtFLDIAAAAAAAAAAAAAAAAAAAAAAGiHiISIPxNteVlQT01KICgAAAAAAAAA AAAAAAAAAADZgoeOiINXFydehIiBWU1IFDEAAAAAAAAAAAAAAAAAAF+JkKaQo4kkF1iFjlqDgU06E10A AAAAAAAAAAAAAAAAnrirq6Snq1NSiohXJYGEgU0iF2wAAAAAAAAAAAAAAHqmurikOaC4pLmlozsTQIiI ZkocIAAAAAAAAAAAAAAAn7zAvVQUR7mnvKagICZsh46DZjwcIwAAAAAAAAAAAAC7wMCtHhVvrLq8tmkd 2ABzjoiESiEgAAAAAAAAAAAAsMfAvnQUKQCwu765USoAANqHhI5QSB0AAAAAAAAAAACuycfvXhMyAACp vq09AAAAAHSIjoFPKwAAAAAAAAAAALHJ7+0+FG4AAH2iegAAAAAA24WEgVN3AAAAAAAAAAAAs87wtR4W 1AAAAAAAAAAAAAAAVlNqAAAAAAAAAAAAAADDzvChFxfUAAAAAAAAAAAAAK+ioqp1cgAAAAAAAAAA5e3O 8FUWGNgAAAAAAAAAAOKolZud0NC3TQAAAAAAAADc7c7wRxgc2AAAAAAAAADkypeAlpvK0cWLXAAAAAAA ADPEye9UHCDYAAAAAAAA4L7zwI2YxsbGnJqVAAAAAAAbD0NnwUIcIG4AAAAAAACGmfT20siRZGOClbQA AAAAADYJBwtBGRICDQAAAAAA583x9vbSt2JOAAAAAAAAAAAAMAYKCAQDAQMBLgAAAADm9vb29tGLYk0A AAAAAAAAAAB8NQ4ECgQDAwE3AAAAAOr39vb20otgTAAAAAAAAAAAAN3pNRA0ERoFDAAAAAAA+vf29va8 lWViTnFbTH4AAAAAAN9wOMzVa0QvAAAAAAD5/Pb28oyUlGFiYX+Pk3YAAAAAAADW9dN7AAAAAAAAAPn9 +Pb2uo2Zv729ydCRAAAAAAAAAADey9cAAAAAAAAAAPr+/Pj38cKS8PT08rQAAAAAAAAAAAAAAAAAAAAA AAAA+fv+/v7488/09rqlAAAAAAAAAAAAAAAAAAAAAAAAAAAA+ezo+/78/Pj4sgAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAADn/fvj4esAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP// /////////gMD//4AAf/8AAD//AAAf/wAAD/4AAA/+AAAH/gAEB/wEDAf8Bh4H/AY+B/wH/x/8B/4H+Af 4A/gH8AH4B+AB8AfgAfAHwB/wA8Af8APAH/AHwAD4B8AAfh/AAP8f4AD//+AB///wA////gf//////// ////////KAAAABAAAAAgAAAAAQAIAAAAAABAAQAAAAAAAAAAAAAAAQAAAAAAAAAAAAAFPwkACVcTABBr HwAKPlQAK3RcAA9GbgASRm0AIFN4ACZYfQAldmYAQmZAAD6DUQBcjW4AT49xAFWddQAPV4wAHWOVAB9l lwApYIkAMmGDADRjhQA1ZIYAMG+LAC1nkAAoapoAOW2SABhppAAbcK4AHXOyACt3rwAserIAJX2+ADd+ sgAYfsoAPICxADeEvAA+hboAY4SdAFaCowBTirQAboyiAHOSqgAbhNQAH4bdACqDxAAphMcALIXFAC6I ygA6iMEAPonAADiKyAA8jcgALo/QADeP0AA5kNAAPJfhADea5gBAjcUAQ5HKAEKTzgBYlcIAWJjHAE2c 1gBHmtgASpnZAFKh2wBXpNwAZqDJAGyhzAB9qssAY6PSAGuo3ABzqtgAQZ3lAEGe7QBMo+IAWK3nAFqs 6ABdreoAbLLlAGu37QByteEAdLXlAHq14AB7vOsAfrzqAGW09ABqvPAAdMD0AHzJ9ACAl4IAn8G9AIO0 2QCHt9oAj7rYAJS/2wCU0skArc3DAInI6wCFzvoAkdT3AJvW9ACq1OcAv97qAJvi/wCe4/8AteP5ALnp /ADN2d8AytzlANDd4gDD4e8AJi8AAEBQAABacAAAdJAAAI6wAACpzwAAwvAAANH/EQDY/zEA3v9RAOP/ cQDp/5EA7/+xAPb/0QD///8AAAAAAC8mAABQQQAAcFsAAJB0AACwjgAAz6kAAPDDAAD/0hEA/9gxAP/d UQD/5HEA/+qRAP/wsQD/9tEA////AAAAAAAvFAAAUCIAAHAwAACQPgAAsE0AAM9bAADwaQAA/3kRAP+K MQD/nVEA/69xAP/BkQD/0rEA/+XRAP///wAAAAAALwMAAFAEAABwBgAAkAkAALAKAADPDAAA8A4AAP8g EgD/PjEA/1xRAP96cQD/l5EA/7axAP/U0QD///8AAAAAAC8ADgBQABcAcAAhAJAAKwCwADYAzwBAAPAA SQD/EVoA/zFwAP9RhgD/cZwA/5GyAP+xyAD/0d8A////AAAAAAAvACAAUAA2AHAATACQAGIAsAB4AM8A jgDwAKQA/xGzAP8xvgD/UccA/3HRAP+R3AD/seUA/9HwAP///wAAAAAALAAvAEsAUABpAHAAhwCQAKUA sADEAM8A4QDwAPAR/wDyMf8A9FH/APZx/wD3kf8A+bH/APvR/wD///8AAAAAABsALwAtAFAAPwBwAFIA kABjALAAdgDPAIgA8ACZEf8ApjH/ALRR/wDCcf8Az5H/ANyx/wDr0f8A////AAAAAAAIAC8ADgBQABUA cAAbAJAAIQCwACYAzwAsAPAAPhH/AFgx/wBxUf8AjHH/AKaR/wC/sf8A2tH/AP///wAAAAAAAAAAAAAA AAAAAAAAAAAAADIkFQAhHhoAAAAAAAAAAD03NhIYLyAcCQAAAAAAAAA/QDE8NB8RLRsUAAAAAABfTiMW Q0wZKTouECYAAAAAVFETAF1CJwBGMB0qAAAAAFNTCAAAAAAAACUoAAAAAABVRwcAAAAAAElBSEUAAAAA Uj4GAAAAAFZKS1c5XgAADAoXBAAAAGBaZDg7XQAAAA0DAgELAABnaWQrRAAAAABiDg8FWwAAaGpYLCIz NQAAAABhXAAAAG9sZU1PWVAAAAAAAAAAAAAAbnBrZmMAAAAAAAAAAAAAAAAAbQAAAAAAAAAAAAAAAAAA AAAAAAAA//////Ef///gD///4Af//8AD///EQ///x+f//8fD///Hgf//hwP//4MH//+DAf//5wH///+D ////7////////ygAAAAgAAAAQAAAAAEAIAAAAAAAgBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALLDcXFjxTQxU8XEgVPFxIDjFKSAQZJjwADBkUAAAAAQAz MwUJLU04CjFNSAo1VEgEJz80ABEaHQAAAAkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADSg1EyFjka8tebH0LX249yl7uPckb6j3Cz5i8AMu TMIFHjIyCTJNOBNakeETZJ73F2mm9xJflusJPWTaASI6qwAZJjwAAAABAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASPFpEK3y48TOJx/8yi8z/LYjL/zCI yf8bYpX/BDdb/gUmQIsUSW+LInm5/h96v/8adLb/GHK1/xdtrP8JRXD/ASVAygAaJycAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAxZJcIoshMP+MojH/zOM zv8uicz/LofI/yR2sP8HPWT/BCxJ1B9jk9gviMr/MIrM/yZ+v/8gebv/GnO0/xNkoP8EOFz9ASpIuwAc KiQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIIjMeI2aY1TaL yv8/k9H/Q5va/z6X1/9BldL/Oo/O/xRPev8JP2b+Knq1/i+IyP8xjtH/KX++/yuDxf8ngML/GXGz/xBZ jv8DNVn9AyxGogAcKhIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACFJ aFM6hr/2SqDg/0mg4P9IoN//RJnX/0mf3P9In+D/LnOn/yxwof89kc//MorK/yd3sf8TUn//KYDA/y2I yv8lf8H/GnKz/wtMe/8FOmD5BjNWlAAfLxAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAJCQHKWaRn0Sb2v9Squn/S6Pj/0SW0v8ZV4P/Q5LL/0mh4P9HmNT/TqXk/0SX1v9Ak9H/HF6N/wU3 Wv4dZZj8MIrN/zGLzf8kfr//Fm6u/wdCbP8HQWz3Bi5NcwAAJAcAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAACNBWjM8icDmUqfm/2Gz7v9ZrOf/NnOg/wQ3Wv80cZz8Uabj/0me3P9QqOj/RZzd/z+R zP8NRG3/Ay5O2xRFaaIuhsf8NI3O/y2GyP8hf8X/EF2V/wZAa/8HQm7uBSY+VgAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAABO2iLd1yq4f1gsev/YrLt/1Ke1v8USG7/AzBR8x4/WphUntPxV6rn/1Oq 6f9Gn+D/OYW9/wc+aPwDJ0OAEjFKKSdxqdk1j9D/L4nK/yuIzf8Ybq7/Ckl2/whDcP0EJkJwAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAABIkJA5RibK7bLrz/2W07f9esOv/SIy+/wY5Xf8CKUbMBxUkI0d/ q7JYquX/XK/r/0yk4v8obaH/BTlg2QYaLiYAAAADGU11gDKIx/wuisz/L4zP/yB5uv8VZqH/Bj1n9wMf NEkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIzhGJFSVxeFwvPT/a7nx/4DG+f9Ee6T/AzZc/wQo RLAAAAAHMVZ3TU+Z0PFesOv/UaDa/xFNd+4FLUhmAAAAAwAAAAANLkMmLXmv2TCLzv8yjtH/J4HC/xp3 uv8KRnHlABcjKwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAApTGZDWZ/U9HG88/+Axfj/gsPy/yxe g/8EOWD/AyM8hwAAAAESJDYOOXOfqkmOv+A/cZetBSlAVwAAJAcAAAAAAAAAAAAAAAMdV4GALYbH/C6K zf8nf7/+F2ik5wtDaogAFxcLAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADBSbkpcpdj3ecH1/4XI +f9wsN//FUht/wU8ZP8DIzmAAAAAAAAAAAAbNlEcGjROJwAcHAkAAAAAAAAAAAAAAAAAAAAAAAAAABAx SC4lap3hJnGn+htRebkJKkVVAA8PEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP2aGcHC2 6f18wvX/hcj5/1eXxP8JPWT/Bj1l/wMjOYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABUq PwwxWXpNOnWlrVeUw/Jel8X8XZTF5jR0r9oLUIerADlfSwAkJAcAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AABFbYuAgMLy/3vC9f+Cxvj/P3un/wY8ZP8GPmf/AyU9gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAxVHEkU4ayn0eU1fEqkOX/MJnx/z2g8/91vvr/dr78/0aY3v8IZqzsAENzZgAAAAMAAAAAAAAAAAAA AAAAAAAAAAAAAklsh4iExPP/fML1/4LG+P82c5//BT5m/wdBbP8FKUOAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAP1xxJGacybt1vff9NpPi/x2F3v8nkez/N5zx/3K8+v96wf7/YLDz/x6E1f8PaK3ZBzFNJAAA AAAAAAAAAAAAAAAaAB0GMBNPKWRd0Xi66v91vPD/gMT2/zVzoP8GP2n/CERy/wUpQ4AAAAAAAAAAAAAA AAAAAAAAAAAAAAAAJAcubZ+YXa7v/ZDW/f9ftOz/JojY/zmW5f9ms/b/bbf0/2q39v8/oOn/Ip30/yOS 4fITRm1BAAAAAAAAAAAAAAAABSIRLApTIscQeCr7KXeB/zqHtP9tter/KGKM/wZAa/8IRXL/Ayc+hwAT AA0AAAAAAAAAAAAAAAAAAAAALVFoOCR3u+c5mOP/kdn7/5ff/v9/y/z/b7n5/zeQ2v8SfM3/D3W98xR1 u+MikN/wHXW0lA8vTxAAAAAAAAAAAAAAAAAAKBQZB04doglvEPwIYRP/DlM+/ydogP8PQl7/BDRQ/wQz OP8DMBHZAh8AWAAAAAIAAAAAAAAAAAAAAAJdhZmIfsfu/oXR9v+a4f//meD//4LJ/v9Jm+L/BHnN/wBs ue0AP2lcBihDJgxCaD0PL08QAAAAAAAAAAAAAAAAAAAAAAAlByIVQxmyJlsY/Qx4GP8JbRL/B1kF/wVF B/8DMAf/BDoB/wQ4APoCLgCxACQAIwAAAAAAAAAAAAAABmuQpaKf4Pv/m+L//5vi//+Z4P//fcX7/yWF 0/8Bes//AWSq4AAeNiEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABStjZZkwhXX+EWYr/wdf Cf8JcRT/BlQG/wVMAP8FQwD/BD0A/QMoAIkAFwALAAAAAAAAAAAqNTUYlrPEzabk/v+b4v//m+L//5rh //+Byfr/J4TT/wJ3zf8BbrrzAD9sWQAAFwsAGDAVAD9oTABJdFMAMzMFAAAAAAAAAAAAAAAEZIJwk53e 0P06i3P/C2A7/yyERf81azf/BmVG/wtNGv8DNgDcAiMAewARAA8AAAAAAAAAAEFBQR+4ys/er+j//5vi //+b4v//l9/9/1Gp6P8pj+b/DnnR/wF4zf8BarXoAFaTvABcncwBbrrzAn7H5gBPekcAAAABAAAAAAAA AABxVDEkaayitmufhviCl3P/ed7O/5+2i/9FmJv/E2Rt2wAoAKkAHgAqAAAAAQAAAAAAAAAAIiIiD6q2 usHF7f3/neP//5vi//+N1/r/KInX/yON5v8jiuP/DHfL/wV4zP8HeMz/HIHR/zCN2f8IkeT/AWefrQAA AAcAAAAAAAAAAAAAAAALQk0XZVAtX1OCcreO6+T9cu7u/1KvsfwgYGt3AB4AGQATAA0AAAAAAAAAAAAA AAAAAAACjpebiNHq9P6w6P7/neL//5ff/v9TrOf/JIbV/z2Z4P9grOn/W6jq/1yq6/9yvPf/d8D7/zOT 2/wEVId+AAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAOGNcJKmjfrZTvcHxgoBbtko5IB8AAAAAAAAAAAAA AAAAAAAAAAAAAAAAAABoaG04w9Pa59Px/f/D7f3/uer+/6bj/f+Ez/X/bb7t/zuV3f+Eyfj/ktj//5HZ /f+O0/z/Y53K2RQuSSYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQjchFxxSWj4hNzcXAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAACQkJAeWoKOJzeHq9dnz/f/Z8/3/1fL9/7bl+/+R1/j/fMny/5fd /P+Z4P7/Vqzn/zaO1u4+bpNmAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADY2Ng6UnaGLnbvN3pG60+bG5PL82PP9/8rv /v++6/7/vur+/7Xo/f9fos/uDk6FawAkSAcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC4uLgsgMTkfHD5ULYSd sKjS5/D+zeTt/H+kusp4nrfDnLO+w3GCjVwkJEgHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAATVNTK36Ehn1+hIZ3RU1NIREiIg8qKjgSAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAA//////4AA//8AAD//AAA//gAAH/4AAA/+AAAH/AAAA/wAAAP4AAAD+AAAA/gABAP4AAwD+AY +B/gH+AH4B/AA8AfgAOAHwADgA8AA4AGAAeABgA/gAYAAYAGAADABgAA4A4AAPg/AAH8fwAB//+AA/// wAf///gP//////////8oAAAAEAAAACAAAAABACAAAAAAAEAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAEkhtDiBWfC8ZTHUyByZFIQA4VAkLQWorC0NrLQAhNxcAAAACAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAiVqnHougLvrKHu37Q5GbtYNPmRyG2yo3xhrqOkMTHnPAydCVAAAAAIAAAAAAAAAAAAA AAAAAAAAAAAAABhIYRUterPKOZDQ/zeP0P8fZZf+GFiF5yyFxf4lfb7/G3Cu/whBa98DLUhDAAAAAQAA AAAAAAAAAAAAAAAAAAAtbJlJRZjV80ea2P86iMH/QpPO/zyNyP8serL/GmGU+yqDxP4YaaT/B0Bp0gU0 UzEAAAAAAAAAAAAAAAAzM2YFSpDBnVqs6P88gLH/G1B24VCg2/RMo+L/ImaY+A0/ZJgugsDoKYTH/w9X jP4GO2OfADMzBQAAAAAAAAAANmR/HGKo29drt+3/KF+J/QgvSndOlMiyUKDb/BlVgrsIK0YdKXOqmi6I yv4dc7L+CD9qkAAAAAIAAAAAAAAAAENyljFpsOPsdLbm/xpPdfgDLEdLOW2VOj97pm0WQ2EiAAAAASdi jUEue7XrIGiexAg+ZjkAAAAAAAAAAAAAAABWh6hEdrrr9WOj0v8LQWn3AypKSAAAAAAAAAAAf39/AlCF sj84h8izRJbY9lyf2eYmdbSsAEeCJwAAAAAAHAAJN29ycnC04ftYmMf/CEFq9wcxUUgAAAAAAAAAADh0 qDtrs+jeQZ3l/0Ge7f9ltPT/Nprm/Rd0uIMAAAABADQaHQ1kJcsldmb/MG+L/wY7UfoELyJ3ACoABipV VQZXm8ehfMn0/4XP+/88l+H/FHa9yxt4vIoccbM2AAAAAA01KBMsa0PEEGsf/wlXE/8FPwn/BDQBvwAm ABRZcn8Ul8vizpvi//+Fzvn/G4TU/wBippkAOGcbAFiKLgBIbQckSEgHb6iWkEiKa/RVnXX/HmxS8AMx B4AAHwAIf3+JGrPY59ae4///arzw/x+G3f8MeMjyE3S+1hOByuEAZqNDAAAAAER3Zg9rinFafsm+01OQ iY0AJhkUAAAAAEhISAe2y9Kiuen8/pHU9/9Yref/Xa3q/3TA9P9dq+PnGGWZNQAAAAAAAAAAf38AAlJ7 ah9IW1sOAAAAAAAAAAAAAAAAmKOoMrvS3sS/3+7uteP5/5vW9P6CxerwPoe+cypVfwYAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFoc4sWhpytTrTH0KqMqrx2h6S0Tz9ffwgAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIkJCQHAAAAAQAAAAAAAAAAAAAAAOAP JgDAByYAwAMmAMADJgCAASYAgAEmAIADJgCDASYAAwAmAAABJgAAACYAAAAmAIIAJgDHACYA/wEmAP/H JgA= ================================================ FILE: WzComparerR2/MemoryTributary.cs ================================================  using System; using System.Collections.Generic; using System.IO; using System.Runtime.InteropServices; namespace System.IO { /// /// MemoryTributary is A re-implementation of MemoryStream that uses A dynamic list of byte arrays as A backing store, instead of A single byte array, the allocation /// of which will fail for relatively small streams as it requires contiguous memory. /// public class MemoryTributary : Stream /* http://msdn.microsoft.com/en-us/library/system.io.stream.aspx */ { #region Constructors public MemoryTributary() { Position = 0; } public MemoryTributary(byte[] source) { this.Write(source, 0, source.Length); Position = 0; } public MemoryTributary(int length) { SetLength(length); Position = length; byte[] d = block; //access block to prompt the allocation of memory Position = 0; } #endregion #region Status Properties public override bool CanRead { get { return true; } } public override bool CanSeek { get { return true; } } public override bool CanWrite { get { return true; } } #endregion #region Public Properties public override long Length { get { return length; } } public override long Position { get; set; } #endregion #region Members protected long length = 0; protected long blockSize = 65536; protected List blocks = new List(); #endregion #region Internal Properties /* Use these properties to gain access to the appropriate block of memory for the current Position */ /// /// The block of memory currently addressed by Position /// protected byte[] block { get { while (blocks.Count <= blockId) blocks.Add(new byte[blockSize]); return blocks[(int)blockId]; } } /// /// The id of the block currently addressed by Position /// protected long blockId { get { return Position / blockSize; } } /// /// The offset of the byte currently addressed by Position, into the block that contains it /// protected long blockOffset { get { return Position % blockSize; } } #endregion #region Public Stream Methods public override void Flush() { } public override int Read(byte[] buffer, int offset, int count) { long lcount = (long)count; if (lcount < 0) { throw new ArgumentOutOfRangeException("count", lcount, "Number of bytes to copy cannot be negative."); } long remaining = (length - Position); if (lcount > remaining) lcount = remaining; if (buffer == null) { throw new ArgumentNullException("buffer", "Buffer cannot be null."); } if (offset < 0) { throw new ArgumentOutOfRangeException("offset", offset, "Destination offset cannot be negative."); } int read = 0; long copysize = 0; do { copysize = Math.Min(lcount, (blockSize - blockOffset)); Buffer.BlockCopy(block, (int)blockOffset, buffer, offset, (int)copysize); lcount -= copysize; offset += (int)copysize; read += (int)copysize; Position += copysize; } while (lcount > 0); return read; } public override long Seek(long offset, SeekOrigin origin) { switch (origin) { case SeekOrigin.Begin: Position = offset; break; case SeekOrigin.Current: Position += offset; break; case SeekOrigin.End: Position = Length - offset; break; } return Position; } public override void SetLength(long value) { length = value; } public override void Write(byte[] buffer, int offset, int count) { long initialPosition = Position; int copysize; try { do { copysize = Math.Min(count, (int)(blockSize - blockOffset)); EnsureCapacity(Position + copysize); Buffer.BlockCopy(buffer, (int)offset, block, (int)blockOffset, copysize); count -= copysize; offset += copysize; Position += copysize; } while (count > 0); } catch (Exception e) { Position = initialPosition; throw e; } } public override int ReadByte() { if (Position >= length) return -1; byte b = block[blockOffset]; Position++; return b; } public override void WriteByte(byte value) { EnsureCapacity(Position + 1); block[blockOffset] = value; Position++; } protected void EnsureCapacity(long intended_length) { if (intended_length > length) length = (intended_length); } #endregion #region IDispose /* http://msdn.microsoft.com/en-us/library/fs2xkftw.aspx */ protected override void Dispose(bool disposing) { /* We do not currently use unmanaged resources */ base.Dispose(disposing); } #endregion } } ================================================ FILE: WzComparerR2/Patcher/Builder/BuildInstruction.cs ================================================ using System; using System.Collections.Generic; using System.Text; using System.IO; namespace WzComparerR2.Patcher.Builder { /// /// 表示一条重构文件的指令。 /// public class BuildInstruction { public BuildInstruction() : this(BuildType.Unknown) { } public BuildInstruction(BuildType type) { this.Type = type; this.Length = 0; this.FillByte = 0; this.OldFilePosition = 0; } public BuildType Type { get; set; } /// /// 将要更新到新文件的字节长度。 /// public int Length { get; set; } /// /// 填充固定字节的值,只用于RebuildType.FillBytes。 /// public byte FillByte { get; set; } /// /// 从原文件中读取的起始偏移,只用于RebuildType.FromOldFile。 /// public int OldFilePosition { get; set; } } } ================================================ FILE: WzComparerR2/Patcher/Builder/BuildType.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace WzComparerR2.Patcher.Builder { public enum BuildType { Unknown = 0, FromPatcher = 1, FillBytes = 2, FromOldFile = 3, Ending = 4 } } ================================================ FILE: WzComparerR2/Patcher/Builder/CheckSum.cs ================================================ using System; using System.Buffers; using System.Collections.Generic; using System.IO; using System.Runtime.InteropServices; using System.Threading; #if NET6_0_OR_GREATER using System.Runtime.Intrinsics; using System.Runtime.Intrinsics.X86; #endif namespace WzComparerR2.Patcher.Builder { public class CheckSum { static CheckSum() { if (sbox == null) { sbox = new uint[SizeSBox * 8]; Generatesbox(sbox); } } private CheckSum() { } private const int PolyNomial = 0x04C11DB7; private const uint TopBit = 0x80000000; private const int SizeSBox = 256; private const int TableNum = 8; private static uint[] sbox; private static void Generatesbox(uint[] sbox) { uint remain; uint i; int bit; for (i = 0; i < SizeSBox; i++) { remain = i << 0x18; for (bit = 0; bit < 8; bit++) { if ((remain & TopBit) != 0) { remain = (remain << 1) ^ PolyNomial; } else { remain = (remain << 1); } } sbox[i] = remain; } for (; i < sbox.Length; i++) { uint r = sbox[i - 256]; sbox[i] = sbox[r >> 24] ^ (r << 8); } } public static uint ComputeHash(Stream stream, long length, CancellationToken cancellationToken = default) { return ComputeHash(stream, length, 0, cancellationToken); } public static uint ComputeHash(Stream stream, long length, uint crc, CancellationToken cancellationToken = default) { var buffer = ArrayPool.Shared.Rent(0x4000); try { while (length > 0) { cancellationToken.ThrowIfCancellationRequested(); int count = stream.Read(buffer, 0, (int)Math.Min(buffer.Length, length)); if (count == 0) { break; } crc = ComputeHash(buffer, 0, count, crc); length -= count; } return crc; } finally { ArrayPool.Shared.Return(buffer); } } public static uint ComputeHash(byte[] data, int startIndex, int count, uint crc) { return ComputeHash(data.AsSpan(startIndex, count), crc); } public static uint ComputeHash(ReadOnlySpan data, uint crc) { #if NET6_0_OR_GREATER // reference paper: Fast CRC Computation for Generic Polynomials Using PCLMULQDQ Instruction if (data.Length >= 32 && Sse42.IsSupported && Pclmulqdq.IsSupported) { unsafe { Vector128 k4k3 = Vector128.Create(0xE8A45605, 0xC5B9CD4C); Vector128 reverseMask = Vector128.Create((byte)15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0); Vector128 x0, x1, x2; fixed (byte* pData = data) x0 = Sse2.LoadVector128(pData); x0 = Ssse3.Shuffle(x0, reverseMask); x0 = Sse2.Xor(x0, Vector128.Create(0, 0, 0, crc).AsByte()); data = data.Slice(16); while (data.Length >= 16) { x1 = Pclmulqdq.CarrylessMultiply(x0.AsInt64(), k4k3, 0x00).AsByte(); x0 = Pclmulqdq.CarrylessMultiply(x0.AsInt64(), k4k3, 0x11).AsByte(); fixed (byte* pData = data) x2 = Sse2.LoadVector128(pData); x2 = Ssse3.Shuffle(x2, reverseMask); x0 = Sse2.Xor(x0, x1); x0 = Sse2.Xor(x0, x2); data = data.Slice(16); } x0 = Ssse3.Shuffle(x0, reverseMask); Span rollingData = stackalloc byte[16]; fixed (byte* pData = rollingData) Sse2.Store(pData, x0); crc = 0; foreach (var b in rollingData) crc = (crc << 8) ^ CheckSum.sbox[(crc >> 24) ^ b]; } } #endif if (data.Length >= 8) { Span pcrc = stackalloc uint[1] { crc }; Span crcBytes = MemoryMarshal.AsBytes(pcrc); ref uint crcRef = ref pcrc[0]; ReadOnlySpan table = sbox.AsSpan(); while (data.Length >= 8) { crcRef ^= (uint)((data[0] << 24) + (data[1] << 16) + (data[2] << 8) + data[3]); crcRef = table[crcBytes[3] + 0x700] ^ table[crcBytes[2] + 0x600] ^ table[crcBytes[1] + 0x500] ^ table[crcBytes[0] + 0x400] ^ table[data[4] + 0x300] ^ table[data[5] + 0x200] ^ table[data[6] + 0x100] ^ table[data[7] + 0x000]; data = data.Slice(8); } crc = crcRef; } foreach (var b in data) crc = (crc << 8) ^ CheckSum.sbox[(crc >> 24) ^ b]; return crc; } } } ================================================ FILE: WzComparerR2/Patcher/Builder/InflateStream.cs ================================================ using System; using System.Buffers; using System.Collections.Generic; using System.IO; using System.IO.Compression; namespace WzComparerR2.Patcher.Builder { public class InflateStream : Stream { public InflateStream(Stream stream, bool buffered = false, bool leaveOpen = false) { this.BaseStream = stream; this.baseStartPosition = stream.Position; this.buffered = buffered; this.leaveOpen = leaveOpen; this.Reset(); } public Stream BaseStream { get; private set; } private readonly long baseStartPosition; private readonly bool buffered; private readonly bool leaveOpen; private Stream deflateStream; private long position; public override long Position { get => this.position; set => this.Seek(value, SeekOrigin.Begin); } public override long Seek(long offset, SeekOrigin origin) { long offsetFromBegin = origin switch { SeekOrigin.Begin => offset, SeekOrigin.Current => offset + this.position, _ => throw new NotSupportedException(), }; if (offsetFromBegin < this.position) { this.Reset(); this.Skip(offsetFromBegin); } else if (offsetFromBegin > this.position) { this.Skip(offsetFromBegin - this.position); } return this.position; } #if NET6_0_OR_GREATER public override int Read(Span buffer) { int length = this.deflateStream.Read(buffer); this.position += length; return length; } #endif public override int Read(byte[] array, int offset, int count) { int length = this.deflateStream.Read(array, offset, count); this.position += length; return length; } public override int ReadByte() { int b = this.deflateStream.ReadByte(); if (b > -1) { this.position += 1; } return b; } public override void Flush() { } public override bool CanSeek => true; public override bool CanRead => true; public override bool CanWrite => false; public override long Length => throw new NotSupportedException(); public override void SetLength(long value) => throw new NotSupportedException(); public override void Write(byte[] buffer, int offset, int count) => throw new NotSupportedException(); public override void WriteByte(byte value) => throw new NotSupportedException(); public void Reset() { if (this.deflateStream != null && this.position == 0) { return; } this.BaseStream.Position = this.baseStartPosition; var deflateStream = new DeflateStream(this.BaseStream, CompressionMode.Decompress, true); if (buffered) { this.deflateStream = new BufferedStream(deflateStream); } else { this.deflateStream = deflateStream; } this.position = 0; } protected override void Dispose(bool disposing) { if (disposing) { this.deflateStream?.Dispose(); if (!this.leaveOpen) { this.BaseStream?.Dispose(); } } } private void Skip(long length) { if (length < 0) { throw new ArgumentOutOfRangeException(nameof(length)); } if (length == 0) { return; } var pool = ArrayPool.Shared; byte[] buffer = pool.Rent(4096); while (length > 0) { int len = this.Read(buffer, 0, (int)Math.Min(length, buffer.Length)); if (len == 0) { break; } length -= len; } pool.Return(buffer); } } } ================================================ FILE: WzComparerR2/Patcher/Builder/PatchPart.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace WzComparerR2.Patcher.Builder { public class PatchPart { public PatchPart() { this.Type = PatchType.Unknown; } public string FileName { get; set; } public PatchType Type { get; set; } public int FileLength { get; set; } public uint Checksum { get; set; } public uint OldChecksum { get; set; } } } ================================================ FILE: WzComparerR2/Patcher/Builder/PatchType.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace WzComparerR2.Patcher.Builder { public enum PatchType { Unknown = -1, Create = 0, Rebuild = 1, Delete = 2, } } ================================================ FILE: WzComparerR2/Patcher/Builder/StreamUtils.cs ================================================ using System; using System.Collections.Generic; using System.Text; using System.IO; namespace WzComparerR2.Patcher.Builder { public class StreamUtils { public static void CopyStream(Stream src, Stream dest) { CopyStream(src, dest, Int32.MaxValue); } public static void CopyStream(Stream src, Stream dest, int length) { byte[] buffer = new byte[0x8000]; while (length > 0) { int count = src.Read(buffer, 0, Math.Min(buffer.Length, length)); if (count == 0) break; dest.Write(buffer, 0, count); length -= count; } } public static void FillStream(Stream stream, int length, byte data) { byte[] buffer = new byte[length]; for (int i = 0; i < length; i++) { buffer[i] = data; } stream.Write(buffer, 0, length); } public static uint MoveStreamWithCrc32(Stream src, Stream dest, int length, uint crc) { byte[] buffer = new byte[0x8000]; while (length > 0) { int count = src.Read(buffer, 0, Math.Min(buffer.Length, length)); if (count == 0) break; crc = CheckSum.ComputeHash(buffer, 0, count, crc); dest.Write(buffer, 0, count); length -= count; } return crc; } public static uint FillStreamWithCrc32(Stream stream, int length, byte data, uint crc) { byte[] buffer = new byte[length]; for (int i = 0; i < length; i++) { buffer[i] = data; } crc = CheckSum.ComputeHash(buffer, 0, length, crc); stream.Write(buffer, 0, length); return crc; } } } ================================================ FILE: WzComparerR2/Patcher/Builder/WzPatcherReader.cs ================================================ using System; using System.Collections.Generic; using System.Text; using System.IO; namespace WzComparerR2.Patcher.Builder { public class WzPatcherReader { public WzPatcherReader(Stream input) { if (input == null) { throw new ArgumentNullException("output"); } this.BaseStream = input; this.reader = new BinaryReader(input); } public Stream BaseStream { get; private set; } private BinaryReader reader; public PatchPart ReadPart() { PatchPart part = new PatchPart(); int switchByte = 0; StringBuilder sb = new StringBuilder(); while ((switchByte = reader.BaseStream.ReadByte()) > 2) { sb.Append((char)switchByte); } if (switchByte == -1) //失败 { return null; } part.Type = (PatchType)switchByte; part.FileName = sb.ToString(); switch (part.Type) { case PatchType.Create: if (Path.HasExtension(part.FileName)) { part.FileLength = reader.ReadInt32(); part.Checksum = reader.ReadUInt32(); } break; case PatchType.Rebuild: part.OldChecksum = reader.ReadUInt32(); part.Checksum = reader.ReadUInt32(); break; case PatchType.Delete: break; } return part; } public BuildInstruction ReadInst() { BuildInstruction inst = new BuildInstruction(); inst.Length = 0; inst.FillByte = 0; inst.OldFilePosition = 0; uint command = reader.ReadUInt32(); if (command == 0) { inst.Type = BuildType.Ending; } else { switch (command >> 0x1c) { case 0x08: inst.Type = BuildType.FromPatcher; inst.Length = (int)command & 0x0fffffff; break; case 0x0c: inst.Type = BuildType.FillBytes; inst.Length = (int)(command & 0x0fffff00) >> 8; inst.FillByte = (byte)(command & 0xff); break; default: inst.Type = BuildType.FromOldFile; inst.Length = (int)command; inst.OldFilePosition = reader.ReadInt32(); break; } } return inst; } public void ReadContent(Stream destStream, int length) { StreamUtils.CopyStream(this.BaseStream, destStream, length); } public int ReadContent(byte[] buffer, int offset, int count) { return this.BaseStream.Read(buffer, offset, count); } public int ReadInt32() { return this.reader.ReadInt32(); } public uint ReadUInt32() { return this.reader.ReadUInt32(); } } } ================================================ FILE: WzComparerR2/Patcher/Builder/WzPatcherWriter.cs ================================================ using System; using System.Collections.Generic; using System.Text; using System.IO; using System.IO.Compression; namespace WzComparerR2.Patcher.Builder { public class WzPatcherWriter { public WzPatcherWriter(Stream output) { if( output == null) { throw new ArgumentNullException("output"); } this.BaseStream = output; this.writer = new BinaryWriter(output, Encoding.ASCII); } public Stream BaseStream { get; private set; } private BinaryWriter writer; private DeflateStream deflateStream; private BinaryWriter deflateWriter; private MemoryTributary tempStream; private bool useTempStream; private long baseStreamPosition; public void Begin() { this.writer.Write(Encoding.ASCII.GetBytes("WzPatch\x1A")); this.writer.Write(2); //version if (this.BaseStream.CanSeek && this.BaseStream.CanRead) { this.writer.Write(0u); //crc 回头计算 this.useTempStream = false; this.deflateStream = new DeflateStream(this.BaseStream, CompressionMode.Compress, true); this.baseStreamPosition = this.BaseStream.Position; //zlib header this.BaseStream.WriteByte(0x78); this.BaseStream.WriteByte(0xDA); } else { this.useTempStream = true; this.tempStream = new MemoryTributary(); this.deflateStream = new DeflateStream(this.tempStream, CompressionMode.Compress, true); this.baseStreamPosition = 0; //zlib header this.tempStream.WriteByte(0x78); this.tempStream.WriteByte(0xDA); } this.deflateWriter = new BinaryWriter(this.deflateStream, Encoding.ASCII); } public void WritePart(PatchPart part) { this.deflateWriter.Write(part.FileName.ToCharArray()); this.deflateWriter.Write((byte)part.Type); //尾部标识 switch (part.Type) { case PatchType.Create: if (Path.HasExtension(part.FileName)) { this.deflateWriter.Write(part.FileLength); this.deflateWriter.Write(part.Checksum); } break; case PatchType.Rebuild: this.deflateWriter.Write(part.OldChecksum); this.deflateWriter.Write(part.Checksum); break; case PatchType.Delete: break; } } public void WriteInst(BuildInstruction inst) { switch (inst.Type) { case BuildType.FromPatcher: this.deflateWriter.Write((0x08 << 0x1c) | (inst.Length & 0x0fffffff)); break; case BuildType.FillBytes: this.deflateWriter.Write((0x0c << 0x1c) | ((inst.Length & 0x000fffff) << 0x08) | (inst.FillByte & 0xff)); break; case BuildType.FromOldFile: int flag = (inst.Length >> 0x1c); if (flag == 0x08 || flag == 0x0c) { throw new Exception("errrrrrrrrr"); } this.deflateWriter.Write(inst.Length); this.deflateWriter.Write(inst.OldFilePosition); break; case BuildType.Ending: this.deflateWriter.Write(0); break; } } public void WriteContent(Stream contentStream, int length) { StreamUtils.CopyStream(contentStream, this.deflateStream, length); } public void WriteContent(byte[] buffer, int offset, int count) { MemoryStream ms = new MemoryStream(buffer, offset, count); WriteContent(ms, (int)ms.Length); } public void End() { this.deflateStream.Flush(); this.deflateStream.Close(); this.deflateWriter.Close(); if (!this.useTempStream) { long curPos = this.BaseStream.Position; this.BaseStream.Seek(this.baseStreamPosition, SeekOrigin.Begin); uint crc = CheckSum.ComputeHash(this.BaseStream, curPos - this.baseStreamPosition); this.BaseStream.Seek(this.baseStreamPosition - 4, SeekOrigin.Begin); this.writer.Write(crc); this.BaseStream.Seek(curPos, SeekOrigin.Begin); } else { this.tempStream.Seek(0, SeekOrigin.Begin); uint crc = CheckSum.ComputeHash(this.tempStream, this.tempStream.Length); this.writer.Write(crc); this.tempStream.Seek(0, SeekOrigin.Begin); StreamUtils.CopyStream(this.tempStream, this.BaseStream, (int)this.tempStream.Length); this.tempStream.Close(); this.tempStream = null; } } } } ================================================ FILE: WzComparerR2/Patcher/PatchPartContext.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace WzComparerR2.Patcher { public class PatchPartContext { public PatchPartContext(string fileName, long offset, int type) { this.Offset = offset; this.FileName = fileName; this.Type = type; } private readonly HashSet dependencyFiles = new HashSet(); public long Offset { get; set; } public string FileName { get; private set; } public int Type { get; private set; } public int NewFileLength { get; set; } public uint? OldChecksum { get; set; } public uint? OldChecksumActual { get; set; } public uint NewChecksum { get; set; } public string TempFilePath { get; set; } public string OldFilePath { get; set; } public int Action0 { get; set; } public int Action1 { get; set; } public int Action2 { get; set; } public ISet DependencyFiles { get; private set; } = new HashSet(); } } ================================================ FILE: WzComparerR2/Patcher/PatcherSetting.cs ================================================ using System; using System.ComponentModel; using System.Configuration; using System.Globalization; using System.Linq; namespace WzComparerR2.Patcher { public class PatcherSetting : ConfigurationElement { public PatcherSetting() { } public PatcherSetting(string serverName) : this(serverName, null, 1) { this.ServerName = serverName; } public PatcherSetting(string serverName, string urlFormat) : this(serverName, urlFormat, 1) { this.UrlFormat = urlFormat; } public PatcherSetting(string serverName, string urlFormat, int maxVersion) { this.UrlFormat = urlFormat; this.ServerName = serverName; this.MaxVersion = maxVersion; } [ConfigurationProperty("serverName", IsRequired = true)] public string ServerName { get { return (string)this["serverName"]; } set { this["serverName"] = value; } } [ConfigurationProperty("urlFormat")] public string UrlFormat { get { return (string)this["urlFormat"]; } set { this["urlFormat"] = value; } } [Obsolete("Deprecated in favor of 'Versions'")] [ConfigurationProperty("version0")] public int? Version0 { get { return (int?)this["version0"]; } set { this["version0"] = value; } } [Obsolete("Deprecated in favor of 'Versions'")] [ConfigurationProperty("version1")] public int? Version1 { get { return (int?)this["version1"]; } set { this["version1"] = value; } } [ConfigurationProperty("maxVersion")] public int MaxVersion { get { return (int)this["maxVersion"]; } set { this["maxVersion"] = value; } } [ConfigurationProperty("versions")] [TypeConverter(typeof(IntArrayToStringConverter))] public int[] Versions { get { return (int[])this["versions"]; } set { this["versions"] = value; } } public string Url { get { if (this.UrlFormat != null) { if (this.Versions != null) { return string.Format(this.UrlFormat, this.Versions.Cast().ToArray()); } else if (this.MaxVersion > 0) { return string.Format(this.UrlFormat, Enumerable.Repeat(0, this.MaxVersion).ToArray()); } else { return this.UrlFormat; } } return null; } } public override string ToString() { return this.ServerName; } public class IntArrayToStringConverter : TypeConverter { public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) { return sourceType == typeof(string); } public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) { return destinationType == typeof(int[]); } public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { if (value is string s) { return s.Split(',').Select(segment => int.Parse(segment, NumberStyles.Integer, CultureInfo.InvariantCulture)).ToArray(); } return null; } public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) { if (value is int[] array && destinationType == typeof(string)) { return string.Join(",", array.Select(v => v.ToString(CultureInfo.InvariantCulture))); } return null; } } } } ================================================ FILE: WzComparerR2/Patcher/PatcherSettingCollection.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Configuration; using WzComparerR2.Config; namespace WzComparerR2.Patcher { public class PatcherSettingCollection : ConfigItemCollectionBase { public PatcherSettingCollection() { } protected override object GetElementKey(ConfigurationElement element) { return (element as PatcherSetting).ServerName; } } } ================================================ FILE: WzComparerR2/Patcher/PatchingEventArgs.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace WzComparerR2.Patcher { public class PatchingEventArgs : EventArgs { public PatchingEventArgs(PatchPartContext part, PatchingState state) : this(part, state, 0) { } public PatchingEventArgs(PatchPartContext part, PatchingState state, long currentFileLength) { this.part = part; this.state = state; this.currentFileLen = currentFileLength; } private PatchPartContext part; private PatchingState state; private long currentFileLen; public PatchPartContext Part { get { return part; } } public PatchingState State { get { return state; } } public long CurrentFileLength { get { return currentFileLen; } } } } ================================================ FILE: WzComparerR2/Patcher/PatchingState.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace WzComparerR2.Patcher { public enum PatchingState { PatchStart, VerifyOldChecksumBegin, VerifyOldChecksumEnd, VerifyNewChecksumBegin, VerifyNewChecksumEnd, TempFileCreated, TempFileBuildProcessChanged, TempFileClosed, PrepareVerifyOldChecksumBegin, PrepareVerifyOldChecksumEnd, ApplyFile, FileSkipped, } } ================================================ FILE: WzComparerR2/Patcher/ReversePatcherBuilder.cs ================================================ using System; using System.Collections.Generic; using System.Text; using WzComparerR2.Patcher.Builder; using System.IO; using System.IO.Compression; namespace WzComparerR2.Patcher { public class ReversePatcherBuilder { public ReversePatcherBuilder() { } public string msDir; public string outputFileName; public string patchFileName; public void Build() { List preReverse = new List(); using (FileStream fs = new FileStream(patchFileName, FileMode.Open, FileAccess.Read)) { fs.Position = 18; InflateStream stream = new InflateStream(fs, true); WzPatcherReader reader = new WzPatcherReader(stream); PatchPart filePart; PatchPart reversePart; while ((filePart = reader.ReadPart()) != null) { switch (filePart.Type) { case PatchType.Create: if (filePart.FileLength > 0) { stream.Seek(filePart.FileLength, SeekOrigin.Current); //在原文件夹寻找同名文件 string oldFile = Path.Combine(msDir, filePart.FileName); if (File.Exists(oldFile)) { reversePart = new PatchPart() { Type = PatchType.Create }; reversePart.FileName = filePart.FileName; reversePart.FileLength = (int)new FileInfo(oldFile).Length; preReverse.Add(new FileReversePart(reversePart)); } else { reversePart = new PatchPart() { Type = PatchType.Delete }; reversePart.FileName = filePart.FileName; preReverse.Add(new FileReversePart(reversePart)); } } break; case PatchType.Rebuild: reversePart = new PatchPart() { Type = PatchType.Rebuild, OldChecksum = filePart.Checksum, Checksum = filePart.OldChecksum, FileName = filePart.FileName }; List instList = new List(); BuildInstruction inst; int filePos = 0; while ((inst = reader.ReadInst())?.Type != null) { if (inst.Type == BuildType.Ending) { break; } switch (inst.Type) { case BuildType.FromPatcher: stream.Seek(inst.Length, SeekOrigin.Current); break; case BuildType.FillBytes: break; case BuildType.FromOldFile: instList.Add(new FileReverseInst() { Inst = inst, NewFilePosition = filePos }); break; } filePos += inst.Length; } preReverse.Add(new FileReversePart(reversePart) { InstList = instList }); break; case PatchType.Delete: { string oldFile = Path.Combine(msDir, filePart.FileName); if (File.Exists(oldFile)) { reversePart = new PatchPart() { Type = PatchType.Create }; reversePart.FileName = filePart.FileName; reversePart.FileLength = (int)new FileInfo(oldFile).Length; preReverse.Add(new FileReversePart(reversePart)); } } break; } }//end while }//end using preReverse.Sort(); using (FileStream dest = new FileStream(outputFileName, FileMode.Create)) { WzPatcherWriter writer = new WzPatcherWriter(dest); writer.Begin(); foreach (var part in preReverse) { string oldFileName = Path.Combine(msDir, part.Part.FileName); switch (part.Part.Type) { case PatchType.Create: //计算hash copy文件 using (FileStream oldFs = new FileStream(oldFileName, FileMode.Open, FileAccess.Read, FileShare.Read)) { part.Part.FileLength = (int)oldFs.Length; part.Part.Checksum = CheckSum.ComputeHash(oldFs, part.Part.FileLength); oldFs.Position = 0; writer.WritePart(part.Part); writer.WriteContent(oldFs, part.Part.FileLength); } break; case PatchType.Rebuild: writer.WritePart(part.Part); using (FileStream oldFs = new FileStream(oldFileName, FileMode.Open, FileAccess.Read, FileShare.Read)) { //计算指令 var instList = Work(part.InstList, (int)oldFs.Length); //开始执行 foreach (var inst in instList) { switch (inst.Inst.Type) { case BuildType.FromOldFile: //参数反转 inst.Inst.OldFilePosition = inst.NewFilePosition; writer.WriteInst(inst.Inst); break; case BuildType.FromPatcher: //go 待优化 writer.WriteInst(inst.Inst); oldFs.Position = inst.NewFilePosition; writer.WriteContent(oldFs, inst.Inst.Length); break; } } //结束执行 writer.WriteInst(new BuildInstruction(BuildType.Ending)); } break; case PatchType.Delete: writer.WritePart(part.Part); break; } } writer.End(); } } private IEnumerable Work(List instList, int oldFileLength) { //筛选两个文件共同部分 List temp = new List(instList); //排序 temp.Sort((a, b) => { BuildInstruction a1 = a.Inst, b1 = b.Inst; int compare = a1.OldFilePosition.CompareTo(b1.OldFilePosition); if (compare == 0) { compare = -a1.Length.CompareTo(b1.Length); } return compare; }); //进链表 LinkedList reverseList = new LinkedList(); foreach (var reverse in temp) { if (reverseList.Count <= 0) { reverseList.AddFirst(reverse); } else { BuildInstruction prev = reverseList.Last.Value.Inst; BuildInstruction cur = reverse.Inst; if (cur.OldFilePosition <= prev.OldFilePosition) //过滤相等 { continue; } if (cur.OldFilePosition < prev.OldFilePosition + prev.Length) //有重叠部分 { int newLength = (cur.OldFilePosition + cur.Length) - (prev.OldFilePosition + prev.Length); if (newLength <= 0) //完全包含 { continue; } //调整不重叠 reverse.NewFilePosition += cur.Length - newLength; cur.OldFilePosition = (cur.OldFilePosition + cur.Length) - newLength; cur.Length = newLength; } reverseList.AddLast(reverse); } } //链表填充 if (reverseList.Count <= 0) { //怎么可能呢闹呢新建文件吧 return null; } //填充不存在的区块 int totalLength = 0; for (var reverse = reverseList.First; reverse != null; reverse = reverse.Next) //懒得写while { BuildInstruction prev = null; BuildInstruction cur = reverse.Value.Inst; if (reverse.Previous == null) //如果是first 构造一个虚指令 { prev = new BuildInstruction(BuildType.FromOldFile); } else { prev = reverse.Previous.Value.Inst; } int newLength = cur.OldFilePosition - (prev.OldFilePosition + prev.Length); if (newLength > 0) //如果中间缺失 添加一块原区段 { reverseList.AddBefore(reverse, new FileReverseInst() { Inst = new BuildInstruction(BuildType.FromPatcher) { Length = newLength }, NewFilePosition = cur.OldFilePosition - newLength }); totalLength += newLength; } else if (newLength < 0) { throw new Exception("?????"); } totalLength += cur.Length; } //补充尾部区块 BuildInstruction last = reverseList.Last.Value.Inst; if (last.Type == BuildType.FromOldFile) { int newLength = oldFileLength - (last.OldFilePosition + last.Length); if (newLength > 0) { reverseList.AddLast(new FileReverseInst() { Inst = new BuildInstruction(BuildType.FromPatcher) { Length = newLength }, NewFilePosition = last.OldFilePosition + last.Length }); totalLength += newLength; } else if (newLength < 0) { throw new Exception("?????"); } } return reverseList; } private class FileReverseInst { public int NewFilePosition { get; set; } public BuildInstruction Inst { get; set; } } private class FileReversePart : IComparable { public FileReversePart(PatchPart part) { this.Part = part; } public PatchPart Part { get; set; } public List InstList { get; set; } int IComparable.CompareTo(FileReversePart other) { int comp = ((int)this.Part.Type).CompareTo((int)other.Part.Type); if (comp == 0) { if (this.Part.Type == PatchType.Create) { if (this.Part.FileLength == 0) return -1; else if (other.Part.FileLength == 0) return 1; } comp = this.Part.FileName.CompareTo(other.Part.FileName); } return comp; } } } } ================================================ FILE: WzComparerR2/Patcher/WzPatcher.cs ================================================ using System; using System.Buffers; using System.Collections.Generic; using System.IO; using System.Text; using System.IO.Compression; using System.Threading; using System.Threading.Tasks; using WzComparerR2.Patcher.Builder; using PartialStream = WzComparerR2.WzLib.Utilities.PartialStream; namespace WzComparerR2.Patcher { public class WzPatcher : IDisposable { public WzPatcher(string fileName) { this.patchFile = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read, 0x4000, FileOptions.Asynchronous | FileOptions.RandomAccess); this.NoticeEncoding = Encoding.Default; this.ThrowOnValidationFailed = true; } private const int MAX_PATH = 260; private FileStream patchFile; private PartialStream patchBlock; private InflateStream inflateStream; private string noticeText; private List patchParts; private Dictionary oldFileHash; public Encoding NoticeEncoding { get; set; } public List PatchParts { get { return patchParts; } } public string NoticeText { get { return noticeText; } } public Dictionary OldFileHash { get { return oldFileHash; } } public bool? IsKMST1125Format { get; private set; } public bool ThrowOnValidationFailed { get; set; } public event EventHandler PatchingStateChanged; /// /// 验证并初始化补丁解压流。 /// public void OpenDecompress(CancellationToken cancellationToken) { var patchBlock = TrySplit(this.patchFile); if (patchBlock == null) { throw new Exception("Decompress Error, cannot find patch block from the stream."); } BinaryReader r = new BinaryReader(patchBlock); patchBlock.Seek(8, SeekOrigin.Begin); int ver = r.ReadInt32(); uint checkSum0 = r.ReadUInt32(); uint checkSum1 = CheckSum.ComputeHash(patchBlock, patchBlock.Length - 0x10, cancellationToken); VerifyCheckSum(checkSum0, checkSum1, "PatchFile", "0"); patchBlock.Seek(16, SeekOrigin.Begin); byte lb = r.ReadByte(), hb = r.ReadByte(); if (!(lb == 0x78 && (lb * 0x100 + hb) % 31 == 0)) // zlib头标识 没有就把这两字节都当数据段好了.. { patchBlock.Seek(-2, SeekOrigin.Current); } #if NET6_0_OR_GREATER // wrap InflateStream with BufferedStream for better performance in net6+ bool buffered = true; #else bool buffered = false; #endif this.patchBlock = patchBlock; this.inflateStream = new InflateStream(patchBlock, buffered); } private PartialStream TrySplit(Stream metaStream) { metaStream.Seek(0, SeekOrigin.Begin); BinaryReader r = new BinaryReader(metaStream); bool TryCheckFileEnding(out PartialStream patchBlock, out string noticeText) { metaStream.Seek(-4, SeekOrigin.End); uint check = r.ReadUInt32(); if (check != 0xf2f7fbf3) // f3 fb f7 f2 { patchBlock = null; noticeText = null; return false; } metaStream.Seek(-12, SeekOrigin.End); long patchBlockLength = r.ReadUInt32(); long noticeLength = r.ReadUInt32(); metaStream.Seek(-12 - noticeLength - patchBlockLength, SeekOrigin.End); patchBlock = new PartialStream(metaStream, metaStream.Position, patchBlockLength); metaStream.Seek(patchBlockLength, SeekOrigin.Current); noticeText = this.NoticeEncoding.GetString(r.ReadBytes((int)noticeLength)); return true; } bool TryCheckFileEnding64(out PartialStream patchBlock, out string noticeText) { metaStream.Seek(-8, SeekOrigin.End); ulong check = r.ReadUInt64(); if (check != 0xf2f7fbf3) // f3 fb f7 f2 00 00 00 00 { patchBlock = null; noticeText = null; return false; } metaStream.Seek(-24, SeekOrigin.End); long patchBlockLength = r.ReadInt64(); long noticeLength = r.ReadInt64(); metaStream.Seek(-24 - noticeLength - patchBlockLength, SeekOrigin.End); patchBlock = new PartialStream(metaStream, metaStream.Position, patchBlockLength); metaStream.Seek(patchBlockLength, SeekOrigin.Current); noticeText = this.NoticeEncoding.GetString(r.ReadBytes((int)noticeLength)); return true; } PartialStream patchBlock; string noticeText; if (r.ReadUInt16() == 0x5a4d)//"MZ" { if (!(TryCheckFileEnding(out patchBlock, out noticeText) || TryCheckFileEnding64(out patchBlock, out noticeText))) { return null; } } else { // for TMS264 patcher, also check file ending if (!TryCheckFileEnding64(out patchBlock, out noticeText)) { patchBlock = new PartialStream(metaStream, 0, metaStream.Length); noticeText = null; } } // check file header patchBlock.Seek(0, SeekOrigin.Begin); r = new BinaryReader(patchBlock); if (!r.ReadBytes(8).AsSpan().SequenceEqual("WzPatch\x1A"u8)) { return null; } this.noticeText = noticeText; patchBlock.Seek(0, SeekOrigin.Begin); return patchBlock; } public long PrePatch(CancellationToken cancellationToken) { if (this.inflateStream == null) { this.OpenDecompress(cancellationToken); } else { this.inflateStream.Reset(); } var patchParts = new List(); var r = new BinaryReader(this.inflateStream); if (this.TryReadKMST1125FileHashList(r, out var fileHashMap)) { this.oldFileHash = fileHashMap; this.IsKMST1125Format = true; } else { this.IsKMST1125Format = false; // reset file cursor this.inflateStream.Reset(); } while (true) { cancellationToken.ThrowIfCancellationRequested(); PatchPartContext part = ReadPatchPart(r); if (part == null) { break; } if (this.IsKMST1125Format.Value && this.oldFileHash.TryGetValue(part.FileName, out var fileHash)) { part.OldChecksum = fileHash.Hash; } patchParts.Add(part); //跳过当前段 switch (part.Type) { case 0: if (part.NewFileLength > 0) { this.inflateStream.Seek(part.NewFileLength, SeekOrigin.Current); } break; case 1: { part.NewFileLength = CalcNewFileLength(part, r); } break; case 2: break; } } this.patchParts = patchParts; return this.inflateStream.Position; } private PatchPartContext ReadPatchPart(BinaryReader r) { string fileName; int patchType = GetFileName(r, out fileName); PatchPartContext part; switch (patchType) { case 0: if (string.IsNullOrEmpty(Path.GetExtension(fileName))) { part = new PatchPartContext(fileName, -1, 0); } else { int fileLength = r.ReadInt32(); uint checkSum0 = r.ReadUInt32(); part = new PatchPartContext(fileName, this.inflateStream.Position, patchType); part.NewChecksum = checkSum0; part.NewFileLength = fileLength; } break; case 1: { uint? oldCheckSum0 = null; if (!this.IsKMST1125Format.Value) { oldCheckSum0 = r.ReadUInt32(); } uint newCheckSum0 = r.ReadUInt32(); part = new PatchPartContext(fileName, this.inflateStream.Position, patchType); part.OldChecksum = oldCheckSum0; part.NewChecksum = newCheckSum0; } break; case 2: { part = new PatchPartContext(fileName, -1, patchType); } break; case -1: return null; default: throw new Exception("Unknown patch type " + patchType + "."); } return part; } /// /// 对于已经解压的patch文件,向客户端执行更新过程。 /// /// 冒险岛客户端所在文件夹。 public void Patch(string mapleStoryFolder, CancellationToken cancellationToken = default) { this.Patch(mapleStoryFolder, mapleStoryFolder, cancellationToken); } /// /// 对于已经解压的patch文件,向客户端执行更新过程,可以自己指定临时文件的文件夹。 /// /// 冒险岛客户端所在文件夹。 /// 生成临时文件的文件夹。 public void Patch(string mapleStoryFolder, string tempFileFolder, CancellationToken cancellationToken = default) { string tempDir = CreateRandomDir(tempFileFolder); if (this.inflateStream.Position > 0) //重置到初始化 { this.inflateStream.Reset(); } if (this.patchParts == null) //边读取边执行 { BinaryReader r = new BinaryReader(this.inflateStream); if (this.TryReadKMST1125FileHashList(r, out var fileHash)) { this.oldFileHash = fileHash; this.IsKMST1125Format = true; this.ValidateFileHash(mapleStoryFolder, this.ThrowOnValidationFailed, cancellationToken); } else { this.IsKMST1125Format = false; // reset file cursor this.inflateStream.Reset(); r = new BinaryReader(this.inflateStream); } this.patchParts = new List(); while (true) { cancellationToken.ThrowIfCancellationRequested(); PatchPartContext part = ReadPatchPart(r); if (part == null) { break; } patchParts.Add(part); //跳过当前段 switch (part.Type) { case 0: CreateNewFile(part, tempDir); break; case 1: RebuildFile(part, tempDir, mapleStoryFolder); break; case 2: break; } } } else //按照调整后顺序执行 { this.ValidateFileHash(mapleStoryFolder, this.ThrowOnValidationFailed, cancellationToken); foreach (PatchPartContext part in this.patchParts) { switch (part.Type) { case 0: CreateNewFile(part, tempDir); break; case 1: RebuildFile(part, tempDir, mapleStoryFolder, cancellationToken); break; case 2: break; } } } foreach (PatchPartContext part in this.patchParts) { if (part.Type != 2 && !string.IsNullOrEmpty(part.TempFilePath)) { this.OnApplyFile(part); SafeMove(part.TempFilePath, Path.Combine(mapleStoryFolder, part.FileName)); } else if (part.Type == 2) { this.OnApplyFile(part); if (part.FileName.EndsWith("\\")) SafeDeleteDirectory(Path.Combine(mapleStoryFolder, part.FileName)); else SafeDeleteFile(Path.Combine(mapleStoryFolder, part.FileName)); } } SafeDeleteDirectory(tempDir); } private bool TryReadKMST1125FileHashList(BinaryReader r, out Dictionary fileHash) { try { fileHash = new Dictionary(); int count = r.ReadInt32(); for (int i = 0; i < count; i++) { string fn = this.ReadStringWithLength(r, MAX_PATH); uint checksum = r.ReadUInt32(); fileHash.Add(fn, new FileHash { Hash = checksum, }); } return true; } catch { fileHash = null; return false; } } private void ValidateFileHash(string msDir, bool failOnValidationFailed, CancellationToken cancellationToken = default) { if (this.OldFileHash != null && this.OldFileHash.Count > 0) { foreach(var kv in this.OldFileHash) { cancellationToken.ThrowIfCancellationRequested(); // The temporary context is only used for triggering event. var part = new PatchPartContext(kv.Key, -1, -1) { OldFilePath = Path.Combine(msDir, kv.Key) }; this.OnPrepareVerifyOldChecksumBegin(part); uint oldCheckSum = 0; if (!File.Exists(part.OldFilePath)) { kv.Value.VerifyState = FileHashVerifyState.FileNotFound; } else { using (var fs = new FileStream(part.OldFilePath, FileMode.Open, FileAccess.Read, FileShare.Read)) { oldCheckSum = CheckSum.ComputeHash(fs, fs.Length, cancellationToken); } part.OldChecksum = kv.Value.Hash; part.OldChecksumActual = oldCheckSum; kv.Value.ActualHash = oldCheckSum; kv.Value.VerifyState = (oldCheckSum == kv.Value.Hash) ? FileHashVerifyState.Verified : FileHashVerifyState.HashNotMatch; } this.OnPrepareVerifyOldChecksumEnd(part); if (failOnValidationFailed) { VerifyCheckSum(kv.Value.Hash, oldCheckSum, part.FileName, "origin"); } } } } private string CreateRandomDir(string folder) { string randomDir = null; do { randomDir = Path.Combine(folder, Path.GetRandomFileName()); } while (Directory.Exists(randomDir)); Directory.CreateDirectory(randomDir); return randomDir; } public void SafeMove(string srcFile, string dstFile) { if (!File.Exists(srcFile)) return; DirectoryInfo dir = new DirectoryInfo(Path.GetDirectoryName(dstFile)); if (!dir.Exists) dir.Create(); SafeDeleteFile(dstFile); File.Move(srcFile, dstFile); } private static void SafeDeleteFile(string fileName) { FileInfo fi = new FileInfo(fileName); if (fi.Exists) { if ((fi.Attributes & FileAttributes.ReadOnly) != 0) { fi.Attributes = fi.Attributes & (~FileAttributes.ReadOnly); } fi.Delete(); } } private static void SafeDeleteDirectory(string dirName) { DirectoryInfo di = new DirectoryInfo(dirName); if (di.Exists) { if ((di.Attributes & FileAttributes.ReadOnly) != 0) { di.Attributes = di.Attributes & (~FileAttributes.ReadOnly); } foreach (var f in di.GetFileSystemInfos()) { if ((f.Attributes & FileAttributes.ReadOnly) != 0) { f.Attributes = f.Attributes & (~FileAttributes.ReadOnly); } } di.Delete(true); } } private int GetFileName(BinaryReader reader, out string fileName) { int switchByte = 0; StringBuilder sb = new StringBuilder(); while ((switchByte = reader.BaseStream.ReadByte()) > 2) { sb.Append((char)switchByte); } fileName = sb.ToString(); return switchByte; } private string ReadStringWithLength(BinaryReader reader, int? maxLength = null) { int length = reader.ReadInt32(); if (length < 0) { throw new Exception($"Invalid length: {length}"); } if (maxLength != null && length > maxLength) { throw new Exception($"String length exceed the limit ({length} > {maxLength})."); } return Encoding.ASCII.GetString(reader.ReadBytes(length)); } public void CreateNewFile(PatchPartContext part, string tempDir) { this.OnPatchStart(part); string tempFileName = Path.Combine(tempDir, part.FileName); EnsureDirExists(tempFileName); if (part.NewFileLength <= 0) return; this.inflateStream.Seek(part.Offset, SeekOrigin.Begin); FileStream tempFileStream = new FileStream(tempFileName, FileMode.Create, FileAccess.ReadWrite); part.TempFilePath = tempFileName; this.OnTempFileCreated(part); //创建文件同时计算checksum uint checkSum1 = StreamUtils.MoveStreamWithCrc32(this.inflateStream, tempFileStream, part.NewFileLength, 0U); tempFileStream.Flush(); this.OnVerifyNewChecksumBegin(part); VerifyCheckSum(part.NewChecksum, checkSum1, part.FileName, "0"); this.OnVerifyNewChecksumEnd(part); tempFileStream.Close(); this.OnTempFileClosed(part); } public void RebuildFile(PatchPartContext part, string tempDir, string msDir, CancellationToken cancellationToken = default) { this.OnPatchStart(part); string tempFileName = Path.Combine(tempDir, part.FileName); EnsureDirExists(tempFileName); part.OldFilePath = Path.Combine(msDir, part.FileName); var oldWzFiles = new Dictionary(); FileStream tempFileStream = null; bool deferredVerifyFileHash = false; FileStream openFile(string fileName) { if (string.IsNullOrEmpty(fileName)) { return null; } if (!oldWzFiles.TryGetValue(fileName, out var fs)) { if (deferredVerifyFileHash && this.oldFileHash != null) { verifyDependencyFileHash(fileName); } fs = new FileStream(Path.Combine(msDir, fileName), FileMode.Open, FileAccess.Read, FileShare.Read); oldWzFiles.Add(fileName, fs); } return fs; } void closeAllFiles() { foreach(var fs in oldWzFiles.Values) { fs.Close(); } tempFileStream?.Close(); } void verifyDependencyFileHash(string depFileName) { if (!this.OldFileHash.TryGetValue(depFileName, out var fileHash)) { throw new Exception($"OldFileHash does not exist. FileName: {depFileName}"); } switch (fileHash.VerifyState) { case FileHashVerifyState.NotVerified: throw new Exception($"OldFile has not verified. FileName: {depFileName}"); case FileHashVerifyState.FileNotFound: throw new Exception($"OldFile not found. FileName: {depFileName}"); case FileHashVerifyState.HashNotMatch: throw new Exception($"OldFile hash not match. FileName: {depFileName}"); } } void verifyOldFileHash(out bool skipUpdate) { skipUpdate = false; var oldWzFile = openFile(part.FileName); this.OnVerifyOldChecksumBegin(part); uint oldCheckSumActual = CheckSum.ComputeHash(oldWzFile, oldWzFile.Length); this.OnVerifyOldChecksumEnd(part); if (oldCheckSumActual == part.NewChecksum && (part.NewFileLength == 0 || oldWzFile.Length == part.NewFileLength)) // file is updated { skipUpdate = true; } else if (part.OldChecksum != null) { VerifyCheckSum(part.OldChecksum.Value, oldCheckSumActual, part.FileName, "origin"); } } void skipIfNeeded() { if (part.NewFileLength == 0) { this.inflateStream.Seek(part.Offset, SeekOrigin.Begin); BinaryReader r = new BinaryReader(this.inflateStream); // Here is a trick that to leverage CalcNewFileLength method to skip this file. part.NewFileLength = CalcNewFileLength(part, r); } else { // prepatch executed, patcher can jump to the next part outside. } } try { if (this.IsKMST1125Format == true) { // prepatch enabled if (this.OldFileHash != null && part.DependencyFiles.Count > 0) { // verify self if (File.Exists(part.OldFilePath)) { if (this.OldFileHash != null && this.OldFileHash.TryGetValue(part.FileName, out var fileHash)) { if (fileHash.ActualHash == part.NewChecksum) { // file already updated, skip. skipIfNeeded(); OnFileSkipped(part); return; } } else { verifyOldFileHash(out bool skipUpdate); if (skipUpdate) { skipIfNeeded(); OnFileSkipped(part); return; } } } // verify dependencies foreach (var depFileName in part.DependencyFiles) { verifyDependencyFileHash(depFileName); } } else { // no DependencyFiles means prepatch is disabled, check old files on-demand. deferredVerifyFileHash = true; } } else if (part.OldChecksum != null) { verifyOldFileHash(out bool skipUpdate); if (skipUpdate) { skipIfNeeded(); OnFileSkipped(part); return; } } int cmd; //int blockLength; tempFileStream = new FileStream(tempFileName, FileMode.Create, FileAccess.ReadWrite, FileShare.Read, 0x4000); part.TempFilePath = tempFileName; if (part.NewFileLength > 0) //预申请硬盘空间 似乎可以加快读写速度 { tempFileStream.SetLength(part.NewFileLength); tempFileStream.Seek(0, SeekOrigin.Begin); } this.OnTempFileCreated(part); uint newCheckSum1 = 0; this.inflateStream.Seek(part.Offset, SeekOrigin.Begin); BinaryReader r = new BinaryReader(this.inflateStream); double patchProc = 0; const double patchProcReportInverval = 0.005; //v3新增读缓冲 List operList = new List(32768); List readFileOperList = new List(operList.Capacity); MemoryStream msBuffer = new MemoryStream(1024 * 1024 * 64); int preLoadByteCount = 0; while (true) { cancellationToken.ThrowIfCancellationRequested(); cmd = r.ReadInt32(); RebuildFileOperation op = null; if (cmd != 0) { switch ((uint)cmd >> 0x1C) { case 0x08: op = new RebuildFileOperation(0); op.Length = cmd & 0x0fffffff; break; case 0x0c: op = new RebuildFileOperation(1); op.FillByte = (byte)(cmd & 0xff); op.Length = (cmd & 0x0fffff00) >> 8; break; default: op = new RebuildFileOperation(2); op.Length = cmd; op.StartPosition = r.ReadInt32(); op.FromFileName = this.IsKMST1125Format.Value ? this.ReadStringWithLength(r) : part.FileName; break; } } //如果大于 先处理当前所有预读操作 if (cmd == 0 || (operList.Count >= operList.Capacity - 1) || (op.OperType != 1 && (op.Length + preLoadByteCount > msBuffer.Capacity))) { //排序预读原文件 readFileOperList.Sort((left, right) => { int cmp; if ((cmp = string.Compare(left.FromFileName, right.FromFileName, StringComparison.OrdinalIgnoreCase)) != 0) return cmp; return left.StartPosition.CompareTo(right.StartPosition); }); foreach (var readFileOp in readFileOperList) { int position = (int)msBuffer.Position; readFileOp.Flush(openFile(readFileOp.FromFileName), null, null, msBuffer); readFileOp.bufferStartIndex = position; } //向新文件输出 foreach (var tempOp in operList) { newCheckSum1 = tempOp.Flush(openFile(tempOp.FromFileName), r.BaseStream, msBuffer, tempFileStream, newCheckSum1); //计算更新进度 if (part.NewFileLength > 0) { double curProc = 1.0 * tempFileStream.Position / part.NewFileLength; if (curProc - patchProc >= patchProcReportInverval)// || curProc >= 1 - patchProcReportInverval) { this.OnTempFileUpdated(part, tempFileStream.Position);//更新进度改变 patchProc = curProc; } } else { if (tempFileStream.Position - patchProc > 1024 * 1024 * 10) { this.OnTempFileUpdated(part, tempFileStream.Position);//更新进度改变 patchProc = tempFileStream.Position; } } } //重置缓冲区 msBuffer.SetLength(0); preLoadByteCount = 0; operList.Clear(); readFileOperList.Clear(); if (cmd == 0) // 更新结束 这里是出口无误 { break; } } if (op.OperType != 1 && op.Length >= msBuffer.Capacity) //还是大于的话 单独执行 { newCheckSum1 = op.Flush(openFile(op.FromFileName), r.BaseStream, null, tempFileStream, newCheckSum1); } else //直接放进缓冲区里 { op.Index = (ushort)operList.Count; operList.Add(op); switch (op.OperType) { case 0: int position = (int)msBuffer.Position; op.Flush(null, r.BaseStream, null, msBuffer); op.bufferStartIndex = position; break; case 1: continue; case 2: readFileOperList.Add(op); break; } preLoadByteCount += op.Length; } } msBuffer.Dispose(); msBuffer = null; tempFileStream.Flush(); tempFileStream.SetLength(tempFileStream.Position); //设置文件大小为当前长度 closeAllFiles(); this.OnVerifyNewChecksumBegin(part); //tempFileStream.Seek(0, SeekOrigin.Begin); //uint _newCheckSum1 = CheckSum.ComputeHash(tempFileStream, (int)tempFileStream.Length); //新生成文件的hash VerifyCheckSum(part.NewChecksum, newCheckSum1, part.FileName, "new"); this.OnVerifyNewChecksumEnd(part); this.OnTempFileClosed(part); } finally { closeAllFiles(); } } private class RebuildFileOperation { public RebuildFileOperation(byte operType) { this.OperType = operType; this.bufferStartIndex = -1; } public byte OperType; //0-从补丁文件复制 1-填充字节 2-从原文件复制 public byte FillByte; //只有oper1时可用 public ushort Index; //操作索引 public int StartPosition; //只有oper2时可用 原文件起始坐标 public int Length; //输出区块长度 public int bufferStartIndex; //输出缓冲流的起始索引 执行后才有值 public string FromFileName; public void Flush(Stream oldStream, Stream patchFileStream, Stream bufferStream, Stream newStream) { this.Flush(oldStream, patchFileStream, bufferStream, newStream, false, 0U); } public uint Flush(Stream oldStream, Stream patchFileStream, Stream bufferStream, Stream newStream, uint crc) { return this.Flush(oldStream, patchFileStream, bufferStream, newStream, true, crc); } private uint Flush(Stream oldStream, Stream patchFileStream, Stream bufferStream, Stream newStream, bool withCrc, uint crc) { Stream srcStream = null; if (this.bufferStartIndex > -1) //使用缓冲流 { srcStream = bufferStream; srcStream.Seek(this.bufferStartIndex, SeekOrigin.Begin); } else //使用原始流 { switch (this.OperType) { case 0: srcStream = patchFileStream; break; case 2: srcStream = oldStream; srcStream.Seek(this.StartPosition, SeekOrigin.Begin); break; } } //执行更新 switch (this.OperType) { case 0: case 2: if (withCrc) { crc = StreamUtils.MoveStreamWithCrc32(srcStream, newStream, this.Length, crc); } else { StreamUtils.CopyStream(srcStream, newStream, this.Length); } break; case 1: if (withCrc) { crc = StreamUtils.FillStreamWithCrc32(newStream, this.Length, this.FillByte, crc); } else { StreamUtils.FillStream(newStream, this.Length, this.FillByte); } break; } return crc; } } private int CalcNewFileLength(PatchPartContext patchPart, BinaryReader reader) { patchPart.Action0 = patchPart.Action1 = patchPart.Action2 = 0; int length = 0; int cmd; int blockLength; while ((cmd = reader.ReadInt32()) != 0) { switch (((uint)cmd) >> 0x1C) { case 0x08: blockLength = cmd & 0x0fffffff; reader.BaseStream.Seek(blockLength, SeekOrigin.Current); // skip len patchPart.Action0++; break; case 0x0c: blockLength = (cmd & 0x0fffff00) >> 8; patchPart.Action1++; break; default: blockLength = cmd; reader.BaseStream.Seek(4, SeekOrigin.Current); // skip content if (this.IsKMST1125Format == true) { // skip old file name var fromFile = ReadStringWithLength(reader, MAX_PATH); patchPart.DependencyFiles.Add(fromFile); } patchPart.Action2++; break; } length += blockLength; } return length; } private void EnsureDirExists(string fileName) { bool isDirectory; EnsureDirExists(fileName, out isDirectory); } private void EnsureDirExists(string fileName, out bool isDirectory) { string ext = Path.GetExtension(fileName); string dir; if (string.IsNullOrEmpty(ext)) { dir = fileName; isDirectory = true; } else { dir = Path.GetDirectoryName(fileName); isDirectory = false; } if (!Directory.Exists(dir)) Directory.CreateDirectory(dir); } private void VerifyCheckSum(uint expected, uint actual, string fileName, string reason) { if (expected != actual) { throw new Exception(string.Format("CheckSum Error on \"{0}\"({1}). (expected: 0x{2:x8}, actual: 0x{3:x8})", fileName, reason, expected, actual)); } } #region eventhandler protected virtual void OnPatchingStateChanged(PatchingEventArgs e) { if (this.PatchingStateChanged != null) { this.PatchingStateChanged(this, e); } } private void OnPatchStart(PatchPartContext part) { PatchingEventArgs e = new PatchingEventArgs(part, PatchingState.PatchStart); OnPatchingStateChanged(e); } private void OnVerifyOldChecksumBegin(PatchPartContext part) { PatchingEventArgs e = new PatchingEventArgs(part, PatchingState.VerifyOldChecksumBegin); OnPatchingStateChanged(e); } private void OnVerifyOldChecksumEnd(PatchPartContext part) { PatchingEventArgs e = new PatchingEventArgs(part, PatchingState.VerifyOldChecksumEnd); OnPatchingStateChanged(e); } private void OnVerifyNewChecksumBegin(PatchPartContext part) { PatchingEventArgs e = new PatchingEventArgs(part, PatchingState.VerifyNewChecksumBegin); OnPatchingStateChanged(e); } private void OnVerifyNewChecksumEnd(PatchPartContext part) { PatchingEventArgs e = new PatchingEventArgs(part, PatchingState.VerifyNewChecksumEnd); OnPatchingStateChanged(e); } private void OnTempFileCreated(PatchPartContext part) { PatchingEventArgs e = new PatchingEventArgs(part, PatchingState.TempFileCreated); OnPatchingStateChanged(e); } private void OnTempFileUpdated(PatchPartContext part, long filelen) { PatchingEventArgs e = new PatchingEventArgs(part, PatchingState.TempFileBuildProcessChanged, filelen); OnPatchingStateChanged(e); } private void OnTempFileClosed(PatchPartContext part) { PatchingEventArgs e = new PatchingEventArgs(part, PatchingState.TempFileClosed); OnPatchingStateChanged(e); } private void OnPrepareVerifyOldChecksumBegin(PatchPartContext part) { PatchingEventArgs e = new PatchingEventArgs(part, PatchingState.PrepareVerifyOldChecksumBegin); OnPatchingStateChanged(e); } private void OnPrepareVerifyOldChecksumEnd(PatchPartContext part) { PatchingEventArgs e = new PatchingEventArgs(part, PatchingState.PrepareVerifyOldChecksumEnd); OnPatchingStateChanged(e); } private void OnApplyFile(PatchPartContext part) { PatchingEventArgs e = new PatchingEventArgs(part, PatchingState.ApplyFile); OnPatchingStateChanged(e); } private void OnFileSkipped(PatchPartContext part) { PatchingEventArgs e = new PatchingEventArgs(part, PatchingState.FileSkipped); OnPatchingStateChanged(e); } #endregion ~WzPatcher() { this.Dispose(false); } public void Close() { this.Dispose(true); } public void Dispose() { this.Dispose(true); } protected virtual void Dispose(bool disposing) { if (disposing) { if (this.patchFile != null) { this.patchFile.Close(); } } this.patchFile = null; this.patchBlock = null; this.inflateStream = null; } public class FileHash { public uint Hash { get; set; } public uint ActualHash { get; set; } public FileHashVerifyState VerifyState { get; set; } } public enum FileHashVerifyState { NotVerified = 0, Verified, HashNotMatch, FileNotFound, } } } ================================================ FILE: WzComparerR2/PictureBoxEx.cs ================================================ using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using SpineV2 = Spine.V2; using WzComparerR2.Animation; using WzComparerR2.Common; using WzComparerR2.Config; using WzComparerR2.Controls; using WzComparerR2.Encoders; using WzComparerR2.Rendering; using WzComparerR2.WzLib; namespace WzComparerR2 { public class PictureBoxEx : AnimationControl { public PictureBoxEx() : base() { this.AutoAdjustPosition = true; this.sbInfo = new StringBuilder(); } public bool AutoAdjustPosition { get; set; } public string PictureName { get; set; } public bool ShowInfo { get; set; } public override System.Drawing.Font Font { get { return base.Font; } set { base.Font = value; this.xnaFont?.Dispose(); this.xnaFont = new XnaFont(this.GraphicsDevice, value); } } public XnaFont XnaFont { get { if (xnaFont == null && this.Font != null) { this.xnaFont = new XnaFont(this.GraphicsDevice, this.Font); } return this.xnaFont; } } private XnaFont xnaFont; private SpriteBatchEx sprite; private StringBuilder sbInfo; public void ShowImage(Wz_Png png) { this.ShowImage(png, 0); } public void ShowImage(Wz_Png png, int page) { //添加到动画控件 var frame = new Animation.Frame() { Texture = png.ToTexture(page, this.GraphicsDevice), Png = png, Page = page, Delay = 0, Origin = Point.Zero, }; var frameData = new Animation.FrameAnimationData(); frameData.Frames.Add(frame); this.ShowAnimation(frameData); } public FrameAnimationData LoadVideo(Wz_Video wzVideo) { return new MaplestoryCanvasVideoLoader().Load(wzVideo, this.GraphicsDevice); } public FrameAnimationData LoadFrameAnimation(Wz_Node node, FrameAnimationCreatingOptions options = default) { return FrameAnimationData.CreateFromNode(node, this.GraphicsDevice, options, PluginBase.PluginManager.FindWz); } public ISpineAnimationData LoadSpineAnimation(Wz_Node node) { return this.LoadSpineAnimation(SpineLoader.Detect(node)); } public ISpineAnimationData LoadSpineAnimation(SpineDetectionResult detectionResult) { if (!detectionResult.Success) return null; var textureLoader = new WzSpineTextureLoader(detectionResult.SourceNode.ParentNode, this.GraphicsDevice, PluginBase.PluginManager.FindWz); // workaround for Map/Back/bossLimbo.img/spine/3/02_Passage_01_BgColor.skel, #266 textureLoader.EnableTextureMissingFallback = true; if (detectionResult.Version == SpineVersion.V2) return SpineAnimationDataV2.Create(detectionResult, textureLoader); else if (detectionResult.Version == SpineVersion.V4) return SpineAnimationDataV4.Create(detectionResult, textureLoader); else return null; } public void ShowAnimation(FrameAnimationData data) { this.ShowAnimation(new FrameAnimator(data)); } public void ShowAnimation(ISpineAnimationData data) { this.ShowAnimation(data.CreateAnimator() as AnimationItem); } public void ShowAnimation(AnimationItem animator) { if (this.Items.Count > 0) { var itemsCopy = new List(this.Items); this.Items.Clear(); foreach (var aniItem in itemsCopy) { this.DisposeAnimationItem(aniItem); } } this.Items.Add(animator); if (this.AutoAdjustPosition) { this.AdjustPosition(); } this.Invalidate(); } public void AdjustPosition() { if (this.Items.Count <= 0) return; var animator = this.Items[0]; if (animator is FrameAnimator) { var aniItem = (FrameAnimator)animator; var rect = aniItem.Data.GetBound(); aniItem.Position = new Point(-rect.Left, -rect.Top); } else if (animator is AnimationItem aniItem) { var rect = aniItem.Measure(); aniItem.Position = new Point(-rect.Left, -rect.Top); } } public bool SaveAsGif(AnimationItem aniItem, string fileName, ImageHandlerConfig config, GifEncoder encoder, bool showOptions) { var rec = new AnimationRecoder(this.GraphicsDevice); var cap = encoder.Compatibility; rec.Items.Add(aniItem); int length = rec.GetMaxLength(); int delay = Math.Max(cap.MinFrameDelay, config.MinDelay); int[] timeline = null; if (!cap.IsFixedFrameRate) { timeline = rec.GetGifTimeLine(delay, cap.MaxFrameDelay); } // calc available canvas area rec.ResetAll(); Microsoft.Xna.Framework.Rectangle bounds = aniItem.Measure(); if (length > 0) { IEnumerable delays = timeline?.Take(timeline.Length - 1) ?? Enumerable.Range(0, (int)Math.Ceiling(1.0 * length / delay) - 1); foreach (var frameDelay in delays) { rec.Update(TimeSpan.FromMilliseconds(frameDelay)); var rect = aniItem.Measure(); bounds = Microsoft.Xna.Framework.Rectangle.Union(bounds, rect); } } bounds.Offset(aniItem.Position); // customize clip/scale options AnimationClipOptions clipOptions = new AnimationClipOptions() { StartTime = 0, StopTime = length, Left = bounds.Left, Top = bounds.Top, Right = bounds.Right, Bottom = bounds.Bottom, OutputWidth = bounds.Width, OutputHeight = bounds.Height, }; if (showOptions) { var frmOptions = new FrmGifClipOptions() { ClipOptions = clipOptions, ClipOptionsNew = clipOptions, }; if (frmOptions.ShowDialog() == DialogResult.OK) { var clipOptionsNew = frmOptions.ClipOptionsNew; clipOptions.StartTime = clipOptionsNew.StartTime ?? clipOptions.StartTime; clipOptions.StopTime = clipOptionsNew.StopTime ?? clipOptions.StopTime; clipOptions.Left = clipOptionsNew.Left ?? clipOptions.Left; clipOptions.Top = clipOptionsNew.Top ?? clipOptions.Top; clipOptions.Right = clipOptionsNew.Right ?? clipOptions.Right; clipOptions.Bottom = clipOptionsNew.Bottom ?? clipOptions.Bottom; clipOptions.OutputWidth = clipOptionsNew.OutputWidth ?? (clipOptions.Right - clipOptions.Left); clipOptions.OutputHeight = clipOptionsNew.OutputHeight ?? (clipOptions.Bottom - clipOptions.Top); } else { return false; } } // validate params bounds = new Rectangle( clipOptions.Left.Value, clipOptions.Top.Value, clipOptions.Right.Value - clipOptions.Left.Value, clipOptions.Bottom.Value - clipOptions.Top.Value ); var targetSize = new Point(clipOptions.OutputWidth.Value, clipOptions.OutputHeight.Value); var startTime = clipOptions.StartTime.Value; var stopTime = clipOptions.StopTime.Value; if (bounds.Width <= 0 || bounds.Height <= 0 || targetSize.X <= 0 || targetSize.Y <= 0 || startTime < 0 || stopTime - startTime <= 0) { return false; } length = stopTime - startTime; // create output dir string framesDirName = Path.Combine(Path.GetDirectoryName(fileName), Path.GetFileNameWithoutExtension(fileName) + ".frames"); if (config.SavePngFramesEnabled && !Directory.Exists(framesDirName)) { Directory.CreateDirectory(framesDirName); } // pre-render rec.ResetAll(); switch (config.BackgroundType.Value) { default: case ImageBackgroundType.Transparent: rec.BackgroundColor = Color.Transparent; break; case ImageBackgroundType.Color: rec.BackgroundColor = System.Drawing.Color.FromArgb(255, config.BackgroundColor.Value).ToXnaColor(); break; case ImageBackgroundType.Mosaic: rec.BackgroundImage = MonogameUtils.CreateMosaic(GraphicsDevice, config.MosaicInfo.Color0.ToXnaColor(), config.MosaicInfo.Color1.ToXnaColor(), Math.Max(1, config.MosaicInfo.BlockSize)); break; } // select encoder encoder.Init(fileName, targetSize.X, targetSize.Y); // pipeline functions IEnumerable> MergeFrames(IEnumerable> frames) { byte[] prevFrame = null; int prevDelay = 0; foreach (var frame in frames) { byte[] currentFrame = frame.Item1; int currentDelay = frame.Item2; if (prevFrame == null) { prevFrame = currentFrame; prevDelay = currentDelay; } else if (prevFrame.AsSpan().SequenceEqual(currentFrame.AsSpan())) { prevDelay += currentDelay; } else { yield return Tuple.Create(prevFrame, prevDelay); prevFrame = currentFrame; prevDelay = currentDelay; } } if (prevFrame != null) { yield return Tuple.Create(prevFrame, prevDelay); } } IEnumerable RenderDelay() { int t = 0; while (t < length) { int frameDelay = Math.Min(length - t, delay); t += frameDelay; yield return frameDelay; } } IEnumerable ClipTimeline(int[] _timeline) { int t = 0; for (int i = 0; ; i = (i + 1) % timeline.Length) { var frameDelay = timeline[i]; if (t < startTime) { if (t + frameDelay > startTime) { frameDelay = t + frameDelay - startTime; t = startTime; } else { t += frameDelay; continue; } } if (t + frameDelay < stopTime) { yield return frameDelay; t += frameDelay; } else { frameDelay = stopTime - t; yield return frameDelay; break; } } } int prevTime = 0; async Task ApplyFrame(byte[] frameData, int frameDelay) { byte[] gifData = null; if (cap.AlphaSupportMode != AlphaSupportMode.FullAlpha && config.BackgroundType.Value == ImageBackgroundType.Transparent) { using (var rt2 = rec.GetGifTexture(config.BackgroundColor.Value.ToXnaColor(), config.MinMixedAlpha)) { if (gifData == null) { gifData = new byte[frameData.Length]; } rt2.GetData(gifData); } } else { gifData = frameData; } var tasks = new List(); // save each frame as png if (config.SavePngFramesEnabled) { tasks.Add(Task.Run(() => { string pngFileName = Path.Combine(framesDirName, $"{prevTime}_{prevTime + frameDelay}.png"); GCHandle gcHandle = GCHandle.Alloc(frameData, GCHandleType.Pinned); try { using (var bmp = new System.Drawing.Bitmap(targetSize.X, targetSize.Y, targetSize.X * 4, System.Drawing.Imaging.PixelFormat.Format32bppArgb, gcHandle.AddrOfPinnedObject())) { bmp.Save(pngFileName, System.Drawing.Imaging.ImageFormat.Png); } } finally { gcHandle.Free(); } })); } // append frame data to gif stream tasks.Add(Task.Run(() => { // TODO: only for gif here? frameDelay = Math.Max(10, (int)(Math.Round(frameDelay / 10.0) * 10)); GCHandle gcHandle = GCHandle.Alloc(frameData, GCHandleType.Pinned); try { encoder.AppendFrame(gcHandle.AddrOfPinnedObject(), frameDelay); } finally { gcHandle.Free(); } })); await Task.WhenAll(tasks); prevTime += frameDelay; return prevTime; } async Task RenderJob(IProgressDialogContext context, CancellationToken cancellationToken) { bool isCompareAndMergeFrames = timeline == null && !cap.IsFixedFrameRate; // build pipeline IEnumerable delayEnumerator = timeline == null ? RenderDelay() : ClipTimeline(timeline); var step1 = delayEnumerator.TakeWhile(_ => !cancellationToken.IsCancellationRequested); var frameRenderEnumerator = step1.Select(frameDelay => { rec.Draw(); rec.Update(TimeSpan.FromMilliseconds(frameDelay)); return frameDelay; }); var step2 = frameRenderEnumerator.TakeWhile(_ => !cancellationToken.IsCancellationRequested); var getFrameData = step2.Select(frameDelay => { using (var t2d = rec.GetPngTexture()) { byte[] frameData = new byte[t2d.Width * t2d.Height * 4]; t2d.GetData(frameData); return Tuple.Create(frameData, frameDelay); } }); var step3 = getFrameData.TakeWhile(_ => !cancellationToken.IsCancellationRequested); if (isCompareAndMergeFrames) { var mergedFrameData = MergeFrames(step3); step3 = mergedFrameData.TakeWhile(_ => !cancellationToken.IsCancellationRequested); } var step4 = step3.Select(item => ApplyFrame(item.Item1, item.Item2)); // run pipeline bool isPlaying = this.IsPlaying; try { this.IsPlaying = false; rec.Begin(bounds, targetSize); if (startTime > 0) { rec.Update(TimeSpan.FromMilliseconds(startTime)); } context.ProgressMin = 0; context.ProgressMax = length; foreach (var task in step4) { int currentTime = await task; context.Progress = currentTime; } } catch (Exception ex) { if (ex is AggregateException aggrEx && aggrEx.InnerExceptions.Count == 1) { context.Message = $"Error: {aggrEx.InnerExceptions[0].Message}"; } else { context.Message = $"Error: {ex.Message}"; } context.FullMessage = ex.ToString(); throw; } finally { rec.End(); this.IsPlaying = isPlaying; } } var dialogResult = ProgressDialog.Show(this.FindForm(), "Exporting...", "Save animation file...", true, false, RenderJob); return dialogResult == DialogResult.OK; } public override AnimationItem GetItemAt(int x, int y) { //固定获取当前显示的物件 无论鼠标在哪 return this.Items.Count > 0 ? this.Items[0] : null; } protected override void Initialize() { base.Initialize(); this.sprite = new SpriteBatchEx(this.GraphicsDevice); } protected override void Update(TimeSpan elapsed) { base.Update(elapsed); } protected override void Draw() { base.Draw(); if (this.ShowInfo && this.XnaFont != null) { UpdateInfoText(); sprite.Begin(); sprite.DrawStringEx(this.XnaFont, this.sbInfo, Vector2.Zero, Color.Black); sprite.End(); } } protected override void OnItemDragSave(AnimationItemEventArgs e) { var fileName = Path.GetTempFileName(); if ((e.Item as FrameAnimator)?.Data.Frames.Count == 1) { using (var bmp = (e.Item as FrameAnimator).Data.Frames[0].Png.ExtractPng()) { bmp.Save(fileName, System.Drawing.Imaging.ImageFormat.Png); } } else { //this.SaveAsGif(e.Item, fileName, ImageHandlerConfig.Default); // this is too lag so we don't support dragging gifs! return; } var imgObj = new ImageDataObject(null, fileName); this.DoDragDrop(imgObj, System.Windows.Forms.DragDropEffects.Copy); e.Handled = true; } private void UpdateInfoText() { this.sbInfo.Clear(); if (this.Items.Count > 0) { var aniItem = this.Items[0]; int time = 0; if (aniItem is FrameAnimator frameAni) { time = frameAni.CurrentTime; } else if (aniItem is ISpineAnimator spineAni) { time = spineAni.CurrentTime; } this.sbInfo.AppendFormat("pos: {0}, scale: {1:p0}, play: {2} / {3}", aniItem.Position, base.GlobalScale, aniItem.Length <= 0 ? 0 : (time % aniItem.Length), aniItem.Length); } } private void DisposeAnimationItem(AnimationItem animationItem) { switch (animationItem) { case FrameAnimator frameAni: if (frameAni.Data?.Frames != null) { foreach (var frame in frameAni.Data.Frames) { if (frame.Texture != null && !frame.Texture.IsDisposed) { frame.Texture.Dispose(); } } } break; case SpineAnimatorV2 spineV2: if (spineV2.Skeleton != null) { foreach(var slot in spineV2.Skeleton.Slots.Items) { var atlasRegion = (slot.Attachment switch { SpineV2.MeshAttachment mesh => mesh.RendererObject, SpineV2.RegionAttachment region => region.RendererObject, SpineV2.SkinnedMeshAttachment skinnedMesh => skinnedMesh.RendererObject, _ => null }) as SpineV2.AtlasRegion; if (atlasRegion?.page?.rendererObject is Texture2D texture && !texture.IsDisposed) { texture.Dispose(); } } } break; case SpineAnimatorV4 spineV4: if (spineV4.Skeleton != null) { foreach (var slot in spineV4.Skeleton.Slots.Items) { var atlasRegion = (slot.Attachment switch { Spine.MeshAttachment mesh => mesh.Region, Spine.RegionAttachment region => region.Region, _ => null }) as Spine.AtlasRegion; if (atlasRegion?.page?.rendererObject is Texture2D texture && !texture.IsDisposed) { texture.Dispose(); } } } break; } } } } ================================================ FILE: WzComparerR2/PluginLoadContext.cs ================================================ #if NET6_0_OR_GREATER using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Reflection; using System.Runtime.Loader; namespace WzComparerR2 { internal class PluginLoadContext : AssemblyLoadContext { public PluginLoadContext(string unmanagedDllFolder, string pluginPath) { this.assemblyResolver = new AssemblyDependencyResolver(pluginPath); this.unmanagedDllResolver = new AssemblyDependencyResolver(unmanagedDllFolder); } private AssemblyDependencyResolver assemblyResolver; private AssemblyDependencyResolver unmanagedDllResolver; protected override Assembly Load(AssemblyName assemblyName) { string assemblyPath = this.assemblyResolver.ResolveAssemblyToPath(assemblyName); if (assemblyPath != null) { return this.LoadFromAssemblyPath(assemblyPath); } return base.Load(assemblyName); } protected override IntPtr LoadUnmanagedDll(string unmanagedDllName) { string libraryPath = this.unmanagedDllResolver.ResolveUnmanagedDllToPath(unmanagedDllName); if (libraryPath != null) { return this.LoadUnmanagedDllFromPath(libraryPath); } return IntPtr.Zero; } } } #endif ================================================ FILE: WzComparerR2/Program.cs ================================================ using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Windows.Forms; using WzComparerR2.PluginBase; #if NET6_0_OR_GREATER using System.Runtime.Loader; using System.Text; #endif namespace WzComparerR2 { public class Program { [STAThread] static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve; Program.SetDllDirectory(); #if NET6_0_OR_GREATER Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); Dotnet6Patch.Patch(); #endif Program.StartMainForm(); } public static string LibPath { get; private set; } private static List loadedPluginAssemblies = new List(); private /// /// 这是程序入口无雾。 /// static void StartMainForm() { //创建主窗体 var frm = new MainForm(); //加载插件 LoadPlugins(frm); //加载配置文件并初始化插件 var cng = Config.ConfigManager.ConfigFile; frm.PluginOnLoad(); PluginManager.PluginOnLoad(); //走你 Application.Run(frm); } static void LoadPlugins(PluginContextProvider provider) { var asmList = PluginManager.GetPluginFiles().Select(asmFile => { try { #if NET6_0_OR_GREATER var ctx = new PluginLoadContext(GetUnmanagedDllDirectory(), asmFile); return ctx.LoadFromAssemblyPath(asmFile); #else var asmName = AssemblyName.GetAssemblyName(asmFile); return Assembly.Load(asmName); #endif } catch (Exception ex) { return null; } }).OfType().ToList(); loadedPluginAssemblies.AddRange(asmList); var context = new PluginContext(provider); foreach (var asm in asmList) { PluginManager.LoadPlugin(asm, context); } } static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) { Exception ex = e.ExceptionObject as Exception; if (ex != null) { string logFile = Path.Combine(Application.StartupPath, "error.log"); try { string content = DateTime.Now.ToString() + "\r\n" + ex.ToString() + "\r\n"; File.AppendAllText(logFile, content); } catch { } } } private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) { foreach (var asm in loadedPluginAssemblies) { if (asm.FullName == args.Name) { return asm; } } #if NET6_0_OR_GREATER try { var assemblyName = new AssemblyName(args.Name); string assemblyPath = Path.Combine(GetManagedDllDirectory(), assemblyName.Name + ".dll"); if (File.Exists(assemblyPath)) { return AssemblyLoadContext.Default.LoadFromAssemblyPath(assemblyPath); } } catch { return null; } #endif return null; } static void SetDllDirectory() { LibPath = GetUnmanagedDllDirectory(); SetDllDirectory(LibPath); foreach (var dllName in Directory.GetFiles(LibPath, "*.dll")) { var handle = LoadLibrary(dllName); } } // System.Runtime.InteropServices.RuntimeInformation.ProcessArchitecture requires .netfx 4.7.1, here we use env var instead. static string GetManagedDllDirectory() => Path.Combine(Application.StartupPath, "Lib"); static string GetUnmanagedDllDirectory() => Path.Combine(Application.StartupPath, "Lib", RuntimeInformation.ProcessArchitecture switch { Architecture.X86 => "x86", Architecture.X64 => "x64", Architecture.Arm64 => "ARM64", _ => null, }); internal static T GetAsmAttr() { object[] attr = typeof(WzComparerR2.Program).Assembly.GetCustomAttributes(typeof(T), true); if (attr != null && attr.Length > 0) { return (T)attr[0]; } return default(T); } private static string _appVersion; internal static string ApplicationVersion => _appVersion ?? (_appVersion = GetAsmAttr()?.InformationalVersion ?? GetAsmAttr()?.Version); [DllImport("kernel32.dll")] static extern bool SetDllDirectory(string path); [DllImport("kernel32.dll")] static extern IntPtr LoadLibrary(string path); } } ================================================ FILE: WzComparerR2/Properties/AssemblyInfo.cs ================================================ using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // 有关程序集的常规信息通过以下 // 特性集控制。更改这些特性值可修改 // 与程序集关联的信息。 [assembly: AssemblyTitle("WzComparerR2")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Kagamia Studio")] [assembly: AssemblyProduct("WzComparerR2")] [assembly: AssemblyCopyright("Copyright © Kagamia Studio 2011-2025")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] // 将 ComVisible 设置为 false 使此程序集中的类型 // 对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型, // 则将该类型上的 ComVisible 特性设置为 true。 [assembly: ComVisible(false)] // 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID [assembly: Guid("5be749fb-87bc-49be-812a-6eababb9c55b")] // 程序集的版本信息由下面四个值组成: // // 主版本 // 次版本 // 内部版本号 // 修订号 // // 可以指定所有这些值,也可以使用“内部版本号”和“修订号”的默认值, // 方法是按如下所示使用“*”: // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("2.2.0.0")] [assembly: AssemblyFileVersion("2.2.0.10725")] ================================================ FILE: WzComparerR2/Properties/Resources.Designer.cs ================================================ //------------------------------------------------------------------------------ // // 此代码由工具生成。 // 运行时版本:4.0.30319.42000 // // 对此文件的更改可能会导致不正确的行为,并且如果 // 重新生成代码,这些更改将会丢失。 // //------------------------------------------------------------------------------ namespace WzComparerR2.Properties { using System; /// /// 一个强类型的资源类,用于查找本地化的字符串等。 /// // 此类是由 StronglyTypedResourceBuilder // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen // (以 /str 作为命令选项),或重新生成 VS 项目。 [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class Resources { private static global::System.Resources.ResourceManager resourceMan; private static global::System.Globalization.CultureInfo resourceCulture; [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal Resources() { } /// /// 返回此类使用的缓存的 ResourceManager 实例。 /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("WzComparerR2.Properties.Resources", typeof(Resources).Assembly); resourceMan = temp; } return resourceMan; } } /// /// 重写当前线程的 CurrentUICulture 属性,对 /// 使用此强类型资源类的所有资源查找执行重写。 /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Globalization.CultureInfo Culture { get { return resourceCulture; } set { resourceCulture = value; } } /// /// 查找 System.Drawing.Bitmap 类型的本地化资源。 /// internal static System.Drawing.Bitmap convex { get { object obj = ResourceManager.GetObject("convex", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// 查找 System.Drawing.Bitmap 类型的本地化资源。 /// internal static System.Drawing.Bitmap dir { get { object obj = ResourceManager.GetObject("dir", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// 查找 System.Drawing.Bitmap 类型的本地化资源。 /// internal static System.Drawing.Bitmap img { get { object obj = ResourceManager.GetObject("img", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// 查找 System.Drawing.Bitmap 类型的本地化资源。 /// internal static System.Drawing.Bitmap mp3 { get { object obj = ResourceManager.GetObject("mp3", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// 查找 System.Drawing.Bitmap 类型的本地化资源。 /// internal static System.Drawing.Bitmap num { get { object obj = ResourceManager.GetObject("num", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// 查找 System.Drawing.Bitmap 类型的本地化资源。 /// internal static System.Drawing.Bitmap Open { get { object obj = ResourceManager.GetObject("Open", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// 查找 System.Drawing.Bitmap 类型的本地化资源。 /// internal static System.Drawing.Bitmap Pause { get { object obj = ResourceManager.GetObject("Pause", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// 查找 System.Drawing.Bitmap 类型的本地化资源。 /// internal static System.Drawing.Bitmap Play { get { object obj = ResourceManager.GetObject("Play", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// 查找 System.Drawing.Bitmap 类型的本地化资源。 /// internal static System.Drawing.Bitmap png { get { object obj = ResourceManager.GetObject("png", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// 查找 System.Drawing.Bitmap 类型的本地化资源。 /// internal static System.Drawing.Bitmap rawdata { get { object obj = ResourceManager.GetObject("rawdata", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// 查找 System.Drawing.Bitmap 类型的本地化资源。 /// internal static System.Drawing.Bitmap Save { get { object obj = ResourceManager.GetObject("Save", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// 查找 System.Drawing.Bitmap 类型的本地化资源。 /// internal static System.Drawing.Bitmap Stop { get { object obj = ResourceManager.GetObject("Stop", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// 查找 System.Drawing.Bitmap 类型的本地化资源。 /// internal static System.Drawing.Bitmap str { get { object obj = ResourceManager.GetObject("str", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// 查找 System.Drawing.Bitmap 类型的本地化资源。 /// internal static System.Drawing.Bitmap uol { get { object obj = ResourceManager.GetObject("uol", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// 查找 System.Drawing.Bitmap 类型的本地化资源。 /// internal static System.Drawing.Bitmap vector { get { object obj = ResourceManager.GetObject("vector", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// 查找 System.Drawing.Bitmap 类型的本地化资源。 /// internal static System.Drawing.Bitmap video { get { object obj = ResourceManager.GetObject("video", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } } } ================================================ FILE: WzComparerR2/Properties/Resources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ..\Resources\dir4.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\dir3.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\mp3.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\num2.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\Pause.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\Play.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\png.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\Stop.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\string.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\uol.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\vector.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\Open.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\Save.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\rawdata.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\convex.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\video.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ================================================ FILE: WzComparerR2/QueryPerformance.cs ================================================ using System; using System.Collections.Generic; using System.Text; using System.Runtime.InteropServices; namespace WzComparerR2 { public static class QueryPerformance { static QueryPerformance() { counter = 0; IsSupposed = QueryPerformanceFrequency(ref counter); if (!IsSupposed) throw new Exception("QueryPerformance无法初始化。"); } [DllImport("kernel32.dll")] private extern static bool QueryPerformanceCounter(ref long lPerformanceCounter); [DllImport("kernel32.dll")] private extern static bool QueryPerformanceFrequency(ref long lFrequency); public static bool IsSupposed; private static long counter; private static long length; private static long tempStart; /// /// 启动计时器,开始计时。 /// public static void Start() { if (IsSupposed) { length = 0; QueryPerformanceCounter(ref tempStart); } } /// /// 关闭计时器,结束计时。 /// public static void End() { if (IsSupposed && length == 0) { QueryPerformanceCounter(ref length); length -= tempStart; } } /// /// 返回上次开始结束计时的时间间隔,单位为秒。 /// /// public static double GetLastInterval() { return (double)length / counter; } public static long GetLastCount() { return length; } } } ================================================ FILE: WzComparerR2/SoundPlayer/BassSoundPlayer.cs ================================================ using System; using System.Collections.Generic; using System.Text; using System.Timers; using ManagedBass; using System.IO; using System.Runtime.InteropServices; namespace WzComparerR2 { public class BassSoundPlayer : ISoundPlayer { public BassSoundPlayer() { volume = 100; autoPlay = true; loadedPlugin = new HashSet(); } private bool inited; private bool autoPlay; private int volume; private bool loop; private int hStream; private bool isDisposed; private string playingSoundName; private byte[] data; private HashSet loadedPlugin; public string PlayingSoundName { get { return playingSoundName; } set { playingSoundName = value; } } public byte[] Data { get { return data; } set { data = value; } } public bool Inited { get { return inited; } } public bool Init() { if (!inited) { try { Bass.Configure(Configuration.IncludeDefaultDevice, true); if (inited = Bass.Init(-1, 44100, DeviceInitFlags.Default, IntPtr.Zero)) { if (Directory.Exists(Program.LibPath)) { foreach (string file in Directory.GetFiles(Program.LibPath, "bass*.dll", SearchOption.AllDirectories)) { int p = Bass.PluginLoad(file); if (p != 0) { loadedPlugin.Add(p); } } } } } catch { } } return inited; } public Errors GetLastError() { return Bass.LastError; } public IEnumerable GetPluginSupportedExt() { if (this.loadedPlugin == null || this.loadedPlugin.Count == 0) yield break; foreach (var p in this.loadedPlugin) { PluginInfo info = Bass.PluginGetInfo(p); foreach (PluginFormat form in info.Formats) { yield return form.Name + "(" + form.FileExtensions + ")" + "|" + form.FileExtensions; } } } public void PreLoad(ISoundFile sound) { if (sound == null) return; this.UnLoad(); try { hStream = Bass.CreateStream(sound.FileName, sound.StartPosition, sound.Length, BassFlags.Default); } catch { hStream = 0; } if (hStream != 0) { this.Volume = this.Volume;//调节音量到设定值 this.Loop = this.Loop; if (this.autoPlay) this.Play(); } } public void PreLoad(byte[] data) { if (data == null) return; this.UnLoad(); try { IntPtr pData = Marshal.UnsafeAddrOfPinnedArrayElement(data,0); hStream = Bass.CreateStream(pData, 0, data.Length, BassFlags.Default); } catch { hStream = 0; } if (hStream != 0) { this.Volume = this.Volume;//调节音量到设定值 this.Loop = this.Loop; this.data = data; if (this.autoPlay) this.Play(); } else { var lastErr = Bass.LastError; } } public void UnLoad() { if (hStream != 0) { Bass.ChannelStop(hStream); Bass.StreamFree(hStream); this.data = null; } } public void Play() { bool success = Bass.ChannelPlay(hStream, false); } public void Pause() { Bass.ChannelPause(hStream); } public void Resume() { Bass.ChannelPlay(hStream, false); } public void Stop() { Bass.ChannelStop(hStream); Bass.ChannelSetPosition(hStream, 0); } public int Volume { get { return this.volume; } set { this.volume = Math.Min(Math.Max(value, 0), 100); Bass.ChannelSetAttribute(hStream, ChannelAttribute.Volume, this.volume * 0.01f); } } public double SoundPosition { get { if (this.hStream != 0) return Bass.ChannelBytes2Seconds(hStream, Bass.ChannelGetPosition(hStream)); else return 0d; } set { if (this.hStream != 0) { double totalLen = this.SoundLength; value = Math.Min(Math.Max(value, 0), totalLen); Bass.ChannelSetPosition(hStream, Bass.ChannelSeconds2Bytes(hStream, value)); } } } public double SoundLength { get { if (this.hStream != 0) return Bass.ChannelBytes2Seconds(hStream, Bass.ChannelGetLength(hStream)); else return 0d; } } public void Dispose() { if (!isDisposed) { this.UnLoad(); Bass.Free(); if (this.loadedPlugin != null && this.loadedPlugin.Count > 0) { bool success = Bass.PluginFree(0); this.loadedPlugin.Clear(); } isDisposed = true; } } public bool AutoPlay { get { return this.autoPlay; } set { this.autoPlay = value; } } public bool Loop { get { return this.loop; } set { this.loop = value; if (this.hStream != 0) { if (this.loop) Bass.ChannelFlags(hStream, BassFlags.Loop, BassFlags.Loop); else Bass.ChannelFlags(hStream, BassFlags.Default, BassFlags.Loop); } } } public PlayState State { get { if (this.hStream != 0) { PlaybackState active = Bass.ChannelIsActive(hStream); switch (active) { case PlaybackState.Stopped: return PlayState.Stopped; case PlaybackState.Playing: return PlayState.Playing; case PlaybackState.Paused: return PlayState.Paused; default: return PlayState.Stopped; } } else { return PlayState.Stopped; } } } } } ================================================ FILE: WzComparerR2/SoundPlayer/CustomSoundFile.cs ================================================ using System; using System.Collections.Generic; using System.Text; using System.IO; using WzComparerR2.WzLib; namespace WzComparerR2 { public class CustomSoundFile : ISoundFile { public CustomSoundFile(string fileName) : this(fileName, 0, 0) { } public CustomSoundFile(string fileName, int startPos, int length) { this.fileName = fileName; this.startPosition = startPos; this.length = length; } private string fileName; private int startPosition; private int length; public string FileName { get { return this.fileName; } } public int StartPosition { get { return this.startPosition; } } public int Length { get { return this.length; } } } } ================================================ FILE: WzComparerR2/SoundPlayer/ISoundFile.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace WzComparerR2 { public interface ISoundFile { string FileName { get; } int StartPosition { get; } int Length { get; } } } ================================================ FILE: WzComparerR2/SoundPlayer/ISoundPlayer.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace WzComparerR2 { public interface ISoundPlayer : IDisposable { bool Inited { get; } bool Init(); void PreLoad(ISoundFile sound); void UnLoad(); void Play(); void Pause(); void Resume(); void Stop(); bool AutoPlay { get; set; } int Volume { get; set; } double SoundPosition { get; set; } double SoundLength { get; } PlayState State { get; } } } ================================================ FILE: WzComparerR2/SoundPlayer/PlayState.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace WzComparerR2 { public enum PlayState { Stopped = 0, Playing, Paused } } ================================================ FILE: WzComparerR2/Updater.cs ================================================ using System; using System.IO; using System.Net.Http; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using Newtonsoft.Json; namespace WzComparerR2 { public class Updater { private const string checkUpdateURL = "https://api.github.com/repos/Kagamia/WzComparerR2/releases/latest"; public Updater() { string appVersion = Program.ApplicationVersion; if (TryParseApplicationVersion(appVersion, out var version)) { this.CurrentVersion = version; this.CurrentVersionString = $"{version.Build}.{version.Revision}"; } else { this.CurrentVersion = default; this.CurrentVersionString = appVersion; } } public bool LatestReleaseFetched { get; private set; } public bool UpdateAvailable { get; private set; } public Version CurrentVersion { get; private set; } public Version LatestVersion { get; private set; } public string CurrentVersionString { get; private set; } public string LatestVersionString { get; private set; } public GithubReleaseResponse Release { get; private set; } public GithubAsset Net462Asset { get; private set; } public GithubAsset Net6Asset { get; private set; } public GithubAsset Net8Asset { get; private set; } public GithubAsset Net10Asset { get; private set; } private readonly TimeSpan defaultRequestTimeout = TimeSpan.FromSeconds(15); public async Task QueryUpdateAsync(CancellationToken cancellationToken = default) { // send request using var client = new HttpClient(); client.Timeout = defaultRequestTimeout; using var request = new HttpRequestMessage(HttpMethod.Get, checkUpdateURL); request.Headers.Accept.ParseAdd("application/vnd.github+json"); request.Headers.UserAgent.ParseAdd($"WzComparerR2/{this.CurrentVersionString ?? "1.0"}"); using var response = await client.SendAsync(request, cancellationToken).ConfigureAwait(false); response.EnsureSuccessStatusCode(); // parse payload using var responseStream = await response.Content.ReadAsStreamAsync(); using var jsonTextReader = new JsonTextReader(new StreamReader(responseStream)); var serializer = new JsonSerializer(); var release = serializer.Deserialize(jsonTextReader); // reset all this.LatestReleaseFetched = true; this.Release = release; this.Net462Asset = null; this.Net6Asset = null; this.Net8Asset = null; this.Net10Asset = null; // check version if (TryParseBuildVersion(release.Name, out var releaseVer)) { this.LatestVersion = releaseVer; this.LatestVersionString = $"{releaseVer.Build}.{releaseVer.Revision}"; if (this.CurrentVersion != default) { this.UpdateAvailable = (releaseVer.Build > this.CurrentVersion.Build) || (releaseVer.Build == this.CurrentVersion.Build && releaseVer.Revision > this.CurrentVersion.Revision); } } else { this.LatestVersion = default; this.LatestVersionString = release.Name; } // check assets if (release.Assets != null) { foreach (var asset in release.Assets) { if (asset.Name != null) { if (asset.Name.Contains("net6")) { this.Net6Asset = asset; } else if (asset.Name.Contains("net8")) { this.Net8Asset = asset; } else if (asset.Name.Contains("net10")) { this.Net10Asset = asset; } else { this.Net462Asset = asset; } } } } } public async Task DownloadAssetAsync(GithubAsset asset, string fileName, OnProgressCallback onProgress = null, CancellationToken cancellationToken = default) { // send request using var client = new HttpClient(); client.Timeout = defaultRequestTimeout; using var request = new HttpRequestMessage(HttpMethod.Get, asset.BrowserDownloadUrl); if (asset.ContentType != null) { request.Headers.Accept.ParseAdd(asset.ContentType); } request.Headers.UserAgent.ParseAdd($"WzComparerR2/{this.CurrentVersionString ?? "1.0"}"); using var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); response.EnsureSuccessStatusCode(); long fileSize = response.Content.Headers.ContentLength ?? asset.Size; // copy to file bool fileCreated = false; try { using var responseStream = await response.Content.ReadAsStreamAsync(); using var fs = File.Create(fileName); fileCreated = true; byte[] buffer = new byte[16 * 1024]; long downloadedBytes = 0; while (true) { int len = await responseStream.ReadAsync(buffer, 0, buffer.Length, cancellationToken); if (len <= 0) { break; } fs.Write(buffer, 0, len); downloadedBytes += len; onProgress?.Invoke(downloadedBytes, fileSize); } await responseStream.CopyToAsync(fs, 16 * 1024, cancellationToken).ConfigureAwait(false); } catch { if (fileCreated && File.Exists(fileName)) { File.Delete(fileName); } throw; } } private static bool TryParseBuildVersion(string releaseName, out Version result) { var m = Regex.Match(releaseName, @"(\d{8})\.(\d+)"); if (m.Success && int.TryParse(m.Groups[1].Value, out int build) && int.TryParse(m.Groups[2].Value, out int revision)) { result = new Version(0, 0, build, revision); return true; } result = default; return false; } private static bool TryParseApplicationVersion(string appVersion, out Version result) { return Version.TryParse(appVersion, out result); } public delegate void OnProgressCallback(long downloadedBytes, long totalBytes); /// public class GithubReleaseResponse { [JsonProperty("name")] public string Name { get; set; } [JsonProperty("body")] public string Body { get; set; } [JsonProperty("created_at")] public DateTime CreatedAt { get; set; } [JsonProperty("assets")] public GithubAsset[] Assets { get; set; } } public class GithubAsset { [JsonProperty("name")] public string Name { get; set; } [JsonProperty("label")] public string Label { get; set; } [JsonProperty("content_type")] public string ContentType { get; set; } [JsonProperty("browser_download_url")] public string BrowserDownloadUrl { get; set; } [JsonProperty("size")] public long Size { get; set; } } } } ================================================ FILE: WzComparerR2/WzComparerR2.csproj ================================================  WinExe net462;net6.0-windows;net8.0-windows true WzComparerR2 WzComparerR2 True True false latest disable wcr2_256.ico true app.manifest false false ..\References\DevComponents.DotNetBar2.dll ..\References\spine-monogame.dll Properties\CommonAssemblyInfo.cs True True Resources.resx ResXFileCodeGenerator Resources.Designer.cs ================================================ FILE: WzComparerR2/app.config ================================================ ================================================ FILE: WzComparerR2/app.manifest ================================================  amd64 arm64 ================================================ FILE: WzComparerR2.Avatar/Action.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace WzComparerR2.Avatar { public class Action { public string Name { get; set; } public int Level { get; set; } public bool Enabled { get; set; } public int Order { get; set; } public override string ToString() { return this.Name; } } } ================================================ FILE: WzComparerR2.Avatar/ActionFrame.cs ================================================ using System; using System.Collections.Generic; using System.Text; using System.Drawing; namespace WzComparerR2.Avatar { public class ActionFrame { public ActionFrame() { } public ActionFrame(string action, int frame) { this.Action = action; this.Frame = frame; } public string Action { get; set; } public int? Frame { get; set; } public int Delay { get; set; } public int AbsoluteDelay { get { return Math.Abs(this.Delay); } } public bool? Face { get; set; } public bool Flip { get; set; } public Point Move { get; set; } public int Rotate { get; set; } public int RotateProp { get; set; } //骑宠用特殊属性 public string ForceCharacterAction { get; set; } public int? ForceCharacterActionFrameIndex { get; set; } public string ForceCharacterFace { get; set; } public int? ForceCharacterFaceFrameIndex { get; set; } public bool ForceCharacterFaceHide { get; set; } public bool ForceCharacterFlip { get; set; } } } ================================================ FILE: WzComparerR2.Avatar/AvatarCanvas.cs ================================================ using System; using System.Collections.Generic; using System.Text; using System.Collections.ObjectModel; using System.Drawing; using WzComparerR2.WzLib; using WzComparerR2.CharaSim; namespace WzComparerR2.Avatar { public class AvatarCanvas { public AvatarCanvas() { this.ZMap = new List(); this.Actions = new List(); this.Emotions = new List(); this.TamingActions = new List(); this.Parts = new AvatarPart[18]; this.EarType = 0; this.WeaponIndex = 0; } public List ZMap { get; private set; } public List Actions { get; private set; } public List Emotions { get; private set; } public List TamingActions { get; private set; } public AvatarPart[] Parts { get; private set; } public string ActionName { get; set; } public string EmotionName { get; set; } public string TamingActionName { get; set; } public bool HairCover { get; set; } public bool ShowHairShade { get; set; } public int WeaponIndex { get; set; } public int WeaponType { get; set; } public int EarType { get; set; } public bool LoadZ() { return LoadZ(PluginBase.PluginManager.FindWz("Base\\zmap.img")); } public bool LoadZ(Wz_Node zMapNode) { if (zMapNode == null) { return false; } this.ZMap.Clear(); this.ZMap.Capacity = zMapNode.Nodes.Count; //读取z层顺序 foreach (Wz_Node node in zMapNode.Nodes) { this.ZMap.Add(node.Text); } return true; } /// /// 从00002000.img中加载全部动作名称。 /// /// public bool LoadActions() { Wz_Node bodyNode = PluginBase.PluginManager.FindWz("Character\\00002000.img"); if (bodyNode == null) { return false; } this.Actions.Clear(); foreach (Wz_Node actionNode in bodyNode.Nodes) { if (actionNode.Text != "info") { var action = LoadAction(actionNode); this.Actions.AddRange(action); } } for (int i = 0; i < this.Actions.Count; i++) { this.Actions[i].Order = i; } this.Actions.Sort((a0, a1) => { int comp = a0.Level.CompareTo(a1.Level); if (comp == 0) { if (a0.Level == 0) //基础动作 { int idx0 = Array.IndexOf(baseActions, a0.Name), idx1 = Array.IndexOf(baseActions, a1.Name); comp = idx0.CompareTo(idx1); } else { comp = a0.Order.CompareTo(a1.Order); } } return comp; }); return true; } /// /// 从00020000.img中加载表情名称。 /// /// public bool LoadEmotions() { Wz_Node faceNode = this.Face != null ? this.Face.Node : PluginBase.PluginManager.FindWz("Character\\Face\\00020000.img"); if (faceNode == null) { return false; } this.Emotions.Clear(); foreach (Wz_Node emotionNode in faceNode.Nodes) { if (emotionNode.Text != "info") { this.Emotions.Add(emotionNode.Text); } } return true; } public bool LoadTamingActions() { this.TamingActions.Clear(); Wz_Node tamingNode = this.Taming == null ? null : this.Taming.Node; if (tamingNode == null) { return false; } foreach (Wz_Node actionNode in tamingNode.Nodes) { switch (actionNode.Text) { case "info": case "characterAction": case "characterEmotion": case "property": case "forcingItem": break; default: this.TamingActions.Add(actionNode.Text); break; } } return true; } public List GetCashWeaponTypes() { List types = new List(); if (this.Weapon != null && this.Weapon.ID != null && Gear.GetGearType(this.Weapon.ID.Value) == GearType.cashWeapon) { foreach (var node in this.Weapon.Node.Nodes) { int typeID; if (Int32.TryParse(node.Text, out typeID)) { types.Add(typeID); } } } types.Sort(); return types; } private IEnumerable LoadAction(Wz_Node actionNode) { if (actionNode.FindNodeByPath("0") != null) { var action = LoadActionFromNode(actionNode, actionNode.Text); if (action != null) { action.Name = actionNode.Text; yield return action; } } else { for (int i = 1; ; i++) { var subActionNode = actionNode.FindNodeByPath(i.ToString()); if (subActionNode == null) { break; } var action = LoadActionFromNode(subActionNode, actionNode.Text); if (action != null) { action.Name = actionNode.Text + "\\" + i; yield return action; } } } } private Action LoadActionFromNode(Wz_Node actionNode, string actionName) { Action act = new Action(); act.Name = actionName; if (BaseActions.Contains(actionName)) //基础动作 { act.Level = 0; } else { Wz_Node frameNode = actionNode.FindNodeByPath("0"); if (frameNode == null) //有鬼 { return null; } if (frameNode.FindNodeByPath("action") != null && frameNode.FindNodeByPath("frame") != null) //引用动作 { act.Level = 2; } else //当成扩展动作 { act.Level = 1; } } return act; } public AvatarPart AddPart(Wz_Node imgNode) { Wz_Node infoNode = imgNode.FindNodeByPath("info"); if (infoNode == null) { return null; } AvatarPart part = new AvatarPart(imgNode); var gearType = Gear.GetGearType(part.ID.Value); switch (gearType) { case GearType.body: this.Body = part; break; case GearType.head: this.Head = part; break; case GearType.face: case GearType.face2: this.Face = part; break; case GearType.hair: case GearType.hair2: case GearType.hair3: case GearType.hair4: this.Hair = part; break; case GearType.cap: this.Cap = part; break; case GearType.coat: this.Coat = part; break; case GearType.longcoat: this.Longcoat = part; break; case GearType.pants: this.Pants = part; break; case GearType.shoes: this.Shoes = part; break; case GearType.glove: this.Glove = part; break; case GearType.shield: case GearType.demonShield: case GearType.soulShield: case GearType.katara: this.SubWeapon = part; break; case GearType.cape: this.Cape = part; break; case GearType.shovel: case GearType.pickaxe: case GearType.cashWeapon: this.Weapon = part; break; case GearType.earrings: this.Earrings = part; break; case GearType.faceAccessory: this.FaceAccessory = part; break; case GearType.eyeAccessory: this.EyeAccessory = part; break; case GearType.taming: case GearType.taming2: case GearType.taming3: case GearType.tamingChair: this.Taming = part; break; case GearType.saddle: this.Saddle = part; break; default: if (Gear.IsWeapon(gearType)) { this.Weapon = part; } break; } return part; } /// /// 获取角色动作的动画帧。 /// public ActionFrame[] GetActionFrames(string actionName) { Action action = this.Actions.Find(act => act.Name == actionName); if (action == null) { return new ActionFrame[0]; } Wz_Node bodyNode = PluginBase.PluginManager.FindWz("Character\\00002000.img"); if (action.Level == 2) { var actionNode = bodyNode.FindNodeByPath(action.Name); if (actionNode == null) { return new ActionFrame[0]; } List frames = new List(); for (int i = 0; ; i++) { var frameNode = actionNode.FindNodeByPath(i.ToString()); if (frameNode == null) { break; } ActionFrame frame = new ActionFrame(); frame.Action = frameNode.Nodes["action"].GetValueEx(null); frame.Frame = frameNode.Nodes["frame"].GetValueEx(0); LoadActionFrameDesc(frameNode, frame); frames.Add(frame); } return frames.ToArray(); } else { Wz_Node actionNode = null; if (this.Body != null) { actionNode = this.Body.Node.FindNodeByPath(action.Name); } if (actionNode == null) { actionNode = bodyNode.FindNodeByPath(action.Name); } List frames = new List(); frames.AddRange(LoadStandardFrames(actionNode, action.Name)); return frames.ToArray(); } } private ActionFrame GetActionFrame(string actionName, int frameIndex) { Action action = this.Actions.Find(act => act.Name == actionName); if (action == null) { return null; } Wz_Node bodyNode = PluginBase.PluginManager.FindWz("Character\\00002000.img"); if (action.Level == 2) { var frameNode = bodyNode.FindNodeByPath($@"{action.Name}\{frameIndex}"); if (frameNode != null) { ActionFrame frame = new ActionFrame(); frame.Action = frameNode.Nodes["action"].GetValueEx(null); frame.Frame = frameNode.Nodes["frame"].GetValueEx(0); LoadActionFrameDesc(frameNode, frame); return frame; } } else { Wz_Node actionNode = this.Body?.Node.FindNodeByPath(action.Name) ?? bodyNode.FindNodeByPath(action.Name); var frameNode = actionNode?.Nodes[frameIndex.ToString()]; if (frameNode != null) { var frame = LoadStandardFrame(frameNode); frame.Action = action.Name; frame.Frame = frameIndex; return frame; } } return null; } public ActionFrame[] GetFaceFrames(string emotion) { List frames = new List(); if (this.Face != null) { if (emotion == "default") { frames.Add(new ActionFrame() { Action = emotion }); } else { var actionNode = this.Face.Node.FindNodeByPath(emotion); frames.AddRange(LoadStandardFrames(actionNode, emotion)); } } return frames.ToArray(); } private ActionFrame GetFaceFrame(string emotion, int frameIndex) { if (this.Face != null) { if (emotion == "default") { return new ActionFrame() { Action = emotion }; } else { var frameNode = this.Face.Node.FindNodeByPath($@"{emotion}\{frameIndex}"); if (frameNode != null) { var frame = LoadStandardFrame(frameNode); frame.Action = emotion; frame.Frame = frameIndex; return frame; } } } return null; } public ActionFrame[] GetTamingFrames(string action) { List frames = new List(); if (this.Taming != null) { var actionNode = this.Taming.Node.FindNodeByPath(action); frames.AddRange(LoadStandardFrames(actionNode, action)); } return frames.ToArray(); } private ActionFrame GetTamingFrame(string action, int frameIndex) { var actionNode = this.Taming?.Node.Nodes[action]?.ResolveUol(); if (actionNode == null) { return null; } var frameNode = actionNode.Nodes[frameIndex.ToString()]; if (frameNode != null) { var frame = LoadStandardFrame(frameNode); frame.Action = action; frame.Frame = frameIndex; return frame; } return null; } /// /// 读取扩展属性。 /// private void LoadActionFrameDesc(Wz_Node frameNode, ActionFrame actionFrame) { actionFrame.Delay = frameNode.Nodes["delay"].GetValueEx(120); actionFrame.Flip = frameNode.Nodes["flip"].GetValueEx(0) != 0; var faceNode = frameNode.Nodes["face"]; if (faceNode != null) { actionFrame.Face = faceNode.GetValue() != 0; } var move = frameNode.Nodes["move"].GetValueEx(null); if (move != null) { actionFrame.Move = move; } actionFrame.RotateProp = frameNode.Nodes["rotateProp"].GetValueEx(0); actionFrame.Rotate = frameNode.Nodes["rotate"].GetValueEx(0); actionFrame.ForceCharacterAction = frameNode.Nodes["forceCharacterAction"].GetValueEx(null); actionFrame.ForceCharacterActionFrameIndex = frameNode.Nodes["forceCharacterActionFrameIndex"].GetValueEx(); actionFrame.ForceCharacterFace = frameNode.Nodes["forceCharacterFace"].GetValueEx(null); actionFrame.ForceCharacterFaceFrameIndex = frameNode.Nodes["forceCharacterFaceFrameIndex"].GetValueEx(); actionFrame.ForceCharacterFaceHide = frameNode.Nodes["forceCharacterFaceHide"].GetValueEx(0) != 0; actionFrame.ForceCharacterFlip = frameNode.Nodes["forceCharacterFlip"].GetValueEx(0) != 0; } private IEnumerable LoadStandardFrames(Wz_Node actionNode, string action) { if (actionNode == null) { yield break; } actionNode = actionNode.ResolveUol(); if (actionNode == null) { yield break; } for (int i = 0; ; i++) { var frameNode = actionNode.FindNodeByPath(i.ToString()); if (frameNode == null) { yield break; } var frame = LoadStandardFrame(frameNode); frame.Action = action; frame.Frame = i; yield return frame; } } private ActionFrame LoadStandardFrame(Wz_Node frameNode) { ActionFrame frame = new ActionFrame(); while (frameNode.Value is Wz_Uol) { frameNode = frameNode.GetValue().HandleUol(frameNode); } LoadActionFrameDesc(frameNode, frame); return frame; } /// /// 计算角色骨骼层次结构。 /// /// /// public Bone CreateFrame(int bodyFrame, int faceFrame, int tamingFrame) { ActionFrame bodyAction = null, faceAction = null, tamingAction = null; string actionName = this.ActionName, emotionName = this.EmotionName, tamingActionName = this.TamingActionName; bool bodyFlip = false; //获取骑宠 if (this.Taming != null) { tamingAction = GetTamingFrame(tamingActionName, tamingFrame); if (tamingAction != null) { if (!string.IsNullOrEmpty(tamingAction.ForceCharacterAction)) //强制动作 { actionName = tamingAction.ForceCharacterAction; bodyFrame = tamingAction.ForceCharacterActionFrameIndex ?? 0; } if (tamingAction.ForceCharacterFaceHide) //强制表情 { emotionName = null; } else if (!string.IsNullOrEmpty(tamingAction.ForceCharacterFace)) { emotionName = tamingAction.ForceCharacterFace; faceFrame = tamingAction.ForceCharacterFaceFrameIndex ?? 0; } if (tamingAction.ForceCharacterFlip) { bodyFlip = true; } else if (this.Taming.Node.FindNodeByPath(@"info\flip").GetValueEx(0) != 0) { bodyFlip = true; } if (this.Taming.Node.FindNodeByPath(@"info\removeBody").GetValueEx(0) != 0) //自动适用动作 { actionName = "hideBody"; bodyFrame = 0; } } } if (!string.IsNullOrEmpty(actionName)) { bodyAction = GetActionFrame(actionName, bodyFrame); if (bodyAction != null && bodyFlip) { bodyAction.Flip = true; } } if (!string.IsNullOrEmpty(emotionName)) { faceAction = GetFaceFrame(emotionName, faceFrame); } return CreateFrame(bodyAction, faceAction, tamingAction); } public Bone CreateFrame(ActionFrame bodyAction, ActionFrame faceAction, ActionFrame tamingAction) { //获取所有部件 Wz_Node[] playerNodes = LinkPlayerParts(bodyAction, faceAction); Wz_Node[] tamingNodes = LinkTamingParts(tamingAction); //根骨骼 作为角色原点 Bone bodyRoot = new Bone("@root"); bodyRoot.Position = Point.Empty; CreateBone(bodyRoot, playerNodes, bodyAction.Face); SetBonePoperty(bodyRoot, BoneGroup.Character, bodyAction); if (tamingNodes != null && tamingNodes.Length > 0) { //骑宠骨骼 Bone tamingRoot = new Bone("@root2"); tamingRoot.Position = Point.Empty; CreateBone(tamingRoot, tamingNodes); //建立虚拟身体骨骼 Bone newRoot = new Bone("@rootAll"); newRoot.Position = Point.Empty; bodyRoot.Parent = newRoot; //合并骨骼 for (int i = tamingRoot.Children.Count - 1; i >= 0; i--) { var childBone = tamingRoot.Children[i]; Bone bone = newRoot.FindChild(childBone.Name); if (bone != null) //翻转骨骼 { RotateBone(newRoot, bone); bone.Name = "@" + bone.Name; childBone.Parent = newRoot; bone.Parent = childBone; bone.Position = Point.Empty; } else //直接添加 { childBone.Parent = newRoot; } } newRoot.Skins.AddRange(tamingRoot.Skins); return newRoot; } return bodyRoot; } private void SetBonePoperty(Bone bone, BoneGroup group, ActionFrame property) { bone.Group = group; bone.Property = property; foreach (var child in bone.Children) { SetBonePoperty(child, group, property); } } private void RotateBone(Bone root, Bone childBone) { while (childBone.Parent != null && childBone.Parent != root) { var p = childBone.Parent; var pp = p.Parent; var cpos = childBone.Position; var ppos = p.Position; childBone.Position = new Point(cpos.X + ppos.X, cpos.Y + ppos.Y); p.Position = new Point(-cpos.X, -cpos.Y); childBone.Parent = pp; p.Parent = childBone; } } private void CreateBone(Bone root, Wz_Node[] frameNodes, bool? bodyFace = null) { bool face = true; foreach (Wz_Node partNode in frameNodes) { Wz_Node linkPartNode = partNode; while (linkPartNode.Value is Wz_Uol) { linkPartNode = linkPartNode.GetValue().HandleUol(linkPartNode); } foreach (Wz_Node childNode in linkPartNode.Nodes) //分析部件 { Wz_Node linkNode = childNode; while (linkNode?.Value is Wz_Uol uol) { linkNode = uol.HandleUol(linkNode); } if (linkNode == null) { continue; } if (childNode.Text == "hairShade") { linkNode = childNode.FindNodeByPath("0"); if (linkNode == null) { continue; } } if (linkNode.Value is Wz_Png) { //过滤纹理 switch (childNode.Text) { case "face": if (!(bodyFace ?? face)) continue; break; case "ear": if (this.EarType != 1) continue; break; case "lefEar": if (this.EarType != 2) continue; break; case "highlefEar": if (this.EarType != 3) continue; break; case "hairOverHead": case "backHairOverCape": case "backHair": if (HairCover) continue; break; case "hair": case "backHairBelowCap": if (!HairCover) continue; break; case "hairShade": if (!ShowHairShade) continue; break; default: if (childNode.Text.StartsWith("weapon")) { //检查是否多武器颜色 if (linkNode.ParentNode.FindNodeByPath("weapon1") != null) { //只追加限定武器 string weaponName = "weapon" + (this.WeaponIndex == 0 ? "" : this.WeaponIndex.ToString()); if (childNode.Text != weaponName) { continue; } } } break; } //读取纹理 Skin skin = new Skin(); skin.Name = childNode.Text; skin.Image = BitmapOrigin.CreateFromNode(linkNode, PluginBase.PluginManager.FindWz); var zNode = linkNode.FindNodeByPath("z"); if (zNode != null) { var val = zNode.Value; var zIndex = zNode.GetValueEx(null); if (zIndex != null) { skin.ZIndex = zIndex.Value; } else { skin.Z = zNode.GetValue(); } } //读取骨骼 Wz_Node mapNode = linkNode.FindNodeByPath("map"); if (mapNode != null) { Bone parentBone = null; foreach (var map in mapNode.Nodes) { string mapName = map.Text; Point mapOrigin = map.GetValue(); if (mapName == "muzzle") //特殊处理 忽略 { continue; } if (parentBone == null) //主骨骼 { parentBone = AppendBone(root, null, skin, mapName, mapOrigin); } else //级联骨骼 { AppendBone(root, parentBone, skin, mapName, mapOrigin); } } } else { root.Skins.Add(skin); } } else { switch (childNode.Text) { case "face": face = Convert.ToInt32(childNode.Value) != 0; break; } } } } } private Bone AppendBone(Bone root, Bone parentBone, Skin skin, string mapName, Point mapOrigin) { Bone bone = root.FindChild(mapName); bool exists; if (bone == null) //创建骨骼 { exists = false; bone = new Bone(mapName); bone.Position = mapOrigin; } else { exists = true; } if (parentBone == null) //主骨骼 { if (!exists) //基准骨骼不存在 加到root { parentBone = root; bone.Parent = parentBone; bone.Skins.Add(skin); skin.Offset = new Point(-mapOrigin.X, -mapOrigin.Y); } else //如果已存在 创建一个关节 { Bone bone0 = new Bone("@" + bone.Name + "_" + skin.Name); bone0.Position = new Point(-mapOrigin.X, -mapOrigin.Y); bone0.Parent = bone; parentBone = bone0; bone0.Skins.Add(skin); skin.Offset = Point.Empty; } return parentBone; } else //级联骨骼 { if (!exists) { bone.Parent = parentBone; bone.Position = mapOrigin; } else //如果已存在 替换 { bone.Parent = parentBone; bone.Position = mapOrigin; } return null; } } public BitmapOrigin DrawFrame(Bone bone) { var bmpLayers = this.CreateFrameLayers(bone); //计算最大图像范围 Rectangle rect = Rectangle.Empty; foreach (var layer in bmpLayers) { var newRect = new Rectangle(layer.OpOrigin, layer.Bitmap.Size); rect = rect.Size.IsEmpty ? newRect : Rectangle.Union(rect, newRect); } rect = rect.Size.IsEmpty ? Rectangle.Empty : rect; if (rect.IsEmpty) { return new BitmapOrigin(); } //绘制图像 Bitmap bmp = new Bitmap(rect.Width, rect.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb); Graphics g = Graphics.FromImage(bmp); foreach (var layer in bmpLayers) { g.DrawImage(layer.Bitmap, layer.OpOrigin.X - rect.X, layer.OpOrigin.Y - rect.Y); } g.Dispose(); return new BitmapOrigin(bmp, -rect.X, -rect.Y); } public BitmapOrigin[] CreateFrameLayers(Bone bone) { List layers = GenerateLayer(bone); layers.Sort((l0, l1) => l1.ZIndex.CompareTo(l0.ZIndex)); var bmpLayers = new BitmapOrigin[layers.Count]; for (int i = 0; i < bmpLayers.Length; i++) { var layer = layers[i]; bmpLayers[i] = new BitmapOrigin(layer.Bitmap, -layer.Position.X, -layer.Position.Y); } return bmpLayers; } private unsafe void TransformPixel(Bitmap src, Bitmap dst, Matrix mt) { var pxData1 = src.LockBits(new Rectangle(0, 0, src.Width, src.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb); var pxData2 = dst.LockBits(new Rectangle(0, 0, dst.Width, dst.Height), System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb); for (int y = 0; y < pxData1.Height; y++) { int* pSrc = (int*)((byte*)pxData1.Scan0 + y * pxData1.Stride); for (int x = 0; x < pxData1.Width; x++, pSrc++) { Point newPoint = new Point( x * mt.m11 + y * mt.m21 + mt.m31, x * mt.m12 + y * mt.m22 + mt.m32 ); int* pDst = (int*)((byte*)pxData2.Scan0 + newPoint.Y * pxData2.Stride + newPoint.X * 4); *pDst = *pSrc; } } dst.UnlockBits(pxData2); src.UnlockBits(pxData1); } private List GenerateLayer(Bone bone) { var layers = new List(); //计算角色原点,用于翻转偏移 var rootBone = bone.FindChild("@root"); Point rootPos = Point.Empty; { var temp = rootBone; while (temp != null) { rootPos.Offset(temp.Position); temp = temp.Parent; } } Action func = null; func = (parent, pos) => { pos.Offset(parent.Position); var prop = parent.Property; foreach (Skin skin in parent.Skins) { var layer = new AvatarLayer(); var bmp = skin.Image.Bitmap; var position = new Point(pos.X + skin.Offset.X - skin.Image.Origin.X, pos.Y + skin.Offset.Y - skin.Image.Origin.Y); //计算身体旋转和反转 if (parent.Group == BoneGroup.Character && prop != null) { Bitmap bmp2; Rectangle rect2; if (RotateFlipImage(bmp, new Rectangle(position, bmp.Size), prop.Flip, prop.Rotate, prop.Move, rootPos, out bmp2, out rect2)) { if (bmp2 != null) { bmp = bmp2; } position = rect2.Location; } } layer.Bitmap = bmp; layer.Position = position; if (!string.IsNullOrEmpty(skin.Z)) { layer.ZIndex = this.ZMap.IndexOf(skin.Z); if (layer.ZIndex < 0) { layer.ZIndex = this.ZMap.Count; } } else { layer.ZIndex = (skin.ZIndex < 0) ? (this.ZMap.Count - skin.ZIndex) : (-1 - skin.ZIndex); } layers.Add(layer); } foreach (var child in parent.Children) { func(child, pos); } }; func(bone, Point.Empty); return layers; } private bool RotateFlipImage(Bitmap bmp, Rectangle rect, bool flip, int rotate, Point move, Point origin, out Bitmap newBmp, out Rectangle newRect) { bool changed = false; newBmp = null; rect.Offset(-origin.X, -origin.Y); if (flip || rotate != 0) //重新绘制 旋转和镜像 { Matrix mt; switch (rotate) { case 0: mt = Matrix.Identity; break; case 90: mt = new Matrix(0, 1, -1, 0, bmp.Height - 1, 0); rect = new Rectangle(-rect.Bottom, rect.X, bmp.Height, bmp.Width); break; case 180: mt = new Matrix(-1, 0, 0, -1, bmp.Width - 1, bmp.Height - 1); rect = new Rectangle(-rect.Right, -rect.Bottom, bmp.Width, bmp.Height); break; case 270: mt = new Matrix(0, -1, 1, 0, 0, bmp.Width - 1); rect = new Rectangle(rect.Y, -rect.Right, bmp.Height, bmp.Width); break; default: goto case 0; } if (flip) { mt *= new Matrix(-1, 0, 0, 1, rect.Width - 1, 0); rect.X = -rect.Right; } newBmp = new Bitmap(rect.Width, rect.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb); TransformPixel(bmp, newBmp, mt); changed = true; } if (move != Point.Empty) { rect.Offset(flip && rotate != 0 ? -move.X : move.X, move.Y); changed = true; } if (changed) { rect.Offset(origin.X, origin.Y); newRect = rect; return true; } else { newBmp = null; newRect = Rectangle.Empty; return false; } } private Wz_Node[] LinkPlayerParts(ActionFrame bodyAction, ActionFrame faceAction) { //寻找所有部件 List partNode = new List(); //链接人 if (this.Body != null && this.Head != null && bodyAction != null && this.Body.Visible && this.Head.Visible) { //身体 Wz_Node bodyNode = FindBodyActionNode(bodyAction); partNode.Add(bodyNode); //计算面向 bool? face = bodyAction.Face; //扩展动作规定头部 if (face == null && bodyNode != null) //链接的body内规定 { Wz_Node propNode = bodyNode.FindNodeByPath("face"); if (propNode != null) { face = propNode.GetValue(0) != 0; } } //脸饰附加属性 bool invisibleFace = false; if (this.FaceAccessory != null && this.FaceAccessory.Visible) { invisibleFace = this.FaceAccessory.Node.FindNodeByPath(@"info\invisibleFace").GetValueEx(0) != 0; } //头部 var headNode = FindActionFrameNode(this.Head.Node, bodyAction); if (headNode == null) { string actName = this.GetHeadActionName(bodyAction.Action, face); if (actName != null) { ActionFrame headAction = new ActionFrame() { Action = actName }; headNode = FindActionFrameNode(this.Head.Node, headAction); } } partNode.Add(headNode); //脸 if (this.Face != null && this.Face.Visible && faceAction != null) { if ((face ?? true) && !invisibleFace) { partNode.Add(FindActionFrameNode(this.Face.Node, faceAction)); } } //毛 if (headNode != null && this.Hair != null && this.Hair.Visible) { var hairNode = FindActionFrameNode(this.Hair.Node, bodyAction); if (hairNode == null) { string actName = this.GetHairActionName(bodyAction.Action, face); if (actName != null) { ActionFrame hairAction = new ActionFrame() { Action = actName, Frame = 0 }; hairNode = FindActionFrameNode(this.Hair.Node, hairAction); } } partNode.Add(hairNode); } //其他部件 for (int i = 4; i < 16; i++) { var part = this.Parts[i]; if (part != null && part.Visible) { if (i == 12 && Gear.GetGearType(part.ID.Value) == GearType.cashWeapon) //点装武器 { var wpNode = part.Node.FindNodeByPath(this.WeaponType.ToString()); partNode.Add(FindActionFrameNode(wpNode, bodyAction)); } else if (i == 14) //脸 { if (face ?? true) { partNode.Add(FindActionFrameNode(part.Node, faceAction)); } } else //其他部件 { partNode.Add(FindActionFrameNode(part.Node, bodyAction)); } } } } partNode.RemoveAll(node => node == null); return partNode.ToArray(); } private Wz_Node[] LinkTamingParts(ActionFrame tamingAction) { List partNode = new List(); //链接马 if (this.Taming != null && this.Taming.Visible && tamingAction != null) { partNode.Add(FindActionFrameNode(this.Taming.Node, tamingAction)); if (this.Saddle != null && this.Saddle.Visible) { var saddleNode = this.Saddle.Node.FindNodeByPath(false, this.Taming.ID.ToString()); partNode.Add(FindActionFrameNode(saddleNode, tamingAction)); } } partNode.RemoveAll(node => node == null); return partNode.ToArray(); } private Wz_Node FindBodyActionNode(ActionFrame actionFrame) { Wz_Node actionNode = null; if (this.Body != null) { actionNode = this.Body.Node.FindNodeByPath(actionFrame.Action); } if (actionNode == null) { Wz_Node bodyNode = PluginBase.PluginManager.FindWz("Character\\00002000.img"); actionNode = bodyNode.FindNodeByPath(actionFrame.Action); } if (actionNode != null) { actionNode = actionNode.FindNodeByPath(actionFrame.Frame.ToString()); } return actionNode; } private Wz_Node FindActionFrameNode(Wz_Node parent, ActionFrame actionFrame) { if (parent == null || actionFrame == null) { return null; } var actionNode = parent; foreach (var path in new[] { actionFrame.Action, actionFrame.Frame.ToString() }) { if (actionNode != null && !string.IsNullOrEmpty(path)) { actionNode = actionNode.FindNodeByPath(path); //处理uol Wz_Uol uol; while ((uol = actionNode.GetValueEx(null)) != null) { actionNode = uol.HandleUol(actionNode); } } } return actionNode; } private string GetHeadActionName(string bodyAction, bool? face) { if (bodyAction.StartsWith("PB") && (face ?? false) == false) { return null; } if (bodyAction.StartsWith("PVPA8")) { return null; } if (face != null) { return (face ?? false) ? "front" : "back"; } return null; } private string GetHairActionName(string bodyAction, bool? face) { if (bodyAction == "hide" || bodyAction == "blink" || bodyAction.EndsWith("Blink")) { return null; } if (bodyAction.StartsWith("PB") && (face ?? false) == false) { return null; } if (bodyAction.StartsWith("create")) { return null; } if (bodyAction.EndsWith("prone") && (face ?? false)) { return "prone"; } if (bodyAction.EndsWith("proneStab") && (face ?? false)) { return "proneStab"; } if (face != null) { return face.Value ? "stand1" : "ladder"; } return null; } #region parts /// /// 身体 /// public AvatarPart Body { get { return this.Parts[0]; } set { this.Parts[0] = value; } } /// /// 头部 /// public AvatarPart Head { get { return this.Parts[1]; } set { this.Parts[1] = value; } } /// /// 脸部 /// public AvatarPart Face { get { return this.Parts[2]; } set { this.Parts[2] = value; } } /// /// 头发 /// public AvatarPart Hair { get { return this.Parts[3]; } set { this.Parts[3] = value; } } /// /// 帽子 /// public AvatarPart Cap { get { return this.Parts[4]; } set { this.Parts[4] = value; } } /// /// 上衣 /// public AvatarPart Coat { get { return this.Parts[5]; } set { this.Parts[5] = value; } } /// /// 套装 /// public AvatarPart Longcoat { get { return this.Parts[6]; } set { this.Parts[6] = value; } } /// /// 胖次 /// public AvatarPart Pants { get { return this.Parts[7]; } set { this.Parts[7] = value; } } /// /// 鞋子 /// public AvatarPart Shoes { get { return this.Parts[8]; } set { this.Parts[8] = value; } } /// /// 手套 /// public AvatarPart Glove { get { return this.Parts[9]; } set { this.Parts[9] = value; } } /// /// 盾牌 /// public AvatarPart SubWeapon { get { return this.Parts[10]; } set { this.Parts[10] = value; } } /// /// 披风 /// public AvatarPart Cape { get { return this.Parts[11]; } set { this.Parts[11] = value; } } /// /// 武器 /// public AvatarPart Weapon { get { return this.Parts[12]; } set { this.Parts[12] = value; } } /// /// 耳环 /// public AvatarPart Earrings { get { return this.Parts[13]; } set { this.Parts[13] = value; } } /// /// 脸饰 /// public AvatarPart FaceAccessory { get { return this.Parts[14]; } set { this.Parts[14] = value; } } /// /// 眼饰 /// public AvatarPart EyeAccessory { get { return this.Parts[15]; } set { this.Parts[15] = value; } } /// /// 骑宠 /// public AvatarPart Taming { get { return this.Parts[16]; } set { this.Parts[16] = value; } } /// /// 鞍子 /// public AvatarPart Saddle { get { return this.Parts[17]; } set { this.Parts[17] = value; } } #endregion #region statics private static readonly string[] baseActions = new[]{ "walk1", "walk2", "stand1", "stand2", "alert", "swingO1", "swingO2", "swingO3", "swingOF", "swingT1", "swingT2", "swingT3", "swingTF", "swingP1", "swingP2", "swingPF", "stabO1", "stabO2", "stabOF", "stabT1", "stabT2", "stabTF", "shoot1", "shoot2", "shootF", "proneStab", "prone", "heal", "fly", "jump", "sit", "ladder", "rope" }; public static readonly ReadOnlyCollection BaseActions = new ReadOnlyCollection(baseActions); #endregion private class AvatarLayer { public Bitmap Bitmap { get; set; } public Point Position { get; set; } public int ZIndex { get; set; } } private struct Matrix { public Matrix(int m11, int m12, int m21, int m22, int m31, int m32) { this.m11 = m11; this.m12 = m12; this.m21 = m21; this.m22 = m22; this.m31 = m31; this.m32 = m32; } public int m11, m12, m21, m22, m31, m32; public static Matrix Identity { get { return new Matrix(1, 0, 0, 1, 0, 0); } } public static Matrix operator *(Matrix mt1, Matrix mt2) { return new Matrix( mt1.m11 * mt2.m11 + mt1.m12 * mt2.m21, mt1.m11 * mt2.m12 + mt1.m12 * mt2.m22, mt1.m21 * mt2.m11 + mt1.m22 * mt2.m21, mt1.m21 * mt2.m12 + mt1.m22 * mt2.m22, mt1.m31 * mt2.m11 + mt1.m32 * mt2.m21 + mt2.m31, mt1.m31 * mt2.m12 + mt1.m32 * mt2.m22 + mt2.m32); } } } } ================================================ FILE: WzComparerR2.Avatar/AvatarPart.cs ================================================ using System; using System.Collections.Generic; using System.Text; using System.Drawing; using WzComparerR2.WzLib; using System.Text.RegularExpressions; namespace WzComparerR2.Avatar { public class AvatarPart { public AvatarPart(Wz_Node node) { this.Node = node; this.Visible = true; this.LoadInfo(); } public Wz_Node Node { get; private set; } public string ISlot { get; private set; } public BitmapOrigin Icon { get; private set; } public bool Visible { get; set; } public int? ID { get; private set; } private void LoadInfo() { var m = Regex.Match(Node.Text, @"^(\d+)\.img$"); if (m.Success) { this.ID = Convert.ToInt32(m.Result("$1")); } Wz_Node infoNode = this.Node.FindNodeByPath("info"); if (infoNode == null) { return; } foreach (var node in infoNode.Nodes) { switch (node.Text) { case "islot": this.ISlot = node.GetValue(); break; case "icon": this.Icon = BitmapOrigin.CreateFromNode(node, PluginBase.PluginManager.FindWz); break; } } } } } ================================================ FILE: WzComparerR2.Avatar/Bone.cs ================================================ using System; using System.Collections.Generic; using System.Text; using System.Drawing; namespace WzComparerR2.Avatar { public class Bone { public Bone(string name) { this.Name = name; this.Children = new List(); this.Skins = new List(); } public string Name { get; set; } public Point Position { get; set; } public List Children { get; private set; } public List Skins { get; private set; } public BoneGroup Group { get; set; } public ActionFrame Property { get; set; } private Bone parent; public Bone Parent { get { return this.parent; } set { Bone oldParent = this.parent; if (oldParent != value) { if (oldParent != null) { oldParent.Children.Remove(this); } if (value != null) { value.Children.Add(this); } this.parent = value; } } } public Bone FindChild(string name) { foreach (Bone bone in Children) { if (bone.Name == name) return bone; if (bone.Children.Count > 0) { Bone c = bone.FindChild(name); if (c != null) return c; } } return null; } } public enum BoneGroup { Unknown = 0, Character, Taming } } ================================================ FILE: WzComparerR2.Avatar/Entry.cs ================================================ using System; using System.Collections.Generic; using System.Text; using System.Drawing; using DevComponents.DotNetBar; using DevComponents.Editors; using WzComparerR2.PluginBase; using WzComparerR2.WzLib; using WzComparerR2.Common; using WzComparerR2.Avatar.UI; using System.Linq; namespace WzComparerR2.Avatar { public class Entry : PluginEntry { public Entry(PluginContext context) : base(context) { } protected override void OnLoad() { var f = new AvatarForm(); f.PluginEntry = this; var tabCtrl = f.GetTabPanel(); Context.AddTab(f.Text, tabCtrl); Context.SelectedNode1Changed += f.OnSelectedNode1Changed; Context.WzClosing += f.OnWzClosing; this.Tab = tabCtrl.TabItem; } public SuperTabItem Tab { get; private set; } public void btnSetting_Click(object sender, EventArgs e) { AvatarCanvas canvas = new AvatarCanvas(); canvas.LoadZ(); canvas.LoadActions(); canvas.LoadEmotions(); /* cmbAction.Items.Clear(); foreach (var action in canvas.Actions) { ComboItem cmbItem = new ComboItem(action.Name); switch (action.Level) { case 0: cmbItem.FontStyle = System.Drawing.FontStyle.Bold; cmbItem.ForeColor = Color.Indigo; break; case 1: cmbItem.ForeColor = Color.Indigo; break; } cmbAction.Items.Add(cmbItem); }*/ canvas.ActionName = "stand1"; canvas.EmotionName = "shine"; canvas.TamingActionName = "stand1"; AddPart(canvas, "Character\\00002000.img"); AddPart(canvas, "Character\\00012000.img"); AddPart(canvas, "Character\\Face\\00020000.img"); AddPart(canvas, "Character\\Hair\\00030000.img"); AddPart(canvas, "Character\\Coat\\01040036.img"); AddPart(canvas, "Character\\Pants\\01060026.img"); //AddPart(canvas, "Character\\Weapon\\01442000.img"); //AddPart(canvas, "Character\\Weapon\\01382007.img"); //AddPart(canvas, "Character\\Weapon\\01332000.img"); //AddPart(canvas, "Character\\Weapon\\01342000.img"); var faceFrames = canvas.GetFaceFrames(canvas.EmotionName); //foreach (var action in canvas.Actions) foreach (var action in new[] { "walk1", "jump", "stand1"}) { Gif gif = new Gif(); var actionFrames = canvas.GetActionFrames(action); foreach (var frame in actionFrames) { if (frame.Delay != 0) { var bone = canvas.CreateFrame(frame, faceFrames[0], null); var bmp = canvas.DrawFrame(bone); Point pos = bmp.OpOrigin; pos.Offset(frame.Flip ? new Point(-frame.Move.X, frame.Move.Y) : frame.Move); GifFrame f = new GifFrame(bmp.Bitmap, new Point(-pos.X, -pos.Y), Math.Abs(frame.Delay)); gif.Frames.Add(f); } } var gifFile = gif.EncodeGif(Color.Transparent); string fileName = "D:\\ms\\new_" + action.Replace('\\', '.'); gifFile.Save(fileName + (gif.Frames.Count == 1 ? ".png" : ".gif")); var fd = new System.Drawing.Imaging.FrameDimension(gifFile.FrameDimensionsList[0]); //获取帧数(gif图片可能包含多帧,其它格式图片一般仅一帧) int count = gifFile.GetFrameCount(fd); for (int i = 0; i < count; i++) { gifFile.SelectActiveFrame(fd, i); gifFile.Save(fileName + "_" + i + ".png", System.Drawing.Imaging.ImageFormat.Png); } gifFile.Dispose(); } if (true) { Gif gif = CreateChair(canvas); var gifFile = gif.EncodeGif(Color.Transparent, 0); string fileName = "D:\\d16"; if (false) { var fd = new System.Drawing.Imaging.FrameDimension(gifFile.FrameDimensionsList[0]); //获取帧数(gif图片可能包含多帧,其它格式图片一般仅一帧) int count = gifFile.GetFrameCount(fd); for (int i = 0; i < count; i++) { gifFile.SelectActiveFrame(fd, i); gifFile.Save(fileName + "_" + i + ".png", System.Drawing.Imaging.ImageFormat.Png); } } gifFile.Save(fileName + (gif.Frames.Count == 1 ? ".png" : ".gif")); gifFile.Dispose(); } } private Gif CreateContinueAction(AvatarCanvas canvas) { string afterImage = null; Wz_Node defaultAfterImageNode = null; if (canvas.Weapon != null) { afterImage = canvas.Weapon.Node.FindNodeByPath(false, "info", "afterImage").GetValueEx(null); if (!string.IsNullOrEmpty(afterImage)) { defaultAfterImageNode = PluginManager.FindWz("Character\\Afterimage\\" + afterImage + ".img\\10"); } } GifCanvas gifCanvas = new GifCanvas(); gifCanvas.Layers.Add(new GifLayer()); int delay = 0; //foreach (string act in new[] { "alert", "swingP1PoleArm", "doubleSwing", "tripleSwing" }) //foreach (var act in new object[] { "alert", "swingP1PoleArm", "overSwingDouble", "overSwingTriple" }) var faceFrames = canvas.GetFaceFrames(canvas.EmotionName); //foreach (string act in new[] { "PBwalk1", "PBstand4", "PBstand5" }) foreach (var act in new object[] { PluginManager.FindWz("Skill\\2312.img\\skill\\23121004"), "stand1", PluginManager.FindWz("Skill\\2312.img\\skill\\23121052"), //PluginManager.FindWz("Skill\\2112.img\\skill\\21120010"), //PluginManager.FindWz("Skill\\200.img\\skill\\2001002"), //PluginManager.FindWz("Skill\\230.img\\skill\\2301003"), //PluginManager.FindWz("Skill\\230.img\\skill\\2301004"), //PluginManager.FindWz("Skill\\231.img\\skill\\2311003"), //PluginManager.FindWz("Skill\\13100.img\\skill\\131001010"), //"PBwalk1" }) { string actionName = null; Wz_Node afterImageNode = null; List effects = new List(); if (act is string) { actionName = (string)act; } else if (act is Wz_Node) { Wz_Node skillNode = (Wz_Node)(object)act; actionName = skillNode.FindNodeByPath("action\\0").GetValueEx(null); if (!string.IsNullOrEmpty(afterImage)) { afterImageNode = skillNode.FindNodeByPath("afterimage\\" + afterImage); } for (int i = -1; ; i++) { Wz_Node effNode = skillNode.FindNodeByPath("effect" + (i > -1 ? i.ToString() : "")); if (effNode == null) break; effects.Add(Gif.CreateFromNode(effNode, PluginManager.FindWz)); } } if (string.IsNullOrEmpty(actionName)) { continue; } //afterImageNode = afterImageNode ?? defaultAfterImageNode; //添加特效帧 foreach (var effGif in effects) { if (effGif != null && effGif.Frames.Count > 0) { var layer = new GifLayer(); if (delay > 0) { layer.AddBlank(delay); } effGif.Frames.ForEach(af => layer.AddFrame((GifFrame)af)); gifCanvas.Layers.Add(layer); } } //添加角色帧 ActionFrame[] actionFrames = canvas.GetActionFrames(actionName); for (int i = 0; i < actionFrames.Length; i++) { var frame = actionFrames[i]; if (frame.Delay != 0) { //绘制角色主动作 var bone = canvas.CreateFrame(frame, null, null); var bmp = canvas.DrawFrame(bone); GifFrame f = new GifFrame(bmp.Bitmap, bmp.Origin, Math.Abs(frame.Delay)); gifCanvas.Layers[0].Frames.Add(f); //寻找刀光帧 if (afterImageNode != null) { var afterImageAction = afterImageNode.FindNodeByPath(false, actionName, i.ToString()); if (afterImageAction != null) { Gif aGif = Gif.CreateFromNode(afterImageAction, PluginManager.FindWz); if (aGif != null && aGif.Frames.Count > 0) //添加新图层 { var layer = new GifLayer(); if (delay > 0) { layer.AddBlank(delay); } aGif.Frames.ForEach(af => layer.AddFrame((GifFrame)af)); gifCanvas.Layers.Add(layer); } } } delay += f.Delay; } } } return gifCanvas.Combine(); } private Gif CreateKeyDownAction(AvatarCanvas canvas) { string afterImage = null; Wz_Node defaultAfterImageNode = null; if (canvas.Weapon != null) { afterImage = canvas.Weapon.Node.FindNodeByPath(false, "info", "afterImage").GetValueEx(null); if (!string.IsNullOrEmpty(afterImage)) { defaultAfterImageNode = PluginManager.FindWz("Character\\Afterimage\\" + afterImage + ".img\\10"); } } GifCanvas gifCanvas = new GifCanvas(); var layers = new List>(); var actLayer = new GifLayer(); //gifCanvas.Layers.Add(new GifLayer()); int delay = 0; var faceFrames = canvas.GetFaceFrames(canvas.EmotionName); var skillNode = PluginManager.FindWz("Skill\\2112.img\\skill\\21120018"); var actionName = skillNode.FindNodeByPath("action\\0").GetValueEx(null); int keydownCount = 2; foreach (var part in new [] {"prepare", "keydown", "keydownend"}) { var effects = new List>(); for (int i = -1; ; i++) { Wz_Node effNode = skillNode.FindNodeByPath(part + (i > -1 ? i.ToString() : "")); if (effNode == null) break; var gif = Gif.CreateFromNode(effNode, PluginManager.FindWz); var z = effNode.FindNodeByPath("z").GetValueEx(0); effects.Add(new Tuple(gif, z)); } int effDelay = 0; //添加特效帧 foreach (var effGif in effects) { if (effGif.Item1 != null && effGif.Item1.Frames.Count > 0) { var layer = new GifLayer(); if (delay > 0) { layer.AddBlank(delay); } int fDelay = 0; for(int i = 0, i0 = part == "keydown" ? keydownCount : 1; i < i0; i++) { effGif.Item1.Frames.ForEach(af => layer.AddFrame((GifFrame)af)); layers.Add(new Tuple(layer,effGif.Item2)); fDelay+= effGif.Item1.Frames.Select(f => f.Delay).Sum(); } effDelay = Math.Max(fDelay, effDelay); } } delay += effDelay; } //添加角色帧 ActionFrame[] actionFrames = canvas.GetActionFrames(actionName); int adelay = 0; while (adelay < delay) { for (int i = 0; i < actionFrames.Length; i++) { var frame = actionFrames[i]; if (frame.Delay != 0) { //绘制角色主动作 var bone = canvas.CreateFrame(frame, null, null); var bmp = canvas.DrawFrame(bone); GifFrame f = new GifFrame(bmp.Bitmap, bmp.Origin, Math.Abs(frame.Delay)); actLayer.Frames.Add(f); adelay += f.Delay; //delay += f.Delay; } } } layers.Add(new Tuple(actLayer, 0)); //按照z排序 layers.Sort((a, b) => a.Item2.CompareTo(b.Item2)); gifCanvas.Layers.AddRange(layers.Select(t => t.Item1)); return gifCanvas.Combine(); } private Gif CreateChair(AvatarCanvas canvas) { GifCanvas gifCanvas = new GifCanvas(); var layers = new List>(); var actLayer = new GifLayer(); //gifCanvas.Layers.Add(new GifLayer()); int delay = 0; var faceFrames = canvas.GetFaceFrames(canvas.EmotionName); var ChairNode = PluginManager.FindWz(@"Item\Install\0301.img\03015660"); var actionName = "sit"; var pos = ChairNode.FindNodeByPath(@"info\bodyRelMove").GetValueEx(null); Point browPos = new Point(-5, -48); //添加特效帧 { var effects = new List>(); for (int i = 1; ; i++) { Wz_Node effNode = ChairNode.FindNodeByPath("effect"+( i > 1 ? i.ToString() : "")); if (effNode == null) break; var gif = Gif.CreateFromNode(effNode, PluginManager.FindWz); var z = effNode.FindNodeByPath("z").GetValueEx(0); var isPos = effNode.Nodes["pos"].GetValueEx(0); delay = Math.Max(delay, gif.Frames.Sum(f => f.Delay)); var layer = new GifLayer(); if (isPos == 1) { layer.Frames.AddRange(gif.Frames.Select(f => { GifFrame frame = (GifFrame)f; frame.Origin = new Point(frame.Origin.X - browPos.X, frame.Origin.Y - browPos.Y); return frame; })); } else { layer.Frames.AddRange(gif.Frames.Select(f => (GifFrame)f)); } layers.Add(new Tuple(layer, z)); } } //添加角色帧 ActionFrame[] actionFrames = canvas.GetActionFrames(actionName); int adelay = 0; var bodyMove = pos == null ? Point.Empty : new Point(pos.X, pos.Y); while (adelay < delay) { for (int i = 0; i < actionFrames.Length; i++) { var frame = actionFrames[i]; if (frame.Delay != 0) { //绘制角色主动作 var bone = canvas.CreateFrame(frame, faceFrames[0], null); bone.Position = bodyMove; var bmp = canvas.DrawFrame(bone); GifFrame f = new GifFrame(bmp.Bitmap, bmp.Origin, Math.Abs(frame.Delay)); actLayer.Frames.Add(f); adelay += f.Delay; //delay += f.Delay; } } } layers.Add(new Tuple(actLayer, 0)); //按照z排序 layers.Sort((a, b) => a.Item2.CompareTo(b.Item2)); gifCanvas.Layers.AddRange(layers.Select(t => t.Item1)); return gifCanvas.Combine(); } void AddPart(AvatarCanvas canvas, string imgPath) { Wz_Node imgNode = PluginManager.FindWz(imgPath); if (imgNode != null) { canvas.AddPart(imgNode); } } } } ================================================ FILE: WzComparerR2.Avatar/Properties/AssemblyInfo.cs ================================================ using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // 有关程序集的常规信息通过以下 // 特性集控制。更改这些特性值可修改 // 与程序集关联的信息。 [assembly: AssemblyTitle("WzComparerR2.Avatar")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Kagamia Studio")] [assembly: AssemblyProduct("WzComparerR2.Avatar")] [assembly: AssemblyCopyright("Copyright © Kagamia Studio 2015-2025")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] // 将 ComVisible 设置为 false 使此程序集中的类型 // 对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型, // 则将该类型上的 ComVisible 特性设置为 true。 [assembly: ComVisible(false)] // 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID [assembly: Guid("1311079c-100f-4dd5-9957-3064078a67de")] // 程序集的版本信息由下面四个值组成: // // 主版本 // 次版本 // 生成号 // 修订号 // // 可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值, // 方法是按如下所示使用“*”: // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("2.2.0.0")] [assembly: AssemblyFileVersion("2.2.0.10725")] ================================================ FILE: WzComparerR2.Avatar/Properties/Resources.Designer.cs ================================================ //------------------------------------------------------------------------------ // // 此代码由工具生成。 // 运行时版本:4.0.30319.42000 // // 对此文件的更改可能会导致不正确的行为,并且如果 // 重新生成代码,这些更改将会丢失。 // //------------------------------------------------------------------------------ namespace WzComparerR2.Avatar.Properties { using System; /// /// 一个强类型的资源类,用于查找本地化的字符串等。 /// // 此类是由 StronglyTypedResourceBuilder // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen // (以 /str 作为命令选项),或重新生成 VS 项目。 [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class Resources { private static global::System.Resources.ResourceManager resourceMan; private static global::System.Globalization.CultureInfo resourceCulture; [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal Resources() { } /// /// 返回此类使用的缓存的 ResourceManager 实例。 /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("WzComparerR2.Avatar.Properties.Resources", typeof(Resources).Assembly); resourceMan = temp; } return resourceMan; } } /// /// 重写当前线程的 CurrentUICulture 属性,对 /// 使用此强类型资源类的所有资源查找执行重写。 /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Globalization.CultureInfo Culture { get { return resourceCulture; } set { resourceCulture = value; } } /// /// 查找 System.Drawing.Bitmap 类型的本地化资源。 /// internal static System.Drawing.Bitmap _lock { get { object obj = ResourceManager.GetObject("_lock", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// 查找 System.Drawing.Bitmap 类型的本地化资源。 /// internal static System.Drawing.Bitmap arrow_in { get { object obj = ResourceManager.GetObject("arrow_in", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// 查找 System.Drawing.Bitmap 类型的本地化资源。 /// internal static System.Drawing.Bitmap disk { get { object obj = ResourceManager.GetObject("disk", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// 查找 System.Drawing.Bitmap 类型的本地化资源。 /// internal static System.Drawing.Bitmap script_code { get { object obj = ResourceManager.GetObject("script_code", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// 查找 System.Drawing.Bitmap 类型的本地化资源。 /// internal static System.Drawing.Bitmap user { get { object obj = ResourceManager.GetObject("user", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } } } ================================================ FILE: WzComparerR2.Avatar/Properties/Resources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ..\Resources\arrow_in.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\disk.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\script_code.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\user.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\lock.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ================================================ FILE: WzComparerR2.Avatar/Skin.cs ================================================ using System; using System.Collections.Generic; using System.Text; using System.Drawing; using WzComparerR2.WzLib; namespace WzComparerR2.Avatar { public class Skin { public string Name { get; set; } public BitmapOrigin Image { get; set; } public Point Offset { get; set; } public string Z { get; set; } public int ZIndex { get; set; } } } ================================================ FILE: WzComparerR2.Avatar/UI/AvatarCodeForm.Designer.cs ================================================ namespace WzComparerR2.Avatar.UI { partial class AvatarCodeForm { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { this.buttonX1 = new DevComponents.DotNetBar.ButtonX(); this.buttonX2 = new DevComponents.DotNetBar.ButtonX(); this.textBoxX1 = new DevComponents.DotNetBar.Controls.TextBoxX(); this.checkBoxX1 = new DevComponents.DotNetBar.Controls.CheckBoxX(); this.checkBoxX2 = new DevComponents.DotNetBar.Controls.CheckBoxX(); this.labelX1 = new DevComponents.DotNetBar.LabelX(); this.SuspendLayout(); // // buttonX1 // this.buttonX1.AccessibleRole = System.Windows.Forms.AccessibleRole.PushButton; this.buttonX1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.buttonX1.ColorTable = DevComponents.DotNetBar.eButtonColor.OrangeWithBackground; this.buttonX1.DialogResult = System.Windows.Forms.DialogResult.OK; this.buttonX1.Location = new System.Drawing.Point(107, 126); this.buttonX1.Name = "buttonX1"; this.buttonX1.Size = new System.Drawing.Size(75, 23); this.buttonX1.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.buttonX1.TabIndex = 0; this.buttonX1.Text = "确定"; // // buttonX2 // this.buttonX2.AccessibleRole = System.Windows.Forms.AccessibleRole.PushButton; this.buttonX2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.buttonX2.ColorTable = DevComponents.DotNetBar.eButtonColor.OrangeWithBackground; this.buttonX2.DialogResult = System.Windows.Forms.DialogResult.Cancel; this.buttonX2.Location = new System.Drawing.Point(197, 126); this.buttonX2.Name = "buttonX2"; this.buttonX2.Size = new System.Drawing.Size(75, 23); this.buttonX2.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.buttonX2.TabIndex = 1; this.buttonX2.Text = "取消"; // // textBoxX1 // this.textBoxX1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); // // // this.textBoxX1.Border.Class = "TextBoxBorder"; this.textBoxX1.Border.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.textBoxX1.Location = new System.Drawing.Point(12, 12); this.textBoxX1.Multiline = true; this.textBoxX1.Name = "textBoxX1"; this.textBoxX1.PreventEnterBeep = true; this.textBoxX1.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; this.textBoxX1.Size = new System.Drawing.Size(260, 85); this.textBoxX1.TabIndex = 2; this.textBoxX1.KeyDown += new System.Windows.Forms.KeyEventHandler(this.textBoxX1_KeyDown); // // checkBoxX1 // this.checkBoxX1.AutoSize = true; // // // this.checkBoxX1.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.checkBoxX1.CheckBoxStyle = DevComponents.DotNetBar.eCheckBoxStyle.RadioButton; this.checkBoxX1.Checked = true; this.checkBoxX1.CheckState = System.Windows.Forms.CheckState.Checked; this.checkBoxX1.CheckValue = "Y"; this.checkBoxX1.Location = new System.Drawing.Point(71, 103); this.checkBoxX1.Name = "checkBoxX1"; this.checkBoxX1.Size = new System.Drawing.Size(51, 18); this.checkBoxX1.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.checkBoxX1.TabIndex = 3; this.checkBoxX1.Text = "覆盖"; // // checkBoxX2 // this.checkBoxX2.AutoSize = true; // // // this.checkBoxX2.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.checkBoxX2.CheckBoxStyle = DevComponents.DotNetBar.eCheckBoxStyle.RadioButton; this.checkBoxX2.Location = new System.Drawing.Point(125, 103); this.checkBoxX2.Name = "checkBoxX2"; this.checkBoxX2.Size = new System.Drawing.Size(51, 18); this.checkBoxX2.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.checkBoxX2.TabIndex = 4; this.checkBoxX2.Text = "合并"; // // labelX1 // this.labelX1.AutoSize = true; // // // this.labelX1.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.labelX1.Location = new System.Drawing.Point(10, 103); this.labelX1.Name = "labelX1"; this.labelX1.Size = new System.Drawing.Size(56, 18); this.labelX1.TabIndex = 5; this.labelX1.Text = "加载方式"; // // AvatarCodeForm // this.ClientSize = new System.Drawing.Size(284, 161); this.Controls.Add(this.labelX1); this.Controls.Add(this.checkBoxX2); this.Controls.Add(this.checkBoxX1); this.Controls.Add(this.textBoxX1); this.Controls.Add(this.buttonX2); this.Controls.Add(this.buttonX1); this.DoubleBuffered = true; this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; this.MaximizeBox = false; this.MinimizeBox = false; this.Name = "AvatarCodeForm"; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; this.Text = "读取代码"; this.ResumeLayout(false); this.PerformLayout(); } #endregion private DevComponents.DotNetBar.ButtonX buttonX1; private DevComponents.DotNetBar.ButtonX buttonX2; private DevComponents.DotNetBar.Controls.TextBoxX textBoxX1; private DevComponents.DotNetBar.Controls.CheckBoxX checkBoxX1; private DevComponents.DotNetBar.Controls.CheckBoxX checkBoxX2; private DevComponents.DotNetBar.LabelX labelX1; } } ================================================ FILE: WzComparerR2.Avatar/UI/AvatarCodeForm.cs ================================================ using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Text; using System.Windows.Forms; using DevComponents.DotNetBar; namespace WzComparerR2.Avatar.UI { public partial class AvatarCodeForm : DevComponents.DotNetBar.OfficeForm { public AvatarCodeForm() { InitializeComponent(); } public string CodeText { get { return textBoxX1.Text; } set { textBoxX1.Text = value; } } public int LoadType { get { if (this.checkBoxX1.Checked) { return 0; } else if (this.checkBoxX2.Checked) { return 1; } else { return 0; } } } private void textBoxX1_KeyDown(object sender, KeyEventArgs e) { if (e.Control && e.KeyCode == Keys.A) { textBoxX1.SelectAll(); } } } } ================================================ FILE: WzComparerR2.Avatar/UI/AvatarCodeForm.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ================================================ FILE: WzComparerR2.Avatar/UI/AvatarContainer.Designer.cs ================================================ namespace WzComparerR2.Avatar.UI { partial class AvatarContainer { /// /// 必需的设计器变量。 /// private System.ComponentModel.IContainer components = null; /// /// 清理所有正在使用的资源。 /// /// 如果应释放托管资源,为 true;否则为 false。 protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region 组件设计器生成的代码 /// /// 设计器支持所需的方法 - 不要 /// 使用代码编辑器修改此方法的内容。 /// private void InitializeComponent() { components = new System.ComponentModel.Container(); } #endregion } } ================================================ FILE: WzComparerR2.Avatar/UI/AvatarContainer.cs ================================================ using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Drawing.Drawing2D; using System.Text; using System.Windows.Forms; namespace WzComparerR2.Avatar.UI { public partial class AvatarContainer : Control { public AvatarContainer() { InitializeComponent(); SetStyle(ControlStyles.UserPaint, true); SetStyle(ControlStyles.AllPaintingInWmPaint, true); // 禁止擦除背景. SetStyle(ControlStyles.DoubleBuffer, true); // 双缓冲 bmpCache = new Dictionary(); } /// /// 获取或设置纸娃娃的绘图原点。 /// public Point Origin { get; set; } //绘图相关 Dictionary bmpCache; string currentKey; Bitmap bg; //事件相关 bool isDragging; Point lastPressingPoint; public void ClearAllCache() { this.bmpCache.Clear(); } public void AddCache(string key, BitmapOrigin[] layers) { this.bmpCache[key] = layers; } public bool HasCache(string key) { return this.bmpCache.ContainsKey(key); } public void SetKey(string key) { this.currentKey = key; this.Invalidate(); } protected override void OnPaint(PaintEventArgs pe) { Graphics g = pe.Graphics; DrawBackgrnd(g); BitmapOrigin[] layers; if (currentKey != null && this.bmpCache.TryGetValue(currentKey, out layers) && layers != null) { foreach(var bmp in layers) { if (bmp.Bitmap != null) { Point point = new Point(this.Origin.X - bmp.Origin.X, this.Origin.Y - bmp.Origin.Y); g.DrawImage(bmp.Bitmap, point); } } } base.OnPaint(pe); } private void DrawBackgrnd(Graphics g) { if (bg == null) { bg = new Bitmap(16, 16, System.Drawing.Imaging.PixelFormat.Format32bppArgb); Graphics g1 = Graphics.FromImage(bg); g1.FillRectangle(Brushes.LightGray, bg.Width / 2, 0, bg.Width / 2, bg.Height / 2); g1.FillRectangle(Brushes.LightGray, 0, bg.Height / 2, bg.Width / 2, bg.Height / 2); g1.Dispose(); } TextureBrush b = new TextureBrush(bg, WrapMode.Tile); g.FillRectangle(b, new Rectangle(Point.Empty, this.Size)); b.Dispose(); } protected override void OnMouseDown(MouseEventArgs e) { if ((e.Button & System.Windows.Forms.MouseButtons.Left) == System.Windows.Forms.MouseButtons.Left) { isDragging = true; lastPressingPoint = e.Location; } base.OnMouseDown(e); } protected override void OnMouseMove(MouseEventArgs e) { if (isDragging) { int dx = e.X - lastPressingPoint.X; int dy = e.Y - lastPressingPoint.Y; this.Origin = new Point(Origin.X + dx, Origin.Y + dy); this.lastPressingPoint = new Point(e.X, e.Y); this.Invalidate(); } base.OnMouseMove(e); } protected override void OnMouseUp(MouseEventArgs e) { if ((e.Button & System.Windows.Forms.MouseButtons.Left) == System.Windows.Forms.MouseButtons.Left) { isDragging = false; } base.OnMouseUp(e); } } } ================================================ FILE: WzComparerR2.Avatar/UI/AvatarForm.Designer.cs ================================================ namespace WzComparerR2.Avatar.UI { partial class AvatarForm { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { this.components = new System.ComponentModel.Container(); this.dotNetBarManager1 = new DevComponents.DotNetBar.DotNetBarManager(this.components); this.dockSite4 = new DevComponents.DotNetBar.DockSite(); this.dockSite1 = new DevComponents.DotNetBar.DockSite(); this.dockSite2 = new DevComponents.DotNetBar.DockSite(); this.bar1 = new DevComponents.DotNetBar.Bar(); this.panelDockContainer1 = new DevComponents.DotNetBar.PanelDockContainer(); this.itemPanel1 = new DevComponents.DotNetBar.ItemPanel(); this.dockContainerItem1 = new DevComponents.DotNetBar.DockContainerItem(); this.bar2 = new DevComponents.DotNetBar.Bar(); this.panelDockContainer2 = new DevComponents.DotNetBar.PanelDockContainer(); this.cmbWeaponIdx = new DevComponents.DotNetBar.Controls.ComboBoxEx(); this.cmbWeaponType = new DevComponents.DotNetBar.Controls.ComboBoxEx(); this.labelX4 = new DevComponents.DotNetBar.LabelX(); this.chkTamingPlay = new DevComponents.DotNetBar.Controls.CheckBoxX(); this.chkEmotionPlay = new DevComponents.DotNetBar.Controls.CheckBoxX(); this.chkBodyPlay = new DevComponents.DotNetBar.Controls.CheckBoxX(); this.cmbTamingFrame = new DevComponents.DotNetBar.Controls.ComboBoxEx(); this.cmbEmotionFrame = new DevComponents.DotNetBar.Controls.ComboBoxEx(); this.cmbBodyFrame = new DevComponents.DotNetBar.Controls.ComboBoxEx(); this.cmbActionTaming = new DevComponents.DotNetBar.Controls.ComboBoxEx(); this.cmbEmotion = new DevComponents.DotNetBar.Controls.ComboBoxEx(); this.labelX3 = new DevComponents.DotNetBar.LabelX(); this.labelX2 = new DevComponents.DotNetBar.LabelX(); this.labelX1 = new DevComponents.DotNetBar.LabelX(); this.cmbActionBody = new DevComponents.DotNetBar.Controls.ComboBoxEx(); this.chkHairShade = new DevComponents.DotNetBar.Controls.CheckBoxX(); this.chkHairCover = new DevComponents.DotNetBar.Controls.CheckBoxX(); this.dockContainerItem2 = new DevComponents.DotNetBar.DockContainerItem(); this.dockSite8 = new DevComponents.DotNetBar.DockSite(); this.dockSite5 = new DevComponents.DotNetBar.DockSite(); this.dockSite6 = new DevComponents.DotNetBar.DockSite(); this.dockSite7 = new DevComponents.DotNetBar.DockSite(); this.bar3 = new DevComponents.DotNetBar.Bar(); this.btnCode = new DevComponents.DotNetBar.ButtonItem(); this.btnCharac = new DevComponents.DotNetBar.ButtonItem(); this.btnMale = new DevComponents.DotNetBar.ButtonItem(); this.btnFemale = new DevComponents.DotNetBar.ButtonItem(); this.btnReset = new DevComponents.DotNetBar.ButtonItem(); this.btnLock = new DevComponents.DotNetBar.ButtonItem(); this.btnSaveAsGif = new DevComponents.DotNetBar.ButtonItem(); this.dockSite3 = new DevComponents.DotNetBar.DockSite(); this.timer1 = new System.Windows.Forms.Timer(this.components); this.avatarContainer1 = new WzComparerR2.Avatar.UI.AvatarContainer(); this.labelX5 = new DevComponents.DotNetBar.LabelX(); this.cmbEar = new DevComponents.DotNetBar.Controls.ComboBoxEx(); this.dockSite2.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.bar1)).BeginInit(); this.bar1.SuspendLayout(); this.panelDockContainer1.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.bar2)).BeginInit(); this.bar2.SuspendLayout(); this.panelDockContainer2.SuspendLayout(); this.dockSite7.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.bar3)).BeginInit(); this.SuspendLayout(); // // dotNetBarManager1 // this.dotNetBarManager1.AutoDispatchShortcuts.Add(DevComponents.DotNetBar.eShortcut.F1); this.dotNetBarManager1.AutoDispatchShortcuts.Add(DevComponents.DotNetBar.eShortcut.CtrlC); this.dotNetBarManager1.AutoDispatchShortcuts.Add(DevComponents.DotNetBar.eShortcut.CtrlA); this.dotNetBarManager1.AutoDispatchShortcuts.Add(DevComponents.DotNetBar.eShortcut.CtrlV); this.dotNetBarManager1.AutoDispatchShortcuts.Add(DevComponents.DotNetBar.eShortcut.CtrlX); this.dotNetBarManager1.AutoDispatchShortcuts.Add(DevComponents.DotNetBar.eShortcut.CtrlZ); this.dotNetBarManager1.AutoDispatchShortcuts.Add(DevComponents.DotNetBar.eShortcut.CtrlY); this.dotNetBarManager1.AutoDispatchShortcuts.Add(DevComponents.DotNetBar.eShortcut.Del); this.dotNetBarManager1.AutoDispatchShortcuts.Add(DevComponents.DotNetBar.eShortcut.Ins); this.dotNetBarManager1.BottomDockSite = this.dockSite4; this.dotNetBarManager1.EnableFullSizeDock = false; this.dotNetBarManager1.LeftDockSite = this.dockSite1; this.dotNetBarManager1.ParentForm = this; this.dotNetBarManager1.RightDockSite = this.dockSite2; this.dotNetBarManager1.ShowCustomizeContextMenu = false; this.dotNetBarManager1.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.dotNetBarManager1.ToolbarBottomDockSite = this.dockSite8; this.dotNetBarManager1.ToolbarLeftDockSite = this.dockSite5; this.dotNetBarManager1.ToolbarRightDockSite = this.dockSite6; this.dotNetBarManager1.ToolbarTopDockSite = this.dockSite7; this.dotNetBarManager1.TopDockSite = this.dockSite3; // // dockSite4 // this.dockSite4.AccessibleRole = System.Windows.Forms.AccessibleRole.Window; this.dockSite4.Dock = System.Windows.Forms.DockStyle.Bottom; this.dockSite4.DocumentDockContainer = new DevComponents.DotNetBar.DocumentDockContainer(); this.dockSite4.Location = new System.Drawing.Point(0, 411); this.dockSite4.Name = "dockSite4"; this.dockSite4.Size = new System.Drawing.Size(584, 0); this.dockSite4.TabIndex = 3; this.dockSite4.TabStop = false; // // dockSite1 // this.dockSite1.AccessibleRole = System.Windows.Forms.AccessibleRole.Window; this.dockSite1.Dock = System.Windows.Forms.DockStyle.Left; this.dockSite1.DocumentDockContainer = new DevComponents.DotNetBar.DocumentDockContainer(); this.dockSite1.Location = new System.Drawing.Point(0, 25); this.dockSite1.Name = "dockSite1"; this.dockSite1.Size = new System.Drawing.Size(0, 386); this.dockSite1.TabIndex = 0; this.dockSite1.TabStop = false; // // dockSite2 // this.dockSite2.AccessibleRole = System.Windows.Forms.AccessibleRole.Window; this.dockSite2.Controls.Add(this.bar1); this.dockSite2.Controls.Add(this.bar2); this.dockSite2.Dock = System.Windows.Forms.DockStyle.Right; this.dockSite2.DocumentDockContainer = new DevComponents.DotNetBar.DocumentDockContainer(new DevComponents.DotNetBar.DocumentBaseContainer[] { ((DevComponents.DotNetBar.DocumentBaseContainer)(new DevComponents.DotNetBar.DocumentBarContainer(this.bar1, 213, 212))), ((DevComponents.DotNetBar.DocumentBaseContainer)(new DevComponents.DotNetBar.DocumentBarContainer(this.bar2, 213, 171)))}, DevComponents.DotNetBar.eOrientation.Vertical); this.dockSite2.Location = new System.Drawing.Point(368, 25); this.dockSite2.Name = "dockSite2"; this.dockSite2.Size = new System.Drawing.Size(216, 386); this.dockSite2.TabIndex = 1; this.dockSite2.TabStop = false; // // bar1 // this.bar1.AccessibleDescription = "DotNetBar Bar (bar1)"; this.bar1.AccessibleName = "DotNetBar Bar"; this.bar1.AccessibleRole = System.Windows.Forms.AccessibleRole.Grouping; this.bar1.AutoSyncBarCaption = true; this.bar1.CloseSingleTab = true; this.bar1.Controls.Add(this.panelDockContainer1); this.bar1.Font = new System.Drawing.Font("宋体", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); this.bar1.GrabHandleStyle = DevComponents.DotNetBar.eGrabHandleStyle.Caption; this.bar1.IsMaximized = false; this.bar1.Items.AddRange(new DevComponents.DotNetBar.BaseItem[] { this.dockContainerItem1}); this.bar1.LayoutType = DevComponents.DotNetBar.eLayoutType.DockContainer; this.bar1.Location = new System.Drawing.Point(3, 0); this.bar1.Name = "bar1"; this.bar1.Size = new System.Drawing.Size(213, 212); this.bar1.Stretch = true; this.bar1.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.bar1.TabIndex = 0; this.bar1.TabStop = false; this.bar1.Text = "零件"; // // panelDockContainer1 // this.panelDockContainer1.ColorSchemeStyle = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.panelDockContainer1.Controls.Add(this.itemPanel1); this.panelDockContainer1.DisabledBackColor = System.Drawing.Color.Empty; this.panelDockContainer1.Location = new System.Drawing.Point(3, 23); this.panelDockContainer1.Name = "panelDockContainer1"; this.panelDockContainer1.Size = new System.Drawing.Size(207, 186); this.panelDockContainer1.Style.Alignment = System.Drawing.StringAlignment.Center; this.panelDockContainer1.Style.BackColor1.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.BarBackground; this.panelDockContainer1.Style.BorderColor.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.BarDockedBorder; this.panelDockContainer1.Style.ForeColor.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.ItemText; this.panelDockContainer1.Style.GradientAngle = 90; this.panelDockContainer1.TabIndex = 0; // // itemPanel1 // this.itemPanel1.AutoScroll = true; // // // this.itemPanel1.BackgroundStyle.Class = "ItemPanel"; this.itemPanel1.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.itemPanel1.ContainerControlProcessDialogKey = true; this.itemPanel1.Dock = System.Windows.Forms.DockStyle.Fill; this.itemPanel1.DragDropSupport = true; this.itemPanel1.LayoutOrientation = DevComponents.DotNetBar.eOrientation.Vertical; this.itemPanel1.Location = new System.Drawing.Point(0, 0); this.itemPanel1.Name = "itemPanel1"; this.itemPanel1.Size = new System.Drawing.Size(207, 186); this.itemPanel1.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.itemPanel1.TabIndex = 8; this.itemPanel1.Text = "itemPanel1"; // // dockContainerItem1 // this.dockContainerItem1.Control = this.panelDockContainer1; this.dockContainerItem1.Name = "dockContainerItem1"; this.dockContainerItem1.Text = "零件"; // // bar2 // this.bar2.AccessibleDescription = "DotNetBar Bar (bar2)"; this.bar2.AccessibleName = "DotNetBar Bar"; this.bar2.AccessibleRole = System.Windows.Forms.AccessibleRole.Grouping; this.bar2.AutoSyncBarCaption = true; this.bar2.CloseSingleTab = true; this.bar2.Controls.Add(this.panelDockContainer2); this.bar2.Font = new System.Drawing.Font("宋体", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); this.bar2.GrabHandleStyle = DevComponents.DotNetBar.eGrabHandleStyle.Caption; this.bar2.IsMaximized = false; this.bar2.Items.AddRange(new DevComponents.DotNetBar.BaseItem[] { this.dockContainerItem2}); this.bar2.LayoutType = DevComponents.DotNetBar.eLayoutType.DockContainer; this.bar2.Location = new System.Drawing.Point(3, 215); this.bar2.Name = "bar2"; this.bar2.Size = new System.Drawing.Size(213, 171); this.bar2.Stretch = true; this.bar2.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.bar2.TabIndex = 1; this.bar2.TabStop = false; this.bar2.Text = "动作"; // // panelDockContainer2 // this.panelDockContainer2.AutoScroll = true; this.panelDockContainer2.ColorSchemeStyle = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.panelDockContainer2.Controls.Add(this.cmbEar); this.panelDockContainer2.Controls.Add(this.cmbWeaponIdx); this.panelDockContainer2.Controls.Add(this.cmbWeaponType); this.panelDockContainer2.Controls.Add(this.labelX4); this.panelDockContainer2.Controls.Add(this.chkTamingPlay); this.panelDockContainer2.Controls.Add(this.chkEmotionPlay); this.panelDockContainer2.Controls.Add(this.chkBodyPlay); this.panelDockContainer2.Controls.Add(this.cmbTamingFrame); this.panelDockContainer2.Controls.Add(this.cmbEmotionFrame); this.panelDockContainer2.Controls.Add(this.cmbBodyFrame); this.panelDockContainer2.Controls.Add(this.cmbActionTaming); this.panelDockContainer2.Controls.Add(this.cmbEmotion); this.panelDockContainer2.Controls.Add(this.labelX3); this.panelDockContainer2.Controls.Add(this.labelX2); this.panelDockContainer2.Controls.Add(this.labelX1); this.panelDockContainer2.Controls.Add(this.cmbActionBody); this.panelDockContainer2.Controls.Add(this.chkHairShade); this.panelDockContainer2.Controls.Add(this.chkHairCover); this.panelDockContainer2.Controls.Add(this.labelX5); this.panelDockContainer2.DisabledBackColor = System.Drawing.Color.Empty; this.panelDockContainer2.Location = new System.Drawing.Point(3, 23); this.panelDockContainer2.Name = "panelDockContainer2"; this.panelDockContainer2.Size = new System.Drawing.Size(207, 145); this.panelDockContainer2.Style.Alignment = System.Drawing.StringAlignment.Center; this.panelDockContainer2.Style.BackColor1.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.BarBackground; this.panelDockContainer2.Style.BorderColor.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.BarDockedBorder; this.panelDockContainer2.Style.ForeColor.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.ItemText; this.panelDockContainer2.Style.GradientAngle = 90; this.panelDockContainer2.TabIndex = 0; // // cmbWeaponIdx // this.cmbWeaponIdx.DisplayMember = "Text"; this.cmbWeaponIdx.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawFixed; this.cmbWeaponIdx.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.cmbWeaponIdx.FormattingEnabled = true; this.cmbWeaponIdx.ItemHeight = 15; this.cmbWeaponIdx.Location = new System.Drawing.Point(89, 103); this.cmbWeaponIdx.Name = "cmbWeaponIdx"; this.cmbWeaponIdx.Size = new System.Drawing.Size(50, 21); this.cmbWeaponIdx.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.cmbWeaponIdx.TabIndex = 13; this.cmbWeaponIdx.SelectedIndexChanged += new System.EventHandler(this.cmbWeaponIdx_SelectedIndexChanged); // // cmbWeaponType // this.cmbWeaponType.DisplayMember = "Text"; this.cmbWeaponType.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawFixed; this.cmbWeaponType.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.cmbWeaponType.FormattingEnabled = true; this.cmbWeaponType.ItemHeight = 15; this.cmbWeaponType.Location = new System.Drawing.Point(35, 103); this.cmbWeaponType.Name = "cmbWeaponType"; this.cmbWeaponType.Size = new System.Drawing.Size(50, 21); this.cmbWeaponType.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.cmbWeaponType.TabIndex = 12; this.cmbWeaponType.SelectedIndexChanged += new System.EventHandler(this.cmbWeaponType_SelectedIndexChanged); // // labelX4 // this.labelX4.AutoSize = true; this.labelX4.BackColor = System.Drawing.Color.Transparent; // // // this.labelX4.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.labelX4.Location = new System.Drawing.Point(3, 106); this.labelX4.Name = "labelX4"; this.labelX4.Size = new System.Drawing.Size(31, 18); this.labelX4.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.labelX4.TabIndex = 13; this.labelX4.Text = "武器"; // // chkTamingPlay // this.chkTamingPlay.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.chkTamingPlay.BackColor = System.Drawing.Color.Transparent; // // // this.chkTamingPlay.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.chkTamingPlay.Location = new System.Drawing.Point(184, 57); this.chkTamingPlay.Name = "chkTamingPlay"; this.chkTamingPlay.Size = new System.Drawing.Size(15, 21); this.chkTamingPlay.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.chkTamingPlay.TabIndex = 8; this.chkTamingPlay.TextVisible = false; this.chkTamingPlay.CheckedChanged += new System.EventHandler(this.chkTamingPlay_CheckedChanged); // // chkEmotionPlay // this.chkEmotionPlay.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.chkEmotionPlay.BackColor = System.Drawing.Color.Transparent; // // // this.chkEmotionPlay.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.chkEmotionPlay.Location = new System.Drawing.Point(184, 30); this.chkEmotionPlay.Name = "chkEmotionPlay"; this.chkEmotionPlay.Size = new System.Drawing.Size(15, 21); this.chkEmotionPlay.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.chkEmotionPlay.TabIndex = 7; this.chkEmotionPlay.TextVisible = false; this.chkEmotionPlay.CheckedChanged += new System.EventHandler(this.chkEmotionPlay_CheckedChanged); // // chkBodyPlay // this.chkBodyPlay.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.chkBodyPlay.BackColor = System.Drawing.Color.Transparent; // // // this.chkBodyPlay.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.chkBodyPlay.Location = new System.Drawing.Point(184, 3); this.chkBodyPlay.Name = "chkBodyPlay"; this.chkBodyPlay.Size = new System.Drawing.Size(15, 21); this.chkBodyPlay.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.chkBodyPlay.TabIndex = 6; this.chkBodyPlay.TextVisible = false; this.chkBodyPlay.CheckedChanged += new System.EventHandler(this.chkBodyPlay_CheckedChanged); // // cmbTamingFrame // this.cmbTamingFrame.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.cmbTamingFrame.DisplayMember = "Text"; this.cmbTamingFrame.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawFixed; this.cmbTamingFrame.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.cmbTamingFrame.FormattingEnabled = true; this.cmbTamingFrame.ItemHeight = 15; this.cmbTamingFrame.Location = new System.Drawing.Point(128, 57); this.cmbTamingFrame.Name = "cmbTamingFrame"; this.cmbTamingFrame.Size = new System.Drawing.Size(50, 21); this.cmbTamingFrame.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.cmbTamingFrame.TabIndex = 5; this.cmbTamingFrame.SelectedIndexChanged += new System.EventHandler(this.cmbTamingFrame_SelectedIndexChanged); // // cmbEmotionFrame // this.cmbEmotionFrame.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.cmbEmotionFrame.DisplayMember = "Text"; this.cmbEmotionFrame.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawFixed; this.cmbEmotionFrame.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.cmbEmotionFrame.FormattingEnabled = true; this.cmbEmotionFrame.ItemHeight = 15; this.cmbEmotionFrame.Location = new System.Drawing.Point(128, 30); this.cmbEmotionFrame.Name = "cmbEmotionFrame"; this.cmbEmotionFrame.Size = new System.Drawing.Size(50, 21); this.cmbEmotionFrame.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.cmbEmotionFrame.TabIndex = 4; this.cmbEmotionFrame.SelectedIndexChanged += new System.EventHandler(this.cmbEmotionFrame_SelectedIndexChanged); // // cmbBodyFrame // this.cmbBodyFrame.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.cmbBodyFrame.DisplayMember = "Text"; this.cmbBodyFrame.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawFixed; this.cmbBodyFrame.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.cmbBodyFrame.FormattingEnabled = true; this.cmbBodyFrame.ItemHeight = 15; this.cmbBodyFrame.Location = new System.Drawing.Point(128, 3); this.cmbBodyFrame.Name = "cmbBodyFrame"; this.cmbBodyFrame.Size = new System.Drawing.Size(50, 21); this.cmbBodyFrame.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.cmbBodyFrame.TabIndex = 3; this.cmbBodyFrame.SelectedIndexChanged += new System.EventHandler(this.cmbBodyFrame_SelectedIndexChanged); // // cmbActionTaming // this.cmbActionTaming.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.cmbActionTaming.DisplayMember = "Text"; this.cmbActionTaming.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawFixed; this.cmbActionTaming.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.cmbActionTaming.FormattingEnabled = true; this.cmbActionTaming.ItemHeight = 15; this.cmbActionTaming.Location = new System.Drawing.Point(35, 57); this.cmbActionTaming.Name = "cmbActionTaming"; this.cmbActionTaming.Size = new System.Drawing.Size(87, 21); this.cmbActionTaming.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.cmbActionTaming.TabIndex = 2; this.cmbActionTaming.SelectedIndexChanged += new System.EventHandler(this.cmbActionTaming_SelectedIndexChanged); // // cmbEmotion // this.cmbEmotion.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.cmbEmotion.DisplayMember = "Text"; this.cmbEmotion.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawFixed; this.cmbEmotion.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.cmbEmotion.FormattingEnabled = true; this.cmbEmotion.ItemHeight = 15; this.cmbEmotion.Location = new System.Drawing.Point(35, 30); this.cmbEmotion.Name = "cmbEmotion"; this.cmbEmotion.Size = new System.Drawing.Size(87, 21); this.cmbEmotion.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.cmbEmotion.TabIndex = 1; this.cmbEmotion.SelectedIndexChanged += new System.EventHandler(this.cmbEmotion_SelectedIndexChanged); // // labelX3 // this.labelX3.AutoSize = true; this.labelX3.BackColor = System.Drawing.Color.Transparent; // // // this.labelX3.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.labelX3.Location = new System.Drawing.Point(3, 60); this.labelX3.Name = "labelX3"; this.labelX3.Size = new System.Drawing.Size(31, 18); this.labelX3.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.labelX3.TabIndex = 3; this.labelX3.Text = "骑兽"; // // labelX2 // this.labelX2.AutoSize = true; this.labelX2.BackColor = System.Drawing.Color.Transparent; // // // this.labelX2.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.labelX2.Location = new System.Drawing.Point(3, 33); this.labelX2.Name = "labelX2"; this.labelX2.Size = new System.Drawing.Size(31, 18); this.labelX2.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.labelX2.TabIndex = 2; this.labelX2.Text = "表情"; // // labelX1 // this.labelX1.AutoSize = true; this.labelX1.BackColor = System.Drawing.Color.Transparent; // // // this.labelX1.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.labelX1.Location = new System.Drawing.Point(3, 6); this.labelX1.Name = "labelX1"; this.labelX1.Size = new System.Drawing.Size(31, 18); this.labelX1.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.labelX1.TabIndex = 1; this.labelX1.Text = "身体"; // // cmbActionBody // this.cmbActionBody.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.cmbActionBody.DisplayMember = "Text"; this.cmbActionBody.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawFixed; this.cmbActionBody.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.cmbActionBody.FormattingEnabled = true; this.cmbActionBody.ItemHeight = 15; this.cmbActionBody.Location = new System.Drawing.Point(35, 3); this.cmbActionBody.Name = "cmbActionBody"; this.cmbActionBody.Size = new System.Drawing.Size(87, 21); this.cmbActionBody.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.cmbActionBody.TabIndex = 0; this.cmbActionBody.SelectedIndexChanged += new System.EventHandler(this.cmbActionBody_SelectedIndexChanged); // // chkHairShade // this.chkHairShade.AutoSize = true; this.chkHairShade.BackColor = System.Drawing.Color.Transparent; // // // this.chkHairShade.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.chkHairShade.Location = new System.Drawing.Point(82, 84); this.chkHairShade.Name = "chkHairShade"; this.chkHairShade.Size = new System.Drawing.Size(85, 19); this.chkHairShade.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.chkHairShade.TabIndex = 10; this.chkHairShade.Text = "hairShade"; this.chkHairShade.CheckedChanged += new System.EventHandler(this.chkHairShade_CheckedChanged); // // chkHairCover // this.chkHairCover.AutoSize = true; this.chkHairCover.BackColor = System.Drawing.Color.Transparent; // // // this.chkHairCover.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.chkHairCover.Location = new System.Drawing.Point(5, 84); this.chkHairCover.Name = "chkHairCover"; this.chkHairCover.Size = new System.Drawing.Size(83, 19); this.chkHairCover.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.chkHairCover.TabIndex = 9; this.chkHairCover.Text = "hairCover"; this.chkHairCover.CheckedChanged += new System.EventHandler(this.chkHairCover_CheckedChanged); // // dockContainerItem2 // this.dockContainerItem2.Control = this.panelDockContainer2; this.dockContainerItem2.Name = "dockContainerItem2"; this.dockContainerItem2.Text = "动作"; // // dockSite8 // this.dockSite8.AccessibleRole = System.Windows.Forms.AccessibleRole.Window; this.dockSite8.Dock = System.Windows.Forms.DockStyle.Bottom; this.dockSite8.Location = new System.Drawing.Point(0, 411); this.dockSite8.Name = "dockSite8"; this.dockSite8.Size = new System.Drawing.Size(584, 0); this.dockSite8.TabIndex = 7; this.dockSite8.TabStop = false; // // dockSite5 // this.dockSite5.AccessibleRole = System.Windows.Forms.AccessibleRole.Window; this.dockSite5.Dock = System.Windows.Forms.DockStyle.Left; this.dockSite5.Location = new System.Drawing.Point(0, 25); this.dockSite5.Name = "dockSite5"; this.dockSite5.Size = new System.Drawing.Size(0, 386); this.dockSite5.TabIndex = 4; this.dockSite5.TabStop = false; // // dockSite6 // this.dockSite6.AccessibleRole = System.Windows.Forms.AccessibleRole.Window; this.dockSite6.Dock = System.Windows.Forms.DockStyle.Right; this.dockSite6.Location = new System.Drawing.Point(584, 25); this.dockSite6.Name = "dockSite6"; this.dockSite6.Size = new System.Drawing.Size(0, 386); this.dockSite6.TabIndex = 5; this.dockSite6.TabStop = false; // // dockSite7 // this.dockSite7.AccessibleRole = System.Windows.Forms.AccessibleRole.Window; this.dockSite7.Controls.Add(this.bar3); this.dockSite7.Dock = System.Windows.Forms.DockStyle.Top; this.dockSite7.Location = new System.Drawing.Point(0, 0); this.dockSite7.Name = "dockSite7"; this.dockSite7.Size = new System.Drawing.Size(584, 25); this.dockSite7.TabIndex = 6; this.dockSite7.TabStop = false; // // bar3 // this.bar3.AccessibleDescription = "DotNetBar Bar (bar3)"; this.bar3.AccessibleName = "DotNetBar Bar"; this.bar3.AccessibleRole = System.Windows.Forms.AccessibleRole.ToolBar; this.bar3.CanCustomize = false; this.bar3.DockSide = DevComponents.DotNetBar.eDockSide.Top; this.bar3.Font = new System.Drawing.Font("Microsoft YaHei UI", 9F); this.bar3.GrabHandleStyle = DevComponents.DotNetBar.eGrabHandleStyle.Office2003; this.bar3.IsMaximized = false; this.bar3.Items.AddRange(new DevComponents.DotNetBar.BaseItem[] { this.btnCode, this.btnCharac, this.btnReset, this.btnLock, this.btnSaveAsGif}); this.bar3.Location = new System.Drawing.Point(0, 0); this.bar3.Name = "bar3"; this.bar3.Size = new System.Drawing.Size(142, 25); this.bar3.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.bar3.TabIndex = 0; this.bar3.TabStop = false; this.bar3.Text = "工具"; // // btnCode // this.btnCode.Image = global::WzComparerR2.Avatar.Properties.Resources.script_code; this.btnCode.Name = "btnCode"; this.btnCode.Tooltip = "代码"; this.btnCode.Click += new System.EventHandler(this.btnCode_Click); // // btnCharac // this.btnCharac.AutoExpandOnClick = true; this.btnCharac.Image = global::WzComparerR2.Avatar.Properties.Resources.user; this.btnCharac.Name = "btnCharac"; this.btnCharac.SubItems.AddRange(new DevComponents.DotNetBar.BaseItem[] { this.btnMale, this.btnFemale}); this.btnCharac.Tooltip = "初始化"; // // btnMale // this.btnMale.Name = "btnMale"; this.btnMale.Text = "男性角色"; this.btnMale.Click += new System.EventHandler(this.btnMale_Click); // // btnFemale // this.btnFemale.Name = "btnFemale"; this.btnFemale.Text = "女性角色"; this.btnFemale.Click += new System.EventHandler(this.btnFemale_Click); // // btnReset // this.btnReset.Image = global::WzComparerR2.Avatar.Properties.Resources.arrow_in; this.btnReset.Name = "btnReset"; this.btnReset.Tooltip = "坐标重置"; this.btnReset.Click += new System.EventHandler(this.btnReset_Click); // // btnLock // this.btnLock.AutoCheckOnClick = true; this.btnLock.Image = global::WzComparerR2.Avatar.Properties.Resources._lock; this.btnLock.Name = "btnLock"; this.btnLock.Tooltip = "锁定"; // // btnSaveAsGif // this.btnSaveAsGif.Image = global::WzComparerR2.Avatar.Properties.Resources.disk; this.btnSaveAsGif.Name = "btnSaveAsGif"; this.btnSaveAsGif.Tooltip = "保存"; this.btnSaveAsGif.Click += new System.EventHandler(this.btnSaveAsGif_Click); // // dockSite3 // this.dockSite3.AccessibleRole = System.Windows.Forms.AccessibleRole.Window; this.dockSite3.Dock = System.Windows.Forms.DockStyle.Top; this.dockSite3.DocumentDockContainer = new DevComponents.DotNetBar.DocumentDockContainer(); this.dockSite3.Location = new System.Drawing.Point(0, 25); this.dockSite3.Name = "dockSite3"; this.dockSite3.Size = new System.Drawing.Size(584, 0); this.dockSite3.TabIndex = 2; this.dockSite3.TabStop = false; // // timer1 // this.timer1.Tick += new System.EventHandler(this.timer1_Tick); // // avatarContainer1 // this.avatarContainer1.BackColor = System.Drawing.Color.White; this.avatarContainer1.Dock = System.Windows.Forms.DockStyle.Fill; this.avatarContainer1.Location = new System.Drawing.Point(0, 25); this.avatarContainer1.Name = "avatarContainer1"; this.avatarContainer1.Origin = new System.Drawing.Point(0, 0); this.avatarContainer1.Size = new System.Drawing.Size(368, 386); this.avatarContainer1.TabIndex = 8; this.avatarContainer1.Text = "avatarContainer1"; // // labelX5 // this.labelX5.AutoSize = true; this.labelX5.BackColor = System.Drawing.Color.Transparent; // // // this.labelX5.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.labelX5.Location = new System.Drawing.Point(139, 106); this.labelX5.Name = "labelX5"; this.labelX5.Size = new System.Drawing.Size(25, 16); this.labelX5.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.labelX5.TabIndex = 14; this.labelX5.Text = "ear"; // // cmbEar // this.cmbEar.DisplayMember = "Text"; this.cmbEar.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawFixed; this.cmbEar.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.cmbEar.FormattingEnabled = true; this.cmbEar.ItemHeight = 15; this.cmbEar.Location = new System.Drawing.Point(163, 103); this.cmbEar.Name = "cmbEar"; this.cmbEar.Size = new System.Drawing.Size(39, 21); this.cmbEar.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.cmbEar.TabIndex = 15; this.cmbEar.SelectedIndexChanged += new System.EventHandler(this.cmbEar_SelectedIndexChanged); // // AvatarForm // this.ClientSize = new System.Drawing.Size(584, 411); this.Controls.Add(this.avatarContainer1); this.Controls.Add(this.dockSite2); this.Controls.Add(this.dockSite1); this.Controls.Add(this.dockSite3); this.Controls.Add(this.dockSite4); this.Controls.Add(this.dockSite5); this.Controls.Add(this.dockSite6); this.Controls.Add(this.dockSite7); this.Controls.Add(this.dockSite8); this.DoubleBuffered = true; this.Name = "AvatarForm"; this.Text = "Avatar"; this.dockSite2.ResumeLayout(false); ((System.ComponentModel.ISupportInitialize)(this.bar1)).EndInit(); this.bar1.ResumeLayout(false); this.panelDockContainer1.ResumeLayout(false); ((System.ComponentModel.ISupportInitialize)(this.bar2)).EndInit(); this.bar2.ResumeLayout(false); this.panelDockContainer2.ResumeLayout(false); this.panelDockContainer2.PerformLayout(); this.dockSite7.ResumeLayout(false); ((System.ComponentModel.ISupportInitialize)(this.bar3)).EndInit(); this.ResumeLayout(false); } #endregion private DevComponents.DotNetBar.DotNetBarManager dotNetBarManager1; private DevComponents.DotNetBar.DockSite dockSite4; private DevComponents.DotNetBar.DockSite dockSite1; private DevComponents.DotNetBar.DockSite dockSite2; private DevComponents.DotNetBar.Bar bar1; private DevComponents.DotNetBar.PanelDockContainer panelDockContainer1; private DevComponents.DotNetBar.DockContainerItem dockContainerItem1; private DevComponents.DotNetBar.Bar bar2; private DevComponents.DotNetBar.PanelDockContainer panelDockContainer2; private DevComponents.DotNetBar.DockContainerItem dockContainerItem2; private DevComponents.DotNetBar.DockSite dockSite3; private DevComponents.DotNetBar.DockSite dockSite5; private DevComponents.DotNetBar.DockSite dockSite6; private DevComponents.DotNetBar.DockSite dockSite7; private DevComponents.DotNetBar.DockSite dockSite8; private DevComponents.DotNetBar.ItemPanel itemPanel1; private DevComponents.DotNetBar.Controls.ComboBoxEx cmbActionTaming; private DevComponents.DotNetBar.Controls.ComboBoxEx cmbEmotion; private DevComponents.DotNetBar.LabelX labelX3; private DevComponents.DotNetBar.LabelX labelX2; private DevComponents.DotNetBar.LabelX labelX1; private DevComponents.DotNetBar.Controls.ComboBoxEx cmbActionBody; private DevComponents.DotNetBar.Controls.CheckBoxX chkTamingPlay; private DevComponents.DotNetBar.Controls.CheckBoxX chkEmotionPlay; private DevComponents.DotNetBar.Controls.CheckBoxX chkBodyPlay; private DevComponents.DotNetBar.Controls.ComboBoxEx cmbTamingFrame; private DevComponents.DotNetBar.Controls.ComboBoxEx cmbEmotionFrame; private DevComponents.DotNetBar.Controls.ComboBoxEx cmbBodyFrame; private System.Windows.Forms.Timer timer1; private AvatarContainer avatarContainer1; private DevComponents.DotNetBar.Controls.CheckBoxX chkHairCover; private DevComponents.DotNetBar.Controls.ComboBoxEx cmbWeaponIdx; private DevComponents.DotNetBar.Controls.ComboBoxEx cmbWeaponType; private DevComponents.DotNetBar.LabelX labelX4; private DevComponents.DotNetBar.Bar bar3; private DevComponents.DotNetBar.ButtonItem btnCode; private DevComponents.DotNetBar.ButtonItem btnCharac; private DevComponents.DotNetBar.ButtonItem btnReset; private DevComponents.DotNetBar.Controls.CheckBoxX chkHairShade; private DevComponents.DotNetBar.ButtonItem btnLock; private DevComponents.DotNetBar.ButtonItem btnMale; private DevComponents.DotNetBar.ButtonItem btnFemale; private DevComponents.DotNetBar.ButtonItem btnSaveAsGif; private DevComponents.DotNetBar.Controls.ComboBoxEx cmbEar; private DevComponents.DotNetBar.LabelX labelX5; } } ================================================ FILE: WzComparerR2.Avatar/UI/AvatarForm.cs ================================================ using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Windows.Forms; using DevComponents.DotNetBar; using DevComponents.Editors; using DevComponents.DotNetBar.Controls; using WzComparerR2.Common; using WzComparerR2.WzLib; using WzComparerR2.PluginBase; using WzComparerR2.Config; namespace WzComparerR2.Avatar.UI { internal partial class AvatarForm : DevComponents.DotNetBar.OfficeForm { public AvatarForm() { InitializeComponent(); this.avatar = new AvatarCanvas(); this.animator = new Animator(); btnReset_Click(btnReset, EventArgs.Empty); FillWeaponIdx(); FillEarSelection(); } public SuperTabControlPanel GetTabPanel() { this.TopLevel = false; this.Dock = DockStyle.Fill; this.FormBorderStyle = FormBorderStyle.None; this.DoubleBuffered = true; var pnl = new SuperTabControlPanel(); pnl.Controls.Add(this); pnl.Padding = new System.Windows.Forms.Padding(1); this.Visible = true; return pnl; } public Entry PluginEntry { get; set; } AvatarCanvas avatar; bool inited; string partsTag; bool suspendUpdate; bool needUpdate; Animator animator; /// /// wz1节点选中事件。 /// public void OnSelectedNode1Changed(object sender, WzNodeEventArgs e) { if (PluginEntry.Context.SelectedTab != PluginEntry.Tab || e.Node == null || this.btnLock.Checked) { return; } Wz_File file = e.Node.GetNodeWzFile(); if (file == null) { return; } switch (file.Type) { case Wz_Type.Character: //读取装备 Wz_Image wzImg = e.Node.GetValue(); if (wzImg != null && wzImg.TryExtract()) { this.SuspendUpdateDisplay(); LoadPart(wzImg.Node); this.ResumeUpdateDisplay(); } break; } } public void OnWzClosing(object sender, WzStructureEventArgs e) { bool hasChanged = false; for (int i = 0; i < avatar.Parts.Length; i++) { var part = avatar.Parts[i]; if (part != null) { var wzFile = part.Node.GetNodeWzFile(); if (wzFile != null && e.WzStructure.wz_files.Contains(wzFile))//将要关闭文件 移除 { avatar.Parts[i] = null; hasChanged = true; } } } if (hasChanged) { this.FillAvatarParts(); UpdateDisplay(); } } /// /// 初始化纸娃娃资源。 /// private bool AvatarInit() { this.inited = this.avatar.LoadZ() && this.avatar.LoadActions() && this.avatar.LoadEmotions(); if (this.inited) { this.FillBodyAction(); this.FillEmotion(); } return this.inited; } /// /// 加载装备部件。 /// /// private void LoadPart(Wz_Node imgNode) { if (!this.inited && !this.AvatarInit() && imgNode == null) { return; } AvatarPart part = this.avatar.AddPart(imgNode); if (part != null) { OnNewPartAdded(part); FillAvatarParts(); UpdateDisplay(); } } private void OnNewPartAdded(AvatarPart part) { if (part == null) { return; } if (part == avatar.Body) //同步head { int headID = 10000 + part.ID.Value % 10000; if (avatar.Head == null || avatar.Head.ID != headID) { var headImgNode = PluginBase.PluginManager.FindWz(string.Format("Character\\{0:D8}.img", headID)); if (headImgNode != null) { this.avatar.AddPart(headImgNode); } } } else if (part == avatar.Head) //同步body { int bodyID = part.ID.Value % 10000; if (avatar.Body == null || avatar.Body.ID != bodyID) { var bodyImgNode = PluginBase.PluginManager.FindWz(string.Format("Character\\{0:D8}.img", bodyID)); if (bodyImgNode != null) { this.avatar.AddPart(bodyImgNode); } } } else if (part == avatar.Face) //同步表情 { this.avatar.LoadEmotions(); FillEmotion(); } else if (part == avatar.Taming) //同步座驾动作 { this.avatar.LoadTamingActions(); FillTamingAction(); SetTamingDefaultBodyAction(); SetTamingDefault(); } else if (part == avatar.Weapon) //同步武器类型 { FillWeaponTypes(); } else if (part == avatar.Pants || part == avatar.Coat) //隐藏套装 { if (avatar.Longcoat != null) { avatar.Longcoat.Visible = false; } } else if (part == avatar.Longcoat) //还是。。隐藏套装 { if (avatar.Pants != null && avatar.Pants.Visible || avatar.Coat != null && avatar.Coat.Visible) { avatar.Longcoat.Visible = false; } } } private void SuspendUpdateDisplay() { this.suspendUpdate = true; this.needUpdate = false; } private void ResumeUpdateDisplay() { if (this.suspendUpdate) { this.suspendUpdate = false; if (this.needUpdate) { this.UpdateDisplay(); } } } /// /// 更新画布。 /// private void UpdateDisplay() { if (suspendUpdate) { this.needUpdate = true; return; } string newPartsTag = GetAllPartsTag(); if (this.partsTag != newPartsTag) { this.partsTag = newPartsTag; this.avatarContainer1.ClearAllCache(); } ComboItem selectedItem; //同步角色动作 selectedItem = this.cmbActionBody.SelectedItem as ComboItem; this.avatar.ActionName = selectedItem != null ? selectedItem.Text : null; //同步表情 selectedItem = this.cmbEmotion.SelectedItem as ComboItem; this.avatar.EmotionName = selectedItem != null ? selectedItem.Text : null; //同步骑宠动作 selectedItem = this.cmbActionTaming.SelectedItem as ComboItem; this.avatar.TamingActionName = selectedItem != null ? selectedItem.Text : null; //获取动作帧 this.GetSelectedBodyFrame(out int bodyFrame, out _); this.GetSelectedEmotionFrame(out int emoFrame, out _); this.GetSelectedTamingFrame(out int tamingFrame, out _); //获取武器状态 selectedItem = this.cmbWeaponType.SelectedItem as ComboItem; this.avatar.WeaponType = selectedItem != null ? Convert.ToInt32(selectedItem.Text) : 0; selectedItem = this.cmbWeaponIdx.SelectedItem as ComboItem; this.avatar.WeaponIndex = selectedItem != null ? Convert.ToInt32(selectedItem.Text) : 0; //获取耳朵状态 selectedItem = this.cmbEar.SelectedItem as ComboItem; this.avatar.EarType = selectedItem != null ? Convert.ToInt32(selectedItem.Text) : 0; if (bodyFrame < 0 && emoFrame < 0 && tamingFrame < 0) { return; } string actionTag = string.Format("{0}:{1},{2}:{3},{4}:{5},{6},{7},{8},{9},{10}", this.avatar.ActionName, bodyFrame, this.avatar.EmotionName, emoFrame, this.avatar.TamingActionName, tamingFrame, this.avatar.HairCover ? 1 : 0, this.avatar.ShowHairShade ? 1 : 0, this.avatar.EarType, this.avatar.WeaponType, this.avatar.WeaponIndex); if (!avatarContainer1.HasCache(actionTag)) { try { var actionFrames = avatar.GetActionFrames(avatar.ActionName); var bone = avatar.CreateFrame(bodyFrame, emoFrame, tamingFrame); var layers = avatar.CreateFrameLayers(bone); avatarContainer1.AddCache(actionTag, layers); } catch { } } avatarContainer1.SetKey(actionTag); } private string GetAllPartsTag() { string[] partsID = new string[avatar.Parts.Length]; for (int i = 0; i < avatar.Parts.Length; i++) { var part = avatar.Parts[i]; if (part != null && part.Visible) { partsID[i] = part.ID.ToString(); } } return string.Join(",", partsID); } void AddPart(string imgPath) { Wz_Node imgNode = PluginManager.FindWz(imgPath); if (imgNode != null) { this.avatar.AddPart(imgNode); } } private void SelectBodyAction(string actionName) { for (int i = 0; i < cmbActionBody.Items.Count; i++) { ComboItem item = cmbActionBody.Items[i] as ComboItem; if (item != null && item.Text == actionName) { cmbActionBody.SelectedIndex = i; return; } } } private void SelectEmotion(string emotionName) { for (int i = 0; i < cmbEmotion.Items.Count; i++) { ComboItem item = cmbEmotion.Items[i] as ComboItem; if (item != null && item.Text == emotionName) { cmbEmotion.SelectedIndex = i; return; } } } #region 同步界面 private void FillBodyAction() { var oldSelection = cmbActionBody.SelectedItem as ComboItem; int? newSelection = null; cmbActionBody.BeginUpdate(); cmbActionBody.Items.Clear(); foreach (var action in this.avatar.Actions) { ComboItem cmbItem = new ComboItem(action.Name); switch (action.Level) { case 0: cmbItem.FontStyle = FontStyle.Bold; cmbItem.ForeColor = Color.Indigo; break; case 1: cmbItem.ForeColor = Color.Indigo; break; } cmbItem.Tag = action; cmbActionBody.Items.Add(cmbItem); if (newSelection == null && oldSelection != null) { if (cmbItem.Text == oldSelection.Text) { newSelection = cmbActionBody.Items.Count - 1; } } } if (cmbActionBody.Items.Count > 0) { cmbActionBody.SelectedIndex = newSelection ?? 0; } cmbActionBody.EndUpdate(); } private void FillEmotion() { FillComboItems(cmbEmotion, avatar.Emotions); } private void FillTamingAction() { FillComboItems(cmbActionTaming, avatar.TamingActions); } private void FillWeaponTypes() { List weaponTypes = avatar.GetCashWeaponTypes(); FillComboItems(cmbWeaponType, weaponTypes.ConvertAll(i => i.ToString())); } private void SetTamingDefaultBodyAction() { string actionName; var tamingAction = (this.cmbActionTaming.SelectedItem as ComboItem)?.Text; switch (tamingAction) { case "ladder": case "rope": actionName = tamingAction; break; default: actionName = "sit"; break; } SelectBodyAction(actionName); } private void SetTamingDefault() { if (this.avatar.Taming != null) { var tamingAction = (this.cmbActionTaming.SelectedItem as ComboItem)?.Text; if (tamingAction != null) { string forceAction = this.avatar.Taming.Node.FindNodeByPath($@"characterAction\{tamingAction}").GetValueEx(null); if (forceAction != null) { this.SelectBodyAction(forceAction); } string forceEmotion = this.avatar.Taming.Node.FindNodeByPath($@"characterEmotion\{tamingAction}").GetValueEx(null); if (forceEmotion != null) { this.SelectEmotion(forceEmotion); } } } } /// /// 更新当前显示部件列表。 /// private void FillAvatarParts() { itemPanel1.BeginUpdate(); itemPanel1.Items.Clear(); foreach (var part in avatar.Parts) { if (part != null) { var btn = new AvatarPartButtonItem(); var stringLinker = this.PluginEntry.Context.DefaultStringLinker; StringResult sr; string text; if (part.ID != null && stringLinker.StringEqp.TryGetValue(part.ID.Value, out sr)) { text = string.Format("{0}\r\n{1}", sr.Name, part.ID); } else { text = string.Format("{0}\r\n{1}", "(null)", part.ID == null ? "-" : part.ID.ToString()); } btn.Text = text; btn.SetIcon(part.Icon.Bitmap); btn.Tag = part; btn.Checked = part.Visible; btn.btnItemShow.Click += BtnItemShow_Click; btn.btnItemDel.Click += BtnItemDel_Click; btn.CheckedChanged += Btn_CheckedChanged; itemPanel1.Items.Add(btn); } } itemPanel1.EndUpdate(); } private void BtnItemShow_Click(object sender, EventArgs e) { var btn = (sender as BaseItem).Parent as AvatarPartButtonItem; if (btn != null) { btn.Checked = !btn.Checked; } } private void BtnItemDel_Click(object sender, EventArgs e) { var btn = (sender as BaseItem).Parent as AvatarPartButtonItem; if (btn != null) { var part = btn.Tag as AvatarPart; if (part != null) { int index = Array.IndexOf(this.avatar.Parts, part); if (index > -1) { this.avatar.Parts[index] = null; this.FillAvatarParts(); this.UpdateDisplay(); } } } } private void Btn_CheckedChanged(object sender, EventArgs e) { var btn = sender as AvatarPartButtonItem; if (btn != null) { var part = btn.Tag as AvatarPart; if (part != null) { part.Visible = btn.Checked; this.UpdateDisplay(); } } } private void FillBodyActionFrame() { ComboItem actionItem = cmbActionBody.SelectedItem as ComboItem; if (actionItem != null) { var frames = avatar.GetActionFrames(actionItem.Text); FillComboItems(cmbBodyFrame, frames); } else { cmbBodyFrame.Items.Clear(); } } private void FillEmotionFrame() { ComboItem emotionItem = cmbEmotion.SelectedItem as ComboItem; if (emotionItem != null) { var frames = avatar.GetFaceFrames(emotionItem.Text); FillComboItems(cmbEmotionFrame, frames); } else { cmbEmotionFrame.Items.Clear(); } } private void FillTamingActionFrame() { ComboItem actionItem = cmbActionTaming.SelectedItem as ComboItem; if (actionItem != null) { var frames = avatar.GetTamingFrames(actionItem.Text); FillComboItems(cmbTamingFrame, frames); } else { cmbTamingFrame.Items.Clear(); } } private void FillWeaponIdx() { FillComboItems(cmbWeaponIdx, 0, 4); } private void FillEarSelection() { FillComboItems(cmbEar, 0, 4); } private void FillComboItems(ComboBoxEx comboBox, int start, int count) { List items = new List(count); for (int i = 0; i < count; i++) { ComboItem item = new ComboItem(); item.Text = (start + i).ToString(); items.Add(item); } FillComboItems(comboBox, items); } private void FillComboItems(ComboBoxEx comboBox, IEnumerable items) { List _items = new List(); foreach (var itemText in items) { ComboItem item = new ComboItem(); item.Text = itemText; _items.Add(item); } FillComboItems(comboBox, _items); } private void FillComboItems(ComboBoxEx comboBox, IEnumerable frames) { List items = new List(); int i = 0; foreach (var f in frames) { ComboItem item = new ComboItem(); item.Text = (i++).ToString(); item.Tag = f; items.Add(item); } FillComboItems(comboBox, items); } private void FillComboItems(ComboBoxEx comboBox, IEnumerable items) { //保持原有选项 var oldSelection = comboBox.SelectedItem as ComboItem; int? newSelection = null; comboBox.BeginUpdate(); comboBox.Items.Clear(); foreach (var item in items) { comboBox.Items.Add(item); if (newSelection == null && oldSelection != null) { if (item.Text == oldSelection.Text) { newSelection = comboBox.Items.Count - 1; } } } //恢复原有选项 if (comboBox.Items.Count > 0) { comboBox.SelectedIndex = newSelection ?? 0; } comboBox.EndUpdate(); } private bool GetSelectedActionFrame(ComboBoxEx comboBox, out int frameIndex, out ActionFrame actionFrame) { var selectedItem = comboBox.SelectedItem as ComboItem; if (selectedItem != null && int.TryParse(selectedItem.Text, out frameIndex) && selectedItem?.Tag is ActionFrame _actionFrame) { actionFrame = _actionFrame; return true; } else { frameIndex = -1; actionFrame = null; return false; } } private bool GetSelectedBodyFrame(out int frameIndex, out ActionFrame actionFrame) { return this.GetSelectedActionFrame(this.cmbBodyFrame, out frameIndex, out actionFrame); } private bool GetSelectedEmotionFrame(out int frameIndex, out ActionFrame actionFrame) { return this.GetSelectedActionFrame(this.cmbEmotionFrame, out frameIndex, out actionFrame); } private bool GetSelectedTamingFrame(out int frameIndex, out ActionFrame actionFrame) { return this.GetSelectedActionFrame(this.cmbTamingFrame, out frameIndex, out actionFrame); } #endregion private void cmbActionBody_SelectedIndexChanged(object sender, EventArgs e) { this.SuspendUpdateDisplay(); FillBodyActionFrame(); this.ResumeUpdateDisplay(); UpdateDisplay(); } private void cmbEmotion_SelectedIndexChanged(object sender, EventArgs e) { this.SuspendUpdateDisplay(); FillEmotionFrame(); this.ResumeUpdateDisplay(); UpdateDisplay(); } private void cmbActionTaming_SelectedIndexChanged(object sender, EventArgs e) { this.SuspendUpdateDisplay(); FillTamingActionFrame(); SetTamingDefaultBodyAction(); SetTamingDefault(); this.ResumeUpdateDisplay(); UpdateDisplay(); } private void cmbBodyFrame_SelectedIndexChanged(object sender, EventArgs e) { UpdateDisplay(); } private void cmbEmotionFrame_SelectedIndexChanged(object sender, EventArgs e) { UpdateDisplay(); } private void cmbTamingFrame_SelectedIndexChanged(object sender, EventArgs e) { UpdateDisplay(); } private void cmbWeaponType_SelectedIndexChanged(object sender, EventArgs e) { UpdateDisplay(); } private void cmbWeaponIdx_SelectedIndexChanged(object sender, EventArgs e) { UpdateDisplay(); } private void cmbEar_SelectedIndexChanged(object sender, EventArgs e) { UpdateDisplay(); } private void chkBodyPlay_CheckedChanged(object sender, EventArgs e) { if (chkBodyPlay.Checked) { if (!this.timer1.Enabled) { AnimateStart(); } if (this.GetSelectedBodyFrame(out _, out var actionFrame) && actionFrame.AbsoluteDelay > 0) { this.animator.BodyDelay = actionFrame.AbsoluteDelay; } } else { this.animator.BodyDelay = -1; TimerEnabledCheck(); } } private void chkEmotionPlay_CheckedChanged(object sender, EventArgs e) { if (chkEmotionPlay.Checked) { if (!this.timer1.Enabled) { AnimateStart(); } if (this.GetSelectedEmotionFrame(out _, out var actionFrame) && actionFrame.AbsoluteDelay > 0) { this.animator.EmotionDelay = actionFrame.AbsoluteDelay; } } else { this.animator.EmotionDelay = -1; TimerEnabledCheck(); } } private void chkTamingPlay_CheckedChanged(object sender, EventArgs e) { if (chkTamingPlay.Checked) { if (!this.timer1.Enabled) { AnimateStart(); } if (this.GetSelectedTamingFrame(out _, out var actionFrame) && actionFrame.AbsoluteDelay > 0) { this.animator.TamingDelay = actionFrame.AbsoluteDelay; } } else { this.animator.TamingDelay = -1; TimerEnabledCheck(); } } private void chkHairCover_CheckedChanged(object sender, EventArgs e) { avatar.HairCover = chkHairCover.Checked; UpdateDisplay(); } private void chkHairShade_CheckedChanged(object sender, EventArgs e) { avatar.ShowHairShade = chkHairShade.Checked; UpdateDisplay(); } private void timer1_Tick(object sender, EventArgs e) { this.animator.Elapse(timer1.Interval); this.AnimateUpdate(); int interval = this.animator.NextFrameDelay; if (interval <= 0) { this.timer1.Stop(); } else { this.timer1.Interval = interval; } } private void AnimateUpdate() { this.SuspendUpdateDisplay(); if (this.animator.BodyDelay == 0 && FindNextFrame(cmbBodyFrame) && this.GetSelectedBodyFrame(out _, out var bodyFrame)) { this.animator.BodyDelay = bodyFrame.AbsoluteDelay; } if (this.animator.EmotionDelay == 0 && FindNextFrame(cmbEmotionFrame) && this.GetSelectedEmotionFrame(out _, out var emoFrame)) { this.animator.EmotionDelay = emoFrame.AbsoluteDelay; } if (this.animator.TamingDelay == 0 && FindNextFrame(cmbTamingFrame) && this.GetSelectedTamingFrame(out _, out var tamingFrame)) { this.animator.TamingDelay = tamingFrame.AbsoluteDelay; } this.ResumeUpdateDisplay(); } private void AnimateStart() { TimerEnabledCheck(); if (timer1.Enabled) { AnimateUpdate(); } } private void TimerEnabledCheck() { if (chkBodyPlay.Checked || chkEmotionPlay.Checked || chkTamingPlay.Checked) { if (!this.timer1.Enabled) { this.timer1.Interval = 1; this.timer1.Start(); } } else { AnimateStop(); } } private void AnimateStop() { chkBodyPlay.Checked = false; chkEmotionPlay.Checked = false; chkTamingPlay.Checked = false; this.timer1.Stop(); } private bool FindNextFrame(ComboBoxEx cmbFrames) { ComboItem item = cmbFrames.SelectedItem as ComboItem; if (item == null) { if (cmbFrames.Items.Count > 0) { cmbFrames.SelectedIndex = 0; return true; } else { return false; } } int selectedIndex = cmbFrames.SelectedIndex; int i = selectedIndex; do { i = (++i) % cmbFrames.Items.Count; item = cmbFrames.Items[i] as ComboItem; if (item != null && item.Tag is ActionFrame actionFrame && actionFrame.AbsoluteDelay > 0) { cmbFrames.SelectedIndex = i; return true; } } while (i != selectedIndex); return false; } private void btnCode_Click(object sender, EventArgs e) { var dlg = new AvatarCodeForm(); string code = GetAllPartsTag(); dlg.CodeText = code; if (dlg.ShowDialog() == DialogResult.OK) { if (dlg.CodeText != code && !string.IsNullOrEmpty(dlg.CodeText)) { LoadCode(dlg.CodeText, dlg.LoadType); } } } private void btnMale_Click(object sender, EventArgs e) { if (this.avatar.Parts.All(part => part == null) || MessageBoxEx.Show("初始化为男性角色?", "提示") == DialogResult.OK) { LoadCode("2000,12000,20000,30000,1040036,1060026", 0); } } private void btnFemale_Click(object sender, EventArgs e) { if (this.avatar.Parts.All(part => part == null) || MessageBoxEx.Show("初始化为女性角色?", "提示") == DialogResult.OK) { LoadCode("2000,12000,21000,31000,1041046,1061039", 0); } } private void btnReset_Click(object sender, EventArgs e) { this.avatarContainer1.Origin = new Point(this.avatarContainer1.Width / 2, this.avatarContainer1.Height / 2 + 40); this.avatarContainer1.Invalidate(); } private void btnSaveAsGif_Click(object sender, EventArgs e) { bool bodyPlaying = chkBodyPlay.Checked && cmbBodyFrame.Items.Count > 1; bool emoPlaying = chkEmotionPlay.Checked && cmbEmotionFrame.Items.Count > 1; bool tamingPlaying = chkTamingPlay.Checked && cmbTamingFrame.Items.Count > 1; int aniCount = new[] { bodyPlaying, emoPlaying, tamingPlaying }.Count(b => b); if (aniCount == 0) { // no animation is playing, save as png var dlg = new SaveFileDialog() { Title = "Save avatar frame", Filter = "*.png|*.png|*.*|*.*", FileName = "avatar.png" }; if (dlg.ShowDialog() != DialogResult.OK) { return; } this.GetSelectedBodyFrame(out int bodyFrame, out _); this.GetSelectedEmotionFrame(out int emoFrame, out _); this.GetSelectedTamingFrame(out int tamingFrame, out _); var bone = this.avatar.CreateFrame(bodyFrame, emoFrame, tamingFrame); var frame = this.avatar.DrawFrame(bone); frame.Bitmap.Save(dlg.FileName, System.Drawing.Imaging.ImageFormat.Png); } else { // get default encoder var config = ImageHandlerConfig.Default; using var encoder = AnimateEncoderFactory.CreateEncoder(config); var cap = encoder.Compatibility; string extensionFilter = string.Join(";", cap.SupportedExtensions.Select(ext => $"*{ext}")); var dlg = new SaveFileDialog() { Title = "Save avatar", Filter = string.Format("{0} Supported Files ({1})|{1}|All files (*.*)|*.*", encoder.Name, extensionFilter), FileName = string.Format("avatar{0}", cap.DefaultExtension) }; if (dlg.ShowDialog() != DialogResult.OK) { return; } string outputFileName = dlg.FileName; var actPlaying = new[] { bodyPlaying, emoPlaying, tamingPlaying }; var actFrames = new[] { cmbBodyFrame, cmbEmotionFrame, cmbTamingFrame } .Select((cmb, i) => { if (actPlaying[i]) { return cmb.Items.OfType().Select(cmbItem => new { index = int.Parse(cmbItem.Text), actionFrame = cmbItem.Tag as ActionFrame, }).ToArray(); } else if (this.GetSelectedActionFrame(cmb, out var index, out var actionFrame)) { return new[] { new { index, actionFrame } }; } else { return null; } }).ToArray(); var gifLayer = new GifLayer(); if (aniCount == 1 && !cap.IsFixedFrameRate) { int aniActIndex = Array.FindIndex(actPlaying, b => b); for (int fIdx = 0, fCnt = actFrames[aniActIndex].Length; fIdx < fCnt; fIdx++) { int[] actionIndices = new int[] { -1, -1, -1 }; int delay = 0; for (int i = 0; i < actFrames.Length; i++) { var act = actFrames[i]; if (i == aniActIndex) { actionIndices[i] = act[fIdx].index; delay = act[fIdx].actionFrame.AbsoluteDelay; } else if (act != null) { actionIndices[i] = act[0].index; } } var bone = this.avatar.CreateFrame(actionIndices[0], actionIndices[1], actionIndices[2]); var frameData = this.avatar.DrawFrame(bone); gifLayer.AddFrame(new GifFrame(frameData.Bitmap, frameData.Origin, delay)); } } else { // more than 2 animating action parts, for simplicity, we use fixed frame delay. int aniLength = actFrames.Max(layer => layer == null ? 0 : layer.Sum(f => f.actionFrame.AbsoluteDelay)); int aniDelay = config.MinDelay; // pipeline functions IEnumerable RenderDelay() { int t = 0; while (t < aniLength) { int frameDelay = Math.Min(aniLength - t, aniDelay); t += frameDelay; yield return frameDelay; } } IEnumerable> GetFrameActionIndices(IEnumerable delayEnumerator) { int[] time = new int[actFrames.Length]; int[] actionState = new int[actFrames.Length]; for (int i = 0; i < actionState.Length; i++) { actionState[i] = actFrames[i] != null ? 0 : -1; } foreach (int delay in delayEnumerator) { // return state int[] actIndices = new int[actionState.Length]; for (int i = 0; i < actionState.Length; i++) { actIndices[i] = actionState[i] > -1 ? actFrames[i][actionState[i]].index : -1; } yield return Tuple.Create(actIndices, delay); // update state for (int i = 0; i < actionState.Length; i++) { if (actPlaying[i]) { var act = actFrames[i]; time[i] += delay; int frameIndex = actionState[i]; while (time[i] >= act[frameIndex].actionFrame.AbsoluteDelay) { time[i] -= act[frameIndex].actionFrame.AbsoluteDelay; frameIndex = (frameIndex + 1) % act.Length; } actionState[i] = frameIndex; } } } } IEnumerable> MergeFrames(IEnumerable> frames) { int[] prevFrame = null; int prevDelay = 0; foreach (var frame in frames) { int[] currentFrame = frame.Item1; int currentDelay = frame.Item2; if (prevFrame == null) { prevFrame = currentFrame; prevDelay = currentDelay; } else if (prevFrame.SequenceEqual(currentFrame)) { prevDelay += currentDelay; } else { yield return Tuple.Create(prevFrame, prevDelay); prevFrame = currentFrame; prevDelay = currentDelay; } } if (prevFrame != null) { yield return Tuple.Create(prevFrame, prevDelay); } } GifFrame ApplyFrame(int[] actionIndices, int delay) { var bone = this.avatar.CreateFrame(actionIndices[0], actionIndices[1], actionIndices[2]); var frameData = this.avatar.DrawFrame(bone); return new GifFrame(frameData.Bitmap, frameData.Origin, delay); } // build pipeline var step1 = RenderDelay(); var step2 = GetFrameActionIndices(step1); var step3 = cap.IsFixedFrameRate ? step2 : MergeFrames(step2); var step4 = step3.Select(tp => ApplyFrame(tp.Item1, tp.Item2)); // run pipeline foreach(var gifFrame in step4) { gifLayer.AddFrame(gifFrame); } } if (gifLayer.Frames.Count <= 0) { MessageBoxEx.Show(this, "计算动画数据失败。", "Error"); return; } Rectangle clientRect = gifLayer.Frames .Select(f => new Rectangle(-f.Origin.X, -f.Origin.Y, f.Bitmap.Width, f.Bitmap.Height)) .Aggregate((rect1, rect2) => { int left = Math.Min(rect1.X, rect2.X); int top = Math.Min(rect1.Y, rect2.Y); int right = Math.Max(rect1.Right, rect2.Right); int bottom = Math.Max(rect1.Bottom, rect2.Bottom); return new Rectangle(left, top, right - left, bottom - top); }); Brush CreateBackgroundBrush() { switch (config.BackgroundType.Value) { default: case ImageBackgroundType.Transparent: return null; case ImageBackgroundType.Color: return new SolidBrush(config.BackgroundColor.Value); case ImageBackgroundType.Mosaic: int blockSize = Math.Max(1, config.MosaicInfo.BlockSize); var texture = new Bitmap(blockSize * 2, blockSize * 2); using (var g = Graphics.FromImage(texture)) using (var brush0 = new SolidBrush(config.MosaicInfo.Color0)) using (var brush1 = new SolidBrush(config.MosaicInfo.Color1)) { g.FillRectangle(brush0, 0, 0, blockSize, blockSize); g.FillRectangle(brush0, blockSize, blockSize, blockSize, blockSize); g.FillRectangle(brush1, 0, blockSize, blockSize, blockSize); g.FillRectangle(brush1, blockSize, 0, blockSize, blockSize); } return new TextureBrush(texture); } } using var bgBrush = CreateBackgroundBrush(); encoder.Init(outputFileName, clientRect.Width, clientRect.Height); foreach (IGifFrame gifFrame in gifLayer.Frames) { using (var bmp = new Bitmap(clientRect.Width, clientRect.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb)) { using (var g = Graphics.FromImage(bmp)) { // draw background if (bgBrush != null) { g.FillRectangle(bgBrush, 0, 0, bmp.Width, bmp.Height); } gifFrame.Draw(g, clientRect); } encoder.AppendFrame(bmp, Math.Max(cap.MinFrameDelay, gifFrame.Delay)); } } } } private void LoadCode(string code, int loadType) { //解析 var matches = Regex.Matches(code, @"(\d+)([,\s]|$)"); if (matches.Count <= 0) { MessageBoxEx.Show("无法解析的装备代码。", "错误"); return; } if (PluginManager.FindWz(Wz_Type.Base) == null) { MessageBoxEx.Show("没有打开Base.Wz。", "错误"); return; } var characWz = PluginManager.FindWz(Wz_Type.Character); //试图初始化 if (!this.inited && !this.AvatarInit()) { MessageBoxEx.Show("Avatar初始化失败。", "错误"); return; } var sl = this.PluginEntry.Context.DefaultStringLinker; if (!sl.HasValues) //生成默认stringLinker { sl.Load(PluginManager.FindWz(Wz_Type.String).GetValueEx(null)); } if (loadType == 0) //先清空。。 { Array.Clear(this.avatar.Parts, 0, this.avatar.Parts.Length); } List failList = new List(); foreach (Match m in matches) { int gearID; if (Int32.TryParse(m.Result("$1"), out gearID)) { Wz_Node imgNode = FindNodeByGearID(characWz, gearID); if (imgNode != null) { var part = this.avatar.AddPart(imgNode); OnNewPartAdded(part); } else { failList.Add(gearID); } } } //刷新 this.FillAvatarParts(); this.UpdateDisplay(); //其他提示 if (failList.Count > 0) { StringBuilder sb = new StringBuilder(); sb.AppendLine("以下部件没有找到:"); foreach (var gearID in failList) { sb.Append(" ").AppendLine(gearID.ToString("D8")); } MessageBoxEx.Show(sb.ToString(), "嗯.."); } } private Wz_Node FindNodeByGearID(Wz_Node characWz, int id) { string imgName = id.ToString("D8") + ".img"; Wz_Node imgNode = null; foreach (var node1 in characWz.Nodes) { if (node1.Text == imgName) { imgNode = node1; break; } else if (node1.Nodes.Count > 0) { foreach (var node2 in node1.Nodes) { if (node2.Text == imgName) { imgNode = node2; break; } } if (imgNode != null) { break; } } } if (imgNode != null) { Wz_Image img = imgNode.GetValue(); if (img != null && img.TryExtract()) { return img.Node; } } return null; } private class Animator { public Animator() { this.delays = new int[3] { -1, -1, -1 }; } private int[] delays; public int NextFrameDelay { get; private set; } public int BodyDelay { get { return this.delays[0]; } set { this.delays[0] = value; Update(); } } public int EmotionDelay { get { return this.delays[1]; } set { this.delays[1] = value; Update(); } } public int TamingDelay { get { return this.delays[2]; } set { this.delays[2] = value; Update(); } } public void Elapse(int millisecond) { for (int i = 0; i < delays.Length; i++) { if (delays[i] >= 0) { delays[i] = delays[i] > millisecond ? (delays[i] - millisecond) : 0; } } } private void Update() { int nextFrame = 0; foreach (int delay in this.delays) { if (delay > 0) { nextFrame = nextFrame <= 0 ? delay : Math.Min(nextFrame, delay); } } this.NextFrameDelay = nextFrame; } } } } ================================================ FILE: WzComparerR2.Avatar/UI/AvatarForm.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 17, 17 190, 17 ================================================ FILE: WzComparerR2.Avatar/UI/AvatarPartButtonItem.Designer.cs ================================================ namespace WzComparerR2.Avatar.UI { partial class AvatarPartButtonItem { /// /// 必需的设计器变量。 /// private System.ComponentModel.IContainer components = null; /// /// 清理所有正在使用的资源。 /// /// 如果应释放托管资源,为 true;否则为 false。 protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows 窗体设计器生成的代码 /// /// 设计器支持所需的方法 - 不要 /// 使用代码编辑器修改此方法的内容。 /// private void InitializeComponent() { this.btnItemShow = new DevComponents.DotNetBar.ButtonItem(); this.btnItemDel = new DevComponents.DotNetBar.ButtonItem(); // // btnItemShow // this.btnItemShow.Name = "btnItemShow"; this.btnItemShow.Text = "显示/隐藏"; // // btnItemDel // this.btnItemDel.Name = "btnItemDel"; this.btnItemDel.Text = "移除"; // // AvatarPartButtonItem // this.AutoCheckOnClick = true; this.ButtonStyle = DevComponents.DotNetBar.eButtonStyle.ImageAndText; this.GlobalItem = false; this.ImageFixedSize = new System.Drawing.Size(32, 32); this.SubItems.AddRange(new DevComponents.DotNetBar.BaseItem[] { this.btnItemShow, this.btnItemDel}); this.SubItemsExpandWidth = 16; } #endregion public DevComponents.DotNetBar.ButtonItem btnItemShow; public DevComponents.DotNetBar.ButtonItem btnItemDel; } } ================================================ FILE: WzComparerR2.Avatar/UI/AvatarPartButtonItem.cs ================================================ using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Text; using System.Windows.Forms; using DevComponents.DotNetBar; using System.Drawing.Imaging; namespace WzComparerR2.Avatar.UI { internal partial class AvatarPartButtonItem : ButtonItem { public AvatarPartButtonItem() { InitializeComponent(); } public void SetIcon(Bitmap icon) { if (icon != null) { if (!this.ImageFixedSize.IsEmpty && icon.Size != this.ImageFixedSize) { Bitmap newIcon = new Bitmap(this.ImageFixedSize.Width, this.ImageFixedSize.Height, PixelFormat.Format32bppArgb); Graphics g = Graphics.FromImage(newIcon); int x = (newIcon.Width - icon.Width) / 2; int y = (newIcon.Height - icon.Height) / 2; g.DrawImage(icon, x, y); g.Dispose(); this.Image = newIcon; } else { this.Image = icon; } } else { this.Image = null; } } } } ================================================ FILE: WzComparerR2.Avatar/UI/AvatarPartButtonItem.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 False ================================================ FILE: WzComparerR2.Avatar/WzComparerR2.Avatar.csproj ================================================  net462;net6.0-windows;net8.0-windows true WzComparerR2.Avatar WzComparerR2.Avatar false true true false all false all false all false all ..\References\DevComponents.DotNetBar2.dll false Properties\CommonAssemblyInfo.cs ================================================ FILE: WzComparerR2.Common/Animation/Frame.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using WzComparerR2.WzLib; using WzComparerR2.Common; using WzComparerR2.Rendering; namespace WzComparerR2.Animation { public class Frame { public Frame() { this.A0 = 255; this.A1 = 255; } public Frame(Texture2D texture) : this(texture, null) { } public Frame(Texture2D atlasPage, Rectangle? atlasRect) : this() { this.Texture = atlasPage; this.AtlasRect = atlasRect; } public Texture2D Texture { get; set; } public Rectangle? AtlasRect { get; set; } public Wz_Png Png { get; set; } public int Page { get; set; } public Point Origin { get; set; } public int Z { get; set; } public int Delay { get; set; } public int A0 { get; set; } public int A1 { get; set; } public bool Blend { get; set; } public Rectangle Rectangle { get { if (AtlasRect != null) { return new Rectangle(-Origin.X, -Origin.Y, AtlasRect.Value.Width, AtlasRect.Value.Height); } else if (Texture != null) { return new Rectangle(-Origin.X, -Origin.Y, Texture.Width, Texture.Height); } else { return Rectangle.Empty; } } } public static Frame CreateFromNode(Wz_Node frameNode, GraphicsDevice graphicsDevice, GlobalFindNodeFunction findNode) { if (frameNode == null || frameNode.Value == null) { return null; } while (frameNode.Value is Wz_Uol) { Wz_Uol uol = frameNode.Value as Wz_Uol; Wz_Node uolNode = uol.HandleUol(frameNode); if (uolNode != null) { frameNode = uolNode; } else { break; } } if (frameNode.Value is Wz_Png) { var linkNode = frameNode.GetLinkedSourceNode(findNode); Wz_Png png = linkNode?.GetValue() ?? (Wz_Png)frameNode.Value; var frame = new Frame(png.ToTexture(graphicsDevice)) { Png = png, }; foreach (Wz_Node propNode in frameNode.Nodes) { switch (propNode.Text) { case "origin": frame.Origin = (propNode.Value as Wz_Vector).ToPoint(); break; case "delay": frame.Delay = propNode.GetValue(); break; case "z": frame.Z = propNode.GetValue(); break; case "a0": frame.A0 = propNode.GetValue(); break; case "a1": frame.A1 = propNode.GetValue(); break; } } if (frame.Delay == 0) { frame.Delay = 120;//给予默认delay } return frame; } return null; } } } ================================================ FILE: WzComparerR2.Common/Animation/FrameAnimationData.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using WzComparerR2.WzLib; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; namespace WzComparerR2.Animation { public class FrameAnimationData { public FrameAnimationData() { this.Frames = new List(); } public FrameAnimationData(IEnumerable frames) { this.Frames = new List(frames); } public List Frames { get; private set; } public Rectangle GetBound() { Rectangle? bound = null; foreach (var frame in this.Frames) { bound = bound == null ? frame.Rectangle : Rectangle.Union(frame.Rectangle, bound.Value); } return bound ?? Rectangle.Empty; } public static FrameAnimationData CreateFromNode(Wz_Node node, GraphicsDevice graphicsDevice, FrameAnimationCreatingOptions options, GlobalFindNodeFunction findNode) { if (node == null) return null; var anime = new FrameAnimationData(); if (options.HasFlag(FrameAnimationCreatingOptions.ScanAllChildrenFrames)) { foreach(var frameNode in node.Nodes) { Frame frame = Frame.CreateFromNode(frameNode, graphicsDevice, findNode); if (frame != null) { anime.Frames.Add(frame); } } } else { for (int i = 0; ; i++) { Wz_Node frameNode = node.FindNodeByPath(i.ToString()); if (frameNode == null || frameNode.Value == null) break; Frame frame = Frame.CreateFromNode(frameNode, graphicsDevice, findNode); if (frame == null) break; anime.Frames.Add(frame); } } if (anime.Frames.Count > 0) return anime; else return null; } } [Flags] public enum FrameAnimationCreatingOptions { None = 0, FindFrameNameInOrdinalNumber = 1 << 0, ScanAllChildrenFrames = 1 << 1, } } ================================================ FILE: WzComparerR2.Common/Animation/FrameAnimator.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Collections.ObjectModel; using WzComparerR2.Controls; using Microsoft.Xna.Framework; namespace WzComparerR2.Animation { public class FrameAnimator : AnimationItem { public FrameAnimator(FrameAnimationData data) { this.Data = data; this.Load(); } public FrameAnimationData Data { get; private set; } public Frame CurrentFrame { get; protected set; } public override int Length { get { return this._length; } } public int CurrentTime { get { return _timeOffset; } protected set { _timeOffset = value; } } private int _timeOffset; private int _length; private int[] _timeline; public override void Update(TimeSpan elapsedTime) { if (_length <= 0) { _timeOffset = 0; } else { _timeOffset += (int)elapsedTime.TotalMilliseconds; _timeOffset %= _length; } this.UpdateFrame(); } public override void Reset() { _timeOffset = 0; this.UpdateFrame(); } public override Rectangle Measure() { return CurrentFrame?.Rectangle ?? Rectangle.Empty; } public KeyFrame[] GetKeyFrames() { return this.Data.Frames.Select(f => new KeyFrame() { Length = f.Delay, Animated = f.A0 != f.A1 } ).ToArray(); } protected virtual void Load() { _timeline = CreateTimeline(this.Data.Frames.Select(f => f.Delay)); _length = _timeline.Last(); _timeOffset = 0; this.UpdateFrame(); } protected virtual void UpdateFrame() { if (this.Data.Frames.Count <= 0) { this.CurrentFrame = null; return; } float progress; int index = GetProcessFromTimeline(_timeline, _timeOffset, out progress); var frame = this.Data.Frames[index]; if (this.CurrentFrame == null) { this.CurrentFrame = new Frame(); } this.CurrentFrame.Texture = frame.Texture; this.CurrentFrame.AtlasRect = frame.AtlasRect; this.CurrentFrame.Z = frame.Z; this.CurrentFrame.Origin = frame.Origin; this.CurrentFrame.A0 = (int)MathHelper.Lerp(frame.A0, frame.A1, progress); this.CurrentFrame.Blend = frame.Blend; } public override object Clone() { return new FrameAnimator(this.Data); } public static int[] CreateTimeline(IEnumerable delays) { var timeLine = new List() { 0 }; foreach (var ms in delays) { timeLine.Add(timeLine[timeLine.Count - 1] + ms); } return timeLine.ToArray(); } public static int GetProcessFromTimeline(int[] timeline, int timeOffset, out float progress) { int index = Array.BinarySearch(timeline, timeOffset); progress = 0; if (index < 0) { index = ~index - 1; progress = (float)(timeOffset - timeline[index]) / (timeline[index + 1] - timeline[index]); } return index; } } } ================================================ FILE: WzComparerR2.Common/Animation/ISpineAnimationData.cs ================================================ using System; using WzComparerR2.Common; namespace WzComparerR2.Animation { public interface ISpineAnimationData { bool PremultipliedAlpha { get; } object SkeletonData { get; } SpineVersion SpineVersion { get; } ISpineAnimator CreateAnimator(); } } ================================================ FILE: WzComparerR2.Common/Animation/ISpineAnimator.cs ================================================ using System; using System.Collections.Generic; using System.Collections.ObjectModel; namespace WzComparerR2.Animation { public interface ISpineAnimator { ISpineAnimationData Data { get; } object Skeleton { get; } ReadOnlyCollection Animations { get; } ReadOnlyCollection Skins { get; } int SelectedAnimationIndex { get; set; } string SelectedAnimationName { get; set; } string SelectedSkin { get; set; } Queue NextAnimationName { get; set; } int CurrentTime { get; } void Render(Spine.SkeletonRenderer renderer); Microsoft.Xna.Framework.Rectangle GetSlotBounds(string slot); } } ================================================ FILE: WzComparerR2.Common/Animation/KeyFrame.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace WzComparerR2.Animation { public struct KeyFrame { public int Length { get; set; } public bool Animated { get; set; } } } ================================================ FILE: WzComparerR2.Common/Animation/MaplestoryCanvasVideoLoader.cs ================================================ using System; using System.Buffers; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; #if NET6_0_OR_GREATER using System.Runtime.Intrinsics; using System.Runtime.Intrinsics.X86; #endif using Microsoft.Xna.Framework.Graphics; using WzComparerR2.WzLib; using WzComparerR2.WzLib.Utilities; namespace WzComparerR2.Animation { public class MaplestoryCanvasVideoLoader { public FrameAnimationData Load(Wz_Video wzVideo, GraphicsDevice graphicsDevice) { var wzFile = wzVideo.WzFile; var stream = wzVideo.WzImage.OpenRead(); var mcvHeader = wzVideo.ReadVideoFileHeader(); Span fourCC = stackalloc byte[4]; MemoryMarshal.Cast(fourCC)[0] = mcvHeader.FourCC; bool separateAlphaChannel = (mcvHeader.DataFlag & McvDataFlags.AlphaMap) != 0; using VpxVideoDecoder dataDecoder = new VpxVideoDecoder(fourCC, mcvHeader.Width, mcvHeader.Height, 0); using VpxVideoDecoder alphaMapDecoder = separateAlphaChannel ? new VpxVideoDecoder(fourCC, mcvHeader.Width, mcvHeader.Height, 0) : null; var frames = new List(mcvHeader.FrameCount); byte[] textureBuffer = new byte[mcvHeader.Width * mcvHeader.Height * 4]; byte[] alphaMapBuffer = separateAlphaChannel ? new byte[mcvHeader.Width * mcvHeader.Height * 4] : null; // shared function to decode frame void readAndDecodeFrame(VpxVideoDecoder decoder, long videoDataOffset, int count, byte[] outputBuffer) { byte[] dataBuffer = ArrayPool.Shared.Rent(count); try { lock (wzFile.ReadLock) { stream.Position = wzVideo.Offset + videoDataOffset; stream.ReadExactly(dataBuffer, 0, count); } // read and decode frame data decoder.DecodeData(dataBuffer.AsSpan(0, count)); } finally { ArrayPool.Shared.Return(dataBuffer); } // get raw frames and convert format to bgra. // expected that we should only get single frame. if (decoder.GetNextFrame(out VpxFrame vpxFrame)) { if (vpxFrame.DisplayWidth != mcvHeader.Width || vpxFrame.DisplayHeight != mcvHeader.Height) { throw new Exception(string.Format("Decoded frame size ({0}*{1}) does not match the video size({2}*{3}).", vpxFrame.DisplayWidth, vpxFrame.DisplayHeight, mcvHeader.Width, mcvHeader.Height)); } // TODO: in future we can support rendering yuv420 in pixel shader instead of soft decoding. switch (vpxFrame.Format) { case VpxVideoDecoder.Interop.vpx_img_fmt.VPX_IMG_FMT_I420: I420ToARGB(vpxFrame.PlanesY, vpxFrame.StrideY, vpxFrame.PlanesU, vpxFrame.StrideU, vpxFrame.PlanesV, vpxFrame.StrideV, outputBuffer, mcvHeader.Width * 4, mcvHeader.Width, mcvHeader.Height); break; default: throw new Exception($"Unsupported frame format: {vpxFrame.Format}"); } if (decoder.GetNextFrame(out vpxFrame)) { throw new Exception($"Unexpectedly read more than one frame."); } } else { throw new Exception($"Failed to get vpx frame."); } } foreach (var fi in mcvHeader.Frames) { readAndDecodeFrame(dataDecoder, fi.DataOffset, fi.DataCount, textureBuffer); if (separateAlphaChannel && fi.AlphaDataOffset > -1 && fi.AlphaDataCount > 0) { readAndDecodeFrame(alphaMapDecoder, fi.AlphaDataOffset, fi.AlphaDataCount, alphaMapBuffer); MergeAlphaMap(textureBuffer, alphaMapBuffer); } var texture = new Texture2D(graphicsDevice, mcvHeader.Width, mcvHeader.Height, false, SurfaceFormat.Bgra32); texture.SetData(textureBuffer); var frame = new Frame(); frame.Texture = texture; frame.Delay = (int)fi.Delay.TotalMilliseconds; frames.Add(frame); } return new FrameAnimationData(frames); } private static unsafe void I420ToARGB(IntPtr src_y, int src_stride_y, IntPtr src_u, int src_stride_u, IntPtr src_v, int src_stride_v, Span dst_bgra, int dst_stride_bgra, int width, int height) { fixed (byte* pDest = dst_bgra) { I420ToARGB((byte*)src_y, src_stride_y, (byte*)src_u, src_stride_u, (byte*)src_v, src_stride_v, pDest, dst_stride_bgra, width, height); } } private static unsafe void MergeAlphaMap(Span textureData, ReadOnlySpan alphaMapData) { if ((textureData.Length & 3) != 0) { throw new ArgumentException("The length of TextureData must be a multiple of 4.", nameof(textureData)); } if ((alphaMapData.Length & 3) != 0) { throw new ArgumentException("The length of AlphaMapData must be a multiple of 4.", nameof(alphaMapData)); } if (textureData.Length != alphaMapData.Length) { throw new ArgumentException("The length of TextureData should be equal to the length of AlphaMapData."); } #if NET6_0_OR_GREATER if (textureData.Length >= 32 && Avx2.IsSupported) { Vector256 blendMask = Vector256.Create(0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255); Vector256 ymm0, ymm1; while (textureData.Length >= 32) { fixed (byte* pData = textureData) ymm0 = Avx.LoadVector256(pData); fixed (byte* pData = alphaMapData) ymm1 = Avx.LoadVector256(pData); // b g r a b g r a b g r a b g r a // => 0 b g r a b g r a b g r a b g r ymm1 = Avx2.ShiftLeftLogical128BitLane(ymm1, 1); // b g r _ b g r _ b g r _ b g r _ // _ _ _ r _ _ _ r _ _ _ r _ _ _ r ymm0 = Avx2.BlendVariable(ymm0, ymm1, blendMask); fixed (byte* pData = textureData) Avx.Store(pData, ymm0); textureData = textureData.Slice(32); alphaMapData = alphaMapData.Slice(32); } } if (textureData.Length >= 16 && Sse41.IsSupported) { Vector128 blendMask = Vector128.Create(0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255); Vector128 xmm0, xmm1; while (textureData.Length >= 16) { fixed (byte* pData = textureData) xmm0 = Sse2.LoadVector128(pData); fixed (byte* pData = alphaMapData) xmm1 = Sse2.LoadVector128(pData); xmm1 = Sse2.ShiftLeftLogical128BitLane(xmm0, 1); xmm0 = Sse41.BlendVariable(xmm0, xmm1, blendMask); fixed (byte* pData = textureData) Sse2.Store(pData, xmm0); textureData = textureData.Slice(16); alphaMapData = alphaMapData.Slice(16); } } #endif for (int i = 0; i < textureData.Length; i += 4) { textureData[i + 3] = alphaMapData[i + 2]; } } #region Interop private const string libYuv = @"libyuv"; [DllImport(libYuv)] private static unsafe extern int I420ToARGB([In] byte* src_y, int src_stride_y, [In] byte* src_u, int src_stride_u, [In] byte* src_v, int src_stride_v, [Out] byte* dst_bgra, int dst_stride_bgra, int width, int height); #endregion } } ================================================ FILE: WzComparerR2.Common/Animation/ModelBound.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using Microsoft.Xna.Framework; namespace WzComparerR2.Animation { public struct ModelBound { public float minX, minY, maxX, maxY; public bool IsEmpty { get { return minX >= maxX || minY >= maxY; } } public void Update(float[] vertices, int count) { int i = 0; if (count % 4 != 0) { if (vertices[0] > maxX) maxX = vertices[0]; if (vertices[0] < minX) minX = vertices[0]; if (vertices[1] > maxY) maxY = vertices[1]; if (vertices[1] < minY) minY = vertices[1]; i += 2; } while (i < count) { if (vertices[i] > vertices[i + 2]) { if (vertices[i] > maxX) maxX = vertices[i]; if (vertices[i + 2] < minX) minX = vertices[i + 2]; } else { if (vertices[i + 2] > maxX) maxX = vertices[i + 2]; if (vertices[i] < minX) minX = vertices[i]; } if (vertices[i + 1] > vertices[i + 3]) { if (vertices[i + 1] > maxY) maxY = vertices[i + 1]; if (vertices[i + 3] < minY) minY = vertices[i + 3]; } else { if (vertices[i + 3] > maxY) maxY = vertices[i + 3]; if (vertices[i + 1] < minY) minY = vertices[i + 1]; } i += 4; } } public Rectangle GetBound() { if (IsEmpty) { return new Rectangle(); } return new Rectangle((int)Math.Round(minX), (int)Math.Round(minY), (int)Math.Round(maxX - minX), (int)Math.Round(maxY - minY)); } public static ModelBound Empty { get { var b = new ModelBound(); b.minX = b.minY = float.MaxValue; b.maxX = b.maxY = float.MinValue; return b; } } } } ================================================ FILE: WzComparerR2.Common/Animation/SpineAnimationDataV2.cs ================================================ using System; using WzComparerR2.Common; using WzComparerR2.WzLib; using Microsoft.Xna.Framework.Graphics; using Spine.V2; namespace WzComparerR2.Animation { public class SpineAnimationDataV2 : ISpineAnimationData { private SpineAnimationDataV2() { } public bool PremultipliedAlpha { get; set; } public SkeletonData SkeletonData { get; private set; } public static SpineAnimationDataV2 CreateFromNode(Wz_Node atlasOrSkelNode, GraphicsDevice graphicsDevice, GlobalFindNodeFunction findNode) { var textureLoader = new WzSpineTextureLoader(atlasOrSkelNode.ParentNode, graphicsDevice, findNode); return CreateFromNode(atlasOrSkelNode, textureLoader); } public static SpineAnimationDataV2 CreateFromNode(Wz_Node atlasOrSkelNode, TextureLoader textureLoader) { return Create(SpineLoader.Detect(atlasOrSkelNode), textureLoader); } public static SpineAnimationDataV2 Create(SpineDetectionResult detectionResult, TextureLoader textureLoader) { var skeletonData = SpineLoader.LoadSkeletonV2(detectionResult, textureLoader); if (skeletonData == null) { return null; } bool pma = detectionResult.SourceNode.ParentNode.FindNodeByPath("PMA").GetValueEx(0) != 0; var anime = new SpineAnimationDataV2(); anime.SkeletonData = skeletonData; anime.PremultipliedAlpha = pma; return anime; } #region ISpineAnimationData bool ISpineAnimationData.PremultipliedAlpha => this.PremultipliedAlpha; object ISpineAnimationData.SkeletonData => this.SkeletonData; SpineVersion ISpineAnimationData.SpineVersion => SpineVersion.V2; ISpineAnimator ISpineAnimationData.CreateAnimator() => new SpineAnimatorV2(this); #endregion } } ================================================ FILE: WzComparerR2.Common/Animation/SpineAnimationDataV4.cs ================================================ using System; using WzComparerR2.Common; using WzComparerR2.WzLib; using Microsoft.Xna.Framework.Graphics; using Spine; namespace WzComparerR2.Animation { public class SpineAnimationDataV4 : ISpineAnimationData { private SpineAnimationDataV4() { } public bool PremultipliedAlpha { get; set; } public SkeletonData SkeletonData { get; private set; } public static SpineAnimationDataV4 CreateFromNode(Wz_Node atlasOrSkelNode, GraphicsDevice graphicsDevice, GlobalFindNodeFunction findNode) { var textureLoader = new WzSpineTextureLoader(atlasOrSkelNode.ParentNode, graphicsDevice, findNode); return CreateFromNode(atlasOrSkelNode, textureLoader); } public static SpineAnimationDataV4 CreateFromNode(Wz_Node atlasOrSkelNode, TextureLoader textureLoader) { return Create(SpineLoader.Detect(atlasOrSkelNode), textureLoader); } public static SpineAnimationDataV4 Create(SpineDetectionResult detectionResult, TextureLoader textureLoader) { var skeletonData = SpineLoader.LoadSkeletonV4(detectionResult, textureLoader); if (skeletonData == null) { return null; } bool pma = detectionResult.SourceNode.ParentNode.FindNodeByPath("PMA").GetValueEx(0) != 0; var anime = new SpineAnimationDataV4(); anime.SkeletonData = skeletonData; anime.PremultipliedAlpha = pma; return anime; } #region ISpineAnimationData bool ISpineAnimationData.PremultipliedAlpha => this.PremultipliedAlpha; object ISpineAnimationData.SkeletonData => this.SkeletonData; SpineVersion ISpineAnimationData.SpineVersion => SpineVersion.V4; ISpineAnimator ISpineAnimationData.CreateAnimator() => new SpineAnimatorV4(this); #endregion } } ================================================ FILE: WzComparerR2.Common/Animation/SpineAnimatorV2.cs ================================================ using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using WzComparerR2.Controls; using Microsoft.Xna.Framework; using Spine.V2; namespace WzComparerR2.Animation { public class SpineAnimatorV2 : AnimationItem, ISpineAnimator { public SpineAnimatorV2(SpineAnimationDataV2 data) { this.Data = data; this._selectedAniIndex = -1; this.Load(); } public SpineAnimationDataV2 Data { get; private set; } public Skeleton Skeleton { get; private set; } public ReadOnlyCollection Animations { get; private set; } public ReadOnlyCollection Skins { get; private set; } public int SelectedAnimationIndex { get { return this._selectedAniIndex; } set { if (value > -1) { string aniName = this.Animations[value]; var ani = this.Data.SkeletonData.FindAnimation(aniName); this._animationState.SetAnimation(0, ani, true); this._selectedAniIndex = value; } else { this._animationState.ClearTracks(); this._selectedAniIndex = -1; } this.Skeleton.SetToSetupPose(); this._animationState.Apply(this.Skeleton); this.Skeleton.UpdateWorldTransform(); } } public string SelectedAnimationName { get { if( this._selectedAniIndex > -1) { return this.Animations[this._selectedAniIndex]; } return null; } set { if (value != null) { this.SelectedAnimationIndex = this.Animations.IndexOf(value); } else { this.SelectedAnimationIndex = -1; } } } public string SelectedSkin { get { return this.Skeleton.Skin?.Name; } set { this.Skeleton.SetSkin(value); } } public int CurrentTime { get { return (int)((this._animationState?.GetCurrent(0)?.Time ?? 0) * 1000); } } internal Spine.V2.Animation SelectedAnimation { get { if (this._selectedAniIndex > -1) { return this.Data.SkeletonData.FindAnimation(SelectedAnimationName); } return null; } } public override void Reset() { this.SelectedAnimationIndex = this.SelectedAnimationIndex; } public override int Length { get { return (int)((this.SelectedAnimation?.Duration ?? 0f) * 1000); } } public Queue NextAnimationName { get; set; } = new Queue(); private int _selectedAniIndex; private AnimationState _animationState; public override void Update(TimeSpan elapsedTime) { if (this.NextAnimationName.Count() > 0 && CurrentTime > Length) { this.SelectedAnimationName = this.NextAnimationName.Dequeue(); } this._animationState.Update((float)elapsedTime.TotalSeconds); this._animationState.Apply(Skeleton); this.Skeleton.UpdateWorldTransform(); } public override Rectangle Measure() { ModelBound bound = ModelBound.Empty; UpdateBounds(ref bound, this.Skeleton); return bound.GetBound(); } public Rectangle GetSlotBounds(string slotName) { Skeleton skeleton = this.Skeleton; if (!string.IsNullOrEmpty(slotName)) { Slot slot = skeleton.FindSlot(slotName); if (slot != null) return GetSlotBoundingBox(slot); } ModelBound bound = ModelBound.Empty; UpdateBounds(ref bound, skeleton); return bound.GetBound(); } private void UpdateBounds(ref ModelBound bound, Skeleton skeleton) { float[] vertices = new float[8]; var drawOrder = skeleton.DrawOrder; for (int i = 0, n = drawOrder.Count; i < n; i++) { Slot slot = drawOrder.Items[i]; Attachment attachment = slot.Attachment; if (attachment is RegionAttachment) { RegionAttachment region = (RegionAttachment)attachment; region.ComputeWorldVertices(slot.Bone, vertices); bound.Update(vertices, 8); } else if (attachment is MeshAttachment) { MeshAttachment mesh = (MeshAttachment)attachment; int vertexCount = mesh.Vertices.Length; if (vertices.Length < vertexCount) vertices = new float[vertexCount]; mesh.ComputeWorldVertices(slot, vertices); bound.Update(vertices, vertexCount); } else if (attachment is SkinnedMeshAttachment) { SkinnedMeshAttachment mesh = (SkinnedMeshAttachment)attachment; int vertexCount = mesh.UVs.Length; if (vertices.Length < vertexCount) vertices = new float[vertexCount]; mesh.ComputeWorldVertices(slot, vertices); bound.Update(vertices, vertexCount); } } } private Rectangle GetSlotBoundingBox(Slot slot) { ModelBound bound = ModelBound.Empty; if (slot.Attachment is BoundingBoxAttachment) { BoundingBoxAttachment bb = (BoundingBoxAttachment)slot.Attachment; int vertexCount = bb.Vertices.Length; float[] vertices = new float[vertexCount]; bb.ComputeWorldVertices(slot.Bone, vertices); bound.Update(vertices, vertexCount); } return bound.GetBound(); } public override object Clone() { var clonedAnimator = new SpineAnimatorV2(this.Data); clonedAnimator.SelectedAnimationIndex = this.SelectedAnimationIndex; if (this.SelectedSkin != null) { clonedAnimator.SelectedSkin = this.SelectedSkin; } return clonedAnimator; } private void Load() { var skeletonData = this.Data.SkeletonData; this.Skeleton = new Skeleton(skeletonData); this.Animations = new ReadOnlyCollection(skeletonData.Animations.Select(ani => ani.Name).ToList()); this.Skins = new ReadOnlyCollection(skeletonData.Skins.Select(skin => skin.Name).ToList()); this._animationState = new AnimationState(new AnimationStateData(skeletonData)); if (this.Animations.Count > 0) { this.SelectedAnimationIndex = 0; } else { this.SelectedAnimationIndex = -1; } } #region ISpineAnimator ISpineAnimationData ISpineAnimator.Data => this.Data; object ISpineAnimator.Skeleton => this.Skeleton; ReadOnlyCollection ISpineAnimator.Animations => this.Animations; ReadOnlyCollection ISpineAnimator.Skins => this.Skins; int ISpineAnimator.SelectedAnimationIndex { get => this.SelectedAnimationIndex; set => this.SelectedAnimationIndex = value; } string ISpineAnimator.SelectedAnimationName { get => this.SelectedAnimationName; set => this.SelectedAnimationName = value; } string ISpineAnimator.SelectedSkin { get => this.SelectedSkin; set => this.SelectedSkin = value; } int ISpineAnimator.CurrentTime { get => this.CurrentTime; } void ISpineAnimator.Render(Spine.SkeletonRenderer renderer) => renderer.Draw(this.Skeleton); #endregion } } ================================================ FILE: WzComparerR2.Common/Animation/SpineAnimatorV4.cs ================================================ using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using WzComparerR2.Controls; using Microsoft.Xna.Framework; using Spine; namespace WzComparerR2.Animation { public class SpineAnimatorV4 : AnimationItem, ISpineAnimator { public SpineAnimatorV4(SpineAnimationDataV4 data) { this.Data = data; this._selectedAniIndex = -1; this.Load(); } public SpineAnimationDataV4 Data { get; private set; } public Skeleton Skeleton { get; private set; } public ReadOnlyCollection Animations { get; private set; } public ReadOnlyCollection Skins { get; private set; } public int SelectedAnimationIndex { get { return this._selectedAniIndex; } set { if (value > -1) { string aniName = this.Animations[value]; var ani = this.Data.SkeletonData.FindAnimation(aniName); this._animationState.SetAnimation(0, ani, true); this._selectedAniIndex = value; } else { this._animationState.ClearTracks(); this._selectedAniIndex = -1; } this.Skeleton.SetToSetupPose(); this._animationState.Apply(this.Skeleton); this.Skeleton.UpdateWorldTransform(); } } public string SelectedAnimationName { get { if (this._selectedAniIndex > -1) { return this.Animations[this._selectedAniIndex]; } return null; } set { if (value != null) { this.SelectedAnimationIndex = this.Animations.IndexOf(value); } else { this.SelectedAnimationIndex = -1; } } } public string SelectedSkin { get { return this.Skeleton.Skin?.Name; } set { this.Skeleton.SetSkin(value); } } public int CurrentTime { get { return (int)((this._animationState?.GetCurrent(0)?.TrackTime ?? 0) * 1000); } } internal Spine.Animation SelectedAnimation { get { if (this._selectedAniIndex > -1) { return this.Data.SkeletonData.FindAnimation(SelectedAnimationName); } return null; } } public override void Reset() { this.SelectedAnimationIndex = this.SelectedAnimationIndex; } public override int Length { get { return (int)((this.SelectedAnimation?.Duration ?? 0f) * 1000); } } public Queue NextAnimationName { get; set; } = new Queue(); private int _selectedAniIndex; private AnimationState _animationState; public override void Update(TimeSpan elapsedTime) { if (this.NextAnimationName.Count() > 0 && CurrentTime > Length) { this.SelectedAnimationName = this.NextAnimationName.Dequeue(); } this._animationState.Update((float)elapsedTime.TotalSeconds); this._animationState.Apply(Skeleton); this.Skeleton.UpdateWorldTransform(); } public override Rectangle Measure() { ModelBound bound = ModelBound.Empty; UpdateBounds(ref bound, this.Skeleton); return bound.GetBound(); } public Rectangle GetSlotBounds(string slotName) { Skeleton skeleton = this.Skeleton; if (!string.IsNullOrEmpty(slotName)) { Slot slot = skeleton.FindSlot(slotName); if (slot != null) return GetSlotBoundingBox(slot); } ModelBound bound = ModelBound.Empty; UpdateBounds(ref bound, skeleton); return bound.GetBound(); } private void UpdateBounds(ref ModelBound bound, Skeleton skeleton) { float[] vertices = new float[8]; var drawOrder = skeleton.DrawOrder; for (int i = 0, n = drawOrder.Count; i < n; i++) { Slot slot = drawOrder.Items[i]; Attachment attachment = slot.Attachment; if (attachment is RegionAttachment) { RegionAttachment region = (RegionAttachment)attachment; region.ComputeWorldVertices(slot, vertices, 0); bound.Update(vertices, 8); } else if (attachment is MeshAttachment) { MeshAttachment mesh = (MeshAttachment)attachment; int vertexCount = mesh.WorldVerticesLength; if (vertices.Length < vertexCount) vertices = new float[vertexCount]; mesh.ComputeWorldVertices(slot, vertices); bound.Update(vertices, vertexCount); } else if (attachment is ClippingAttachment) { // ignore, don't know how it works } } } private Rectangle GetSlotBoundingBox(Slot slot) { ModelBound bound = ModelBound.Empty; if (slot.Attachment is BoundingBoxAttachment) { BoundingBoxAttachment bb = (BoundingBoxAttachment)slot.Attachment; int vertexCount = bb.WorldVerticesLength; float[] vertices = new float[vertexCount]; bb.ComputeWorldVertices(slot, vertices); bound.Update(vertices, vertexCount); } return bound.GetBound(); } public override object Clone() { var clonedAnimator = new SpineAnimatorV4(this.Data); clonedAnimator.SelectedAnimationIndex = this.SelectedAnimationIndex; if (this.SelectedSkin != null) { clonedAnimator.SelectedSkin = this.SelectedSkin; } return clonedAnimator; } private void Load() { var skeletonData = this.Data.SkeletonData; this.Skeleton = new Skeleton(skeletonData); this.Animations = new ReadOnlyCollection(skeletonData.Animations.Select(ani => ani.Name).ToList()); this.Skins = new ReadOnlyCollection(skeletonData.Skins.Select(skin => skin.Name).ToList()); this._animationState = new AnimationState(new AnimationStateData(skeletonData)); if (this.Animations.Count > 0) { this.SelectedAnimationIndex = 0; } else { this.SelectedAnimationIndex = -1; } } #region ISpineAnimator ISpineAnimationData ISpineAnimator.Data => this.Data; object ISpineAnimator.Skeleton => this.Skeleton; ReadOnlyCollection ISpineAnimator.Animations => this.Animations; ReadOnlyCollection ISpineAnimator.Skins => this.Skins; int ISpineAnimator.SelectedAnimationIndex { get => this.SelectedAnimationIndex; set => this.SelectedAnimationIndex = value; } string ISpineAnimator.SelectedAnimationName { get => this.SelectedAnimationName; set => this.SelectedAnimationName = value; } string ISpineAnimator.SelectedSkin { get => this.SelectedSkin; set => this.SelectedSkin = value; } int ISpineAnimator.CurrentTime { get => this.CurrentTime; } void ISpineAnimator.Render(Spine.SkeletonRenderer renderer) => renderer.Draw(this.Skeleton); #endregion } } ================================================ FILE: WzComparerR2.Common/Animation/WzSpineTextureLoader.cs ================================================ using System; using WzComparerR2.Common; using WzComparerR2.Rendering; using WzComparerR2.WzLib; using Microsoft.Xna.Framework.Graphics; namespace WzComparerR2.Animation { public class WzSpineTextureLoader : Spine.TextureLoader, Spine.V2.TextureLoader { public WzSpineTextureLoader(Wz_Node topNode, GraphicsDevice graphicsDevice) : this(topNode, graphicsDevice, null) { } public WzSpineTextureLoader(Wz_Node topNode, GraphicsDevice graphicsDevice, GlobalFindNodeFunction findNodeFunc) { this.TopNode = topNode; this.GraphicsDevice = graphicsDevice; this.FindNodeFunction = findNodeFunc; } public Wz_Node TopNode { get; private set; } public GraphicsDevice GraphicsDevice { get; private set; } public GlobalFindNodeFunction FindNodeFunction { get; set; } public bool EnableTextureMissingFallback { get; set; } public void Load(Spine.AtlasPage page, string path) { if (this.TryLoadTexture(path, out var texture)) { page.rendererObject = texture; page.width = texture.Width; page.height = texture.Height; } else if (this.EnableTextureMissingFallback && page.width > 0 && page.height > 0) { page.rendererObject = this.CreateEmptyTexture(page.width, page.height); } } public void Load(Spine.V2.AtlasPage page, string path) { if (this.TryLoadTexture(path, out var texture)) { page.rendererObject = texture; page.width = texture.Width; page.height = texture.Height; } else if (this.EnableTextureMissingFallback && page.width > 0 && page.height > 0) { page.rendererObject = this.CreateEmptyTexture(page.width, page.height); } } public void Unload(object texture) { (texture as Texture2D)?.Dispose(); } private bool TryLoadTexture(string path, out Texture2D texture) { texture = null; var frameNode = this.TopNode.FindNodeByPath(path); frameNode = frameNode.ResolveUol(); if (frameNode?.Value is Wz_Png) { var linkNode = frameNode.GetLinkedSourceNode(FindNodeFunction); Wz_Png png = (linkNode ?? frameNode).GetValue(); texture = png.ToTexture(this.GraphicsDevice); return true; } return false; } private Texture2D CreateEmptyTexture(int width, int height) { return new Texture2D(this.GraphicsDevice, width, height, false, SurfaceFormat.Alpha8); } } } ================================================ FILE: WzComparerR2.Common/BitmapOrigin.cs ================================================ using System; using System.Collections.Generic; using System.Text; using System.Drawing; using WzComparerR2.WzLib; using WzComparerR2.Common; namespace WzComparerR2 { public struct BitmapOrigin { public BitmapOrigin(Bitmap bitmap) : this(bitmap, new Point(0, 0)) { } public BitmapOrigin(Bitmap bitmap, int x, int y) : this(bitmap, new Point(x, y)) { } public BitmapOrigin(Bitmap bitmap, Point origin) { this.bitmap = bitmap; this.origin = origin; } private Bitmap bitmap; private Point origin; /// /// 获取图片。 /// public Bitmap Bitmap { get { return bitmap; } set { bitmap = value; } } /// /// 获取或设置图片的原点坐标。 /// public Point Origin { get { return origin; } set { origin = value; } } /// /// 获取图片原点的相反数,一般为绘图坐标区域的实际绘图原点。 /// public Point OpOrigin { get { return new Point(-origin.X, -origin.Y); } } /// /// 获取图片的实际绘图区域,它由图片大小和原点的相反数组成。 /// public Rectangle Rectangle { get { if (this.bitmap == null) return new Rectangle(this.OpOrigin, new Size()); else return new Rectangle(this.OpOrigin, this.bitmap.Size); } } public static BitmapOrigin CreateFromNode(Wz_Node node, GlobalFindNodeFunction findNode) { BitmapOrigin bp = new BitmapOrigin(); Wz_Uol uol; while ((uol = node.GetValue(null)) != null) { node = uol.HandleUol(node); } //获取linkNode var linkNode = node.GetLinkedSourceNode(findNode); Wz_Png png = linkNode?.GetValue() ?? (Wz_Png)node.Value; bp.Bitmap = png?.ExtractPng(); Wz_Node originNode = node.FindNodeByPath("origin"); Wz_Vector vec = (originNode == null) ? null : originNode.GetValue(); bp.Origin = (vec == null) ? new Point() : new Point(vec.X, vec.Y); return bp; } } } ================================================ FILE: WzComparerR2.Common/Calculator.cs ================================================ using System; using System.Collections.Generic; using System.Text; using System.Text.RegularExpressions; namespace WzComparerR2 { public static class Calculator { public static decimal Parse(string mathExpression, params decimal[] args) { var tokens = Lexer(mathExpression); var inst = Suffix(tokens); var paramList = new Dictionary(); if (args != null) { for (int i = 0; i < args.Length; i++) { switch (i) { case 0: paramList["x"] = args[0]; paramList["X"] = args[0]; break; case 1: paramList["y"] = args[1]; break; case 2: paramList["z"] = args[2]; break; case 3: paramList["w"] = args[3]; break; } } } return Execute(inst, new EvalContext(paramList)); } private static List Lexer(string expr) { var tokens = new List(); if (string.IsNullOrEmpty(expr)) return tokens; int begin; for (int i = 0; i < expr.Length; i++) { switch (expr[i]) { case '+': case '-': case '*': case '/': tokens.Add(new Token(TokenType.Operator, expr[i].ToString())); break; case '.': tokens.Add(new Token(TokenType.Dot, null)); break; case '(': tokens.Add(new Token(TokenType.BracketStart, null)); break; case ')': tokens.Add(new Token(TokenType.BracketEnd, null)); break; case ',': tokens.Add(new Token(TokenType.Comma, null)); break; case ' ': break; //whitespace当不存在 case '%': break; //ignore default: if (char.IsDigit(expr[i])) { //尽力读取number begin = i; bool dot = false; while (++i < expr.Length) { if (char.IsDigit(expr[i])) { //继续读 } else if (expr[i] == '.' && !dot) { dot = true; } else { break; } } tokens.Add(new Token(TokenType.Number, expr.Substring(begin, i - begin))); i--; } else if (char.IsLetter(expr[i])) { //尽力读取id begin = i; while (++i < expr.Length) { if (!char.IsLetterOrDigit(expr[i])) { break; } } tokens.Add(new Token(TokenType.ID, expr.Substring(begin, i - begin))); i--; } else if (char.IsWhiteSpace(expr[i])) { //空白字符跳过 } else { //无效字符 throw new Exception("Unknown char '" + expr[i] + "'."); } break; } } return tokens; } //suffix 逆波兰表达式 private static List Suffix(List tokens) { var value = new List(); var stack = new Stack(); for (int i = 0; i < tokens.Count; i++) { var token = tokens[i]; switch (token.Type) { case TokenType.BracketStart: //括号 推进stack stack.Push(token); if (token.Tag == Tag.Call) value.Add(new Token(TokenType.CallStart, "")); break; case TokenType.BracketEnd: //括号结束 弹出到上一个括号 { bool foundBracketEnd = false; while (stack.Count > 0) { Token t = stack.Pop(); if (t.Type != TokenType.BracketStart) { value.Add(t); } else { if (t.Tag == Tag.Call) value.Add(new Token(TokenType.CallEnd, "")); foundBracketEnd = true; break; } } // ignore redundant bracketEnd at the end of expression, workaround for skill 80003671,80003672,80003677 if (!foundBracketEnd && i != tokens.Count - 1) { throw new ArgumentException("Brackets are not paired."); } } break; case TokenType.Operator: //运算符 if (i == 0 || tokens[i - 1].Type == TokenType.BracketStart || tokens[i - 1].Type == TokenType.Operator) { //独立判定一元运算符 token.Tag = Tag.Unary; } goto case TokenType.Dot; case TokenType.Dot: //取成员 while (stack.Count > 0) { //比较优先级 Token t = stack.Peek(); if (Priority(token) > Priority(t) || (token.Tag == Tag.Unary && t.Tag == Tag.Unary)) { //优先级比上个高 break; } else { value.Add(stack.Pop()); } } stack.Push(token); break; case TokenType.ID: //预判如果后面是括号 当成函数处理 value.Add(token); if (i + 1 < tokens.Count && tokens[i + 1].Type == TokenType.BracketStart) { while (stack.Count > 0) { Token t = stack.Peek(); if (t.Type == TokenType.Dot) { value.Add(stack.Pop()); } else { break; } } //标记下一个括号为call tokens[i + 1].Tag = Tag.Call; } break; case TokenType.Number: value.Add(token); break; case TokenType.Comma: //逗号 忽略好像也没事..感觉像卖萌的.. break; } } value.AddRange(stack); return value; } private static decimal Execute(List inst, EvalContext param) { var stack = new Stack(); object obj; decimal d1, d2; foreach (var token in inst) { switch (token.Type) { case TokenType.Number: stack.Push(Convert.ToDecimal(token.Value)); break; case TokenType.ID: if (param.TryGetValue(token.Value, out obj)) { stack.Push(obj); } else { throw new Exception("ID '" + token.Value + "' not found."); } break; case TokenType.Operator: if (token.Tag == Tag.Unary) { d1 = Convert.ToDecimal(stack.Pop()); switch (token.Value) { case "+": stack.Push(d1); break; case "-": stack.Push(-d1); break; } } else { d2 = Convert.ToDecimal(stack.Pop()); d1 = Convert.ToDecimal(stack.Pop()); switch (token.Value) { case "+": stack.Push(d1 + d2); break; case "-": stack.Push(d1 - d2); break; case "*": stack.Push(d1 * d2); break; case "/": stack.Push(d1 / d2); break; } } break; case TokenType.Dot: throw new NotSupportedException(); case TokenType.CallStart: stack.Push(TokenType.CallStart); break; case TokenType.CallEnd: var p = new Stack(); while (!TokenType.CallStart.Equals(obj = stack.Pop())) { p.Push(obj); } obj = (stack.Pop() as Delegate).DynamicInvoke(p.ToArray()); stack.Push(obj); break; } } return stack.Count <= 0 ? 0 : Convert.ToDecimal(stack.Pop()); } private class Token { public Token(TokenType type, String value) { this.Type = type; this.Value = value; } public TokenType Type; public string Value; public Tag Tag; } //优先级 private static int Priority(Token token) { if (token.Tag == Tag.Unary) return 4; switch (token.Value) { case "+": case "-": return 1; case "*": case "/": return 2; case ".": return 3; default: return 0; } } private enum TokenType { ID, //x,funcName Number, //123.45 BracketStart, //( BracketEnd, //) Dot, //.成员运算符 Operator, //+-*/ Comma, //,逗号 函数参数分隔符 CallStart = 100, //标记用 参数开始 CallEnd, //标记用 } private enum Tag { None = 0, Call, Unary, } private class EvalContext { public EvalContext() : this(null) { } public EvalContext(Dictionary parameters) { this._dict = new Dictionary(); if (parameters != null && parameters.Count > 0) { foreach (var kv in parameters) { _dict.Add(kv.Key, kv.Value); } } } public Dictionary _dict; public bool TryGetValue(string key, out object value) { return _dict.TryGetValue(key, out value) || TryGetFunction(key, out value); } private bool TryGetFunction(string key, out object value) { Match m; if (key == "u") { value = (Func)Math.Ceiling; return true; } else if (key == "d") { value = (Func)Math.Floor; return true; } else if (key == "min") { value = (Func)Math.Min; return true; } else if ((m = Regex.Match(key, @"^log(\d+)$")).Success) { var logBase = int.Parse(m.Result("$1")); value = (Func)(x => x <= 0 ? 0 : (decimal)Math.Floor(Math.Log(decimal.ToDouble(x), logBase))); return true; } value = null; return false; } } } } ================================================ FILE: WzComparerR2.Common/CharaSim/Addition.cs ================================================ using System; using System.Collections.Generic; using System.Text; using WzComparerR2.WzLib; namespace WzComparerR2.CharaSim { public class Addition { public Addition() { Props = new Dictionary(); ConValue = new List(); } public AdditionType Type { get; set; } public GearPropType ConType { get; set; } public List ConValue { get; private set; } public Dictionary Props { get; private set; } public string GetPropString() { StringBuilder sb; switch (this.Type) { case AdditionType.boss: sb = new StringBuilder(); sb.Append("攻击BOSS时,"); { string v1; if (this.Props.TryGetValue("prob", out v1)) sb.Append("有" + v1 + "的几率"); sb.Append("造成" + Props["damage"] + "%的额外伤害"); } return sb.ToString(); case AdditionType.critical: sb = new StringBuilder(); { string val; if (this.Props.TryGetValue("prob", out val)) { sb.AppendFormat("爆击率{0}%\r\n", val); } if (this.Props.TryGetValue("damage", out val)) { sb.AppendFormat("爆击伤害增加{0}%\r\n", val); } if (sb.Length > 2) { sb.Remove(sb.Length - 2, 2); } } return sb.ToString(); case AdditionType.elemboost: { string v1, elem; if (this.Props.TryGetValue("elemVol", out v1)) { switch (v1[0]) { case 'I': elem = "冰"; break; case 'F': elem = "火"; break; case 'L': elem = "雷"; break; default: elem = v1[0].ToString(); break; } return elem + "属性效果强化" + v1.Substring(1) + "%"; } } break; case AdditionType.hpmpchange: sb = new StringBuilder(); sb.Append("每10秒恢复"); { string v1; if (this.Props.TryGetValue("hpChangePerTime", out v1)) { sb.Append("HP " + v1); } } return sb.ToString(); case AdditionType.mobcategory: return "攻击" + ItemStringHelper.GetMobCategoryName(Convert.ToInt32(this.Props["category"])) + "怪物时,造成" + this.Props["damage"] + "%额外伤害"; case AdditionType.mobdie: sb = new StringBuilder(); { string v1; if (this.Props.TryGetValue("hpIncOnMobDie", out v1)) { sb.AppendLine("怪物死亡时 HP恢复" + v1); } if (this.Props.TryGetValue("hpIncRatioOnMobDie", out v1)) { sb.AppendLine("怪物死亡时 有" + Props["hpRatioProp"] + "%的几率 伤害的" + v1 + "%转换为HP (但不超过最大HP的10%。)"); } if (this.Props.TryGetValue("mpIncOnMobDie", out v1)) { sb.AppendLine("怪物死亡时 HP恢复" + v1); } if (this.Props.TryGetValue("mpIncRatioOnMobDie", out v1)) { sb.AppendLine("怪物死亡时 有" + Props["mpRatioProp"] + "%的几率 伤害的" + v1 + "%转换为MP (但不超过最大MP的10%。)"); } } if (sb.Length > 0) { sb.Append("在部分地区功能可能会受到限制。"); return sb.ToString(); } break; case AdditionType.skill: switch (Convert.ToInt32(this.Props["id"])) { case 90000000: return "有一定几率增加必杀效果"; case 90001001: return "有一定几率增加眩晕效果"; case 90001002: return "有一定几率增加缓速术效果"; case 90001003: return "有一定几率增加毒效果"; case 90001004: return "有一定几率增加暗黑效果"; case 90001005: return "有一定几率增加封印效果"; case 90001006: return "有一定几率增加结冰效果"; } break; case AdditionType.statinc: sb = new StringBuilder(); { foreach (var kv in Props) { try { GearPropType propType = (GearPropType)Enum.Parse(typeof(GearPropType), kv.Key); sb.AppendLine(ItemStringHelper.GetGearPropString(propType, Convert.ToInt32(kv.Value))); } catch { } } } if (sb.Length > 0) { return sb.ToString(); } break; default: return null; } return null; } public string GetConString() { switch (this.ConType) { case GearPropType.reqJob: string[] reqJobs = new string[this.ConValue.Count]; for (int i = 0; i < reqJobs.Length; i++) { reqJobs[i] = ItemStringHelper.GetJobName(this.ConValue[i]) ?? this.ConValue[i].ToString(); } return "职业为" + string.Join(" 或者 ", reqJobs) + "时"; case GearPropType.reqLevel: return this.ConValue[0] + "级以上时"; case GearPropType.reqCraft: int lastExp; return "手技经验值在" + this.ConValue[0] + "(" + getPersonalityLevel(this.ConValue[0], out lastExp) + "级" + lastExp + "点)以上时"; case GearPropType.reqWeekDay: string[] weekdays = new string[this.ConValue.Count]; for (int i = 0; i < this.ConValue.Count; i++) { weekdays[i] = GetWeekDayString(this.ConValue[i]); } return string.Join(", ", weekdays) + "时"; default: return null; } } private int getPersonalityLevel(int totalExp, out int lastExp) { int curExp = 0; for (int level = 0; ; level++) { if (level == 0) { curExp = 20; } else if (level < 10) { curExp = (int)Math.Round(curExp * 1.3, MidpointRounding.AwayFromZero); } else if (level < 20) { curExp = (int)Math.Round(curExp * 1.1, MidpointRounding.AwayFromZero); } else if (level < 30) { curExp = (int)Math.Round(curExp * 1.03, MidpointRounding.AwayFromZero); } else if (level < 70) { curExp = (int)Math.Round(curExp * 1.015, MidpointRounding.AwayFromZero); } else if (level < 100) { curExp = (int)Math.Round(curExp * 1.003, MidpointRounding.AwayFromZero); } else { lastExp = 0; return 100; } if (totalExp - curExp <= 0) { lastExp = totalExp; return level; } else { totalExp -= curExp; } } } private static string GetWeekDayString(int weekDay) { switch (weekDay) { case 0: return "周日"; case 1: return "周一"; case 2: return "周二"; case 3: return "周三"; case 4: return "周四"; case 5: return "周五"; case 6: return "周六"; default: return "周" + weekDay; //这怎么可能... } } public static Addition CreateFromNode(Wz_Node node) { if (node == null) return null; foreach (AdditionType type in Enum.GetValues(typeof(AdditionType))) { if (type.ToString() == node.Text) { Addition addition = new Addition(); addition.Type = type; Action addInt32 = n => addition.ConValue.Add(n.GetValue()); Action addWeekDay = n => { try { DayOfWeek weekday = (DayOfWeek)Enum.Parse(typeof(DayOfWeek), n.GetValue(), true); addition.ConValue.Add((int)weekday); } catch { } }; foreach (Wz_Node subNode in node.Nodes) { if (subNode.Text == "con") { Action addValueFunc = addInt32; foreach (Wz_Node conNode in subNode.Nodes) { switch (conNode.Text) { case "job": addition.ConType = GearPropType.reqJob; break; //case "lv": //已不被官方识别了 case "level": addition.ConType = GearPropType.reqLevel; break; case "craft": addition.ConType = GearPropType.reqCraft; break; case "weekDay": addition.ConType = GearPropType.reqWeekDay; addValueFunc = addWeekDay; //改变解析方法 break; default: //不识别的东西 addition.ConType = (GearPropType)0; continue; } if (conNode.Nodes.Count > 0) { foreach (Wz_Node conValNode in conNode.Nodes) { addValueFunc(conValNode); } } else { addValueFunc(conNode); } } } else { addition.Props.Add(subNode.Text, Convert.ToString(subNode.Value)); } } return addition; } } return null; } } } ================================================ FILE: WzComparerR2.Common/CharaSim/AdditionType.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace WzComparerR2.CharaSim { public enum AdditionType { skill = 1, mobcategory, elemboost, hpmpchange, critical, mobdie, boss, statinc = 8 } } ================================================ FILE: WzComparerR2.Common/CharaSim/AlienStone.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace WzComparerR2.CharaSim { public class AlienStone { public AlienStoneGrade Grade { get; set; } } } ================================================ FILE: WzComparerR2.Common/CharaSim/AlienStoneGrade.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace WzComparerR2.CharaSim { public enum AlienStoneGrade { /// /// 表示D级星岩。 /// Normal = 0, /// /// 表示C级星岩。 /// Rare = 1, /// /// 表示B级星岩。 /// Epic = 2, /// /// 表示A级星岩。 /// Unique = 3, /// /// 表示S级星岩。 /// Legendary = 4 } } ================================================ FILE: WzComparerR2.Common/CharaSim/DamageSkin.cs ================================================ using System; using System.Collections.Generic; using System.Text; using System.Drawing; using WzComparerR2.WzLib; using System.Linq; namespace WzComparerR2.CharaSim { public class DamageSkin { public DamageSkin() { MiniDigit = new Dictionary(); BigDigit = new Dictionary(); MiniCriticalDigit = new Dictionary(); BigCriticalDigit = new Dictionary(); MiniUnit = new Dictionary(); BigUnit = new Dictionary(); MiniCriticalUnit = new Dictionary(); BigCriticalUnit = new Dictionary(); EtcBitmap = new Dictionary(); MiniDigitSpacing = 0; BigDigitSpacing = 0; MiniCriticalDigitSpacing = 0; BigCriticalDigitSpacing = 0; MiniUnitSpacing = 0; BigUnitSpacing = 0; MiniCriticalUnitSpacing = 0; BigCriticalUnitSpacing = 0; } public int DamageSkinID { get; set; } public int ExtractItemID { get; set; } public Dictionary MiniDigit { get; set; } public int MiniDigitSpacing { get; set; } public Dictionary BigDigit { get; set; } public int BigDigitSpacing { get; set; } public Dictionary MiniCriticalDigit { get; set; } public int MiniCriticalDigitSpacing { get; set; } public Dictionary BigCriticalDigit { get; set; } public int BigCriticalDigitSpacing { get; set; } public Dictionary MiniUnit { get; set; } public int MiniUnitSpacing { get; set; } public Dictionary BigUnit { get; set; } public int BigUnitSpacing { get; set; } public Dictionary MiniCriticalUnit { get; set; } public int MiniCriticalUnitSpacing { get; set; } public Dictionary BigCriticalUnit { get; set; } public int BigCriticalUnitSpacing { get; set; } public Dictionary EtcBitmap { get; set; } public BitmapOrigin Sample { get; set; } public string Desc { get; set; } public string CustomType { get; set; } public static DamageSkin CreateFromNode(Wz_Node damageSkinNode, GlobalFindNodeFunction findNode) { if (damageSkinNode == null) return null; DamageSkin damageSkin = new DamageSkin(); damageSkin.DamageSkinID = Convert.ToInt32(damageSkinNode.Text); foreach (Wz_Node subNode in damageSkinNode.Nodes) { switch (subNode.Text) { case "desc": damageSkin.Desc = subNode.GetValue(); break; case "extractID": damageSkin.ExtractItemID = subNode.GetValue(); break; case "sample": damageSkin.Sample = BitmapOrigin.CreateFromNode(subNode, findNode); break; case "effect": foreach (Wz_Node effectNode in subNode.Nodes) { switch (effectNode.Text) { case "NoRed0": foreach (Wz_Node digitNode in effectNode.Nodes) { if (digitNode.Value is Wz_Uol || digitNode.Value is Wz_Png) { damageSkin.MiniDigit.Add(digitNode.Text, BitmapOrigin.CreateFromNode(digitNode, findNode)); } else if (digitNode.Nodes.Count > 1) { foreach (Wz_Node node in digitNode.Nodes) { if (node.Value is Wz_Uol || node.Value is Wz_Png) { damageSkin.MiniDigit.Add(digitNode.Text, BitmapOrigin.CreateFromNode(node, findNode)); break; } } } else if (digitNode.Text == "numberSpace") { damageSkin.MiniDigitSpacing = digitNode.GetValue(); } } break; case "NoRed1": foreach (Wz_Node digitNode in effectNode.Nodes) { if (digitNode.Value is Wz_Uol || digitNode.Value is Wz_Png) { damageSkin.BigDigit.Add(digitNode.Text, BitmapOrigin.CreateFromNode(digitNode, findNode)); } else if (digitNode.Nodes.Count > 1) { foreach (Wz_Node node in digitNode.Nodes) { if (node.Value is Wz_Uol || node.Value is Wz_Png) { damageSkin.BigDigit.Add(digitNode.Text, BitmapOrigin.CreateFromNode(node, findNode)); break; } } } else if (digitNode.Text == "numberSpace") { damageSkin.BigDigitSpacing = digitNode.GetValue(); } } break; case "NoCri0": foreach (Wz_Node digitNode in effectNode.Nodes) { if (digitNode.Value is Wz_Uol || digitNode.Value is Wz_Png) { damageSkin.MiniCriticalDigit.Add(digitNode.Text, BitmapOrigin.CreateFromNode(digitNode, findNode)); } else if (digitNode.Nodes.Count > 1) { foreach (Wz_Node node in digitNode.Nodes) { if (node.Value is Wz_Uol || node.Value is Wz_Png) { damageSkin.MiniCriticalDigit.Add(digitNode.Text, BitmapOrigin.CreateFromNode(node, findNode)); break; } } } else if (digitNode.Text == "numberSpace") { damageSkin.MiniCriticalDigitSpacing = digitNode.GetValue(); } } break; case "NoCri1": foreach (Wz_Node digitNode in effectNode.Nodes) { if (digitNode.Value is Wz_Uol || digitNode.Value is Wz_Png) { damageSkin.BigCriticalDigit.Add(digitNode.Text, BitmapOrigin.CreateFromNode(digitNode, findNode)); } else if (digitNode.Nodes.Count > 1) { foreach (Wz_Node node in digitNode.Nodes) { if (node.Value is Wz_Uol || node.Value is Wz_Png) { damageSkin.BigCriticalDigit.Add(digitNode.Text, BitmapOrigin.CreateFromNode(node, findNode)); break; } } } else if (digitNode.Text == "numberSpace") { damageSkin.BigCriticalDigitSpacing = digitNode.GetValue(); } } break; case "NoCustom": foreach (Wz_Node customSubNode in effectNode.Nodes) { switch (customSubNode.Text) { case "NoRed0": foreach (Wz_Node digitNode in customSubNode.Nodes) { if (digitNode.Value is Wz_Uol || digitNode.Value is Wz_Png) { damageSkin.MiniUnit.Add(digitNode.Text, BitmapOrigin.CreateFromNode(digitNode, findNode)); } else if (digitNode.Nodes.Count > 1) { foreach (Wz_Node node in digitNode.Nodes) { if (node.Value is Wz_Uol || node.Value is Wz_Png) { damageSkin.MiniUnit.Add(digitNode.Text, BitmapOrigin.CreateFromNode(node, findNode)); break; } } } else if (digitNode.Text == "numberSpace") { damageSkin.MiniUnitSpacing = digitNode.GetValue(); } } break; case "NoRed1": foreach (Wz_Node digitNode in customSubNode.Nodes) { if (digitNode.Value is Wz_Uol || digitNode.Value is Wz_Png) { damageSkin.BigUnit.Add(digitNode.Text, BitmapOrigin.CreateFromNode(digitNode, findNode)); } else if (digitNode.Nodes.Count > 1) { foreach (Wz_Node node in digitNode.Nodes) { if (node.Value is Wz_Uol || node.Value is Wz_Png) { damageSkin.BigUnit.Add(digitNode.Text, BitmapOrigin.CreateFromNode(node, findNode)); break; } } } else if (digitNode.Text == "numberSpace") { damageSkin.BigUnitSpacing = digitNode.GetValue(); } } break; case "NoCri0": foreach (Wz_Node digitNode in customSubNode.Nodes) { if (digitNode.Value is Wz_Uol || digitNode.Value is Wz_Png) { damageSkin.MiniCriticalUnit.Add(digitNode.Text, BitmapOrigin.CreateFromNode(digitNode, findNode)); } else if (digitNode.Nodes.Count > 1) { foreach (Wz_Node node in digitNode.Nodes) { if (node.Value is Wz_Uol || node.Value is Wz_Png) { damageSkin.MiniCriticalUnit.Add(digitNode.Text, BitmapOrigin.CreateFromNode(node, findNode)); break; } } } else if (digitNode.Text == "numberSpace") { damageSkin.MiniCriticalUnitSpacing = digitNode.GetValue(); } } break; case "NoCri1": foreach (Wz_Node digitNode in customSubNode.Nodes) { if (digitNode.Value is Wz_Uol || digitNode.Value is Wz_Png) { damageSkin.BigCriticalUnit.Add(digitNode.Text, BitmapOrigin.CreateFromNode(digitNode, findNode)); } else if (digitNode.Nodes.Count > 1) { foreach (Wz_Node node in digitNode.Nodes) { if (node.Value is Wz_Uol || node.Value is Wz_Png) { damageSkin.BigCriticalUnit.Add(digitNode.Text, BitmapOrigin.CreateFromNode(node, findNode)); break; } } } else if (digitNode.Text == "numberSpace") { damageSkin.BigCriticalUnitSpacing = digitNode.GetValue(); } } break; case "customType": damageSkin.CustomType = customSubNode.GetValue(); break; } } break; } } break; // Legacy below case "NoRed0": foreach (Wz_Node digitNode in subNode.Nodes) { if (digitNode.Value is Wz_Uol || digitNode.Value is Wz_Png) { damageSkin.MiniDigit.Add(digitNode.Text, BitmapOrigin.CreateFromNode(digitNode, findNode)); } else if (digitNode.Nodes.Count > 1) { foreach (Wz_Node node in digitNode.Nodes) { if (node.Value is Wz_Uol || node.Value is Wz_Png) { damageSkin.MiniDigit.Add(digitNode.Text, BitmapOrigin.CreateFromNode(node, findNode)); break; } } } else if (digitNode.Text == "numberSpace") { damageSkin.MiniDigitSpacing = digitNode.GetValue(); } } break; case "NoRed1": foreach (Wz_Node digitNode in subNode.Nodes) { if (digitNode.Value is Wz_Uol || digitNode.Value is Wz_Png) { damageSkin.BigDigit.Add(digitNode.Text, BitmapOrigin.CreateFromNode(digitNode, findNode)); } else if (digitNode.Nodes.Count > 1) { foreach (Wz_Node node in digitNode.Nodes) { if (node.Value is Wz_Uol || node.Value is Wz_Png) { damageSkin.BigDigit.Add(digitNode.Text, BitmapOrigin.CreateFromNode(node, findNode)); break; } } } else if (digitNode.Text == "numberSpace") { damageSkin.BigDigitSpacing = digitNode.GetValue(); } } break; case "NoCri0": foreach (Wz_Node digitNode in subNode.Nodes) { if (digitNode.Value is Wz_Uol || digitNode.Value is Wz_Png) { damageSkin.MiniCriticalDigit.Add(digitNode.Text, BitmapOrigin.CreateFromNode(digitNode, findNode)); } else if (digitNode.Nodes.Count > 1) { foreach (Wz_Node node in digitNode.Nodes) { if (node.Value is Wz_Uol || node.Value is Wz_Png) { damageSkin.MiniCriticalDigit.Add(digitNode.Text, BitmapOrigin.CreateFromNode(node, findNode)); break; } } } else if (digitNode.Text == "numberSpace") { damageSkin.MiniCriticalDigitSpacing = digitNode.GetValue(); } } break; case "NoCri1": foreach (Wz_Node digitNode in subNode.Nodes) { if (digitNode.Value is Wz_Uol || digitNode.Value is Wz_Png) { damageSkin.BigCriticalDigit.Add(digitNode.Text, BitmapOrigin.CreateFromNode(digitNode, findNode)); } else if (digitNode.Nodes.Count > 1) { foreach (Wz_Node node in digitNode.Nodes) { if (node.Value is Wz_Uol || node.Value is Wz_Png) { damageSkin.BigCriticalDigit.Add(digitNode.Text, BitmapOrigin.CreateFromNode(node, findNode)); break; } } } else if (digitNode.Text == "numberSpace") { damageSkin.BigCriticalDigitSpacing = digitNode.GetValue(); } } break; case "NoCustom": foreach (Wz_Node customSubNode in subNode.Nodes) { switch (customSubNode.Text) { case "NoRed0": foreach (Wz_Node digitNode in customSubNode.Nodes) { if (digitNode.Value is Wz_Uol || digitNode.Value is Wz_Png) { damageSkin.MiniUnit.Add(digitNode.Text, BitmapOrigin.CreateFromNode(digitNode, findNode)); } else if (digitNode.Nodes.Count > 1) { foreach (Wz_Node node in digitNode.Nodes) { if (node.Value is Wz_Uol || node.Value is Wz_Png) { damageSkin.MiniUnit.Add(digitNode.Text, BitmapOrigin.CreateFromNode(node, findNode)); break; } } } else if (digitNode.Text == "numberSpace") { damageSkin.MiniUnitSpacing = digitNode.GetValue(); } } break; case "NoRed1": foreach (Wz_Node digitNode in customSubNode.Nodes) { if (digitNode.Value is Wz_Uol || digitNode.Value is Wz_Png) { damageSkin.BigUnit.Add(digitNode.Text, BitmapOrigin.CreateFromNode(digitNode, findNode)); } else if (digitNode.Nodes.Count > 1) { foreach (Wz_Node node in digitNode.Nodes) { if (node.Value is Wz_Uol || node.Value is Wz_Png) { damageSkin.BigUnit.Add(digitNode.Text, BitmapOrigin.CreateFromNode(node, findNode)); break; } } } else if (digitNode.Text == "numberSpace") { damageSkin.BigUnitSpacing = digitNode.GetValue(); } } break; case "NoCri0": foreach (Wz_Node digitNode in customSubNode.Nodes) { if (digitNode.Value is Wz_Uol || digitNode.Value is Wz_Png) { damageSkin.MiniCriticalUnit.Add(digitNode.Text, BitmapOrigin.CreateFromNode(digitNode, findNode)); } else if (digitNode.Nodes.Count > 1) { foreach (Wz_Node node in digitNode.Nodes) { if (node.Value is Wz_Uol || node.Value is Wz_Png) { damageSkin.MiniCriticalUnit.Add(digitNode.Text, BitmapOrigin.CreateFromNode(node, findNode)); break; } } } else if (digitNode.Text == "numberSpace") { damageSkin.MiniCriticalUnitSpacing = digitNode.GetValue(); } } break; case "NoCri1": foreach (Wz_Node digitNode in customSubNode.Nodes) { if (digitNode.Value is Wz_Uol || digitNode.Value is Wz_Png) { damageSkin.BigCriticalUnit.Add(digitNode.Text, BitmapOrigin.CreateFromNode(digitNode, findNode)); } else if (digitNode.Nodes.Count > 1) { foreach (Wz_Node node in digitNode.Nodes) { if (node.Value is Wz_Uol || node.Value is Wz_Png) { damageSkin.BigCriticalUnit.Add(digitNode.Text, BitmapOrigin.CreateFromNode(node, findNode)); break; } } } else if (digitNode.Text == "numberSpace") { damageSkin.BigCriticalUnitSpacing = digitNode.GetValue(); } } break; case "customType": damageSkin.CustomType = customSubNode.GetValue(); break; } } break; } } return damageSkin; } } } ================================================ FILE: WzComparerR2.Common/CharaSim/ExclusiveEquip.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using WzComparerR2.WzLib; namespace WzComparerR2.CharaSim { public class ExclusiveEquip { public ExclusiveEquip() { this.Items = new List(); } public string Info { get; set; } public List Items { get; private set; } public string Msg { get; set; } public static ExclusiveEquip CreateFromNode(Wz_Node exclusiveEquipNode) { if (exclusiveEquipNode == null) return null; ExclusiveEquip exclusiveEquip = new ExclusiveEquip(); foreach (Wz_Node subNode in exclusiveEquipNode.Nodes) { switch (subNode.Text) { case "info": exclusiveEquip.Info = Convert.ToString(subNode.Value); break; case "item": foreach (Wz_Node itemNode in subNode.Nodes) { int itemID = Convert.ToInt32(itemNode.Value); exclusiveEquip.Items.Add(itemID); } break; case "msg": exclusiveEquip.Msg = Convert.ToString(subNode.Value); break; } } return exclusiveEquip; } } } ================================================ FILE: WzComparerR2.Common/CharaSim/Familiar.cs ================================================ using System; using System.Text.RegularExpressions; using WzComparerR2.WzLib; namespace WzComparerR2.CharaSim { public class Familiar { public Familiar() { FamiliarAttribute = "N"; FamiliarCategory = 1; } public int FamiliarID { get; set; } public int MobID { get; set; } public int MonsterCardID { get; set; } public int FamiliarCategory { get; set; } public int SkillID { get; set; } public int SkillEffectAfter { get; set; } public int Range { get; set; } public string FamiliarAttribute { get; set; } public BitmapOrigin FamiliarCover { get; set; } public static Familiar CreateFromNode(Wz_Node node, GlobalFindNodeFunction findNode) { if (node == null) return null; int familiarID; Match m = Regex.Match(node.Text, @"^(\d+)\.img$"); if (!(m.Success && Int32.TryParse(m.Result("$1"), out familiarID))) { return null; } Familiar familiar = new Familiar(); familiar.FamiliarID = familiarID; Wz_Node standNode = node.FindNodeByPath("stand\\0").ResolveUol(); if (standNode != null) { familiar.FamiliarCover = BitmapOrigin.CreateFromNode(standNode, findNode); } Wz_Node infoNode = node.FindNodeByPath("info").ResolveUol(); if (infoNode != null) { foreach (Wz_Node subNode in infoNode.Nodes) { switch (subNode.Text) { case "FAttribute": familiar.FamiliarAttribute = subNode.GetValue(); break; case "FCategory": familiar.FamiliarCategory = subNode.GetValue(); break; case "MobID": familiar.MobID = subNode.GetValue(); break; case "monsterCardID": familiar.MonsterCardID = subNode.GetValue(); break; case "range": familiar.Range = subNode.GetValue(); break; case "skill": foreach (Wz_Node skillNode in subNode.Nodes) { switch (skillNode.Text) { case "id": familiar.SkillID = skillNode.GetValue(); break; case "effectAfter": familiar.SkillEffectAfter = skillNode.GetValue(); break; } } break; case "portrait": familiar.FamiliarCover = BitmapOrigin.CreateFromNode(subNode, findNode); break; } } } return familiar; } } } ================================================ FILE: WzComparerR2.Common/CharaSim/FormulaVersion.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace WzComparerR2.CharaSim { public enum FormulaVersion { Bigbang = 0, Chaos = 1 } } ================================================ FILE: WzComparerR2.Common/CharaSim/Gear.cs ================================================ using System; using System.Collections.Generic; using System.Text; using System.Text.RegularExpressions; using System.Drawing; using System.Linq; using WzComparerR2.WzLib; namespace WzComparerR2.CharaSim { public class Gear : ItemBase { public Gear() { Props = new Dictionary(); VariableStat = new Dictionary(); AbilityTimeLimited = new Dictionary(); ReqSpecJobs = new List(); Options = new Potential[3]; AdditionalOptions = new Potential[3]; Additions = new List(); } public GearGrade Grade { get; set; } public GearGrade AdditionGrade { get; set; } public GearType type; public GearState State { get; set; } public int diff; public Potential[] Options { get; private set; } public Potential[] AdditionalOptions { get; private set; } public AlienStone AlienStoneSlot { get; set; } public int Star { get; set; } public int ScrollUp { get; set; } public int Hammer { get; set; } public bool HasTuc { get; internal set; } public int PlatinumHammer { get; set; } public bool CanPotential { get; internal set; } public string EpicHs { get; internal set; } public bool FixLevel { get; internal set; } public List Levels { get; internal set; } public List Seals { get; internal set; } public List Additions { get; private set; } public bool AdditionHideDesc { get; set; } public Dictionary Props { get; private set; } public Dictionary VariableStat { get; private set; } public Dictionary AbilityTimeLimited { get; private set; } public List ReqSpecJobs { get; private set; } /// /// 获取或设置装备的标准属性。 /// public Dictionary StandardProps { get; private set; } public bool Epic { get { return GetBooleanValue(GearPropType.epicItem); } } public bool TimeLimited { get { return GetBooleanValue(GearPropType.timeLimited); } } public bool Cash { get { return GetBooleanValue(GearPropType.cash); } } public bool GetBooleanValue(GearPropType type) { int value; return this.Props.TryGetValue(type, out value) && value != 0; } public IEnumerable> PropsV5 { get { return this.Props.Where(kv => IsV5SupportPropType(kv.Key)); } } public int GetMaxStar() { if (!this.HasTuc) { return 0; } if (this.Cash) { return 0; } if (this.GetBooleanValue(GearPropType.onlyUpgrade)) { return 0; } if (this.type == GearType.machineEngine || this.type == GearType.machineArms || this.type == GearType.machineLegs || this.type == GearType.machineBody || this.type == GearType.machineTransistors || this.type == GearType.dragonMask || this.type == GearType.dragonPendant || this.type == GearType.dragonWings || this.type == GearType.dragonTail) { return 0; } int reqLevel; this.Props.TryGetValue(GearPropType.reqLevel, out reqLevel); int[] data = null; foreach (int[] item in starData) { if (reqLevel >= item[0]) { data = item; } else { break; } } if (data == null) { return 0; } return data[this.GetBooleanValue(GearPropType.superiorEqp) ? 2 : 1]; } private static readonly int[][] starData = new int[][] { new[]{ 0, 5, 3 }, new[]{ 95, 8, 5 }, new[]{ 110, 10, 8 }, new[]{ 120, 15, 10 }, new[]{ 130, 20, 12 }, new[]{ 140, 25, 15 }, }; public override object Clone() { Gear gear = (Gear)this.MemberwiseClone(); gear.Props = new Dictionary(this.Props.Count); foreach (KeyValuePair p in this.Props) { gear.Props.Add(p.Key, p.Value); } gear.Options = (Potential[])this.Options.Clone(); gear.Additions = new List(this.Additions); return gear; } public void MakeTimeLimitedPropAvailable() { if (AbilityTimeLimited.Count > 0 && !this.GetBooleanValue(GearPropType.abilityTimeLimited)) { int diff = 0; foreach(var kv in AbilityTimeLimited) { this.Props.TryGetValue(kv.Key, out int oldValue); this.Props[kv.Key] = oldValue + kv.Value; diff += kv.Value / Gear.GetPropTypeWeight(kv.Key); } this.Props[GearPropType.abilityTimeLimited] = 1; this.diff += diff; } } public void RestoreStandardProperties() { if (this.StandardProps != null) { this.Props.Clear(); foreach (var kv in this.StandardProps) { this.Props[kv.Key] = kv.Value; } this.diff = 0; } } public bool IsGenesisWeapon { get { // There's no better way to determine if a weapon is a Genesis weapon, the game itself also uses a hard-coded list to check it. if (IsWeapon(this.type) && this.Props.TryGetValue(GearPropType.setItemID, out var setItemID) && 886 <= setItemID && setItemID <= 890) { return true; } return false; } } public static bool IsWeapon(GearType type) { return IsLeftWeapon(type) || IsDoubleHandWeapon(type); } /// /// 获取一个值,指示装备类型是否为主手武器。 /// /// 装备类型。 /// public static bool IsLeftWeapon(GearType type) { return (int)type >= 121 && (int)type <= 139 && type != GearType.katara || ((int)type / 10) == 121 || ((int)type / 10) == 125; } public static bool IsSubWeapon(GearType type) { switch (type) { case GearType.katara: //case GearType.shield: case GearType.demonShield: case GearType.soulShield: return true; default: if ((int)type / 1000 == 135) { return true; } return false; } } /// /// 获取一个值,指示装备类型是否为双手武器。 /// /// 装备类型。 /// public static bool IsDoubleHandWeapon(GearType type) { int _type = (int)type; return (_type >= 140 && _type <= 149) || (_type >= 152 && _type <= 159) || type == GearType.boxingCannon || type == GearType.chakram; } public static bool IsMechanicGear(GearType type) { return (int)type >= 161 && (int)type <= 165; } public static bool IsDragonGear(GearType type) { return (int)type >= 194 && (int)type <= 197; } public static int Compare(Gear gear, Gear originGear) { if (gear.ItemID != originGear.ItemID) return 0; int diff = 0; int tempValue; foreach (KeyValuePair prop in gear.Props) { originGear.Props.TryGetValue(prop.Key, out tempValue);//在原装备中寻找属性 若没有找到 视为0 diff += (prop.Value - tempValue) / GetPropTypeWeight(prop.Key); } foreach (KeyValuePair prop in originGear.Props) { if (!gear.Props.TryGetValue(prop.Key, out tempValue))//寻找装备原属性里新装备没有的 { diff -= prop.Value / GetPropTypeWeight(prop.Key); } } return diff; } private static int GetPropTypeWeight(GearPropType type) { if ((int)type < 100) { switch (type) { case GearPropType.incMHP: case GearPropType.incMMP: case GearPropType.incACC: case GearPropType.incEVA: return 10; } return 1; } return int.MaxValue; } public static bool IsEpicPropType(GearPropType type) { switch (type) { case GearPropType.incPAD: case GearPropType.incMAD: case GearPropType.incSTR: case GearPropType.incDEX: case GearPropType.incINT: case GearPropType.incLUK: return true; default: return false; } } public static bool IsV5SupportPropType(GearPropType type) { switch (type) { case GearPropType.incMDD: case GearPropType.incMDDr: case GearPropType.incACC: case GearPropType.incACCr: case GearPropType.incEVA: case GearPropType.incEVAr: return false; default: return true; } } /// /// 获取装备类型。 /// /// /// public static GearType GetGearType(int code) { switch (code / 1000) { case 1098: return GearType.soulShield; case 1099: return GearType.demonShield; case 1212: return GearType.shiningRod; case 1213: return GearType.tuner; case 1214: return GearType.breathShooter; case 1252: return GearType.memorialStaff; case 1253: return GearType.celestialLight; case 1254: return GearType.onmyoSen; case 1259: return GearType.magicStick; case 1403: return GearType.boxingCannon; case 1404: return GearType.chakram; } if (code / 10000 == 135) { switch (code / 100) { case 13522: case 13528: case 13529: case 13540: return (GearType)(code / 10); default: return (GearType)(code / 100 * 10); } } if (code / 10000 == 119) { switch(code / 100) { case 11902: return (GearType)(code / 10); } } // MSN support if (code / 10000 == 179) { switch (code / 1000) { case 1790: case 1791: case 1792: case 1793: return (GearType)(code / 1000); default: return (GearType)(code / 100 * 10); } } return (GearType)(code / 10000); } public static int GetGender(int code) { GearType type = GetGearType(code); switch (type) { case GearType.emblem: case GearType.bit: case GearType.jewel: case GearType.astra: case (GearType)3: //发型 return 2; } return code / 1000 % 10; } public static bool SpecialCanPotential(GearType type) { switch (type) { case GearType.soulShield: case GearType.demonShield: case GearType.katara: case GearType.magicArrow: case GearType.card: case GearType.box: case GearType.orb: case GearType.novaMarrow: case GearType.soulBangle: case GearType.mailin: case GearType.emblem: return true; default: return false; } } public static IEnumerable> CombineProperties(IEnumerable> props) { var wrappedProp = props.Select(kv => new KeyValuePair(kv.Key, kv.Value)); var combinedProp = CombineProperties(wrappedProp); return combinedProp.Select(kv => new KeyValuePair(kv.Key, Convert.ToInt32(kv.Value))); } public static IEnumerable> CombineProperties(IEnumerable> props) { var combinedProps = new SortedDictionary(); var propCache = new SortedDictionary(); foreach (var kv in props) { propCache.Add(kv.Key, kv.Value); } object obj; foreach (var prop in propCache) { switch (prop.Key) { case GearPropType.incMHP: case GearPropType.incMMP: if (combinedProps.ContainsKey(GearPropType.incMHP_incMMP)) { break; } else if (propCache.TryGetValue(prop.Key == GearPropType.incMHP ? GearPropType.incMMP : GearPropType.incMHP, out obj) && object.Equals(prop.Value, obj)) { combinedProps.Add(GearPropType.incMHP_incMMP, prop.Value); break; } goto default; case GearPropType.incMHPr: case GearPropType.incMMPr: if (combinedProps.ContainsKey(GearPropType.incMHPr_incMMPr)) { break; } else if (propCache.TryGetValue(prop.Key == GearPropType.incMHPr ? GearPropType.incMMPr : GearPropType.incMHPr, out obj) && object.Equals(prop.Value, obj)) { combinedProps.Add(GearPropType.incMHPr_incMMPr, prop.Value); break; } goto default; case GearPropType.incPAD: case GearPropType.incMAD: if (combinedProps.ContainsKey(GearPropType.incPAD_incMAD)) { break; } else if (propCache.TryGetValue(prop.Key == GearPropType.incPAD ? GearPropType.incMAD : GearPropType.incPAD, out obj) && object.Equals(prop.Value, obj)) { combinedProps.Add(GearPropType.incPAD_incMAD, prop.Value); break; } goto default; case GearPropType.incPDD: case GearPropType.incMDD: if (combinedProps.ContainsKey(GearPropType.incPDD_incMDD)) { break; } else if (propCache.TryGetValue(prop.Key == GearPropType.incPDD ? GearPropType.incMDD : GearPropType.incPDD, out obj) && object.Equals(prop.Value, obj)) { combinedProps.Add(GearPropType.incPDD_incMDD, prop.Value); break; } goto default; case GearPropType.incACC: case GearPropType.incEVA: if (combinedProps.ContainsKey(GearPropType.incACC_incEVA)) { break; } else if (propCache.TryGetValue(prop.Key == GearPropType.incACC ? GearPropType.incEVA : GearPropType.incACC, out obj) && object.Equals(prop.Value, obj)) { combinedProps.Add(GearPropType.incACC_incEVA, prop.Value); break; } goto default; default: combinedProps.Add(prop.Key, prop.Value); break; } } return combinedProps; } public static Gear CreateFromNode(Wz_Node node, GlobalFindNodeFunction findNode) { int gearID; Match m = Regex.Match(node.Text, @"^(\d{8})\.img$"); if (!(m.Success && Int32.TryParse(m.Result("$1"), out gearID))) { return null; } Gear gear = new Gear(); gear.ItemID = gearID; gear.type = Gear.GetGearType(gear.ItemID); Wz_Node infoNode = node.FindNodeByPath("info"); if (infoNode != null) { foreach (Wz_Node subNode in infoNode.Nodes) { switch (subNode.Text) { case "icon": if (subNode.Value is Wz_Uol || subNode.Value is Wz_Png) { gear.Icon = BitmapOrigin.CreateFromNode(subNode, findNode); } break; case "iconRaw": if (subNode.Value is Wz_Uol || subNode.Value is Wz_Png) { gear.IconRaw = BitmapOrigin.CreateFromNode(subNode, findNode); } break; case "sample": if (subNode.Value is Wz_Uol || subNode.Value is Wz_Png) { gear.Sample = BitmapOrigin.CreateFromNode(subNode, findNode); } break; case "addition": //附加属性信息 foreach (Wz_Node addiNode in subNode.Nodes) { if (addiNode.Text == "hideDesc") { gear.AdditionHideDesc = true; } else { Addition addi = Addition.CreateFromNode(addiNode); if (addi != null) gear.Additions.Add(addi); } } gear.Additions.Sort((add1, add2) => (int)add1.Type - (int)add2.Type); break; case "option": //附加潜能信息 Wz_Node itemWz = findNode !=null? findNode("Item\\ItemOption.img"):null; if (itemWz == null) break; int optIdx = 0; foreach (Wz_Node optNode in subNode.Nodes) { int optId = 0, optLevel = 0; foreach (Wz_Node optArgNode in optNode.Nodes) { switch (optArgNode.Text) { case "option": optId = Convert.ToInt32(optArgNode.Value); break; case "level": optLevel = Convert.ToInt32(optArgNode.Value); break; } } Potential opt = Potential.CreateFromNode(itemWz.FindNodeByPath(optId.ToString("d6")), optLevel); if (opt != null) gear.Options[optIdx++] = opt; } break; case "level": //可升级信息 if (subNode.Nodes["fixLevel"].GetValueEx(0) != 0) { gear.FixLevel = true; } Wz_Node levelInfo = subNode.Nodes["info"]; gear.Levels = new List(); if (levelInfo != null) { for (int i = 1; ; i++) { Wz_Node levelInfoNode = levelInfo.Nodes[i.ToString()]; if (levelInfoNode != null) { GearLevelInfo info = GearLevelInfo.CreateFromNode(levelInfoNode); int lv; Int32.TryParse(levelInfoNode.Text, out lv); info.Level = lv; gear.Levels.Add(info); } else { break; } } } Wz_Node levelCase = subNode.Nodes["case"]; if (levelCase != null) { int probTotal = 0; foreach (Wz_Node caseNode in levelCase.Nodes) { int prob = caseNode.Nodes["prob"].GetValueEx(0); probTotal += prob; for (int i = 0; i < gear.Levels.Count; i++) { GearLevelInfo info = gear.Levels[i]; Wz_Node caseLevel = caseNode.Nodes[info.Level.ToString()]; if (caseLevel != null) { //desc Wz_Node caseHS = caseLevel.Nodes["hs"]; if (caseHS != null) { info.HS = caseHS.GetValue(); } //随机技能 Wz_Node caseSkill = caseLevel.Nodes["Skill"]; if (caseSkill != null) { foreach (Wz_Node skillNode in caseSkill.Nodes) { int id = skillNode.Nodes["id"].GetValueEx(-1); int level = skillNode.Nodes["level"].GetValueEx(-1); if (id >= 0 && level >= 0) { info.Skills[id] = level; } } } //装备技能 Wz_Node equipSkill = caseLevel.Nodes["EquipmentSkill"]; if (equipSkill != null) { foreach (Wz_Node skillNode in equipSkill.Nodes) { int id = skillNode.Nodes["id"].GetValueEx(-1); int level = skillNode.Nodes["level"].GetValueEx(-1); if (id >= 0 && level >= 0) { info.EquipmentSkills[id] = level; } } } info.Prob = prob; } } } foreach (var info in gear.Levels) { info.ProbTotal = probTotal; } } gear.Props.Add(GearPropType.level, 1); break; case "sealed": //封印解除信息 Wz_Node sealedInfo = subNode.Nodes["info"]; gear.Seals = new List(); if (sealedInfo != null) { foreach (Wz_Node levelInfoNode in sealedInfo.Nodes) { GearSealedInfo info = GearSealedInfo.CreateFromNode(levelInfoNode, findNode); int lv; Int32.TryParse(levelInfoNode.Text, out lv); info.Level = lv; gear.Seals.Add(info); } } gear.Props.Add(GearPropType.@sealed, 1); break; case "variableStat": //升级奖励属性 foreach (Wz_Node statNode in subNode.Nodes) { GearPropType type; if (Enum.TryParse(statNode.Text, out type)) { try { gear.VariableStat.Add(type, Convert.ToSingle(statNode.Value)); } finally { } } } break; case "abilityTimeLimited": //限时属性 foreach (Wz_Node statNode in subNode.Nodes) { GearPropType type; if (Enum.TryParse(statNode.Text, out type)) { try { gear.AbilityTimeLimited.Add(type, Convert.ToInt32(statNode.Value)); } finally { } } } break; case "onlyUpgrade": int upgradeItemID = subNode.Nodes["0"]?.GetValueEx(0) ?? 0; gear.Props.Add(GearPropType.onlyUpgrade, upgradeItemID); break; case "epic": Wz_Node hsNode = subNode.Nodes["hs"]; if (hsNode != null) { gear.EpicHs = Convert.ToString(hsNode.Value); } break; case "gatherTool": foreach (Wz_Node gatherNode in subNode.Nodes) { GearPropType type; if (Enum.TryParse(subNode.Text + "_" + gatherNode.Text, out type)) { try { gear.Props.Add(type, Convert.ToInt32(gatherNode.Value)); } finally { } } } break; case "reqSpecJobs": foreach (Wz_Node jobNode in subNode.Nodes) { gear.ReqSpecJobs.Add(jobNode.GetValue()); } break; default: { GearPropType type; if (!int.TryParse(subNode.Text, out _) && Enum.TryParse(subNode.Text, out type)) { try { gear.Props.Add(type, Convert.ToInt32(subNode.Value)); } finally { } } } break; } } } int value; //读取默认可升级状态 if (gear.Props.TryGetValue(GearPropType.tuc, out value) && value > 0) { gear.HasTuc = true; gear.CanPotential = true; } else if (Gear.SpecialCanPotential(gear.type)) { gear.CanPotential = true; } //读取默认gearGrade if (gear.Props.TryGetValue(GearPropType.fixedGrade, out value)) { gear.Grade = (GearGrade)(value - 1); } //自动填充Grade if (gear.Options.Any(opt => opt != null) && gear.Grade == GearGrade.C) { gear.Grade = GearGrade.B; } //添加默认装备要求 GearPropType[] types = new GearPropType[]{ GearPropType.reqJob,GearPropType.reqLevel,GearPropType.reqSTR,GearPropType.reqDEX, GearPropType.reqINT,GearPropType.reqLUK}; foreach (GearPropType type in types) { if (!gear.Props.ContainsKey(type)) { gear.Props.Add(type, 0); } } //修复恶魔盾牌特殊属性 if (gear.type == GearType.demonShield) { if (gear.Props.TryGetValue(GearPropType.incMMP, out value)) { gear.Props.Remove(GearPropType.incMMP); gear.Props.Add(GearPropType.incMDF, value); } } //检查道具默认的剪刀次数 var cuttableCountOverride = findNode?.Invoke(@$"Etc\KarmaScissor_WZ2.img\ItemList\{gear.ItemID}")?.GetValueEx(); if (cuttableCountOverride != null && cuttableCountOverride > 0) { gear.Props[GearPropType.CuttableCount] = cuttableCountOverride.Value; } //备份标准属性 gear.StandardProps = new Dictionary(gear.Props); //追加限时属性 gear.MakeTimeLimitedPropAvailable(); return gear; } } } ================================================ FILE: WzComparerR2.Common/CharaSim/GearGrade.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace WzComparerR2.CharaSim { public enum GearGrade { /// /// C级(一般物品) /// C = 0, /// /// B级(高级物品) /// B = 1, /// /// A级(史诗物品) /// A = 2, /// /// S级(传说物品) /// S = 3, /// /// SS级(垃圾物品) /// SS = 4, /// /// (特殊物品) /// Special=5, } } ================================================ FILE: WzComparerR2.Common/CharaSim/GearLevelInfo.cs ================================================ using System; using System.Collections.Generic; using System.Text; using WzComparerR2.WzLib; namespace WzComparerR2.CharaSim { public class GearLevelInfo { public GearLevelInfo() { this.BonusProps = new Dictionary(); this.Skills = new Dictionary(); this.EquipmentSkills = new Dictionary(); } public int Level { get; set; } public Dictionary BonusProps { get; private set; } public int Exp { get; set; } public string HS { get; set; } public int Prob { get; set; } public int ProbTotal { get; set; } public Dictionary Skills { get; private set; } public Dictionary EquipmentSkills { get; private set; } public static GearLevelInfo CreateFromNode(Wz_Node node) { GearLevelInfo info = new GearLevelInfo(); foreach (Wz_Node child in node.Nodes) { if (child.Text == "exp") { info.Exp = child.GetValue(0); } else { string prefix; if (child.Text.EndsWith("Min") || child.Text.EndsWith("Max")) { prefix = child.Text.Substring(0, child.Text.Length - 3); } else { prefix = child.Text; } Range range; try { GearPropType propType = (GearPropType)Enum.Parse(typeof(GearPropType), prefix, true); info.BonusProps.TryGetValue(propType, out range); if (child.Text.EndsWith("Min")) { range.Min = child.GetValue(0); info.BonusProps[propType] = range; } else if (child.Text.EndsWith("Max")) { range.Max = child.GetValue(0); info.BonusProps[propType] = range; } else { range.Min = range.Max = child.GetValue(0); info.BonusProps[propType] = range; } } catch { } } } return info; } public struct Range { public Range(int min, int max) { this.min = min; this.max = max; } private int min; private int max; public int Min { get { return min; } set { min = value; } } public int Max { get { return max; } set { max = value; } } } } } ================================================ FILE: WzComparerR2.Common/CharaSim/GearPropType.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace WzComparerR2.CharaSim { public enum GearPropType { //普通装备属性 incSTR = 1, incSTRr, incDEX, incDEXr, incINT, incINTr, incLUK, incLUKr, incAllStat, incMHP_incMMP, incMHPr_incMMPr, incMHP, incMHPr, incMMP, incMMPr, incMDF, incARC, incAUT, incPAD_incMAD, incPAD, incMAD, incPDD_incMDD, incPDD, incMDD, incACC_incEVA, incACC, incEVA, incSpeed, incJump, incCraft, knockback, incPVPDamage, bdR, incBDR, imdR, incIMDR, damR, nbdR, //潜能属性 incPADr = 100, incMADr, incPDDr, incMDDr, incACCr, incEVAr, incCr, incCDr, incDAMr, RecoveryHP, RecoveryMP, face, prop, time, HP, MP, attackType, ignoreTargetDEF, ignoreDAM, ignoreDAMr, DAMreflect, mpconReduce, mpRestore, incMesoProp, incRewardProp, incAllskill, RecoveryUP, boss, level, incTerR, incAsrR, incEXPr, reduceCooltime, incCriticaldamageMax, incCriticaldamageMin, @sealed, incSTRlv, incDEXlv, incINTlv, incLUKlv, incMaxDamage, incPADlv, incMADlv, incCriticaldamage, Option, OptionToMob, activeSkill, bonusByTime, //特殊装备属性 attackSpeed = 200, tuc, setItemID, durability, reqCraft, cash, royalSpecial, masterSpecial, reduceReq, //技能特有属性 mastery = 300, criticaldamageMin, criticaldamageMax, epad, emad, epdd, emdd, emhp, emmp, smartpad, smartacc, smarteva, //装备特有属性 reqLevel = 1000, reqSTR, reqDEX, reqINT, reqLUK, reqJob, reqPOP, reqSpecJob, reqWeekDay, //要求日子 grade, only = 1100, //notSale, //dropBlock, tradeBlock, accountSharable, accountSharableAfterExchange, onlyEquip, tradeAvailable, equipTradeBlock, sharableOnce, notExtend, epicItem, charismaEXP, senseEXP, insightEXP, willEXP, craftEXP, charmEXP, accountShareTag, noPotential, fixedPotential, timeLimited, specialGrade, fixedGrade, unchangeable, superiorEqp, incPQEXPr, limitBreak, nActivatedSocket, jokerToSetItem, medalTag, ringOptionSkill, ringOptionSkillLv, abilityTimeLimited, blockGoldHammer, exceptUpgrade, colorvar, noMoveToLocker, onlyUpgrade, cantRepair, noPetEquipStatMoveItem, BTSLabel, BLACKPINKLabel, android, noLookChange, tucIgnoreForPotential, Etuc, CuttableCount, //MSN专属属性 blockUpgradeExtraOption, blockUpgradeStarforce, mintable, gatherTool_incSkillLevel = 2000, gatherTool_incSpeed, gatherTool_incNum, gatherTool_reqSkillLevel, } } ================================================ FILE: WzComparerR2.Common/CharaSim/GearSealedInfo.cs ================================================ using System; using System.Collections.Generic; using System.Text; using WzComparerR2.WzLib; namespace WzComparerR2.CharaSim { public class GearSealedInfo { public GearSealedInfo() { this.BonusProps = new Dictionary(); } public int Level { get; set; } public Dictionary BonusProps { get; private set; } public int Exp { get; set; } public bool HasIcon { get; set; } public BitmapOrigin Icon { get; set; } public BitmapOrigin IconRaw { get; set; } public static GearSealedInfo CreateFromNode(Wz_Node node, GlobalFindNodeFunction findNode) { GearSealedInfo info = new GearSealedInfo(); foreach (Wz_Node child in node.Nodes) { switch (child.Text) { case "exp": info.Exp = child.GetValue(0); break; case "icon": info.Icon = BitmapOrigin.CreateFromNode(child, findNode); info.HasIcon = true; break; case "iconRaw": info.IconRaw = BitmapOrigin.CreateFromNode(child, findNode); info.HasIcon = true; break; default: try { GearPropType propType = (GearPropType)Enum.Parse(typeof(GearPropType), child.Text, true); info.BonusProps[propType] = child.GetValue(0); } finally { } break; } } return info; } } } ================================================ FILE: WzComparerR2.Common/CharaSim/GearState.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace WzComparerR2.CharaSim { public enum GearState { itemList = 0, enable = 1, disable = 2 } } ================================================ FILE: WzComparerR2.Common/CharaSim/GearType.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace WzComparerR2.CharaSim { public enum GearType { body = 0, head = 1, face = 2, hair = 3, hair2 = 4, face2 = 5, hair3 = 6, hair4 = 7, /// /// 脸饰 101 /// faceAccessory = 101, /// /// 眼饰 102 /// eyeAccessory = 102, /// /// 耳环 103 /// earrings = 103, /// /// 坠子 112 /// pendant = 112, /// /// 腰带 113 /// belt = 113, /// /// 勋章 114 /// medal = 114, /// /// 肩饰 115 /// shoulderPad = 115, /// /// 头盔 100 /// cap = 100, /// /// 披风 110 /// cape = 110, /// /// 上衣 104 /// coat = 104, /// /// 龙神帽子 194 /// dragonMask = 194, /// /// 龙神吊坠 195 /// dragonPendant = 195, /// /// 龙神翅膀 196 /// dragonWings = 196, /// /// 龙神尾巴 197 /// dragonTail = 197, /// /// 手套 108 /// glove = 108, /// /// 套服 105 /// longcoat = 105, /// /// 机甲引擎 161 /// machineEngine = 161, /// /// 机甲机械臂 162 /// machineArms = 162, /// /// 机甲机械腿 163 /// machineLegs = 163, /// /// 机甲机身材质 164 /// machineBody = 164, /// /// 机甲晶体管 165 /// machineTransistors = 165, /// /// 安卓 166 /// android = 166, /// /// 心脏 167 /// machineHeart = 167, /// /// 口袋物品 116 /// pocket = 116, /// /// 徽章 118 /// badge = 118, /// /// 纹章 119 /// emblem = 119, powerSource = 119020, /// /// 裤/裙 106 /// pants = 106, /// /// 戒指 111 /// ring = 111, /// /// 盾牌 109 /// shield = 109, /// /// 灵魂盾 1098xxx /// soulShield = 1098, /// /// 精气盾 1099xxx /// demonShield = 1099, /// /// 鞋子 107 /// shoes = 107, /// /// 双头杖 1212 /// shiningRod = 1212, /// /// 调谐器 1213 /// tuner = 1213, /// /// 龙息臂箭 1214 /// breathShooter = 1214, /// /// 长剑 1215 /// longSword = 1215, /// /// 灵魂手铳 122 /// soulShooter = 122, /// /// 亡命剑 123 /// desperado = 123, /// /// 能量剑 124 /// energySword = 124, /// /// 记忆长杖 1252 /// memorialStaff = 1252, /// /// 星灵权杖 1253 /// celestialLight = 1253, /// /// 阴阳扇 1254 /// onmyoSen = 1254, /// /// 驯兽魔法棒 1259 /// magicStick = 1259, /// /// ESP限制器 /// espLimiter = 126, /// /// 锁链 127 /// chain2 = 127, /// /// 魔力手套 128 /// magicGauntlet = 128, /// /// 扇子 129 /// handFan = 129, /// /// 单手剑 130 /// ohSword = 130, /// /// 单手斧 131 /// ohAxe = 131, /// /// 单手钝器 132 /// ohBlunt = 132, /// /// 短刀 133 /// dagger = 133, /// /// 刀 134 /// katara = 134, /// /// 魔法箭矢 135_00 /// magicArrow = 135200, /// /// 卡片 135_10 /// card = 135210, /// /// 吊坠 135_20 /// heroMedal = 135220, /// /// 念珠 135_21 /// rosario = 135221, /// /// 铁链 135_22 /// chain = 135222, /// /// 魔导书(火毒) 135_23 /// book1 = 135223, /// /// 魔导书(冰雷) 135_24 /// book2 = 135224, /// /// 魔导书(牧师) 135_25 /// book3 = 135225, /// /// 箭羽 135_26 /// bowMasterFeather = 135226, /// /// 扳指 135_27 /// crossBowThimble = 135227, /// /// 短剑剑鞘 135_28 /// shadowerSheath = 135228, /// /// 护身符 135_29 /// nightLordPoutch = 135229, /// /// 宝盒 135_30 /// box = 135230, /// /// 宝珠 135_40 /// orb = 135240, /// /// 龙之精髓 135_50 /// novaMarrow = 135250, /// /// 灵魂戒指 135_60 /// soulBangle = 135260, /// /// 麦林 135_70 /// mailin = 135270, /// /// 小太刀 135_80 /// katana2 = 135280, /// /// 哨子 135_81 /// whistle = 135281, /// /// 拳爪 135_82 /// boxingClaw = 135282, /// /// 拳天 135_86 /// boxingSky = 135286, /// /// 罗盘 135_87 /// compass = 135287, /// /// 手腕护带 135_90 /// viperWristband = 135290, /// /// 望远镜 135_91 /// captainSight = 135291, /// /// 火药桶 135_92 /// connonGunPowder = 135292, /// /// 砝码 135_93 /// aranPendulum = 135293, /// /// 文件 135_94 /// evanPaper = 135294, /// /// 魔法球 135_95 /// battlemageBall = 135295, /// /// 箭轴 135_96 /// wildHunterArrowHead = 135296, /// /// 珠宝 135_97 /// cygnusGem = 135297, /// /// 火药桶 135_98 /// connonGunPowder2 = 135298, /// /// 控制器 135300 /// controller = 135300, /// /// 狐狸珠 135310 /// foxPearl = 135310, /// /// 棋子 135320 /// chess = 135320, /// /// 武器传送装置 135330 /// transmitter = 135330, /// /// 装弹 135340 /// ExplosivePill = 135340, /// /// 魔力翅膀 135350 /// magicWing = 135350, /// /// 精气珠 135360 /// pathOfAbyss = 135360, /// /// 遗物 135370x /// relic = 135370, /// /// 扇坠 135380x /// fanTassel = 135380, /// /// 手链 135400x /// bracelet = 135400, /// /// 武器腰带 135401x /// weaponBelt = 135401, /// /// 饰品 135402x /// ornament = 135402, /// /// 索魂器 135403x /// hexSeeker = 135403, /// /// 如意宝珠 135404 /// yeouiGem = 135404, /// /// 灵符 135430 /// kannaReifu = 135430, /// /// 手杖 /// cane = 136, /// /// 短杖 137 /// wand = 137, /// /// 长杖 138 /// staff = 138, /// /// 空手 139 /// barehand = 139, /// /// 双手剑 140 /// thSword = 140, /// /// 拳封 140_3xxx /// boxingCannon = 1403, /// /// 环刃 140_4xxx /// chakram = 1404, /// /// 双手斧 141 /// thAxe = 141, /// /// 双手钝器 142 /// thBlunt = 142, /// /// 枪 143 /// spear = 143, /// /// 矛 144 /// polearm = 144, /// /// 弓 145 /// bow = 145, /// /// 弩 146 /// crossbow = 146, /// /// 拳套 147 /// throwingGlove = 147, /// /// 指节 148 /// knuckle = 148, /// /// 短枪 149 /// gun = 149, /// /// 采药工具 150 /// shovel = 150, /// /// 采矿工具 151 /// pickaxe = 151, /// /// 双弓 152 /// dualBow = 152, /// /// 手持火炮 153 /// handCannon = 153, /// /// 太刀 154 /// katana = 154, /// /// 扇 155 /// fan = 155, /// /// 大剑 156 /// swordZB = 156, /// /// 太刀 157 /// swordZL = 157, /// /// 机甲手枪 158 /// GauntletBuster = 158, /// /// 远古弓 159 /// ancientBow = 159, /// /// Astra 172 /// astra = 172, /// /// 拼图 168 /// bit = 168, /// /// 点装武器 170 /// cashWeapon = 170, /// /// 武器 -1 /// weapon = -1, /// /// 武器 -1 /// subWeapon = -2, /// /// 图腾 120 /// totem = 120, /// /// 珠宝 178 /// jewel = 178, /// /// MSN纸娃娃 179 /// face_n = 1790, hair_n = 1791, head_n = 1792, hair2_n = 1793, /// /// 宠物装备 180 /// petEquip = 180, /// /// 骑兽 190 /// taming = 190, /// /// 鞍子 191 /// saddle = 191, /// /// 骑兽 193 /// taming2 = 193, /// /// 椅子用骑兽 198 /// tamingChair = 198, /// /// 骑兽 199 /// taming3 = 199 } } ================================================ FILE: WzComparerR2.Common/CharaSim/HyperSkillType.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace WzComparerR2.CharaSim { public enum HyperSkillType { None = 0, S = 1, P = 2, A = 3 } } ================================================ FILE: WzComparerR2.Common/CharaSim/Item.cs ================================================ using System; using System.Collections.Generic; using System.Text; using WzComparerR2.WzLib; namespace WzComparerR2.CharaSim { public class Item : ItemBase { public Item() { this.Props = new Dictionary(); this.Specs = new Dictionary(); this.Recipes = new List(); } public int Level { get; set; } public int? DamageSkinID { get; set; } public int? FamiliarID { get; set; } public int Grade { get; set; } public Dictionary Props { get; private set; } public Dictionary Specs { get; private set; } public List Recipes { get; private set; } public bool Cash { get { return GetBooleanValue(ItemPropType.cash); } } public bool TimeLimited { get { return GetBooleanValue(ItemPropType.timeLimited); } } public bool GetBooleanValue(ItemPropType type) { return this.Props.TryGetValue(type, out long value) && value != 0; } public static Item CreateFromNode(Wz_Node node, GlobalFindNodeFunction findNode) { Item item = new Item(); int value; if (node == null || !Int32.TryParse(node.Text, out value) && !((value = node.Text.IndexOf(".img")) > -1 && Int32.TryParse(node.Text.Substring(0, value), out value))) { return null; } item.ItemID = value; // in msn the node could be UOL. if (node.Value is Wz_Uol) { if ((node = node.ResolveUol()) == null) { return item; } } Wz_Node infoNode = node.FindNodeByPath("info"); if (infoNode != null) { foreach (Wz_Node subNode in infoNode.Nodes) { switch (subNode.Text) { case "icon": if (subNode.Value is Wz_Uol || subNode.Value is Wz_Png) { item.Icon = BitmapOrigin.CreateFromNode(subNode, findNode); } break; case "iconRaw": if (subNode.Value is Wz_Uol || subNode.Value is Wz_Png) { item.IconRaw = BitmapOrigin.CreateFromNode(subNode, findNode); } break; case "sample": if (subNode.Value is Wz_Uol || subNode.Value is Wz_Png) { item.Sample = BitmapOrigin.CreateFromNode(subNode, findNode); } break; case "lv": item.Level = Convert.ToInt32(subNode.Value); break; case "damageSkinID": item.DamageSkinID = Convert.ToInt32(subNode.Value); break; case "familiarID": item.FamiliarID = Convert.ToInt32(subNode.Value); break; case "grade": if (int.TryParse(Convert.ToString(subNode.Value), out _)) { item.Grade = Convert.ToInt32(subNode.Value); } else { switch (Convert.ToString(subNode.Value)) { default: case "normal": item.Grade = 0; break; case "rare": item.Grade = 1; break; case "epic": item.Grade = 2; break; case "unique": item.Grade = 3; break; case "legendary": item.Grade = 4; break; } } break; default: if (!int.TryParse(subNode.Text, out _) && Enum.TryParse(subNode.Text, out ItemPropType type)) { try { item.Props.Add(type, Convert.ToInt64(subNode.Value)); } finally { } } break; } } } Wz_Node specNode = node.FindNodeByPath("spec"); if (specNode != null) { foreach (Wz_Node subNode in specNode.Nodes) { if (subNode.Text == "recipe") { if (subNode.Value == null && subNode.Nodes.Count > 0) { foreach (var recipeNode in subNode.Nodes) { item.Recipes.Add(recipeNode.GetValue()); } } else { item.Recipes.Add(subNode.GetValue()); } } else if(Enum.TryParse(subNode.Text, out ItemSpecType type)) { try { item.Specs.Add(type, Convert.ToInt64(subNode.Value)); } finally { } } } } return item; } } } ================================================ FILE: WzComparerR2.Common/CharaSim/ItemBase.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace WzComparerR2.CharaSim { public abstract class ItemBase : ICloneable { public ItemBase() { } public int ItemID { get; set; } public BitmapOrigin Icon { get; set; } public BitmapOrigin IconRaw { get; set; } public BitmapOrigin Sample { get; set; } public virtual ItemBaseType Type { get { return (ItemBaseType)(this.ItemID / 1000000); } } public virtual object Clone() { return this.MemberwiseClone(); } } } ================================================ FILE: WzComparerR2.Common/CharaSim/ItemBaseType.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace WzComparerR2.CharaSim { public enum ItemBaseType { Unknown = 0, Equip = 1, Consume = 2, Install = 3, Etc = 4, Cash = 5 } } ================================================ FILE: WzComparerR2.Common/CharaSim/ItemPropType.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace WzComparerR2.CharaSim { public enum ItemPropType { price = 1, lv, reqLevel, cash, quest, pquest, tradeBlock, notSale, only, tradeAvailable, accountSharable, accountSharableAfterExchange, timeLimited, setItemID, nickTag, wonderGrade, life, permanent, //MSN专属属性 mintable, } } ================================================ FILE: WzComparerR2.Common/CharaSim/ItemSpecType.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace WzComparerR2.CharaSim { public enum ItemSpecType { recipe = 1, reqSkill, reqSkillLevel, reqSkillProficiency, recipeValidDay, recipeUseCount, } } ================================================ FILE: WzComparerR2.Common/CharaSim/ItemStringHelper.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace WzComparerR2.CharaSim { public static class ItemStringHelper { /// /// 获取怪物category属性对应的类型说明。 /// /// 怪物的category属性的值。 /// public static string GetMobCategoryName(int category) { switch (category) { case 0: return "无形态"; case 1: return "动物型"; case 2: return "植物型"; case 3: return "鱼类型"; case 4: return "爬虫类型"; case 5: return "精灵型"; case 6: return "恶魔型"; case 7: return "不死型"; case 8: return "无机物型"; default: return null; } } public static string GetGearPropString(GearPropType propType, long value) { return GetGearPropString(propType, value, 0); } /// /// 获取GearPropType所对应的文字说明。 /// /// 表示装备属性枚举GearPropType。 /// 表示propType属性所对应的值。 /// public static string GetGearPropString(GearPropType propType, long value, int signFlag) { string sign; switch (signFlag) { default: case 0: //默认处理符号 sign = value > 0 ? "+" : null; break; case 1: //固定加号 sign = "+"; break; case 2: //无特别符号 sign = ""; break; } switch (propType) { case GearPropType.incSTR: return "力量 : " + sign + value; case GearPropType.incSTRr: return "力量 : " + sign + value + "%"; case GearPropType.incDEX: return "敏捷 : " + sign + value; case GearPropType.incDEXr: return "敏捷 : " + sign + value + "%"; case GearPropType.incINT: return "智力 : " + sign + value; case GearPropType.incINTr: return "智力 : " + sign + value + "%"; case GearPropType.incLUK: return "运气 : " + sign + value; case GearPropType.incLUKr: return "运气 : " + sign + value + "%"; case GearPropType.incAllStat: return "所有属性 : " + sign + value; case GearPropType.incMHP: return "最大血量: " + sign + value; case GearPropType.incMHPr: return "最大血量: " + sign + value + "%"; case GearPropType.incMMP: return "最大魔量: " + sign + value; case GearPropType.incMMPr: return "最大魔量: " + sign + value + "%"; case GearPropType.incMDF: return "MaxDF : " + sign + value; case GearPropType.incPAD: return "攻击力 : " + sign + value; case GearPropType.incPADr: return "攻击力 : " + sign + value + "%"; case GearPropType.incMAD: return "魔法力 : " + sign + value; case GearPropType.incMADr: return "魔法力 : " + sign + value + "%"; case GearPropType.incPDD: return "防御力 : " + sign + value; case GearPropType.incPDDr: return "物理防御力 : " + sign + value + "%"; case GearPropType.incMDD: return "魔法防御力 : " + sign + value; case GearPropType.incMDDr: return "魔法防御力 : " + sign + value + "%"; case GearPropType.incACC: return "命中值 : " + sign + value; case GearPropType.incACCr: return "命中值 : " + sign + value + "%"; case GearPropType.incEVA: return "回避值 : " + sign + value; case GearPropType.incEVAr: return "回避值 : " + sign + value + "%"; case GearPropType.incSpeed: return "移动速度 : " + sign + value; case GearPropType.incJump: return "跳跃力 : " + sign + value; case GearPropType.incCraft: return "手技 : " + sign + value; case GearPropType.damR: case GearPropType.incDAMr: return "总伤害 : " + sign + value + "%"; case GearPropType.incCr: return "爆击率 : " + sign + value + "%"; case GearPropType.incCDr: return "爆击伤害 : " + sign + value + "%"; case GearPropType.knockback: return "直接攻击时" + value + "的比率发生后退现象。"; case GearPropType.incPVPDamage: return "大乱斗时追加攻击力" + sign + value; case GearPropType.incPQEXPr: return "组队任务经验值增加" + value + "%"; case GearPropType.incEXPr: return "经验值增加" + value + "%"; case GearPropType.incBDR: case GearPropType.bdR: return "攻击首领怪时,伤害+" + value + "%"; case GearPropType.incIMDR: case GearPropType.imdR: return "无视怪物防御率:+" + value + "%"; case GearPropType.limitBreak:return "伤害上限突破至" + ToChineseNumberExpr(value) + "。"; case GearPropType.reduceReq: return "装备等级降低:- " + value; case GearPropType.nbdR: return "攻击普通怪物时,伤害+" + value + "%"; case GearPropType.only: return value == 0 ? null : "固有道具"; case GearPropType.tradeBlock: return value == 0 ? null : "不可交换"; case GearPropType.equipTradeBlock: return value == 0 ? null : "装备后无法交换"; case GearPropType.accountSharable: return value == 0 ? null : "服务器内只有我的角色之间可以移动"; case GearPropType.onlyEquip: return value == 0 ? null : "固有装备物品"; case GearPropType.notExtend: return value == 0 ? null : "无法延长有效时间。"; case GearPropType.accountSharableAfterExchange: return value == 0 ? null : "可交换1次\n(交易后只能在世界内我的角色之间移动)"; case GearPropType.mintable: return value == 0 ? null : "可铸造"; case GearPropType.tradeAvailable: switch (value) { case 1: return " #c使用宿命剪刀,可以使物品交易1次。#"; case 2: return " #c使用白金宿命剪刀,可以使物品交易1次。#"; default: return null; } case GearPropType.accountShareTag: switch (value) { case 1: return " #c使用物品共享牌,可以在同一账号内的角色间移动1次。#"; default: return null; } case GearPropType.noPotential: return value == 0 ? null : "无法设置潜能。"; case GearPropType.fixedPotential: return value == 0 ? null : "无法重设潜能"; case GearPropType.superiorEqp: return value == 0 ? null : "道具强化成功时,可以获得更高的效果。"; case GearPropType.nActivatedSocket: return value == 0 ? null : "#c可以镶嵌星岩#"; case GearPropType.jokerToSetItem: return value == 0 ? null : " #c当前装备3个以上的所有套装道具中包含的幸运物品!#"; case GearPropType.abilityTimeLimited: return value == 0 ? null : "限期能力值"; case GearPropType.blockGoldHammer: return value == 0 ? null : "无法使用黄金锤"; case GearPropType.colorvar: return value == 0 ? null : "#c该装备可通过染色颜料来变更颜色.#"; case GearPropType.incMHP_incMMP: return "最大血量/最大魔量:" + sign + value; case GearPropType.incMHPr_incMMPr: return "最大血量/最大魔量:" + sign + value + "%"; case GearPropType.incPAD_incMAD: return "攻击力/魔力:" + sign + value; case GearPropType.incPDD_incMDD: return "物理/魔法防御力:" + sign + value; case GearPropType.incACC_incEVA: return "命中值/回避值:" + sign + value; case GearPropType.incARC: return "神秘之力 : " + sign + value; case GearPropType.incAUT: return "原初之力 : " + sign + value; case GearPropType.Etuc: return "可进行卓越强化。(最多:" + value + "次)"; case GearPropType.CuttableCount: return "可使用剪刀:" + value + "次"; default: return null; } } public static string GetGearPropDiffString(GearPropType propType, int value, int standardValue) { var propStr = GetGearPropString(propType, value); if (value > standardValue) { string suffix = null; switch (propType) { case GearPropType.incSTR: case GearPropType.incDEX: case GearPropType.incINT: case GearPropType.incLUK: case GearPropType.incMHP: case GearPropType.incMMP: case GearPropType.incMDF: case GearPropType.incARC: case GearPropType.incAUT: case GearPropType.incPAD: case GearPropType.incMAD: case GearPropType.incPDD: case GearPropType.incMDD: case GearPropType.incSpeed: case GearPropType.incJump: suffix = $"({standardValue} #$e+{value - standardValue}#)"; break; case GearPropType.bdR: case GearPropType.incBDR: case GearPropType.imdR: case GearPropType.incIMDR: case GearPropType.damR: case GearPropType.incDAMr: suffix = $"({standardValue}% #$y+{value - standardValue}%#)"; break; } propStr = "#$y" + propStr + "# " + suffix; } return propStr; } /// /// 获取gearGrade所对应的字符串。 /// /// 表示装备的潜能等级GearGrade。 /// public static string GetGearGradeString(GearGrade rank) { switch (rank) { case GearGrade.C: return "C级(一般物品)"; case GearGrade.B: return "B级(高级物品)"; case GearGrade.A: return "A级(史诗物品)"; case GearGrade.S: return "S级(传说物品)"; case GearGrade.SS: return "SS级(传说极品)"; case GearGrade.Special: return "(特殊物品)"; default: return null; } } /// /// 获取gearType所对应的字符串。 /// /// 表示装备类型GearType。 /// public static string GetGearTypeString(GearType type) { switch (type) { case GearType.body: return "纸娃娃(身体)"; case GearType.head: case GearType.head_n: return "纸娃娃(头部)"; case GearType.face: case GearType.face2: case GearType.face_n: return "纸娃娃(脸型)"; case GearType.hair: case GearType.hair2: case GearType.hair3: case GearType.hair4: case GearType.hair_n: case GearType.hair2_n: return "纸娃娃(发型)"; case GearType.faceAccessory: return "脸饰"; case GearType.eyeAccessory: return "眼饰"; case GearType.earrings: return "耳环"; case GearType.pendant: return "坠子"; case GearType.belt: return "腰带"; case GearType.medal: return "勋章"; case GearType.shoulderPad: return "肩饰"; case GearType.cap: return "帽子"; case GearType.cape: return "披风"; case GearType.coat: return "上衣"; case GearType.dragonMask: return "龙神帽子"; case GearType.dragonPendant: return "龙神吊坠"; case GearType.dragonWings: return "龙神翅膀"; case GearType.dragonTail: return "龙神尾巴"; case GearType.glove: return "手套"; case GearType.longcoat: return "套服"; case GearType.machineEngine: return "机甲引擎"; case GearType.machineArms: return "机甲机械臂"; case GearType.machineLegs: return "机甲机械腿"; case GearType.machineBody: return "机甲机身材质"; case GearType.machineTransistors: return "机甲晶体管"; case GearType.pants: return "裤/裙"; case GearType.ring: return "戒指"; case GearType.shield: return "盾牌"; case GearType.shoes: return "鞋子"; case GearType.shiningRod: return "双头杖"; case GearType.soulShooter: return "灵魂手铳"; case GearType.ohSword: return "单手剑"; case GearType.ohAxe: return "单手斧"; case GearType.ohBlunt: return "单手钝器"; case GearType.dagger: return "短刀"; case GearType.katara: return "刀"; case GearType.magicArrow: return "魔法箭矢"; case GearType.card: return "卡片"; case GearType.box: return "宝盒"; case GearType.orb: return "宝珠"; case GearType.novaMarrow: return "龙之精髓"; case GearType.soulBangle: return "灵魂手镯"; case GearType.mailin: return "麦林"; case GearType.cane: return "手杖"; case GearType.wand: return "短杖"; case GearType.staff: return "长杖"; case GearType.thSword: return "双手剑"; case GearType.thAxe: return "双手斧"; case GearType.thBlunt: return "双手钝器"; case GearType.spear: return "枪"; case GearType.polearm: return "矛"; case GearType.bow: return "弓"; case GearType.crossbow: return "弩"; case GearType.throwingGlove: return "拳套"; case GearType.knuckle: return "指节"; case GearType.gun: return "短枪"; case GearType.android: return "智能机器人"; case GearType.machineHeart: return "机械心脏"; case GearType.pickaxe: return "采矿工具"; case GearType.shovel: return "采药工具"; case GearType.pocket: return "口袋物品"; case GearType.dualBow: return "双弩枪"; case GearType.handCannon: return "手持火炮"; case GearType.badge: return "徽章"; case GearType.emblem: return "纹章"; case GearType.soulShield: return "灵魂盾"; case GearType.demonShield: return "精气盾"; case GearType.totem: return "图腾"; case GearType.petEquip: return "宠物装备"; case GearType.taming: case GearType.taming2: case GearType.taming3: case GearType.tamingChair: return "骑兽"; case GearType.saddle: return "鞍子"; case GearType.katana: return "武士刀"; case GearType.fan: return "折扇"; case GearType.swordZB: return "大剑"; case GearType.swordZL: return "太刀"; case GearType.weapon: return "武器"; case GearType.subWeapon: return "辅助武器"; case GearType.heroMedal: return "吊坠"; case GearType.rosario: return "念珠"; case GearType.chain: return "铁链"; case GearType.book1: case GearType.book2: case GearType.book3: return "魔导书"; case GearType.bowMasterFeather: return "箭羽"; case GearType.crossBowThimble: return "扳指"; case GearType.shadowerSheath: return "短剑剑鞘"; case GearType.nightLordPoutch: return "护身符"; case GearType.viperWristband: return "手腕护带"; case GearType.captainSight: return "瞄准器"; case GearType.connonGunPowder: case GearType.connonGunPowder2: return "火药桶"; case GearType.aranPendulum: return "砝码"; case GearType.evanPaper: return "文件"; case GearType.battlemageBall: return "魔法球"; case GearType.wildHunterArrowHead: return "箭轴"; case GearType.cygnusGem: return "宝石"; case GearType.controller: return "控制器"; case GearType.foxPearl: return "狐狸珠"; case GearType.chess: return "棋子"; case GearType.powerSource: return "能源"; case GearType.energySword: return "能量剑"; case GearType.desperado: return "亡命剑"; case GearType.memorialStaff: return "记忆长杖"; case GearType.magicStick: return "驯兽魔法棒"; case GearType.whistle: return "飞越"; case GearType.boxingClaw: return "拳爪"; case GearType.katana2: return "小太刀"; case GearType.espLimiter: return "ESP限制器"; case GearType.GauntletBuster: return "机甲手枪"; case GearType.ExplosivePill: return "装弹"; case GearType.chain2: return "锁链"; case GearType.magicGauntlet: return "魔力手套"; case GearType.transmitter: return "武器传送装置"; case GearType.magicWing: return "魔法之翼"; case GearType.pathOfAbyss: return "深渊精气珠"; case GearType.relic: return "遗物"; case GearType.ancientBow: return "远古弓"; case GearType.handFan: return "扇子"; case GearType.fanTassel: return "扇坠"; case GearType.tuner: return "调谐器"; case GearType.bracelet: return "手链"; case GearType.boxingCannon: return "拳封"; case GearType.boxingSky: return "拳天"; case GearType.breathShooter: return "龙息臂箭"; case GearType.weaponBelt: return "武器腰带"; case GearType.ornament: return "饰品"; case GearType.chakram: return "环刃"; case GearType.hexSeeker: return "索魂器"; case GearType.jewel: return "珠宝"; case GearType.celestialLight: return "星光权杖"; case GearType.compass: return "罗盘"; case GearType.longSword: return "长剑"; case GearType.yeouiGem: return "如意宝珠"; case GearType.onmyoSen: return "阴阳扇"; case GearType.kannaReifu: return "灵符"; default: return null; } } /// /// 获取武器攻击速度所对应的字符串。 /// /// 表示武器的攻击速度,通常为2~9的数字。 /// public static string GetAttackSpeedString(int attackSpeed) { switch (attackSpeed) { case 2: case 3: return "极快"; case 4: case 5: return "快"; case 6: return "普通"; case 7: case 8: return "缓慢"; case 9: return "较慢"; default: return attackSpeed.ToString(); } } /// /// 获取套装装备类型的字符串。 /// /// 表示套装装备类型的GearType。 /// public static string GetSetItemGearTypeString(GearType type) { return GetGearTypeString(type); } /// /// 获取装备额外职业要求说明的字符串。 /// /// 表示装备类型的GearType。 /// public static string GetExtraJobReqString(GearType type) { switch (type) { //0xxx case GearType.heroMedal: return "英雄职业群可穿戴装备"; case GearType.rosario: return "圣骑士职业群可穿戴装备"; case GearType.chain: return "黑骑士职业群可穿戴装备"; case GearType.book1: return "火毒系列魔法师可穿戴装备"; case GearType.book2: return "冰雷系列魔法师可穿戴装备"; case GearType.book3: return "主教系列魔法师可穿戴装备"; case GearType.bowMasterFeather: return "神射手职业群可穿戴装备"; case GearType.crossBowThimble: return "箭神职业群可穿戴装备"; case GearType.shadowerSheath: return "侠盗职业群可穿戴装备"; case GearType.nightLordPoutch: return "隐士职业群可穿戴装备"; case GearType.katara: return "暗影双刀可穿戴装备"; case GearType.viperWristband: return "冲锋队长职业群可穿戴装备"; case GearType.captainSight: return "船长职业群可穿戴装备"; case GearType.connonGunPowder: case GearType.connonGunPowder2: return "火炮手职业群可穿戴装备"; case GearType.box: case GearType.boxingClaw: return "龙的传人可穿戴装备"; case GearType.relic: return "古迹猎人职业群可穿戴装备"; //1xxx case GearType.cygnusGem: return "冒险骑士团可穿戴装备"; //2xxx case GearType.aranPendulum: return GetExtraJobReqString(21); case GearType.evanPaper: return GetExtraJobReqString(22); case GearType.magicArrow: return GetExtraJobReqString(23); case GearType.card: return GetExtraJobReqString(24); case GearType.foxPearl: return GetExtraJobReqString(25); case GearType.orb: case GearType.shiningRod: return GetExtraJobReqString(27); //3xxx case GearType.demonShield: return GetExtraJobReqString(31); case GearType.desperado: return "恶魔复仇者可穿戴装备"; case GearType.battlemageBall: return "唤灵斗师职业群可穿戴装备"; case GearType.wildHunterArrowHead: return "豹弩游侠职业群可穿戴装备"; case GearType.mailin: return "机械师可穿戴装备"; case GearType.controller: case GearType.energySword: return GetExtraJobReqString(36); case GearType.GauntletBuster: case GearType.ExplosivePill: return GetExtraJobReqString(37); //4xxx case GearType.katana: case GearType.katana2: return "剑豪可穿戴装备"; case GearType.onmyoSen: case GearType.kannaReifu: case GearType.fan: return "阴阳师可穿戴装备"; //5xxx case GearType.soulShield: return "米哈尔可穿戴装备"; //6xxx case GearType.novaMarrow: return GetExtraJobReqString(61); case GearType.weaponBelt: case GearType.breathShooter: return GetExtraJobReqString(63); case GearType.chain2: case GearType.transmitter: return GetExtraJobReqString(64); case GearType.soulBangle: case GearType.soulShooter: return GetExtraJobReqString(65); //10xxx case GearType.swordZB: case GearType.swordZL: return GetExtraJobReqString(101); case GearType.whistle: case GearType.memorialStaff: return GetExtraJobReqString(172); case GearType.magicStick: return GetExtraJobReqString(112); case GearType.espLimiter: case GearType.chess: return GetExtraJobReqString(142); case GearType.magicGauntlet: case GearType.magicWing: return GetExtraJobReqString(152); case GearType.pathOfAbyss: return GetExtraJobReqString(155); case GearType.handFan: case GearType.fanTassel: return GetExtraJobReqString(164); case GearType.tuner: case GearType.bracelet: return GetExtraJobReqString(151); case GearType.boxingCannon: case GearType.boxingSky: return GetExtraJobReqString(175); case GearType.ornament: return GetExtraJobReqString(162); case GearType.chakram: case GearType.hexSeeker: return GetExtraJobReqString(154); case GearType.celestialLight: case GearType.compass: return GetExtraJobReqString(182); case GearType.longSword: case GearType.yeouiGem: return GetExtraJobReqString(161); default: return null; } } /// /// 获取装备额外职业要求说明的字符串。 /// /// 表示装备属性的reqSpecJob的值。 /// public static string GetExtraJobReqString(int specJob) { switch (specJob) { case 21: return "战神可穿戴装备"; case 22: return "龙神职业群可穿戴装备"; case 23: return "双弩精灵可穿戴装备"; case 24: return "幻影可穿戴装备"; case 25: return "隐月可穿戴装备"; case 27: return "夜光法师可穿戴装备"; case 31: return "恶魔猎手可穿戴装备"; case 36: return "尖兵可穿戴装备"; case 37: return "爆破手可使用"; case 41: return "剑豪可穿戴装备"; case 42: return "阴阳师可穿戴装备"; case 51: return "米哈尔可穿戴装备"; case 61: return "狂龙战士可穿戴装备"; case 63: return "炼狱黑客可穿戴装备"; case 64: return "魔链影士可穿戴装备"; case 65: return "爆莉萌天使可穿戴装备"; case 101: return "神之子可穿戴装备"; case 112: return "林之灵可穿戴装备"; case 142: return "超能力者可穿戴装备"; case 151: return "御剑骑士可穿戴装备"; case 152: return "圣晶使徒可穿戴装备"; case 154: return "飞刃沙士可穿戴装备"; case 155: return "影魂异人可穿戴装备"; case 161: return "莲可穿戴装备"; case 162: return "元素师可穿戴装备"; case 164: return "虎影可穿戴装备"; case 172: return "琳可穿戴装备"; case 175: return "墨玄可穿戴装备"; case 182: return "施亚可穿戴装备"; default: return null; } } public static string GetExtraJobReqString(IEnumerable specJobs) { List extraJobNames = new List(); foreach (int specJob in specJobs) { switch (specJob) { case 1: extraJobNames.AddRange(new[] { "英雄", "圣骑士" }); break; case 2: extraJobNames.AddRange(new[] { "冰雷魔导师", "火毒魔导师", "主教" }); break; case 4: extraJobNames.Add("侠盗"); break; case 11: extraJobNames.Add("魂骑士"); break; case 12: extraJobNames.Add("炎术士"); break; case 22: extraJobNames.Add("龙神"); break; case 32: extraJobNames.Add("唤灵斗师"); break; case 172: extraJobNames.Add("森林小主"); break; default: extraJobNames.Add(specJob.ToString()); break; } } if (extraJobNames.Count == 0) { return null; } return string.Join("、", extraJobNames) + "可穿戴装备"; } public static string GetItemPropString(ItemPropType propType, long value) { switch (propType) { case ItemPropType.tradeBlock: return GetGearPropString(GearPropType.tradeBlock, value); case ItemPropType.tradeAvailable: return GetGearPropString(GearPropType.tradeAvailable, value); case ItemPropType.only: return GetGearPropString(GearPropType.only, value); case ItemPropType.accountSharable: return GetGearPropString(GearPropType.accountSharable, value); case ItemPropType.accountSharableAfterExchange: return GetGearPropString(GearPropType.accountSharableAfterExchange, value); case ItemPropType.quest: return value == 0 ? null : "任务道具"; case ItemPropType.pquest: return value == 0 ? null : "组队任务道具"; case ItemPropType.permanent: return value == 0 ? null : "可以一直使用魔法的神奇宠物。"; case ItemPropType.mintable: return GetGearPropString(GearPropType.mintable, value); default: return null; } } public static string GetSkillReqAmount(int skillID, int reqAmount) { switch (skillID / 10000) { case 11200: return "[需要巨熊技能点: " + reqAmount + "]"; case 11210: return "[需要雪豹技能点: " + reqAmount + "]"; case 11211: return "[需要猛禽技能点: " + reqAmount + "]"; case 11212: return "[需要猫咪技能点: " + reqAmount + "]"; default: return "[需要??技能点: " + reqAmount + "]"; } } public static string GetJobName(int jobCode) { switch (jobCode) { case 0: return "新手"; case 100: return "战士"; case 110: return "剑客"; case 111: return "勇士"; case 112: return "英雄"; case 114: return "英雄(6次)"; case 120: return "准骑士"; case 121: return "骑士"; case 122: return "圣骑士"; case 124: return "圣骑士(6次)"; case 130: return "枪战士"; case 131: return "狂战士"; case 132: return "黑骑士"; case 134: return "黑骑士(6次)"; case 200: return "魔法师"; case 210: return "法师(火,毒)"; case 211: return "巫师(火,毒)"; case 212: return "魔导师(火,毒)"; case 214: return "魔导师(火,毒)(6次)"; case 220: return "法师(冰,雷)"; case 221: return "巫师(冰,雷)"; case 222: return "魔导师(冰,雷)"; case 224: return "魔导师(冰,雷)(6次)"; case 230: return "牧师"; case 231: return "祭司"; case 232: return "主教"; case 234: return "主教(6次)"; case 300: return "弓箭手"; case 301: return "弓箭手(古迹猎人)"; case 310: return "猎人"; case 311: return "射手"; case 312: return "神射手"; case 314: return "神射手(6次)"; case 320: return "弩弓手"; case 321: return "游侠"; case 322: return "箭神"; case 324: return "箭神(6次)"; case 330: return "上古射手"; case 331: return "追逐者"; case 332: return "古迹猎人"; case 334: return "古迹猎人(6次)"; case 400: return "飞侠"; case 410: return "刺客"; case 411: return "无影人"; case 412: return "隐士"; case 414: return "隐士(6次)"; case 420: return "侠客"; case 421: return "独行客"; case 422: return "侠盗"; case 424: return "侠盗(6次)"; case 430: return "见习刀客"; case 431: return "双刀客"; case 432: return "双刀侠"; case 433: return "血刀"; case 434: return "暗影双刀"; case 436: return "暗影双刀(6次)"; case 500: return "海盗"; case 501: return "海盗(炮手)"; case 510: return "拳手"; case 511: return "斗士"; case 512: return "冲锋队长"; case 514: return "冲锋队长(6次)"; case 520: return "火枪手"; case 521: return "大副"; case 522: return "船长"; case 524: return "船长(6次)"; case 530: return "火炮手"; case 531: return "毁灭炮手"; case 532: return "神炮王"; case 534: return "神炮王(6次)"; case 800: return "管理员"; case 900: return "管理员"; case 1000: return "初心者"; case 1100: return "魂骑士(1次)"; case 1110: return "魂骑士(2次)"; case 1111: return "魂骑士(3次)"; case 1112: return "魂骑士(4次)"; case 1114: return "魂骑士(6次)"; case 1200: return "炎术士(1次)"; case 1210: return "炎术士(2次)"; case 1211: return "炎术士(3次)"; case 1212: return "炎术士(4次)"; case 1214: return "炎术士(6次)"; case 1300: return "风灵使者(1次)"; case 1310: return "风灵使者(2次)"; case 1311: return "风灵使者(3次)"; case 1312: return "风灵使者(4次)"; case 1314: return "风灵使者(6次)"; case 1400: return "夜行者(1次)"; case 1410: return "夜行者(2次)"; case 1411: return "夜行者(3次)"; case 1412: return "夜行者(4次)"; case 1414: return "夜行者(6次)"; case 1500: return "奇袭者(1次)"; case 1510: return "奇袭者(2次)"; case 1511: return "奇袭者(3次)"; case 1512: return "奇袭者(4次)"; case 1514: return "奇袭者(6次)"; case 2000: return "战童"; case 2001: return "小不点"; case 2002: return "双弩精灵"; case 2003: return "幻影"; case 2004: return "夜光法师"; case 2005: return "隐月"; case 2100: return "战神(1次)"; case 2110: return "战神(2次)"; case 2111: return "战神(3次)"; case 2112: return "战神(4次)"; case 2114: return "战神(6次)"; case 2200: case 2210: return "龙神(1次)"; case 2211: case 2212: case 2213: return "龙神(2次)"; case 2214: case 2215: case 2216: return "龙神(3次)"; case 2217: case 2218: return "龙神(4次)"; case 2220: return "龙神(6次)"; case 2300: return "双弩精灵(1次)"; case 2310: return "双弩精灵(2次)"; case 2311: return "双弩精灵(3次)"; case 2312: return "双弩精灵(4次)"; case 2314: return "双弩精灵(6次)"; case 2400: return "幻影(1次)"; case 2410: return "幻影(2次)"; case 2411: return "幻影(3次)"; case 2412: return "幻影(4次)"; case 2414: return "幻影(6次)"; case 2500: return "隐月(1次)"; case 2510: return "隐月(2次)"; case 2511: return "隐月(3次)"; case 2512: return "隐月(4次)"; case 2514: return "隐月(6次)"; case 2700: return "夜光法师(1次)"; case 2710: return "夜光法师(2次)"; case 2711: return "夜光法师(3次)"; case 2712: return "夜光法师(4次)"; case 2714: return "夜光法师(6次)"; case 3000: return "市民"; case 3001: return "恶魔"; case 3100: return "恶魔猎手(1次)"; case 3110: return "恶魔猎手(2次)"; case 3111: return "恶魔猎手(3次)"; case 3112: return "恶魔猎手(4次)"; case 3114: return "恶魔猎手(6次)"; case 3101: return "恶魔复仇者(1次)"; case 3120: return "恶魔复仇者(2次)"; case 3121: return "恶魔复仇者(3次)"; case 3122: return "恶魔复仇者(4次)"; case 3124: return "恶魔复仇者(6次)"; case 3200: return "唤灵斗师(1次)"; case 3210: return "唤灵斗师(2次)"; case 3211: return "唤灵斗师(3次)"; case 3212: return "唤灵斗师(4次)"; case 3214: return "唤灵斗师(6次)"; case 3300: return "豹弩游侠(1次)"; case 3310: return "豹弩游侠(2次)"; case 3311: return "豹弩游侠(3次)"; case 3312: return "豹弩游侠(4次)"; case 3314: return "豹弩游侠(6次)"; case 3500: return "机械师(1次)"; case 3510: return "机械师(2次)"; case 3511: return "机械师(3次)"; case 3512: return "机械师(4次)"; case 3514: return "机械师(6次)"; case 3002: return "尖兵"; case 3600: return "尖兵(1次)"; case 3610: return "尖兵(2次)"; case 3611: return "尖兵(3次)"; case 3612: return "尖兵(4次)"; case 3614: return "尖兵(6次)"; case 3700: return "爆破手"; case 3710: return "爆破手(2次)"; case 3711: return "爆破手(3次)"; case 3712: return "爆破手(4次)"; case 3714: return "爆破手(6次)"; case 4001: return "阴阳师"; case 4002: return "阴阳师"; case 4100: return "剑豪(1次)"; case 4110: return "剑豪(2次)"; case 4111: return "剑豪(3次)"; case 4112: return "剑豪(4次)"; case 4114: return "剑豪(6次)"; case 4200: return "阴阳师(1次)"; case 4210: return "阴阳师(2次)"; case 4211: return "阴阳师(3次)"; case 4212: return "阴阳师(4次)"; case 4214: return "阴阳师(6次)"; case 5000: return "米哈尔"; case 5100: return "米哈尔(1次)"; case 5110: return "米哈尔(2次)"; case 5111: return "米哈尔(3次)"; case 5112: return "米哈尔(4次)"; case 5114: return "米哈尔(6次)"; case 6000: return "狂龙战士"; case 6001: return "爆莉萌天使"; case 6002: return "魔链影士"; case 6003: return "炼狱黑客"; case 6100: return "狂龙战士(1次)"; case 6110: return "狂龙战士(2次)"; case 6111: return "狂龙战士(3次)"; case 6112: return "狂龙战士(4次)"; case 6114: return "狂龙战士(6次)"; case 6300: return "炼狱黑客(1次)"; case 6310: return "炼狱黑客(2次)"; case 6311: return "炼狱黑客(3次)"; case 6312: return "炼狱黑客(4次)"; case 6314: return "炼狱黑客(6次)"; case 6400: return "魔链影士(1次)"; case 6410: return "魔链影士(2次)"; case 6411: return "魔链影士(3次)"; case 6412: return "魔链影士(4次)"; case 6414: return "魔链影士(6次)"; case 6500: return "爆莉萌天使(1次)"; case 6510: return "爆莉萌天使(2次)"; case 6511: return "爆莉萌天使(3次)"; case 6512: return "爆莉萌天使(4次)"; case 6514: return "爆莉萌天使(6次)"; case 7000: return "内在能力"; case 7100: return "联盟"; case 7200: return "怪物农庄"; case 9100: return "公会"; case 9200: case 9201: case 9202: case 9203: case 9204: return "专业技术"; case 10000: return "神之子"; case 10100: return "神之子"; case 10110: return "神之子"; case 10111: return "神之子"; case 10112: return "神之子"; case 10114: return "神之子(6次)"; case 11000: return "林之灵"; case 11200: return "林之灵(1次)"; case 11210: return "林之灵(2次)"; case 11211: return "林之灵(3次)"; case 11212: return "林之灵(4次)"; case 11214: return "林之灵(6次)"; case 12000: case 12005: case 12100: return "灶门炭治郎"; case 12006: case 12200: return "埼玉"; case 13000: case 13100: return "品克缤"; case 13001: case 13500: return "白雪人"; case 14000: return "超能力者"; case 14200: return "超能力者(1次)"; case 14210: return "超能力者(2次)"; case 14211: return "超能力者(3次)"; case 14212: return "超能力者(4次)"; case 14214: return "超能力者(6次)"; case 15000: return "圣晶使徒"; case 15001: return "影魂异人"; case 15002: return "御剑骑士"; case 15003: return "飞刃沙士"; case 15100: return "御剑骑士(1次)"; case 15110: return "御剑骑士(2次)"; case 15111: return "御剑骑士(3次)"; case 15112: return "御剑骑士(4次)"; case 15114: return "御剑骑士(6次)"; case 15200: return "圣晶使徒(1次)"; case 15210: return "圣晶使徒(2次)"; case 15211: return "圣晶使徒(3次)"; case 15212: return "圣晶使徒(4次)"; case 15214: return "圣晶使徒(6次)"; case 15400: return "飞刃沙士(1次)"; case 15410: return "飞刃沙士(2次)"; case 15411: return "飞刃沙士(3次)"; case 15412: return "飞刃沙士(4次)"; case 15414: return "飞刃沙士(6次)"; case 15500: return "影魂异人(1次)"; case 15510: return "影魂异人(2次)"; case 15511: return "影魂异人(3次)"; case 15512: return "影魂异人(4次)"; case 15514: return "影魂异人(6次)"; case 16000: return "虎影"; case 16001: return "软萌宝"; case 16002: return "莲"; case 16100: return "莲(1次)"; case 16110: return "莲(2次)"; case 16111: return "莲(3次)"; case 16112: return "莲(4次)"; case 16114: return "莲(6次)"; case 16200: return "元素师(1次)"; case 16210: return "元素师(2次)"; case 16211: return "元素师(3次)"; case 16212: return "元素师(4次)"; case 16214: return "元素师(6次)"; case 16400: return "虎影(1次)"; case 16410: return "虎影(2次)"; case 16411: return "虎影(3次)"; case 16412: return "虎影(4次)"; case 16414: return "虎影(6次)"; case 17000: return "墨玄"; case 17001: return "琳"; case 17200: return "琳(1次)"; case 17210: return "琳(2次)"; case 17211: return "琳(3次)"; case 17212: return "琳(4次)"; case 17214: return "琳(5次)"; case 17500: return "墨玄(1次)"; case 17510: return "墨玄(2次)"; case 17511: return "墨玄(3次)"; case 17512: return "墨玄(4次)"; case 17514: return "墨玄(6次)"; case 18000: return "施亚"; case 18200: return "施亚(1次)"; case 18210: return "施亚(2次)"; case 18211: return "施亚(3次)"; case 18212: return "施亚(4次)"; case 18214: return "施亚(6次)"; case 40000: return "5转"; case 40001: return "5转(战士)"; case 40002: return "5转(魔法师)"; case 40003: return "5转(弓箭手)"; case 40004: return "5转(飞侠)"; case 40005: return "5转(海盗)"; case 50000: return "6转"; case 50006: return "6转(强化核心)"; case 50007: return "6转(HEXA属性)"; } return null; } public static string ToChineseNumberExpr(long value, bool detailedExpr = false) { var sb = new StringBuilder(32); bool firstPart = true; if (value < 0) { sb.Append("-"); value = -value; // just ignore the exception -2147483648 } if (detailedExpr) { string[] smallUnits = { "", "十", "百", "千" }; string[] bigUnits = { "", "万", "亿", "兆", "京" }; string digits = value.ToString(); int len = digits.Length; bool blockHasValue = false; int zeroCount = 0; for (int i = 0; i < len; i++) { int posFromRight = len - i - 1; int smallUnitIndex = posFromRight % 4; int bigUnitIndex = posFromRight / 4; char d = digits[i]; if (d == '0') { zeroCount++; } else { if (zeroCount > 0 && zeroCount <= 3) { sb.Append('0'); } zeroCount = 0; sb.Append(d); if (smallUnitIndex > 0) sb.Append(smallUnits[smallUnitIndex]); blockHasValue = true; } if (smallUnitIndex == 0) { if (blockHasValue && bigUnitIndex > 0 && bigUnitIndex < bigUnits.Length) sb.Append(bigUnits[bigUnitIndex]); blockHasValue = false; zeroCount = 0; } } } else { if (value >= 1_0000_0000_0000_0000) { long part = value / 1_0000_0000_0000_0000; sb.Append(firstPart ? null : " "); sb.AppendFormat("{0}京", part); value -= part * 1_0000_0000_0000_0000; firstPart = false; } if (value >= 1_0000_0000_0000) { long part = value / 1_0000_0000_0000; sb.Append(firstPart ? null : " "); sb.AppendFormat("{0}兆", part); value -= part * 1_0000_0000_0000; firstPart = false; } if (value >= 1_0000_0000) { long part = value / 1_0000_0000; sb.Append(firstPart ? null : " "); sb.AppendFormat("{0}亿", part); value -= part * 1_0000_0000; firstPart = false; } if (value >= 1_0000) { long part = value / 1_0000; sb.Append(firstPart ? null : " "); sb.AppendFormat("{0}万", part); value -= part * 1_0000; firstPart = false; } if (value > 0) { sb.Append(firstPart ? null : " "); sb.AppendFormat("{0}", value); } } return sb.Length > 0 ? sb.ToString() : "0"; } public static string ToThousandsNumberExpr(long value, bool isMsea = false) { var sb = new StringBuilder(32); bool firstPart = true; if (isMsea) { if (value < 0) { sb.Append("-"); value = -value; // just ignore the exception -2147483648 } if (value >= 1_0000_0000_0000_0000) { long part = value / 1_0000_0000_0000_0000; sb.Append(firstPart ? null : " "); sb.AppendFormat("{0}Q", part); value -= part * 1_0000_0000_0000_0000; firstPart = false; } if (value >= 1_000_000_000_000) { long part = value / 1_000_000_000_000; sb.Append(firstPart ? null : " "); sb.AppendFormat("{0}T", part); value -= part * 1_000_000_000_000; firstPart = false; } if (value >= 1_000_000_000) { long part = value / 1_000_000_000; sb.Append(firstPart ? null : " "); sb.AppendFormat("{0}B", part); value -= part * 1_000_000_000; firstPart = false; } if (value >= 1_000_000) { long part = value / 1_000_000; sb.Append(firstPart ? null : " "); sb.AppendFormat("{0}M", part); value -= part * 1_000_000; firstPart = false; } if (value >= 1_000) { long part = value / 1_000; sb.Append(firstPart ? null : " "); sb.AppendFormat("{0}K", part); value -= part * 1_000; firstPart = false; } if (value > 0) { sb.Append(firstPart ? null : " "); sb.AppendFormat("{0}", value); } } else { if (value < 0) { sb.Append("-"); value = -value; // just ignore the exception -2147483648 } /* if (value >= 1_000_000_000_000) // For future proofing { double part = Math.Round((double)value / 1_000_000_000_000, 1); sb.AppendFormat("{0}T", part); } */ if (value >= 1_000_000_000) { double part = Math.Round((double)value / 1_000_000_000, 1); sb.AppendFormat("{0}B", part); } else if (value >= 1_000_000) { double part = Math.Round((double)value / 1_000_000, 1); sb.AppendFormat("{0}M", part); } else if (value >= 1_000) { double part = Math.Round((double)value / 1_000, 1); sb.AppendFormat("{0}K", part); } else if (value > 0) { sb.AppendFormat("{0}", value); } } return sb.Length > 0 ? sb.ToString() : "0"; } } } ================================================ FILE: WzComparerR2.Common/CharaSim/Mob.cs ================================================ using System; using System.Collections.Generic; using System.Text; using System.Text.RegularExpressions; using WzComparerR2.WzLib; namespace WzComparerR2.CharaSim { public class Mob { public Mob() { this.ID = -1; this.ElemAttr = new MobElemAttr(null); this.Revive = new List(); //this.Animates = new LifeAnimateCollection(); this.FirstAttack = false; this.BodyAttack = false; this.DamagedByMob = false; } public int ID { get; set; } public int Level { get; set; } public string DefaultHP { get; set; } public string DefaultMP { get; set; } public string FinalMaxHP { get; set; } public string FinalMaxMP { get; set; } public long MaxHP { get; set; } public long MaxMP { get; set; } public int HPRecovery { get; set; } public int MPRecovery { get; set; } public int? Speed { get; set; } public int? FlySpeed { get; set; } public int PADamage { get; set; } public int MADamage { get; set; } public int PDRate { get; set; } public int MDRate { get; set; } public int Acc { get; set; } public int Eva { get; set; } public int Pushed { get; set; } public int Exp { get; set; } public bool Boss { get; set; } public bool Undead { get; set; } public int Category { get; set; } public bool FirstAttack { get; set; } public bool BodyAttack { get; set; } public int RemoveAfter { get; set; } public bool DamagedByMob { get; set; } public bool Invincible { get; set; } public bool NotAttack { get; set; } public int FixedDamage { get; set; } public MobElemAttr ElemAttr { get; set; } public int? Link { get; set; } public bool Skeleton { get; set; } public bool JsonLoad { get; set; } public List Revive { get; private set; } public BitmapOrigin Default { get; set; } //public LifeAnimateCollection Animates { get; private set; } public static Mob CreateFromNode(Wz_Node node, GlobalFindNodeFunction findNode) { int mobID; Match m = Regex.Match(node.Text, @"^(\d{7})\.img$"); if (!(m.Success && Int32.TryParse(m.Result("$1"), out mobID))) { return null; } Mob mobInfo = new Mob(); mobInfo.ID = mobID; Wz_Node infoNode = node.FindNodeByPath("info"); //加载基础属性 if (infoNode != null) { foreach (var propNode in infoNode.Nodes) { switch (propNode.Text) { case "level": mobInfo.Level = propNode.GetValueEx(0); break; case "defaultHP": mobInfo.DefaultHP = propNode.GetValueEx(null); break; case "defaultMP": mobInfo.DefaultMP = propNode.GetValueEx(null); break; case "finalmaxHP": mobInfo.FinalMaxHP = propNode.GetValueEx(null); break; case "finalmaxMP": mobInfo.FinalMaxMP = propNode.GetValueEx(null); break; case "maxHP": mobInfo.MaxHP = propNode.GetValueEx(0); break; case "maxMP": mobInfo.MaxMP = propNode.GetValueEx(0); break; case "hpRecovery": mobInfo.HPRecovery = propNode.GetValueEx(0); break; case "mpRecovery": mobInfo.MPRecovery = propNode.GetValueEx(0); break; case "speed": mobInfo.Speed = propNode.GetValueEx(0); break; case "flySpeed": mobInfo.FlySpeed = propNode.GetValueEx(0); break; case "PADamage": mobInfo.PADamage = propNode.GetValueEx(0); break; case "MADamage": mobInfo.MADamage = propNode.GetValueEx(0); break; case "PDRate": mobInfo.PDRate = propNode.GetValueEx(0); break; case "MDRate": mobInfo.MDRate = propNode.GetValueEx(0); break; case "acc": mobInfo.Acc = propNode.GetValueEx(0); break; case "eva": mobInfo.Eva = propNode.GetValueEx(0); break; case "pushed": mobInfo.Pushed = propNode.GetValueEx(0); break; case "exp": mobInfo.Exp = propNode.GetValueEx(0); break; case "boss": mobInfo.Boss = propNode.GetValueEx(0) != 0; break; case "undead": mobInfo.Undead = propNode.GetValueEx(0) != 0; break; case "firstAttack": mobInfo.FirstAttack = propNode.GetValueEx(0) != 0; break; case "bodyAttack": mobInfo.BodyAttack = propNode.GetValueEx(0) != 0; break; case "category": mobInfo.Category = propNode.GetValueEx(0); break; case "removeAfter": mobInfo.RemoveAfter = propNode.GetValueEx(0); break; case "damagedByMob": mobInfo.DamagedByMob = propNode.GetValueEx(0) != 0; break; case "invincible": mobInfo.Invincible = propNode.GetValueEx(0) != 0; break; case "notAttack": mobInfo.NotAttack = propNode.GetValueEx(0) != 0; break; case "fixedDamage": mobInfo.FixedDamage = propNode.GetValueEx(0); break; case "elemAttr": mobInfo.ElemAttr = new MobElemAttr(propNode.GetValueEx(null)); break; case "link": mobInfo.Link = propNode.GetValueEx(0); break; case "skeleton": mobInfo.Skeleton = propNode.GetValueEx(0) != 0; break; case "jsonLoad": mobInfo.JsonLoad = propNode.GetValueEx(0) != 0; break; //case "skill": LoadSkill(mobInfo, propNode); break; //case "attack": LoadAttack(mobInfo, propNode); break; //case "buff": LoadBuff(mobInfo, propNode); break; case "revive": for (int i = 0; ; i++) { var reviveNode = propNode.FindNodeByPath(i.ToString()); if (reviveNode == null) { break; } mobInfo.Revive.Add(reviveNode.GetValue()); } break; } } } //读取怪物默认动作 { Wz_Node linkNode = null; if (mobInfo.Link != null && findNode != null) { linkNode = findNode(string.Format("Mob\\{0:d7}.img", mobInfo.Link)); } if (linkNode == null) { linkNode = node; } var imageFrame = new BitmapOrigin(); foreach (var action in new[] { "stand", "move", "fly" }) { var actNode = linkNode.FindNodeByPath(action + @"\0"); if (actNode != null) { imageFrame = BitmapOrigin.CreateFromNode(actNode, findNode); if (imageFrame.Bitmap != null) { break; } } } mobInfo.Default = imageFrame; } return mobInfo; } } } ================================================ FILE: WzComparerR2.Common/CharaSim/MobElemAttr.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace WzComparerR2.CharaSim { public class MobElemAttr { public MobElemAttr(string elemAttr) { this.StringValue = elemAttr; if (string.IsNullOrEmpty(elemAttr)) { return; } for (int i = 0; i < elemAttr.Length; i += 2) { ElemResistance resist = (ElemResistance)(elemAttr[i + 1] - 48); switch (elemAttr[i]) { case 'I': this.I = resist; break; case 'L': this.L = resist; break; case 'F': this.F = resist; break; case 'S': this.S = resist; break; case 'H': this.H = resist; break; case 'D': this.D = resist; break; case 'P': this.P = resist; break; } } } public string StringValue { get; private set; } public ElemResistance I { get; private set; } public ElemResistance L { get; private set; } public ElemResistance F { get; private set; } public ElemResistance S { get; private set; } public ElemResistance H { get; private set; } public ElemResistance D { get; private set; } public ElemResistance P { get; private set; } } public enum ElemResistance : byte { Normal = 0, Immune = 1, Resist = 2, Weak = 3, } } ================================================ FILE: WzComparerR2.Common/CharaSim/Npc.cs ================================================ using System; using System.Collections.Generic; using System.Text; using System.Text.RegularExpressions; using WzComparerR2.WzLib; namespace WzComparerR2.CharaSim { public class Npc { public Npc() { this.ID = -1; //this.Animates = new LifeAnimateCollection(); } public int ID { get; set; } public bool Shop { get; set; } public int? Link { get; set; } public BitmapOrigin Default { get; set; } //public LifeAnimateCollection Animates { get; private set; } public static Npc CreateFromNode(Wz_Node node, GlobalFindNodeFunction findNode) { int npcID; Match m = Regex.Match(node.Text, @"^(\d{7})\.img$"); if (!(m.Success && Int32.TryParse(m.Result("$1"), out npcID))) { return null; } Npc npcInfo = new Npc(); npcInfo.ID = npcID; Wz_Node infoNode = node.FindNodeByPath("info"); //加载基础属性 if (infoNode != null) { foreach (var propNode in infoNode.Nodes) { switch (propNode.Text) { case "shop": npcInfo.Shop = propNode.GetValueEx(0) != 0; break; case "link": npcInfo.Link = propNode.GetValueEx(0); break; case "default": npcInfo.Default = BitmapOrigin.CreateFromNode(propNode, findNode); break; } } } //读取默认图片 if (npcInfo.Default.Bitmap == null) { Wz_Node linkNode = null; if (npcInfo.Link != null && findNode != null) { linkNode = findNode(string.Format("Npc\\{0:d7}.img", npcInfo.Link)); } if (linkNode == null) { linkNode = node; } var imageFrame = new BitmapOrigin(); foreach (var action in new[] { "stand", "move", "fly" }) { var actNode = linkNode.FindNodeByPath(action + @"\0"); if (actNode != null) { imageFrame = BitmapOrigin.CreateFromNode(actNode, findNode); if (imageFrame.Bitmap != null) { break; } } } npcInfo.Default = imageFrame; } return npcInfo; } } } ================================================ FILE: WzComparerR2.Common/CharaSim/Potential.cs ================================================ using System; using System.Collections.Generic; using System.Text; using WzComparerR2.WzLib; namespace WzComparerR2.CharaSim { public class Potential { public Potential() { props = new Dictionary(); } public int code; public int optionType; public int reqLevel; public Dictionary props; public int weight; public string stringSummary; /// /// 指示潜能是否是附加潜能。 /// public bool IsPotentialEx { get { return this.code / 1000 % 10 == 2; } } public override string ToString() { return this.code.ToString("d6") + " " + ConvertSummary() + (weight > 0 ? (" - " + weight) : null); } public string ConvertSummary() { if (string.IsNullOrEmpty(this.stringSummary)) return null; List types = new List(this.props.Keys.Count); foreach (GearPropType k in this.props.Keys) types.Add(k.ToString()); types.Sort((a, b) => b.Length.CompareTo(a.Length)); string str = this.stringSummary; foreach (string s in types) { GearPropType t = (GearPropType)Enum.Parse(typeof(GearPropType), s); str = str.Replace("#" + s, this.props[t].ToString()); } return str; } public static int GetPotentialLevel(int gearReqLevel) { if (gearReqLevel <= 0) return 1; else if (gearReqLevel >= 200) return 20; else return (gearReqLevel + 9) / 10; } public static bool CheckOptionType(int optionType, GearType gearType) { switch (optionType) { case 0: return true; case 10: return Gear.IsWeapon(gearType) || (Gear.IsSubWeapon(gearType) && gearType != GearType.shield); case 11: return !CheckOptionType(10, gearType); case 20: return gearType == GearType.pants || gearType == GearType.shoes || gearType == GearType.cap || gearType == GearType.coat || gearType == GearType.longcoat || gearType == GearType.glove || gearType == GearType.cape; case 40: return gearType == GearType.ring || gearType == GearType.earrings || gearType == GearType.pendant || gearType == GearType.belt; case 51: return gearType == GearType.cap; case 52: return gearType == GearType.coat || gearType == GearType.longcoat; case 53: return gearType == GearType.pants || gearType == GearType.longcoat; case 54: return gearType == GearType.glove; case 55: return gearType == GearType.shoes; default: return false; } } public static Potential CreateFromNode(Wz_Node potentialNode, int pLevel) { Potential potential = new Potential(); if (potentialNode == null || !Int32.TryParse(potentialNode.Text, out potential.code)) return null; foreach (Wz_Node subNode in potentialNode.Nodes) { if (subNode.Text == "info") { foreach (Wz_Node infoNode in subNode.Nodes) { switch (infoNode.Text) { case "optionType": potential.optionType = Convert.ToInt32(infoNode.Value); break; case "reqLevel": potential.reqLevel = Convert.ToInt32(infoNode.Value); break; case "weight": potential.weight = Convert.ToInt32(infoNode.Value); break; case "string": potential.stringSummary = Convert.ToString(infoNode.Value); break; } } } else if (subNode.Text == "level") { Wz_Node levelNode = subNode.FindNodeByPath(pLevel.ToString()); if (levelNode != null) { foreach (Wz_Node propNode in levelNode.Nodes) { try { GearPropType propType = (GearPropType)Enum.Parse(typeof(GearPropType), propNode.Text); int value = (propType == GearPropType.face ? 0 : Convert.ToInt32(propNode.Value)); potential.props.Add(propType, value); } catch { } } } else { return null; } } } return potential; } public static Potential LoadFromWz(int optID, int optLevel, GlobalFindNodeFunction findNode) { Wz_Node itemWz = findNode("Item\\ItemOption.img"); if (itemWz == null) return null; Potential opt = Potential.CreateFromNode(itemWz.FindNodeByPath(optID.ToString("d6")), optLevel); return opt; } } } ================================================ FILE: WzComparerR2.Common/CharaSim/Recipe.cs ================================================ using System; using System.Collections.Generic; using System.Text; using WzComparerR2.WzLib; namespace WzComparerR2.CharaSim { public class Recipe { public Recipe() { this.TargetItems = new List(); this.RecipeItems = new List(); this.Props = new Dictionary(); } public int RecipeID { get; set; } public List TargetItems { get; private set; } public List RecipeItems { get; private set; } public Dictionary Props { get; private set; } public int MainTargetItemID { get { if (this.TargetItems.Count > 0) { return this.TargetItems[0].ItemID; } return 0; } } public static Recipe CreateFromNode(Wz_Node node) { Recipe recipe = new Recipe(); int recipeID; if (!Int32.TryParse(node.Text, out recipeID)) return null; recipe.RecipeID = recipeID; foreach (Wz_Node subNode in node.Nodes) { switch (subNode.Text) { case "target": for (int i = 0; ; i++) { Wz_Node itemNode = subNode.FindNodeByPath(i.ToString()); if (itemNode == null) { break; } RecipeItemInfo itemInfo = new RecipeItemInfo(); foreach (var itemPropNode in itemNode.Nodes) { switch (itemPropNode.Text) { case "item": itemInfo.ItemID = itemPropNode.GetValue(); break; case "count": itemInfo.Count = itemPropNode.GetValue(); break; case "probWeight": itemInfo.ProbWeight = itemPropNode.GetValue(); break; } } recipe.TargetItems.Add(itemInfo); } break; case "recipe": for (int i = 0; ; i++) { Wz_Node itemNode = subNode.FindNodeByPath(i.ToString()); if (itemNode == null) { break; } RecipeItemInfo itemInfo = new RecipeItemInfo(); foreach (var itemPropNode in itemNode.Nodes) { switch (itemPropNode.Text) { case "item": itemInfo.ItemID = itemPropNode.GetValue(); break; case "count": itemInfo.Count = itemPropNode.GetValue(); break; } } recipe.RecipeItems.Add(itemInfo); } break; default: RecipePropType type; if (Enum.TryParse(subNode.Text, out type)) { try { recipe.Props.Add(type, Convert.ToInt32(subNode.Value)); } finally { } } break; } } return recipe; } } } ================================================ FILE: WzComparerR2.Common/CharaSim/RecipeItemInfo.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace WzComparerR2.CharaSim { public class RecipeItemInfo { public RecipeItemInfo() { } public int ItemID { get; set; } public int Count { get; set; } public int ProbWeight { get; set; } } } ================================================ FILE: WzComparerR2.Common/CharaSim/RecipePropType.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace WzComparerR2.CharaSim { public enum RecipePropType { reqSkillLevel = 1, reqSkillProficiency, coolTimeSec, intFatigability, incSkillMasterProficiency, probMod, } } ================================================ FILE: WzComparerR2.Common/CharaSim/SetItem.cs ================================================ using System; using System.Collections.Generic; using System.Text; using WzComparerR2.WzLib; namespace WzComparerR2.CharaSim { public class SetItem { public SetItem() { ItemIDs = new SetItemIDList(); Effects = new Dictionary(); } public int SetItemID { get; set; } public int CompleteCount { get; set; } public int currentCount; public bool Parts { get; set; } public bool ExpandToolTip { get; set; } public SetItemIDList ItemIDs { get; private set; } public string SetItemName { get; set; } public Dictionary Effects { get; private set; } public static SetItem CreateFromNode(Wz_Node setItemNode, Wz_Node optionNode) { if (setItemNode == null) return null; SetItem setItem = new SetItem(); int setItemID; if (int.TryParse(setItemNode.Text, out setItemID)) { setItem.SetItemID = setItemID; } Dictionary desc = new Dictionary(); foreach (Wz_Node subNode in setItemNode.Nodes) { switch (subNode.Text) { case "setItemName": setItem.SetItemName = Convert.ToString(subNode.Value); break; case "completeCount": setItem.CompleteCount = Convert.ToInt32(subNode.Value); break; case "parts": setItem.Parts = subNode.GetValue() != 0; break; case "expandToolTip": setItem.ExpandToolTip = subNode.GetValue() != 0; break; case "ItemID": foreach (Wz_Node itemNode in subNode.Nodes) { int idx = Convert.ToInt32(itemNode.Text); if (itemNode.Nodes.Count == 0) { int itemID = Convert.ToInt32(itemNode.Value); setItem.ItemIDs.Add(idx, new SetItemIDPart(itemID)); } else { SetItemIDPart part = new SetItemIDPart(); foreach (Wz_Node itemNode2 in itemNode.Nodes) { switch (itemNode2.Text) { case "representName": part.RepresentName = Convert.ToString(itemNode2.Value); break; case "typeName": part.TypeName = Convert.ToString(itemNode2.Value); break; default: if (Int32.TryParse(itemNode2.Text, out int index) && index >= 0) // the key can start from 0 { part.ItemIDs[Convert.ToInt32(itemNode2.Value)] = false; } break; } } setItem.ItemIDs.Add(idx, part); } } break; case "Effect": foreach (Wz_Node effectNode in subNode.Nodes) { int count = Convert.ToInt32(effectNode.Text); SetItemEffect effect = new SetItemEffect(); foreach (Wz_Node propNode in effectNode.Nodes) { switch (propNode.Text) { case "Option": if (optionNode != null) { List potens = new List(); foreach (Wz_Node pNode in propNode.Nodes) { string optText = Convert.ToString(pNode.FindNodeByPath("option").Value).PadLeft(6, '0'); Wz_Node opn = optionNode.FindNodeByPath(optText); if (opn == null) continue; Potential p = Potential.CreateFromNode(opn, Convert.ToInt32(pNode.FindNodeByPath("level").Value)); if (p != null) { potens.Add(p); } } effect.Props.Add(GearPropType.Option, potens); } break; case "OptionToMob": List opToMobList = new List(); for (int i = 1; ; i++) { Wz_Node optNode = propNode.FindNodeByPath(i.ToString()); if (optNode == null) { break; } SetItemOptionToMob option = new SetItemOptionToMob(); foreach (Wz_Node pNode in optNode.Nodes) { switch (pNode.Text) { case "mob": foreach (Wz_Node mobNode in pNode.Nodes) { option.Mobs.Add(mobNode.GetValue()); } break; case "mobName": option.MobName = pNode.GetValue(); break; default: { GearPropType type; if (Enum.TryParse(pNode.Text, out type)) { option.Props.Add(type, pNode.GetValue()); } } break; } } opToMobList.Add(option); } effect.Props.Add(GearPropType.OptionToMob, opToMobList); break; case "activeSkill": List activeSkillList = new List(); for (int i = 0; ; i++) { Wz_Node optNode = propNode.FindNodeByPath(i.ToString()); if (optNode == null) { break; } SetItemActiveSkill activeSkill = new SetItemActiveSkill(); foreach (Wz_Node pNode in optNode.Nodes) { switch (pNode.Text) { case "id": activeSkill.SkillID = pNode.GetValue(); break; case "level": activeSkill.Level= pNode.GetValue(); break; } } activeSkillList.Add(activeSkill); } effect.Props.Add(GearPropType.activeSkill, activeSkillList); break; case "bonusByTime": var bonusByTimeList = new List(); for (int i = 0; ; i++) { Wz_Node optNode = propNode.FindNodeByPath(i.ToString()); if (optNode == null) { break; } var bonusByTime = new SetItemBonusByTime(); foreach (Wz_Node pNode in optNode.Nodes) { switch (pNode.Text) { case "termStart": bonusByTime.TermStart = pNode.GetValue(); break; default: { GearPropType type; if (Enum.TryParse(pNode.Text, out type)) { bonusByTime.Props.Add(type, pNode.GetValue()); } } break; } } bonusByTimeList.Add(bonusByTime); } effect.Props.Add(GearPropType.bonusByTime, bonusByTimeList); break; default: { GearPropType type; if (Enum.TryParse(propNode.Text, out type)) { effect.Props.Add(type, Convert.ToInt32(propNode.Value)); } } break; } } setItem.Effects.Add(count, effect); } break; case "Desc": foreach (var descNode in subNode.Nodes) { desc[descNode.Text] = Convert.ToString(descNode.Value); } break; } } //处理额外分组 if (desc.Count > 0) { foreach (var kv in desc) { SetItemIDPart combinePart = null; string combineTypeName = null; switch (kv.Key) { case "weapon": combinePart = CombinePart(setItem, gearID => Gear.IsWeapon(Gear.GetGearType(gearID))); combineTypeName = ItemStringHelper.GetSetItemGearTypeString(GearType.weapon); break; case "subweapon": combinePart = CombinePart(setItem, gearID => Gear.IsSubWeapon(Gear.GetGearType(gearID))); combineTypeName = ItemStringHelper.GetSetItemGearTypeString(GearType.subWeapon); break; case "pocket": combinePart = CombinePart(setItem, gearID => Gear.GetGearType(gearID) == GearType.pocket); combineTypeName = ItemStringHelper.GetSetItemGearTypeString(GearType.pocket); break; case "emblem": combinePart = CombinePart(setItem, gearID => Gear.GetGearType(gearID) == GearType.emblem); combineTypeName = ItemStringHelper.GetSetItemGearTypeString(GearType.emblem); break; } if (combinePart != null) { combinePart.RepresentName = kv.Value; combinePart.TypeName = combineTypeName; ItemStringHelper.GetSetItemGearTypeString(GearType.weapon); } } } return setItem; } /// /// 按一定条件合并装备部件的分组。 /// /// 装备id符合条件的判断方法。 /// private static SetItemIDPart CombinePart(SetItem setItem, Predicate predicate) { List itemIDList = new List(); List preRemovedPartIdx = new List(); int? idx = null; foreach (var part in setItem.ItemIDs.Parts) { bool add = false; foreach (var itemID in part.Value.ItemIDs.Keys) { if (predicate(itemID)) //id满足条件 { itemIDList.Add(itemID); add = true; } } if (add) //提取出被合并项的最大partID { //idx = idx == null ? part.Key : Math.Max(part.Key, idx.Value); if (!preRemovedPartIdx.Contains(part.Key)) preRemovedPartIdx.Add(part.Key); } idx = idx == null ? part.Key : Math.Max(part.Key, idx.Value); } if (itemIDList.Count > 0) { SetItemIDPart part = new SetItemIDPart(itemIDList); foreach (int i in preRemovedPartIdx) { setItem.ItemIDs.Remove(i); } setItem.ItemIDs.Add(idx.Value + 1, part); return part; } return null; } } } ================================================ FILE: WzComparerR2.Common/CharaSim/SetItemActiveSkill.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace WzComparerR2.CharaSim { public class SetItemActiveSkill { public SetItemActiveSkill() { } public int SkillID { get; set; } public int Level { get; set; } } } ================================================ FILE: WzComparerR2.Common/CharaSim/SetItemBonusByTime.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace WzComparerR2.CharaSim { public class SetItemBonusByTime { public SetItemBonusByTime() { this.Props = new Dictionary(); } public int TermStart { get; set; } public Dictionary Props { get; private set; } } } ================================================ FILE: WzComparerR2.Common/CharaSim/SetItemEffect.cs ================================================ using System; using System.Collections.Generic; using System.Text; using System.Linq; namespace WzComparerR2.CharaSim { public class SetItemEffect { public SetItemEffect() { props = new SortedDictionary(); enabled = false; } private SortedDictionary props; private bool enabled; public SortedDictionary Props { get { return props; } } public IEnumerable> PropsV5 { get { return props.Where(kv => Gear.IsV5SupportPropType(kv.Key)); } } public bool Enabled { get { return enabled; } set { enabled = value; } } } } ================================================ FILE: WzComparerR2.Common/CharaSim/SetItemIDList.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace WzComparerR2.CharaSim { public class SetItemIDList { public SetItemIDList() { this.Parts = new List>(); } public List> Parts {get;private set;} public void Add(int partID, SetItemIDPart part) { this.Parts.Add(new KeyValuePair(partID, part)); } public void Remove(int partID) { this.Parts.RemoveAll(kv => kv.Key == partID); } /// /// 获取或设置装备是否有效。 /// /// 装备ID。 /// public bool this[int itemID] { get { foreach (var kv in Parts) { bool enabled; kv.Value.ItemIDs.TryGetValue(itemID, out enabled); if (enabled) return true; } return false; } set { foreach (var kv in Parts) { bool enabled; if (kv.Value.ItemIDs.TryGetValue(itemID, out enabled) && (enabled ^ value)) { kv.Value.ItemIDs[itemID] = value; } } } } } } ================================================ FILE: WzComparerR2.Common/CharaSim/SetItemIDPart.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace WzComparerR2.CharaSim { public class SetItemIDPart { public SetItemIDPart() { itemIDs = new Dictionary(); } /// /// 通过一件装备ID初始化SetItemIDPart的实例。 /// /// 要初始化的装备ID。 public SetItemIDPart(int itemID) : this() { itemIDs[itemID] = false; } /// /// 通过一个装备ID集合初始化SetItemIDPart的实例。 /// /// 要初始化的装备ID集合。 public SetItemIDPart(IEnumerable itemIDList) : this() { foreach (int itemID in itemIDList) { itemIDs[itemID] = false; } } private Dictionary itemIDs; private string representName; private string typeName; public Dictionary ItemIDs { get { return itemIDs; } } /// /// 获取一个值,它表示是否当前的装备ID中,至少有一个是生效的。 /// public bool Enabled { get { foreach (var kv in itemIDs) { if (kv.Value) return true; } return false; } } /// /// 获取或设置套装部件的显示名称。 /// public string RepresentName { get { return representName; } set { representName = value; } } /// /// 获取或设置套装部件的类型显示名称。 /// public string TypeName { get { return typeName; } set { typeName = value; } } } } ================================================ FILE: WzComparerR2.Common/CharaSim/SetItemOptionToMob.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace WzComparerR2.CharaSim { public class SetItemOptionToMob { public SetItemOptionToMob() { this.Mobs = new List(); this.Props = new Dictionary(); } public List Mobs { get; private set; } public string MobName { get; set; } public Dictionary Props { get; private set; } public string ConvertSummary() { StringBuilder sb = new StringBuilder(); string mobStr = null; if (MobName != null) { mobStr = MobName; } else if (Mobs.Count > 0) { mobStr = Mobs[0].ToString(); } sb.AppendFormat("攻击{0}时,", mobStr); foreach (var kv in this.Props) { if (kv.Key == GearPropType.damR) { sb.AppendFormat("伤害增加{0}%,", kv.Value); } else { sb.Append(ItemStringHelper.GetGearPropString(kv.Key, kv.Value)); } } return sb.ToString(0, sb.Length - 1); } } } ================================================ FILE: WzComparerR2.Common/CharaSim/Skill.cs ================================================ using System; using System.Collections.Generic; using System.Text; using WzComparerR2.WzLib; namespace WzComparerR2.CharaSim { public class Skill { public Skill() { this.level = 0; this.levelCommon = new List>(); this.common = new Dictionary(); this.PVPcommon = new Dictionary(); this.ReqSkill = new Dictionary(); this.Action = new List(); this.PerJobAttackInfo = new Dictionary>(); this.perJobIndex = 0; } private int level; private int perJobIndex; internal List> levelCommon; internal Dictionary common; public Dictionary Common { get { if (PreBBSkill && this.level > 0 && this.level <= levelCommon.Count) return levelCommon[this.level - 1]; else return common; } } public Dictionary PVPcommon { get; private set; } public int SkillID { get; set; } public BitmapOrigin Icon { get; set; } public BitmapOrigin IconMouseOver { get; set; } public BitmapOrigin IconDisabled { get; set; } public HyperSkillType Hyper { get; set; } public int Level { get { return level; } set { bool canBreakLevel = this.CombatOrders || this.VSkill || this.SkillID / 100000 == 4000; //fix for evan int maxLevel = canBreakLevel ? 100 : this.MaxLevel; level = Math.Max(0, Math.Min(value, maxLevel)); } } public int PerJobIndex { get { return perJobIndex; } set { perJobIndex = Math.Max(0, Math.Min(value, this.PerJobAttackInfo.Count - 1)); } } public int ReqLevel { get; set; } public int ReqAmount { get; set; } public bool PreBBSkill { get; set; } public bool Invisible { get; set; } public bool CombatOrders { get; set; } public bool NotRemoved { get; set; } public bool VSkill { get; set; } public bool Origin { get; set; } public int MasterLevel { get; set; } public Dictionary ReqSkill { get; private set; } public List Action { get; private set; } public Dictionary> PerJobAttackInfo { get; private set; } public int MaxLevel { get { string v; if (this.PreBBSkill) return levelCommon.Count; else if (common.TryGetValue("maxLevel", out v)) return Convert.ToInt32(v); return 0; } } public static Skill CreateFromNode(Wz_Node node, GlobalFindNodeFunction findNode) { Skill skill = new Skill(); int skillID; if (!Int32.TryParse(node.Text, out skillID)) return null; skill.SkillID = skillID; foreach (Wz_Node childNode in node.Nodes) { switch (childNode.Text) { case "icon": skill.Icon = BitmapOrigin.CreateFromNode(childNode, findNode); break; case "iconMouseOver": skill.IconMouseOver = BitmapOrigin.CreateFromNode(childNode, findNode); break; case "iconDisabled": skill.IconDisabled = BitmapOrigin.CreateFromNode(childNode, findNode); break; case "common": foreach (Wz_Node commonNode in childNode.Nodes) { if (commonNode.Value != null && !(commonNode.Value is Wz_Vector)) { skill.common[commonNode.Text] = commonNode.Value.ToString(); } else if (commonNode.Text == "attackInfo") { if (commonNode.Nodes.Count > 0) { foreach (Wz_Node jobNode in commonNode.Nodes) { int jobID; if (Int32.TryParse(jobNode.Text, out jobID)) { Dictionary attackInfo = new Dictionary(); foreach (Wz_Node infoNode in jobNode.Nodes) { attackInfo[infoNode.Text] = infoNode.Value.ToString(); } skill.PerJobAttackInfo[jobID] = attackInfo; } } } } } break; case "PVPcommon": foreach (Wz_Node commonNode in childNode.Nodes) { if (commonNode.Value != null && !(commonNode.Value is Wz_Vector)) { skill.PVPcommon[commonNode.Text] = commonNode.Value.ToString(); } } break; case "level": for (int i = 1; ; i++) { Wz_Node levelNode = childNode.FindNodeByPath(i.ToString()); if (levelNode == null) break; Dictionary levelInfo = new Dictionary(); foreach (Wz_Node commonNode in levelNode.Nodes) { if (commonNode.Value != null && !(commonNode.Value is Wz_Vector)) { levelInfo[commonNode.Text] = commonNode.Value.ToString(); } } skill.levelCommon.Add(levelInfo); } break; case "hyper": skill.Hyper = (HyperSkillType)childNode.GetValue(); break; case "invisible": skill.Invisible = childNode.GetValue() != 0; break; case "combatOrders": skill.CombatOrders = childNode.GetValue() != 0; break; case "notRemoved": skill.NotRemoved = childNode.GetValue() != 0; break; case "vSkill": skill.VSkill = childNode.GetValue() != 0; break; case "origin": skill.Origin = childNode.GetValue() != 0; break; case "masterLevel": skill.MasterLevel = childNode.GetValue(); break; case "reqLev": skill.ReqLevel = childNode.GetValue(); break; case "req": foreach (Wz_Node reqNode in childNode.Nodes) { if (reqNode.Text == "level") { skill.ReqLevel = reqNode.GetValue(); } else if (reqNode.Text == "reqAmount") { skill.ReqAmount = reqNode.GetValue(); } else { int reqSkill; if (Int32.TryParse(reqNode.Text, out reqSkill)) { skill.ReqSkill[reqSkill] = reqNode.GetValue(); } } } break; case "action": for (int i = 0; ; i++) { Wz_Node idxNode = childNode.FindNodeByPath(i.ToString()); if (idxNode == null) break; skill.Action.Add(idxNode.GetValue()); } break; } } //判定技能声明版本 skill.PreBBSkill = false; if (skill.levelCommon.Count > 0) { if (skill.common.Count <= 0 || skill.common.ContainsKey("maxLevel")) { skill.PreBBSkill = true; } } return skill; } } } ================================================ FILE: WzComparerR2.Common/CharaSim/SummaryParams.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace WzComparerR2.CharaSim { public struct SummaryParams { private string r; private string n; private string cStart; private string cEnd; private string gStart; private string gEnd; /// /// 获取或设置回车符(\r)的替换字符串。 /// public string R { get { return r; } set { r = value; } } /// /// 获取或设置换行符(\n)的替换字符串。 /// public string N { get { return n; } set { n = value; } } /// /// 获取或设置高亮起始符(#c)的替换字符串。 /// public string CStart { get { return cStart; } set { cStart = value; } } /// /// 获取或设置高亮结束符(#)的替换字符串 /// public string CEnd { get { return cEnd; } set { cEnd = value; } } /// /// 获取或设置自定义高亮起始符(#g)的替换字符串。 /// public string GStart { get { return gStart; } set { gStart = value; } } /// /// 获取或设置自定义高亮结束符(#)的替换字符串 /// public string GEnd { get { return gEnd; } set { gEnd = value; } } /// /// 获取默认的替换字符串组合。 /// public static SummaryParams Default { get { return new SummaryParams() { R = @"\r", N = @"\n", cStart = @"#c", cEnd = @"#", gStart = @"#$g", gEnd = @"#" }; } } public static SummaryParams Text { get { return new SummaryParams() { R = "\r", N = "\n", cStart = @"#c", cEnd = @"#", gStart = @"#$g", gEnd = @"#" }; } } public static SummaryParams Html { get { return new SummaryParams() { R = null, N = "
", cStart = @"", cEnd = @"", gStart = @"", gEnd = @"" }; } } } } ================================================ FILE: WzComparerR2.Common/CharaSim/SummaryParser.cs ================================================ using System; using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Text; using System.Text.RegularExpressions; using WzComparerR2.Common; namespace WzComparerR2.CharaSim { public class SummaryParser { static SummaryParser() { GlobalVariableMapping = new Dictionary(); GlobalVariableMapping["comboConAran"] = "aranComboCon"; } public static string GetSkillSummary(string H, int Level, Dictionary CommonProps, SummaryParams param, SkillSummaryOptions options = default) { if (H == null) return null; int idx = 0; StringBuilder sb = new StringBuilder(); bool beginC = false; while (idx < H.Length) { if (H[idx] == '#') { int end = idx, len = 0; while ((++end) < H.Length) { if (H[end] == '_' || ('a' <= H[end] && H[end] <= 'z') || ('A' <= H[end] && H[end] <= 'Z') || (end - idx > 1 && '0' <= H[end] && H[end] <= '9')) //^[_A-Za-z][_A-Za-z0-9]*$ { len++; } else { break; } } //优先匹配common string prop = null; string propKey = null; if (CommonProps != null) { for (int i = len; i > 0; i--) { propKey = H.Substring(idx + 1, i); if (GetValueIgnoreCase(CommonProps, propKey, out prop)) { len = i; break; } } } if (prop != null) { try { decimal val = Calculator.Parse(prop, Level); if (options.ConvertCooltimeMS && propKey == "cooltimeMS") { sb.AppendFormat("{0:f2}", val / 1000); } else if (options.ConvertPerM && propKey.EndsWith("PerM", StringComparison.Ordinal)) { sb.AppendFormat("{0:f1}", val / 100); } else { sb.Append(val); } } catch { if (options.IgnoreEvalError) { sb.Append("NaN"); } else { throw; } } idx += len + 1; continue; } else //试图匹配全局变量 { string key = null; for (int i = len; i > 0; i--) { key = H.Substring(idx + 1, i); if (GlobalVariableMapping.TryGetValue(key, out prop)) { break; } } if (prop != null) { if (prop != "" && GetValueIgnoreCase(CommonProps, prop, out prop)) { try { decimal val = Calculator.Parse(prop, Level); sb.Append(val); } catch { if (options.IgnoreEvalError) { sb.Append("NaN"); } else { throw; } } } else { sb.Append(param.GStart).Append("[").Append(key).Append("]").Append(param.GEnd); } idx += len + 1; continue; } } //匹配#c...#段落 if (beginC) { beginC = false; sb.Append(param.CEnd); idx++; } else if (idx + 1 < H.Length && H[idx + 1] == 'c') { beginC = true; sb.Append(param.CStart); idx += 2; } else if (idx + 1 < H.Length && len == 0)//匹配省略c的段落 { beginC = true; sb.Append(param.CStart); idx++; } else if (len > 0)//无法匹配 取最长的common段 { string key = H.Substring(idx + 1, len); if (Regex.IsMatch(key, @"^\d+$")) { sb.Append(key); } else { sb.Append(0);//默认值 } idx += len + 1; } else // skip last # { idx++; } } else if (H[idx] == '\\') { if (idx + 1 < H.Length) { switch (H[idx + 1]) { case 'c': break; // \c忽略掉 原因不明 case 'r': sb.Append(param.R); break; case 'n': if (beginC && options.EndColorOnNewLine) { beginC = false; sb.Append(param.CEnd); } sb.Append(param.N); break; case '\\': sb.Append('\\'); break; default: sb.Append(H[idx + 1]); break; } idx += 2; } else //转义失败 { idx++; } } else { sb.Append(H[idx++]); } } return sb.ToString(); } private static bool GetValueIgnoreCase(Dictionary dict, string key, out string value) { //bool find = false; foreach (var kv in dict) { if (kv.Key.Equals(key, StringComparison.OrdinalIgnoreCase)) { value = kv.Value; return true; } } value = null; return false; } public static string GetSkillSummary(Skill skill, StringResultSkill sr, SummaryParams param) { if (skill == null) return null; return GetSkillSummary(skill, skill.Level, sr, param); } public static string GetSkillSummary(Skill skill, int level, StringResultSkill sr, SummaryParams param, SkillSummaryOptions options = default, Dictionary overrideSkillCommon = null) { if (skill == null || sr == null) return null; string h = null; if (skill.PreBBSkill) //用level声明的技能 { string hsSummary; if (skill.Level == level && skill.Common.TryGetValue("hs", out string hs) && (hsSummary = sr[hs]) != null) // fix for skill 170001005, 170011005 { h = hsSummary; } else if (sr.SkillH.Count >= level) { h = sr.SkillH[level - 1]; } else if (sr.SkillH.Count == 1) { h = sr.SkillH[0]; } var levelCommon = level <= skill.levelCommon.Count ? skill.levelCommon[level - 1] : skill.common; return GetSkillSummary(h, level, levelCommon, param, options); } else { if (sr.SkillH.Count > 0) { h = sr.SkillH[0]; } if (sr.SkillExtraH.Count > 0) { // SkillExtraH is always sorted foreach (var kv in sr.SkillExtraH) { if (level < kv.Key) { break; } h = kv.Value; } } return GetSkillSummary(h, level, overrideSkillCommon ?? skill.Common, param, options); } } public static Dictionary GlobalVariableMapping { get; private set; } } public struct SkillSummaryOptions { public bool ConvertCooltimeMS { get; set; } public bool ConvertPerM { get; set; } public bool IgnoreEvalError { get; set; } public bool EndColorOnNewLine { get; set; } } } ================================================ FILE: WzComparerR2.Common/Config/ConfigArrayList.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Configuration; namespace WzComparerR2.Config { public class ConfigArrayList : ConfigurationElementCollection, IEnumerable { public T this[int index] { get { return ((ItemElement)base.BaseGet(index)).Value; } set { base.BaseRemoveAt(index); base.BaseAdd(index, new ItemElement() { Value = value }); } } public void Add(T item) { base.BaseAdd(new ItemElement() { Value = item }, false); } public void Insert(int index, T item) { base.BaseAdd(index, new ItemElement() { Value = item }); } public void RemoveAt(int index) { base.BaseRemoveAt(index); } public bool Remove(T item) { int index = this.IndexOf(item); if (index > -1) { base.BaseRemoveAt(index); return true; } return false; } public int IndexOf(T item) { var elem = this.OfType().FirstOrDefault(e => object.Equals(e.Value, item)); int index = elem == null ? -1 : base.BaseIndexOf(elem); return index; } public void Clear() { base.BaseClear(); } protected override ConfigurationElement CreateNewElement() { return new ItemElement(); } protected override object GetElementKey(ConfigurationElement element) { return element; } public new IEnumerator GetEnumerator() { return this.OfType().Select(elem => elem.Value).GetEnumerator(); } protected override string ElementName { get { return "item"; } } public class ItemElement : ConfigurationElement { public ItemElement() { this.Hash = Guid.NewGuid(); } [ConfigurationProperty("hash", IsRequired = true)] public Guid Hash { get { return (Guid)this["hash"]; } set { this["hash"] = value; } } [ConfigurationProperty("value", IsRequired = true)] public T Value { get { return (T)this["value"]; } set { this["value"] = value; } } } } } ================================================ FILE: WzComparerR2.Common/Config/ConfigItem.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Configuration; namespace WzComparerR2.Config { public class ConfigItem : ConfigurationElement { [ConfigurationProperty("value", IsRequired = true)] public T Value { get { return (T)this["value"]; } set { this["value"] = value; } } public static implicit operator T(ConfigItem item) { return item.Value; } public static implicit operator ConfigItem(T item) { return new ConfigItem() { Value = item }; } public override string ToString() { return nameof(ConfigItem) + " [" + this.Value + "]"; } } } ================================================ FILE: WzComparerR2.Common/Config/ConfigItemCollectionBase.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Configuration; namespace WzComparerR2.Config { public class ConfigItemCollectionBase : ConfigurationElementCollection where T : ConfigurationElement, new() { public T this[int index] { get { return (T)base.BaseGet(index); } set { base.BaseRemoveAt(index); base.BaseAdd(index, value); } } public void Add(T item) { base.BaseAdd(item, false); } public void Insert(int index, T item) { base.BaseAdd(index, item); } public void RemoveAt(int index) { base.BaseRemoveAt(index); } public void Remove(T item) { int index = base.BaseIndexOf(item); if (index > -1) { base.BaseRemoveAt(index); } } public void Clear() { base.BaseClear(); } protected override ConfigurationElement CreateNewElement() { return new T(); } protected override object GetElementKey(ConfigurationElement element) { return element; } } } ================================================ FILE: WzComparerR2.Common/Config/ConfigManager.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Configuration; using System.IO; using System.Windows.Forms; using System.Reflection; using System.Runtime.CompilerServices; namespace WzComparerR2.Config { public static class ConfigManager { public static string ConfigFileName { get { return Path.Combine(Application.StartupPath, "Setting.config"); } } public static Configuration ConfigFile { get { return _configFile = (_configFile ?? Open()); } } private static Configuration _configFile; private static Configuration Open() { var configFile = ConfigurationManager.OpenMappedMachineConfiguration( new ConfigurationFileMap() { MachineConfigFilename = ConfigFileName }); return configFile; } public static void Reload() { _configFile = Open(); } public static void Save() { _configFile?.Save(ConfigurationSaveMode.Full); } public static bool RegisterSection() where T : ConfigSectionBase, new() { return RegisterSection(typeof(T)); } public static bool RegisterSection(Type type) { if (type == null || !type.IsSubclassOf(typeof(ConfigSectionBase<>).MakeGenericType(type))) { throw new ArgumentException($"类型{type}没有继承于{typeof(ConfigSectionBase<>)}。"); } string secName = GetSectionName(type); if (ConfigFile.GetSection(secName) == null) { ConfigFile.Sections.Add(secName, Activator.CreateInstance(type) as ConfigurationSection); var section = ConfigFile.GetSection(secName); return true; } return false; } /// /// 对此方法的调用不应为尾调用, 否则会因尾调用优化出错. /// /// public static void RegisterAllSection() { var asm = Assembly.GetCallingAssembly(); RegisterAllSection(asm); } public static void RegisterAllSection(Assembly assembly) { var secTypes = assembly.GetExportedTypes().Where(type => { try { var baseType = type.BaseType; return baseType != null && baseType.IsGenericType && baseType.GetGenericTypeDefinition() == typeof(ConfigSectionBase<>); } catch { return false; } }); bool needSave = false; foreach (var type in secTypes) { needSave |= RegisterSection(type); } if (needSave) { Save(); } } public static string GetSectionName(Type type) { var attrList = type.GetCustomAttributes(typeof(SectionNameAttribute), false).OfType(); return attrList.Select(attr => attr.Name).FirstOrDefault(secName => !string.IsNullOrEmpty(secName)) ?? type.Name; } } } ================================================ FILE: WzComparerR2.Common/Config/ConfigSectionBase.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Configuration; using System.Reflection; namespace WzComparerR2.Config { public abstract class ConfigSectionBase : ConfigurationSection where T : ConfigSectionBase, new() { public static T Default { get { string secName = ConfigManager.GetSectionName(typeof(T)); try { return ConfigManager.ConfigFile.GetSection(secName) as T; } catch (ConfigurationErrorsException e) { ConfigManager.Reload(); return ConfigManager.ConfigFile.GetSection(secName) as T; } } } } } ================================================ FILE: WzComparerR2.Common/Config/SectionNameAttribute.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace WzComparerR2.Config { public sealed class SectionNameAttribute : Attribute { public SectionNameAttribute(string sectionName) { this.Name = sectionName; } public string Name { get; set; } } } ================================================ FILE: WzComparerR2.Common/Controls/AlphaForm.cs ================================================ using System; using System.Windows.Forms; using System.Runtime.InteropServices; using System.Drawing; using System.Drawing.Imaging; namespace WzComparerR2.Controls { public class AlphaForm : PerPixelAlphaForm { public AlphaForm() { TopMost = true; //TopLevel = false; ShowInTaskbar = false; Visible = true; AutoSize = false; MaximizeBox = false; MinimizeBox = false; HideOnHover = false; } private bool hideOnHover; private Rectangle captionRectangle; private Bitmap bitmap; /// /// 获取或设置一个值,表示窗体是否在鼠标滑过时自动隐藏。 /// public bool HideOnHover { get { return hideOnHover; } set { hideOnHover = value; } } /// /// 获取或设置一个Rectangle,表示窗体的虚拟标题栏区域。 /// public Rectangle CaptionRectangle { get { return captionRectangle; } set { captionRectangle = value; } } protected override void WndProc(ref Message m) { switch (m.Msg) { case 0x0084: /*WM_NCHITTEST*/ if (!hideOnHover) { Point hitPoint = this.PointToClient(new Point((int)m.LParam)); if (captionHitTest(hitPoint)) { m.Result = (IntPtr)2; //HTCAPTION return; } } else { m.Msg = 0x0018; /*WM_SHOWWINDOW*/ m.WParam = (IntPtr)0; break; } break; case 0x00A5: /*WM_NCRBUTTONUP*/ { Point hitPoint = this.PointToClient(new Point((int)m.LParam)); this.OnMouseClick(new MouseEventArgs(System.Windows.Forms.MouseButtons.Right, 1, hitPoint.X, hitPoint.Y, 0)); } return; case 0x00A3: /*WM_NCLBUTTONDBLCLK*/ //防止双击最大化 return; } base.WndProc(ref m); } protected override void OnKeyDown(KeyEventArgs e) { base.OnKeyDown(e); } protected override void OnFormClosing(FormClosingEventArgs e) { if (e.CloseReason == System.Windows.Forms.CloseReason.UserClosing) { e.Cancel = true; this.Hide(); return; } base.OnFormClosing(e); } protected virtual bool captionHitTest(Point point) { return this.captionRectangle.Contains(point); } public Bitmap Bitmap { get { return bitmap; } set { bitmap = value; } } } public class PerPixelAlphaForm : Form { public PerPixelAlphaForm() { // This form should not have A border or else Windows will clip it. FormBorderStyle = FormBorderStyle.None; } /// Changes the current Bitmap. public void SetBitmap(Bitmap bitmap) { SetBitmap(bitmap, 255); } /// Changes the current Bitmap with A custom opacity Level. Here is where all happens! public void SetBitmap(Bitmap bitmap, byte opacity) { if (bitmap.PixelFormat != PixelFormat.Format32bppArgb) throw new ApplicationException("The bitmap must be 32ppp with alpha-channel."); // The ideia of this is very simple, // 1. Create A compatible DC with screen; // 2. Select the Bitmap with 32bpp with alpha-channel in the compatible DC; // 3. Call the UpdateLayeredWindow. IntPtr screenDc = Win32.GetDC(IntPtr.Zero); IntPtr memDc = Win32.CreateCompatibleDC(screenDc); IntPtr hBitmap = IntPtr.Zero; IntPtr oldBitmap = IntPtr.Zero; try { hBitmap = bitmap.GetHbitmap(Color.FromArgb(0)); // grab A GDI handle from this GDI+ Bitmap oldBitmap = Win32.SelectObject(memDc, hBitmap); Win32.Size size = new Win32.Size(bitmap.Width, bitmap.Height); Win32.Point pointSource = new Win32.Point(0, 0); Win32.Point topPos = new Win32.Point(Left, Top); Win32.BLENDFUNCTION blend = new Win32.BLENDFUNCTION(); blend.BlendOp = Win32.AC_SRC_OVER; blend.BlendFlags = 0; blend.SourceConstantAlpha = opacity; blend.AlphaFormat = Win32.AC_SRC_ALPHA; Win32.UpdateLayeredWindow(Handle, screenDc, ref topPos, ref size, memDc, ref pointSource, 0, ref blend, Win32.ULW_ALPHA); //if (this.AutoSize) // this.Size = bitmap.Size; } finally { Win32.ReleaseDC(IntPtr.Zero, screenDc); if (hBitmap != IntPtr.Zero) { Win32.SelectObject(memDc, oldBitmap); //Windows.DeleteObject(hBitmap); // The documentation says that we have to use the Windows.DeleteObject... but since there is no such method I use the Normal DeleteObject from Win32 GDI and it's working fine without any resource leak. Win32.DeleteObject(hBitmap); } Win32.DeleteDC(memDc); } } protected override CreateParams CreateParams { get { CreateParams cp = base.CreateParams; cp.ExStyle |= 0x00080000; // This form has to have the WS_EX_LAYERED extended style return cp; } } } class Win32 { public enum Bool { False = 0, True }; [StructLayout(LayoutKind.Sequential)] public struct Point { public Int32 x; public Int32 y; public Point(Int32 x, Int32 y) { this.x = x; this.y = y; } } [StructLayout(LayoutKind.Sequential)] public struct Size { public Int32 cx; public Int32 cy; public Size(Int32 cx, Int32 cy) { this.cx = cx; this.cy = cy; } } [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct BLENDFUNCTION { public byte BlendOp; public byte BlendFlags; public byte SourceConstantAlpha; public byte AlphaFormat; } public const Int32 ULW_COLORKEY = 0x00000001; public const Int32 ULW_ALPHA = 0x00000002; public const Int32 ULW_OPAQUE = 0x00000004; public const byte AC_SRC_OVER = 0x00; public const byte AC_SRC_ALPHA = 0x01; [DllImport("user32.dll", ExactSpelling = true, SetLastError = true)] public static extern Bool UpdateLayeredWindow(IntPtr hwnd, IntPtr hdcDst, ref Point pptDst, ref Size psize, IntPtr hdcSrc, ref Point pprSrc, Int32 crKey, ref BLENDFUNCTION pblend, Int32 dwFlags); [DllImport("user32.dll", ExactSpelling = true, SetLastError = true)] public static extern IntPtr GetDC(IntPtr hWnd); [DllImport("user32.dll", ExactSpelling = true)] public static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC); [DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)] public static extern IntPtr CreateCompatibleDC(IntPtr hDC); [DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)] public static extern Bool DeleteDC(IntPtr hdc); [DllImport("gdi32.dll", ExactSpelling = true)] public static extern IntPtr SelectObject(IntPtr hDC, IntPtr hObject); [DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)] public static extern Bool DeleteObject(IntPtr hObject); } } ================================================ FILE: WzComparerR2.Common/Controls/AnimationClipOptions.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using Microsoft.Xna.Framework; namespace WzComparerR2.Controls { public class AnimationClipOptions { public int? StartTime { get; set; } public int? StopTime { get; set; } public int? Left { get; set; } public int? Top { get; set; } public int? Right { get; set; } public int? Bottom { get; set; } public int? OutputWidth { get; set; } public int? OutputHeight { get; set; } } } ================================================ FILE: WzComparerR2.Common/Controls/AnimationControl.Designer.cs ================================================ namespace WzComparerR2.Controls { partial class AnimationControl { /// /// 必需的设计器变量。 /// private System.ComponentModel.IContainer components = null; /// /// 清理所有正在使用的资源。 /// /// 如果应释放托管资源,为 true;否则为 false。 protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region 组件设计器生成的代码 /// /// 设计器支持所需的方法 - 不要修改 /// 使用代码编辑器修改此方法的内容。 /// private void InitializeComponent() { components = new System.ComponentModel.Container(); } #endregion } } ================================================ FILE: WzComparerR2.Common/Controls/AnimationControl.cs ================================================ using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Text; using System.Diagnostics; using System.Windows.Forms; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using WzComparerR2.Animation; using WzComparerR2.Rendering; namespace WzComparerR2.Controls { public partial class AnimationControl : GraphicsDeviceControl { public AnimationControl() { InitializeComponent(); this.MouseDown += AnimationControl_MouseDown; this.MouseUp += AnimationControl_MouseUp; this.MouseMove += AnimationControl_MouseMove; this.MouseWheel += AnimationControl_MouseWheel; this.Items = new List(); this.MouseDragEnabled = true; this.GlobalScale = 1f; this.timer = new Timer(); timer.Interval = 30; timer.Tick += Timer_Tick; timer.Enabled = true; this.sw = Stopwatch.StartNew(); } public List Items { get; private set; } public bool MouseDragEnabled { get; set; } public bool MouseDragSaveEnabled { get; set; } public bool ShowPositionGridOnDrag { get; set; } public float GlobalScale { get { return this.globalScale; } set { this.globalScale = MathHelper.Clamp(value, 0.1f, 10f); } } public bool IsPlaying { get { return this.timer.Enabled; } set { if (value) { this.lastUpdateTime = TimeSpan.Zero; this.sw.Restart(); } this.timer.Enabled = value; } } public int FrameInterval { get { return this.timer.Interval; } set { this.timer.Interval = value; } } private float globalScale; private Timer timer; private Stopwatch sw; private TimeSpan lastUpdateTime; private SpriteBatchEx sprite; private AnimationGraphics graphics; //拖拽相关 private MouseDragContext mouseDragContext; //离屏绘制相关 protected override void Initialize() { sprite = new SpriteBatchEx(this.GraphicsDevice); graphics = new AnimationGraphics(this.GraphicsDevice, sprite); } protected virtual void Update(TimeSpan elapsed) { foreach (var animation in this.Items) { if (animation != null) { animation.Update(elapsed); } } } public virtual void DrawBackground() { this.GraphicsDevice.Clear(this.BackColor.ToXnaColor()); } protected override void Draw() { //绘制背景色 this.DrawBackground(); //绘制场景 Matrix mtViewport = Matrix.CreateTranslation(this.Padding.Left, this.Padding.Top, 0); Matrix mtAnimation = Matrix.CreateScale(GlobalScale, GlobalScale, 1) * mtViewport; foreach (var animation in this.Items) { if (animation != null) { if (animation is FrameAnimator frameAni) { graphics.Draw(frameAni, mtAnimation); } else if (animation is ISpineAnimator spineAni) { graphics.Draw(spineAni, mtAnimation); } } } //绘制辅助内容 if (ShowPositionGridOnDrag && this.mouseDragContext.IsDragging && this.mouseDragContext.DraggingItem != null) { var pos = this.mouseDragContext.DraggingItem.Position; this.sprite.Begin(transformMatrix: mtViewport); this.sprite.DrawLine(new Point(0, pos.Y), new Point(this.Width, pos.Y), 1, Color.Indigo); this.sprite.DrawLine(new Point(pos.X, 0), new Point(pos.X, this.Height), 1, Color.Indigo); this.sprite.End(); } } public virtual AnimationItem GetItemAt(int x, int y) { for(int i = this.Items.Count - 1; i >= 0; i--) { var item = this.Items[i]; var bound = item.Measure(); var rect = new Rectangle( (int)Math.Round(item.Position.X + bound.X* this.GlobalScale), (int)Math.Round(item.Position.Y + bound.Y * this.GlobalScale), (int)Math.Round(bound.Width * this.GlobalScale), (int)Math.Round(bound.Height * this.GlobalScale)); if (rect.Contains(x, y)) { return item; } } return null; } #region EVENTS protected virtual void OnItemDragSave(AnimationItemEventArgs e) { } private void AnimationControl_MouseDown(object sender, MouseEventArgs e) { this.Focus(); if (this.MouseDragEnabled && e.Button == MouseButtons.Left) { var item = GetItemAt(e.X, e.Y); if (item != null) { this.mouseDragContext.IsDragging = true; this.mouseDragContext.MouseDownPoint = new Point(e.X, e.Y); this.mouseDragContext.DraggingItem = item; this.mouseDragContext.StartPosition = item.Position; } } if ((Control.ModifierKeys & Keys.Control) != 0 && e.Button == MouseButtons.Middle) { this.GlobalScale = 1f; } } private void AnimationControl_MouseUp(object sender, MouseEventArgs e) { if (this.MouseDragEnabled && e.Button == MouseButtons.Left) { this.mouseDragContext.IsDragging = false; } } private void AnimationControl_MouseMove(object sender, MouseEventArgs e) { if (this.MouseDragEnabled && this.mouseDragContext.IsDragging && this.mouseDragContext.DraggingItem != null) { this.mouseDragContext.DraggingItem.Position = new Point( e.X - mouseDragContext.MouseDownPoint.X + mouseDragContext.StartPosition.X, e.Y - mouseDragContext.MouseDownPoint.Y + mouseDragContext.StartPosition.Y); //处理拖拽保存 if (this.MouseDragSaveEnabled && (Control.ModifierKeys & Keys.Control) != 0) { var dragSize = SystemInformation.DragSize; var dragBox = new Rectangle(mouseDragContext.MouseDownPoint, new Point(dragSize.Width, dragSize.Height)); if (!dragBox.Contains(new Point(e.X, e.Y))) { var e2 = new AnimationItemEventArgs(this.mouseDragContext.DraggingItem); this.OnItemDragSave(e2); if (e2.Handled) { this.mouseDragContext.IsDragging = false; } } } } } private void AnimationControl_MouseWheel(object sender, MouseEventArgs e) { const int WHEEL_DELTA = 120; if ((Control.ModifierKeys & Keys.Control) != 0) { float wheelTicks = e.Delta / WHEEL_DELTA; float oldScale = this.GlobalScale; float newScale = oldScale * (1 + 0.1f * wheelTicks); if (oldScale.CompareTo(1f) * newScale.CompareTo(1f) == -1) // scaling cross 100% { newScale = 1f; } this.GlobalScale = newScale; } } private void Timer_Tick(object sender, EventArgs e) { var curTime = sw.Elapsed; var elapsed = curTime - lastUpdateTime; lastUpdateTime = curTime; if (this.Visible) { this.Update(elapsed); this.Invalidate(); } } #endregion private struct MouseDragContext { public bool IsDragging; public Point MouseDownPoint; public Point StartPosition; public AnimationItem DraggingItem; } } } ================================================ FILE: WzComparerR2.Common/Controls/AnimationItem.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using Microsoft.Xna.Framework; namespace WzComparerR2.Controls { public abstract class AnimationItem : ICloneable { public Point Position { get; set; } public virtual int Length { get { return 0; } } public abstract void Update(TimeSpan elapsed); public virtual Rectangle Measure() { return Rectangle.Empty; } public virtual void Reset() { } public virtual object Clone() { var aniItem = (AnimationItem)base.MemberwiseClone(); aniItem.Reset(); return aniItem; } } } ================================================ FILE: WzComparerR2.Common/Controls/AnimationItemEventArgs.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace WzComparerR2.Controls { public class AnimationItemEventArgs { public AnimationItemEventArgs(AnimationItem item) { this.Item = item; } /// /// 事件相关联的AnimationItem。 /// public AnimationItem Item { get; private set; } public bool Handled { get; set; } } } ================================================ FILE: WzComparerR2.Common/Controls/AnimationRecoder.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using WzComparerR2.Rendering; using WzComparerR2.Animation; namespace WzComparerR2.Controls { public class AnimationRecoder { public AnimationRecoder(GraphicsDevice graphicsDevice) { this._device = graphicsDevice; this._graphics = new AnimationGraphics(_device); this.Items = new List(); this.BackgroundColor = Color.Transparent; } public List Items { get; private set; } public System.Drawing.Color GdipBackgroundColor { get { var c = this.BackgroundColor; return System.Drawing.Color.FromArgb(c.A, c.R, c.G, c.B); } set { this.BackgroundColor = value.ToXnaColor(); } } public Color BackgroundColor { get; set; } public Texture2D BackgroundImage { get; set; } private GraphicsDevice _device; private RenderTargetBinding[] _oldBuffer; private RenderTarget2D _rt2d; private Rectangle _viewport; private Point _targetSize; private AnimationGraphics _graphics; private SpriteBatch _sb; private PngEffect _eff; public void Begin(Rectangle viewport, Point? targetSize = null) { this._targetSize = targetSize ?? viewport.Size; _rt2d = new RenderTarget2D(_device, _targetSize.X, _targetSize.Y, false, SurfaceFormat.Bgra32, DepthFormat.None, 0, RenderTargetUsage.PreserveContents); var binding = _device.GetRenderTargets(); _device.SetRenderTarget(_rt2d); this._viewport = viewport; this._oldBuffer = binding; this._sb = new SpriteBatch(_device); this._eff = new PngEffect(_device); } public void Update(TimeSpan elapsed) { foreach (var aniItem in this.Items) { aniItem.Update(elapsed); } } public void Draw() { System.Threading.Monitor.Enter(this._device); try { _device.SetRenderTarget(_rt2d); this.DrawAnimation(); } finally { _device.SetRenderTargets(_oldBuffer); System.Threading.Monitor.Exit(this._device); } } private void DrawAnimation() { if (this.BackgroundImage != null) { this._device.Clear(Color.Black); var rect = new Rectangle(0, 0, _targetSize.X, _targetSize.Y); _sb.Begin(blendState: BlendState.Opaque, samplerState: SamplerState.PointWrap); _sb.Draw(this.BackgroundImage, Vector2.Zero, rect, Color.White); _sb.End(); } else { this._device.Clear(this.BackgroundColor); } Matrix world = Matrix.CreateTranslation(-this._viewport.Left, -this._viewport.Top, 0); if (_targetSize != _viewport.Size) { world *= Matrix.CreateScale( 1f * _targetSize.X / _viewport.Width, 1f * _targetSize.Y / _viewport.Height, 1f); } foreach (var animation in this.Items.Where(_ani => _ani != null)) { if (animation is FrameAnimator framAni) { _graphics.Draw(framAni, world); } else if (animation is ISpineAnimator spineAni) { _graphics.Draw(spineAni, world); } } } public Texture2D GetPngTexture() { System.Threading.Monitor.Enter(this._device); try { var texture = new RenderTarget2D(_device, _rt2d.Width, _rt2d.Height, false, SurfaceFormat.Bgra32, DepthFormat.None); _device.SetRenderTarget(texture); _eff.AlphaMixEnabled = false; _device.Clear(Color.Transparent); _sb.Begin(SpriteSortMode.Immediate, BlendState.Opaque, SamplerState.LinearClamp, null, null, _eff, null); _sb.Draw(_rt2d, Vector2.Zero, Color.White); _sb.End(); return texture; } finally { _device.SetRenderTargets(_oldBuffer); System.Threading.Monitor.Exit(this._device); } } public Texture2D GetGifTexture(Color mixColor, int minMixedAlpha) { System.Threading.Monitor.Enter(this._device); try { var texture = new RenderTarget2D(_device, _rt2d.Width, _rt2d.Height, false, SurfaceFormat.Bgra32, DepthFormat.None); _device.SetRenderTarget(texture); _eff.AlphaMixEnabled = true; _eff.MixedColor = mixColor; _eff.MinMixedAlpha = minMixedAlpha; _device.Clear(Color.Transparent); _sb.Begin(SpriteSortMode.Immediate, BlendState.Opaque, SamplerState.LinearClamp, null, null, _eff, null); _sb.Draw(_rt2d, Vector2.Zero, Color.White); _sb.End(); return texture; } finally { _device.SetRenderTargets(_oldBuffer); System.Threading.Monitor.Exit(this._device); } } public void ResetAll() { foreach (var aniItem in this.Items) { aniItem.Reset(); } } public int GetMaxLength() { return this.Items.Select(aniItem => Math.Max(0, aniItem.Length)).Max(); } public int[] GetGifTimeLine(int preferredFrameDelay, int? maxFrameDelay = null) { if (preferredFrameDelay <= 0) { preferredFrameDelay = 1; } if (maxFrameDelay != null && maxFrameDelay < preferredFrameDelay) { maxFrameDelay = preferredFrameDelay; } // only calculate the first layer foreach (var animation in this.Items.Where(_ani => _ani != null)) { if (animation is FrameAnimator frameAni) { // we won't skip any frame even frame delay is greater than preferred delay var timeline = new List(); int totalLength = 0; foreach (var frame in frameAni.GetKeyFrames()) { totalLength += frame.Length; if (frame.Animated) { for (int ms = frame.Length; ms > 0;) { if (ms >= preferredFrameDelay) { timeline.Add(preferredFrameDelay); ms -= preferredFrameDelay; } else { if (timeline.Count > 0) { timeline[timeline.Count - 1] += ms; } else { // duration of the first frame less than minFrameDelay, but we can't simply ignore it. timeline.Add(ms); } ms = 0; } } } else { if (maxFrameDelay != null) { for (int ms = frame.Length; ms > 0;) { if (ms >= maxFrameDelay.Value) { timeline.Add(maxFrameDelay.Value); ms -= maxFrameDelay.Value; } else { timeline.Add(ms); ms = 0; } } } else { timeline.Add(frame.Length); } } } return timeline.ToArray(); } else if (animation is ISpineAnimator) { return null; } } return null; } public void End() { var binding = _device.GetRenderTargets(); _device.SetRenderTargets(_oldBuffer); this._device = null; this._oldBuffer = null; for (int i = 0; i < binding.Length; i++) { binding[i].RenderTarget.Dispose(); } _eff.Dispose(); _sb.Dispose(); } } } ================================================ FILE: WzComparerR2.Common/Controls/FrmProgressDialog.Designer.cs ================================================  namespace WzComparerR2.Controls { partial class FrmProgressDialog { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); this.labelX1 = new DevComponents.DotNetBar.LabelX(); this.progressBarX1 = new DevComponents.DotNetBar.Controls.ProgressBarX(); this.tableLayoutPanel1.SuspendLayout(); this.SuspendLayout(); // // tableLayoutPanel1 // this.tableLayoutPanel1.ColumnCount = 1; this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tableLayoutPanel1.Controls.Add(this.labelX1, 0, 0); this.tableLayoutPanel1.Controls.Add(this.progressBarX1, 0, 1); this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0); this.tableLayoutPanel1.Name = "tableLayoutPanel1"; this.tableLayoutPanel1.RowCount = 2; this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F)); this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F)); this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F)); this.tableLayoutPanel1.Size = new System.Drawing.Size(284, 51); this.tableLayoutPanel1.TabIndex = 0; // // labelX1 // this.labelX1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); // // // this.labelX1.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.labelX1.Location = new System.Drawing.Point(3, 3); this.labelX1.Name = "labelX1"; this.labelX1.Size = new System.Drawing.Size(278, 19); this.labelX1.TabIndex = 0; this.labelX1.Text = "Message"; this.labelX1.MouseClick += new System.Windows.Forms.MouseEventHandler(this.labelX1_MouseClick); // // progressBarX1 // this.progressBarX1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); // // // this.progressBarX1.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.progressBarX1.Location = new System.Drawing.Point(3, 28); this.progressBarX1.Name = "progressBarX1"; this.progressBarX1.Size = new System.Drawing.Size(278, 20); this.progressBarX1.TabIndex = 1; this.progressBarX1.Text = "progressBarX1"; // // FrmProgressDialog // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(284, 51); this.Controls.Add(this.tableLayoutPanel1); this.DoubleBuffered = true; this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow; this.MaximizeBox = false; this.MinimizeBox = false; this.Name = "FrmProgressDialog"; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; this.Text = "Processing"; this.tableLayoutPanel1.ResumeLayout(false); this.ResumeLayout(false); } #endregion private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; private DevComponents.DotNetBar.LabelX labelX1; private DevComponents.DotNetBar.Controls.ProgressBarX progressBarX1; } } ================================================ FILE: WzComparerR2.Common/Controls/FrmProgressDialog.cs ================================================ using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using DevComponents.DotNetBar; namespace WzComparerR2.Controls { public partial class FrmProgressDialog : DevComponents.DotNetBar.Office2007Form { public FrmProgressDialog() { InitializeComponent(); } public string Message { get { return this.labelX1.Text; } set { this.labelX1.Text = value; } } public int Progress { get { return this.progressBarX1.Value; } set { this.progressBarX1.Value = value; } } public int ProgressMin { get { return this.progressBarX1.Minimum; } set { this.progressBarX1.Minimum = value; } } public int ProgressMax { get { return this.progressBarX1.Maximum; } set { this.progressBarX1.Maximum = value; } } public string FullMessage { get; set; } private void labelX1_MouseClick(object sender, MouseEventArgs e) { if (e.Button == MouseButtons.Right) { Clipboard.SetText(this.FullMessage ?? this.Message); ToastNotification.Show(this, "已复制到剪切板。", 1000, eToastPosition.TopCenter); } } } } ================================================ FILE: WzComparerR2.Common/Controls/FrmProgressDialog.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ================================================ FILE: WzComparerR2.Common/Controls/GraphicsDeviceControl.cs ================================================ #region File Description //----------------------------------------------------------------------------- // GraphicsDeviceControl.cs // // Microsoft XNA Community Game Platform // Copyright (C) Microsoft Corporation. All rights reserved. //----------------------------------------------------------------------------- #endregion #region Using Statements using System; using System.Drawing; using System.Reflection; using System.Windows.Forms; using Microsoft.Xna.Framework.Graphics; #endregion namespace WzComparerR2.Controls { // System.Drawing and the XNA Framework both define Color and Rectangle // types. To avoid conflicts, we specify exactly which ones to use. using Color = System.Drawing.Color; using Rectangle = Microsoft.Xna.Framework.Rectangle; /// /// Custom control uses the XNA Framework GraphicsDevice to render onto /// a Windows Form. Derived classes can override the Initialize and Draw /// methods to add their own drawing code. /// abstract public class GraphicsDeviceControl : Control { #region Fields // However many GraphicsDeviceControl instances you have, they all share // the same underlying GraphicsDevice, managed by this helper service. GraphicsDeviceService graphicsDeviceService; SwapChainRenderTarget swapChainRT; #endregion #region Properties /// /// Gets a GraphicsDevice that can be used to draw onto this control. /// public GraphicsDevice GraphicsDevice { get { return graphicsDeviceService.GraphicsDevice; } } /// /// Gets an IServiceProvider containing our IGraphicsDeviceService. /// This can be used with components such as the ContentManager, /// which use this service to look up the GraphicsDevice. /// public ServiceContainer Services { get { return services; } } ServiceContainer services = new ServiceContainer(); #endregion #region Initialization /// /// Initializes the control. /// protected override void OnCreateControl() { // Don't initialize the graphics device if we are running in the designer. if (!DesignMode) { this.graphicsDeviceService = GraphicsDeviceService.AddRef(Handle, ClientSize.Width, ClientSize.Height); this.swapChainRT = new SwapChainRenderTarget(graphicsDeviceService.GraphicsDevice, this.Handle, ClientSize.Width, ClientSize.Height); this.swapChainRT.Disposing += SwapChainRT_Disposing; // Register the service, so components like ContentManager can find it. services.AddService(graphicsDeviceService); // Give derived classes a chance to initialize themselves. Initialize(); } base.OnCreateControl(); } /// /// Disposes the control. /// protected override void Dispose(bool disposing) { if (this.swapChainRT != null && !this.swapChainRT.IsDisposed) { this.swapChainRT.Dispose(); this.swapChainRT = null; } if (this.graphicsDeviceService != null) { this.graphicsDeviceService.Release(disposing); this.graphicsDeviceService = null; } base.Dispose(disposing); } private void SwapChainRT_Disposing(object sender, EventArgs e) { // fix monogame memory leak bug. if (sender is SwapChainRenderTarget swapChainRT) { var backBufferField = sender.GetType().GetField("_backBuffer", BindingFlags.Instance | BindingFlags.NonPublic); if (backBufferField != null && backBufferField.GetValue(swapChainRT) is SharpDX.Direct3D11.Resource d3dResource) { SharpDX.Utilities.Dispose(ref d3dResource); backBufferField.SetValue(swapChainRT, null); } } } #endregion #region Paint /// /// Redraws the control in response to a WinForms paint message. /// protected override void OnPaint(PaintEventArgs e) { try { if (!DesignMode) { System.Threading.Monitor.Enter(this.GraphicsDevice); } string beginDrawError = this.BeginDraw(); if (string.IsNullOrEmpty(beginDrawError)) { // Draw the control using the GraphicsDevice. Draw(); EndDraw(); } else { // If BeginDraw failed, show an error message using System.Drawing. PaintUsingSystemDrawing(e.Graphics, beginDrawError); } } finally { if (!DesignMode) { System.Threading.Monitor.Exit(this.GraphicsDevice); } } } /// /// Attempts to begin drawing the control. Returns an error message string /// if this was not possible, which can happen if the graphics device is /// lost, or if we are running inside the Form designer. /// string BeginDraw() { // If we have no graphics device, we must be running in the designer. if (graphicsDeviceService == null) { return Text + "\n\n" + GetType(); } // Make sure the graphics device is big enough, and is not lost. string deviceResetError = HandleDeviceReset(); if (!string.IsNullOrEmpty(deviceResetError)) { return deviceResetError; } // Many GraphicsDeviceControl instances can be sharing the same // GraphicsDevice. The device backbuffer will be resized to fit the // largest of these controls. But what if we are currently drawing // a smaller control? To avoid unwanted stretching, we set the // viewport to only use the top left portion of the full backbuffer. Viewport viewport = new Viewport(); viewport.X = 0; viewport.Y = 0; viewport.Width = ClientSize.Width; viewport.Height = ClientSize.Height; viewport.MinDepth = 0; viewport.MaxDepth = 1; GraphicsDevice.Viewport = viewport; GraphicsDevice.PresentationParameters.BackBufferWidth = viewport.Width; GraphicsDevice.PresentationParameters.BackBufferHeight = viewport.Height; GraphicsDevice.SetRenderTarget(swapChainRT); return null; } /// /// Ends drawing the control. This is called after derived classes /// have finished their Draw method, and is responsible for presenting /// the finished image onto the screen, using the appropriate WinForms /// control handle to make sure it shows up in the right place. /// void EndDraw() { try { Rectangle sourceRectangle = new Rectangle(0, 0, ClientSize.Width, ClientSize.Height); //GraphicsDevice.Present(sourceRectangle, null, this.Handle); this.swapChainRT.Present(); GraphicsDevice.SetRenderTarget(null); } catch { // Present might throw if the device became lost while we were // drawing. The lost device will be handled by the next BeginDraw, // so we just swallow the exception. } } /// /// Helper used by BeginDraw. This checks the graphics device status, /// making sure it is big enough for drawing the current control, and /// that the device is not lost. Returns an error string if the device /// could not be reset. /// string HandleDeviceReset() { bool deviceNeedsReset = false; var clientSize = this.ClientSize; switch (GraphicsDevice.GraphicsDeviceStatus) { case GraphicsDeviceStatus.Lost: // If the graphics device is lost, we cannot use it at all. return "Graphics device lost"; case GraphicsDeviceStatus.NotReset: // If device is in the not-reset state, we should try to reset it. deviceNeedsReset = true; break; default: // If the device state is ok, check whether it is big enough. PresentationParameters pp = GraphicsDevice.PresentationParameters; deviceNeedsReset = (clientSize.Width != pp.BackBufferWidth) || (clientSize.Height != pp.BackBufferHeight); break; } // Do we need to reset the device? if (deviceNeedsReset) { try { this.swapChainRT.Dispose(); this.swapChainRT = new SwapChainRenderTarget( this.graphicsDeviceService.GraphicsDevice, this.Handle, clientSize.Width, clientSize.Height); this.swapChainRT.Disposing += SwapChainRT_Disposing; } catch (Exception e) { return "Graphics device reset failed\n\n" + e; } } return null; } /// /// If we do not have a valid graphics device (for instance if the device /// is lost, or if we are running inside the Form designer), we must use /// regular System.Drawing method to display a status message. /// protected virtual void PaintUsingSystemDrawing(Graphics graphics, string text) { graphics.Clear(Color.CornflowerBlue); using (Brush brush = new SolidBrush(Color.Black)) { using (StringFormat format = new StringFormat()) { format.Alignment = StringAlignment.Center; format.LineAlignment = StringAlignment.Center; graphics.DrawString(text, Font, brush, ClientRectangle, format); } } } /// /// Ignores WinForms paint-background messages. The default implementation /// would clear the control to the current background color, causing /// flickering when our OnPaint implementation then immediately draws some /// other color over the top using the XNA Framework GraphicsDevice. /// protected override void OnPaintBackground(PaintEventArgs pevent) { } #endregion #region Abstract Methods /// /// Derived classes override this to initialize their drawing code. /// protected abstract void Initialize(); /// /// Derived classes override this to draw themselves using the GraphicsDevice. /// protected abstract void Draw(); #endregion } } ================================================ FILE: WzComparerR2.Common/Controls/GraphicsDeviceService.cs ================================================ #region File Description //----------------------------------------------------------------------------- // GraphicsDeviceService.cs // // Microsoft XNA Community Game Platform // Copyright (C) Microsoft Corporation. All rights reserved. //----------------------------------------------------------------------------- #endregion #region Using Statements using System; using System.Threading; using Microsoft.Xna.Framework.Graphics; #endregion // The IGraphicsDeviceService interface requires a DeviceCreated event, but we // always just create the device inside our constructor, so we have no place to // raise that event. The C# compiler warns us that the event is never used, but // we don't care so we just disable this warning. #pragma warning disable 67 namespace WzComparerR2.Controls { /// /// Helper class responsible for creating and managing the GraphicsDevice. /// All GraphicsDeviceControl instances share the same GraphicsDeviceService, /// so even though there can be many controls, there will only ever be a single /// underlying GraphicsDevice. This implements the standard IGraphicsDeviceService /// interface, which provides notification events for when the device is reset /// or disposed. /// class GraphicsDeviceService : IGraphicsDeviceService { #region Fields // Singleton device service instance. static GraphicsDeviceService singletonInstance; // Keep track of how many controls are sharing the singletonInstance. static int referenceCount; #endregion /// /// Constructor is private, because this is a singleton class: /// client controls should use the public AddRef method instead. /// GraphicsDeviceService(IntPtr windowHandle, int width, int height) { parameters = new PresentationParameters(); parameters.BackBufferWidth = Math.Max(width, 1); parameters.BackBufferHeight = Math.Max(height, 1); parameters.BackBufferFormat = SurfaceFormat.Color; parameters.DepthStencilFormat = DepthFormat.Depth24; parameters.DeviceWindowHandle = windowHandle; parameters.PresentationInterval = PresentInterval.Immediate; parameters.IsFullScreen = false; graphicsDevice = new GraphicsDevice(GraphicsAdapter.DefaultAdapter, GraphicsProfile.HiDef, parameters); } /// /// Gets a reference to the singleton instance. /// public static GraphicsDeviceService AddRef(IntPtr windowHandle, int width, int height) { // Increment the "how many controls sharing the device" reference count. if (Interlocked.Increment(ref referenceCount) == 1) { // If this is the first control to start using the // device, we must create the singleton instance. singletonInstance = new GraphicsDeviceService(IntPtr.Zero, 1, 1); } return singletonInstance; } /// /// Releases a reference to the singleton instance. /// public void Release(bool disposing) { // Decrement the "how many controls sharing the device" reference count. if (Interlocked.Decrement(ref referenceCount) == 0) { // If this is the last control to finish using the // device, we should dispose the singleton instance. if (disposing) { if (DeviceDisposing != null) DeviceDisposing(this, EventArgs.Empty); graphicsDevice.Dispose(); } graphicsDevice = null; } } /// /// Resets the graphics device to whichever is bigger out of the specified /// resolution or its current size. This behavior means the device will /// demand-grow to the largest of all its GraphicsDeviceControl clients. /// public void ResetDevice(int width, int height) { if (DeviceResetting != null) DeviceResetting(this, EventArgs.Empty); parameters.BackBufferWidth = Math.Max(1, width); parameters.BackBufferHeight = Math.Max(1, height); graphicsDevice.Reset(parameters); if (DeviceReset != null) DeviceReset(this, EventArgs.Empty); } /// /// Gets the current graphics device. /// public GraphicsDevice GraphicsDevice { get { return graphicsDevice; } } GraphicsDevice graphicsDevice; // Store the current device settings. PresentationParameters parameters; // IGraphicsDeviceService events. public event EventHandler DeviceCreated; public event EventHandler DeviceDisposing; public event EventHandler DeviceReset; public event EventHandler DeviceResetting; } } ================================================ FILE: WzComparerR2.Common/Controls/ProgressDialog.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; namespace WzComparerR2.Controls { public static class ProgressDialog { public static DialogResult Show(IWin32Window owner, string text, string caption, bool closeOnComplete, bool closeOnError, Func factory) { return new ProgressDialogContext(text, caption, closeOnComplete, closeOnError, factory).ShowDialog(owner); } } } ================================================ FILE: WzComparerR2.Common/Controls/ProgressDialogContext.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows.Forms; using System.Threading; using System.Threading.Tasks; namespace WzComparerR2.Controls { public interface IProgressDialogContext { string Message { get; set; } string FullMessage { get; set; } int Progress { get; set; } int ProgressMin { get; set; } int ProgressMax { get; set; } } internal class ProgressDialogContext : IProgressDialogContext { internal ProgressDialogContext( string text, string caption, bool closeOnComplete, bool closeOnError, Func factory) { this.dialog = new FrmProgressDialog(); if (caption != null) { this.dialog.Text = caption; } if (text != null) { this.dialog.Message = text; } this.cancellationTokenSource = new CancellationTokenSource(); this.closeOnComplete = closeOnComplete; this.closeOnError = closeOnError; this.factory = factory; } public string Message { get { return this.dialog.Message; } set { this.dialog.Message = value; } } public string FullMessage { get { return this.dialog.FullMessage; } set { this.dialog.FullMessage = value; } } public int Progress { get { return this.dialog.Progress; } set { this.dialog.Progress = value; } } public int ProgressMin { get { return this.dialog.ProgressMin; } set { this.dialog.ProgressMin = value; } } public int ProgressMax { get { return this.dialog.ProgressMax; } set { this.dialog.ProgressMax = value; } } private readonly FrmProgressDialog dialog; private readonly CancellationTokenSource cancellationTokenSource; private readonly bool closeOnComplete; private readonly bool closeOnError; private readonly Func factory; private DialogResult dialogResult; internal DialogResult ShowDialog(IWin32Window owner) { this.dialog.Load += Dialog_Load; this.dialog.FormClosing += Dialog_FormClosing; this.dialogResult = DialogResult.None; dialog.ShowDialog(owner); return this.dialogResult; } private async void Dialog_Load(object sender, EventArgs e) { try { if (this.factory != null) { await this.factory(this, this.cancellationTokenSource.Token); } this.dialog.FormClosing -= Dialog_FormClosing; this.dialogResult = DialogResult.OK; if (this.closeOnComplete) { this.dialog.Close(); } } catch { this.OnCancel(); if (this.closeOnError) { this.dialog.Close(); } } } private void Dialog_FormClosing(object sender, FormClosingEventArgs e) { this.OnCancel(); } private void OnCancel() { this.dialogResult = DialogResult.Cancel; if (!this.cancellationTokenSource.IsCancellationRequested) { this.cancellationTokenSource.Cancel(); } } } } ================================================ FILE: WzComparerR2.Common/Controls/ServiceContainer.cs ================================================ #region File Description //----------------------------------------------------------------------------- // ServiceContainer.cs // // Microsoft XNA Community Game Platform // Copyright (C) Microsoft Corporation. All rights reserved. //----------------------------------------------------------------------------- #endregion #region Using Statements using System; using System.Collections.Generic; #endregion namespace WzComparerR2.Controls { /// /// Container class implements the IServiceProvider interface. This is used /// to pass shared services between different components, for instance the /// ContentManager uses it to locate the IGraphicsDeviceService implementation. /// public class ServiceContainer : IServiceProvider { Dictionary services = new Dictionary(); /// /// Adds a new service to the collection. /// public void AddService(T service) { services.Add(typeof(T), service); } /// /// Looks up the specified service. /// public object GetService(Type serviceType) { object service; services.TryGetValue(serviceType, out service); return service; } } } ================================================ FILE: WzComparerR2.Common/Encoders/BuildInApngEncoder.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Drawing; using System.Runtime.InteropServices; namespace WzComparerR2.Encoders { public class BuildInApngEncoder : GifEncoder { public BuildInApngEncoder() { } public bool OptimizeEnabled { get; set; } private IntPtr handle; public override GifEncoderCompatibility Compatibility => new GifEncoderCompatibility() { IsFixedFrameRate = false, MinFrameDelay = 1, MaxFrameDelay = 655350, FrameDelayStep = 1, AlphaSupportMode = AlphaSupportMode.FullAlpha, DefaultExtension = ".png", SupportedExtensions = new[] { ".png" }, }; public override void Init(string fileName, int width, int height) { base.Init(fileName, width, height); var err = apng_init(fileName, width, height, out handle); if (err != ApngError.Success) { throw new Exception($"Apng error: {err}."); } } public override void AppendFrame(IntPtr pBuffer, int delay) { var err = apng_append_frame(handle, pBuffer, 0, 0, Width, Height, Width * 4, delay, OptimizeEnabled); if (err != ApngError.Success) { throw new Exception($"Apng error: {err}."); } } protected override void Dispose(bool disposing) { if (disposing) { if (handle != IntPtr.Zero) { apng_write_end(handle); apng_destroy(ref handle); handle = IntPtr.Zero; } } base.Dispose(disposing); } enum ApngError : int { Success = 0, ContextCreateFailed = 1, FileError = 2, ArgumentError = 3, MemoryError = 4, }; [DllImport("libapng.dll")] static extern ApngError apng_init([MarshalAs(UnmanagedType.LPWStr)] string fileName, int width, int height, out IntPtr ppEnc); [DllImport("libapng.dll")] static extern ApngError apng_append_frame(IntPtr pEnc, IntPtr pData, int x, int y, int width, int height, int stride, int delay_ms, bool optimize); [DllImport("libapng.dll")] static extern void apng_write_end(IntPtr pEnc); [DllImport("libapng.dll")] static extern void apng_destroy(ref IntPtr ppEnc); } } ================================================ FILE: WzComparerR2.Common/Encoders/BuildInGifEncoder.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO; using System.Drawing; using System.Drawing.Imaging; using ImageManipulation; namespace WzComparerR2.Encoders { public class BuildInGifEncoder : GifEncoder { public BuildInGifEncoder() { mStream = new MemoryStream(); quantizer = new OctreeQuantizer(255, 8); } public override GifEncoderCompatibility Compatibility => new GifEncoderCompatibility() { IsFixedFrameRate = false, MinFrameDelay = 10, MaxFrameDelay = 655350, FrameDelayStep = 10, AlphaSupportMode = AlphaSupportMode.OneBitAlpha, DefaultExtension = ".gif", SupportedExtensions = new[] { ".gif" }, }; private BinaryWriter bWriter; private MemoryStream mStream; private Quantizer quantizer; private static readonly byte[] gifHeader = new byte[] { 0x47, 0x49, 0x46, 0x38, 0x39, 0x61 };//GIF89a private static readonly byte[] logicalScreen = new byte[] { 0x70, 0x00, 0x00 };//无全局色彩表 无视背景色 无视像素纵横比 private static readonly byte[] appExtension = new byte[] { 0x21,0xff,0x0b, //块标志 0x4e,0x45,0x54,0x53,0x43,0x41,0x50,0x45,0x32,0x2e,0x30, //NETSCAPE2.0 0x03,0x01,0x00,0x00,0x00};//循环信息 其他信息 private static readonly byte[] gifEnd = new byte[] { 0x3b };//结束信息 public override void Init(string fileName, int width, int height) { base.Init(fileName, width, height); bWriter = new BinaryWriter(File.Create(fileName)); WriteHeader(); } public override void AppendFrame(Bitmap image, int delay) { mStream.SetLength(0); mStream.Position = 0; using (var tempGif = quantizer.Quantize(image)) { tempGif.Save(mStream, ImageFormat.Gif); } byte[] tempArray = mStream.GetBuffer(); // 781开始为Graphic Control Extension块 标志为21 F9 04 tempArray[784] = 0x09; //图像刷新时屏幕返回初始帧 貌似不打会bug 意味不明 测试用 delay = delay / 10; tempArray[785] = (byte)(delay & 0xff); tempArray[786] = (byte)(delay >> 8 & 0xff); //写入2字节的帧delay // 787为透明色索引 788为块结尾0x00 tempArray[787] = 0xff; // 789开始为Image Descriptor块 标志位2C // 790~793为帧偏移大小 默认为0 // 794~797为帧图像大小 默认他 tempArray[798] = (byte)(tempArray[798] | 0X87); //本地色彩表标志 //写入到gif文件 bWriter.Write(tempArray, 781, 18); bWriter.Write(tempArray, 13, 768); bWriter.Write(tempArray, 799, (int)mStream.Length - 800); } public override void AppendFrame(IntPtr pBuffer, int delay) { using (var bmp = new Bitmap(Width, Height, Width * 4, PixelFormat.Format32bppArgb, pBuffer)) { AppendFrame(bmp, delay); } } private void WriteHeader() { //写入gif头信息 bWriter.Write(gifHeader); bWriter.Write((ushort)Width); bWriter.Write((ushort)Height); bWriter.Write(logicalScreen); //写入循环标记 bWriter.Write(appExtension); } protected override void Dispose(bool disposing) { if (disposing) { if (bWriter != null) { bWriter.Write(gifEnd); bWriter.Close(); bWriter = null; } } if (mStream != null) { mStream.Dispose(); mStream = null; } } } } ================================================ FILE: WzComparerR2.Common/Encoders/FFmpegEncoder.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Drawing; using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using System.IO; using System.IO.Pipes; namespace WzComparerR2.Encoders { public class FFmpegEncoder : GifEncoder { public static readonly string DefaultExecutionFileName = "ffmpeg"; /// /// Default ffmpeg argument format to encode mp4 with avc H.264 video format. /// public static readonly string DefaultArgumentFormat = @$"-y -f rawvideo -pixel_format bgra -s %w*%h -r 1000/%t -i ""%i"" -vf ""crop=trunc(iw/2)*2:trunc(ih/2)*2"" -vcodec libx264 -pix_fmt yuv420p ""%o"""; public static readonly string DefaultOutputFileExtension = ".mp4"; public FFmpegEncoder() { } public string FFmpegBinPath { get; set; } public string FFmpegArgumentFormat { get; set; } public string OutputFileExtension { get; set; } private Process ffmpegProc; private NamedPipeServerStream server; private CancellationTokenSource ffmpegProcExitedCts; private StringBuilder ffmpegStdout = new StringBuilder(); private StringBuilder ffmpegStderr = new StringBuilder(); private bool disposed; public override GifEncoderCompatibility Compatibility => new GifEncoderCompatibility() { IsFixedFrameRate = true, MinFrameDelay = 1, MaxFrameDelay = int.MaxValue, FrameDelayStep = 1, AlphaSupportMode = AlphaSupportMode.NoAlpha, DefaultExtension = string.IsNullOrEmpty(OutputFileExtension) ? DefaultOutputFileExtension : OutputFileExtension, SupportedExtensions = new[] { ".*" }, }; public unsafe override void AppendFrame(IntPtr pBuffer, int delay) { if (server == null && ffmpegProc == null) { StartFFmpeg(delay).Wait(); } int frameDataLen = Width * Height * 4; using UnmanagedMemoryStream ms = new((byte*)pBuffer.ToPointer(), frameDataLen, frameDataLen, FileAccess.Read); ms.CopyToAsync(server, 32768, ffmpegProcExitedCts?.Token ?? default).ConfigureAwait(false).GetAwaiter().GetResult(); } private async Task StartFFmpeg(int delay) { // create random pipe name string pipeName = $"{nameof(FFmpegEncoder)}-{Process.GetCurrentProcess().Id}-{(uint)Environment.TickCount}"; // create named-pipe server and listen NamedPipeServerStream server = new(pipeName, PipeDirection.Out, 1, PipeTransmissionMode.Byte); var task1 = server.WaitForConnectionAsync(); // start ffmpeg ProcessStartInfo psi = new() { FileName = string.IsNullOrEmpty(FFmpegBinPath) ? DefaultExecutionFileName : FFmpegBinPath, Arguments = SubstituteParams(string.IsNullOrEmpty(FFmpegArgumentFormat) ? DefaultArgumentFormat : FFmpegArgumentFormat, @$"\\.\pipe\{pipeName}", Width, Height, delay, FileName), WorkingDirectory = Environment.CurrentDirectory, UseShellExecute = false, RedirectStandardOutput = true, RedirectStandardError = true, CreateNoWindow = true, }; Process ffmpegProc = new() { StartInfo = psi, }; ffmpegProc.OutputDataReceived += FFmpegProc_OutputDataReceived; ffmpegProc.ErrorDataReceived += FFmpegProc_ErrorDataReceived; ffmpegProc.Exited += FFmpegProc_Exited; ffmpegProc.Start(); ffmpegProc.BeginOutputReadLine(); ffmpegProc.BeginErrorReadLine(); await task1.ConfigureAwait(false); this.server = server; this.ffmpegProc = ffmpegProc; ffmpegProcExitedCts = new CancellationTokenSource(); } private void FFmpegProc_OutputDataReceived(object sender, DataReceivedEventArgs e) { ffmpegStdout?.AppendLine(e.Data); } private void FFmpegProc_ErrorDataReceived(object sender, DataReceivedEventArgs e) { ffmpegStderr?.AppendLine(e.Data); } private void FFmpegProc_Exited(object sender, EventArgs e) { ffmpegProcExitedCts?.Cancel(); } protected override void Dispose(bool disposing) { base.Dispose(disposing); if (!disposed) { if (disposing) { if (server != null) { if (server.IsConnected) { server.Flush(); server.Disconnect(); } server.Dispose(); } if (ffmpegProc != null) { if (!ffmpegProc.HasExited) { ffmpegProc.WaitForExit(); } ffmpegProc.Dispose(); } if (ffmpegProcExitedCts != null) { ffmpegProcExitedCts.Dispose(); } } ffmpegStdout = null; ffmpegStderr = null; disposed = true; } } private string SubstituteParams(string format, string inputFileName, int width, int height, int frameDelay, string outputFileName) { // %i: inputFileName // %w: width // %h: height // %t: frameDelay // %o: outputFileName // %%: escape '%' char return Regex.Replace(format, "%[iwhto%]", match => match.Value switch { "%i" => inputFileName, "%w" => width.ToString(), "%h" => height.ToString(), "%t" => frameDelay.ToString(), "%o" => outputFileName, "%%" => "%", _ => throw new FormatException($"Unknown format: {match.Value}") }); } } } ================================================ FILE: WzComparerR2.Common/Encoders/GifEncoder.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Drawing; using System.Drawing.Imaging; namespace WzComparerR2.Encoders { public abstract class GifEncoder : IDisposable { protected GifEncoder() { } public string FileName { get; private set; } public int Width { get; private set; } public int Height { get; private set; } public virtual string Name { get { return GetType().Name; } } public abstract GifEncoderCompatibility Compatibility { get; } public virtual void Init(string fileName, int width, int height) { FileName = fileName; Width = width; Height = height; } public virtual void AppendFrame(Bitmap image, int delay) { BitmapData data = image.LockBits(new Rectangle(Point.Empty, image.Size), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb); AppendFrame(data.Scan0, delay); image.UnlockBits(data); } public abstract void AppendFrame(IntPtr pBuffer, int delay); public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { } ~GifEncoder() { Dispose(false); } } } ================================================ FILE: WzComparerR2.Common/Encoders/GifEncoderCompatibility.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace WzComparerR2.Encoders { public class GifEncoderCompatibility { public GifEncoderCompatibility() { } public bool IsFixedFrameRate { get; set; } public int MinFrameDelay { get; set; } public int MaxFrameDelay { get; set; } public int FrameDelayStep { get; set; } public AlphaSupportMode AlphaSupportMode { get; set; } public string DefaultExtension { get; set; } public IReadOnlyList SupportedExtensions { get; set; } } public enum AlphaSupportMode { NoAlpha, OneBitAlpha, FullAlpha } } ================================================ FILE: WzComparerR2.Common/Encoders/IndexGifEncoder.cs ================================================ using System; using System.Drawing; using System.Drawing.Imaging; using System.Runtime.InteropServices; namespace WzComparerR2.Encoders { public class IndexGifEncoder : GifEncoder { public IndexGifEncoder() { } private IntPtr encoder_pointer; public override GifEncoderCompatibility Compatibility => new GifEncoderCompatibility() { IsFixedFrameRate = false, MinFrameDelay = 10, MaxFrameDelay = 655350, FrameDelayStep = 10, AlphaSupportMode = AlphaSupportMode.OneBitAlpha, DefaultExtension = ".gif", SupportedExtensions = new[] { ".gif" }, }; public override void Init(string fileName, int width, int height) { base.Init(fileName, width, height); encoder_pointer = construct(fileName, width, height, 255, 0); } public override void AppendFrame(IntPtr pBuffer, int delay) { encoder.append_frame(pBuffer, delay, encoder_pointer); } protected override void Dispose(bool disposing) { if (disposing) { if (encoder_pointer != IntPtr.Zero) { encoder.destruct(encoder_pointer); encoder_pointer = IntPtr.Zero; } } } private gif_encoder_structure encoder { get { return (gif_encoder_structure)Marshal.PtrToStructure(encoder_pointer, typeof(gif_encoder_structure)); } } [DllImport("libgif.dll", EntryPoint = "#1", CharSet = CharSet.Unicode)] private extern static IntPtr construct(string location, int width, int height, int maxColor, int backColor); private delegate void gif_encoder_destruct(IntPtr encoder_pointer); private delegate void gif_encoder_append_frame(IntPtr pixels, int delay, IntPtr encoder_pointer); [StructLayout(LayoutKind.Sequential)] private struct gif_encoder_structure { public gif_encoder_destruct destruct; public gif_encoder_append_frame append_frame; } } } ================================================ FILE: WzComparerR2.Common/Gif.cs ================================================ using System; using System.Collections.Generic; using System.Drawing; using System.Drawing.Imaging; using System.IO; using WzComparerR2.Encoders; using WzComparerR2.WzLib; namespace WzComparerR2.Common { public class Gif { public Gif() { this.Frames = new List(); } public List Frames { get; private set; } public Bitmap EncodeGif(Color backgrnd) { return EncodeGif(backgrnd, 0x00); } public Bitmap EncodeGif(Color backgrnd, int minAlpha) { return EncodeGif(backgrnd, minAlpha, 0, this.Frames.Count); } public Bitmap EncodeGif(Color backgrnd, int minAlpha, int startIndex, int frameCount) { if (frameCount <= 0) { return null; } return EncodeGif(backgrnd, minAlpha, startIndex, frameCount); } public Bitmap EncodeGif2(Color backgrnd, int minAlpha) { return EncodeGif2(backgrnd, minAlpha, 0, this.Frames.Count); } public Bitmap EncodeGif2(Color backgrnd, int minAlpha, int startIndex, int frameCount) { if (frameCount <= 0) { return null; } return EncodeGif(backgrnd, minAlpha, startIndex, frameCount); } private Bitmap EncodeGif(Color backgrnd, int minAlpha, int startIndex, int frameCount) where T : GifEncoder { //预判大小 Rectangle rect = this.GetRect(); Bitmap canvas = new Bitmap(rect.Width, rect.Height, PixelFormat.Format32bppArgb); string tempFileName = Path.GetTempFileName(); GifEncoder enc = (GifEncoder)Activator.CreateInstance(typeof(T), tempFileName, rect.Width, rect.Height); //写入帧信息 for (int i = startIndex, j = startIndex + frameCount; i < j; i++) { if (i >= this.Frames.Count) break; IGifFrame frame = this.Frames[i]; if (frame == null) { continue; } PrepareFrame(canvas, frame, rect, backgrnd, minAlpha); enc.AppendFrame(canvas, frame.Delay); } enc.Dispose(); return Image.FromFile(tempFileName) as Bitmap; } public Rectangle GetRect() { Rectangle rect = Rectangle.Empty; foreach (var f in this.Frames) { var newRect = ((IGifFrame)f).Region; rect = rect.Size.IsEmpty ? newRect : Rectangle.Union(rect, newRect); } return rect.Size.IsEmpty ? Rectangle.Empty : rect; } public Bitmap GetFrame(int i) { var iFrame = this.Frames[i]; var rect = iFrame.Region; return GetFrame(i, rect); } private Bitmap GetFrame(int i, Rectangle canvasRect) { var iFrame = this.Frames[i]; Bitmap bmp = new Bitmap(canvasRect.Width, canvasRect.Height, PixelFormat.Format32bppArgb); Graphics g = Graphics.FromImage(bmp); iFrame.Draw(g, canvasRect); g.Dispose(); return bmp; } private static Bitmap PrepareFrame(IGifFrame frame, Rectangle canvasRect, Color backgrnd, int minAlpha) { Bitmap gifFrame = new Bitmap(canvasRect.Width, canvasRect.Height, PixelFormat.Format32bppArgb); PrepareFrame(gifFrame, frame, canvasRect, backgrnd, minAlpha); return gifFrame; } /// /// 预处理帧坐标,生成新的图片。 /// private static void PrepareFrame(Bitmap canvas, IGifFrame frame, Rectangle canvasRect, Color backgrnd, int minAlpha) { Graphics g = Graphics.FromImage(canvas); if (backgrnd.A == 0xff) //背景色 { g.Clear(backgrnd); frame.Draw(g, canvasRect); } else //透明混合色 { g.Clear(Color.Transparent); Rectangle frameRect = frame.Region; frameRect.Offset(-canvasRect.X, -canvasRect.Y); frame.Draw(g, canvasRect); BitmapData data = canvas.LockBits(new Rectangle(Point.Empty, canvas.Size), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb); unsafe { byte* buffer = (byte*)data.Scan0.ToPointer(); for (int y = frameRect.Top; y < frameRect.Bottom; y++) { for (int x = frameRect.Left; x < frameRect.Right; x++) { int i = 4 * x + y * data.Stride; byte a = buffer[i + 3]; if (a <= minAlpha) { buffer[i] = buffer[i + 1] = buffer[i + 2] = buffer[i + 3] = 0; } else if (a < 0xff) { float al = a / 255f; float be = (1 - al); buffer[i] = (byte)(buffer[i] * al + backgrnd.B * be); buffer[i + 1] = (byte)(buffer[i + 1] * al + backgrnd.G * be); buffer[i + 2] = (byte)(buffer[i + 2] * al + backgrnd.R * be); buffer[i + 3] = 0xff; } } } } canvas.UnlockBits(data); } } public static Gif CreateFromNode(Wz_Node node, GlobalFindNodeFunction findNode) { if (node == null) return null; Gif gif = new Gif(); for (int i = 0; ; i++) { Wz_Node frameNode = node.FindNodeByPath(i.ToString()); if (frameNode == null || frameNode.Value == null) break; GifFrame gifFrame = CreateFrameFromNode(frameNode, findNode); if (gifFrame == null) break; gif.Frames.Add(gifFrame); } if (gif.Frames.Count > 0) return gif; else return null; } public static GifFrame CreateFrameFromNode(Wz_Node frameNode, GlobalFindNodeFunction findNode) { if (frameNode == null || frameNode.Value == null) { return null; } while (frameNode.Value is Wz_Uol) { Wz_Uol uol = frameNode.Value as Wz_Uol; Wz_Node uolNode = uol.HandleUol(frameNode); if (uolNode != null) { frameNode = uolNode; } } if (frameNode.Value is Wz_Png) { var linkNode = frameNode.GetLinkedSourceNode(findNode); Wz_Png png = linkNode?.GetValue() ?? (Wz_Png)frameNode.Value; var gifFrame = new GifFrame(png.ExtractPng()); foreach (Wz_Node propNode in frameNode.Nodes) { switch (propNode.Text) { case "origin": gifFrame.Origin = (propNode.Value as Wz_Vector); break; case "delay": gifFrame.Delay = propNode.GetValue(); break; case "a0": gifFrame.A0 = propNode.GetValue(); break; case "a1": gifFrame.A1 = propNode.GetValue(); break; } } if (gifFrame.Delay == 0) { gifFrame.Delay = 120;//给予默认delay } return gifFrame; } return null; } } } ================================================ FILE: WzComparerR2.Common/GifCanvas.cs ================================================ using System; using System.Collections.Generic; using System.Text; using System.Drawing; namespace WzComparerR2.Common { public class GifCanvas { public GifCanvas() { this.Layers = new List(); this.AlphaGradientDelay = 30; } public List Layers { get; private set; } public int AlphaGradientDelay { get; set; } public Gif Combine() { //获取全部关键帧延时 List delays = new List(); delays.Add(0); foreach (var layer in this.Layers) { int delay = 0; foreach (var frame in layer.Frames) { delay += frame.Delay; int idx = delays.BinarySearch(delay); if (idx < 0) { delays.Insert(~idx, delay); } } } //构建关键帧 LinkedList keyFrames = new LinkedList(); for (int i = 1; i < delays.Count; i++) { keyFrames.AddLast(new KeyFrame() { Delay = delays[i] - delays[i - 1] }); } //开始填充 foreach (var layer in this.Layers) { var node = keyFrames.First; foreach (var frame in layer.Frames) //把图层按关键帧拆分 { var frame0 = frame; int delay = frame.Delay; while (delay > 0) { if (frame.Bitmap != null) { if (node.Value.Delay == frame0.Delay) //直接加入 { node.Value.Frames.Add(frame0); } else if (node.Value.Delay < frame0.Delay) //拆分 { GifFrame f1, f2; SplitGifFrame(frame0, node.Value.Delay, out f1, out f2); node.Value.Frames.Add(f1); frame0 = f2; } else { throw new Exception("key frame delay error."); } } delay -= node.Value.Delay; node = node.Next; } } } //开始合并 Gif gif = new Gif(); { var node = keyFrames.First; while (node != null) { if (AlphaGradientDelay > 0 && node.Value.HasAlphaGradient && AlphaGradientDelay < node.Value.Delay) //分离渐变帧 { KeyFrame f1, f2; node.Value.Split(AlphaGradientDelay, out f1, out f2); node.Value = f1; keyFrames.AddAfter(node, f2); } gif.Frames.Add(node.Value); node = node.Next; } } return gif; } private static void SplitGifFrame(GifFrame frame, int time, out GifFrame frame1, out GifFrame frame2) { double p = (double)time / frame.Delay; int a = frame.A0 == frame.A1 ? frame.A0 : (int)Math.Round(frame.A0 * (1 - p) + frame.A1 * p); frame1 = new GifFrame(frame.Bitmap, frame.Origin, time) { A0 = frame.A0, A1 = a }; frame2 = new GifFrame(frame.Bitmap, frame.Origin, frame.Delay - time) { A0 = a, A1 = frame.A1 }; } private class KeyFrame : IGifFrame { public KeyFrame() { this.Frames = new List(); } public List Frames { get; private set; } public int Delay { get; set; } public bool HasAlphaGradient { get { return !this.Frames.TrueForAll(f => f.A0 == f.A1); } } public void Split(int time, out KeyFrame keyFrame1, out KeyFrame keyFrame2) { keyFrame1 = new KeyFrame(); keyFrame2 = new KeyFrame(); double p = (double)time / this.Delay; foreach (var f in this.Frames) { GifFrame f1, f2; SplitGifFrame(f, time, out f1, out f2); keyFrame1.Frames.Add(f1); keyFrame2.Frames.Add(f2); } keyFrame1.Delay = time; keyFrame2.Delay = this.Delay - time; } int IGifFrame.Delay { get { return this.Delay; } } Rectangle IGifFrame.Region { get { Rectangle rect = Rectangle.Empty; foreach (var f in this.Frames) { var newRect = ((IGifFrame)f).Region; rect = rect.Size.IsEmpty ? newRect : Rectangle.Union(rect, newRect); } return rect.Size.IsEmpty ? Rectangle.Empty : rect; } } void IGifFrame.Draw(Graphics g, Rectangle canvasRect) { foreach (var f in this.Frames) { ((IGifFrame)f).Draw(g, canvasRect); } } } } } ================================================ FILE: WzComparerR2.Common/GifFrame.cs ================================================ using System; using System.Collections.Generic; using System.Text; using System.Drawing; using System.Drawing.Imaging; namespace WzComparerR2.Common { public class GifFrame : IGifFrame { public GifFrame() { this.A0 = 255; this.A1 = 255; } public GifFrame(Bitmap bitmap) : this(bitmap, Point.Empty, 0) { } public GifFrame(Bitmap bitmap, int delay) : this(bitmap, Point.Empty, delay) { } public GifFrame(Bitmap bitmap, Point origin, int delay) : this() { this.Bitmap = bitmap; this.Origin = origin; this.Delay = delay; } public Bitmap Bitmap { get; set; } public Point Origin { get; set; } public int Delay { get; set; } public int A0 { get; set; } public int A1 { get; set; } int IGifFrame.Delay { get { return this.Delay; } } Rectangle IGifFrame.Region { get { var size = this.Bitmap == null ? Size.Empty : this.Bitmap.Size; return new Rectangle(-this.Origin.X, -this.Origin.Y, size.Width, size.Height); } } void IGifFrame.Draw(Graphics g, Rectangle canvasRect) { if (this.Bitmap == null) { return; } Point pos = new Point(-this.Origin.X - canvasRect.X, -this.Origin.Y - canvasRect.Y); if (A0 >= 255) { g.DrawImage(this.Bitmap, pos); } else if (A0 > 0) { var imageAttr = new ImageAttributes(); float a = A0 / 255f; var mt = new ColorMatrix(new[]{ new float[]{1,0,0,0,0}, new float[]{0,1,0,0,0}, new float[]{0,0,1,0,0}, new float[]{0,0,0,a,0}, new float[]{0,0,0,0,1}, }); imageAttr.SetColorMatrix(mt); g.DrawImage(this.Bitmap, new Rectangle(pos, this.Bitmap.Size), 0, 0, this.Bitmap.Width, this.Bitmap.Height, GraphicsUnit.Pixel, imageAttr); imageAttr.Dispose(); } } } } ================================================ FILE: WzComparerR2.Common/GifLayer.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace WzComparerR2.Common { public class GifLayer { public GifLayer() { this.Frames = new List(); } public List Frames { get; private set; } public void AddFrame(GifFrame frame) { this.Frames.Add(frame); } public void AddBlank(int delay) { this.Frames.Add(new GifFrame(null, delay)); } } } ================================================ FILE: WzComparerR2.Common/GlobalFindNodeFunction.cs ================================================ using System; using System.Collections.Generic; using System.Text; using WzComparerR2.WzLib; namespace WzComparerR2 { public delegate Wz_Node GlobalFindNodeFunction(string fullPath); } ================================================ FILE: WzComparerR2.Common/IGifFrame.cs ================================================ using System; using System.Collections.Generic; using System.Text; using System.Drawing; namespace WzComparerR2.Common { public interface IGifFrame { Rectangle Region { get; } int Delay { get; } void Draw(Graphics g, Rectangle canvasRect); } } ================================================ FILE: WzComparerR2.Common/ImageDataObject.cs ================================================ using System; using System.Collections.Generic; using System.Text; using System.Windows.Forms; using System.Drawing; using System.IO; namespace WzComparerR2.Common { public class ImageDataObject : DataObject { public ImageDataObject(Image image, string fileName) { this.Image = image; this.FileName = fileName; this.SetData(DataFormats.Bitmap, fileName); this.SetData(DataFormats.FileDrop, fileName); this.SetData(QQ_RichEdit_Format, new MemoryStream(new byte[0])); this.SetData(QQ_Unicode_RichEdit_Format, new MemoryStream(new byte[0])); } public Image Image { get; private set; } public string FileName { get; private set; } private static readonly string QQ_RichEdit_Format = "QQ_RichEdit_Format"; private static readonly string QQ_Unicode_RichEdit_Format = "QQ_Unicode_RichEdit_Format"; public override object GetData(string format, bool autoConvert) { if (format == DataFormats.Bitmap || format == typeof(Bitmap).FullName) { PrepareImageFile(); base.SetData(DataFormats.Bitmap, this.FileName); } else if (format == DataFormats.FileDrop || format == "FileName" || format == "FileNameW") { PrepareImageFile(); base.SetData(DataFormats.FileDrop, new string[] { this.FileName }); } else if (format == QQ_RichEdit_Format) { PrepareImageFile(); byte[] buffer = Encoding.Default.GetBytes(GetQQRichFormatString()); this.SetData(QQ_RichEdit_Format, new MemoryStream(buffer)); } else if (format == QQ_Unicode_RichEdit_Format) { PrepareImageFile(); byte[] buffer = Encoding.Unicode.GetBytes(GetQQRichFormatString()); this.SetData(QQ_Unicode_RichEdit_Format, new MemoryStream(buffer)); } return base.GetData(format, autoConvert); } private void PrepareImageFile() { string fileName = this.FileName; string tempDir = new DirectoryInfo(Environment.GetEnvironmentVariable("TEMP")).FullName; bool willSaveImage = false; if (string.IsNullOrEmpty(fileName)) { fileName = Path.Combine(tempDir, Path.GetRandomFileName()); willSaveImage = true; } else { if (string.IsNullOrEmpty(Path.GetDirectoryName(fileName)))//没有文件夹 保存文件 { fileName = Path.Combine(tempDir, fileName); if (File.Exists(fileName)) { string fileNameNoExt = Path.GetFileNameWithoutExtension(fileName); string ext = Path.GetExtension(fileName); for (int i = 1; ; i++) { fileName = Path.Combine(tempDir, string.Format("{0}({1}){2}", fileNameNoExt, i, ext)); if (!File.Exists(fileName)) { break; } } } willSaveImage = true; } } if (willSaveImage) { Image.Save(fileName, Image.RawFormat); this.FileName = fileName; } } private string GetQQRichFormatString() { return string.Format(@"", FileName); } } } ================================================ FILE: WzComparerR2.Common/Properties/AssemblyInfo.cs ================================================ using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // 有关程序集的常规信息通过以下 // 特性集控制。更改这些特性值可修改 // 与程序集关联的信息。 [assembly: AssemblyTitle("WzComparerR2.Common")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("WzComparerR2.Common")] [assembly: AssemblyCopyright("Copyright © Kagamia Studio 2015-2025")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] // 将 ComVisible 设置为 false 使此程序集中的类型 // 对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型, // 则将该类型上的 ComVisible 特性设置为 true。 [assembly: ComVisible(false)] // 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID [assembly: Guid("b7285749-1724-4f18-9824-7d067ac26402")] // 程序集的版本信息由下面四个值组成: // // 主版本 // 次版本 // 生成号 // 修订号 // // 可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值, // 方法是按如下所示使用“*”: // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("2.2.0.0")] [assembly: AssemblyFileVersion("2.2.0.10725")] ================================================ FILE: WzComparerR2.Common/Rendering/AnimationGraphics.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using WzComparerR2.Animation; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using WzComparerR2.Controls; namespace WzComparerR2.Rendering { public class AnimationGraphics { public AnimationGraphics(GraphicsDevice graphicsDevice) : this (graphicsDevice, new SpriteBatch(graphicsDevice)) { } public AnimationGraphics(GraphicsDevice graphicsDevice, SpriteBatch sprite) { this.GraphicsDevice = graphicsDevice; this.sprite = sprite; this.spineRenderer = new Spine.SkeletonRenderer(graphicsDevice); this.blendState = StateEx.NonPremultipled_Hidef(); } public GraphicsDevice GraphicsDevice { get; private set; } private SpriteBatch sprite; private Spine.SkeletonRenderer spineRenderer; private BlendState blendState; public void Draw(FrameAnimator animator, Matrix world) { Frame frame = animator.CurrentFrame; if (frame != null && frame.Texture != null) { if (animator.Position != Point.Zero) { world *= Matrix.CreateTranslation(animator.Position.X, animator.Position.Y, 0); } sprite.Begin(SpriteSortMode.Deferred, this.blendState, transformMatrix: world); sprite.Draw(frame.Texture, Vector2.Zero, frame.AtlasRect, new Color(Color.White, frame.A0), 0, frame.Origin.ToVector2(), 1, SpriteEffects.None, 0); sprite.End(); } } public void Draw(ISpineAnimator animator, Matrix world) { if (animator is AnimationItem aniItem && aniItem.Position != Point.Zero) { world *= Matrix.CreateTranslation(aniItem.Position.X, aniItem.Position.Y, 0); } spineRenderer.PremultipliedAlpha = animator.Data.PremultipliedAlpha; if (spineRenderer.Effect is BasicEffect basicEff) { basicEff.World = world; basicEff.Projection = Matrix.CreateOrthographicOffCenter(0, this.GraphicsDevice.Viewport.Width, this.GraphicsDevice.Viewport.Height, 0, 1, 0); } spineRenderer.Begin(); animator.Render(spineRenderer); spineRenderer.End(); } } } ================================================ FILE: WzComparerR2.Common/Rendering/BlendEx.cs ================================================ using System; using Microsoft.Xna.Framework.Graphics; namespace WzComparerR2.Rendering { public static class StateEx { public static BlendState NonPremultipled_Hidef() => new BlendState() { AlphaSourceBlend = Blend.One, AlphaDestinationBlend = Blend.InverseSourceAlpha, AlphaBlendFunction = BlendFunction.Add, ColorSourceBlend = Blend.SourceAlpha, ColorDestinationBlend = Blend.InverseSourceAlpha, ColorBlendFunction = BlendFunction.Add, }; public static BlendState SrcAlphaMask() => new BlendState() { AlphaSourceBlend = Blend.Zero, AlphaDestinationBlend = Blend.InverseSourceAlpha, AlphaBlendFunction = BlendFunction.Add, ColorSourceBlend = Blend.Zero, ColorDestinationBlend = Blend.InverseSourceAlpha, ColorBlendFunction = BlendFunction.Add, }; public static BlendState MultiplyRGB() => new BlendState() { AlphaSourceBlend = Blend.Zero, AlphaDestinationBlend = Blend.One, AlphaBlendFunction = BlendFunction.Add, ColorSourceBlend = Blend.Zero, ColorDestinationBlend = Blend.SourceColor, ColorBlendFunction = BlendFunction.Add, }; public static RasterizerState Scissor() => new RasterizerState() { ScissorTestEnable = true, CullMode = CullMode.None, MultiSampleAntiAlias = false, FillMode = FillMode.Solid }; } } ================================================ FILE: WzComparerR2.Common/Rendering/D2DContext.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using SharpDX.Direct2D1; using SharpDX.DirectWrite; using SharpDX.DXGI; namespace WzComparerR2.Rendering { public sealed class D2DContext : IDisposable { public Surface DxgiSurface { get; internal set; } public RenderTarget D2DRenderTarget { get; internal set; } internal bool IsBeginEndPair { get; private set; } private SolidColorBrush cachedBrush; public Brush GetBrush(Microsoft.Xna.Framework.Color color) { return this.GetBrush(color.XnaToDxColor()); } public Brush GetBrush(SharpDX.Color4 color) { if (this.cachedBrush == null || this.cachedBrush.IsDisposed) { this.cachedBrush = new SolidColorBrush(this.D2DRenderTarget, color); } else { this.cachedBrush.Color = color; } return this.cachedBrush; } public void Dispose() { this.cachedBrush?.Dispose(); this.DxgiSurface?.Dispose(); this.D2DRenderTarget?.Dispose(); } } } ================================================ FILE: WzComparerR2.Common/Rendering/D2DFactory.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Runtime.CompilerServices; using System.Reflection; using Microsoft.Xna.Framework.Graphics; using SharpDX.Direct2D1; using SharpDX.DirectWrite; namespace WzComparerR2.Rendering { public class D2DFactory : IDisposable { private static D2DFactory _instance; public static D2DFactory Instance { get { if (_instance == null || _instance.IsDisposed) { _instance = new D2DFactory(); } return _instance; } } private D2DFactory() { this.factory2D = new SharpDX.Direct2D1.Factory(); this.factoryDWrite = new SharpDX.DirectWrite.Factory(); this.dictContext = new ConditionalWeakTable(); this.deviceSwapChainField = typeof(GraphicsDevice) .GetField("_swapChain", BindingFlags.Instance | BindingFlags.NonPublic); this.textureResourceField = typeof(Texture) .GetField("_texture", BindingFlags.Instance | BindingFlags.NonPublic); } public bool IsDisposed { get; private set; } internal readonly SharpDX.Direct2D1.Factory factory2D; internal readonly SharpDX.DirectWrite.Factory factoryDWrite; private ConditionalWeakTable dictContext; private readonly FieldInfo deviceSwapChainField; private readonly FieldInfo textureResourceField; public D2DContext GetContext(GraphicsDevice graphicsDevice) { SharpDX.ComObject obj = GetRenderTargetResource(graphicsDevice); D2DContext context = GetOrCreateContext(obj); if (context == null) { return null; } AlphaMode alphaMode = AlphaMode.Ignore; if (context.DxgiSurface == null || context.DxgiSurface.IsDisposed) { if (obj is SharpDX.DXGI.SwapChain) { var swapChain = (SharpDX.DXGI.SwapChain)obj; context.DxgiSurface = SharpDX.DXGI.Surface.FromSwapChain(swapChain, 0); alphaMode = AlphaMode.Ignore; } else if (obj is SharpDX.Direct3D11.Resource) { context.DxgiSurface = obj.QueryInterface(); alphaMode = AlphaMode.Premultiplied; } else { return null; } } if (context.D2DRenderTarget == null || context.D2DRenderTarget.IsDisposed) { var rtProp = new RenderTargetProperties(new PixelFormat(SharpDX.DXGI.Format.Unknown, alphaMode)); var d2drt = new RenderTarget(this.factory2D, context.DxgiSurface, rtProp); d2drt.TextRenderingParams = new RenderingParams(factoryDWrite, 1f, 0f, 0f, PixelGeometry.Flat, RenderingMode.CleartypeGdiClassic); d2drt.TextAntialiasMode = SharpDX.Direct2D1.TextAntialiasMode.Grayscale; context.D2DRenderTarget = d2drt; context.DxgiSurface.Disposing += (o, e) => d2drt.Dispose(); } return context; } public void ReleaseContext(GraphicsDevice graphicsDevice) { SharpDX.ComObject obj = GetRenderTargetResource(graphicsDevice); D2DContext context; if (this.dictContext.TryGetValue(obj, out context)) { context.Dispose(); } } private SharpDX.ComObject GetRenderTargetResource(GraphicsDevice graphicsDevice) { var rt = graphicsDevice.GetRenderTargets(); SharpDX.ComObject obj; if (rt.Length <= 0) { obj = (SharpDX.DXGI.SwapChain)this.deviceSwapChainField.GetValue(graphicsDevice); } else { obj = (SharpDX.Direct3D11.Resource)this.textureResourceField.GetValue(rt[0].RenderTarget); } return obj; } private D2DContext GetOrCreateContext(SharpDX.ComObject comObject) { if (comObject == null) { return null; } if (comObject.IsDisposed) { dictContext.Remove(comObject); return null; } D2DContext context; if (!this.dictContext.TryGetValue(comObject, out context)) { context = new D2DContext(); comObject.Disposing += ComObject_Disposing; this.dictContext.Add(comObject, context); } return context; } private void ComObject_Disposing(object sender, EventArgs e) { var comObject = sender as SharpDX.ComObject; D2DContext context; if (comObject != null && this.dictContext.TryGetValue(comObject, out context)) { context?.Dispose(); this.dictContext.Remove(comObject); } } ~D2DFactory() { this.Dispose(false); } public void Dispose() { this.Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (disposing && !this.IsDisposed) { if (this.factory2D != null) { this.factory2D.Dispose(); } if (this.factoryDWrite != null) { this.factoryDWrite.Dispose(); } this.dictContext = null; this.IsDisposed = true; } } } } ================================================ FILE: WzComparerR2.Common/Rendering/D2DFont.cs ================================================ using System; using System.Collections.Generic; using System.Collections.Concurrent; using System.Linq; using System.Text; using SharpDX.Direct2D1; using SharpDX.DXGI; using SharpDX.DirectWrite; using Microsoft.Xna.Framework; namespace WzComparerR2.Rendering { public class D2DFont : IDisposable { public D2DFont(string familyName, float size) : this(familyName, size, false, false) { } public D2DFont(string familyName, float size, bool bold, bool italic) { this.FamilyName = familyName; this.Size = size; FontWeight weight = bold ? FontWeight.Bold : FontWeight.Normal; FontStyle style = italic ? FontStyle.Italic : FontStyle.Normal; var factory = D2DFactory.Instance.factoryDWrite; this.textFormat = new TextFormat(factory, this.FamilyName, weight, style, this.Size); this.CacheFontMetrics(); this.Height = this.Height; } public D2DFont(System.Drawing.Font font) : this(font.Name, font.SizeInPoints * 96 / 72, font.Bold, font.Italic) { } public string FamilyName { get; private set; } public float Size { get; private set; } public float Height { get { if (this.lineHeight <= 0) { if (this.metrics.DesignUnitsPerEm > 0) { float ratio = this.textFormat.FontSize / this.metrics.DesignUnitsPerEm; float size = (this.metrics.Ascent + this.metrics.Descent + this.metrics.LineGap) * ratio; this.lineHeight = (float)Math.Ceiling(size); } else { this.lineHeight = this.Size; } } return this.lineHeight; } set { LineSpacingMethod method; float lineSpacing; float baseLine; this.textFormat.GetLineSpacing(out method, out lineSpacing, out baseLine); if (method == LineSpacingMethod.Default || baseLine <= 0) { if (this.metrics.DesignUnitsPerEm > 0) { float ratio = this.textFormat.FontSize / metrics.DesignUnitsPerEm; baseLine = metrics.Ascent * ratio; } else { baseLine = this.Size; } } this.textFormat.SetLineSpacing(LineSpacingMethod.Uniform, value, baseLine); this.lineHeight = value; } } private readonly TextFormat textFormat; private float lineHeight; private FontMetrics metrics; private Font GetMatchingFont() { var fontCollection = this.textFormat.FontCollection; int index; if (fontCollection.FindFamilyName(this.textFormat.FontFamilyName, out index)) { using (var family = fontCollection.GetFontFamily(index)) { var font = family.GetFirstMatchingFont(this.textFormat.FontWeight, this.textFormat.FontStretch, this.textFormat.FontStyle); return font; } } return null; } private bool CacheFontMetrics() { var font = this.GetMatchingFont(); if (font != null) { using (font) { this.metrics = font.Metrics; return true; } } return false; } internal void DrawText(D2DContext context, string text, Vector2 position, Color color) { this.DrawText(context, text, position, Vector2.Zero, color); } internal void DrawText(D2DContext context, string text, Vector2 position, Vector2 size, Color color) { var rt = context.D2DRenderTarget; using (var layout = this.LayoutString(text, size.X, size.Y)) { rt.DrawTextLayout(new SharpDX.Vector2(position.X, position.Y), layout, context.GetBrush(color), DrawTextOptions.None); } } public Vector2 MeasureString(string text) { return this.MeasureString(text, Vector2.Zero); } public Vector2 MeasureString(string text, Vector2 size) { using (var layout = this.LayoutString(text, size.X, size.Y)) { var metrics = layout.Metrics; if (metrics.LineCount > 0 && this.metrics.DesignUnitsPerEm > 0) { float ratio = this.textFormat.FontSize / this.metrics.DesignUnitsPerEm; var gap = this.lineHeight - (this.metrics.Ascent + this.metrics.Descent) * ratio; if (gap > 0) { metrics.Height -= gap; } } return new Vector2(metrics.WidthIncludingTrailingWhitespace, metrics.Height); } } private TextLayout LayoutString(string text, float maxWidth, float maxHeight) { if (maxWidth <= 0) { maxWidth = Int16.MaxValue; } var layout = new TextLayout(D2DFactory.Instance.factoryDWrite, text, this.textFormat, maxWidth, 0, 1, false); layout.WordWrapping = WordWrapping.Wrap; layout.TextAlignment = TextAlignment.Leading; layout.ParagraphAlignment = ParagraphAlignment.Near; return layout; } ~D2DFont() { this.Dispose(false); } public void Dispose() { this.Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (disposing) { this.textFormat.Dispose(); } } } } ================================================ FILE: WzComparerR2.Common/Rendering/D2DRenderer.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; namespace WzComparerR2.Rendering { public class D2DRenderer { public D2DRenderer(GraphicsDevice graphicsDevice) { this.GraphicsDevice = graphicsDevice; } public GraphicsDevice GraphicsDevice { get; private set; } internal bool IsBeginEndPair { get; private set; } protected D2DContext context; public void Begin() { this.Begin(SharpDX.Matrix3x2.Identity); } public void Begin(Microsoft.Xna.Framework.Matrix transform) { var mt3x2 = new SharpDX.Matrix3x2(transform.M11, transform.M12, transform.M21, transform.M22, transform.M41, transform.M42); this.Begin(mt3x2); } private void Begin(SharpDX.Matrix3x2 transform) { this.context = D2DFactory.Instance.GetContext(this.GraphicsDevice); if (this.context == null) { throw new Exception("Create D2D context failed."); } this.context.D2DRenderTarget.Transform = transform; this.context.D2DRenderTarget.BeginDraw(); this.IsBeginEndPair = true; } public void PushClip(Rectangle clipRect) { this.context.D2DRenderTarget.PushAxisAlignedClip(clipRect.XnaToDxRect(), SharpDX.Direct2D1.AntialiasMode.PerPrimitive); } public void PopClip() { this.context.D2DRenderTarget.PopAxisAlignedClip(); } public void DrawString(D2DFont font, string text, Vector2 position, Color color) { font.DrawText(this.context, text, position, color); } public void DrawString(D2DFont font, string text, Vector2 position, Vector2 size, Color color) { font.DrawText(this.context, text, position, size, color); } public void DrawLine(Vector2 point0, Vector2 point1, float width, Color color) { var rt = this.context.D2DRenderTarget; rt.DrawLine(new SharpDX.Vector2(point0.X, point0.Y), new SharpDX.Vector2(point1.X, point1.Y), this.context.GetBrush(color), width); } public void DrawRectangle(Rectangle rectangle, Color color) { var rt = this.context.D2DRenderTarget; rt.DrawRectangle(rectangle.XnaToDxRect(), this.context.GetBrush(color)); } public void FillRectangle(Rectangle rectangle, Color color) { var rt = this.context.D2DRenderTarget; rt.FillRectangle(rectangle.XnaToDxRect(), this.context.GetBrush(color)); } public void FillRoundedRectangle(Rectangle rectangle, float cornerRadius, Color color) { var rt = this.context.D2DRenderTarget; var rRect = new SharpDX.Direct2D1.RoundedRectangle() { RadiusX = cornerRadius, RadiusY = cornerRadius, Rect = rectangle.XnaToDxRect() }; rt.FillRoundedRectangle(rRect, this.context.GetBrush(color)); } public void End() { this.context.D2DRenderTarget.EndDraw(); this.IsBeginEndPair = false; } } } ================================================ FILE: WzComparerR2.Common/Rendering/DxExtension.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using Microsoft.Xna.Framework; namespace WzComparerR2.Rendering { public static class DxExtension { public static Color DxToXnaColor(this SharpDX.Color color) { return new Color(color.R, color.G, color.B, color.A); } public static SharpDX.Color XnaToDxColor(this Color color) { return SharpDX.Color.FromRgba(color.PackedValue); } public static SharpDX.RectangleF XnaToDxRect(this Rectangle rect) { return new SharpDX.RectangleF(rect.X, rect.Y, rect.Width, rect.Height); } } } ================================================ FILE: WzComparerR2.Common/Rendering/Effect/EffectCompiler.bat ================================================ @echo off setlocal if [%1]==[] goto error set monogame_ver=%1 :check @echo Check dotnet-mgfxc tool... dotnet tool list --global | findstr "dotnet-mgfxc" || goto install :exists dotnet tool list --global | findstr "dotnet-mgfxc.*%monogame_ver%" >nul 2>&1 || goto uninstall goto build :uninstall dotnet tool uninstall --global dotnet-mgfxc || goto failed :install dotnet tool install --global dotnet-mgfxc --version %monogame_ver% || goto failed :build mgfxc "PngEffect.fx" "PngEffect.%monogame_ver%.mgfxo" /Profile:DirectX_11 || goto failed goto :eof :error @echo %0 [monogame_ver] @echo monogame_ver: 3.8.0.1641/3.8.1.303 goto :failed :failed exit /B 1 endlocal ================================================ FILE: WzComparerR2.Common/Rendering/Effect/Macros.fxh ================================================ //----------------------------------------------------------------------------- // Macros.fxh // // Microsoft XNA Community Game Platform // Copyright (C) Microsoft Corporation. All rights reserved. //----------------------------------------------------------------------------- #ifdef SM4 // Macros for targetting shader model 4.0 (DX11) #define TECHNIQUE(name, vsname, psname ) \ technique name { pass { VertexShader = compile vs_4_0_level_9_1 vsname (); PixelShader = compile ps_4_0_level_9_1 psname(); } } #define BEGIN_CONSTANTS cbuffer Parameters : register(b0) { #define MATRIX_CONSTANTS #define END_CONSTANTS }; #define _vs(r) #define _ps(r) #define _cb(r) #define DECLARE_TEXTURE(Name, index) \ Texture2D Name : register(t##index); \ sampler Name##Sampler : register(s##index) #define DECLARE_CUBEMAP(Name, index) \ TextureCube Name : register(t##index); \ sampler Name##Sampler : register(s##index) #define SAMPLE_TEXTURE(Name, texCoord) Name.Sample(Name##Sampler, texCoord) #define SAMPLE_CUBEMAP(Name, texCoord) Name.Sample(Name##Sampler, texCoord) #else // Macros for targetting shader model 2.0 (DX9) #define TECHNIQUE(name, vsname, psname ) \ technique name { pass { VertexShader = compile vs_2_0 vsname (); PixelShader = compile ps_2_0 psname(); } } #define BEGIN_CONSTANTS #define MATRIX_CONSTANTS #define END_CONSTANTS #define _vs(r) : register(vs, r) #define _ps(r) : register(ps, r) #define _cb(r) #define DECLARE_TEXTURE(Name, index) \ sampler2D Name : register(s##index); #define DECLARE_CUBEMAP(Name, index) \ samplerCUBE Name : register(s##index); #define SAMPLE_TEXTURE(Name, texCoord) tex2D(Name, texCoord) #define SAMPLE_CUBEMAP(Name, texCoord) texCUBE(Name, texCoord) #endif //Extensions #ifdef SM4 #define TECHNIQUE_SB(name, psname ) \ technique name { pass { PixelShader = compile ps_4_0_level_9_1 psname(); } } #else #define TECHNIQUE_SB(name, psname ) \ technique name { pass { PixelShader = compile ps_2_0 psname(); } } #endif ================================================ FILE: WzComparerR2.Common/Rendering/Effect/PngEffect.fx ================================================ #include "Macros.fxh" DECLARE_TEXTURE(Texture, 0); BEGIN_CONSTANTS float4 mixedColor; float clipAlpha; MATRIX_CONSTANTS END_CONSTANTS struct VSOutput { float4 position : SV_Position; float4 color : COLOR0; float2 texCoord : TEXCOORD0; }; float4 RGBAtoNonPremultiplied(float4 input) { if (input.a <= 1) { input.rgb /= input.a; } return input; } float4 Blend(float4 background, float4 premultipliedColor) { return float4(background.rgb * (1 - premultipliedColor.a) + premultipliedColor.rgb, 1); } float4 PS(VSOutput input) : SV_Target0 { float4 color = SAMPLE_TEXTURE(Texture, input.texCoord) * input.color; return RGBAtoNonPremultiplied(color); } float4 PS_AlphaTest(VSOutput input) : SV_Target0 { float4 color = SAMPLE_TEXTURE(Texture, input.texCoord) * input.color; clip(color.a <= clipAlpha ? -1 : 1); return Blend(mixedColor, color); } TECHNIQUE_SB(tech0, PS); TECHNIQUE_SB(tech1, PS_AlphaTest); ================================================ FILE: WzComparerR2.Common/Rendering/EffectCompiler/ConstantBuffer.cs ================================================ using Microsoft.Xna.Framework.Graphics; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace WzComparerR2.Rendering.EffectCompiler { public class ConstantBuffer { public string Name { get; set; } public int Slot { get; set; } public int SizeInBytes { get; set; } public List Parameters { get; set; } = new(); } public class ShaderParameter { public ShaderParameter() { } public ShaderParameter(string name, string semantic, int bufferOffset, EffectParameterClass parameterClass, EffectParameterType parameterType, int rowCount, int columnCount, int elementCount) { Name = name; Semantic = semantic; BufferOffset = bufferOffset; ParameterClass = parameterClass; ParameterType = parameterType; RowCount = rowCount; ColumnCount = columnCount; ElementCount = elementCount; } public string Name { get; set; } public string Semantic { get; set; } public int BufferOffset { get; set; } public EffectParameterClass ParameterClass { get; set; } public EffectParameterType ParameterType { get; set; } public int RowCount { get; set; } public int ColumnCount { get; set; } public int ElementCount { get; set; } } } ================================================ FILE: WzComparerR2.Common/Rendering/EffectCompiler/Internal/ConstantBufferData.cs ================================================ // Port from https://github.com/MonoGame/MonoGame/blob/develop/Tools/MonoGame.Effect.Compiler/Effect/ConstantBufferData.cs using System; using System.Collections.Generic; using System.IO; namespace WzComparerR2.Rendering.EffectCompiler.Internal { public class ConstantBufferData { public string Name; public int Size; public List ParameterIndex = new List(); public List ParameterOffset = new List(); public List Parameters = new List(); public void Write(BinaryWriter writer) { writer.Write(Name); writer.Write((ushort)Size); writer.MgfxWriteElementCount(ParameterIndex.Count); for (var i = 0; i < ParameterIndex.Count; i++) { writer.MgfxWriteElementCount(ParameterIndex[i]); writer.Write((ushort)ParameterOffset[i]); } } } } ================================================ FILE: WzComparerR2.Common/Rendering/EffectCompiler/Internal/D3DXObjects.cs ================================================ // Port from https://github.com/MonoGame/MonoGame/blob/develop/Tools/MonoGame.Effect.Compiler/Effect/EffectObject.cs using System; using System.Collections.Generic; using Microsoft.Xna.Framework.Graphics; namespace WzComparerR2.Rendering.EffectCompiler.Internal { public class d3dx_parameter { public string name; public string semantic; public object data; public EffectParameterClass class_; public EffectParameterType type; public uint rows; public uint columns; public uint element_count; public uint annotation_count = 0; public uint member_count; public uint flags = 0; public uint bytes = 0; public int bufferIndex = -1; public int bufferOffset = -1; public d3dx_parameter[] annotation_handles = null; public d3dx_parameter[] member_handles; public override string ToString() { if (rows > 0 || columns > 0) return string.Format("{0} {1}{2}x{3} {4} : cb{5},{6}", class_, type, rows, columns, name, bufferIndex, bufferOffset); else return string.Format("{0} {1} {2}", class_, type, name); } } public class d3dx_state { public uint operation; public uint index; public STATE_TYPE type; public d3dx_parameter parameter; } public class d3dx_sampler { public uint state_count = 0; public d3dx_state[] states = null; } public enum STATE_TYPE { CONSTANT, PARAMETER, EXPRESSION, EXPRESSIONINDEX, } public enum STATE_CLASS { LIGHTENABLE, FVF, LIGHT, MATERIAL, NPATCHMODE, PIXELSHADER, RENDERSTATE, SETSAMPLER, SAMPLERSTATE, TEXTURE, TEXTURESTAGE, TRANSFORM, VERTEXSHADER, SHADERCONST, UNKNOWN, }; public class d3dx_pass { public string name; public uint state_count; public uint annotation_count = 0; public BlendState blendState; public DepthStencilState depthStencilState; public RasterizerState rasterizerState; public d3dx_state[] states; public d3dx_parameter[] annotation_handles = null; } public class d3dx_technique { public string name; public uint pass_count; public uint annotation_count = 0; public d3dx_parameter[] annotation_handles = null; public d3dx_pass[] pass_handles; } } ================================================ FILE: WzComparerR2.Common/Rendering/EffectCompiler/Internal/EffectObject.cs ================================================ // Port from https://github.com/MonoGame/MonoGame/blob/develop/Tools/MonoGame.Effect.Compiler/Effect/EffectObject.writer.cs using System; using System.Collections.Generic; using System.IO; using System.Reflection; using Microsoft.Xna.Framework.Graphics; namespace WzComparerR2.Rendering.EffectCompiler.Internal { public class EffectObject { static EffectObject() { Type mgfxHeaderType = typeof(GraphicsDevice).Assembly.GetType("Microsoft.Xna.Framework.Graphics.Effect+MGFXHeader", true); MGFXSignature = (int)mgfxHeaderType.GetField("MGFXSignature", BindingFlags.Static | BindingFlags.Public).GetValue(null); MGFXVersion = (int)mgfxHeaderType.GetField("MGFXVersion", BindingFlags.Static | BindingFlags.Public).GetValue(null); Type hashType = typeof(GraphicsDevice).Assembly.GetType("MonoGame.Framework.Utilities.Hash", true); var computeHashMethod = hashType.GetMethod("ComputeHash", BindingFlags.NonPublic | BindingFlags.Static, null, new[] { typeof(Stream) }, null); ComputeHash = (Func)computeHashMethod.CreateDelegate(typeof(Func)); } public static readonly int MGFXSignature; public static readonly int MGFXVersion; public static readonly int ShaderProfile = 1; //Directx_11 public static readonly Func ComputeHash; public d3dx_parameter[] Parameters; public d3dx_technique[] Techniques; public List Shaders; public List ConstantBuffers; /// /// Writes the effect for loading later. /// public void Write(BinaryWriter writer) { writer.Write(MGFXSignature); writer.Write((byte)MGFXVersion); writer.Write((byte)ShaderProfile); // Write the rest to a memory stream. using (MemoryStream memStream = new MemoryStream()) using (BinaryWriter memWriter = new BinaryWriter(memStream)) { // Write all the constant buffers. memWriter.MgfxWriteElementCount(ConstantBuffers.Count); foreach (var cbuffer in ConstantBuffers) cbuffer.Write(memWriter); // Write all the shaders. memWriter.MgfxWriteElementCount(Shaders.Count); foreach (var shader in Shaders) shader.Write(memWriter); // Write the parameters. WriteParameters(memWriter, Parameters, Parameters.Length); // Write the techniques. memWriter.MgfxWriteElementCount(Techniques.Length); foreach (var technique in Techniques) { memWriter.Write(technique.name); WriteAnnotations(memWriter, technique.annotation_handles); // Write the passes. memWriter.MgfxWriteElementCount((int)technique.pass_count); for (var p = 0; p < technique.pass_count; p++) { var pass = technique.pass_handles[p]; memWriter.Write(pass.name); WriteAnnotations(memWriter, pass.annotation_handles); // Write the index for the vertex and pixel shaders. memWriter.MgfxWriteElementCount(GetShaderIndex(STATE_CLASS.VERTEXSHADER, pass.states)); memWriter.MgfxWriteElementCount(GetShaderIndex(STATE_CLASS.PIXELSHADER, pass.states)); // Write the state objects too! if (pass.blendState != null) { memWriter.Write(true); memWriter.Write((byte)pass.blendState.AlphaBlendFunction); memWriter.Write((byte)pass.blendState.AlphaDestinationBlend); memWriter.Write((byte)pass.blendState.AlphaSourceBlend); memWriter.Write(pass.blendState.BlendFactor.R); memWriter.Write(pass.blendState.BlendFactor.G); memWriter.Write(pass.blendState.BlendFactor.B); memWriter.Write(pass.blendState.BlendFactor.A); memWriter.Write((byte)pass.blendState.ColorBlendFunction); memWriter.Write((byte)pass.blendState.ColorDestinationBlend); memWriter.Write((byte)pass.blendState.ColorSourceBlend); memWriter.Write((byte)pass.blendState.ColorWriteChannels); memWriter.Write((byte)pass.blendState.ColorWriteChannels1); memWriter.Write((byte)pass.blendState.ColorWriteChannels2); memWriter.Write((byte)pass.blendState.ColorWriteChannels3); memWriter.Write(pass.blendState.MultiSampleMask); } else memWriter.Write(false); if (pass.depthStencilState != null) { memWriter.Write(true); memWriter.Write((byte)pass.depthStencilState.CounterClockwiseStencilDepthBufferFail); memWriter.Write((byte)pass.depthStencilState.CounterClockwiseStencilFail); memWriter.Write((byte)pass.depthStencilState.CounterClockwiseStencilFunction); memWriter.Write((byte)pass.depthStencilState.CounterClockwiseStencilPass); memWriter.Write(pass.depthStencilState.DepthBufferEnable); memWriter.Write((byte)pass.depthStencilState.DepthBufferFunction); memWriter.Write(pass.depthStencilState.DepthBufferWriteEnable); memWriter.Write(pass.depthStencilState.ReferenceStencil); memWriter.Write((byte)pass.depthStencilState.StencilDepthBufferFail); memWriter.Write(pass.depthStencilState.StencilEnable); memWriter.Write((byte)pass.depthStencilState.StencilFail); memWriter.Write((byte)pass.depthStencilState.StencilFunction); memWriter.Write(pass.depthStencilState.StencilMask); memWriter.Write((byte)pass.depthStencilState.StencilPass); memWriter.Write(pass.depthStencilState.StencilWriteMask); memWriter.Write(pass.depthStencilState.TwoSidedStencilMode); } else memWriter.Write(false); if (pass.rasterizerState != null) { memWriter.Write(true); memWriter.Write((byte)pass.rasterizerState.CullMode); memWriter.Write(pass.rasterizerState.DepthBias); memWriter.Write((byte)pass.rasterizerState.FillMode); memWriter.Write(pass.rasterizerState.MultiSampleAntiAlias); memWriter.Write(pass.rasterizerState.ScissorTestEnable); memWriter.Write(pass.rasterizerState.SlopeScaleDepthBias); } else memWriter.Write(false); } } // Calculate a hash code from memory stream // and write it to the header. var effectKey = ComputeHash(memStream); writer.Write(effectKey); //write content from memory stream to final stream. memStream.WriteTo(writer.BaseStream); } // Write a tail to be used by the reader for validation. if (MGFXVersion >= 10) writer.Write(MGFXSignature); } private static void WriteParameters(BinaryWriter writer, d3dx_parameter[] parameters, int count) { if (MGFXVersion < 10) writer.Write7BitEncodedInt(count); else writer.Write(count); for (var i = 0; i < count; i++) WriteParameter(writer, parameters[i]); } private static void WriteParameter(BinaryWriter writer, d3dx_parameter param) { writer.Write((byte)param.class_); writer.Write((byte)param.type); writer.Write(param.name); writer.Write(param.semantic ?? string.Empty); WriteAnnotations(writer, param.annotation_handles); writer.Write((byte)param.rows); writer.Write((byte)param.columns); // Write the elements or struct members. WriteParameters(writer, param.member_handles, (int)param.element_count); WriteParameters(writer, param.member_handles, (int)param.member_count); if (param.element_count == 0 && param.member_count == 0) { switch (param.type) { case EffectParameterType.Bool: case EffectParameterType.Int32: case EffectParameterType.Single: writer.Write((byte[])param.data); break; } } } private static void WriteAnnotations(BinaryWriter writer, d3dx_parameter[] annotations) { // annotation is not supported yet writer.MgfxWriteElementCount(0); //var count = annotations == null ? 0 : annotations.Length; //writer.Write(count); //for (var i = 0; i < count; i++) // WriteParameter(writer, annotations[i]); } internal static int GetShaderIndex(STATE_CLASS type, d3dx_state[] states) { foreach (var state in states) { var class_ = (STATE_CLASS)state.operation; if (class_ != type) continue; if (state.type != STATE_TYPE.CONSTANT) throw new NotSupportedException("We do not support shader expressions!"); return (int)state.parameter.data; } return -1; } } internal static class EffectWriterExtensions { public static void MgfxWriteElementCount(this BinaryWriter writer, int value) { if (EffectObject.MGFXVersion < 10) { writer.Write((byte)value); } else { writer.Write(value); } } #if NETFRAMEWORK private static Action write7BitEncodedIntFunc; public static void Write7BitEncodedInt(this BinaryWriter writer, int value) { if (write7BitEncodedIntFunc == null) { MethodInfo method = typeof(BinaryWriter).GetMethod("Write7BitEncodedInt", BindingFlags.Instance | BindingFlags.NonPublic); write7BitEncodedIntFunc = (Action)method.CreateDelegate(typeof(Action)); } write7BitEncodedIntFunc(writer, value); } #endif } } ================================================ FILE: WzComparerR2.Common/Rendering/EffectCompiler/Internal/ShaderData.cs ================================================ // Port from https://github.com/MonoGame/MonoGame/blob/develop/Tools/MonoGame.Effect.Compiler/Effect/ShaderData.cs using System; using System.Collections.Generic; using System.IO; using Microsoft.Xna.Framework.Graphics; namespace WzComparerR2.Rendering.EffectCompiler.Internal { public class ShaderData { public int[] _cbuffers; public Sampler[] _samplers; public Attribute[] _attributes; public byte[] ShaderCode; public bool IsVertexShader; public struct Sampler { public SamplerType type; public int textureSlot; public int samplerSlot; public string samplerName; public string parameterName; public int parameter; public SamplerState state; } public struct Attribute { public string name; public VertexElementUsage usage; public int index; public int location; } public void Write(BinaryWriter writer) { writer.Write(IsVertexShader); writer.Write(ShaderCode.Length); writer.Write(ShaderCode); writer.Write((byte)_samplers.Length); foreach (var sampler in _samplers) { writer.Write((byte)sampler.type); writer.Write((byte)sampler.textureSlot); writer.Write((byte)sampler.samplerSlot); if (sampler.state != null) { writer.Write(true); writer.Write((byte)sampler.state.AddressU); writer.Write((byte)sampler.state.AddressV); writer.Write((byte)sampler.state.AddressW); writer.Write(sampler.state.BorderColor.R); writer.Write(sampler.state.BorderColor.G); writer.Write(sampler.state.BorderColor.B); writer.Write(sampler.state.BorderColor.A); writer.Write((byte)sampler.state.Filter); writer.Write(sampler.state.MaxAnisotropy); writer.Write(sampler.state.MaxMipLevel); writer.Write(sampler.state.MipMapLevelOfDetailBias); } else writer.Write(false); writer.Write(sampler.samplerName); writer.Write((byte)sampler.parameter); } writer.Write((byte)_cbuffers.Length); foreach (var cb in _cbuffers) writer.Write((byte)cb); writer.Write((byte)_attributes.Length); foreach (var attrib in _attributes) { writer.Write(attrib.name); writer.Write((byte)attrib.usage); writer.Write((byte)attrib.index); writer.Write((short)attrib.location); } } } } ================================================ FILE: WzComparerR2.Common/Rendering/EffectCompiler/SampleInfo.cs ================================================ using System; using Microsoft.Xna.Framework.Graphics; namespace WzComparerR2.Rendering.EffectCompiler { public class SamplerInfo { public string Name { get; set; } public string TextureName { get; set; } public SamplerType Type { get; set; } public int TextureSlot { get; set; } public int SamplerSlot { get; set; } public SamplerState State { get; set; } } public enum SamplerType { Sampler2D, SamplerCube, SamplerVolume, Sampler1D } } ================================================ FILE: WzComparerR2.Common/Rendering/EffectCompiler/ShaderConverter.cs ================================================ using System; using System.Collections.Generic; using System.IO; using System.Linq; using Microsoft.Xna.Framework.Graphics; using WzComparerR2.Rendering.EffectCompiler.Internal; namespace WzComparerR2.Rendering.EffectCompiler { public static class ShaderConverter { public static byte[] D3DShaderByteCodeToMgfx(ReadOnlySpan shaderByteCode, ShaderStage shaderStage, IReadOnlyList constantBuffers, IReadOnlyList samplers) { var cbuffers = new List(); if (constantBuffers != null) { foreach (var constantBuffer in constantBuffers) { while (cbuffers.Count <= constantBuffer.Slot) { cbuffers.Add(null); } cbuffers[constantBuffer.Slot] = ConvertConstantBuffer(constantBuffer); } } // fill default constant buffer for (int i = 0; i < cbuffers.Count; i++) { if (cbuffers[i] == null) { cbuffers[i] = new ConstantBufferData() { Name = $"dummy_{i}", Size = 16, Parameters = new List(), }; } } var rawSamplers = new List(); if (samplers != null) { foreach (var sampler in samplers) { rawSamplers.Add(ConvertSampler(sampler)); } } var shaderData = new ShaderData() { _attributes = Array.Empty(), _samplers = rawSamplers.ToArray(), _cbuffers = Enumerable.Range(0, cbuffers.Count).Select(i=>i).ToArray(), ShaderCode = shaderByteCode.ToArray(), IsVertexShader = shaderStage == ShaderStage.Vertex, }; var effectObject = new EffectObject(); effectObject.ConstantBuffers = cbuffers; effectObject.Shaders = new List() { shaderData }; effectObject.Techniques = new d3dx_technique[1] { new d3dx_technique() { name = "tech0", pass_count = 1, pass_handles = new d3dx_pass[1] { new d3dx_pass() { name = "pass0", blendState = null, depthStencilState = null, rasterizerState = null, state_count = 2, states = new d3dx_state[2] { new d3dx_state() { type = STATE_TYPE.CONSTANT, operation = (int)STATE_CLASS.VERTEXSHADER, parameter = new d3dx_parameter() { data = shaderStage == ShaderStage.Vertex ? 0 : -1, } }, new d3dx_state() { type = STATE_TYPE.CONSTANT, operation = (int)STATE_CLASS.PIXELSHADER, parameter = new d3dx_parameter() { data = shaderStage == ShaderStage.Pixel ? 0 : -1, } } }, } }, } }; var parameters = new List(); foreach (var cb in effectObject.ConstantBuffers) { foreach (var param in cb.Parameters) { var match = parameters.FindIndex(e => e.name == param.name); if (match == -1) { cb.ParameterIndex.Add(parameters.Count); parameters.Add(param); } else { if (param.type != parameters[match].type || param.rows != parameters[match].rows || param.columns != parameters[match].columns || param.element_count != parameters[match].element_count) { throw new Exception($"Parameter {param.name} conflicts with existing parameter."); } cb.ParameterIndex.Add(match); } } } foreach (var shader in effectObject.Shaders) { for (var s = 0; s < shader._samplers.Length; s++) { var sampler = shader._samplers[s]; var match = parameters.FindIndex(e => e.name == sampler.parameterName); if (match == -1) { shader._samplers[s].parameter = parameters.Count; parameters.Add(CreateTextureParamater(sampler)); } else { // TODO: Make sure the type and size of the parameter match up! shader._samplers[s].parameter = match; } } } effectObject.Parameters = parameters.ToArray(); using var ms = new MemoryStream(); using var writer = new BinaryWriter(ms); effectObject.Write(writer); return ms.ToArray(); } private static ConstantBufferData ConvertConstantBuffer(ConstantBuffer constantBuffer) { var cb = new ConstantBufferData() { Name = constantBuffer.Name, Size = constantBuffer.SizeInBytes, ParameterIndex = new List(constantBuffer.Parameters.Count), ParameterOffset = new List(constantBuffer.Parameters.Count), Parameters = new List(constantBuffer.Parameters.Count), }; for (int i = 0; i < constantBuffer.Parameters.Count; i++) { var d3dxParam = ConvertShaderParameter(constantBuffer.Parameters[i]); cb.ParameterOffset.Add(d3dxParam.bufferOffset); cb.Parameters.Add(d3dxParam); } return cb; } private static d3dx_parameter ConvertShaderParameter(ShaderParameter parameter) { var d3dxParam = new d3dx_parameter() { name = parameter.Name, semantic = parameter.Semantic, bufferOffset = parameter.BufferOffset, rows = (uint)parameter.RowCount, columns = (uint)parameter.ColumnCount, class_ = parameter.ParameterClass, type = parameter.ParameterType, element_count = 0, member_count = 0, }; if (parameter.ElementCount > 0) // array { d3dxParam.element_count = (uint)parameter.ElementCount; d3dxParam.member_handles = new d3dx_parameter[parameter.ElementCount]; for (int i = 0; i < d3dxParam.member_handles.Length; i++) { d3dxParam.member_handles[i] = new d3dx_parameter() { name = string.Empty, semantic = string.Empty, rows = d3dxParam.rows, columns = d3dxParam.columns, class_ = parameter.ParameterClass, type = parameter.ParameterType, data = new byte[d3dxParam.rows * d3dxParam.columns * 4], }; } d3dxParam.data = new byte[d3dxParam.rows * d3dxParam.columns * 4]; } else { d3dxParam.member_handles = Array.Empty(); d3dxParam.data = new byte[d3dxParam.rows * d3dxParam.columns * 4]; } return d3dxParam; } private static ShaderData.Sampler ConvertSampler(SamplerInfo samplerInfo) { var sampler = new ShaderData.Sampler() { samplerName = samplerInfo.Name, textureSlot = samplerInfo.TextureSlot, samplerSlot = samplerInfo.SamplerSlot, parameterName = samplerInfo.TextureName, parameter = -1, type = samplerInfo.Type, state = samplerInfo.State, }; return sampler; } private static d3dx_parameter CreateTextureParamater(ShaderData.Sampler sampler) { var d3dxParam = new d3dx_parameter(); d3dxParam.class_ = EffectParameterClass.Object; d3dxParam.name = sampler.parameterName; d3dxParam.semantic = string.Empty; switch (sampler.type) { case SamplerType.Sampler1D: d3dxParam.type = EffectParameterType.Texture1D; break; case SamplerType.Sampler2D: d3dxParam.type = EffectParameterType.Texture2D; break; case SamplerType.SamplerVolume: d3dxParam.type = EffectParameterType.Texture3D; break; case SamplerType.SamplerCube: d3dxParam.type = EffectParameterType.TextureCube; break; } return d3dxParam; } } } ================================================ FILE: WzComparerR2.Common/Rendering/EffectCompiler/ShaderStage.cs ================================================ namespace WzComparerR2.Rendering.EffectCompiler { public enum ShaderStage { Vertex, Pixel } } ================================================ FILE: WzComparerR2.Common/Rendering/MonogameUtils.cs ================================================ using System; using System.Runtime.InteropServices; using GdipColor = System.Drawing.Color; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using SharpDX.Direct3D11; using Texture2D = Microsoft.Xna.Framework.Graphics.Texture2D; using WzComparerR2.WzLib; namespace WzComparerR2.Rendering { public static class MonogameUtils { public static Color ToXnaColor(this GdipColor color) { return new Color(color.R, color.G, color.B, color.A); } public static Color ToXnaColor(int argbPackedValue) { var bgra = BitConverter.GetBytes(argbPackedValue); return new Color(bgra[2], bgra[1], bgra[0], bgra[3]); } public static Color GetXnaColor(this Wz_Node node) { var argbColor = node.GetValueEx(0); return ToXnaColor(argbColor); } public static Texture2D CreateMosaic(GraphicsDevice device, Color c0, Color c1, int blockSize) { var t2d = new Texture2D(device, blockSize * 2, blockSize * 2, false, SurfaceFormat.Color); Color[] colorData = new Color[blockSize * blockSize * 4]; int offset = blockSize * blockSize * 2; for (int i = 0; i < blockSize; i++) { colorData[i] = c0; colorData[blockSize + i] = c1; colorData[offset + i] = c1; colorData[offset + blockSize + i] = c0; } for (int i = 1; i < blockSize; i++) { Array.Copy(colorData, 0, colorData, blockSize * 2 * i, blockSize * 2); Array.Copy(colorData, offset, colorData, offset + blockSize * 2 * i, blockSize * 2); } t2d.SetData(colorData); return t2d; } public static Texture2D ToTexture(this System.Drawing.Bitmap bitmap, GraphicsDevice device) { var t2d = new Texture2D(device, bitmap.Width, bitmap.Height, false, SurfaceFormat.Bgra32); bitmap.ToTexture(t2d, Point.Zero); return t2d; } public static void ToTexture(this System.Drawing.Bitmap bitmap, Texture2D texture, Point origin) { var rect = new System.Drawing.Rectangle(0, 0, bitmap.Width, bitmap.Height); var bmpData = bitmap.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb); byte[] buffer = new byte[bmpData.Stride * bmpData.Height]; Marshal.Copy(bmpData.Scan0, buffer, 0, buffer.Length); bitmap.UnlockBits(bmpData); texture.SetData(0, 0, new Rectangle(origin.X, origin.Y, rect.Width, rect.Height), buffer, 0, buffer.Length); } public static Device _d3dDevice(this GraphicsDevice device) { return (Device)device.Handle; } public static DeviceContext _d3dContext(this GraphicsDevice device) { var d3dContextField = typeof(GraphicsDevice).GetField("_d3dContext", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic); return (DeviceContext)d3dContextField.GetValue(device); } public static SharpDX.DXGI.SwapChain _swapChain(this GraphicsDevice device) { var _swapChainField = typeof(GraphicsDevice).GetField("_swapChain", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic); return (SharpDX.DXGI.SwapChain)_swapChainField.GetValue(device); } public static Resource GetTexture(this Texture texture) { var _getTextureFunc = typeof(Texture).GetMethod("GetTexture", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic); return (Resource)_getTextureFunc.Invoke(texture, Array.Empty()); } public static void CopyBackBuffer(this GraphicsDevice graphicsDevice, Texture2D destTexture) { var pp = graphicsDevice.PresentationParameters; if (pp.BackBufferWidth != destTexture.Width || pp.BackBufferHeight != destTexture.Height || pp.BackBufferFormat != destTexture.Format) { throw new Exception("Destination texture size or format does not compatible with back buffer."); } var d3dContext = graphicsDevice._d3dContext(); var swapChain = graphicsDevice._swapChain(); var dest = destTexture.GetTexture(); using SharpDX.Direct3D11.Texture2D source = SharpDX.Direct3D11.Resource.FromSwapChain(swapChain, 0); lock (d3dContext) { d3dContext.CopyResource(source, dest); } } public static bool IsSupportFormat(this GraphicsDevice device, SharpDX.DXGI.Format format) { var d3dDevice = device._d3dDevice(); var fmtSupport = d3dDevice.CheckFormatSupport(format); return (fmtSupport & SharpDX.Direct3D11.FormatSupport.Texture2D) != 0; } public static bool IsSupportBgra4444(this GraphicsDevice device) { return device.IsSupportFormat(SharpDX.DXGI.Format.B4G4R4A4_UNorm); } public static bool IsSupportBgr565(this GraphicsDevice device) { return device.IsSupportFormat(SharpDX.DXGI.Format.B5G6R5_UNorm); } public static bool IsSupportBgra5551(this GraphicsDevice device) { return device.IsSupportFormat(SharpDX.DXGI.Format.B5G5R5A1_UNorm); } } } ================================================ FILE: WzComparerR2.Common/Rendering/PngEffect.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Reflection; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; namespace WzComparerR2.Rendering { public class PngEffect : Effect { public PngEffect(GraphicsDevice graphicDevice) :base(graphicDevice, GetEffectCode()) { this.AlphaMixEnabled = false; this.MinMixedAlpha = 255; this.MixedColor = Color.White; } public bool AlphaMixEnabled { get { return alphaMixed; } set { this.CurrentTechnique = this.Techniques[value ? "tech1" : "tech0"]; this.alphaMixed = value; } } public int MinMixedAlpha { get { return (int)(this.Parameters["clipAlpha"].GetValueSingle() * 255); } set { this.Parameters["clipAlpha"].SetValue((float)value / 255); } } public Color MixedColor { get { return new Color(this.Parameters["mixedColor"].GetValueVector4()); } set { this.Parameters["mixedColor"].SetValue(value.ToVector4()); } } private bool alphaMixed; private static byte[] GetEffectCode() { var asm = Assembly.GetAssembly(typeof(PngEffect)); using (var input = asm.GetManifestResourceStream("WzComparerR2.Rendering.Effect.PngEffect.mgfxo")) { byte[] code = new byte[input.Length]; input.Read(code, 0, code.Length); return code; } } } } ================================================ FILE: WzComparerR2.Common/Rendering/SpriteBatchEx.cs ================================================ using System; using System.Collections.Generic; using System.Text; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using System.Drawing; using System.Drawing.Imaging; using System.Drawing.Drawing2D; using GDIColor = System.Drawing.Color; using GDIRect = System.Drawing.Rectangle; using Rectangle = Microsoft.Xna.Framework.Rectangle; using Color = Microsoft.Xna.Framework.Color; using Point = Microsoft.Xna.Framework.Point; namespace WzComparerR2.Rendering { public class SpriteBatchEx : SpriteBatch { public SpriteBatchEx(GraphicsDevice graphicsDevice) : base(graphicsDevice) { this.singlePixel = CreateSinglePixel(); } Texture2D singlePixel; private Texture2D CreateSinglePixel() { Texture2D pixel = new Texture2D(this.GraphicsDevice, 1, 1, false, SurfaceFormat.Color); pixel.SetData(new Color[] { Color.White }); return pixel; } public void DrawStringEx(XnaFont xnaFont, string text, Vector2 location, Color color) { DrawStringEx(xnaFont, text, 0, text == null ? 0 : text.Length, location, Vector2.Zero, color); } public void DrawStringEx(XnaFont xnaFont, string text, Vector2 location, Vector2 size, Color color) { DrawStringEx(xnaFont, text, 0, text == null ? 0 : text.Length, location, size, color); } private void DrawStringEx(XnaFont xnaFont, string text, int startIndex, int length, Vector2 location, Vector2 size, Color color) { IEnumerable e = TextUtils.CreateCharEnumerator(text, startIndex, length); DrawStringEx(xnaFont, e, location, size, color, Vector2.Zero, 0); } public void DrawStringEx(XnaFont xnaFont, StringBuilder stringBuilder, Vector2 location, Color color) { DrawStringEx(xnaFont, stringBuilder, 0, stringBuilder == null ? 0 : stringBuilder.Length, location, color, Vector2.Zero); } public void DrawStringEx(XnaFont xnaFont, StringBuilder stringBuilder, Vector2 location, Color color, Vector2 origin) { DrawStringEx(xnaFont, stringBuilder, 0, stringBuilder == null ? 0 : stringBuilder.Length, location, color, origin); } public void DrawStringEx(XnaFont xnaFont, StringBuilder stringBuilder, int startIndex, int length, Vector2 location, Color color) { DrawStringEx(xnaFont, stringBuilder, startIndex, length, location, color, Vector2.Zero); } public void DrawStringEx(XnaFont xnaFont, StringBuilder stringBuilder, int startIndex, int length, Vector2 location, Color color, Vector2 origin) { IEnumerable e = TextUtils.CreateCharEnumerator(stringBuilder, startIndex, length); DrawStringEx(xnaFont, e, location, Vector2.Zero, color, origin, 0); } private void DrawStringEx(XnaFont font, IEnumerable text, Vector2 location, Vector2 size, Color color, Vector2 origin, float layerDepth) { if (font == null || text == null) { return; } float dx = location.X, dy = location.Y; foreach (char c in text) { if (c == '\r') { continue; } else if (c == '\n') //换行符 { dy += font.Height; dx = location.X; continue; } else { Rectangle rect = font.TryGetRect(c); if (size.X > 0 && dx > location.X && dx + rect.Width > location.X + size.X) //强制换行 { dy += font.Height; dx = location.X; } base.Draw(font.TextureBuffer, new Vector2(dx, dy), rect, color, 0f, origin, 1f, SpriteEffects.None, layerDepth); dx += rect.Width; } } location.X = dx; location.Y = dy; } public void DrawPath(Point[] path, Color color) { Rectangle[] rectPath = new Rectangle[path.Length]; for (int i = 0; i < path.Length - 1; i++) { if (path[i].X == path[i + 1].X) { int dy = path[i + 1].Y - path[i].Y; if (dy > 0) { rectPath[i] = new Rectangle(path[i].X, path[i].Y, 1, dy); } else if (dy < 0) { rectPath[i] = new Rectangle(path[i].X, path[i + 1].Y + 1, 1, -dy); } } else if (path[i].Y == path[i + 1].Y) { int dx = path[i + 1].X - path[i].X; if (dx > 0) { rectPath[i] = new Rectangle(path[i].X, path[i].Y, dx, 1); } else if (dx < 0) { rectPath[i] = new Rectangle(path[i + 1].X + 1, path[i].Y, -dx, 1); } } } if (path[0] != path[path.Length - 1] || path.Length == 1) { rectPath[path.Length - 1] = new Rectangle( path[path.Length - 1].X, path[path.Length - 1].Y, 1, 1); } for (int i = 0; i < rectPath.Length; i++) { if (rectPath[i].Width > 0 && rectPath[i].Height > 0) { this.FillRectangle(rectPath[i], color); } } } public void FillRectangle(Rectangle rectangle, Color color) { base.Draw(singlePixel, rectangle, color); } public void FillRectangle(Rectangle rectangle, Color color, Vector2 origin) { rectangle.X -= (int)origin.X; rectangle.Y -= (int)origin.Y; base.Draw(singlePixel, rectangle, color); } public void FillRoundedRectangle(Rectangle rectangle, Color color) { if (rectangle.Width > 2 && rectangle.Height > 2) { base.Draw(singlePixel, new Rectangle(rectangle.X + 1, rectangle.Y, rectangle.Width - 2, 1), color); base.Draw(singlePixel, new Rectangle(rectangle.X, rectangle.Y + 1, rectangle.Width, rectangle.Height - 2), color); base.Draw(singlePixel, new Rectangle(rectangle.X + 1, rectangle.Bottom - 1, rectangle.Width - 2, 1), color); } else { base.Draw(singlePixel, rectangle, color); } } public void DrawRectangle(Rectangle rectangle, Color color) { if (!rectangle.IsEmpty) { Point[] path = new Point[5]; path[0] = new Point(rectangle.X, rectangle.Y); path[1] = new Point(path[0].X, rectangle.Y + rectangle.Height); path[2] = new Point(rectangle.X + rectangle.Width, path[1].Y); path[3] = new Point(path[2].X, path[0].Y); path[4] = path[0]; this.DrawPath(path, color); } } public void DrawLine(Point point1, Point point2, int width, Color color) { if (point1 != point2) { float length = Vector2.Distance(new Vector2(point1.X, point1.Y), new Vector2(point2.X, point2.Y)); Rectangle dest = new Rectangle(point1.X, point1.Y, (int)length, width); Vector2 origin = new Vector2(0, 0.5f); float rot = (float)Math.Atan2(point2.Y - point1.Y, point2.X - point1.X); this.Draw(this.singlePixel, dest, null, color, rot, origin, SpriteEffects.None, 0); } } public void Flush() { this.End(); this.GetType().BaseType.GetField("_beginCalled", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic) .SetValue(this, true); } protected override void Dispose(bool disposing) { if (disposing) { if (this.singlePixel != null) { this.singlePixel.Dispose(); } } base.Dispose(disposing); } } } ================================================ FILE: WzComparerR2.Common/Rendering/SurfaceFormatEx.cs ================================================ using System; using Microsoft.Xna.Framework.Graphics; namespace WzComparerR2.Rendering { public static class SurfaceFormatEx { public const SurfaceFormat BC7 = (SurfaceFormat)0x101; public const SurfaceFormat R16 = (SurfaceFormat)0x102; } } ================================================ FILE: WzComparerR2.Common/Rendering/TextUtils.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace WzComparerR2.Rendering { internal static class TextUtils { public static IEnumerable CreateCharEnumerator(string text, int startIndex, int length) { for (int i = 0; i < length; i++) { yield return text[startIndex + i]; } } public static IEnumerable CreateCharEnumerator(StringBuilder stringBuilder, int startIndex, int length) { for (int i = 0; i < length; i++) { yield return stringBuilder[startIndex + i]; } } } } ================================================ FILE: WzComparerR2.Common/Rendering/Texture2DEx.cs ================================================ using System; using System.Reflection; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using SharpDX.Direct3D11; using Texture2D = Microsoft.Xna.Framework.Graphics.Texture2D; namespace WzComparerR2.Rendering { public static class Texture2DEx { public static Texture2D CreateEx(GraphicsDevice graphicsDevice, int width, int height, SurfaceFormat format) { SharpDX.DXGI.Format dxgiFormat = format switch { SurfaceFormatEx.BC7 => SharpDX.DXGI.Format.BC7_UNorm, SurfaceFormatEx.R16 => SharpDX.DXGI.Format.R16_UNorm, _ => throw new ArgumentException($"Unsupported texture format {format}") }; var texture = new Texture2D(graphicsDevice, width, height, false, format); TextureInitialize(texture, dxgiFormat); return texture; } public static unsafe void SetDataEx(this Texture2D texture, ReadOnlySpan data, int pitch) { var region = new Rectangle(0, 0, texture.Width, texture.Height); int expectedDataSize = texture.Format switch { SurfaceFormatEx.BC7 => pitch * (region.Height / 4), // (w/4)*(h/4)*16 SurfaceFormatEx.R16 => region.Height * pitch, _ => throw new ArgumentException($"Unsupported texture format {texture.Format}") }; if (data.Length < expectedDataSize) throw new ArgumentException($"Incorrect data length ({data.Length} < {expectedDataSize}).", nameof(data)); TextureSetData(texture, region, data, pitch); } private static void TextureInitialize(Texture2D texture2D, SharpDX.DXGI.Format format) { Texture2DDescription description = new Texture2DDescription { Width = texture2D.Width, Height = texture2D.Height, MipLevels = 1, ArraySize = 1, Format = format, BindFlags = BindFlags.ShaderResource, CpuAccessFlags = CpuAccessFlags.None, Usage = ResourceUsage.Default, OptionFlags = ResourceOptionFlags.None, SampleDescription = new SharpDX.DXGI.SampleDescription() { Count = 1, Quality = 0, } }; var _device = texture2D.GraphicsDevice._d3dDevice(); var _textureField = typeof(Texture).GetField("_texture", BindingFlags.Instance | BindingFlags.NonPublic); Resource dx11Texture = new SharpDX.Direct3D11.Texture2D(_device, description); Resource oldTexture = _textureField.GetValue(texture2D) as Resource; _textureField.SetValue(texture2D, dx11Texture); SharpDX.Utilities.Dispose(ref oldTexture); } private static unsafe void TextureSetData(Texture2D texture2D, Rectangle region, ReadOnlySpan data, int pitch) { fixed (byte* pData = data) { var dataPtr = new IntPtr(pData); var resourceRegion = new ResourceRegion() { Top = region.Top, Front = 0, Back = 1, Bottom = region.Bottom, Left = region.Left, Right = region.Right, }; var d3dContext = texture2D.GraphicsDevice._d3dContext(); lock (d3dContext) d3dContext.UpdateSubresource(texture2D.GetTexture(), 0, resourceRegion, dataPtr, pitch, 0); } } } } ================================================ FILE: WzComparerR2.Common/Rendering/WzLibExtension.cs ================================================ using System; using System.Buffers; using WzComparerR2.WzLib; using WzComparerR2.WzLib.Utilities; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; namespace WzComparerR2.Rendering { public static class WzLibExtension { public static Texture2D ToTexture(this Wz_Png png, GraphicsDevice graphicsDevice) { return ToTexture(png, 0, graphicsDevice); } public static Texture2D ToTexture(this Wz_Png png, int page, GraphicsDevice graphicsDevice) { var format = GetTextureFormatOfPng(png.Format); if (format == SurfaceFormat.Bgra4444) { //检测是否支持 pre-win8 if (!graphicsDevice.IsSupportBgra4444()) { format = SurfaceFormat.Bgra32; } } else if (format == SurfaceFormat.Bgr565) { //检测是否支持 pre-win8 if (!graphicsDevice.IsSupportBgr565()) { format = SurfaceFormat.Bgra32; } } else if (format == SurfaceFormat.Bgra5551) { //检测是否支持 pre-win8 if (!graphicsDevice.IsSupportBgra5551()) { format = SurfaceFormat.Bgra32; } } Texture2D t2d = format switch { SurfaceFormatEx.BC7 => Texture2DEx.CreateEx(graphicsDevice, png.Width & ~3, png.Height & ~3, format), SurfaceFormatEx.R16 => Texture2DEx.CreateEx(graphicsDevice, png.Width, png.Height, format), _ => new Texture2D(graphicsDevice, png.Width, png.Height, false, format), }; png.ToTexture(page, t2d, Point.Zero); return t2d; } public static void ToTexture(this Wz_Png png, int page, Texture2D texture, Point origin) { Rectangle rect = new Rectangle(origin, new Point(png.Width, png.Height)); if (png.Format == Wz_TextureFormat.BC7) { rect.Width = png.Width & ~3; rect.Height = png.Height & ~3; } //检查大小 if (rect.X < 0 || rect.Y < 0 || rect.Right > texture.Width || rect.Bottom > texture.Height) { throw new ArgumentException("Png rectangle is out of bounds."); } if (texture.Format == SurfaceFormat.Bgra32 && png.Format != Wz_TextureFormat.ARGB8888) { // soft decoding using (var bmp = png.ExtractPng(page)) { bmp.ToTexture(texture, origin); } } else if (texture.Format != GetTextureFormatOfPng(png.Format)) { throw new ArgumentException($"Texture format({texture.Format}) does not fit the png form({png.Format})."); } else { int bufferSize = png.GetRawDataSizePerPage(); byte[] rawData = ArrayPool.Shared.Rent(bufferSize); int actualBytes = png.GetRawData(bufferSize * page, rawData.AsSpan(0, bufferSize)); if (actualBytes != bufferSize) { throw new ArgumentException($"Not enough bytes have been read. (actual:{actualBytes}, expected:{bufferSize})"); } switch (png.Format) { case Wz_TextureFormat.ARGB4444 when png.ActualScale == 1: case Wz_TextureFormat.ARGB8888 when png.ActualScale == 1: case Wz_TextureFormat.ARGB1555 when png.ActualScale == 1: case Wz_TextureFormat.RGB565 when png.ActualScale == 1: case Wz_TextureFormat.DXT3 when png.ActualScale == 1: case Wz_TextureFormat.DXT5 when png.ActualScale == 1: case Wz_TextureFormat.RGBA1010102 when png.ActualScale == 1: texture.SetData(0, 0, rect, rawData, 0, bufferSize); break; case Wz_TextureFormat.RGB565 when png.ActualScale == 16: int textureDataSize = png.Width * png.Height * 2; byte[] textureData = ArrayPool.Shared.Rent(textureDataSize); ImageCodec.ScalePixels(rawData, 2, png.Width / png.ActualScale, png.Width / png.ActualScale * 2, png.Height / png.ActualScale, png.ActualScale, png.ActualScale, textureData.AsSpan(0, textureDataSize), png.Width * 2); texture.SetData(0, 0, rect, textureData, 0, textureDataSize); ArrayPool.Shared.Return(textureData); break; case Wz_TextureFormat.BC7 when png.ActualScale == 1: texture.SetDataEx(rawData.AsSpan(0, bufferSize), png.Width * 4); break; case Wz_TextureFormat.R16 when png.ActualScale == 1: texture.SetDataEx(rawData.AsSpan(0, bufferSize), png.Width * 2); break; default: throw new Exception($"Unsupported png format ({png.Format}, scale={png.ActualScale})."); } ArrayPool.Shared.Return(rawData); } } public static SurfaceFormat GetTextureFormatOfPng(Wz_TextureFormat textureFormat) { switch (textureFormat) { case Wz_TextureFormat.ARGB4444: return SurfaceFormat.Bgra4444; case Wz_TextureFormat.ARGB8888: return SurfaceFormat.Bgra32; case Wz_TextureFormat.ARGB1555: return SurfaceFormat.Bgra5551; case Wz_TextureFormat.RGB565: return SurfaceFormat.Bgr565; case Wz_TextureFormat.R16: return SurfaceFormatEx.R16; case Wz_TextureFormat.DXT3: return SurfaceFormat.Dxt3; case Wz_TextureFormat.DXT5: return SurfaceFormat.Dxt5; case Wz_TextureFormat.A8: return SurfaceFormat.Alpha8; case Wz_TextureFormat.RGBA1010102: return SurfaceFormat.Rgba1010102; case Wz_TextureFormat.DXT1: return SurfaceFormat.Dxt1; case Wz_TextureFormat.BC7: return SurfaceFormatEx.BC7; case Wz_TextureFormat.RGBA32Float: return SurfaceFormat.Vector4; default: return SurfaceFormat.Bgra32; } } public static Point ToPoint(this Wz_Vector vector) { return new Point(vector.X, vector.Y); } } } ================================================ FILE: WzComparerR2.Common/Rendering/XnaFont.cs ================================================ using System; using System.Collections.Generic; using System.Text; using System.Drawing; using System.Drawing.Imaging; using System.Drawing.Drawing2D; using System.Drawing.Text; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using GDIColor = System.Drawing.Color; using GDIRect = System.Drawing.Rectangle; using Rectangle = Microsoft.Xna.Framework.Rectangle; namespace WzComparerR2.Rendering { public class XnaFont : IDisposable { public XnaFont(GraphicsDevice graphicsDevice, FontFamily fontFamily, float size) : this(graphicsDevice, new Font(fontFamily, size, GraphicsUnit.Pixel)) { } public XnaFont(GraphicsDevice graphicsDevice, string familyName, float size) : this(graphicsDevice, new Font(familyName, size, GraphicsUnit.Pixel)) { } public XnaFont(GraphicsDevice graphicsDevice, Font baseFont) { if (graphicsDevice == null || baseFont == null) { throw new ArgumentNullException(); } this.baseFont = baseFont; this.Height = baseFont.Height; this.textureBuffer = new Texture2D(graphicsDevice, 2048, 2048, false, SurfaceFormat.Bgra32); RebuildGdiBuffer(this.baseFont.Height); this.charLocation = new Dictionary(); } Font baseFont; Texture2D textureBuffer; Bitmap gdiBuffer; Graphics g; Dictionary charLocation; int textureSpaceX; int textureSpaceY; int textureCurLineHeight; int gdiBufferX; public Texture2D TextureBuffer { get { return textureBuffer; } } public Font BaseFont { get { return baseFont; } } public int Height { get; set; } public Vector2 MeasureString(string text) { return MeasureString(text, Vector2.Zero); } public Vector2 MeasureString(StringBuilder stringBuilder) { return MeasureString(stringBuilder, Vector2.Zero); } public Vector2 MeasureString(string text, Vector2 size) { var ie = TextUtils.CreateCharEnumerator(text, 0, text.Length); return MeasureString(ie, size); } public Vector2 MeasureString(StringBuilder stringBuilder, Vector2 size) { var ie = TextUtils.CreateCharEnumerator(stringBuilder, 0, stringBuilder.Length); return MeasureString(ie, size); } public Vector2 MeasureString(string text, int startIndex, int length) { return MeasureString(TextUtils.CreateCharEnumerator(text, startIndex, length), Vector2.Zero); } public Vector2 MeasureString(StringBuilder stringBuilder, int startIndex, int length) { return MeasureString(TextUtils.CreateCharEnumerator(stringBuilder, startIndex, length), Vector2.Zero); } public Vector2 MeasureString(IEnumerable text, Vector2 layoutSize) { if (text == null) return Vector2.Zero; Size size = new Size(); int maxWidth = 0; int lineHeight = 0; foreach (char c in text) { if (c == '\r') { continue; } if (c == '\n') { if (lineHeight <= 0) { //lineHeight = this.baseFont.Height; } size.Height += this.baseFont.Height; maxWidth = Math.Max(maxWidth, size.Width); size.Width = 0; lineHeight = 0; } else { Rectangle rect = TryGetRect(c); if (layoutSize.X > 0 && size.Width > 0 && size.Width + rect.Width > layoutSize.X) //强制换行 { size.Height += this.baseFont.Height; maxWidth = Math.Max(maxWidth, size.Width); size.Width = 0; lineHeight = 0; } size.Width += rect.Width; lineHeight = Math.Max(rect.Height, lineHeight); } } size.Width = Math.Max(maxWidth, size.Width); size.Height += lineHeight; if (size.Width <= 0) return Vector2.Zero; return new Vector2(size.Width, size.Height); } public Rectangle TryGetRect(char c) { Rectangle rect; if (!this.charLocation.TryGetValue(c, out rect)) { rect = this.CreateCharBuffer(c); this.charLocation[c] = rect; } return rect; } private Rectangle CreateCharBuffer(char c) { string text = c.ToString(); SizeF size; size = g.MeasureString(text, this.baseFont, byte.MaxValue, StringFormat.GenericTypographic); GDIRect originRect = new GDIRect(gdiBufferX, 0, (int)Math.Ceiling(size.Width), (int)Math.Ceiling(size.Height)); if (originRect.Width == 0) { originRect.Width = (int)(this.baseFont.Size / 2); } if (gdiBuffer.Height < originRect.Height) { RebuildGdiBuffer(originRect.Height); originRect.X = 0; //2012-10-3 } if (gdiBufferX + originRect.Width > gdiBuffer.Width) { g.Clear(GDIColor.Transparent); gdiBufferX = 0; originRect.X = 0; } g.DrawString(text, baseFont, Brushes.White, originRect.Location, StringFormat.GenericTypographic); //计算范围并且复制图像数据到数组 byte[] b = new byte[4 * originRect.Width * originRect.Height]; BitmapData data = gdiBuffer.LockBits(originRect, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); for (int i = 0; i < originRect.Height; i++) { IntPtr source = IntPtr.Add(data.Scan0, data.Stride * i); System.Runtime.InteropServices.Marshal.Copy(source, b, i * 4 * originRect.Width, 4 * originRect.Width); } gdiBuffer.UnlockBits(data); gdiBufferX += originRect.Width; //调整xnaTexture的大小并粘贴图像 textureBuffer.GraphicsDevice.Textures[0] = null; if (textureSpaceX + originRect.Width > textureBuffer.Width) { textureSpaceX = 0; textureSpaceY += textureCurLineHeight; textureCurLineHeight = 0; } textureCurLineHeight = Math.Max(textureCurLineHeight, originRect.Height); if (textureSpaceY + textureCurLineHeight > textureBuffer.Height) { ClearTextureBuffer(); textureSpaceX = 0; textureSpaceY = 0; charLocation.Clear(); } Rectangle rect = new Rectangle(textureSpaceX, textureSpaceY, originRect.Width, originRect.Height); textureBuffer.SetData(0, rect, b, 0, b.Length); textureSpaceX += rect.Width; return rect; } private void RebuildGdiBuffer(int height) { if (gdiBuffer != null) { g.Dispose(); gdiBuffer.Dispose(); } gdiBuffer = new Bitmap(textureBuffer.Width, height, PixelFormat.Format32bppArgb); gdiBufferX = 0; g = Graphics.FromImage(gdiBuffer); g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit; g.SmoothingMode = SmoothingMode.HighQuality; // g.CompositingMode = CompositingMode.SourceCopy; 乱用这句出事故... } private void ClearTextureBuffer() { int[] ary = new int[textureBuffer.Width * textureBuffer.Height]; textureBuffer.SetData(ary); ary = null; } ~XnaFont() { this.Dispose(false); } public void Dispose() { this.Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (disposing) { this.textureBuffer.Dispose(); this.g.Dispose(); this.baseFont.Dispose(); this.gdiBuffer.Dispose(); } } public static FontFamily GdiLoadFontFile(string fontFileName) { try { PrivateFontCollection font = new PrivateFontCollection(); font.AddFontFile(fontFileName); gdiFontCache.Add(font); return font.Families[0]; } catch { return null; } } private static List gdiFontCache = new List(); } } ================================================ FILE: WzComparerR2.Common/Rendering/XnaFontRenderer.cs ================================================ using System; using System.Collections.Generic; using System.Drawing; using System.Linq; using System.Text; using WzComparerR2.Text; namespace WzComparerR2.Rendering { public class XnaFontRenderer : TextRenderer { public XnaFontRenderer(SpriteBatchEx spriteBatch) { this.SpriteBatch = spriteBatch; } public SpriteBatchEx SpriteBatch { get; set; } protected override void MeasureRuns(List runs) { int x = 0; foreach (var run in runs) { if (run.IsBreakLine) { run.X = x; run.Length = 0; } else { var size = base.font.MeasureString(base.sb, run.StartIndex, run.Length); run.X = x; run.Width = (int)size.X; x += run.Width; } } } protected override Rectangle[] MeasureChars(int startIndex, int length) { var regions = new Rectangle[length]; int x = 0; for (int i = 0; i < length; i++) { var rect = this.font.TryGetRect(this.sb[startIndex + i]); regions[i] = new Rectangle(x, 0, rect.Width, rect.Height); x += rect.Width; } return regions; } protected override void Flush(StringBuilder sb, int startIndex, int length, int x, int y, string colorID) { var color = this.GetColor(colorID); var pos = new Microsoft.Xna.Framework.Vector2(x, y); this.SpriteBatch.DrawStringEx(this.font, sb, startIndex, length, pos, color); } public virtual Microsoft.Xna.Framework.Color GetColor(string colorID) { switch (colorID) { case "c": return new Microsoft.Xna.Framework.Color(255, 153, 0); default: return Microsoft.Xna.Framework.Color.White; } } } } ================================================ FILE: WzComparerR2.Common/SpineLoader.cs ================================================ using System; using System.Collections.Generic; using System.IO; using WzComparerR2.WzLib; namespace WzComparerR2.Common { public static class SpineLoader { private const string AtlasExtension = ".atlas"; private const string JsonExtension = ".json"; private const string SkelExtension = ".skel"; private const string SharedAtlasNodeName = "atlas"; public static SpineDetectionResult Detect(Wz_Node wzNode) { if (wzNode == null || wzNode.ParentNode == null) { return SpineDetectionResult.Failed("WzNode or its parent cannot be null."); } Wz_Node parentNode = wzNode.ParentNode; Wz_Node atlasNode = null; Wz_Node skelNode = null; SkeletonLoadType loadType; SpineVersion spineVersion; if (wzNode.Text.EndsWith(AtlasExtension)) // detect from atlasNode { atlasNode = wzNode; string spineName = atlasNode.Text.Substring(0, atlasNode.Text.Length - AtlasExtension.Length); // find skel node in sibling nodes if ((skelNode = parentNode.Nodes[spineName + JsonExtension]) != null) { loadType = SkeletonLoadType.Json; } else if ((skelNode = parentNode.Nodes[spineName] ?? parentNode.Nodes[spineName + SkelExtension]) != null) { loadType = SkeletonLoadType.Binary; } else { return SpineDetectionResult.Failed("Failed to find skel node."); } } else // detect from skel node { skelNode = wzNode; string spineName = null; if (skelNode.Text.EndsWith(JsonExtension)) { spineName = skelNode.Text.Substring(0, skelNode.Text.Length - JsonExtension.Length); loadType = SkeletonLoadType.Json; } else if (skelNode.Text.EndsWith(SkelExtension)) { spineName = skelNode.Text.Substring(0, skelNode.Text.Length - SkelExtension.Length); loadType = SkeletonLoadType.Binary; } else { switch (skelNode.ResolveUol()?.Value) { case Wz_Sound sound when sound.SoundType == Wz_SoundType.Binary: case Wz_RawData rawData: spineName = skelNode.Text; loadType = SkeletonLoadType.Binary; break; default: return SpineDetectionResult.Failed("Failed to infer the wzNode as atlasNode or skelNode."); } } if (spineName != null) { // find atlas node in sibling nodes // KMST 1172: the atlas node name could be constant atlasNode = parentNode.Nodes[spineName + AtlasExtension] ?? parentNode.Nodes[SharedAtlasNodeName]; if (atlasNode == null) { return SpineDetectionResult.Failed("Failed to find atlas node."); } } } // resolve uols if ((atlasNode = atlasNode.ResolveUol()) == null) { return SpineDetectionResult.Failed("Failed to resolve uol for atlasNode."); } if ((skelNode = skelNode.ResolveUol()) == null) { return SpineDetectionResult.Failed("Failed to resolve uol for skelNode."); } // check atlas data type if (atlasNode.Value is not string) { return SpineDetectionResult.Failed("AtlasNode does not contain a string value."); } // inference spine version string versionStr = null; switch (loadType) { case SkeletonLoadType.Json when skelNode.Value is string json: versionStr = ReadSpineVersionFromJson(json); break; case SkeletonLoadType.Binary when skelNode.Value is Wz_Sound wzSound && wzSound.SoundType == Wz_SoundType.Binary: case SkeletonLoadType.Binary when skelNode.Value is Wz_RawData wzRawData: var blob = skelNode.Value as IMapleStoryBlob; var data = new byte[blob.Length]; blob.CopyTo(data, 0); var ms = new MemoryStream(data); versionStr = ReadSpineVersionFromBinary(ms, 0, blob.Length); break; } if (versionStr == null) { return SpineDetectionResult.Failed($"Failed to read version string from skel {loadType}."); } if (!Version.TryParse(versionStr, out var version)) { return SpineDetectionResult.Failed($"Failed to parse version '{versionStr}'."); } switch (version.Major) { case 2: spineVersion = SpineVersion.V2; break; case 4: spineVersion = SpineVersion.V4; break; default: return SpineDetectionResult.Failed($"Spine version '{versionStr}' is not supported."); ; } return SpineDetectionResult.Create(wzNode, atlasNode, skelNode, loadType, spineVersion); } private static string ReadSpineVersionFromJson(string jsonText) { // { "skeleton": { "spine": "2.1.27" } } using var sr = new StringReader(jsonText); object skelObj = Spine.Json.Deserialize(sr); if (skelObj is IDictionary jRootDict && jRootDict.TryGetValue("skeleton", out var jSkeleton) && jSkeleton is IDictionary jSkeletonDict && jSkeletonDict.TryGetValue("spine", out var jSpine) && Version.TryParse(jSpine as string, out var spineVer)) { return jSpine as string; } return null; } private static string ReadSpineVersionFromBinary(Stream stream, uint offset, int length) { /* * v4 format: * 00-07 hash * 08 version len * 09-XX version (len-1 bytes) * * v2 format: * 00 hash len * 01-XX hash (len-1 bytes) * (XX+1) version len * (XX+2)-YY version (len-1 bytes) */ long oldPos = stream.Position; try { stream.Position = offset; // this method can detect version from v4 and pre-v3 file format. string version = Spine.SkeletonBinary.GetVersionString(stream); return version; } catch { // ignore error; return null; } finally { stream.Position = oldPos; } } public static Spine.V2.SkeletonData LoadSkeletonV2(Wz_Node wzNode, Spine.V2.TextureLoader textureLoader) { var detectionResult = Detect(wzNode); if (detectionResult.Success && detectionResult.Version == SpineVersion.V2) { return LoadSkeletonV2(detectionResult, textureLoader); } return null; } public static Spine.V2.SkeletonData LoadSkeletonV2(SpineDetectionResult detectionResult, Spine.V2.TextureLoader textureLoader) { using var atlasReader = new StringReader((string)detectionResult.ResolvedAtlasNode.Value); var atlas = new Spine.V2.Atlas(atlasReader, "", textureLoader); switch (detectionResult.LoadType) { case SkeletonLoadType.Json: using (var skeletonReader = new StringReader((string)detectionResult.ResolvedSkelNode.Value)) { var skeletonJson = new Spine.V2.SkeletonJson(atlas); return skeletonJson.ReadSkeletonData(skeletonReader); } case SkeletonLoadType.Binary when detectionResult.ResolvedSkelNode.Value is IMapleStoryBlob blob: byte[] data = new byte[blob.Length]; blob.CopyTo(data, 0); var ms = new MemoryStream(data); var skeletonBinary = new Spine.V2.SkeletonBinary(atlas); return skeletonBinary.ReadSkeletonData(ms); default: return null; } } public static Spine.SkeletonData LoadSkeletonV4(Wz_Node atlasOrSkelNode, Spine.TextureLoader textureLoader) { var detectionResult = Detect(atlasOrSkelNode); if (detectionResult.Success && detectionResult.Version == SpineVersion.V4) { return LoadSkeletonV4(detectionResult, textureLoader); } return null; } public static Spine.SkeletonData LoadSkeletonV4(SpineDetectionResult detectionResult, Spine.TextureLoader textureLoader) { using var atlasReader = new StringReader((string)detectionResult.ResolvedAtlasNode.Value); var atlas = new Spine.Atlas(atlasReader, "", textureLoader); switch (detectionResult.LoadType) { case SkeletonLoadType.Json: using (var skeletonReader = new StringReader((string)detectionResult.ResolvedSkelNode.Value)) { var skeletonJson = new Spine.SkeletonJson(atlas); return skeletonJson.ReadSkeletonData(skeletonReader); } case SkeletonLoadType.Binary when detectionResult.ResolvedSkelNode.Value is IMapleStoryBlob blob: byte[] data = new byte[blob.Length]; blob.CopyTo(data, 0); var ms = new MemoryStream(data); var skeletonBinary = new Spine.SkeletonBinary(atlas); return skeletonBinary.ReadSkeletonData(ms); default: return null; } } } public enum SkeletonLoadType { None = 0, Json = 1, Binary = 2, } public enum SpineVersion { Unknown = 0, V2 = 2, V4 = 4 } public sealed class SpineDetectionResult { internal SpineDetectionResult() { } public bool Success { get; internal set; } public string ErrorDetail { get; internal set; } public Wz_Node SourceNode { get; internal set; } public Wz_Node ResolvedAtlasNode { get; internal set; } public Wz_Node ResolvedSkelNode { get; internal set; } public SkeletonLoadType LoadType { get; internal set; } public SpineVersion Version { get; internal set; } public static SpineDetectionResult Failed(string error = null) => new SpineDetectionResult { Success = false, ErrorDetail = error, }; public static SpineDetectionResult Create(Wz_Node sourceNode, Wz_Node atlasNode, Wz_Node skelNode, SkeletonLoadType loadType, SpineVersion version) => new SpineDetectionResult { Success = true, ErrorDetail = null, SourceNode = sourceNode, ResolvedAtlasNode = atlasNode, ResolvedSkelNode = skelNode, LoadType = loadType, Version = version }; } } ================================================ FILE: WzComparerR2.Common/StringLinker.cs ================================================ using System; using System.Collections.Generic; using System.Text; using WzComparerR2.WzLib; namespace WzComparerR2.Common { public class StringLinker { public StringLinker() { stringEqp = new Dictionary(); stringFamiliarSkill = new Dictionary(); stringItem = new Dictionary(); stringMap = new Dictionary(); stringMob = new Dictionary(); stringNpc = new Dictionary(); stringSkill = new Dictionary(); stringSkill2 = new Dictionary(); } public bool Load(Wz_File stringWz) { if (stringWz == null || stringWz.Node == null) return false; this.Clear(); int id; foreach (Wz_Node node in stringWz.Node.Nodes) { Wz_Image image = node.Value as Wz_Image; if (image == null) continue; switch (node.Text) { case "Pet.img": case "Cash.img": case "Ins.img": case "Consume.img": if (!image.TryExtract()) break; foreach (Wz_Node tree in image.Node.Nodes) { if (Int32.TryParse(tree.Text, out id) && tree.ResolveUol() is Wz_Node linkNode) { StringResult strResult = new StringResult(); strResult.Name = GetDefaultString(linkNode, "name"); strResult.Desc = GetDefaultString(linkNode, "desc"); strResult.AutoDesc = GetDefaultString(linkNode, "autodesc"); strResult.FullPath = tree.FullPath; // always use the original node path AddAllValue(strResult, linkNode); stringItem[id] = strResult; } } break; case "Etc.img": if (!image.TryExtract()) break; foreach (Wz_Node tree0 in image.Node.Nodes) { foreach (Wz_Node tree in tree0.Nodes) { if (Int32.TryParse(tree.Text, out id) && tree.ResolveUol() is Wz_Node linkNode) { StringResult strResult = new StringResult(); strResult.Name = GetDefaultString(linkNode, "name"); strResult.Desc = GetDefaultString(linkNode, "desc"); strResult.FullPath = tree.FullPath; AddAllValue(strResult, linkNode); stringItem[id] = strResult; } } } break; case "Familiar.img": case "FamiliarSkill.img": if (!image.TryExtract()) break; foreach (Wz_Node tree0 in image.Node.Nodes) { if (tree0.Text == "skill") { foreach (Wz_Node tree1 in tree0.Nodes) { if (Int32.TryParse(tree1.Text, out id) && tree1.ResolveUol() is Wz_Node linkNode) { StringResult strResult = null; if (strResult == null) strResult = new StringResult(); strResult.Name = GetDefaultString(linkNode, "name") ?? strResult.Name ?? string.Empty; strResult.Desc = GetDefaultString(linkNode, "desc") ?? strResult.Desc; strResult.FullPath = tree1.FullPath; AddAllValue(strResult, linkNode); stringFamiliarSkill[id] = strResult; } } } } break; case "Mob.img": if (!image.TryExtract()) break; foreach (Wz_Node tree in image.Node.Nodes) { if (Int32.TryParse(tree.Text, out id) && tree.ResolveUol() is Wz_Node linkNode) { StringResult strResult = new StringResult(); strResult.Name = GetDefaultString(linkNode, "name"); strResult.FullPath = tree.FullPath; AddAllValue(strResult, linkNode); stringMob[id] = strResult; } } break; case "Npc.img": if (!image.TryExtract()) break; foreach (Wz_Node tree in image.Node.Nodes) { if (Int32.TryParse(tree.Text, out id) && tree.ResolveUol() is Wz_Node linkNode) { StringResult strResult = new StringResult(); strResult.Name = GetDefaultString(linkNode, "name"); strResult.Desc = GetDefaultString(linkNode, "func"); strResult.FullPath = tree.FullPath; AddAllValue(strResult, linkNode); stringNpc[id] = strResult; } } break; case "Map.img": if (!image.TryExtract()) break; foreach (Wz_Node tree0 in image.Node.Nodes) { foreach (Wz_Node tree in tree0.Nodes) { if (Int32.TryParse(tree.Text, out id) && tree.ResolveUol() is Wz_Node linkNode) { StringResult strResult = new StringResult(); strResult.Name = string.Format("{0}:{1}", GetDefaultString(linkNode, "streetName"), GetDefaultString(linkNode, "mapName")); strResult.Desc = GetDefaultString(linkNode, "mapDesc"); strResult.FullPath = tree.FullPath; AddAllValue(strResult, linkNode); stringMap[id] = strResult; } } } break; case "Skill.img": if (!image.TryExtract()) break; foreach (Wz_Node tree in image.Node.Nodes) { if (tree.ResolveUol() is not Wz_Node linkNode) { continue; } StringResultSkill strResult = new StringResultSkill(); strResult.Name = GetDefaultString(linkNode, "name");//?? GetDefaultString(tree, "bookName"); strResult.Desc = GetDefaultString(linkNode, "desc"); strResult.Pdesc = GetDefaultString(linkNode, "pdesc"); strResult.SkillH.Add(GetDefaultString(linkNode, "h")); strResult.SkillpH.Add(GetDefaultString(linkNode, "ph")); strResult.SkillhcH.Add(GetDefaultString(linkNode, "hch")); if (strResult.SkillH[0] == null) { strResult.SkillH.RemoveAt(0); for (int i = 1; ; i++) { string hi = GetDefaultString(linkNode, "h" + i); if (string.IsNullOrEmpty(hi)) break; strResult.SkillH.Add(hi); } } // KMST1196, add h_ prefix strings foreach (Wz_Node child in linkNode.Nodes) { if (child.Text.StartsWith("h_") && int.TryParse(child.Text.Substring(2), out int level) && level > 0 && child.Value != null) { strResult.SkillExtraH.Add(new KeyValuePair(level, child.GetValue())); } } if (strResult.SkillExtraH.Count > 1) { strResult.SkillExtraH.Sort((left, right) => left.Key.CompareTo(right.Key)); } strResult.SkillH.TrimExcess(); strResult.SkillpH.TrimExcess(); strResult.FullPath = tree.FullPath; AddAllValue(strResult, linkNode); if (tree.Text.Length >= 7 && Int32.TryParse(tree.Text, out id)) { stringSkill[id] = strResult; } stringSkill2[tree.Text] = strResult; } break; case "Eqp.img": if (!image.TryExtract()) break; foreach (Wz_Node tree0 in image.Node.Nodes) { foreach (Wz_Node tree1 in tree0.Nodes) { foreach (Wz_Node tree in tree1.Nodes) { if (Int32.TryParse(tree.Text, out id) && tree.ResolveUol() is Wz_Node linkNode) { StringResult strResult = new StringResult(); strResult.Name = GetDefaultString(linkNode, "name"); strResult.Desc = GetDefaultString(linkNode, "desc"); strResult.FullPath = tree.FullPath; AddAllValue(strResult, linkNode); stringEqp[id] = strResult; } } } } break; } } return this.HasValues; } public void Clear() { stringEqp.Clear(); stringFamiliarSkill.Clear(); stringItem.Clear(); stringMob.Clear(); stringMap.Clear(); stringNpc.Clear(); stringSkill.Clear(); stringSkill2.Clear(); } public bool HasValues { get { return (stringEqp.Count + stringItem.Count + stringMap.Count + stringMob.Count + stringNpc.Count + stringSkill.Count > 0); } } private Dictionary stringEqp; private Dictionary stringFamiliarSkill; private Dictionary stringItem; private Dictionary stringMap; private Dictionary stringMob; private Dictionary stringNpc; private Dictionary stringSkill; private Dictionary stringSkill2; private string GetDefaultString(Wz_Node node, string searchNodeText) { node = node.FindNodeByPath(searchNodeText); return node == null ? null : Convert.ToString(node.Value); } private void AddAllValue(StringResult sr, Wz_Node node) { foreach (Wz_Node child in node.Nodes) { if (child.Value != null) { sr[child.Text] = child.GetValue(); } } } public Dictionary StringEqp { get { return stringEqp; } } public Dictionary StringFamiliarSkill { get { return stringFamiliarSkill; } } public Dictionary StringItem { get { return stringItem; } } public Dictionary StringMap { get { return stringMap; } } public Dictionary StringMob { get { return stringMob; } } public Dictionary StringNpc { get { return stringNpc; } } public Dictionary StringSkill { get { return stringSkill; } } public Dictionary StringSkill2 { get { return stringSkill2; } } } } ================================================ FILE: WzComparerR2.Common/StringResult.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace WzComparerR2.Common { public class StringResult { public StringResult() { } public string Name { get; set; } public string Desc { get; set; } public string Pdesc { get; set; } public string AutoDesc { get; set; } public string FullPath { get; set; } private List> allValues; public string this[string key] { get { if (this.allValues != null && key != null) { foreach(var kv in this.allValues) { if (kv.Key == key) { return kv.Value; } } } return null; } set { if (key != null) { if (this.allValues == null) { this.allValues = new List>(); } for(int i = 0; i < this.allValues.Count; i++) { var kv = this.allValues[i]; if (kv.Key == key) { this.allValues[i] = new KeyValuePair(key, value); return; } } this.allValues.Add(new KeyValuePair(key, value)); } } } } public sealed class StringResultSkill : StringResult { public StringResultSkill() { this.skillH = new List(); this.skillpH = new List(); this.skillhcH = new List(); this.skillExtraH = new List>(); } public List SkillH { get { return this.skillH; } } public List SkillpH { get { return this.skillpH; } } public List SkillhcH { get { return this.skillhcH; } } public List> SkillExtraH { get { return this.skillExtraH; } } private readonly List skillH; private readonly List skillpH; private readonly List skillhcH; private readonly List> skillExtraH; } } ================================================ FILE: WzComparerR2.Common/Text/DocumentElements.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Drawing; namespace WzComparerR2.Text { public abstract class DocElement { } public sealed class Span : DocElement { public string ColorID { get; set; } public string Text { get; set; } } public sealed class LineBreak : DocElement { private LineBreak() { } public static readonly LineBreak Instance = new LineBreak(); } } ================================================ FILE: WzComparerR2.Common/Text/Parser.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace WzComparerR2.Text { public class Parser { private Parser() { } public static IList Parse(string format) { var elements = new List(); var sb = new StringBuilder(); var colorStack = new Stack(); colorStack.Push(""); int strPos = 0; char curChar; int offset = 0; Action flushRun = () => { if (offset < format.Length && sb.Length > offset) { elements.Add(new Span() { Text = sb.ToString(offset, sb.Length - offset), ColorID = colorStack.Peek() }); offset = sb.Length; } }; while (strPos < format.Length) { curChar = format[strPos++]; if (curChar == '\\') { if (strPos < format.Length) { curChar = format[strPos++]; switch (curChar) { case 'r': curChar = '\r'; break; case 'n': curChar = '\n'; break; } } else //结束符处理 { curChar = '#'; } } switch (curChar) { case '#': if (strPos < format.Length && format[strPos] == 'c')//遇到#c 换橙刷子并flush { flushRun(); colorStack.Push("c"); strPos++; } else if (strPos < format.Length && format[strPos] == '$' && strPos + 1 < format.Length )//遇到#$(自定义) 更换为自定义颜色表 { flushRun(); colorStack.Push(format.Substring(strPos, 2)); strPos += 2; } else if (colorStack.Count == 1) //同#c { flushRun(); colorStack.Push("c"); //strPos++; } else//遇到# 换白刷子并flush { flushRun(); colorStack.Pop(); } break; case '\r': //忽略 break; case '\n': //插入换行 flushRun(); elements.Add(LineBreak.Instance); break; default: sb.Append(curChar); break; } } flushRun(); return elements; } } } ================================================ FILE: WzComparerR2.Common/Text/TextAlignment.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace WzComparerR2.Text { public enum TextAlignment { Left = 0, Center = 1, Right = 2, } } ================================================ FILE: WzComparerR2.Common/Text/TextRenderer.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Drawing; namespace WzComparerR2.Text { public abstract class TextRenderer { public TextRenderer() { sb = new StringBuilder(); } public bool WordWrapEnabled { get; set; } protected StringBuilder sb; protected TFont font; public void DrawFormatString(string s, TFont font, int width, ref int y, int height, TextAlignment alignment = TextAlignment.Left) { //初始化环境 this.font = font; this.sb.Clear(); this.sb.EnsureCapacity(s.Length); //读取格式 var doc = Parser.Parse(s); this.DrawRunsInner(this.PrepareRuns(doc), width, ref y, height, alignment); } public void DrawPlainText(string s, TFont font, int width, ref int y, int height, TextAlignment alignment = TextAlignment.Left) { this.font = font; this.sb.Clear(); this.sb.EnsureCapacity(s.Length); this.DrawRunsInner(this.PrepareRuns(s), width, ref y, height, alignment); } private void DrawRunsInner(List runs, int width, ref int y, int height, TextAlignment alignment) { runs = runs.SelectMany(run => SplitWords(run)).ToList(); this.MeasureRuns(runs); var layout = LayoutRuns(runs, width, ref y, height, alignment); this.FlushAll(layout); } private List PrepareRuns(IList doc) { var runs = new List(); foreach (var elem in doc) { if (elem is Span) { var span = (Span)elem; int start = sb.Length; sb.Append(span.Text); runs.Add(new Run(start, sb.Length - start) { ColorID = span.ColorID }); } else if (elem is LineBreak) { runs.Add(new Run(sb.Length, 0) { IsBreakLine = true }); } } return runs; } private List PrepareRuns(string text) { List runs = new List(); var sr = new System.IO.StringReader(text); for (int row = 0; sr.Peek() > -1; row++) { if (row > 0) { runs.Add(new Run(sb.Length, 0) { IsBreakLine = true }); } var line = sr.ReadLine(); if (!string.IsNullOrEmpty(line)) { sb.Append(line); runs.Add(new Run(sb.Length - line.Length, line.Length)); } } return runs; } private List SplitWords(Run run) { List runs = new List(); if (run.IsBreakLine) { runs.Add(run); } else { for (int i = run.StartIndex, i0 = run.StartIndex + run.Length; i < i0; i++) { int start = i, len; switch (sb[i]) { case ' ': case '\t': while (++i < i0) { if (!(sb[i] == ' ' || sb[i] == '\t')) { break; } } len = (i--) - start; runs.Add(new Run(start, len) { IsWhiteSpace = true }); break; case '\r': if (i + 1 < i0 && sb[i + 1] == '\n') { i++; goto case '\n'; } else { runs.Add(new Run(start, 1) { IsWhiteSpace = true }); } break; case '\n': len = i - start + 1; runs.Add(new Run(start, len) { IsBreakLine = true }); break; default: if (this.WordWrapEnabled) { while (++i < i0) { if (sb[i] == ' ' || sb[i] == '\t' || sb[i] == '\r' || sb[i] == '\n') { break; } } len = (i--) - start; runs.Add(new Run(start, len) { ColorID = run.ColorID }); } else { runs.Add(new Run(start, 1) { ColorID = run.ColorID }); } break; } } } return runs; } private float GetFontLineHeight(Font font) { var ff = font.FontFamily; return (float)Math.Ceiling(1.0 * font.Height * ff.GetLineSpacing(font.Style) / ff.GetEmHeight(font.Style)); } protected abstract void MeasureRuns(List runs); protected abstract Rectangle[] MeasureChars(int startIndex, int length); protected abstract void Flush(StringBuilder sb, int startIndex, int length, int x, int y, string ColorID); private List LayoutRuns(List runs, int width, ref int y, int lineHeight, TextAlignment alignment) { int drawX = 0; int drawY = y; int start = -1, end = -1; int xOffset = 0; int curX = drawX; string colorID = null; List result = new(); int lastLineStartIndex = 0; bool hasContent() => start > -1 && end > start; void flush(bool isNewLine) { if (hasContent()) { result.Add(new PositionedText() { StartIndex = start, Length = end - start, X = drawX, Y = drawY, ColorID = colorID }); } if (isNewLine) { if (lastLineStartIndex < result.Count && alignment != TextAlignment.Left) { // recalculate offsetX by alignment int contentWidth = curX; int adjustOffsetX = alignment switch { TextAlignment.Center => (width - contentWidth) / 2, TextAlignment.Right => (width - contentWidth), _ => 0, }; for (int i = lastLineStartIndex; i < result.Count; i++) { result[i].X += adjustOffsetX; } } drawX = curX = 0; drawY += lineHeight; lastLineStartIndex = result.Count; } else { drawX = curX; } start = end = -1; }; for (int r = 0; r < runs.Count; r++) { var run = runs[r]; if (run.IsBreakLine) { //强行换行 并且flush flush(true); if (r < runs.Count - 1) { xOffset = runs[r + 1].X; } } else { if (!run.IsWhiteSpace && run.ColorID != colorID) { end = run.StartIndex; curX = run.X - xOffset; flush(false); colorID = run.ColorID; } if (start < 0) { start = run.StartIndex; } if (!(run.IsWhiteSpace && run.Width <= 0)) { //非空 计算宽度 curX = run.X - xOffset; if (this.WordWrapEnabled ? (width - curX < run.Width) : (curX >= width)) //奇怪的算法 暂定 { //宽度不够 if (curX > 0) //(hasContent()) { //有内容 // 判断行尾标点是否追加 if (run.ColorID == colorID && run.Length == 1 && ",.".IndexOf(this.sb[run.StartIndex]) > -1) { end = run.StartIndex + run.Length; if (++r >= runs.Count) { break; } run = runs[r]; } flush(true); start = run.StartIndex; xOffset = run.X; } if (width - curX < run.Width) { //宽度还是不够 按字符拆分 var rects = MeasureChars(run.StartIndex, run.Length); for (int i = 0, ir = run.StartIndex; i < rects.Length; i++, ir++) { rects[i].X += run.X; if (start < 0) { start = ir; xOffset = run.X; } if (rects[i].Right - xOffset > width) { //超宽 flush之前内容 if (ir - start <= 0) { //限定至少输出一个字符 end = start + 1; curX = rects[i].Right - xOffset; flush(true); xOffset = rects[i].Right; continue; } else { end = ir; curX = rects[i].X - xOffset; flush(true); start = ir; xOffset = rects[i].X; } } } end = run.StartIndex + run.Length; curX = rects[rects.Length - 1].Right - xOffset; flush(false); continue; } } } //正常绘制 end = run.StartIndex + run.Length; curX = run.X + run.Width - xOffset; } } //输出结尾 flush(true); y = drawY; return result; } private void FlushAll(List texts) { foreach (PositionedText text in texts) { this.Flush(sb, text.StartIndex, text.Length, text.X, text.Y, text.ColorID); } } private class PositionedText { public int StartIndex; public int Length; public int X; public int Y; public string ColorID; } } public class Run { public Run(int startIndex, int length) { this.StartIndex = startIndex; this.Length = length; } public int StartIndex; public int Length; public bool IsWhiteSpace; public bool IsBreakLine; public int X; public int Width; public string ColorID; } } ================================================ FILE: WzComparerR2.Common/VpxVideoDecoder.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; using vpx_codec_ctx_t = WzComparerR2.VpxVideoDecoder.Interop.vpx_codec_ctx; using vpx_codec_dec_cfg_t = WzComparerR2.VpxVideoDecoder.Interop.vpx_codec_dec_cfg; using vpx_color_range_t = WzComparerR2.VpxVideoDecoder.Interop.vpx_color_range; using vpx_codec_err_t = WzComparerR2.VpxVideoDecoder.Interop.vpx_codec_err; using vpx_codec_flags_t = WzComparerR2.VpxVideoDecoder.Interop.vpx_codec_flags; using vpx_image_t = WzComparerR2.VpxVideoDecoder.Interop.vpx_image; using vpx_img_fmt_t = WzComparerR2.VpxVideoDecoder.Interop.vpx_img_fmt; using vpx_codec_iface_ptr = nint; using vpx_codec_iter_t = nint; using System.Runtime.CompilerServices; using System.Windows.Forms.VisualStyles; namespace WzComparerR2 { public class VpxVideoDecoder : IDisposable { public static ReadOnlySpan FourCC_VP8 => "VP80"u8; public static ReadOnlySpan FourCC_VP9 => "VP90"u8; public VpxVideoDecoder(ReadOnlySpan fourCC) { this.VpxCodecInit(fourCC, new vpx_codec_dec_cfg_t()); } public VpxVideoDecoder(ReadOnlySpan fourCC, int width, int height, int threads) { this.VpxCodecInit(fourCC, new vpx_codec_dec_cfg_t { w = (uint)width, h = (uint)height, threads = (uint)threads, }); } private IntPtr pVpxCodecCtx; private IntPtr pVpxCodecDecConfig; private vpx_codec_iter_t iter; private unsafe void VpxCodecInit(ReadOnlySpan fourCC, vpx_codec_dec_cfg_t config) { vpx_codec_iface_ptr iface; if (fourCC.Length != 4) { throw new ArgumentException("Invalid length.", nameof(fourCC)); } if (fourCC.SequenceEqual(FourCC_VP9)) { iface = Interop.vpx_codec_vp9_dx(); } else if (fourCC.SequenceEqual(FourCC_VP8)) { iface = Interop.vpx_codec_vp8_dx(); } else { throw new ArgumentException($"Unknown fourCC value: {MemoryMarshal.Read(fourCC)}", nameof(fourCC)); } IntPtr pVpxCodecCtx = Marshal.AllocHGlobal(Marshal.SizeOf()); IntPtr pVpxCodecDecConfig = Marshal.AllocHGlobal(Marshal.SizeOf()); try { Unsafe.Copy(pVpxCodecDecConfig.ToPointer(), ref config); vpx_codec_err_t err = Interop.vpx_codec_dec_init_ver((vpx_codec_ctx_t*)pVpxCodecCtx, iface, (vpx_codec_dec_cfg_t*)pVpxCodecDecConfig, 0, Interop.VPX_DECODER_ABI_VERSION); ThrowOnNonSuccessfulError(err, nameof(Interop.vpx_codec_dec_init_ver)); } catch { Marshal.FreeHGlobal(pVpxCodecCtx); Marshal.FreeHGlobal(pVpxCodecDecConfig); } this.pVpxCodecCtx = pVpxCodecCtx; this.pVpxCodecDecConfig = pVpxCodecDecConfig; } public unsafe void DecodeData(ReadOnlySpan data) { this.ThrowOnObjectDisposed(); if (data.IsEmpty) { return; } fixed (byte* pData = data) { this.iter = 0; var err = Interop.vpx_codec_decode((vpx_codec_ctx_t*)this.pVpxCodecCtx, pData, (uint)data.Length, 0, 0); ThrowOnNonSuccessfulError(err, nameof(Interop.vpx_codec_decode)); } } public unsafe bool GetNextFrame(out VpxFrame frame) { this.ThrowOnObjectDisposed(); vpx_codec_iter_t iter = this.iter; vpx_image_t* img = Interop.vpx_codec_get_frame((vpx_codec_ctx_t*)this.pVpxCodecCtx, &iter); this.iter = iter; if (img != null) { frame = new VpxFrame(img); return true; } else { frame = default; return false; } } #region IDisposable private bool isDisposed; public void Dispose() { this.Dispose(true); GC.SuppressFinalize(this); } ~VpxVideoDecoder() { this.Dispose(false); } protected virtual unsafe void Dispose(bool disposing) { if (isDisposed) { return; } if (disposing) { if (this.pVpxCodecCtx != IntPtr.Zero) { Interop.vpx_codec_destroy((vpx_codec_ctx_t*)this.pVpxCodecCtx); Marshal.FreeHGlobal(this.pVpxCodecCtx); this.pVpxCodecCtx = IntPtr.Zero; } if (this.pVpxCodecDecConfig != IntPtr.Zero) { Marshal.FreeHGlobal(this.pVpxCodecDecConfig); this.pVpxCodecDecConfig = IntPtr.Zero; } } isDisposed = true; } private void ThrowOnObjectDisposed() { if (this.isDisposed) { throw new ObjectDisposedException(nameof(VpxVideoDecoder)); } } private void ThrowOnNonSuccessfulError(vpx_codec_err_t err, string methodName = null) { if (err != vpx_codec_err_t.VPX_CODEC_OK) { throw new Exception($"{methodName ?? "libVpx"} returns error: ${err}"); } } #endregion #region interop public static class Interop { private const string libVpx = @"libvpx"; [DllImport(libVpx)] public static unsafe extern vpx_codec_err_t vpx_codec_dec_init_ver([Out] vpx_codec_ctx_t* ctx, vpx_codec_iface_ptr iface, [In] vpx_codec_dec_cfg* cfg, vpx_codec_flags_t flags, int ver); [DllImport(libVpx)] public static extern vpx_codec_iface_ptr vpx_codec_vp8_dx(); [DllImport(libVpx)] public static extern vpx_codec_iface_ptr vpx_codec_vp9_dx(); [DllImport(libVpx)] public static unsafe extern vpx_codec_err_t vpx_codec_decode([In] vpx_codec_ctx_t* ctx, [In] byte* data, uint data_sz, nint user_priv, long deadline); [DllImport(libVpx)] public static unsafe extern vpx_image_t* vpx_codec_get_frame([In] vpx_codec_ctx_t* ctx, [In][Out] vpx_codec_iter_t* iter); [DllImport(libVpx)] public static unsafe extern vpx_codec_err_t vpx_codec_destroy([In] vpx_codec_ctx_t* ctx); public const int VPX_IMAGE_ABI_VERSION = 5; public const int VPX_CODEC_ABI_VERSION = (4 + VPX_IMAGE_ABI_VERSION); public const int VPX_DECODER_ABI_VERSION = (3 + VPX_CODEC_ABI_VERSION); public enum vpx_codec_err { VPX_CODEC_OK = 0, VPX_CODEC_ERROR, VPX_CODEC_MEM_ERROR, VPX_CODEC_ABI_MISMATCH, VPX_CODEC_INCAPABLE, VPX_CODEC_UNSUP_BITSTREAM, VPX_CODEC_UNSUP_FEATURE, VPX_CODEC_CORRUPT_FRAME, VPX_CODEC_INVALID_PARAM, VPX_CODEC_LIST_END } public struct vpx_codec_dec_cfg { public uint threads; public uint w; public uint h; } public unsafe struct vpx_codec_ctx { public nint name; public vpx_codec_iface_ptr iface; public vpx_codec_err err; public nint err_detail; public int init_flags; public vpx_codec_dec_cfg* config; public nint priv; } public enum vpx_img_fmt { VPX_IMG_FMT_NONE = 0, VPX_IMG_FMT_PLANAR = 0x100, VPX_IMG_FMT_UV_FLIP = 0x200, VPX_IMG_FMT_HAS_ALPHA = 0x400, VPX_IMG_FMT_HIGHBITDEPTH = 0x800, VPX_IMG_FMT_YV12 = VPX_IMG_FMT_PLANAR | VPX_IMG_FMT_UV_FLIP | 1, VPX_IMG_FMT_I420 = VPX_IMG_FMT_PLANAR | 2, VPX_IMG_FMT_I422 = VPX_IMG_FMT_PLANAR | 5, VPX_IMG_FMT_I444 = VPX_IMG_FMT_PLANAR | 6, VPX_IMG_FMT_I440 = VPX_IMG_FMT_PLANAR | 7, VPX_IMG_FMT_NV12 = VPX_IMG_FMT_PLANAR | 9, VPX_IMG_FMT_I42016 = VPX_IMG_FMT_I420 | VPX_IMG_FMT_HIGHBITDEPTH, VPX_IMG_FMT_I42216 = VPX_IMG_FMT_I422 | VPX_IMG_FMT_HIGHBITDEPTH, VPX_IMG_FMT_I44416 = VPX_IMG_FMT_I444 | VPX_IMG_FMT_HIGHBITDEPTH, VPX_IMG_FMT_I44016 = VPX_IMG_FMT_I440 | VPX_IMG_FMT_HIGHBITDEPTH } public enum vpx_codec_flags { VPX_CODEC_USE_POSTPROC = 0x10000, VPX_CODEC_USE_ERROR_CONCEALMENT = 0x20000, VPX_CODEC_USE_INPUT_FRAGMENTS = 0x40000, VPX_CODEC_USE_FRAME_THREADING = 0x80000, } public enum vpx_color_space { VPX_CS_UNKNOWN = 0, VPX_CS_BT_601 = 1, VPX_CS_BT_709 = 2, VPX_CS_SMPTE_170 = 3, VPX_CS_SMPTE_240 = 4, VPX_CS_BT_2020 = 5, VPX_CS_RESERVED = 6, VPX_CS_SRGB = 7, } public enum vpx_color_range { VPX_CR_STUDIO_RANGE = 0, VPX_CR_FULL_RANGE = 1, } public const int VPX_PLANE_PACKED = 0; public const int VPX_PLANE_Y = 0; public const int VPX_PLANE_U = 1; public const int VPX_PLANE_V = 2; public const int VPX_PLANE_ALPHA = 3; [StructLayout(LayoutKind.Sequential)] public unsafe struct vpx_image { public vpx_img_fmt fmt; public vpx_color_space cs; public vpx_color_range_t range; public uint w; public uint h; public uint bit_depth; public uint d_w; public uint d_h; public uint r_w; public uint r_h; public uint x_chroma_shift; public uint y_chroma_shift; // fixed byte* planes[4]; //error: CS1663 private byte* planes_0; private byte* planes_1; private byte* planes_2; private byte* planes_3; public byte** planes { get { fixed (vpx_image* pImage = &this) return &pImage->planes_0; } } public fixed int stride[4]; public int bps; public void* user_priv; public byte* img_data; public int img_data_owner; public int self_allocd; public void* fb_priv; } } #endregion } public unsafe struct VpxFrame { internal VpxFrame(vpx_image_t* image) { this.image = image; } private readonly vpx_image_t* image; public vpx_img_fmt_t Format => image->fmt; public int DisplayWidth => (int)image->d_w; public int DisplayHeight => (int)image->d_h; public IntPtr PlanesY => new IntPtr(image->planes[VpxVideoDecoder.Interop.VPX_PLANE_Y]); public IntPtr PlanesU => new IntPtr(image->planes[VpxVideoDecoder.Interop.VPX_PLANE_U]); public IntPtr PlanesV => new IntPtr(image->planes[VpxVideoDecoder.Interop.VPX_PLANE_V]); public IntPtr PlanesAlpha => new IntPtr(image->planes[VpxVideoDecoder.Interop.VPX_PLANE_ALPHA]); public int StrideY => image->stride[VpxVideoDecoder.Interop.VPX_PLANE_Y]; public int StrideU => image->stride[VpxVideoDecoder.Interop.VPX_PLANE_U]; public int StrideV => image->stride[VpxVideoDecoder.Interop.VPX_PLANE_V]; public int StrideAlpha => image->stride[VpxVideoDecoder.Interop.VPX_PLANE_ALPHA]; } } ================================================ FILE: WzComparerR2.Common/WzComparerR2.Common.csproj ================================================  net462;net6.0-windows;net8.0-windows true WzComparerR2.Common WzComparerR2 false True false latest disable ..\References\ImageManipulation.dll ..\References\spine-monogame.dll ..\References\DevComponents.DotNetBar2.dll Properties\CommonAssemblyInfo.cs ================================================ FILE: WzComparerR2.Common/Wz_NodeExtension2.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using WzComparerR2.WzLib; namespace WzComparerR2.Common { public static class Wz_NodeExtension2 { public static Wz_Node GetLinkedSourceNode(this Wz_Node node, GlobalFindNodeFunction findNode) { string path; if (!string.IsNullOrEmpty(path = node.Nodes["source"].GetValueEx(null))) { return findNode?.Invoke(path); } else if (!string.IsNullOrEmpty(path = node.Nodes["_inlink"].GetValueEx(null))) { var img = node.GetNodeWzImage(); return img?.Node.FindNodeByPath(true, path.Split('/')); } else if (!string.IsNullOrEmpty(path = node.Nodes["_outlink"].GetValueEx(null))) { return findNode?.Invoke(path); } else { return node; } } } } ================================================ FILE: WzComparerR2.LuaConsole/AppSyntaxModeProvider.cs ================================================ using System; using System.Collections.Generic; using System.Text; using System.IO; using System.Xml; using ICSharpCode.TextEditor.Document; namespace WzComparerR2.LuaConsole { public class AppSyntaxModeProvider : ISyntaxModeFileProvider { public AppSyntaxModeProvider() { this.list = new List(); UpdateSyntaxModeList(); } private List list; public XmlTextReader GetSyntaxModeFile(SyntaxMode syntaxMode) { try { string resourceName = Path.GetFileNameWithoutExtension(syntaxMode.FileName); byte[] fileContent = Properties.Resources.ResourceManager.GetObject(resourceName) as byte[]; if (fileContent != null) { Stream stream = new MemoryStream(fileContent, false); return new XmlTextReader(stream); } else { return null; } } catch (Exception) { throw; } } public ICollection SyntaxModes { get { return list; } } public void UpdateSyntaxModeList() { this.list.Clear(); this.list.Add(new SyntaxMode("Lua.xshd", "Lua", "*.lua")); } } } ================================================ FILE: WzComparerR2.LuaConsole/Config/LuaConsoleConfig.cs ================================================ using System; using System.Collections.Generic; using System.Configuration; using System.Linq; using System.Text; using System.Threading.Tasks; using WzComparerR2.Config; namespace WzComparerR2.LuaConsole.Config { [SectionName("WcR2.LuaConsole")] public class LuaConsoleConfig : ConfigSectionBase { public LuaConsoleConfig() { } /// /// 获取最近打开的文档列表。 /// [ConfigurationProperty("recentDocuments")] [ConfigurationCollection(typeof(ConfigArrayList.ItemElement))] public ConfigArrayList RecentDocuments { get { return (ConfigArrayList)this["recentDocuments"]; } } } } ================================================ FILE: WzComparerR2.LuaConsole/Entry.cs ================================================ using System; using System.Collections.Generic; using System.Text; using WzComparerR2.Config; using WzComparerR2.PluginBase; using DevComponents.DotNetBar; namespace WzComparerR2.LuaConsole { public class Entry : PluginEntry { public Entry(PluginContext context) : base(context) { Instance = this; } internal static Entry Instance { get; private set; } protected override void OnLoad() { var bar = this.Context.AddRibbonBar("Tools", "控制台"); ButtonItem btnItem = new ButtonItem("", "Lua控制台"); btnItem.Click += btnItem_Click; bar.Items.Add(btnItem); ConfigManager.RegisterAllSection(this.GetType().Assembly); } FrmConsole frm; void btnItem_Click(object sender, EventArgs e) { if (frm == null || frm.IsDisposed) { frm = new FrmConsole(); frm.Owner = Context.MainForm; } frm.Show(); frm.Focus(); } protected override void OnUnload() { base.OnUnload(); } } } ================================================ FILE: WzComparerR2.LuaConsole/Examples/DumpAnimations.lua ================================================ import 'WzComparerR2.PluginBase' import 'WzComparerR2.WzLib' import 'WzComparerR2.Common' import 'WzComparerR2.Encoders' import 'System.IO' import 'System.Xml' import 'System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' import 'System.Drawing' import 'System.Drawing.Imaging' require 'Helper' ------------------------------------------------------------ local function isPng(value) return value and type(value) == "userdata" and value:GetType().Name == 'Wz_Png' end local function isDelay(node) return node.Text == "delay" and node:GetType().Name == 'Wz_Node' end local function isPngWithDelay(node) if isPng(node.Value) then for n in enumAllWzNodes(node) do if isDelay(n) then return true end end end return false end local function enumAllWzPngNodesWithDelay(node) return coroutine.wrap(function() if isPng(node.Value) and isPngWithDelay(node) then coroutine.yield(node) end for _, n in each(node.Nodes) do for child in enumAllWzPngNodesWithDelay(n) do coroutine.yield(child) end end end) end local function findNodeFunc(path) return PluginManager.FindWz(path) end local t_IGifFrame = {} t_IGifFrame.typeRef = luanet.import_type('WzComparerR2.Common.IGifFrame') t_IGifFrame.Draw = luanet.get_method_bysig(t_IGifFrame.typeRef, 'Draw', "System.Drawing.Graphics", "System.Drawing.Rectangle") local function saveAnimatedImage(node, fileName) local gif = Gif.CreateFromNode(node, findNodeFunc) local rect = gif:GetRect() local enc = BuildInApngEncoder() enc:Init(fileName, rect.Width, rect.Height) enc.OptimizeEnabled = false for i,frame in each(gif.Frames) do local bmp = Bitmap(rect.Width, rect.Height, PixelFormat.Format32bppArgb) local g = Graphics.FromImage(bmp) t_IGifFrame.Draw(frame, g, rect) g:Dispose() enc:AppendFrame(bmp, frame.Delay) bmp:Dispose() bmp = nil end enc:Dispose() for i,frame in each(gif.Frames) do frame.Bitmap:Dispose(); end gif = nil end ------------------------------------------------------------ -- all variables local topWzPath = 'Skill' local topNode = PluginManager.FindWz(topWzPath) local outputDir = "D:\\wzDump" ------------------------------------------------------------ -- main function if not topNode then env:WriteLine('"{0}" not loaded.', topWzPath) return end for n in enumAllWzNodes(topNode) do local img = Wz_NodeExtension.GetNodeWzImage(n) if img then --extract wz image env:WriteLine('(extract) '..(img.Name)) if img:TryExtract() then local dir = outputDir.."\\"..(n.FullPathToFile) local dirCreated = false local fullpaths = {} for n2 in enumAllWzPngNodesWithDelay(img.Node) do local parentNode = n2.ParentNode local fullpath = parentNode.FullPath -- Skip if 'fullpath' already done if (not fullpaths[fullpath]) then local fn = fullpath:sub(img.Name:len()+2):gsub("\\", ".") fn = removeInvalidPathChars(fn) fn = Path.Combine(dir, fn .. ".apng") --ensure dir exists if not dirCreated then if not Directory.Exists(dir) then Directory.CreateDirectory(dir) end dirCreated = true end saveAnimatedImage(parentNode, fn) fullpaths[fullpath] = true end end img:Unextract() else --error env:WriteLine((img.Name)..' extract failed.') end --end extract end -- end type validate end -- end foreach env:WriteLine('--------Done.---------') ================================================ FILE: WzComparerR2.LuaConsole/Examples/DumpImages.lua ================================================ import 'WzComparerR2.PluginBase' import 'WzComparerR2.WzLib' import 'System.IO' import 'System.Xml' require 'Helper' ------------------------------------------------------------ local function isPng(value) return value and type(value) == "userdata" and value:GetType().Name == 'Wz_Png' end ------------------------------------------------------------ -- all variables local topWzPath = 'Character' local topNode = PluginManager.FindWz(topWzPath) local outputDir = "D:\\wzDump" local errorList = {} -- collect error messages here ------------------------------------------------------------ -- main function if not topNode then env:WriteLine('"{0}" not loaded.', topWzPath) return end -- enum all wz_images for n in enumAllWzNodes(topNode) do local img = Wz_NodeExtension.GetNodeWzImage(n) if img then env:WriteLine('(extract) ' .. (img.Name)) local success, extractErr = pcall(function() if img:TryExtract() then local dir = outputDir .. "\\" .. (n.FullPathToFile) local dirCreated = false for n2 in enumAllWzNodes(img.Node) do local png = n2.Value if isPng(png) and (png.Width > 1 or png.Height > 1) then local fn = n2.FullPath:sub(img.Name:len() + 2):gsub("\\", ".") fn = removeInvalidPathChars(fn) fn = Path.Combine(dir, fn .. ".png") if not dirCreated then if not Directory.Exists(dir) then Directory.CreateDirectory(dir) end dirCreated = true end -- Try saving PNG local successSave, saveErr = pcall(function() local bmp = png:ExtractPng() if bmp then bmp:Save(fn) bmp:Dispose() else error("ExtractPng() returned nil.") end end) if not successSave then local errMsg = string.format("Error saving PNG [%s]: %s", n2.FullPath, saveErr) env:WriteLine(errMsg) table.insert(errorList, errMsg) end end end img:Unextract() else local errMsg = string.format("%s extract failed.", img.Name) env:WriteLine(errMsg) table.insert(errorList, errMsg) end end) if not success then local errMsg = string.format("Unexpected error in image [%s]: %s", img.Name, extractErr) env:WriteLine(errMsg) table.insert(errorList, errMsg) end end end -- Final error report if #errorList > 0 then env:WriteLine("-------- Error Summary --------") for i, err in ipairs(errorList) do env:WriteLine(err) end end env:WriteLine('-------- Done. --------') ================================================ FILE: WzComparerR2.LuaConsole/Examples/DumpSounds.lua ================================================ import 'WzComparerR2.PluginBase' import 'WzComparerR2.WzLib' import 'System.IO' import 'System.Xml' require 'Helper' ------------------------------------------------------------ local function isSound(value) return value and type(value) == "userdata" and value:GetType().Name == 'Wz_Sound' end ------------------------------------------------------------ -- all variables local topWzPath = 'Sound\\Bgm00.img' local topNode = PluginManager.FindWz(topWzPath) local outputDir = "D:\\wzDump" ------------------------------------------------------------ -- main function if not topNode then env:WriteLine('"{0}" not loaded.', topWzPath) return end -- enum all wz_images for n in enumAllWzNodes(topNode) do local img = Wz_NodeExtension.GetNodeWzImage(n) if img then --extract wz image env:WriteLine('(extract)'..(img.Name)) if img:TryExtract() then local dir = outputDir.."\\"..(n.FullPathToFile) local dirCreated = false --find all sound for n2 in enumAllWzNodes(img.Node) do local sound = n2.Value if isSound(sound) then local fn = n2.FullPath:sub(img.Name:len()+2):gsub("\\", ".") fn = removeInvalidPathChars(fn) if not n2.Text:find("\\.") then if sound.SoundType == Wz_SoundType.Mp3 then fn = fn .. ".mp3" end if sound.SoundType == Wz_SoundType.Pcm then fn = fn .. ".wav" end end fn = Path.Combine(dir, fn) --ensure dir exists if not dirCreated then if not Directory.Exists(dir) then Directory.CreateDirectory(dir) end dirCreated = true end --save sound env:WriteLine('(output)'..fn) File.WriteAllBytes(fn, sound:ExtractSound()) env:WriteLine('(close)'..fn) end end img:Unextract() else --error env:WriteLine((img.Name)..' extract failed.') end --end extract end -- end type validate end -- end foreach env:WriteLine('--------Done.---------') ================================================ FILE: WzComparerR2.LuaConsole/Examples/DumpXml.lua ================================================ import 'WzComparerR2.PluginBase' import 'WzComparerR2.WzLib' import 'System.IO' import 'System.Xml' require "helper" ------------------------------------------------------------ -- all variables local topNode = PluginManager.FindWz('Etc') local outputDir = "D:\\wzDump" ------------------------------------------------------------ -- main function if not topNode then env:WriteLine('Base.wz not loaded.') return end -- enum all wz_images for n in enumAllWzNodes(topNode) do local value = n.Value if isWzImage(value) then local img = value --extract wz image env:WriteLine('(extract)'..(img.Name)) if img:TryExtract() then --dump as Xml local xmlFileName = outputDir.."\\"..(n.FullPathToFile)..".xml" local dir = Path.GetDirectoryName(xmlFileName) --ensure dir exists if not Directory.Exists(dir) then Directory.CreateDirectory(dir) end --create file env:WriteLine('(output)'..xmlFileName) local fs = File.Create(xmlFileName) local xw = XmlWriter.Create(fs) xw:WriteStartDocument(true); Wz_NodeExtension.DumpAsXml(img.Node, xw) xw:WriteEndDocument() xw:Flush() fs:Close() env:WriteLine('(close)'..xmlFileName) img:Unextract() else --error env:WriteLine((img.Name)..' extract failed.') end --end extract end -- end type validate end -- end foreach env:WriteLine('--------Done.---------') ================================================ FILE: WzComparerR2.LuaConsole/Examples/Helper.lua ================================================ import 'System.IO' function isWzImage(value) return value and type(value) == "userdata" and (value:GetType().Name == 'Wz_Image' or value:GetType().Name == 'Ms_Image') end function enumAllWzNodes(node) return coroutine.wrap(function() coroutine.yield(node) for _,v in each(node.Nodes) do for child in enumAllWzNodes(v) do coroutine.yield(child) end end end) end local p = Path.GetInvalidFileNameChars() local ivStr = "" for i, v in each(p) do if v >= 32 then ivStr = ivStr .. string.char(v) end end local ivPattern = "["..ivStr.."]" function removeInvalidPathChars(fileName) return fileName:gsub(ivPattern, "") end ================================================ FILE: WzComparerR2.LuaConsole/FrmConsole.Designer.cs ================================================ namespace WzComparerR2.LuaConsole { partial class FrmConsole { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { this.components = new System.ComponentModel.Container(); this.dotNetBarManager1 = new DevComponents.DotNetBar.DotNetBarManager(this.components); this.dockSite4 = new DevComponents.DotNetBar.DockSite(); this.bar2 = new DevComponents.DotNetBar.Bar(); this.panelDockContainer1 = new DevComponents.DotNetBar.PanelDockContainer(); this.textBoxX2 = new DevComponents.DotNetBar.Controls.TextBoxX(); this.dockContainerItem1 = new DevComponents.DotNetBar.DockContainerItem(); this.dockSite9 = new DevComponents.DotNetBar.DockSite(); this.dockSite1 = new DevComponents.DotNetBar.DockSite(); this.dockSite2 = new DevComponents.DotNetBar.DockSite(); this.dockSite8 = new DevComponents.DotNetBar.DockSite(); this.dockSite5 = new DevComponents.DotNetBar.DockSite(); this.dockSite6 = new DevComponents.DotNetBar.DockSite(); this.dockSite7 = new DevComponents.DotNetBar.DockSite(); this.bar1 = new DevComponents.DotNetBar.Bar(); this.menuFile = new DevComponents.DotNetBar.ButtonItem(); this.menuNew = new DevComponents.DotNetBar.ButtonItem(); this.menuOpen = new DevComponents.DotNetBar.ButtonItem(); this.menuSave = new DevComponents.DotNetBar.ButtonItem(); this.menuSaveAs = new DevComponents.DotNetBar.ButtonItem(); this.menuExit = new DevComponents.DotNetBar.ButtonItem(); this.menuDebug = new DevComponents.DotNetBar.ButtonItem(); this.menuReset = new DevComponents.DotNetBar.ButtonItem(); this.menuRun = new DevComponents.DotNetBar.ButtonItem(); this.menuStopRun = new DevComponents.DotNetBar.ButtonItem(); this.dockSite3 = new DevComponents.DotNetBar.DockSite(); this.dockContainerItem3 = new DevComponents.DotNetBar.DockContainerItem(); this.tabStrip1 = new DevComponents.DotNetBar.TabStrip(); this.menuRecent = new DevComponents.DotNetBar.ButtonItem(); this.dockSite4.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.bar2)).BeginInit(); this.bar2.SuspendLayout(); this.panelDockContainer1.SuspendLayout(); this.dockSite7.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.bar1)).BeginInit(); this.SuspendLayout(); // // dotNetBarManager1 // this.dotNetBarManager1.AutoDispatchShortcuts.Add(DevComponents.DotNetBar.eShortcut.F1); this.dotNetBarManager1.AutoDispatchShortcuts.Add(DevComponents.DotNetBar.eShortcut.CtrlC); this.dotNetBarManager1.AutoDispatchShortcuts.Add(DevComponents.DotNetBar.eShortcut.CtrlA); this.dotNetBarManager1.AutoDispatchShortcuts.Add(DevComponents.DotNetBar.eShortcut.CtrlV); this.dotNetBarManager1.AutoDispatchShortcuts.Add(DevComponents.DotNetBar.eShortcut.CtrlX); this.dotNetBarManager1.AutoDispatchShortcuts.Add(DevComponents.DotNetBar.eShortcut.CtrlZ); this.dotNetBarManager1.AutoDispatchShortcuts.Add(DevComponents.DotNetBar.eShortcut.CtrlY); this.dotNetBarManager1.AutoDispatchShortcuts.Add(DevComponents.DotNetBar.eShortcut.Del); this.dotNetBarManager1.AutoDispatchShortcuts.Add(DevComponents.DotNetBar.eShortcut.Ins); this.dotNetBarManager1.BottomDockSite = this.dockSite4; this.dotNetBarManager1.EnableFullSizeDock = false; this.dotNetBarManager1.FillDockSite = this.dockSite9; this.dotNetBarManager1.LeftDockSite = this.dockSite1; this.dotNetBarManager1.MdiSystemItemVisible = false; this.dotNetBarManager1.ParentForm = this; this.dotNetBarManager1.RightDockSite = this.dockSite2; this.dotNetBarManager1.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.dotNetBarManager1.ToolbarBottomDockSite = this.dockSite8; this.dotNetBarManager1.ToolbarLeftDockSite = this.dockSite5; this.dotNetBarManager1.ToolbarRightDockSite = this.dockSite6; this.dotNetBarManager1.ToolbarTopDockSite = this.dockSite7; this.dotNetBarManager1.TopDockSite = this.dockSite3; // // dockSite4 // this.dockSite4.AccessibleRole = System.Windows.Forms.AccessibleRole.Window; this.dockSite4.Controls.Add(this.bar2); this.dockSite4.Dock = System.Windows.Forms.DockStyle.Bottom; this.dockSite4.DocumentDockContainer = new DevComponents.DotNetBar.DocumentDockContainer(new DevComponents.DotNetBar.DocumentBaseContainer[] { ((DevComponents.DotNetBar.DocumentBaseContainer)(new DevComponents.DotNetBar.DocumentBarContainer(this.bar2, 488, 95)))}, DevComponents.DotNetBar.eOrientation.Vertical); this.dockSite4.Location = new System.Drawing.Point(0, 282); this.dockSite4.Name = "dockSite4"; this.dockSite4.Size = new System.Drawing.Size(488, 98); this.dockSite4.TabIndex = 7; this.dockSite4.TabStop = false; // // bar2 // this.bar2.AccessibleDescription = "DotNetBar Bar (bar2)"; this.bar2.AccessibleName = "DotNetBar Bar"; this.bar2.AccessibleRole = System.Windows.Forms.AccessibleRole.Grouping; this.bar2.AutoSyncBarCaption = true; this.bar2.CloseSingleTab = true; this.bar2.Controls.Add(this.panelDockContainer1); this.bar2.Font = new System.Drawing.Font("宋体", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); this.bar2.GrabHandleStyle = DevComponents.DotNetBar.eGrabHandleStyle.Caption; this.bar2.IsMaximized = false; this.bar2.Items.AddRange(new DevComponents.DotNetBar.BaseItem[] { this.dockContainerItem1}); this.bar2.LayoutType = DevComponents.DotNetBar.eLayoutType.DockContainer; this.bar2.Location = new System.Drawing.Point(0, 3); this.bar2.Name = "bar2"; this.bar2.Size = new System.Drawing.Size(488, 95); this.bar2.Stretch = true; this.bar2.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.bar2.TabIndex = 0; this.bar2.TabStop = false; this.bar2.Text = "输出"; // // panelDockContainer1 // this.panelDockContainer1.ColorSchemeStyle = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.panelDockContainer1.Controls.Add(this.textBoxX2); this.panelDockContainer1.DisabledBackColor = System.Drawing.Color.Empty; this.panelDockContainer1.Location = new System.Drawing.Point(3, 23); this.panelDockContainer1.Name = "panelDockContainer1"; this.panelDockContainer1.Size = new System.Drawing.Size(482, 69); this.panelDockContainer1.Style.Alignment = System.Drawing.StringAlignment.Center; this.panelDockContainer1.Style.BackColor1.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.BarBackground; this.panelDockContainer1.Style.BorderColor.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.BarDockedBorder; this.panelDockContainer1.Style.ForeColor.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.ItemText; this.panelDockContainer1.Style.GradientAngle = 90; this.panelDockContainer1.TabIndex = 0; // // textBoxX2 // // // // this.textBoxX2.Border.Class = "TextBoxBorder"; this.textBoxX2.Border.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.textBoxX2.Dock = System.Windows.Forms.DockStyle.Fill; this.textBoxX2.Location = new System.Drawing.Point(0, 0); this.textBoxX2.Multiline = true; this.textBoxX2.Name = "textBoxX2"; this.textBoxX2.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; this.textBoxX2.Size = new System.Drawing.Size(482, 69); this.textBoxX2.TabIndex = 1; this.textBoxX2.Text = "--调用env:Help() 获取帮助\r\n"; // // dockContainerItem1 // this.dockContainerItem1.Control = this.panelDockContainer1; this.dockContainerItem1.Name = "dockContainerItem1"; this.dockContainerItem1.Text = "输出"; // // dockSite9 // this.dockSite9.AccessibleRole = System.Windows.Forms.AccessibleRole.Window; this.dockSite9.DocumentDockContainer = new DevComponents.DotNetBar.DocumentDockContainer(); this.dockSite9.Location = new System.Drawing.Point(0, 26); this.dockSite9.Name = "dockSite9"; this.dockSite9.Size = new System.Drawing.Size(0, 0); this.dockSite9.TabIndex = 12; this.dockSite9.TabStop = false; // // dockSite1 // this.dockSite1.AccessibleRole = System.Windows.Forms.AccessibleRole.Window; this.dockSite1.Dock = System.Windows.Forms.DockStyle.Left; this.dockSite1.DocumentDockContainer = new DevComponents.DotNetBar.DocumentDockContainer(); this.dockSite1.Location = new System.Drawing.Point(0, 26); this.dockSite1.Name = "dockSite1"; this.dockSite1.Size = new System.Drawing.Size(0, 256); this.dockSite1.TabIndex = 4; this.dockSite1.TabStop = false; // // dockSite2 // this.dockSite2.AccessibleRole = System.Windows.Forms.AccessibleRole.Window; this.dockSite2.Dock = System.Windows.Forms.DockStyle.Right; this.dockSite2.DocumentDockContainer = new DevComponents.DotNetBar.DocumentDockContainer(); this.dockSite2.Location = new System.Drawing.Point(488, 26); this.dockSite2.Name = "dockSite2"; this.dockSite2.Size = new System.Drawing.Size(0, 256); this.dockSite2.TabIndex = 5; this.dockSite2.TabStop = false; // // dockSite8 // this.dockSite8.AccessibleRole = System.Windows.Forms.AccessibleRole.Window; this.dockSite8.Dock = System.Windows.Forms.DockStyle.Bottom; this.dockSite8.Location = new System.Drawing.Point(0, 380); this.dockSite8.Name = "dockSite8"; this.dockSite8.Size = new System.Drawing.Size(488, 0); this.dockSite8.TabIndex = 11; this.dockSite8.TabStop = false; // // dockSite5 // this.dockSite5.AccessibleRole = System.Windows.Forms.AccessibleRole.Window; this.dockSite5.Dock = System.Windows.Forms.DockStyle.Left; this.dockSite5.Location = new System.Drawing.Point(0, 26); this.dockSite5.Name = "dockSite5"; this.dockSite5.Size = new System.Drawing.Size(0, 354); this.dockSite5.TabIndex = 8; this.dockSite5.TabStop = false; // // dockSite6 // this.dockSite6.AccessibleRole = System.Windows.Forms.AccessibleRole.Window; this.dockSite6.Dock = System.Windows.Forms.DockStyle.Right; this.dockSite6.Location = new System.Drawing.Point(488, 26); this.dockSite6.Name = "dockSite6"; this.dockSite6.Size = new System.Drawing.Size(0, 354); this.dockSite6.TabIndex = 9; this.dockSite6.TabStop = false; // // dockSite7 // this.dockSite7.AccessibleRole = System.Windows.Forms.AccessibleRole.Window; this.dockSite7.Controls.Add(this.bar1); this.dockSite7.Dock = System.Windows.Forms.DockStyle.Top; this.dockSite7.Location = new System.Drawing.Point(0, 0); this.dockSite7.Name = "dockSite7"; this.dockSite7.Size = new System.Drawing.Size(488, 26); this.dockSite7.TabIndex = 10; this.dockSite7.TabStop = false; // // bar1 // this.bar1.AccessibleDescription = "DotNetBar Bar (bar1)"; this.bar1.AccessibleName = "DotNetBar Bar"; this.bar1.AccessibleRole = System.Windows.Forms.AccessibleRole.MenuBar; this.bar1.DockSide = DevComponents.DotNetBar.eDockSide.Top; this.bar1.Font = new System.Drawing.Font("Microsoft YaHei UI", 9F); this.bar1.IsMaximized = false; this.bar1.Items.AddRange(new DevComponents.DotNetBar.BaseItem[] { this.menuFile, this.menuDebug}); this.bar1.Location = new System.Drawing.Point(0, 0); this.bar1.MenuBar = true; this.bar1.Name = "bar1"; this.bar1.Size = new System.Drawing.Size(488, 25); this.bar1.Stretch = true; this.bar1.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.bar1.TabIndex = 0; this.bar1.TabStop = false; this.bar1.Text = "bar1"; // // menuFile // this.menuFile.Name = "menuFile"; this.menuFile.SubItems.AddRange(new DevComponents.DotNetBar.BaseItem[] { this.menuNew, this.menuOpen, this.menuSave, this.menuSaveAs, this.menuRecent, this.menuExit}); this.menuFile.Text = "文件(&F)"; // // menuNew // this.menuNew.Name = "menuNew"; this.menuNew.Shortcuts.Add(DevComponents.DotNetBar.eShortcut.CtrlN); this.menuNew.Text = "新建"; this.menuNew.Click += new System.EventHandler(this.menuNew_Click); // // menuOpen // this.menuOpen.Name = "menuOpen"; this.menuOpen.Shortcuts.Add(DevComponents.DotNetBar.eShortcut.CtrlO); this.menuOpen.Text = "打开"; this.menuOpen.Click += new System.EventHandler(this.menuOpen_Click); // // menuSave // this.menuSave.BeginGroup = true; this.menuSave.Name = "menuSave"; this.menuSave.Shortcuts.Add(DevComponents.DotNetBar.eShortcut.CtrlS); this.menuSave.Text = "保存"; this.menuSave.Click += new System.EventHandler(this.menuSave_Click); // // menuSaveAs // this.menuSaveAs.Name = "menuSaveAs"; this.menuSaveAs.Text = "另存为..."; this.menuSaveAs.Click += new System.EventHandler(this.menuSaveAs_Click); // // menuExit // this.menuExit.BeginGroup = true; this.menuExit.Name = "menuExit"; this.menuExit.Shortcuts.Add(DevComponents.DotNetBar.eShortcut.AltF4); this.menuExit.Text = "退出"; this.menuExit.Click += new System.EventHandler(this.menuExit_Click); // // menuDebug // this.menuDebug.Name = "menuDebug"; this.menuDebug.SubItems.AddRange(new DevComponents.DotNetBar.BaseItem[] { this.menuReset, this.menuRun, this.menuStopRun}); this.menuDebug.Text = "调试(&D)"; // // menuReset // this.menuReset.Name = "menuReset"; this.menuReset.Symbol = ""; this.menuReset.SymbolColor = System.Drawing.Color.Gray; this.menuReset.SymbolSize = 9F; this.menuReset.Text = "重置"; this.menuReset.Click += new System.EventHandler(this.menuReset_Click); // // menuRun // this.menuRun.BeginGroup = true; this.menuRun.Name = "menuRun"; this.menuRun.Shortcuts.Add(DevComponents.DotNetBar.eShortcut.F5); this.menuRun.Symbol = ""; this.menuRun.SymbolColor = System.Drawing.Color.FromArgb(((int)(((byte)(29)))), ((int)(((byte)(127)))), ((int)(((byte)(29))))); this.menuRun.SymbolSize = 9F; this.menuRun.Text = "运行"; this.menuRun.Tooltip = "F5"; this.menuRun.Click += new System.EventHandler(this.menuRun_Click); // // menuStopRun // this.menuStopRun.Name = "menuStopRun"; this.menuStopRun.Shortcuts.Add(DevComponents.DotNetBar.eShortcut.ShiftF5); this.menuStopRun.Symbol = ""; this.menuStopRun.SymbolColor = System.Drawing.Color.FromArgb(((int)(((byte)(192)))), ((int)(((byte)(0)))), ((int)(((byte)(0))))); this.menuStopRun.SymbolSize = 9F; this.menuStopRun.Text = "停止运行"; this.menuStopRun.Tooltip = "Shift+F5"; this.menuStopRun.Click += new System.EventHandler(this.menuStopRun_Click); // // dockSite3 // this.dockSite3.AccessibleRole = System.Windows.Forms.AccessibleRole.Window; this.dockSite3.Dock = System.Windows.Forms.DockStyle.Top; this.dockSite3.DocumentDockContainer = new DevComponents.DotNetBar.DocumentDockContainer(); this.dockSite3.Location = new System.Drawing.Point(0, 26); this.dockSite3.Name = "dockSite3"; this.dockSite3.Size = new System.Drawing.Size(488, 0); this.dockSite3.TabIndex = 6; this.dockSite3.TabStop = false; // // dockContainerItem3 // this.dockContainerItem3.Name = "dockContainerItem3"; this.dockContainerItem3.Text = "dockContainerItem3"; // // tabStrip1 // this.tabStrip1.AutoSelectAttachedControl = true; this.tabStrip1.CanReorderTabs = true; this.tabStrip1.CloseButtonVisible = true; this.tabStrip1.Dock = System.Windows.Forms.DockStyle.Top; this.tabStrip1.Location = new System.Drawing.Point(0, 26); this.tabStrip1.MdiForm = this; this.tabStrip1.MdiTabbedDocuments = true; this.tabStrip1.Name = "tabStrip1"; this.tabStrip1.SelectedTab = null; this.tabStrip1.SelectedTabFont = new System.Drawing.Font("宋体", 9F, System.Drawing.FontStyle.Bold); this.tabStrip1.Size = new System.Drawing.Size(488, 23); this.tabStrip1.Style = DevComponents.DotNetBar.eTabStripStyle.Office2007Document; this.tabStrip1.TabAlignment = DevComponents.DotNetBar.eTabStripAlignment.Top; this.tabStrip1.TabIndex = 14; this.tabStrip1.TabLayoutType = DevComponents.DotNetBar.eTabLayoutType.FixedWithNavigationBox; this.tabStrip1.Text = "tabStrip1"; // // menuRecent // this.menuRecent.BeginGroup = true; this.menuRecent.Name = "menuRecent"; this.menuRecent.Text = "最近打开的文件"; // // FrmConsole // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(488, 380); this.Controls.Add(this.tabStrip1); this.Controls.Add(this.dockSite9); this.Controls.Add(this.dockSite2); this.Controls.Add(this.dockSite1); this.Controls.Add(this.dockSite3); this.Controls.Add(this.dockSite4); this.Controls.Add(this.dockSite5); this.Controls.Add(this.dockSite6); this.Controls.Add(this.dockSite7); this.Controls.Add(this.dockSite8); this.DoubleBuffered = true; this.IsMdiContainer = true; this.Name = "FrmConsole"; this.Text = "Lua控制台"; this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.FrmConsole_FormClosing); this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.FrmConsole_FormClosed); this.MdiChildActivate += new System.EventHandler(this.FrmConsole_MdiChildActivate); this.dockSite4.ResumeLayout(false); ((System.ComponentModel.ISupportInitialize)(this.bar2)).EndInit(); this.bar2.ResumeLayout(false); this.panelDockContainer1.ResumeLayout(false); this.dockSite7.ResumeLayout(false); ((System.ComponentModel.ISupportInitialize)(this.bar1)).EndInit(); this.ResumeLayout(false); } #endregion private DevComponents.DotNetBar.DotNetBarManager dotNetBarManager1; private DevComponents.DotNetBar.DockSite dockSite4; private DevComponents.DotNetBar.Bar bar2; private DevComponents.DotNetBar.PanelDockContainer panelDockContainer1; private DevComponents.DotNetBar.DockContainerItem dockContainerItem1; private DevComponents.DotNetBar.DockSite dockSite9; private DevComponents.DotNetBar.DockSite dockSite1; private DevComponents.DotNetBar.DockSite dockSite2; private DevComponents.DotNetBar.DockSite dockSite3; private DevComponents.DotNetBar.DockSite dockSite5; private DevComponents.DotNetBar.DockSite dockSite6; private DevComponents.DotNetBar.DockSite dockSite7; private DevComponents.DotNetBar.Bar bar1; private DevComponents.DotNetBar.ButtonItem menuRun; private DevComponents.DotNetBar.DockSite dockSite8; private DevComponents.DotNetBar.Controls.TextBoxX textBoxX2; private DevComponents.DotNetBar.DockContainerItem dockContainerItem3; private DevComponents.DotNetBar.ButtonItem menuStopRun; private DevComponents.DotNetBar.ButtonItem menuFile; private DevComponents.DotNetBar.TabStrip tabStrip1; private DevComponents.DotNetBar.ButtonItem menuNew; private DevComponents.DotNetBar.ButtonItem menuOpen; private DevComponents.DotNetBar.ButtonItem menuSave; private DevComponents.DotNetBar.ButtonItem menuExit; private DevComponents.DotNetBar.ButtonItem menuDebug; private DevComponents.DotNetBar.ButtonItem menuReset; private DevComponents.DotNetBar.ButtonItem menuRecent; private DevComponents.DotNetBar.ButtonItem menuSaveAs; } } ================================================ FILE: WzComparerR2.LuaConsole/FrmConsole.cs ================================================ using System; using System.Collections.Generic; using System.IO; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; using DevComponents.DotNetBar; using NLua; using WzComparerR2.Config; using WzComparerR2.LuaConsole.Config; using WzComparerR2.PluginBase; namespace WzComparerR2.LuaConsole { public partial class FrmConsole : DevComponents.DotNetBar.Office2007Form { public FrmConsole() { InitializeComponent(); this.refreshRecentDocItems(); this.env = new LuaEnvironment(this); } private FrmLuaEditor SelectedLuaEditor => tabStrip1.SelectedTab?.AttachedControl as FrmLuaEditor; private readonly LuaEnvironment env; private Task runningTask; private CancellationTokenSource cancellationTokenSource; private void FrmConsole_FormClosing(object sender, FormClosingEventArgs e) { if (e.CloseReason == CloseReason.UserClosing && this.runningTask?.IsCompleted == false) { if (DialogResult.Yes != MessageBoxEx.Show(this, "任务还在运行中,是否关闭?", "提示", MessageBoxButtons.YesNo, MessageBoxIcon.Warning)) { e.Cancel = true; return; } } } private void FrmConsole_FormClosed(object sender, FormClosedEventArgs e) { if (this.runningTask?.IsCompleted == false && this.cancellationTokenSource != null) { this.cancellationTokenSource.Cancel(); try { this.runningTask.Wait(); } catch { // ignore any error } } foreach (var frm in this.MdiChildren) { if (frm is FrmLuaEditor editor && editor.Tag is LuaSandbox sandbox) { sandbox.Dispose(); } } } private void FrmConsole_MdiChildActivate(object sender, EventArgs e) { } private void menuReset_Click(object sender, EventArgs e) { if (this.runningTask?.IsCompleted == false) { ToastNotification.Show(this, "已有任务正在运行", 1000, eToastPosition.TopCenter); return; } var selectedEditor = this.SelectedLuaEditor; if (selectedEditor == null || selectedEditor.Tag is not LuaSandbox sandbox) { MessageBoxEx.Show(this, "获取当前所选窗体失败。", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } string baseDir = null; if (selectedEditor.FileName != null) { baseDir = Path.GetDirectoryName(selectedEditor.FileName); } sandbox.InitLuaEnv(baseDir, true); this.env.WriteLine("运行时已重置"); } private void menuNew_Click(object sender, EventArgs e) { this.CreateNewTab(); } private async void menuRun_Click(object sender, EventArgs e) { if (this.runningTask?.IsCompleted == false) { ToastNotification.Show(this, "已有任务正在运行", 1000, eToastPosition.TopCenter); return; } var selectedEditor = this.SelectedLuaEditor; if (selectedEditor == null || selectedEditor.Tag is not LuaSandbox sandbox) { MessageBoxEx.Show(this, "获取当前所选窗体失败。", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } try { string baseDir = null; if (selectedEditor.FileName != null) { baseDir = Path.GetDirectoryName(selectedEditor.FileName); } this.env.WriteLine("开始执行{0}...", selectedEditor.BaseFileName); sandbox.InitLuaEnv(baseDir); this.cancellationTokenSource = new CancellationTokenSource(); this.runningTask = sandbox.DoStringAsync(selectedEditor.CodeContent, cancellationTokenSource.Token); await this.runningTask; } catch (NLua.Exceptions.LuaScriptException ex) { env.WriteLine(ex); if (ex.IsNetException && ex.InnerException != null) { env.WriteLine(ex.InnerException); } } catch (Exception ex) { env.WriteLine(ex); } } private void menuStopRun_Click(object sender, EventArgs e) { if (this.runningTask?.IsCompleted == false && this.cancellationTokenSource != null) { this.cancellationTokenSource.Cancel(); ToastNotification.Show(this, "正在中止运行...", 1000, eToastPosition.TopCenter); } } private void menuOpen_Click(object sender, EventArgs e) { using OpenFileDialog dlg = new(); dlg.Filter = "*.lua|*.lua|*.*|*.*"; if (dlg.ShowDialog() == DialogResult.OK) { this.OpenFile(dlg.FileName); } } private void menuSave_Click(object sender, EventArgs e) { if (tabStrip1.SelectedTab?.AttachedControl is FrmLuaEditor editor) { this.SaveFile(editor, false); } } private void menuSaveAs_Click(object sender, EventArgs e) { if (tabStrip1.SelectedTab?.AttachedControl is FrmLuaEditor editor) { this.SaveFile(editor, true); } } private void refreshRecentDocItems() { this.menuRecent.SubItems.Clear(); foreach (var doc in LuaConsoleConfig.Default.RecentDocuments) { ButtonItem item = new ButtonItem() { Text = "&" + (this.menuRecent.SubItems.Count + 1) + ". " + Path.GetFileName(doc), Tooltip = doc, Tag = doc }; item.Click += RecentDocumentItem_Click; this.menuRecent.SubItems.Add(item); } } private void RecentDocumentItem_Click(object sender, EventArgs e) { if (sender is ButtonItem item && item.Tag is string fileName) { OpenFile(fileName); } } private void menuExit_Click(object sender, EventArgs e) { this.Close(); } private bool SaveFile(FrmLuaEditor editor, bool saveAs = false) { if (saveAs || string.IsNullOrEmpty(editor.FileName)) { using SaveFileDialog dlg = new SaveFileDialog(); dlg.Filter = "*.lua|*.lua|*.*|*.*"; if (editor.BaseFileName != null) { dlg.FileName = editor.BaseFileName; } if (dlg.ShowDialog() != DialogResult.OK) { return false; } editor.FileName = dlg.FileName; } editor.SaveFile(editor.FileName); this.env.WriteLine($"====已经保存{editor.FileName}===="); return true; } private void OpenFile(string fileName) { try { this.CreateNewTab(fileName); } catch (Exception ex) { MessageBoxEx.Show(this, ex.ToString(), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } ConfigManager.Reload(); var config = LuaConsoleConfig.Default; config.RecentDocuments.Remove(fileName); config.RecentDocuments.Insert(0, fileName); for (int i = config.RecentDocuments.Count - 1; i >= 10; i--) { config.RecentDocuments.RemoveAt(i); } ConfigManager.Save(); refreshRecentDocItems(); } private void CreateNewTab(string fileName = null) { FrmLuaEditor frm = new FrmLuaEditor(); frm.FileNameChanged += this.FrmLuaEditor_FileNameChanged; frm.FormClosing += this.FrmLuaEditor_FormClosing; frm.FormClosed += this.FrmLuaEditor_FormClosed; frm.Tag = new LuaSandbox(this.env); try { if (!string.IsNullOrEmpty(fileName)) { frm.LoadFile(fileName); } frm.MdiParent = this; frm.WindowState = FormWindowState.Maximized; frm.Show(); } catch { frm.Dispose(); throw; } } private void FrmLuaEditor_FileNameChanged(object sender, EventArgs e) { if (sender is FrmLuaEditor frm) { foreach (TabItem tab in this.tabStrip1.Tabs) { if (tab.AttachedControl == frm) { tab.Tooltip = frm.FileName; break; } } } } private void FrmLuaEditor_FormClosing(object sender, FormClosingEventArgs e) { if (sender is FrmLuaEditor editor && editor.IsContentModified) { switch(MessageBoxEx.Show(this, "关闭窗口将丢失所有未保存的修改,是否保存?", "提示", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Warning)) { case DialogResult.Yes: e.Cancel = !this.SaveFile(editor, false); break; case DialogResult.No: e.Cancel = false; break; default: e.Cancel = true; break; } } } private void FrmLuaEditor_FormClosed(object sender, FormClosedEventArgs e) { if (sender is FrmLuaEditor frm && frm.Tag is Lua lua) { lua.Dispose(); } } private Lua GetOrCreateLuaVM(FrmLuaEditor frm) { if (frm.Tag is not Lua lua) { lua = null; } return lua; } public class LuaEnvironment { internal LuaEnvironment(FrmConsole form) { this.form = form; } private FrmConsole form; public PluginContext Context { get { return Entry.Instance?.Context; } } public void Write(object value) { if (value != null) { this.AppendText(value.ToString()); } } public void Write(string format, params object[] args) { if (format != null) { string content = string.Format(format, args ?? new object[0]); this.AppendText(content); } } public void WriteLine() { this.WriteLine(null); } public void WriteLine(object value) { if (value != null) { this.AppendText(value.ToString()); } this.AppendText(Environment.NewLine); } public void WriteLine(string format, object arg0) { this.WriteLine(format, new object[] { arg0 }); } public void WriteLine(string format, object arg0, object arg1) { this.WriteLine(format, new object[] { arg0, arg1 }); } public void WriteLine(string format, object arg0, object arg1, object arg2) { this.WriteLine(format, new object[] { arg0, arg1, arg2 }); } public void WriteLine(string format, params object[] args) { if (format != null) { string content; if (args == null || args.Length <= 0) { content = format; } else { content = string.Format(format, args ?? new object[0]); } this.AppendText(content); } this.AppendText(Environment.NewLine); } public void Help() { this.WriteLine(@"-- 标准输出函数: env:Write(object) env:Write(string format, object[] args) env:WriteLine(object) env:WriteLine(string format, object[] args) "); } private void AppendText(string text) { if (!this.form.textBoxX2.IsDisposed) { text = Regex.Replace(text, @"(? text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 17, 17 ================================================ FILE: WzComparerR2.LuaConsole/FrmLuaEditor.Designer.cs ================================================ namespace WzComparerR2.LuaConsole { partial class FrmLuaEditor { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { this.textEditorControl1 = new ICSharpCode.TextEditor.TextEditorControl(); this.SuspendLayout(); // // textEditorControl1 // this.textEditorControl1.Dock = System.Windows.Forms.DockStyle.Fill; this.textEditorControl1.IsReadOnly = false; this.textEditorControl1.Location = new System.Drawing.Point(0, 0); this.textEditorControl1.Name = "textEditorControl1"; this.textEditorControl1.Size = new System.Drawing.Size(284, 261); this.textEditorControl1.TabIndex = 0; this.textEditorControl1.TextChanged += new System.EventHandler(this.textEditorControl1_TextChanged); this.textEditorControl1.FileNameChanged += new System.EventHandler(this.textEditorControl1_FileNameChanged); // // FrmLuaEditor // this.ClientSize = new System.Drawing.Size(284, 261); this.Controls.Add(this.textEditorControl1); this.DoubleBuffered = true; this.Name = "FrmLuaEditor"; this.Text = "FrmLuaEditor"; this.ResumeLayout(false); } #endregion private ICSharpCode.TextEditor.TextEditorControl textEditorControl1; } } ================================================ FILE: WzComparerR2.LuaConsole/FrmLuaEditor.cs ================================================ using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.IO; using System.Text; using System.Windows.Forms; using DevComponents.DotNetBar; using ICSharpCode.TextEditor.Document; namespace WzComparerR2.LuaConsole { public partial class FrmLuaEditor : DevComponents.DotNetBar.OfficeForm { static bool globalInit = false; public FrmLuaEditor() { if (!globalInit) { HighlightingManager.Manager.AddSyntaxModeFileProvider(new AppSyntaxModeProvider()); globalInit = true; } InitializeComponent(); textEditorControl1.SetHighlighting("Lua"); this.BaseFileName = "untitled"; } private string _baseFileName; private bool _isContentModified; public void LoadFile(string fileName) { this.textEditorControl1.LoadFile(fileName, false, true); this.BaseFileName = Path.GetFileName(fileName); this.IsContentModified = false; } public void SaveFile(string fileName) { this.textEditorControl1.SaveFile(fileName); this.BaseFileName = Path.GetFileName(fileName); this.IsContentModified = false; } public string FileName { get { return this.textEditorControl1.FileName; } set { this.textEditorControl1.FileName = value; } } public string CodeContent { get { return this.textEditorControl1.Text; } } public bool IsContentModified { get => this._isContentModified; private set { if (this._isContentModified != value) { this._isContentModified = value; this.UpdateTitle(); } } } public string BaseFileName { get => this._baseFileName; private set { if (this._baseFileName != value) { this._baseFileName = value; this.UpdateTitle(); } } } public event EventHandler FileNameChanged; private void UpdateTitle() { this.Text = (this._baseFileName ?? "(null)") + (this._isContentModified ? "*" : null); } private void textEditorControl1_TextChanged(object sender, EventArgs e) { this.IsContentModified = true; } private void textEditorControl1_FileNameChanged(object sender, EventArgs e) { this.FileNameChanged?.Invoke(this, e); } } } ================================================ FILE: WzComparerR2.LuaConsole/FrmLuaEditor.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ================================================ FILE: WzComparerR2.LuaConsole/Lua/CLRPackage.lua ================================================ --- --- This lua module provides auto importing of .net classes into a named package. --- Makes for super easy use of LuaInterface glue --- --- example: --- Threading = CLRPackage("System", "System.Threading") --- Threading.Thread.Sleep(100) --- --- Extensions: --- import() is a version of CLRPackage() which puts the package into a list which is used by a global __index lookup, --- and thus works rather like C#'s using statement. It also recognizes the case where one is importing a local --- assembly, which must end with an explicit .dll extension. --- Alternatively, luanet.namespace can be used for convenience without polluting the global namespace: --- local sys,sysi = luanet.namespace {'System','System.IO'} -- sys.Console.WriteLine("we are at {0}",sysi.Directory.GetCurrentDirectory()) -- LuaInterface hosted with stock Lua interpreter will need to explicitly require this... if not luanet then require 'luanet' end local import_type, load_assembly = luanet.import_type, luanet.load_assembly local mt = { --- Lookup a previously unfound class and add it to our table __index = function(package, classname) local class = rawget(package, classname) if class == nil then class = import_type(package.packageName .. "." .. classname) package[classname] = class -- keep what we found around, so it will be shared end return class end } function luanet.namespace(ns) if type(ns) == 'table' then local res = {} for i = 1,#ns do res[i] = luanet.namespace(ns[i]) end return unpack(res) end -- FIXME - table.packageName could instead be a private index (see Lua 13.4.4) local t = { packageName = ns } setmetatable(t,mt) return t end local globalMT, packages local function set_global_mt() packages = {} globalMT = { __index = function(T,classname) for i,package in ipairs(packages) do local class = package[classname] if class then _G[classname] = class return class end end end } setmetatable(_G, globalMT) end --- Create a new Package class function CLRPackage(assemblyName, packageName) -- a sensible default... packageName = packageName or assemblyName local ok = pcall(load_assembly,assemblyName) -- Make sure our assembly is loaded return luanet.namespace(packageName) end function import (assemblyName, packageName) if not globalMT then set_global_mt() end if not packageName then local i = assemblyName:find('%.dll$') if i then packageName = assemblyName:sub(1,i-1) else packageName = assemblyName end end local t = CLRPackage(assemblyName,packageName) table.insert(packages,t) return t end function luanet.make_array (tp,tbl) local arr = tp[#tbl] for i,v in ipairs(tbl) do arr:SetValue(v,i-1) end return arr end function luanet.each(o) local e = o:GetEnumerator() return function() if e:MoveNext() then return e.Current end end end ================================================ FILE: WzComparerR2.LuaConsole/LuaSandbox.cs ================================================ using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; using WzComparerR2.WzLib; using NLua; namespace WzComparerR2.LuaConsole { public class LuaSandbox : IDisposable { public LuaSandbox(object env) { this.Env = env; } public Lua Lua { get; private set; } public object Env { get; private set; } private bool hookEventAttched; public void InitLuaEnv(string scriptBaseDir = null, bool forceReset = false) { if (this.Lua != null) { if (forceReset) { this.Lua.Dispose(); this.Lua = null; } else { return; } } var lua = new Lua(); lua.State.Encoding = Encoding.UTF8; lua.LoadCLRPackage(); lua["env"] = this.Env; // TODO: will move this function to external lua file lua.DoString(@" local t_IEnumerable = {} t_IEnumerable.typeRef = luanet.import_type('System.Collections.IEnumerable') t_IEnumerable.GetEnumerator = luanet.get_method_bysig(t_IEnumerable.typeRef, 'GetEnumerator') local t_IEnumerator = {} t_IEnumerator.typeRef = luanet.import_type('System.Collections.IEnumerator') t_IEnumerator.MoveNext = luanet.get_method_bysig(t_IEnumerator.typeRef, 'MoveNext') t_IEnumerator.get_Current = luanet.get_method_bysig(t_IEnumerator.typeRef, 'get_Current') function each(userData) if type(userData) == 'userdata' then local i = 0; local ienum = t_IEnumerable.GetEnumerator(userData) return function() if ienum and t_IEnumerator.MoveNext(ienum) then i = i + 1 return i, t_IEnumerator.get_Current(ienum) end return nil, nil end end end "); // Only set the package path on the first run. // path order: // 1. script file folder // 2. luaConsole plugin folder // 3. wzComparerR2 folder // 4. current folder string pluginBaseDir = Path.GetDirectoryName(typeof(Entry).Assembly.Location); string wcR2Folder = Application.StartupPath; List packagePath = new List(16); foreach (var baseDir in new[] { scriptBaseDir, pluginBaseDir, wcR2Folder }) { if (!string.IsNullOrEmpty(baseDir)) { packagePath.Add(Path.Combine(baseDir, "?.lua")); packagePath.Add(Path.Combine(baseDir, "?", "init.lua")); packagePath.Add(Path.Combine(baseDir, "lua", "?.lua")); packagePath.Add(Path.Combine(baseDir, "lua", "?", "init.lua")); } } packagePath.Add(Path.Combine(".", "?.lua")); packagePath.Add(Path.Combine(".", "?", "init.lua")); lua.SetObjectToPath("package.path", string.Join(";", packagePath.Distinct())); // Register commonly used delegate types lua.RegisterLuaDelegateType(typeof(WzComparerR2.GlobalFindNodeFunction), typeof(LuaGlobalFindNodeFunctionHandler)); this.Lua = lua; } public async Task DoStringAsync(string chunk, CancellationToken cancellationToken = default) { cancellationToken.Register(this.HookCancellationEvent); try { return await Task.Run(() => this.Lua.DoString(chunk), cancellationToken).ConfigureAwait(false); } finally { this.RemoveCancellationEvent(); } } private void HookCancellationEvent() { if (!this.hookEventAttched) { this.Lua.DebugHook += this.Lua_DebugHook; this.Lua.SetDebugHook(KeraLua.LuaHookMask.Count, 1); this.hookEventAttched = true; } } private void RemoveCancellationEvent() { if (this.hookEventAttched) { this.Lua.DebugHook -= this.Lua_DebugHook; this.Lua.RemoveDebugHook(); this.hookEventAttched = false; } } private void Lua_DebugHook(object sender, NLua.Event.DebugHookEventArgs e) { ((Lua)sender).State.Error("Operation cancelled."); } public void Dispose() { if (this.Lua != null) { this.Lua.Dispose(); this.Lua = null; } } class LuaGlobalFindNodeFunctionHandler : NLua.Method.LuaDelegate { Wz_Node CallFunction(string wzPath) { object[] args = { wzPath }; object[] inArgs = { wzPath }; int[] outArgs = { }; object ret = CallFunction(args, inArgs, outArgs); return (Wz_Node)ret; } } } } ================================================ FILE: WzComparerR2.LuaConsole/Properties/AssemblyInfo.cs ================================================ using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // 有关程序集的常规信息通过以下 // 特性集控制。更改这些特性值可修改 // 与程序集关联的信息。 [assembly: AssemblyTitle("WzComparerR2.LuaConsole")] [assembly: AssemblyDescription("用于WzComparerR2的控制台插件。")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Kagamia Studio")] [assembly: AssemblyProduct("WzComparerR2.LuaConsole")] [assembly: AssemblyCopyright("Copyright © Kagamia Studio 2014-2025")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] // 将 ComVisible 设置为 false 使此程序集中的类型 // 对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型, // 则将该类型上的 ComVisible 特性设置为 true。 [assembly: ComVisible(false)] // 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID [assembly: Guid("8ffb04b2-8c73-4d19-8f2d-3c8de3ecfd27")] // 程序集的版本信息由下面四个值组成: // // 主版本 // 次版本 // 生成号 // 修订号 // // 可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值, // 方法是按如下所示使用“*”: // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("2.2.0.0")] [assembly: AssemblyFileVersion("2.2.0.10725")] ================================================ FILE: WzComparerR2.LuaConsole/Properties/Resources.Designer.cs ================================================ //------------------------------------------------------------------------------ // // 此代码由工具生成。 // 运行时版本:4.0.30319.42000 // // 对此文件的更改可能会导致不正确的行为,并且如果 // 重新生成代码,这些更改将会丢失。 // //------------------------------------------------------------------------------ namespace WzComparerR2.LuaConsole.Properties { using System; /// /// 一个强类型的资源类,用于查找本地化的字符串等。 /// // 此类是由 StronglyTypedResourceBuilder // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen // (以 /str 作为命令选项),或重新生成 VS 项目。 [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class Resources { private static global::System.Resources.ResourceManager resourceMan; private static global::System.Globalization.CultureInfo resourceCulture; [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal Resources() { } /// /// 返回此类使用的缓存的 ResourceManager 实例。 /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("WzComparerR2.LuaConsole.Properties.Resources", typeof(Resources).Assembly); resourceMan = temp; } return resourceMan; } } /// /// 重写当前线程的 CurrentUICulture 属性,对 /// 使用此强类型资源类的所有资源查找执行重写。 /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Globalization.CultureInfo Culture { get { return resourceCulture; } set { resourceCulture = value; } } /// /// 查找 System.Byte[] 类型的本地化资源。 /// internal static byte[] Lua { get { object obj = ResourceManager.GetObject("Lua", resourceCulture); return ((byte[])(obj)); } } /// /// 查找 System.Byte[] 类型的本地化资源。 /// internal static byte[] SharpLua { get { object obj = ResourceManager.GetObject("SharpLua", resourceCulture); return ((byte[])(obj)); } } } } ================================================ FILE: WzComparerR2.LuaConsole/Properties/Resources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ..\resources\lua.xshd;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ..\Resources\SharpLua.xshd;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ================================================ FILE: WzComparerR2.LuaConsole/Resources/Lua.xshd ================================================ &<>~!%^*()-+=|\/{}[]:;"' , .? --[[ ]] -- " " ' ' [[ ]] ( ================================================ FILE: WzComparerR2.LuaConsole/Resources/SharpLua.xshd ================================================ TODO FIXME HACK UNDONE --- --\[[=]*\[ \][=]*] -- " " ' ' \[[=]*\[ \][=]*] true false and break do else elseif end false for function if in local not or repeat return then true until while using break return local nil \b [\d\w_]+ # an identifier (?=\s*\() # followed by ( \b [\d\w_]+ # an identifier (?=\s*\") # followed by " \b [\d\w_]+ # an identifier (?=\s*\') # followed by ' \b [\d\w_]+ # an identifier (?=\s*\{) # followed by { \b [\d\w_]+ # an identifier (?=\s*\[) # followed by [ \b0[xX][0-9a-fA-F]+ # hex number | ( \b\d+(\.[0-9]+)? #number with optional floating point | \.[0-9]+ #or just starting with floating point ) ([eE][+-]?[0-9]+)? # optional exponent [?,.;()\[\]{}+\-/%*<>^+~!|&]+ ================================================ FILE: WzComparerR2.LuaConsole/WzComparerR2.LuaConsole.csproj ================================================  net462;net6.0-windows;net8.0-windows true WzComparerR2.LuaConsole WzComparerR2.LuaConsole false true false false all false all false all ..\References\DevComponents.DotNetBar2.dll false PreserveNewest Properties\CommonAssemblyInfo.cs ================================================ FILE: WzComparerR2.MapRender/Animation/IStateMachineAnimationData.cs ================================================ using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Text; namespace WzComparerR2.Animation { public interface IStateMachineAnimationData { ReadOnlyCollection States { get; } int SelectedStateIndex { get; set; } string SelectedState { get; } void Update(TimeSpan elapsedTime); event EventHandler AnimationEnd; object GetMesh(); } } ================================================ FILE: WzComparerR2.MapRender/Animation/RepeatableFrameAnimationData.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace WzComparerR2.Animation { public class RepeatableFrameAnimationData : FrameAnimationData { public RepeatableFrameAnimationData() { } public RepeatableFrameAnimationData(IEnumerable frames) : base(frames) { } public bool? Repeat { get; set; } } } ================================================ FILE: WzComparerR2.MapRender/Animation/RepeatableFrameAnimator.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using Microsoft.Xna.Framework; namespace WzComparerR2.Animation { public class RepeatableFrameAnimator : FrameAnimator { public RepeatableFrameAnimator(RepeatableFrameAnimationData data) : base(data) { this.Data = data; this.IsLoop = this.Data.Repeat ?? true; } public new RepeatableFrameAnimationData Data { get; private set; } public bool IsStopped { get; private set; } public bool IsLoop { get; set; } public override void Update(TimeSpan elapsedTime) { if (!IsLoop) { int _timeOffset = base.CurrentTime; if (this.Length <= 0) { _timeOffset = 0; } else { _timeOffset += (int)elapsedTime.TotalMilliseconds; if (!this.IsStopped) { if (_timeOffset >= this.Length) { _timeOffset = this.Length; this.IsStopped = true; } } else { _timeOffset = this.Length; } } base.CurrentTime = _timeOffset; this.UpdateFrame(); } else { base.Update(elapsedTime); } } public override void Reset() { base.Reset(); IsStopped = false; } protected override void Load() { base.Load(); } protected override void UpdateFrame() { if (!IsLoop && this.IsStopped) { var frame = this.Data.Frames.Last(); if (this.CurrentFrame == null) { this.CurrentFrame = new Frame(); } this.CurrentFrame.Texture = frame.Texture; this.CurrentFrame.AtlasRect = frame.AtlasRect; this.CurrentFrame.Z = frame.Z; this.CurrentFrame.Origin = frame.Origin; this.CurrentFrame.A0 = frame.A1; this.CurrentFrame.Blend = frame.Blend; } else { base.UpdateFrame(); } } } } ================================================ FILE: WzComparerR2.MapRender/Animation/StateMachineAnimator.cs ================================================ using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Text; using Microsoft.Xna.Framework; namespace WzComparerR2.Animation { public class StateMachineAnimator { public StateMachineAnimator(IDictionary data) : this(new FrameStateMachineData(data)) { } public StateMachineAnimator(ISpineAnimationData data) { throw new NotImplementedException(); } private StateMachineAnimator(IStateMachineAnimationData data) { this.Data = data; this.Data.AnimationEnd += Data_AnimationEnd; } public IStateMachineAnimationData Data { get; private set; } public event EventHandler AnimationEnd; public void SetAnimation(string aniName) { int idx = this.Data.States.IndexOf(aniName); this.Data.SelectedStateIndex = idx; } public string GetCurrent() { return this.Data.SelectedState; } public void Update(TimeSpan elapsedTime) { this.Data.Update(elapsedTime); } protected virtual void OnAnimationEnd(AnimationEndEventArgs e) { this.AnimationEnd?.Invoke(this, e); if (!e.IsHandled) { if (e.CurrentState != e.NextState) { this.SetAnimation(e.NextState); } } } private void Data_AnimationEnd(object sender, EventArgs e) { string state = GetCurrent(); var ev = new AnimationEndEventArgs(state); ev.NextState = state; this.OnAnimationEnd(ev); } public class AnimationEndEventArgs : EventArgs { public AnimationEndEventArgs(string currentState) { this.CurrentState = currentState; } public bool IsHandled { get; set; } public string CurrentState { get; } public string NextState { get; set; } } /// /// 用于帧动画的状态机数据。 /// private class FrameStateMachineData : IStateMachineAnimationData { public FrameStateMachineData(IDictionary data) { this.data = new Dictionary(data); this.States = new ReadOnlyCollection(new List(this.data.Keys)); this.SelectedStateIndex = -1; } private IDictionary data; private int selectedIndex; private RepeatableFrameAnimator selectedData; public ReadOnlyCollection States { get; private set; } public int SelectedStateIndex { get { return this.selectedIndex; } set { if (value < 0 || value >= this.States.Count) { this.selectedIndex = -1; this.selectedData = null; } else { this.selectedIndex = value; this.selectedData = new RepeatableFrameAnimator(this.data[SelectedState]); } } } public string SelectedState { get { return this.selectedIndex < 0 ? null : this.States[selectedIndex]; } } public event EventHandler AnimationEnd; protected virtual void OnAnimationEnd(EventArgs e) { this.AnimationEnd?.Invoke(this, e); } public void Update(TimeSpan elapsedTime) { if (this.selectedData == null) return; if (this.selectedData.CurrentTime + elapsedTime.TotalMilliseconds >= selectedData.Length) { this.selectedData.Update(elapsedTime); //this.Update(TimeSpan.FromMilliseconds(selectedData.Length - selectedData.CurrentTime)); OnAnimationEnd(EventArgs.Empty); } else { this.selectedData.Update(elapsedTime); } } public object GetMesh() { return this.selectedData?.CurrentFrame; } } /// /// 用于骨骼动画的状态机数据。 /// private class SpineStateMachineData : IStateMachineAnimationData { public string SelectedState { get { throw new NotImplementedException(); } } public int SelectedStateIndex { get { throw new NotImplementedException(); } set { throw new NotImplementedException(); } } public ReadOnlyCollection States { get { throw new NotImplementedException(); } } public event EventHandler AnimationEnd; public object GetMesh() { throw new NotImplementedException(); } public void Update(TimeSpan deltaTime) { throw new NotImplementedException(); } } } } ================================================ FILE: WzComparerR2.MapRender/Camera.cs ================================================ using System; using System.Collections.Generic; using System.Text; using Microsoft.Xna.Framework; namespace WzComparerR2.MapRender { public class Camera { public Camera(GraphicsDeviceManager graphics) { this.graphics = graphics; this.AdjustRectEnabled = true; } GraphicsDeviceManager graphics; Rectangle worldRect; Vector2 center; int displayMode; bool useWorldRect; public GraphicsDeviceManager Graphics { get { return graphics; } set { graphics = value; } } /// /// 获取或设置摄像机中心对应的世界坐标。 /// public Vector2 Center { get { return center; } set { center = value; } } /// /// 获取摄像机宽度。 /// public int Width { get { return graphics.PreferredBackBufferWidth; } } /// /// 获取摄像机高度。 /// public int Height { get { return graphics.PreferredBackBufferHeight; } } /// /// 获取摄像机矩形的世界坐标。 /// public Rectangle ClipRect { get { if (useWorldRect) return worldRect; else return new Rectangle((int)center.X - Width / 2, (int)center.Y - Height / 2, Width, Height); } } /// /// 获取摄像机左上角对应的世界坐标。 /// public Vector2 Origin { get { Rectangle rect = ClipRect; return new Vector2(rect.X, rect.Y); } } public Rectangle WorldRect { get { return worldRect; } set { worldRect = value; } } public int DisplayMode { get { return displayMode; } set { displayMode = value; ChangeDisplayMode(); } } public bool UseWorldRect { get { return useWorldRect; } set { useWorldRect = value; } } public bool AdjustRectEnabled { get; set; } private void ChangeDisplayMode() { switch (this.displayMode) { case 0: graphics.PreferredBackBufferWidth = 800; graphics.PreferredBackBufferHeight = 600; break; case 1: graphics.PreferredBackBufferWidth = 1024; graphics.PreferredBackBufferHeight = 768; break; case 2: graphics.PreferredBackBufferWidth = 1366; graphics.PreferredBackBufferHeight = 768; break; case 3: graphics.PreferredBackBufferWidth = graphics.GraphicsDevice.DisplayMode.Width; graphics.PreferredBackBufferHeight = graphics.GraphicsDevice.DisplayMode.Height; break; default: goto case 0; } graphics.ApplyChanges(); } public void AdjustToWorldRect() { if (this.useWorldRect) return; if (!this.AdjustRectEnabled) return; if (this.Width > worldRect.Width) { this.center.X = worldRect.Center.X; } else { this.center.X = MathHelper.Clamp(this.center.X, worldRect.Left + this.Width / 2, worldRect.Right - this.Width / 2); } if (this.Height > worldRect.Height) { this.center.Y = worldRect.Center.Y; } else { this.center.Y = MathHelper.Clamp(this.center.Y, worldRect.Top + this.Height / 2, worldRect.Bottom - this.Height / 2); } } public Rectangle MeasureDrawingRect(int width, int height, Vector2 position, Vector2 origin, bool flipX) { Rectangle drawingRect; drawingRect.Width = width; drawingRect.Height = height; if (flipX) { drawingRect.X = (int)(position.X + origin.X - width); drawingRect.Y = (int)(position.Y - origin.Y); } else { drawingRect.X = (int)(position.X - origin.X); drawingRect.Y = (int)(position.Y - origin.Y); } return drawingRect; } #if MapRenderV1 public bool CheckSpriteVisible(RenderFrame frame, Vector2 position, bool flip) { Rectangle drawingRect; return CheckSpriteVisible(frame, position, flip, out drawingRect); } public bool CheckSpriteVisible(RenderFrame frame, Vector2 position, bool flip, out Rectangle drawingRect) { if (frame == null || frame.Texture == null) { drawingRect = Rectangle.Empty; return false; } drawingRect = MeasureDrawingRect( frame.Texture.Width, frame.Texture.Height, position, frame.Origin, flip); return this.ClipRect.Intersects(drawingRect); } #endif public Point CameraToWorld(Point cameraPoint) { cameraPoint.X += this.ClipRect.X; cameraPoint.Y += this.ClipRect.Y; return cameraPoint; } } } ================================================ FILE: WzComparerR2.MapRender/Chat.cs ================================================ using System; using System.Collections.Generic; using System.Text; //using PokeIn.Comet; namespace WzComparerR2.MapRender { /* public class Chat { public Chat() { this.caller = new APICaller(); this.client = new DesktopClient(caller, "http://kagamia.sitecloud.cytanium.com/wcweb/chat.ashx", null); client.OnClientConnected += (e) => { if (this.Connected != null) { this.Connected(this, EventArgs.Empty); } }; client.OnClientDisconnected += (e) => { if (this.Disconnected != null) { this.Disconnected(this, EventArgs.Empty); } }; client.OnErrorReceived += (o, e) => { if (this.Error != null) { this.Error(this, new ChatErrorEventArgs() { Error = e }); } }; this.caller.MessageReceived += (o, e) => { if (this.MessageReceived != null) { this.MessageReceived(this, e); } }; } private DesktopClient client; private APICaller caller; public bool IsConnected { get { return this.client.IsConnected; } } public event EventHandler Connected; public event EventHandler Disconnected; public event EventHandler Error; public event EventHandler MessageReceived; public void Connect() { client.Connect(); } public void Disconnect() { client.Close(); client.Dispose(); } public void Talk(string message) { if (client.IsConnected) { client.SendAsync("Chat.Talk", message); } } public void SetName(string name) { if (client.IsConnected) { client.SendAsync("Chat.SetName", name); } } public class APICaller { public event EventHandler MessageReceived; public void OnMessage(int type, string from, string to, string msg) { if (this.MessageReceived != null) { ChatMessageEventArgs e = new ChatMessageEventArgs(); e.MsgType = type; e.FromName = from; e.ToName = to; e.MessageText = msg; this.MessageReceived(this, e); } } } } public class ChatErrorEventArgs : EventArgs { public string Error { get; set; } } public class ChatMessageEventArgs : EventArgs { public int MsgType { get; set; } public string FromName { get; set; } public string ToName { get; set; } public string MessageText { get; set; } } */ } ================================================ FILE: WzComparerR2.MapRender/Config/MapRenderConfig.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Configuration; using WzComparerR2.Config; namespace WzComparerR2.MapRender.Config { [SectionName("WcR2.MapRender")] public sealed class MapRenderConfig : ConfigSectionBase { public MapRenderConfig() { this.Volume = 1f; this.MuteOnLeaveFocus = true; this.ClipMapRegion = true; } [ConfigurationProperty("volume")] public ConfigItem Volume { get { return (ConfigItem)this["volume"]; } set { this["volume"] = value; } } [ConfigurationProperty("muteOnLeaveFocus")] public ConfigItem MuteOnLeaveFocus { get { return (ConfigItem)this["muteOnLeaveFocus"]; } set { this["muteOnLeaveFocus"] = value; } } [ConfigurationProperty("defaultFontIndex")] public ConfigItem DefaultFontIndex { get { return (ConfigItem)this["defaultFontIndex"]; } set { this["defaultFontIndex"] = value; } } [ConfigurationProperty("clipMapRegion")] public ConfigItem ClipMapRegion { get { return (ConfigItem)this["clipMapRegion"]; } set { this["clipMapRegion"] = value; } } [ConfigurationProperty("useD2DRenderer")] public ConfigItem UseD2dRenderer { get { return (ConfigItem)this["useD2DRenderer"]; } set { this["useD2DRenderer"] = value; } } [ConfigurationProperty("topBar.Visible")] public ConfigItem TopBarVisible { get { return (ConfigItem)this["topBar.Visible"]; } set { this["topBar.Visible"] = value; } } [ConfigurationProperty("minimap.CameraRegionVisible")] public ConfigItem Minimap_CameraRegionVisible { get { return (ConfigItem)this["minimap.CameraRegionVisible"]; } set { this["minimap.CameraRegionVisible"] = value; } } [ConfigurationProperty("worldMap.UseImageNameAsInfoName")] public ConfigItem WorldMap_UseImageNameAsInfoName { get { return (ConfigItem)this["worldMap.UseImageNameAsInfoName"]; } set { this["worldMap.UseImageNameAsInfoName"] = value; } } [ConfigurationProperty("screenshotBackgroundColor")] public ConfigItem ScreenshotBackgroundColor { get { return (ConfigItem)this["screenshotBackgroundColor"]; } set { this["screenshotBackgroundColor"] = value; } } } } ================================================ FILE: WzComparerR2.MapRender/Coroutine.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Diagnostics; using System.Threading.Tasks; using Microsoft.Xna.Framework; using IE = System.Collections.IEnumerator; namespace WzComparerR2.MapRender { class CoroutineManager : GameComponent { public CoroutineManager(Game game) : base(game) { this.Enabled = true; this.runList = new LinkedList(); this.preAdd = new List(); this.preRemove = new List(); } public GameTime GameTime { get; private set; } private LinkedList runList; private List preAdd; private List preRemove; private bool isUpdating; public override void Update(GameTime gameTime) { this.GameTime = gameTime; this.isUpdating = true; LinkedListNode node = null; while ((node = (node == null ? runList.First : node.Next)) != null) { preAdd.Clear(); preRemove.Clear(); bool removeMe = true; var coroutine = node.Value; while (coroutine != null) { if (coroutine.Enumerator != null) { bool hasNext = coroutine.Enumerator.MoveNext(); if (hasNext) { removeMe = false; var value = coroutine.Enumerator.Current; if (value == null) { //跳到下次update } else if (value is YieldCoroutine) { node.Value = ((YieldCoroutine)value); } else if (value is Coroutine) { //栈执行 var nextCoroutine = (Coroutine)value; nextCoroutine.Prev = coroutine; node.Value = nextCoroutine; preAdd.Remove(nextCoroutine); } else { //其他奇妙类型 忽略 } break; } } coroutine = coroutine.Prev; } foreach (var c in preRemove) { runList.Remove(c); } foreach (var c in preAdd) { runList.AddLast(c); } if (removeMe) { var nextNode = node.Previous; runList.Remove(node); node = nextNode; } } this.isUpdating = false; } public Coroutine StartCoroutine(IE ie) { return this.StartCoroutine(new Coroutine() { Enumerator = ie }); } public Coroutine StartCoroutine(Coroutine coroutine) { lock (this) { if (this.isUpdating) { preAdd.Add(coroutine); } else { this.runList.AddLast(coroutine); } return coroutine; } } public Coroutine Yield(IE ie) { return new YieldCoroutine() { Enumerator = ie }; } public Coroutine Yield(Coroutine coroutine) { return new YieldCoroutine() { Enumerator = coroutine.Enumerator }; } public Coroutine Post(Action action) { return new InvokeActionCoroutine(action); } public Coroutine Post(Action action, T arg0) { return new InvokeActionCoroutine(action, arg0); } public void StopCoroutine(Coroutine coroutine) { if (this.isUpdating) { preRemove.Add(coroutine); } else { this.runList.Remove(coroutine); } } } class Coroutine { public IE Enumerator { get; set; } public Coroutine Prev { get; set; } } sealed class YieldCoroutine : Coroutine { } sealed class WaitTaskCompletedCoroutine : Coroutine { public WaitTaskCompletedCoroutine(Task task) { this.Task = task; this.Enumerator = this.GetEnumerator(); } public Task Task { get; set; } private IE GetEnumerator() { while (!Task.IsCompleted) { yield return null; } if (Task.IsFaulted) { PluginBase.PluginManager.LogError("MapRender", Task.Exception, "Coroutine Error: "); #if DEBUG if (Debugger.IsAttached) { var ex = Task.Exception; Debugger.Break(); } #endif } } } sealed class InvokeActionCoroutine : Coroutine { public InvokeActionCoroutine(Action action) { this.Action = action; this.Enumerator = this.GetEnumerator(); } public Action Action { get; set; } private IE GetEnumerator() { this.Action.Invoke(); yield break; } } sealed class InvokeActionCoroutine : Coroutine { public InvokeActionCoroutine(Action action, T arg0) { this.Action = action; this.Arg0 = arg0; this.Enumerator = this.GetEnumerator(); } public Action Action { get; set; } public T Arg0 { get; set; } private IE GetEnumerator() { this.Action.Invoke(this.Arg0); yield break; } } } ================================================ FILE: WzComparerR2.MapRender/Effects/EffectResources.cs ================================================ using Microsoft.Xna.Framework.Graphics; using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using WzComparerR2.Rendering.EffectCompiler; namespace WzComparerR2.MapRender.Effects { public static class EffectResources { static EffectResources() { nativeShaderDescriptions = new Dictionary(); nativeShaderMgfxCache = new Dictionary(); nativeShaderDescriptions.Add("default", new NativeShaderDesc() { Name = "default", OriginFileName = "g_module_default_ps", FileLength = 796, Version = "KMST1186", Stage = ShaderStage.Pixel, ConstantBuffers = new List(), Samplers = new List() { new SamplerInfo() { Name = "src_sampler_sampler_s", SamplerSlot = 0, TextureName = "src_sampler", TextureSlot = 0, Type = SamplerType.Sampler2D, } }, }); nativeShaderDescriptions.Add("light", new NativeShaderDesc { Name = "light", OriginFileName = "g_module_light_ps", FileLength = 1828, Version = "KMST1186", Stage = ShaderStage.Pixel, ConstantBuffers = new List() { new ConstantBuffer { Name = "_2", Slot = 2, SizeInBytes = 16, Parameters = new List() { new ShaderParameter("z", null, 0, EffectParameterClass.Scalar, EffectParameterType.Single, 1, 1, 0), } }, new ConstantBuffer { Name = "_4", Slot = 4, SizeInBytes = 96, Parameters = new List() { new ShaderParameter("player_pos", "cb1[0].xy", 0, EffectParameterClass.Vector, EffectParameterType.Single, 1, 2, 0), new ShaderParameter("light_inner_radius", "cb1[0].z", 8, EffectParameterClass.Scalar, EffectParameterType.Single, 1, 1, 0), new ShaderParameter("light_outer_radius", "cb1[0].w", 12, EffectParameterClass.Scalar, EffectParameterType.Single, 1, 1, 0), new ShaderParameter("player_light_color", "cb1[1].xyzw", 16, EffectParameterClass.Vector, EffectParameterType.Single, 1, 4, 0), new ShaderParameter("top_color", "cb1[2].xyzw", 32, EffectParameterClass.Vector, EffectParameterType.Single, 1, 4, 0), new ShaderParameter("bottom_color", "cb1[3].xyzw", 48, EffectParameterClass.Vector, EffectParameterType.Single, 1, 4, 0), new ShaderParameter("min_y", "cb1[4].x", 64, EffectParameterClass.Scalar, EffectParameterType.Single, 1, 1, 0), new ShaderParameter("max_y", "cb1[4].y", 68, EffectParameterClass.Scalar, EffectParameterType.Single, 1, 1, 0), } }, }, Samplers = new List() { new SamplerInfo() { Name = "src_tex_sampler_s", SamplerSlot = 0, TextureName = "src_tex", TextureSlot = 0, Type = SamplerType.Sampler2D, } }, }); nativeShaderDescriptions.Add("waterBack", new NativeShaderDesc { Name = "waterBack", OriginFileName = "g_module_water_back_ps", FileLength = 1536, Version = "KMST1186", Stage = ShaderStage.Pixel, ConstantBuffers = new List() { new ConstantBuffer { Name = "_2", Slot = 2, SizeInBytes = 176, Parameters = new List() { new ShaderParameter("min_y", null, 0, EffectParameterClass.Scalar, EffectParameterType.Single, 1, 1, 0), new ShaderParameter("max_y", null, 4, EffectParameterClass.Scalar, EffectParameterType.Single, 1, 1, 0), new ShaderParameter("color0", null, 16, EffectParameterClass.Vector, EffectParameterType.Single, 1, 3, 0), new ShaderParameter("level0", null, 28, EffectParameterClass.Scalar, EffectParameterType.Single, 1, 1, 0), new ShaderParameter("color1", null, 32, EffectParameterClass.Vector, EffectParameterType.Single, 1, 3, 0), new ShaderParameter("level1", null, 44, EffectParameterClass.Scalar, EffectParameterType.Single, 1, 1, 0), new ShaderParameter("color2", null, 48, EffectParameterClass.Vector, EffectParameterType.Single, 1, 3, 0), new ShaderParameter("level2", null, 60, EffectParameterClass.Scalar, EffectParameterType.Single, 1, 1, 0), new ShaderParameter("color3", null, 64, EffectParameterClass.Vector, EffectParameterType.Single, 1, 3, 0), new ShaderParameter("level3", null, 76, EffectParameterClass.Scalar, EffectParameterType.Single, 1, 1, 0), new ShaderParameter("color4", null, 80, EffectParameterClass.Vector, EffectParameterType.Single, 1, 3, 0), new ShaderParameter("level4", null, 92, EffectParameterClass.Scalar, EffectParameterType.Single, 1, 1, 0), new ShaderParameter("color5", null, 96, EffectParameterClass.Vector, EffectParameterType.Single, 1, 3, 0), new ShaderParameter("level5", null, 108, EffectParameterClass.Scalar, EffectParameterType.Single, 1, 1, 0), new ShaderParameter("color6", null, 112, EffectParameterClass.Vector, EffectParameterType.Single, 1, 3, 0), new ShaderParameter("level6", null, 124, EffectParameterClass.Scalar, EffectParameterType.Single, 1, 1, 0), new ShaderParameter("color7", null, 128, EffectParameterClass.Vector, EffectParameterType.Single, 1, 3, 0), new ShaderParameter("level7", null, 140, EffectParameterClass.Scalar, EffectParameterType.Single, 1, 1, 0), new ShaderParameter("color8", null, 144, EffectParameterClass.Vector, EffectParameterType.Single, 1, 3, 0), new ShaderParameter("level8", null, 156, EffectParameterClass.Scalar, EffectParameterType.Single, 1, 1, 0), new ShaderParameter("color9", null, 160, EffectParameterClass.Vector, EffectParameterType.Single, 1, 3, 0), new ShaderParameter("level9", null, 172, EffectParameterClass.Scalar, EffectParameterType.Single, 1, 1, 0), } } } }); nativeShaderDescriptions.Add("waterFront", new NativeShaderDesc { Name = "waterFront", OriginFileName = "g_module_water_front_ps", FileLength = 3872, Version = "KMST1186", Stage = ShaderStage.Pixel, ConstantBuffers = new List() { new ConstantBuffer() { Name = "_0", Slot = 0, SizeInBytes = 144, Parameters = new List() { new ShaderParameter("vp", null, 0, EffectParameterClass.Matrix, EffectParameterType.Single, 4, 4, 0), new ShaderParameter("vp_inv", null, 64, EffectParameterClass.Matrix, EffectParameterType.Single, 4, 4, 0), new ShaderParameter("resolution_time", null, 128, EffectParameterClass.Vector, EffectParameterType.Single, 1, 4, 0), } }, new ConstantBuffer() { Name = "_2", Slot = 2, SizeInBytes = 112, Parameters = new List() { new ShaderParameter("value", null, 0, EffectParameterClass.Vector, EffectParameterType.Single, 1, 2, 0), new ShaderParameter("cgrad", null, 8, EffectParameterClass.Vector, EffectParameterType.Single, 1, 2, 0), new ShaderParameter("octave", null, 16, EffectParameterClass.Vector, EffectParameterType.Single, 1, 2, 0), new ShaderParameter("strength", null, 24, EffectParameterClass.Scalar, EffectParameterType.Single, 1, 1, 0), new ShaderParameter("edge", null, 28, EffectParameterClass.Scalar, EffectParameterType.Single, 1, 1, 0), new ShaderParameter("godray_min_y", null, 32, EffectParameterClass.Scalar, EffectParameterType.Single, 1, 1, 0), new ShaderParameter("godray_max_y", null, 36, EffectParameterClass.Scalar, EffectParameterType.Single, 1, 1, 0), new ShaderParameter("godray_move", null, 40, EffectParameterClass.Scalar, EffectParameterType.Single, 1, 1, 0), new ShaderParameter("godray_bright", null, 44, EffectParameterClass.Scalar, EffectParameterType.Single, 1, 1, 0), new ShaderParameter("godray_color", null, 48, EffectParameterClass.Vector, EffectParameterType.Single, 1, 3, 0), new ShaderParameter("godray_uv_rot", null, 60, EffectParameterClass.Scalar, EffectParameterType.Single, 1, 1, 0), new ShaderParameter("godray_uv_scale", null, 64, EffectParameterClass.Vector, EffectParameterType.Single, 1, 2, 0), new ShaderParameter("aberration", null, 72, EffectParameterClass.Scalar, EffectParameterType.Single, 1, 1, 0), new ShaderParameter("dist_strength", null, 76, EffectParameterClass.Scalar, EffectParameterType.Single, 1, 1, 0), new ShaderParameter("diffuse", null, 80, EffectParameterClass.Vector, EffectParameterType.Single, 1, 3, 0), new ShaderParameter("screen_min_y", null, 92, EffectParameterClass.Scalar, EffectParameterType.Single, 1, 1, 0), new ShaderParameter("screen_max_y", null, 96, EffectParameterClass.Scalar, EffectParameterType.Single, 1, 1, 0), new ShaderParameter("screen_color", null, 100, EffectParameterClass.Vector, EffectParameterType.Single, 1, 3, 0), }, }, new ConstantBuffer() { Name = "_4", Slot = 4, SizeInBytes = 96, Parameters = new List() { new ShaderParameter("player_pos", "cb1[0].xy", 0, EffectParameterClass.Vector, EffectParameterType.Single, 1, 2, 0), new ShaderParameter("distance_factor1", "cb1[1].x", 16, EffectParameterClass.Scalar, EffectParameterType.Single, 1, 1, 0), new ShaderParameter("min_y", "cb1[4].x", 64, EffectParameterClass.Scalar, EffectParameterType.Single, 1, 1, 0), new ShaderParameter("max_y", "cb1[4].y", 68, EffectParameterClass.Scalar, EffectParameterType.Single, 1, 1, 0), new ShaderParameter("dist_center_pos", "cb1[4].zw", 72, EffectParameterClass.Vector, EffectParameterType.Single, 1, 2, 0), new ShaderParameter("distance_factor2", "cb1[5].x", 80, EffectParameterClass.Scalar, EffectParameterType.Single, 1, 1, 0), } }, }, Samplers = new List() { new SamplerInfo() { Name = "noise_tex_sampler_s", SamplerSlot = 1, TextureName = "noise_tex", TextureSlot = 1, Type = SamplerType.Sampler2D, }, new SamplerInfo() { Name = "godray_noise_tex_sampler_s", SamplerSlot = 2, TextureName = "godray_noise_tex", TextureSlot = 2, Type = SamplerType.Sampler2D, }, new SamplerInfo() { Name = "bg_tex_sampler_s", SamplerSlot = 3, TextureName = "bg_tex", TextureSlot = 3, Type = SamplerType.Sampler2D, } }, }); nativeShaderDescriptions.Add("vs_position_color_texture", new NativeShaderDesc { Name = "vs_position_color_texture", OriginFileName = "off_42b030", FileLength = 1668, Version = "KMST1186", Stage = ShaderStage.Vertex, ConstantBuffers = new List() { new ConstantBuffer() { Name = "_0", Slot = 0, SizeInBytes = 144, Parameters = new List() { new ShaderParameter("vp", null, 0, EffectParameterClass.Matrix, EffectParameterType.Single, 4, 4, 0), new ShaderParameter("vp_inv", null, 64, EffectParameterClass.Matrix, EffectParameterType.Single, 4, 4, 0), new ShaderParameter("resolution_time", null, 128, EffectParameterClass.Vector, EffectParameterType.Single, 1, 4, 0), } }, new ConstantBuffer() { Name = "_1", Slot = 1, SizeInBytes = 64, Parameters = new List() { new ShaderParameter("world", null, 0, EffectParameterClass.Matrix, EffectParameterType.Single, 4, 4, 0), } }, }, }); } private static Dictionary nativeShaderDescriptions; private static Dictionary nativeShaderMgfxCache; public static ReadOnlySpan GetNativeShaderEffectBytes(string shaderName) { return GetNativeShaderEffectBytesInternal(shaderName); } public static Effect CreateNativeShader(GraphicsDevice graphicsDevice, string shaderName) { return new Effect(graphicsDevice, GetNativeShaderEffectBytesInternal(shaderName)); } private static byte[] GetNativeShaderEffectBytesInternal(string shaderName) { if (!nativeShaderMgfxCache.TryGetValue(shaderName, out byte[] effectFile)) { effectFile = CompileNativeShader(shaderName); nativeShaderMgfxCache.Add(shaderName, effectFile); } return effectFile; } private static byte[] CompileNativeShader(string shaderName) { if (!nativeShaderDescriptions.TryGetValue(shaderName, out var nativeShaderDesc)) { throw new ArgumentException($"Can't find shader description of '{shaderName}'.", nameof(shaderName)); } var asm = Assembly.GetAssembly(typeof(EffectResources)); byte[] shaderByteCode; using (var input = asm.GetManifestResourceStream($"WzComparerR2.MapRender.Effects.Resources.Native.{shaderName}")) { shaderByteCode = new byte[input.Length]; input.Read(shaderByteCode, 0, shaderByteCode.Length); } var effectFile = ShaderConverter.D3DShaderByteCodeToMgfx(shaderByteCode, nativeShaderDesc.Stage, nativeShaderDesc.ConstantBuffers, nativeShaderDesc.Samplers); return effectFile; } } } ================================================ FILE: WzComparerR2.MapRender/Effects/NativeShaderDesc.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using WzComparerR2.Rendering.EffectCompiler; namespace WzComparerR2.MapRender.Effects { public class NativeShaderDesc { public string Name { get; set; } public string OriginFileName { get; set; } public int FileLength { get; set; } public string Version { get; set; } public ShaderStage Stage { get; set; } public List ConstantBuffers { get; set; } public List Samplers { get; set; } } } ================================================ FILE: WzComparerR2.MapRender/Effects/ShaderMaterials.cs ================================================ using System; using System.Linq; using System.Reflection; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; namespace WzComparerR2.MapRender.Effects { public static class ShaderMaterialFactory { public static ShaderMaterial Create(MsCustomSpriteData msSprite) { var shaderMaterial = Create(msSprite.Shader.ID); shaderMaterial.LoadFromMsSprite(msSprite); return shaderMaterial; } public static ShaderMaterial Create(string shaderID) { return shaderID switch { "light" => new LightPixelShaderMaterial(), "waterBack" => new WaterBackPixelShaderMaterial(), "waterFront" => new WaterFrontPixelShaderMaterial(), _ => throw new Exception($"Unsupported shader: {shaderID}"), }; } } public abstract class ShaderMaterial : IEquatable { public ShaderMaterial(string shaderID) { this.ShaderID = shaderID; } public string ShaderID { get; protected set; } public abstract void ApplyParameters(Effect effect); public virtual void ApplySamplerStates(GraphicsDevice graphicsDevice) { } public virtual void LoadFromMsSprite(MsCustomSpriteData msSprite) { } public virtual bool Equals(ShaderMaterial other) { // TODO: check if all exported shader parameters are identical. return Object.ReferenceEquals(this, other); } protected void LoadBindingParameters(MsCustomSpriteData msCustomSprite) { foreach (var propInfo in this.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public)) { var bindingAttr = propInfo.GetCustomAttribute(); if (bindingAttr != null) { if (bindingAttr.TextureIndex > -1 && bindingAttr.TextureIndex < msCustomSprite.Textures.Length) { var msCustomTexture = msCustomSprite.Textures[bindingAttr.TextureIndex]; if (propInfo.PropertyType == typeof(Texture2D)) { propInfo.SetValue(this, msCustomTexture.Texture); } else if (propInfo.PropertyType == typeof(SamplerState)) { var samplerState = new SamplerState(); samplerState.AddressU = msCustomTexture.AddressU switch { 1 => TextureAddressMode.Wrap, _ => TextureAddressMode.Clamp, }; samplerState.AddressV = msCustomTexture.AddressV switch { 1 => TextureAddressMode.Wrap, _ => TextureAddressMode.Clamp, }; propInfo.SetValue(this, samplerState); } } else if (msCustomSprite.Shader.Constants.TryGetValue(bindingAttr.ParameterName, out var shaderConstant)) { if (propInfo.PropertyType == typeof(float)) { propInfo.SetValue(this, shaderConstant.ToScalar()); } else if (propInfo.PropertyType == typeof(Vector2)) { propInfo.SetValue(this, shaderConstant.ToVector2()); } else if (propInfo.PropertyType == typeof(Vector3)) { propInfo.SetValue(this, shaderConstant.ToVector3()); } } } } } protected void ApplyBindingParameters(Effect effect) { foreach (var propInfo in this.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public)) { var bindingAttr = propInfo.GetCustomAttribute(); if (bindingAttr != null && !string.IsNullOrEmpty(bindingAttr.ParameterName)) { switch (propInfo.GetValue(this)) { case float _float: effect.Parameters[bindingAttr.ParameterName].SetValue(_float); break; case Vector2 vec2: effect.Parameters[bindingAttr.ParameterName].SetValue(vec2); break; case Vector3 vec3: effect.Parameters[bindingAttr.ParameterName].SetValue(vec3); break; case Vector4 vec4: effect.Parameters[bindingAttr.ParameterName].SetValue(vec4); break; case Matrix matrix: effect.Parameters[bindingAttr.ParameterName].SetValue(matrix); break; case Texture tex: effect.Parameters[bindingAttr.ParameterName].SetValue(tex); break; } } } } protected void ApplyBindingSamplers(GraphicsDevice graphicsDevice) { foreach (var propInfo in this.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public)) { var bindingAttr = propInfo.GetCustomAttribute(); if (bindingAttr != null && bindingAttr.TextureIndex > -1) { switch (propInfo.GetValue(this)) { case SamplerState samplerState: graphicsDevice.SamplerStates[bindingAttr.TextureIndex] = samplerState; break; } } } } } internal class ShaderParameterBindingAttribute : Attribute { public ShaderParameterBindingAttribute(string parameterName, int textureIndex = -1) { this.ParameterName = parameterName; this.TextureIndex = textureIndex; } public string ParameterName { get; protected set; } public int TextureIndex { get; protected set; } } public interface IMaplestoryEffectMatrices { Matrix ViewProjection { get; set; } Matrix ViewProjectionInverse { get; set; } Vector4 ResolutionTime { get; set; } } public interface IBackgroundCaptureEffect { Texture2D BackgroundTexture { get; set; } } public class LightPixelShaderMaterial : ShaderMaterial { public LightPixelShaderMaterial() : base("light") { } [ShaderParameterBinding("z")] public float Z { get; set; } [ShaderParameterBinding("src_tex", 0)] public Texture2D SrcTexture { get; set; } [ShaderParameterBinding(null, 0)] public SamplerState SrcTextureSamplerState { get; set; } [ShaderParameterBinding("player_pos")] public Vector2 PlayerPos { get; set; } [ShaderParameterBinding("light_inner_radius")] public float LightInnerRadius { get; set; } [ShaderParameterBinding("light_outer_radius")] public float LightOuterRadius { get; set; } [ShaderParameterBinding("player_light_color")] public Vector4 PlayerLightColor { get; set; } [ShaderParameterBinding("top_color")] public Vector4 TopColor { get; set; } [ShaderParameterBinding("bottom_color")] public Vector4 BottomColor { get; set; } [ShaderParameterBinding("min_y")] public float MinY { get; set; } [ShaderParameterBinding("max_y")] public float MaxY { get; set; } public override void LoadFromMsSprite(MsCustomSpriteData sprite) { base.LoadBindingParameters(sprite); } public override void ApplyParameters(Effect effect) { base.ApplyBindingParameters(effect); } public override void ApplySamplerStates(GraphicsDevice graphicsDevice) { base.ApplyBindingSamplers(graphicsDevice); } } public class WaterBackPixelShaderMaterial : ShaderMaterial { public WaterBackPixelShaderMaterial() : base("waterBack") { } [ShaderParameterBinding("min_y")] public float MinY { get; set; } [ShaderParameterBinding("max_y")] public float MaxY { get; set; } [ShaderParameterBinding("color0")] public Vector3 Color0 { get; set; } [ShaderParameterBinding("level0")] public float Level0 { get; set; } [ShaderParameterBinding("color1")] public Vector3 Color1 { get; set; } [ShaderParameterBinding("level1")] public float Level1 { get; set; } [ShaderParameterBinding("color2")] public Vector3 Color2 { get; set; } [ShaderParameterBinding("level2")] public float Level2 { get; set; } [ShaderParameterBinding("color3")] public Vector3 Color3 { get; set; } [ShaderParameterBinding("level3")] public float Level3 { get; set; } [ShaderParameterBinding("color4")] public Vector3 Color4 { get; set; } [ShaderParameterBinding("level4")] public float Level4 { get; set; } [ShaderParameterBinding("color5")] public Vector3 Color5 { get; set; } [ShaderParameterBinding("level5")] public float Level5 { get; set; } [ShaderParameterBinding("color6")] public Vector3 Color6 { get; set; } [ShaderParameterBinding("level6")] public float Level6 { get; set; } [ShaderParameterBinding("color7")] public Vector3 Color7 { get; set; } [ShaderParameterBinding("level7")] public float Level7 { get; set; } [ShaderParameterBinding("color8")] public Vector3 Color8 { get; set; } [ShaderParameterBinding("level8")] public float Level8 { get; set; } [ShaderParameterBinding("color9")] public Vector3 Color9 { get; set; } [ShaderParameterBinding("level9")] public float Level9 { get; set; } public override void LoadFromMsSprite(MsCustomSpriteData sprite) { base.LoadBindingParameters(sprite); } public override void ApplyParameters(Effect effect) { base.ApplyBindingParameters(effect); } } public class WaterFrontPixelShaderMaterial : ShaderMaterial, IMaplestoryEffectMatrices, IBackgroundCaptureEffect { public WaterFrontPixelShaderMaterial() : base("waterFront") { } [ShaderParameterBinding("vp")] public Matrix ViewProjection { get; set; } [ShaderParameterBinding("vp_inv")] public Matrix ViewProjectionInverse { get; set; } [ShaderParameterBinding("resolution_time")] public Vector4 ResolutionTime { get; set; } [ShaderParameterBinding("value")] public Vector2 Value { get; set; } [ShaderParameterBinding("cgrad")] public Vector2 Cgrad { get; set; } [ShaderParameterBinding("octave")] public Vector2 Octave { get; set; } [ShaderParameterBinding("strength")] public float Strength { get; set; } [ShaderParameterBinding("edge")] public float Edge { get; set; } [ShaderParameterBinding("godray_min_y")] public float GodrayMinY { get; set; } [ShaderParameterBinding("godray_max_y")] public float GodrayMaxY { get; set; } [ShaderParameterBinding("godray_move")] public float GodrayMove { get; set; } [ShaderParameterBinding("godray_bright")] public float GodrayBright { get; set; } [ShaderParameterBinding("godray_color")] public Vector3 GodrayColor { get; set; } [ShaderParameterBinding("godray_uv_rot")] public float GodrayUVRot { get; set; } [ShaderParameterBinding("godray_uv_scale")] public Vector2 GodrayUVScale { get; set; } [ShaderParameterBinding("aberration")] public float Aberration { get; set; } [ShaderParameterBinding("dist_strength")] public float DistStrength { get; set; } [ShaderParameterBinding("diffuse")] public Vector3 Diffuse { get; set; } [ShaderParameterBinding("screen_min_y")] public float ScreenMinY { get; set; } [ShaderParameterBinding("screen_max_y")] public float ScreenMaxY { get; set; } [ShaderParameterBinding("screen_color")] public Vector3 ScreenColor { get; set; } [ShaderParameterBinding("noise_tex", 1)] public Texture2D NoiseTexture { get; set; } [ShaderParameterBinding(null, 1)] public SamplerState NoiseTextureSamplerState { get; set; } [ShaderParameterBinding("godray_noise_tex", 2)] public Texture2D GodrayNoiseTexture { get; set; } [ShaderParameterBinding(null, 2)] public SamplerState GodrayNoiseTextureSamplerState { get; set; } [ShaderParameterBinding("bg_tex")] public Texture2D BackgroundTexture { get; set; } [ShaderParameterBinding("player_pos")] public Vector2 PlayerPos { get; set; } [ShaderParameterBinding("distance_factor1")] public float Factor1 { get; set; } [ShaderParameterBinding("min_y")] public float MinY { get; set; } [ShaderParameterBinding("max_y")] public float MaxY { get; set; } [ShaderParameterBinding("dist_center_pos")] public Vector2 DistNoiseCenterPos { get; set; } [ShaderParameterBinding("distance_factor2")] public float Factor2 { get; set; } public override void LoadFromMsSprite(MsCustomSpriteData sprite) { base.LoadBindingParameters(sprite); } public override void ApplyParameters(Effect effect) { base.ApplyBindingParameters(effect); } public override void ApplySamplerStates(GraphicsDevice graphicsDevice) { base.ApplyBindingSamplers(graphicsDevice); } } } ================================================ FILE: WzComparerR2.MapRender/Entry.cs ================================================ using System; using System.Collections.Generic; using System.Text; using WzComparerR2.WzLib; using WzComparerR2.Common; using WzComparerR2.Config; using WzComparerR2.PluginBase; using DevComponents.DotNetBar; using System.Threading; using System.Windows.Forms; using Game = Microsoft.Xna.Framework.Game; namespace WzComparerR2.MapRender { public class Entry : PluginEntry { public Entry(PluginContext context) : base(context) { } #if MapRenderV1 private RibbonBar bar; private ButtonItem btnItemMapRender; private FrmMapRender mapRenderGame1; #endif private RibbonBar bar2; private ButtonItem btnItemMapRenderV2; private FrmMapRender2 mapRenderGame2; protected override void OnLoad() { #if MapRenderV1 this.bar = Context.AddRibbonBar("Modules", "MapRender"); btnItemMapRender = new ButtonItem("", "MapRender"); btnItemMapRender.Click += btnItem_Click; bar.Items.Add(btnItemMapRender); #endif this.bar2 = Context.AddRibbonBar("Modules", "MapRender2"); btnItemMapRenderV2 = new ButtonItem("", "MapRenderV2"); btnItemMapRenderV2.Click += btnItem_Click; bar2.Items.Add(btnItemMapRenderV2); ConfigManager.RegisterAllSection(this.GetType().Assembly); } void btnItem_Click(object sender, EventArgs e) { Wz_Node node = Context.SelectedNode1; if (node != null) { Wz_Image img = node.Value as Wz_Image; Wz_File wzFile = node.GetNodeWzFile(); if (img != null && img.TryExtract()) { if (wzFile == null || wzFile.Type != Wz_Type.Map) { if (MessageBoxEx.Show("所选Img不属于Map.wz,是否继续?", "提示", MessageBoxButtons.OKCancel) != DialogResult.OK) { goto exit; } } StringLinker sl = this.Context.DefaultStringLinker; if (!sl.HasValues) //生成默认stringLinker { sl = new StringLinker(); sl.Load(PluginManager.FindWz(Wz_Type.String).GetValueEx(null)); } //开始绘制 Thread thread = new Thread(() => { #if !DEBUG try { #endif #if MapRenderV1 if (sender == btnItemMapRender) { if (this.mapRenderGame1 != null) { return; } this.mapRenderGame1 = new FrmMapRender(img) { StringLinker = sl }; try { using (this.mapRenderGame1) { this.mapRenderGame1.Run(); } } finally { this.mapRenderGame1 = null; } } else #endif { if (this.mapRenderGame2 != null) { // post message to the opening game. this.mapRenderGame2.LoadMap(img); return; } else { this.mapRenderGame2 = new FrmMapRender2() { StringLinker = sl }; this.mapRenderGame2.Window.Title = "MapRender " + this.Version; this.mapRenderGame2.LoadMap(img); try { using (this.mapRenderGame2) { this.mapRenderGame2.Run(); } } finally { this.mapRenderGame2 = null; } } } #if !DEBUG } catch (Exception ex) { PluginManager.LogError("MapRender", ex, "MapRender error:"); MessageBoxEx.Show(ex.ToString(), "MapRender"); } #endif }); thread.SetApartmentState(ApartmentState.STA); thread.IsBackground = true; thread.Start(); goto exit; } } MessageBoxEx.Show("没有选择一个map的img", "MapRender"); exit: return; } } } ================================================ FILE: WzComparerR2.MapRender/FpsCounter.cs ================================================ using System; using System.Collections.Generic; using System.Text; using System.Diagnostics; using Microsoft.Xna.Framework; namespace WzComparerR2.MapRender { public class FpsCounter : DrawableGameComponent { public FpsCounter(Game game) : base(game) { this.CountInterval = TimeSpan.FromMilliseconds(1000); this.swUpdate = new Stopwatch(); this.swDraw = new Stopwatch(); } public TimeSpan CountInterval { get; set; } public double UpdatePerSec { get; private set; } public double DrawPerSec { get; private set; } public bool UseStopwatch { get; set; } private TimeSpan lastUpdate; private int updateCount; private Stopwatch swUpdate; private TimeSpan lastDraw; private int drawCount; private Stopwatch swDraw; public override void Update(GameTime gameTime) { this.updateCount++; if (UseStopwatch) { if (!this.swUpdate.IsRunning) { this.swUpdate.Start(); return; } TimeSpan totalElapsed = this.swUpdate.Elapsed; if (totalElapsed >= CountInterval) { this.swUpdate.Reset(); this.UpdatePerSec = this.updateCount / totalElapsed.TotalSeconds; this.updateCount = 0; this.swUpdate.Start(); } } else { TimeSpan totalElapsed = gameTime.TotalGameTime - lastUpdate; if (totalElapsed >= this.CountInterval) { this.UpdatePerSec = this.updateCount / totalElapsed.TotalSeconds; this.updateCount = 0; this.lastUpdate = gameTime.TotalGameTime; } } } public override void Draw(GameTime gameTime) { this.drawCount++; if (UseStopwatch) { if (!this.swDraw.IsRunning) { this.swDraw.Start(); return; } TimeSpan totalElapsed = this.swDraw.Elapsed; if (totalElapsed >= CountInterval) { this.swDraw.Reset(); this.DrawPerSec = this.drawCount / totalElapsed.TotalSeconds; this.drawCount = 0; this.swDraw.Start(); } } else { TimeSpan totalElapsed = gameTime.TotalGameTime - lastDraw; if (totalElapsed >= this.CountInterval) { this.DrawPerSec = this.drawCount / totalElapsed.TotalSeconds; this.drawCount = 0; this.lastDraw = gameTime.TotalGameTime; } } } } } ================================================ FILE: WzComparerR2.MapRender/FrmMapRender.cs ================================================ #if MapRenderV1 using System; using System.Collections.Generic; using System.Text; using System.Runtime.InteropServices; using WzComparerR2.WzLib; using WzComparerR2.PluginBase; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; using System.IO; using System.IO.Compression; using System.Threading; using Form = System.Windows.Forms.Form; using Un4seen.Bass; using WzComparerR2.MapRender.UI; //using JLChnToZ.IMEHelper; using WzComparerR2.MapRender.Patches; using WzComparerR2.Common; using WzComparerR2.Rendering; namespace WzComparerR2.MapRender { public class FrmMapRender : Game { public FrmMapRender() { graphics = new GraphicsDeviceManager(this); this.loadState = LoadState.NotLoad; this.IsMouseVisible = true; this.renderingList = new List(); this.willReloadBgm = true; this.profile = new StringBuilder(); this.showProfile = true; this.fpsCounter = new FpsCounter(this); this.fpsCounter.UseStopwatch = true; this.cameraInMap = true; this.patchVisibility = new PatchVisibility(); this.patchVisibility.FootHoldVisible = false; this.patchVisibility.LadderRopeVisible = false; this.volumeGlobal = 100; Form windowForm = (Form)Form.FromHandle(this.Window.Handle); windowForm.GotFocus += windowForm_GotFocus; windowForm.LostFocus += windowForm_LostFocus; GameExt.FixKeyboard(this); /* this.chat = new Chat(); this.chat.Connected += chat_Connected; this.chat.Error += chat_Error; this.chat.MessageReceived += chat_MessageReceived;*/ } void chat_Connected(object sender, EventArgs e) { if (this.txtChat.Length > 0) { this.txtChat.AppendLine(); } this.txtChat.Append("已经连接服务器"); } protected override void OnActivated(object sender, EventArgs args) { base.OnActivated(sender, args); GameExt.FixKeyboard(this); } protected override void OnDeactivated(object sender, EventArgs args) { base.OnDeactivated(sender, args); } /* void chat_Error(object sender, ChatErrorEventArgs e) { if (this.txtChat.Length > 0) { this.txtChat.AppendLine(); } this.txtChat.Append("错误:" + e.Error); } void chat_MessageReceived(object sender, ChatMessageEventArgs e) { if (this.txtChat.Length > 0) { this.txtChat.AppendLine(); } this.txtChat.AppendFormat("[{0}] {1}", e.FromName ?? "系统公告", e.MessageText); }*/ public FrmMapRender(Wz_Image mapImg) : this() { this.mapImg = mapImg; } RenderEnv renderEnv; Tooltip tooltip; GraphicsDeviceManager graphics; LoadState loadState; TimeSpan stateChangedTime; const int EnteringTime = 1500; const int ExitingTime = 1500; //双缓冲用... RenderTarget2D target2D; //资源... Wz_Image mapImg; int mapID; string mapMark; MiniMap miniMap; List renderingList; StringLinker stringLinker; TextureLoader texLoader; PatchVisibility patchVisibility; //bgm用 IntPtr bgmStream; byte[] bgmFile; string bgmName; float bgmVolume; int volumeGlobal; //0-100 bool isSilent; bool silentOnDeactive; //和地图切换相关的参数... bool willReloadBgm; string enterPortal; //截图用 bool prepareCapture; bool snapshotSaving; //调试用 StringBuilder profile; bool showProfile; FpsCounter fpsCounter; bool cameraInMap; //ui UIMiniMap uiMinimap; //输入 //IMEHandler ime; StringBuilder txtInput = new StringBuilder(); bool isOnCommand; //Chat chat; StringBuilder txtChat = new StringBuilder(); public StringLinker StringLinker { get { return stringLinker; } set { stringLinker = value; } } protected override void Initialize() { base.Initialize(); //this.ime = new IMEHandler(this, true); //this.ime.onResultReceived += ime_onResultReceived; ChangeDisplayMode(0); //ThreadPool.QueueUserWorkItem((o) => this.chat.Connect()); } /* void ime_onResultReceived(object sender, IMEResultEventArgs e) { if (isOnCommand) { switch (e.result) { case '\b': if (txtInput.Length > 0) { txtInput.Remove(txtInput.Length - 1, 1); } break; case '\x03': //复制 break; case '\x16': //粘贴 { string text = System.Windows.Forms.Clipboard.GetText(); if (text != null) { int idx = text.IndexOfAny("\r\n".ToCharArray()); if (idx > -1) { text = text.Substring(0, idx); } txtInput.Append(text); } } break; case '\r': //回车 if (this.chat.IsConnected) { string content = this.txtInput.ToString(); if (content.StartsWith("/setName ")) { ThreadPool.QueueUserWorkItem(o => this.chat.SetName(content.Substring(9))); } else { ThreadPool.QueueUserWorkItem(o => this.chat.Talk(content)); } this.txtInput.Remove(0, this.txtInput.Length); } this.ime.Enabled = false; break; default: txtInput.Append(e.result); break; } } }*/ protected override void LoadContent() { base.LoadContent(); this.renderEnv = new RenderEnv(this, this.graphics); this.tooltip = new Tooltip(this.GraphicsDevice); this.texLoader = new TextureLoader(this.GraphicsDevice); //初始化UI this.uiMinimap = new UIMiniMap(this.GraphicsDevice); this.uiMinimap.MapNameFont = this.renderEnv.Fonts.MapNameFont; } void windowForm_LostFocus(object sender, EventArgs e) { if (this.silentOnDeactive) { isSilent = true; this.UpdateBgmVolume(); } } void windowForm_GotFocus(object sender, EventArgs e) { if (this.silentOnDeactive) { isSilent = false; this.UpdateBgmVolume(); } } private void ChangeDisplayMode(int i) { this.renderEnv.Camera.DisplayMode = i; Form windowForm = (Form)Form.FromHandle(this.Window.Handle); switch (i) { default: case 0: case 1: case 2: windowForm.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; break; case 3: windowForm.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None; break; } windowForm.SetDesktopLocation(0, 0); } protected override void OnExiting(object sender, EventArgs args) { foreach (RenderPatch patch in this.renderingList) { patch.Dispose(); } this.renderingList.Clear(); Bass.BASS_StreamFree(this.bgmStream.ToInt32()); this.renderEnv.Dispose(); // this.ime.Dispose(); //this.chat.Disconnect(); } protected override void Update(GameTime gameTime) { this.fpsCounter.Update(gameTime); this.renderEnv.Input.Update(gameTime); LoadState oldState = this.loadState; switch (this.loadState) { case LoadState.NotLoad: if (this.mapImg != null) { Thread loadMapThread = new Thread(LoadMap); loadMapThread.Start(); this.loadState = LoadState.Loading; } break; case LoadState.Loading: // waiting... break; case LoadState.LoadSuccessed: if (willReloadBgm) { this.bgmVolume = 0f; UpdateBgmVolume(); } Bass.BASS_ChannelPlay(this.bgmStream.ToInt32(), false); //prepare camera { foreach (RenderPatch patch in this.renderingList) { PortalPatch portal; if (patch.ObjectType == RenderObjectType.Portal && (portal = patch as PortalPatch) != null && ((this.enterPortal == null) ? portal.PortalType == 0 : //sp portal.PortalName == this.enterPortal)) { this.renderEnv.Camera.Center = portal.Position; break; } } } this.loadState = LoadState.Entering; //prepare minimap this.uiMinimap.MiniMap = this.miniMap; StringResult sr; if (this.stringLinker != null && this.stringLinker.StringMap.TryGetValue(this.mapID, out sr)) { this.uiMinimap.MapName = sr["mapName"]; this.uiMinimap.StreetName = sr["streetName"]; } else { this.uiMinimap.MapName = null; this.uiMinimap.StreetName = null; } this.uiMinimap.UpdateSize(); //首次更新portal状态 UpdatePortalVisibility(); break; case LoadState.LoadFailed: // flag state break; case LoadState.Entering: UpdateCamera(gameTime); UpdatePatch(gameTime); { double time = (gameTime.TotalGameTime - stateChangedTime).TotalMilliseconds; if (willReloadBgm) { this.bgmVolume = (float)(time / EnteringTime); UpdateBgmVolume(); } if (time > EnteringTime) { this.loadState = LoadState.Rendering; } } break; case LoadState.Rendering: UpdateInput(gameTime); UpdateCamera(gameTime); UpdatePatch(gameTime); break; case LoadState.Exiting: UpdateInput(gameTime); UpdatePatch(gameTime); { double time = (gameTime.TotalGameTime - stateChangedTime).TotalMilliseconds; if (willReloadBgm) { this.bgmVolume = (float)(1 - time / ExitingTime); UpdateBgmVolume(); } if (time > ExitingTime) { if (willReloadBgm) { Bass.BASS_ChannelStop(this.bgmStream.ToInt32()); Bass.BASS_StreamFree(this.bgmStream.ToInt32()); } DisposePatch(); this.loadState = LoadState.NotLoad; } } break; } if (this.loadState != oldState) { stateChangedTime = gameTime.TotalGameTime; } base.Update(gameTime); } private void UpdateInput(GameTime gameTime) { if (!this.IsActive) return; InputState input = renderEnv.Input; if (!this.isOnCommand) { OnGlobalHotKey(gameTime); } else { //OnInputMethod(gameTime); } } /* private void OnInputMethod(GameTime gameTime) { InputState input = renderEnv.Input; if (!this.ime.Enabled) { this.isOnCommand = false; } }*/ private void OnGlobalHotKey(GameTime gameTime) { Camera camera = renderEnv.Camera; InputState input = renderEnv.Input; if (input.IsAltPressing && input.IsCtrlPressing && input.IsShiftPressing && input.IsKeyDown(Keys.Insert)) { this.isOnCommand = !this.isOnCommand; //this.ime.Enabled = this.isOnCommand; //开关输入法支持 } if (input.IsAltPressing && input.IsKeyDown(Keys.Enter)) { camera.DisplayMode = (camera.DisplayMode + 1) % 4; this.ChangeDisplayMode(camera.DisplayMode); } if (input.IsKeyDown(Keys.Scroll)) { if (!snapshotSaving) { this.renderEnv.Camera.UseWorldRect = true; prepareCapture = true; } } if (input.IsKeyDown(Keys.F1)) { this.SaveAllTexture(); } if (input.IsKeyDown(Keys.F5)) { this.showProfile = !this.showProfile; } //调整音量 if (input.IsKeyDown(Keys.OemPlus) || input.IsKeyDown(Keys.Add)) { if (input.IsCtrlPressing) //ctrl+加号 { this.silentOnDeactive = true; } else { this.volumeGlobal = Math.Max(0, Math.Min(this.volumeGlobal + 5, 100)); } this.UpdateBgmVolume(); } if (input.IsKeyDown(Keys.OemMinus) || input.IsKeyDown(Keys.Subtract)) { if (input.IsCtrlPressing) //ctrl+减号 { this.silentOnDeactive = false; } else { this.volumeGlobal = Math.Max(0, Math.Min(this.volumeGlobal - 5, 100)); } this.UpdateBgmVolume(); } if (input.IsKeyDown(Keys.M)) { this.uiMinimap.Visible = !this.uiMinimap.Visible; } if (input.IsCtrlPressing) { if (input.IsKeyDown(Keys.U)) //设置镜头限制 { this.cameraInMap = !this.cameraInMap; } if (input.IsKeyDown(Keys.D1)) { this.patchVisibility.BackVisible = !this.patchVisibility.BackVisible; } if (input.IsKeyDown(Keys.D2)) { this.patchVisibility.ReactorVisible = !this.patchVisibility.ReactorVisible; } if (input.IsKeyDown(Keys.D3)) { this.patchVisibility.ObjVisible = !this.patchVisibility.ObjVisible; } if (input.IsKeyDown(Keys.D4)) { this.patchVisibility.TileVisible = !this.patchVisibility.TileVisible; } if (input.IsKeyDown(Keys.D5)) { this.patchVisibility.NpcVisible = !this.patchVisibility.NpcVisible; } if (input.IsKeyDown(Keys.D6)) { this.patchVisibility.MobVisible = !this.patchVisibility.MobVisible; } if (input.IsKeyDown(Keys.D7)) { if (this.patchVisibility.FootHoldVisible) { this.patchVisibility.FootHoldVisible = false; this.patchVisibility.LadderRopeVisible = false; } else { this.patchVisibility.FootHoldVisible = true; this.patchVisibility.LadderRopeVisible = true; } } if (input.IsKeyDown(Keys.D8)) { if (!this.patchVisibility.PortalVisible) { this.patchVisibility.PortalVisible = true; this.patchVisibility.PortalInEditMode = false; } else if (!this.patchVisibility.PortalInEditMode) { this.patchVisibility.PortalInEditMode = true; } else { this.patchVisibility.PortalVisible = false; } UpdatePortalVisibility(); } if (input.IsKeyDown(Keys.D9)) { this.patchVisibility.FrontVisible = !this.patchVisibility.FrontVisible; } } } private void UpdatePortalVisibility() { PortalPatch portal; foreach (RenderPatch patch in this.renderingList) { portal = patch as PortalPatch; if (portal != null) { portal.EditMode = this.patchVisibility.PortalInEditMode; } } } private void UpdateCamera(GameTime gameTime) { if (!this.IsActive) { return; } InputState input = renderEnv.Input; int boost = 1; Vector2 offs = new Vector2(); if (input.IsKeyPressing(Keys.Left)) offs.X -= 5; if (input.IsKeyPressing(Keys.Right)) offs.X += 5; if (input.IsKeyPressing(Keys.Up)) offs.Y -= 5; if (input.IsKeyPressing(Keys.Down)) offs.Y += 5; if (input.IsMouseButtonPressing(MouseButton.RightButton)) { offs.X = 5f * input.MousePosition.X / this.renderEnv.Camera.Width - 2.5f; if (offs.X > 1.5f || offs.X < -1.5f) { offs.X = offs.X - 1.5f * Math.Sign(offs.X); } else { offs.X = 0; } offs.Y = 5f * input.MousePosition.Y / this.renderEnv.Camera.Height - 2.5f; if (offs.Y > 1.5f || offs.Y < -1.5f) { offs.Y = offs.Y - 1.5f * Math.Sign(offs.Y); } else { offs.Y = 0; } offs *= 10; } if (input.IsCtrlPressing) boost = 2; this.renderEnv.Camera.Center += offs * boost; if (this.cameraInMap) { this.renderEnv.Camera.AdjustToWorldRect(); } } private void UpdateBgmVolume() { float percent = this.bgmVolume; if (this.isSilent) { percent = 0; } else { percent *= 0.01f * volumeGlobal; } percent = MathHelper.Clamp(percent, 0, 1); Bass.BASS_ChannelSetAttribute(this.bgmStream.ToInt32(), BASSAttribute.BASS_ATTRIB_VOL, percent); } private void UpdatePatch(GameTime gameTime) { bool movingOnce = false; tooltip.TooltipTarget = null; foreach (RenderPatch patch in this.renderingList) { patch.Update(gameTime, renderEnv); bool mouseover = patch.RenderArgs.DisplayRectangle.Contains(renderEnv.Camera.CameraToWorld(renderEnv.Input.MousePosition)); if (mouseover) { tooltip.TooltipTarget = patch; } if (this.IsActive && !movingOnce && patch.ObjectType == RenderObjectType.Portal) { PortalPatch portal = patch as PortalPatch; if (mouseover && this.renderEnv.Input.IsMouseButtonDown(MouseButton.LeftButton)) { if (portal.ToMap == this.mapID) { PortalPatch portal2; foreach (RenderPatch patch2 in this.renderingList) { if (patch.ObjectType == RenderObjectType.Portal && (portal2 = patch2 as PortalPatch) != null && portal2.PortalName == portal.ToName) { this.renderEnv.Camera.Center = portal2.Position; movingOnce = true; break; } } } else { if (PreLoadNextMap(portal.ToMap)) { this.enterPortal = portal.ToName; this.loadState = LoadState.Exiting; } } } } } } private bool PreLoadNextMap(int mapID) { Wz_Image newMapImg = FindMapByID(mapID); if (newMapImg == null) return false; this.mapImg = newMapImg; string newBgm = newMapImg.Node.FindNodeByPath("info\\bgm").GetValueEx(null); this.willReloadBgm = (newBgm != this.bgmName); return true; } private void DisposePatch() { //foreach (RenderPatch patch in this.renderingList) //{ // patch.Dispose(); //} this.renderingList.Clear(); } protected override void Draw(GameTime gameTime) { this.fpsCounter.Draw(gameTime); Color bgColor = Color.Black; if (prepareCapture) { //检查显卡支持纹理大小 var maxTextureWidth = 4096; var maxTextureHeight = 4096; Rectangle oldRect = this.renderEnv.Camera.WorldRect; int width = Math.Min(oldRect.Width, maxTextureWidth); int height = Math.Min(oldRect.Height, maxTextureHeight); var target2d = new RenderTarget2D(this.GraphicsDevice, width, height, false, SurfaceFormat.Color, DepthFormat.Depth24); //计算一组截图区 int horizonBlock = (int)Math.Ceiling(1.0 * oldRect.Width / width); int verticalBlock = (int)Math.Ceiling(1.0 * oldRect.Height / height); Color[,][] picBlocks = new Color[horizonBlock, verticalBlock][]; for (int j = 0; j < verticalBlock; j++) { for (int i = 0; i < horizonBlock; i++) { //计算镜头区域 this.renderEnv.Camera.WorldRect = new Rectangle( oldRect.X + i * width, oldRect.Y + j * height, width, height); //绘制 GraphicsDevice.SetRenderTarget(target2d); GraphicsDevice.Clear(bgColor); this.renderEnv.Sprite.Begin(SpriteSortMode.Deferred, StateEx.NonPremultipled_Hidef()); //this.SetRenderState(); foreach (RenderPatch patch in this.renderingList) { if (this.patchVisibility.IsVisible(patch.ObjectType) && patch.RenderArgs.Visible) { patch.Draw(gameTime, renderEnv); } } this.renderEnv.Sprite.End(); GraphicsDevice.SetRenderTarget(null); //保存 Texture2D t2d = target2d; Color[] data = new Color[target2d.Width * target2d.Height]; t2d.GetData(data); picBlocks[i, j] = data; } } target2d.Dispose(); SaveTexture(picBlocks, oldRect.Width, oldRect.Height, target2d.Width, target2d.Height); //这帧就过去吧 阿门... GraphicsDevice.Clear(bgColor); this.renderEnv.Camera.WorldRect = oldRect; this.renderEnv.Camera.UseWorldRect = false; prepareCapture = false; } else { PresentationParameters pp = GraphicsDevice.PresentationParameters; if (this.target2D == null || this.target2D.Width != pp.BackBufferWidth || this.target2D.Height != pp.BackBufferHeight) { if (this.target2D != null) { this.target2D.Dispose(); } this.target2D = new RenderTarget2D(this.GraphicsDevice, pp.BackBufferWidth, pp.BackBufferHeight, false , SurfaceFormat.Color, DepthFormat.Depth24); } GraphicsDevice.SetRenderTarget(this.target2D); GraphicsDevice.Clear(bgColor); switch (this.loadState) { case LoadState.Entering: case LoadState.Rendering: case LoadState.Exiting: this.renderEnv.Sprite.Begin(SpriteSortMode.Deferred, StateEx.NonPremultipled_Hidef()); //this.SetRenderState(); foreach (RenderPatch patch in this.renderingList) { if (this.patchVisibility.IsVisible(patch.ObjectType) && patch.RenderArgs.Visible && !patch.RenderArgs.Culled) { patch.Draw(gameTime, renderEnv); tooltip.DrawNameTooltip(gameTime, renderEnv, patch, stringLinker); } } //绘制tooltip tooltip.DrawTooltip(gameTime, renderEnv, stringLinker); this.renderEnv.Sprite.End(); //渲染UI if (this.uiMinimap.Visible) { this.uiMinimap.Position = new Vector2(0, this.showProfile ? 16 : 0); this.uiMinimap.Draw(this.renderEnv, gameTime); } break; } GraphicsDevice.SetRenderTarget(null); GraphicsDevice.Clear(bgColor); float alpha; switch (this.loadState) { case LoadState.Entering: { double time = (gameTime.TotalGameTime - stateChangedTime).TotalMilliseconds; alpha = MathHelper.Clamp((float)(time / EnteringTime), 0, 1); } break; case LoadState.Exiting: { double time = (gameTime.TotalGameTime - stateChangedTime).TotalMilliseconds; alpha = MathHelper.Clamp((float)(1 - time / ExitingTime), 0, 1); } break; case LoadState.Rendering: alpha = 1f; break; default: alpha = 0f; break; } this.renderEnv.Sprite.Begin(SpriteSortMode.Deferred, StateEx.NonPremultipled_Hidef()); //this.SetRenderState(); this.renderEnv.Sprite.Draw(this.target2D, new Rectangle(0, 0, this.target2D.Width, this.target2D.Height), new Color(Color.White, alpha)); this.renderEnv.Sprite.End(); this.renderEnv.Sprite.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied); if (showProfile) { profile.Remove(0, profile.Length); profile.Append("[F5-Hide] "); //显示地图名字 if (!this.uiMinimap.Visible) { profile.Append("[").Append(this.mapID).Append(" "); StringResult sr; if (this.stringLinker != null && this.stringLinker.StringMap.TryGetValue(this.mapID, out sr)) profile.Append(sr.Name); else profile.Append("(null)"); profile.Append("] "); } //显示bgm名字 profile.Append("["); profile.Append(bgmName); profile.AppendFormat(" {0}%{1}", volumeGlobal, this.silentOnDeactive ? "A" : null); profile.Append("] "); //显示当前渲染状态机 profile.AppendFormat("[{0:p2} {1}] ", alpha, this.loadState); profile.AppendFormat("[fps u:{0:f2} d:{1:f2}] ", fpsCounter.UpdatePerSec, fpsCounter.DrawPerSec); //可见性: profile.Append(" ctrl+"); int[] array = new[] { 1, 2, 3, 4, 5, 6, 7, 9, 10 }; for (int i = 0; i < array.Length; i++) { RenderObjectType type = (RenderObjectType)array[i]; profile.Append(this.patchVisibility.IsVisible(type) ? "-" : (i + 1).ToString()); } this.renderEnv.Sprite.FillRectangle(new Rectangle(0, 0, renderEnv.Camera.Width, 16), new Color(Color.Black, 0.4f)); this.renderEnv.Sprite.DrawStringEx(this.renderEnv.Fonts.DefaultFont, profile, Vector2.Zero, Color.Cyan); } this.renderEnv.Sprite.End(); //绘制文本框 this.renderEnv.Sprite.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied); if (this.isOnCommand) { Rectangle rect = new Rectangle(0, this.renderEnv.Camera.Height - 16, renderEnv.Camera.Width, 16); this.renderEnv.Sprite.FillRectangle(rect, new Color(Color.Black, 0.8f)); this.renderEnv.Sprite.DrawRectangle(rect, Color.Gray); this.renderEnv.Sprite.DrawStringEx(this.renderEnv.Fonts.DefaultFont, txtInput, new Vector2(rect.Left + 2, rect.Top + 2), Color.White); } this.renderEnv.Sprite.End(); //绘制聊天栏 if (this.txtChat.Length > 0) { Rectangle rect = new Rectangle(0, this.renderEnv.Camera.Height - 16 - 150, renderEnv.Camera.Width / 2, 150); XnaFont font = this.renderEnv.Fonts.DefaultFont; Vector2 size = font.MeasureString(this.txtChat); Vector2 origin = Vector2.Zero; if (size.Y > rect.Height) { origin = new Vector2(0, rect.Height - size.Y); } this.GraphicsDevice.ScissorRectangle = rect; this.GraphicsDevice.RasterizerState = StateEx.Scissor(); this.renderEnv.Sprite.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied); this.renderEnv.Sprite.FillRectangle(rect, new Color(Color.Black, this.isOnCommand ? 0.8f : 0.5f)); this.renderEnv.Sprite.DrawRectangle(rect, Color.Gray); this.renderEnv.Sprite.DrawStringEx(font, txtChat, new Vector2(rect.X + origin.X, rect.Y + origin.Y), Color.White); this.renderEnv.Sprite.End(); this.GraphicsDevice.ScissorRectangle = Rectangle.Empty; this.GraphicsDevice.RasterizerState = RasterizerState.CullCounterClockwise; } } base.Draw(gameTime); } private void SaveTexture(Color[,][] picBlocks, int mapWidth, int mapHeight, int blockWidth, int blockHeight) { this.snapshotSaving = true; new Thread(() => { //透明处理 foreach (Color[] data in picBlocks) { for (int i = 0, j = data.Length; i < j; i++) { if (data[i].A < 255) { //data[i].R = (byte)((data[i].R * data[i].A + bgColor.R * (255 - data[i].A)) / 255); //data[i].G = (byte)((data[i].G * data[i].A + bgColor.G * (255 - data[i].A)) / 255); //data[i].B = (byte)((data[i].B * data[i].A + bgColor.B * (255 - data[i].A)) / 255); data[i].A = 255; } } } //组装 byte[] mapData = new byte[mapWidth * mapHeight * 4]; for (int j = 0; j < picBlocks.GetLength(1); j++) { for (int i = 0; i < picBlocks.GetLength(0); i++) { Color[] data = picBlocks[i, j]; IntPtr pData = Marshal.UnsafeAddrOfPinnedArrayElement(data, 0); Rectangle blockRect = new Rectangle(); blockRect.X = i * blockWidth; blockRect.Y = j * blockHeight; blockRect.Width = Math.Min(mapWidth - blockRect.X, blockWidth); blockRect.Height = Math.Min(mapHeight - blockRect.Y, blockHeight); int length = blockRect.Width * 4; if (blockRect.X == 0 && blockRect.Width == mapWidth) //整块复制 { int startIndex = mapWidth * 4 * blockRect.Y; Marshal.Copy(pData, mapData, startIndex, blockRect.Width * blockRect.Height * 4); } else //逐行扫描 { for (int y = blockRect.Top, y0 = blockRect.Bottom; y < y0; y++) { int startIndex = (y * mapWidth + blockRect.X) * 4; Marshal.Copy(pData, mapData, startIndex, length); pData = new IntPtr(pData.ToInt32() + blockWidth * 4); } } } } //反色 MonogameUtils.BgraToColor(mapData); try { System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap( mapWidth, mapHeight, mapWidth * 4, System.Drawing.Imaging.PixelFormat.Format32bppArgb, System.Runtime.InteropServices.Marshal.UnsafeAddrOfPinnedArrayElement(mapData, 0)); bitmap.Save(DateTime.Now.ToString("yyyyMMddHHmmssfff") + "_" + mapID.ToString("D9") + ".png", System.Drawing.Imaging.ImageFormat.Png); bitmap.Dispose(); } catch { } finally { this.snapshotSaving = false; } }).Start(); } private void SaveAllTexture() { DirectoryInfo dir = Directory.CreateDirectory(DateTime.Now.ToString("yyyyMMddHHmmssfff")); foreach (RenderPatch patch in this.renderingList) { string path = Path.Combine(dir.FullName, patch.Name + ".png"); if (patch.Frames != null) { var texture = patch.Frames[0].Texture; using (var file = File.OpenWrite(path)) { texture.SaveAsPng(file); } } } } private void LoadMap() { System.Diagnostics.Stopwatch sw = System.Diagnostics.Stopwatch.StartNew(); this.texLoader.BeginCounting(); try { this.LoadInfo(); this.LoadBack(); this.LoadObjTile(); this.LoadFoothold(); this.LoadLife(); this.LoadPortal(); this.LoadReactor(); this.LoadTooltip(); this.LoadLadderRope(); this.LoadMinimap(); this.CalcMapSize(); this.renderingList.Sort(new Comparison(RenderPatchComarison)); this.loadState = LoadState.LoadSuccessed; } catch (Exception ex) { this.loadState = LoadState.LoadFailed; System.Windows.Forms.MessageBox.Show(ex.ToString(), "MapRender"); } this.texLoader.EndCounting(); this.texLoader.ClearUnusedTexture(); sw.Stop(); double ms = sw.Elapsed.TotalMilliseconds; StringResult sr; if (this.stringLinker != null && this.stringLinker.StringMap.TryGetValue(this.mapID, out sr)) { this.Window.Title = this.mapID + " " + sr.Name; } else { this.Window.Title = this.mapID + " (null)"; } } private int RenderPatchComarison(RenderPatch a, RenderPatch b) { for (int i = 0; i < a.ZIndex.Length; i++) { int dz = a.ZIndex[i].CompareTo(b.ZIndex[i]); if (dz != 0) return dz; } return ((int)a.ObjectType).CompareTo((int)b.ObjectType); } private void LoadInfo() { this.renderEnv.Camera.WorldRect = new Rectangle(); this.mapID = -1; if (mapImg == null || !mapImg.TryExtract()) return; int mapID; if (mapImg.Name.Length >= 9 && Int32.TryParse(mapImg.Name.Substring(0, 9), out mapID)) { this.mapID = mapID; } Wz_Node info = mapImg.Node.FindNodeByPath("info"); if (info != null) { Wz_Node left = info.FindNodeByPath("VRLeft"), top = info.FindNodeByPath("VRTop"), right = info.FindNodeByPath("VRRight"), bottom = info.FindNodeByPath("VRBottom"), bgm = info.FindNodeByPath("bgm"), link = info.FindNodeByPath("link"), mapMark = info.FindNodeByPath("mapMark"); // load mapSize if (!(left == null || top == null || right == null || bottom == null)) { int l = left.GetValue(); int t = top.GetValue(); int r = right.GetValue(); int b = bottom.GetValue(); this.renderEnv.Camera.WorldRect = new Rectangle(l, t, r - l, b - t); } // load sound if (bgm != null) { string bgmPath = bgm.GetValueEx(null); if (bgmPath != this.bgmName) { this.bgmName = bgmPath; LoadBgm(); } } // load minimap this.mapMark = mapMark.GetValueEx(null); // load link int _link = link.GetValueEx(-1); if (_link > -1) { Wz_Image linkMapImg = FindMapByID(_link); if (linkMapImg != null) { this.mapImg = linkMapImg; } } } /* 暂时不用小地图方式来判定了 Wz_Node minimap = mapImg.Node.FindChildByPath("miniMap"); if (minimap != null && this.renderEnv.Camera.WorldRect.IsEmpty) { Wz_Node width = minimap.FindChildByPath("width"), height = minimap.FindChildByPath("height"), centerX = minimap.FindChildByPath("centerX"), centerY = minimap.FindChildByPath("centerY"); if (width != null && height != null && centerX != null && centerY != null) { int w = width.GetValue(); int h = height.GetValue(); int ox = centerX.GetValue(); int oy = centerY.GetValue(); this.renderEnv.Camera.WorldRect = new Rectangle(-ox + 50, -oy + 50, w - 100, h - 100); return; } } */ } private Wz_Image FindMapByID(int mapID) { string fullPath = string.Format(@"Map\Map\Map{0}\{1:D9}.img", (mapID / 100000000), mapID); Wz_Node mapImgNode = PluginManager.FindWz(fullPath); Wz_Image mapImg; if (mapImgNode != null && (mapImg = mapImgNode.GetValueEx(null)) != null && mapImg.TryExtract()) { return mapImg; } return null; } private void LoadBgm() { Wz_Node soundWz = PluginManager.FindWz(Wz_Type.Sound); this.bgmFile = null; this.bgmStream = IntPtr.Zero; if (soundWz == null || this.bgmName == null) return; string[] path = this.bgmName.Split('/'); if (path.Length <= 1) return; path[0] += ".img"; Wz_Node soundNode = soundWz.FindNodeByPath(true, true, path); Wz_Sound _sound = soundNode.GetValueEx(null); if (_sound != null) { byte[] newBgmFile = _sound.ExtractSound(); if (newBgmFile != null) { int newBgmStream = Bass.BASS_StreamCreateFile( Marshal.UnsafeAddrOfPinnedArrayElement(newBgmFile, 0), 0, newBgmFile.LongLength, BASSFlag.BASS_DEFAULT ); if (newBgmStream != 0) { this.bgmFile = newBgmFile; this.bgmStream = new IntPtr(newBgmStream); Bass.BASS_ChannelFlags(newBgmStream, BASSFlag.BASS_SAMPLE_LOOP, BASSFlag.BASS_SAMPLE_LOOP); } } } } private void LoadMinimap() { if (this.uiMinimap != null && !this.uiMinimap.ResourceLoaded) { this.uiMinimap.LoadResource(this.GraphicsDevice); } MiniMap miniMap = new MiniMap(); Wz_Node miniMapNode = mapImg.Node.FindNodeByPath("miniMap"); //读取小地图标记 if (mapMark != null) { Wz_Node markNode = PluginManager.FindWz("Map\\MapHelper.img\\mark\\" + mapMark); Wz_Png markImage = markNode.GetValueEx(null); if (markImage != null) { miniMap.MapMark = TextureLoader.PngToTexture(this.GraphicsDevice, markImage); } } //读取小地图 if (miniMapNode != null) { Wz_Node canvas = miniMapNode.FindNodeByPath("canvas"), width = miniMapNode.FindNodeByPath("width"), height = miniMapNode.FindNodeByPath("height"), centerX = miniMapNode.FindNodeByPath("centerX"), centerY = miniMapNode.FindNodeByPath("centerY"), mag = miniMapNode.FindNodeByPath("mag"); Wz_Png _canvas = canvas.GetValueEx(null); if (_canvas != null) { miniMap.Canvas = TextureLoader.PngToTexture(this.GraphicsDevice, _canvas); } miniMap.Width = width.GetValueEx(0); miniMap.Height = height.GetValueEx(0); miniMap.CenterX = centerX.GetValueEx(0); miniMap.CenterY = centerY.GetValueEx(0); miniMap.Mag = mag.GetValueEx(0); } else { this.miniMap = null; return; } //读取传送门 this.uiMinimap.Portals.Clear(); this.uiMinimap.Transports.Clear(); foreach (var patch in this.renderingList) { if (patch.ObjectType == RenderObjectType.Portal) { PortalPatch portal = patch as PortalPatch; switch (portal.PortalType) { case 2: //一般传送门 case 7: //指令传送门 this.uiMinimap.Portals.Add(portal.Position); break; case 10: //地图内传送 this.uiMinimap.Transports.Add(portal.Position); break; } } } this.miniMap = miniMap; } private void LoadObjTile() { Dictionary loadedObjRes = new Dictionary(); Dictionary loadedTileRes = new Dictionary(); Dictionary loadedFrames = new Dictionary(); for (int layer = 0; ; layer++) { Wz_Node objTileNode = mapImg.Node.FindNodeByPath(layer.ToString()); if (objTileNode == null) { break; } Wz_Node objLstNode = objTileNode.FindNodeByPath("obj"); if (objLstNode != null && objLstNode.Nodes.Count > 0) { string[] path = new string[5]; int loadIndex = 0; foreach (Wz_Node node in objLstNode.Nodes) { Wz_Node oS = node.FindNodeByPath("oS"), l0 = node.FindNodeByPath("l0"), l1 = node.FindNodeByPath("l1"), l2 = node.FindNodeByPath("l2"), x = node.FindNodeByPath("x"), y = node.FindNodeByPath("y"), z = node.FindNodeByPath("z"), f = node.FindNodeByPath("f"), zM = node.FindNodeByPath("zM"), tags = node.FindNodeByPath("tags"); if (oS != null && l0 != null && l1 != null && l2 != null) { path[0] = "Obj"; path[1] = oS.GetValue() + ".img"; path[2] = l0.GetValue(); path[3] = l1.GetValue(); path[4] = l2.GetValue(); string key = string.Join("\\", path); RenderFrame[] frames; Wz_Node objResNode = PluginManager.FindWz("Map\\" + key); if (objResNode == null) continue; if (!loadedFrames.TryGetValue(key, out frames)) { frames = LoadFrames(objResNode, loadedObjRes); loadedFrames[key] = frames; } RenderPatch patch = new ObjTilePatch(); patch.ObjectType = RenderObjectType.Obj; patch.Position = new Vector2(x.GetValueEx(0), y.GetValueEx(0)); patch.Flip = f.GetValueEx(0) != 0; patch.ZIndex[0] = (int)RenderObjectType.Obj; patch.ZIndex[1] = layer; patch.ZIndex[2] = (int)patch.ObjectType; patch.ZIndex[3] = z.GetValueEx(0); patch.ZIndex[4] = loadIndex; patch.ZIndex[5] = zM.GetValueEx(0); //not use for sort patch.Frames = new RenderAnimate(frames); patch.Frames.Repeat = objResNode.FindNodeByPath("repeat").GetValueEx(0); patch.Name = string.Format("obj_{0}_{1}", layer, node.Text); //patch.RenderArgs.Visible = string.IsNullOrEmpty(tags.GetValueEx(null)); this.renderingList.Add(patch); } loadIndex++; } } Wz_Node tS = objTileNode.FindNodeByPath("info\\tS"); string _tS = tS.GetValueEx(null); Wz_Node tileLstNode = objTileNode.FindNodeByPath("tile"); if (tileLstNode != null) { string[] path = new string[4]; int loadIndex = 0; foreach (Wz_Node node in tileLstNode.Nodes) { Wz_Node x = node.FindNodeByPath("x"), y = node.FindNodeByPath("y"), u = node.FindNodeByPath("u"), no = node.FindNodeByPath("no"), zM = node.FindNodeByPath("zM"); if (u != null && no != null) { path[0] = "Tile"; path[1] = _tS + ".img"; path[2] = u.GetValue(); path[3] = no.GetValue(); string key = string.Join("\\", path); RenderFrame[] frames; if (!loadedFrames.TryGetValue(key, out frames)) { Wz_Node objResNode = PluginManager.FindWz("Map\\" + key); if (objResNode == null) continue; frames = LoadFrames(objResNode, loadedObjRes); loadedFrames[key] = frames; } RenderPatch patch = new ObjTilePatch(); patch.ObjectType = RenderObjectType.Tile; patch.Position = new Vector2(x.GetValueEx(0), y.GetValueEx(0)); patch.ZIndex[0] = (int)RenderObjectType.Obj; patch.ZIndex[1] = layer; patch.ZIndex[2] = (int)patch.ObjectType; patch.ZIndex[3] = frames[0].Z; patch.ZIndex[4] = loadIndex; patch.ZIndex[5] = zM.GetValueEx(0); //not use for order patch.Frames = new RenderAnimate(frames); patch.Name = string.Format("tile_{0}_{1}", layer, node.Text); this.renderingList.Add(patch); } loadIndex++; } } } } private void LoadBack() { Dictionary loadedBackRes = new Dictionary(); Dictionary loadedFrames = new Dictionary(); Wz_Node backLstNode = mapImg.Node.FindNodeByPath("back"); if (backLstNode != null) { string[] path = new string[4]; int loadIndex = 0; foreach (Wz_Node node in backLstNode.Nodes) { Wz_Node x = node.FindNodeByPath("x"), y = node.FindNodeByPath("y"), bs = node.FindNodeByPath("bS"), ani = node.FindNodeByPath("ani"), no = node.FindNodeByPath("no"), f = node.FindNodeByPath("f"), front = node.FindNodeByPath("front"), type = node.FindNodeByPath("type"), cx = node.FindNodeByPath("cx"), cy = node.FindNodeByPath("cy"), rx = node.FindNodeByPath("rx"), ry = node.FindNodeByPath("ry"), a = node.FindNodeByPath("a"), screenMode = node.FindNodeByPath("screenMode"); if (bs != null && no != null) { int _ani = ani.GetValueEx(0); int _type = type.GetValueEx(0); path[0] = "Back"; path[1] = bs.GetValue() + ".img"; switch (_ani) { case 0: path[2] = "back"; break; case 1: path[2] = "ani"; break; case 2: path[2] = "spine"; break; } path[3] = no.GetValue(); string key = string.Join("\\", path); RenderFrame[] frames; if (!loadedFrames.TryGetValue(key, out frames)) { Wz_Node objResNode = PluginManager.FindWz("Map\\" + key); if (objResNode == null) continue; frames = LoadFrames(objResNode, loadedBackRes); loadedFrames[key] = frames; } BackPatch patch = new BackPatch(); patch.ObjectType = front.GetValueEx(0) != 0 ? RenderObjectType.Front : RenderObjectType.Back; patch.Position = new Vector2(x.GetValueEx(0), y.GetValueEx(0)); patch.Cx = cx.GetValueEx(0); patch.Cy = cy.GetValueEx(0); patch.Rx = rx.GetValueEx(0); patch.Ry = ry.GetValueEx(0); patch.Frames = new RenderAnimate(frames); patch.Flip = f.GetValueEx(0) != 0; patch.TileMode = GetBackTileMode(_type); patch.Alpha = a.GetValueEx(255); patch.ScreenMode = screenMode.GetValueEx(0); patch.ZIndex[0] = (int)patch.ObjectType; Int32.TryParse(node.Text, out patch.ZIndex[1]); patch.Name = string.Format("back_{0}", node.Text); this.renderingList.Add(patch); } loadIndex++; } } } private void LoadFoothold() { Wz_Node fhListNode = mapImg.Node.FindNodeByPath("foothold"); if (fhListNode != null) { int _layer, _z, _fh; foreach (Wz_Node layerNode in fhListNode.Nodes) { Int32.TryParse(layerNode.Text, out _layer); foreach (Wz_Node zNode in layerNode.Nodes) { Int32.TryParse(zNode.Text, out _z); foreach (Wz_Node fhNode in zNode.Nodes) { Int32.TryParse(fhNode.Text, out _fh); Wz_Node x1 = fhNode.FindNodeByPath("x1"), x2 = fhNode.FindNodeByPath("x2"), y1 = fhNode.FindNodeByPath("y1"), y2 = fhNode.FindNodeByPath("y2"), prev = fhNode.FindNodeByPath("prev"), next = fhNode.FindNodeByPath("next"), piece = fhNode.FindNodeByPath("piece"); FootholdPatch patch = new FootholdPatch(); patch.ObjectType = RenderObjectType.Foothold; patch.X1 = x1.GetValueEx(0); patch.X2 = x2.GetValueEx(0); patch.Y1 = y1.GetValueEx(0); patch.Y2 = y2.GetValueEx(0); patch.Prev = prev.GetValueEx(0); patch.Next = next.GetValueEx(0); patch.Piece = piece.GetValueEx(0); patch.ZIndex[0] = (int)patch.ObjectType; patch.ZIndex[1] = _layer; patch.ZIndex[2] = _z; patch.ZIndex[3] = _fh; patch.Name = string.Format("foothold_{0}", fhNode.Text); this.renderingList.Add(patch); } } } } } private void LoadLadderRope() { Wz_Node ropeListNode = mapImg.Node.FindNodeByPath("ladderRope"); if (ropeListNode != null) { int index; foreach (Wz_Node ropeNode in ropeListNode.Nodes) { Int32.TryParse(ropeNode.Text, out index); Wz_Node x = ropeNode.FindNodeByPath("x"), y1 = ropeNode.FindNodeByPath("y1"), y2 = ropeNode.FindNodeByPath("y2"), l = ropeNode.FindNodeByPath("l"), uf = ropeNode.FindNodeByPath("uf"), page = ropeNode.FindNodeByPath("page"); LadderRopePatch patch = new LadderRopePatch(); patch.ObjectType = RenderObjectType.LadderRope; patch.X = x.GetValueEx(0); patch.Y1 = y1.GetValueEx(0); patch.Y2 = y2.GetValueEx(0); patch.L = l.GetValueEx(0); patch.Uf = uf.GetValueEx(0); patch.Page = page.GetValueEx(0); patch.ZIndex[0] = (int)patch.ObjectType; patch.ZIndex[1] = 0; patch.ZIndex[2] = 0; patch.ZIndex[3] = index; patch.Name = string.Format("ladderRope_{0}", ropeNode.Text); this.renderingList.Add(patch); } } } private void LoadLife() { Dictionary> loadedMob = new Dictionary>(); Dictionary> loadedNpc = new Dictionary>(); Wz_Node lifeLstNode = mapImg.Node.FindNodeByPath("life"); if (lifeLstNode != null) { string[] path = new string[1]; int loadIndex = 0; foreach (Wz_Node node in lifeLstNode.Nodes) { Wz_Node type = node.FindNodeByPath("type"), id = node.FindNodeByPath("id"), x = node.FindNodeByPath("x"), cy = node.FindNodeByPath("cy"), f = node.FindNodeByPath("f"), fh = node.FindNodeByPath("fh"), hide = node.FindNodeByPath("hide"); int _id = id.GetValueEx(-1); if (type != null && _id > -1) { LifePatch patch = new LifePatch(); patch.Position = new Vector2(x.GetValueEx(0), cy.GetValueEx(0)); patch.Flip = f.GetValueEx(0) != 0; patch.RenderArgs.Visible = hide.GetValueEx(0) == 0; patch.LifeID = _id; patch.Foothold = fh.GetValueEx(0); path[0] = string.Format("{0:D7}.img", _id); Dictionary> loadedLife; string lifeWz; switch (type.GetValueEx(null)) { case "m": loadedLife = loadedMob; lifeWz = "Mob\\"; patch.ObjectType = RenderObjectType.Mob; break; case "n": loadedLife = loadedNpc; lifeWz = "Npc\\"; patch.ObjectType = RenderObjectType.Npc; break; default: continue; } if (lifeWz == null) continue; // load info Wz_Node lifeImgNode = PluginManager.FindWz(lifeWz+ path[0]); if (lifeImgNode == null) continue; LoadLifeInfo(lifeImgNode.FindNodeByPath("info"), patch.LifeInfo); // load actions Dictionary actions; if (!loadedLife.TryGetValue(_id, out actions)) { Wz_Node link = lifeImgNode.FindNodeByPath("info\\link"); int _link = link.GetValueEx(-1); if (_link >= 0) { if (!loadedLife.TryGetValue(_link, out actions)) { Wz_Node linkImgNode = PluginManager.FindWz(lifeWz + string.Format("{0:D7}.img", _link)); if (linkImgNode == null) continue; actions = LoadLifeActions(linkImgNode); loadedLife[_link] = actions; } } else { actions = LoadLifeActions(lifeImgNode); loadedLife[_id] = actions; } } foreach (var kv in actions) { patch.Actions.Add(kv.Key, new RenderAnimate(kv.Value)); } patch.SwitchToDefaultAction(); patch.Name = string.Format("life_{0}", node.Text); //查找已读取的foothold计算zindex bool find = false; foreach (RenderPatch fhPatch in renderingList) { if (fhPatch.ObjectType == RenderObjectType.Foothold && fhPatch.ZIndex[3] == patch.Foothold) { patch.ZIndex[0] = (int)RenderObjectType.Obj; patch.ZIndex[1] = fhPatch.ZIndex[1]; patch.ZIndex[2] = (int)patch.ObjectType; find = true; break; } } if (!find) { patch.ZIndex[0] = (int)patch.ObjectType; } patch.ZIndex[4] = loadIndex; this.renderingList.Add(patch); } loadIndex++; } } } private void LoadLifeInfo(Wz_Node infoNode, LifeInfo lifeInfo) { if (infoNode == null || lifeInfo == null) return; foreach (Wz_Node node in infoNode.Nodes) { switch (node.Text) { case "level": lifeInfo.level = node.GetValueEx(0); break; case "maxHP": lifeInfo.maxHP = node.GetValueEx(0); break; case "maxMP": lifeInfo.maxMP = node.GetValueEx(0); break; case "speed": lifeInfo.speed = node.GetValueEx(0); break; case "PADamage": lifeInfo.PADamage = node.GetValueEx(0); break; case "PDDamage": lifeInfo.PDDamage = node.GetValueEx(0); break; case "PDRate": lifeInfo.PDRate = node.GetValueEx(0); break; case "MADamage": lifeInfo.MADamage = node.GetValueEx(0); break; case "MDDamage": lifeInfo.MDDamage = node.GetValueEx(0); break; case "MDRate": lifeInfo.MDRate = node.GetValueEx(0); break; case "acc": lifeInfo.acc = node.GetValueEx(0); break; case "eva": lifeInfo.eva = node.GetValueEx(0); break; case "pushed": lifeInfo.pushed = node.GetValueEx(0); break; case "exp": lifeInfo.exp = node.GetValueEx(0); break; case "undead": lifeInfo.undead = node.GetValueEx(0) != 0; break; case "boss": lifeInfo.boss = node.GetValueEx(0) != 0; break; case "elemAttr": string elem = node.GetValueEx(string.Empty); for (int i = 0; i < elem.Length; i += 2) { LifeInfo.ElemResistance resist = (LifeInfo.ElemResistance)(elem[i + 1] - 48); switch (elem[i]) { case 'I': lifeInfo.elemAttr.I = resist; break; case 'L': lifeInfo.elemAttr.L = resist; break; case 'F': lifeInfo.elemAttr.F = resist; break; case 'S': lifeInfo.elemAttr.S = resist; break; case 'H': lifeInfo.elemAttr.H = resist; break; case 'D': lifeInfo.elemAttr.D = resist; break; case 'P': lifeInfo.elemAttr.P = resist; break; } } break; } } } private void LoadPortal() { Dictionary loadedRes = new Dictionary(); Dictionary loadedFrames = new Dictionary(); Wz_Node portalImageNode = PluginManager.FindWz("Map\\MapHelper.img\\portal"); if (portalImageNode == null) return; Wz_Node editorNode = portalImageNode.FindNodeByPath("editor"); if (editorNode == null) return; string[] ptList = new [] { "sp", "pi", "pv", "pc", "pg", "tp", "ps", "pgi", "psi", "pcs", "ph", "psh", "pcj", "pci", "pcig", "pshg" }; Wz_Node portalNode = mapImg.Node.FindNodeByPath("portal"); if (portalNode != null) { string[] path = new string[4]; foreach (Wz_Node node in portalNode.Nodes) { Wz_Node pn = node.FindNodeByPath("pn"), pt = node.FindNodeByPath("pt"), x = node.FindNodeByPath("x"), y = node.FindNodeByPath("y"), tm = node.FindNodeByPath("tm"), tn = node.FindNodeByPath("tn"), script = node.FindNodeByPath("script"), image = node.FindNodeByPath("image"); if (pt != null) { PortalPatch patch = new PortalPatch(); patch.ObjectType = RenderObjectType.Portal; patch.PortalName = pn.GetValueEx(null); patch.ToMap = tm.GetValueEx(0); patch.ToName = tn.GetValueEx(null); patch.Script = script.GetValueEx(null); patch.Position = new Vector2(x.GetValueEx(0), y.GetValueEx(0)); int _pt = pt.GetValueEx(-1); patch.PortalType = _pt; if (_pt < 0 || _pt >= ptList.Length) continue; //读取aniEditor path[0] = "editor"; path[1] = ptList[_pt]; string key = string.Join("\\", path, 0, 2); RenderFrame[] frames; Wz_Node resNode; if (!loadedFrames.TryGetValue(key, out frames)) { resNode = portalImageNode.FindNodeByPath(false, path[0], path[1]); if (resNode == null) continue; frames = LoadFrames(resNode, loadedRes); loadedFrames[key] = frames; } patch.AniEditor = new RenderAnimate(frames); //读取aniPortal switch (_pt) { case 7: _pt = 2; break; } path[0] = "game"; path[1] = ptList[_pt]; resNode = portalImageNode.FindNodeByPath(false, path[0], path[1]); if (resNode != null) { if (resNode.FindNodeByPath("default") != null) //寻找image对应的节点 否则本身作为根节点 { int _image = image.GetValueEx(0); if (_image == 0) path[2] = "default"; else path[2] = _image.ToString(); if ((resNode = resNode.FindNodeByPath(path[2])) == null) continue; } string[] animeNames = new string[] { "portalStart", "portalContinue", "portalExit" }; bool existsAnimes = false; foreach (string animeName in animeNames) { if (resNode.FindNodeByPath(animeName) != null) { existsAnimes = true; break; } } if (existsAnimes) //三段式读取动画 { RenderFrame[][] animeFrames = new RenderFrame[animeNames.Length][]; for (int i = 0; i < animeNames.Length; i++) { path[3] = animeNames[i]; key = string.Join("\\", path); if (!loadedFrames.TryGetValue(key, out frames)) { Wz_Node aniNode = resNode.FindNodeByPath(animeNames[i]); if (aniNode == null) continue; frames = LoadFrames(aniNode, loadedRes); loadedFrames[key] = frames; } animeFrames[i] = frames; } patch.AniStart = new RenderAnimate(animeFrames[0]); patch.AniContinue = new RenderAnimate(animeFrames[1]); patch.AniExit = new RenderAnimate(animeFrames[2]); } else //只读取动画作为continue { key = string.Join("\\", path, 0, 2); if (!loadedFrames.TryGetValue(key, out frames)) { frames = LoadFrames(resNode, loadedRes); loadedFrames[key] = frames; } patch.AniContinue = new RenderAnimate(frames); } } patch.ZIndex[0] = (int)patch.ObjectType; Int32.TryParse(node.Text, out patch.ZIndex[3]); patch.Name = string.Format("portal_{0}", node.Text); this.renderingList.Add(patch); } } } } private void LoadReactor() { Wz_Node reactorWz = PluginManager.FindWz(Wz_Type.Reactor); Dictionary> loadedFrames = new Dictionary>(); if (reactorWz == null) return; Wz_Node reactorNode = mapImg.Node.FindNodeByPath("reactor"); if (reactorNode != null) { string[] path = new string[1]; foreach (Wz_Node node in reactorNode.Nodes) { Wz_Node id = node.FindNodeByPath("id"), x = node.FindNodeByPath("x"), y = node.FindNodeByPath("y"), f = node.FindNodeByPath("f"), reactorTime = node.FindNodeByPath("reactorTime"), name = node.FindNodeByPath("name"); int _id = id.GetValueEx(-1); if (_id > -1) { ReactorPatch patch = new ReactorPatch(); patch.ReactorID = _id; patch.Position = new Vector2(x.GetValueEx(0), y.GetValueEx(0)); patch.ReactorName = name.GetValueEx(null); patch.Flip = (f.GetValueEx(0) != 0); patch.ObjectType = RenderObjectType.Reactor; path[0] = string.Format("{0:D7}.img", _id); Wz_Node reactorImgNode = reactorWz.FindNodeByPath(path[0], true); if (reactorImgNode == null) continue; List stages; if (!loadedFrames.TryGetValue(_id, out stages)) { Wz_Node link = reactorImgNode.FindNodeByPath("info\\link"); int _link = link.GetValueEx(-1); if (_link >= 0) { if (!loadedFrames.TryGetValue(_link, out stages)) { Wz_Node linkImgNode = reactorWz.FindNodeByPath(string.Format("{0:D7}.img", _link), true); if (linkImgNode == null) continue; stages = LoadReactorStages(linkImgNode); loadedFrames[_link] = stages; } } else { stages = LoadReactorStages(reactorImgNode); loadedFrames[_link] = stages; } } foreach (var stage in stages) { patch.Stages.Add(new RenderAnimate(stage)); } int _time = reactorTime.GetValueEx(0); if (_time < 0) _time = 0; if (_time < patch.Stages.Count) patch.Frames = patch.Stages[_time]; patch.ZIndex[0] = (int)patch.ObjectType; Int32.TryParse(node.Text, out patch.ZIndex[3]); patch.Name = string.Format("reactor_{0}", node.Text); this.renderingList.Add(patch); } } } } private void LoadTooltip() { Wz_Node tooltipNode = mapImg.Node.FindNodeByPath("ToolTip"); } private void CalcMapSize() { if (this.renderEnv.Camera.WorldRect.IsEmpty) { Rectangle worldRect = Rectangle.Empty; foreach (RenderPatch patch in this.renderingList) { Rectangle patchRect = Rectangle.Empty; switch (patch.ObjectType) { case RenderObjectType.Foothold: FootholdPatch fh = patch as FootholdPatch; patchRect = new Rectangle(fh.X1, fh.Y1, fh.X2 - fh.X1, fh.Y2 - fh.Y1); break; case RenderObjectType.LadderRope: LadderRopePatch rope = patch as LadderRopePatch; patchRect = new Rectangle(rope.X, rope.Y1, 1, rope.Y2 - rope.Y1); break; case RenderObjectType.Obj: case RenderObjectType.Tile: ObjTilePatch objTile = patch as ObjTilePatch; foreach (RenderFrame f in objTile.Frames) { if (f.Texture != null && !f.Texture.IsDisposed) { Rectangle rect = this.renderEnv.Camera.MeasureDrawingRect(f.Texture.Width, f.Texture.Height, objTile.Position, f.Origin, objTile.Flip); if (patchRect.IsEmpty) { patchRect = rect; } else { Rectangle.Union(ref patchRect, ref rect, out patchRect); } } } break; } if (!patchRect.IsEmpty) { if (worldRect.IsEmpty) { worldRect = patchRect; } else { Rectangle.Union(ref worldRect, ref patchRect, out worldRect); } } } worldRect.Y -= 400; worldRect.Height += 400; this.renderEnv.Camera.WorldRect = worldRect; } } private Dictionary LoadLifeActions(Wz_Node lifeNode) { Dictionary actions = new Dictionary(); Dictionary loadedRes = new Dictionary(); foreach (Wz_Node actionNode in lifeNode.Nodes) { if (actionNode.Text != "info") { RenderFrame[] frames = LoadFrames(actionNode, loadedRes); if (frames.Length > 0) { actions[actionNode.Text] = frames; } } } return actions; } private List LoadReactorStages(Wz_Node reactorNode) { List stages = new List(); Dictionary loadedRes = new Dictionary(); for (int i = 0; ; i++) { Wz_Node stageNode = reactorNode.FindNodeByPath(i.ToString()); if (stageNode == null) break; stages.Add(LoadFrames(stageNode, loadedRes)); } return stages; } private RenderFrame[] LoadFrames(Wz_Node resNode, Dictionary loadedRes) { if (resNode.Value is Wz_Png) { RenderFrame frame; if (!loadedRes.TryGetValue(resNode.FullPath, out frame)) { frame = this.texLoader.CreateFrame(resNode); loadedRes[resNode.FullPath] = frame; } return new RenderFrame[1] { frame }; } List frames = new List(); Wz_Uol uol; for (int i = 0; ; i++) { Wz_Node frameNode = resNode.FindNodeByPath(i.ToString()); if (frameNode == null) break; while ((uol = frameNode.Value as Wz_Uol) != null) { frameNode = uol.HandleUol(frameNode); } RenderFrame frame; if (!loadedRes.TryGetValue(frameNode.FullPath, out frame)) { frame = this.texLoader.CreateFrame(frameNode); loadedRes[frameNode.FullPath] = frame; } frames.Add(frame); } return frames.ToArray(); } private TileMode GetBackTileMode(int type) { switch (type) { case 0: return TileMode.None; case 1: return TileMode.Horizontal; case 2: return TileMode.Vertical; case 3: return TileMode.BothTile; case 4: return TileMode.Horizontal | TileMode.ScrollHorizontial; case 5: return TileMode.Vertical | TileMode.ScrollVertical; case 6: return TileMode.BothTile | TileMode.ScrollHorizontial; case 7: return TileMode.BothTile | TileMode.ScrollVertical; default: return TileMode.None; } } private enum LoadState { NotLoad, Loading, LoadSuccessed, Entering, Rendering, Exiting, LoadFailed } } } #endif ================================================ FILE: WzComparerR2.MapRender/FrmMapRender2.SceneManager.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using WzComparerR2.WzLib; using WzComparerR2.PluginBase; using WzComparerR2.Common; using WzComparerR2.MapRender.Patches2; using WzComparerR2.MapRender.UI; using Microsoft.Xna.Framework; using IE = System.Collections.IEnumerator; namespace WzComparerR2.MapRender { public partial class FrmMapRender2 { enum SceneManagerState { Starting = 0, Loading, Entering, Running, Exiting, } #region private fields MapViewData viewData; LinkedList viewHistory; SceneManagerState sceneManagerState; Wz_Image mapImgLoading; #endregion public void LoadMap(Wz_Image mapImg) { if (this.sceneManagerState == SceneManagerState.Starting || this.sceneManagerState == SceneManagerState.Running) { this.mapImgLoading = mapImg; } } /* * State machine transition table * * FromState | condition | ToState * ----------|--------------|----------- * Starting | mapImg != null | Loading * Starting | mapImg == null | Running (empty scene) * Loading | mapData != null | Entering * Loading | mapData == null | Running (empty scene) * Entering | global_light >= 1.0 | Running * Running | viewData.toMap != null | Exiting * Exiting | viewData.toMap != null | Loading * */ private IE OnStart() { // initialize this.viewHistory = new LinkedList(); this.sceneManagerState = SceneManagerState.Starting; // add view state this.viewData = new MapViewData() { MapID = -1, ToMapID = null, }; if (this.mapImgLoading != null) { this.viewData.ToPortal = "sp"; yield return cm.Yield(OnMapLoading()); } else { this.opacity = 1; yield return cm.Yield(OnSceneRunning()); } } private IE OnMapLoading() { this.sceneManagerState = SceneManagerState.Loading; var loadMapTask = this.LoadMap(); yield return new WaitTaskCompletedCoroutine(loadMapTask); if (loadMapTask.Exception != null) { this.ui.ChatBox.AppendTextSystem($"Failed to load map:{loadMapTask.Exception}"); this.mapImgLoading = null; this.opacity = 1; yield return cm.Yield(OnSceneRunning()); yield break; } // backfill toMapID if (this.viewData.ToMapID == null && this.mapData?.ID != null) { this.viewData.ToMapID = this.mapData.ID; } //记录历史 if (this.viewData.MapID > -1 && this.viewData.MapID != this.viewData.ToMapID && this.viewData.ToMapID != null) { if (this.viewData.IsMoveBack && this.viewData.ToMapID == this.viewHistory.Last?.Value?.MapID) { var last = this.viewHistory.Last.Value; this.viewHistory.RemoveLast(); var toViewData = new MapViewData() { MapID = last.MapID, Portal = last.Portal ?? "sp" }; this.viewData = toViewData; } else { viewHistory.AddLast(this.viewData); var toViewData = new MapViewData() { MapID = this.viewData.ToMapID.Value, Portal = this.viewData.ToPortal ?? "sp" }; this.viewData = toViewData; } } else { this.viewData.MapID = this.viewData.ToMapID ?? -1; this.viewData.ToMapID = null; this.viewData.Portal = this.viewData.ToPortal; this.viewData.ToPortal = null; } this.viewData.IsMoveBack = false; yield return cm.Yield(OnSceneEnter()); } private async Task LoadMap() { if (this.mapImgLoading == null) { return false; } //开始加载 this.resLoader.ClearAnimationCache(); this.resLoader.BeginCounting(); //加载地图数据 var mapData = new MapData(this.Services.GetService()); mapData.Load(this.mapImgLoading.Node, resLoader); //处理bgm Music newBgm = LoadBgm(mapData); Task bgmTask = null; bool willSwitchBgm = this.mapData?.Bgm != mapData.Bgm; if (willSwitchBgm && this.bgm != null) //准备切换 { bgmTask = FadeOut(this.bgm, 1000); } //加载资源 mapData.PreloadResource(resLoader); //准备UI和初始化 this.AfterLoadMap(mapData); if (bgmTask != null) { await bgmTask; } //回收资源 this.resLoader.EndCounting(); this.resLoader.Recycle(); //准备场景和bgm this.mapImg = this.mapImgLoading; this.mapImgLoading = null; this.mapData = mapData; this.bgm = newBgm; if (willSwitchBgm && this.bgm != null) { bgmTask = FadeIn(this.bgm, 1000); } return true; } private async Task FadeOut(Music music, int ms) { float vol = music.Volume; for (int i = 0; i < ms; i += 30) { music.Volume = vol * (ms - i) / ms; await Task.Delay(30); } music.Volume = 0f; music.Stop(); } private async Task FadeIn(Music music, int ms) { music.Play(); float vol = music.Volume; for (int i = 0; i < ms; i += 30) { music.Volume = vol + (1 - vol) * i / ms; await Task.Delay(30); } music.Volume = 1f; } private Music LoadBgm(MapData mapData, string multiBgmText = null) { if (!string.IsNullOrEmpty(mapData.Bgm)) { var path = new List() { "Sound" }; path.AddRange(mapData.Bgm.Split('/')); path[1] += ".img"; var bgmNode = PluginManager.FindWz(string.Join("\\", path)); if (bgmNode != null) { if (bgmNode.Value == null) { bgmNode = multiBgmText == null ? bgmNode.Nodes.FirstOrDefault(n => n.Value is Wz_Sound || n.Value is Wz_Uol) : bgmNode.Nodes[multiBgmText]; if (bgmNode == null) { return null; } } while (bgmNode.Value is Wz_Uol uol) { bgmNode = uol.HandleUol(bgmNode); } var bgm = resLoader.Load(bgmNode); bgm.IsLoop = true; return bgm; } } return null; } private void AfterLoadMap(MapData mapData) { //同步可视化状态 foreach (var portal in mapData.Scene.Portals) { portal.View.IsEditorMode = this.patchVisibility.PortalInEditMode; } //同步UI this.renderEnv.Camera.WorldRect = mapData.VRect; this.ui.MirrorFrame.Visibility = mapData.ID / 10000000 == 32 ? EmptyKeys.UserInterface.Visibility.Visible : EmptyKeys.UserInterface.Visibility.Collapsed; this.ui.Minimap.Mirror = mapData.ID / 10000000 == 32; StringResult sr; if (mapData.ID != null && this.StringLinker != null && StringLinker.StringMap.TryGetValue(mapData.ID.Value, out sr)) { this.ui.Minimap.StreetName = sr["streetName"]; this.ui.Minimap.MapName = sr["mapName"]; } else { this.ui.Minimap.StreetName = null; this.ui.Minimap.MapName = null; } if (mapData.MiniMap.MapMark != null) { this.ui.Minimap.MapMark = engine.Renderer.CreateTexture(mapData.MiniMap.MapMark); } else { this.ui.Minimap.MapMark = null; } if (mapData.MiniMap.Canvas != null) { this.ui.Minimap.MinimapCanvas = engine.Renderer.CreateTexture(mapData.MiniMap.Canvas); } else { this.ui.Minimap.MinimapCanvas = null; } this.ui.Minimap.Icons.Clear(); foreach (var portal in mapData.Scene.Portals) { switch (portal.Type) { case 2: case 7: object tooltip = portal.Tooltip; if (tooltip == null && portal.ToMap != null && portal.ToMap != 999999999 && StringLinker.StringMap.TryGetValue(portal.ToMap.Value, out sr)) { tooltip = sr["mapName"]; } this.ui.Minimap.Icons.Add(new UIMinimap2.MapIcon() { IconType = UIMinimap2.IconType.Portal, Tooltip = tooltip, WorldPosition = new EmptyKeys.UserInterface.PointF(portal.X, portal.Y) }); break; case 8: if (portal.ShownAtMinimap) { this.ui.Minimap.Icons.Add(new UIMinimap2.MapIcon() { IconType = UIMinimap2.IconType.HiddenPortal, Tooltip = portal.Tooltip, WorldPosition = new EmptyKeys.UserInterface.PointF(portal.X, portal.Y) }); } break; case 10: this.ui.Minimap.Icons.Add(new UIMinimap2.MapIcon() { IconType = portal.ToMap == mapData.ID ? UIMinimap2.IconType.ArrowUp : UIMinimap2.IconType.HiddenPortal, Tooltip = portal.Tooltip, WorldPosition = new EmptyKeys.UserInterface.PointF(portal.X, portal.Y) }); break; case 11: if (portal.ShownAtMinimap) { this.ui.Minimap.Icons.Add(new UIMinimap2.MapIcon() { IconType = UIMinimap2.IconType.HiddenPortal, Tooltip = portal.Tooltip, WorldPosition = new EmptyKeys.UserInterface.PointF(portal.X, portal.Y) }); } break; } } foreach (var illuminantCluster in mapData.Scene.IlluminantClusters) { this.ui.Minimap.Icons.Add(new UIMinimap2.MapIcon() { IconType = UIMinimap2.IconType.HiddenPortal, WorldPosition = new EmptyKeys.UserInterface.PointF(illuminantCluster.Start.X, illuminantCluster.Start.Y) }); } foreach (var npc in mapData.Scene.Npcs) { object tooltip = null; var npcNode = PluginManager.FindWz(string.Format("Npc/{0:D7}.img/info", npc.ID)); if ((npcNode?.Nodes["hide"].GetValueEx(0) ?? 0) != 0 || (npcNode?.Nodes["hideName"].GetValueEx(0) ?? 0) != 0) { continue; } if (StringLinker.StringNpc.TryGetValue(npc.ID, out sr)) { if (sr.Desc != null) { tooltip = new KeyValuePair(sr.Name, sr.Desc); } else { tooltip = sr.Name; } } if (npc.ID == 9010022) { this.ui.Minimap.Icons.Add(new UIMinimap2.MapIcon() { IconType = UIMinimap2.IconType.Transport, Tooltip = tooltip, WorldPosition = new EmptyKeys.UserInterface.PointF(npc.X, npc.Y) }); } else if ((npcNode?.Nodes["shop"].GetValueEx(0) ?? 0) != 0) { this.ui.Minimap.Icons.Add(new UIMinimap2.MapIcon() { IconType = UIMinimap2.IconType.Shop, Tooltip = tooltip, WorldPosition = new EmptyKeys.UserInterface.PointF(npc.X, npc.Y) }); } else if (npc.ID / 10000 == 900 || npc.ID / 10000 == 901) { this.ui.Minimap.Icons.Add(new UIMinimap2.MapIcon() { IconType = UIMinimap2.IconType.EventNpc, Tooltip = tooltip, WorldPosition = new EmptyKeys.UserInterface.PointF(npc.X, npc.Y) }); } else if ((npcNode?.Nodes["trunkPut"].GetValueEx(0) ?? 0) != 0) { this.ui.Minimap.Icons.Add(new UIMinimap2.MapIcon() { IconType = UIMinimap2.IconType.Trunk, Tooltip = tooltip, WorldPosition = new EmptyKeys.UserInterface.PointF(npc.X, npc.Y) }); } else { this.ui.Minimap.Icons.Add(new UIMinimap2.MapIcon() { IconType = UIMinimap2.IconType.Npc, Tooltip = tooltip, WorldPosition = new EmptyKeys.UserInterface.PointF(npc.X, npc.Y) }); } } foreach (var mob in mapData.Scene.Mobs) { var mobNode = PluginManager.FindWz(string.Format("Mob/{0:D7}.img/info", mob.ID)); if ((mobNode?.Nodes["minimap"].GetValueEx(0) ?? 0) != 0) { this.ui.Minimap.Icons.Add(new UIMinimap2.MapIcon() { IconType = UIMinimap2.IconType.Another, WorldPosition = new EmptyKeys.UserInterface.PointF(mob.X, mob.Y) }); } } if (mapData.MiniMap.Width > 0 && mapData.MiniMap.Height > 0) { this.ui.Minimap.MapRegion = new Rectangle(-mapData.MiniMap.CenterX, -mapData.MiniMap.CenterY, mapData.MiniMap.Width, mapData.MiniMap.Height).ToRect(); } else { this.ui.Minimap.MapRegion = mapData.VRect.ToRect(); } this.ui.WorldMap.CurrentMapID = mapData?.ID; } private IE OnSceneEnter() { this.sceneManagerState = SceneManagerState.Entering; //初始化指向传送门 if (!string.IsNullOrEmpty(viewData.Portal)) { var portal = this.mapData.Scene.FindPortal(viewData.Portal); if (portal != null) { this.renderEnv.Camera.Center = new Vector2(portal.X, portal.Y); } else { this.renderEnv.Camera.Center = Vector2.Zero; } this.renderEnv.Camera.AdjustToWorldRect(); } viewData.Portal = null; //场景渐入 this.opacity = 0; double time = 500; for (double i = 0; i < time; i += cm.GameTime.ElapsedGameTime.TotalMilliseconds) { this.opacity = (float)(i / time); SceneUpdate(); yield return null; } this.opacity = 1; yield return cm.Yield(OnSceneRunning()); } private IE OnSceneExit() { this.sceneManagerState = SceneManagerState.Exiting; //场景渐出 this.opacity = 1; double time = 500; for (double i = 0; i < time; i += cm.GameTime.ElapsedGameTime.TotalMilliseconds) { this.opacity = 1f - (float)(i / time); yield return null; } this.opacity = 0; yield return null; yield return cm.Yield(OnMapLoading()); } private IE OnSceneRunning() { this.sceneManagerState = SceneManagerState.Running; while (true) { SceneUpdate(); if (this.mapImgLoading != null) { break; } yield return null; } yield return cm.Yield(OnSceneExit()); } private IE OnCameraMoving(Point toPos, int ms) { Vector2 cameraFrom = this.renderEnv.Camera.Center; Vector2 cameraTo = toPos.ToVector2(); for (double i = 0; i < ms; i += cm.GameTime.ElapsedGameTime.TotalMilliseconds) { var percent = (i / ms); this.renderEnv.Camera.Center = Vector2.Lerp(cameraFrom, cameraTo, (float)Math.Sqrt(percent)); this.renderEnv.Camera.AdjustToWorldRect(); yield return null; } this.renderEnv.Camera.Center = cameraTo; this.renderEnv.Camera.AdjustToWorldRect(); } private void SceneUpdate() { var gameTime = cm.GameTime; var mapData = this.mapData; if (this.IsActive) { this.renderEnv.Input.Update(gameTime); this.ui.UpdateInput(gameTime.ElapsedGameTime.TotalMilliseconds); } //需要手动更新数据部分 this.renderEnv.Camera.AdjustToWorldRect(); { var rect = this.renderEnv.Camera.ClipRect; this.ui.Minimap.CameraViewPort = new EmptyKeys.UserInterface.Rect(rect.X, rect.Y, rect.Width, rect.Height); } //更新topbar UpdateTopBar(); //更新ui this.ui.UpdateLayout(gameTime.ElapsedGameTime.TotalMilliseconds); //更新场景 if (mapData != null) { UpdateAllItems(mapData.Scene, gameTime.ElapsedGameTime); } //更新tooltip UpdateTooltip(); } private void MoveToPortal(int? toMap, string pName, string fromPName = null, bool isBack = false) { if (toMap != null && toMap != this.mapData?.ID) //跳转地图 { //寻找地图数据 Wz_Node node; if (MapData.FindMapByID(toMap.Value, out node)) { Wz_Image img = node.GetNodeWzImage(); if (img != null) { this.mapImgLoading = img; viewData.ToMapID = toMap; viewData.ToPortal = pName; viewData.Portal = fromPName; viewData.IsMoveBack = isBack; } } else { this.ui.ChatBox.AppendTextSystem($"没有找到ID:{toMap.Value}的地图。"); } } else //当前地图 { viewData.ToMapID = null; viewData.ToPortal = null; var portal = this.mapData.Scene.FindPortal(pName); if (portal != null) { this.cm.StartCoroutine(OnCameraMoving(new Point(portal.X, portal.Y), 500)); } } } private void MoveToLastMap() { if (viewHistory.Count > 0) { var last = viewHistory.Last.Value; if (last.MapID > -1) { MoveToPortal(last.MapID, last.Portal, null, true); } } } class MapViewData { public int MapID { get; set; } public string Portal { get; set; } public int? ToMapID { get; set; } public string ToPortal { get; set; } public bool IsMoveBack { get; set; } } } } ================================================ FILE: WzComparerR2.MapRender/FrmMapRender2.SceneRendering.cs ================================================ using System; using System.Collections.Generic; using System.Text; using System.Linq; using WzComparerR2.Common; using WzComparerR2.MapRender.UI; using WzComparerR2.MapRender.Patches2; using WzComparerR2.Animation; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using WzComparerR2.Controls; using WzComparerR2.MapRender.Effects; namespace WzComparerR2.MapRender { public partial class FrmMapRender2 { private void UpdateAllItems(SceneNode node, TimeSpan elapsed) { var container = node as ContainerNode; if (container != null) //暂时不考虑缩进z层递归合并 container下没有子节点 { foreach (var item in container.Slots) { if (item is BackItem) { var back = (BackItem)item; (back.View.Animator as WzComparerR2.Controls.AnimationItem)?.Update(elapsed); back.View.Time += (int)elapsed.TotalMilliseconds; } else if (item is ObjItem) { var _item = (ObjItem)item; ApplyMapEvents(_item.Events, _item.View.Animator, _item.SpineAni); (_item.View.Animator as WzComparerR2.Controls.AnimationItem)?.Update(elapsed); _item.View.Time += (int)elapsed.TotalMilliseconds; } else if (item is TileItem) { var tile = (TileItem)item; (tile.View.Animator as WzComparerR2.Controls.AnimationItem)?.Update(elapsed); tile.View.Time += (int)elapsed.TotalMilliseconds; } else if (item is LifeItem) { var life = (LifeItem)item; var smAni = (life.View.Animator as StateMachineAnimator); if (smAni != null) { if (smAni.GetCurrent() == null) //当前无动作 { smAni.SetAnimation(smAni.Data.States[0]); //动作0 } smAni.Update(elapsed); } life.View.Time += (int)elapsed.TotalMilliseconds; } else if (item is PortalItem) { var portal = (PortalItem)item; //更新状态 var cursorPos = renderEnv.Camera.CameraToWorld(renderEnv.Input.MousePosition); var sensorRect = new Rectangle(portal.X - 250, portal.Y - 150, 500, 300); portal.View.IsFocusing = sensorRect.Contains(cursorPos); //更新动画 var ani = portal.View.IsEditorMode ? portal.View.EditorAnimator : portal.View.Animator; if (ani is StateMachineAnimator) { if (portal.View.Controller != null) { portal.View.Controller.Update(elapsed); } else { ((StateMachineAnimator)ani).Update(elapsed); } } else if (ani is FrameAnimator) { var frameAni = (FrameAnimator)ani; frameAni.Update(elapsed); } } else if (item is IlluminantClusterItem) { var illuminantCluster = (IlluminantClusterItem)item; (illuminantCluster.StartView.Animator as WzComparerR2.Controls.AnimationItem)?.Update(elapsed); illuminantCluster.StartView.Time += (int)elapsed.TotalMilliseconds; (illuminantCluster.EndView.Animator as WzComparerR2.Controls.AnimationItem)?.Update(elapsed); illuminantCluster.EndView.Time += (int)elapsed.TotalMilliseconds; } else if (item is ReactorItem) { var reactor = (ReactorItem)item; var ani = reactor.View.Animator; if (ani is StateMachineAnimator) { if (reactor.View.Controller != null) { reactor.View.Controller.Update(elapsed); } else { ((StateMachineAnimator)ani).Update(elapsed); } } } else if (item is ParticleItem) { var particle = (ParticleItem)item; var pSystem = particle.View?.ParticleSystem; if (pSystem != null) { pSystem.Update(elapsed); } } } } else { for (int i = 0, i1 = node.Nodes.Count; i < i1; i++) { UpdateAllItems(node.Nodes[i], elapsed); } } } private void ApplyMapEvents(IEnumerable itemEvents, object animator, string defaultAniName) { if (itemEvents.Count() == 0 || animator is not ISpineAnimator) { return; } var cursorPos = renderEnv.Camera.CameraToWorld(renderEnv.Input.MousePosition); var eventList = itemEvents.Select(ie => { return new { SlotName = ie.SlotName, Animation = ie.Animation, MapEvent = this.mapData.MapEvents.Where(me => ie.ActionKeys.Contains(me.Index)), Rect = (animator as ISpineAnimator).GetSlotBounds(ie.SlotName), }; }).Where(data => data.MapEvent != null || !string.IsNullOrEmpty(data.Animation)); foreach (var data in eventList) { var sensorRect = data.Rect; if (sensorRect.Contains(cursorPos)) { if (!string.IsNullOrEmpty(data.Animation)) { var tmpMapEvent = new MapEvent(null, "SetAnimationOnceAndReturn", defaultAniName, data.Animation, null); InvokeMapEvent(animator as ISpineAnimator, new List() { tmpMapEvent }); } else InvokeMapEvent(animator as ISpineAnimator, data.MapEvent.ToList()); } } } private void InvokeMapEvent(ISpineAnimator sender, List mapEvents) { if (sender != null) { foreach (var mapEvent in mapEvents) { if (mapEvent == null) continue; List targets = mapEvent.Tags == null ? new List() { sender as ISpineAnimator } : GetSceneContainers(this.mapData?.Scene) .SelectMany(container => container.Slots) .Where(sceneItem => sceneItem.Tags != null && sceneItem.Tags.Contains(mapEvent.Tags)) .Select(sceneItem => (ISpineAnimator)((sceneItem as ObjItem)?.View?.Animator) ?? null) .Where(spine => spine != null) .Distinct() .ToList(); switch (mapEvent.Type) { case MapEventType.SetAnimationOnceAndReturn: foreach (var spine in targets) { if (spine.NextAnimationName.Count > 0) { return; } else { spine.SelectedAnimationName = mapEvent.ChangedAnimation; spine.NextAnimationName.Enqueue(mapEvent.DefaultAnimation); } } break; } } } } private void UpdateTooltip() { var mouse = renderEnv.Input.MousePosition; var mouseElem = EmptyKeys.UserInterface.Input.InputManager.Current.MouseDevice.MouseOverElement; object target = null; if (mouseElem == this.ui.ContentControl) { var mouseTarget = this.allItems.Reverse().FirstOrDefault(item => { return item.rect.Contains(mouse) && (item.item is LifeItem || item.item is PortalItem || item.item is IlluminantClusterItem || item.item is ReactorItem); }); target = mouseTarget.item; } else if (mouseElem is ITooltipTarget) { var pos = EmptyKeys.UserInterface.Input.InputManager.Current.MouseDevice.GetPosition(mouseElem); target = ((ITooltipTarget)mouseElem).GetTooltipTarget(pos); } tooltip.TooltipTarget = target; } private void UpdateTopBar() { StringBuilder sb = new StringBuilder(); var topbar = this.ui.TopBar; //显示地图名字 int? mapID = this.mapData?.ID; sb.Append("[").Append(mapID != null ? mapID.ToString() : mapImg?.Node.FullPathToFile); if (!topbar.IsShortMode) { sb.Append(" "); StringResult sr; if (this.StringLinker != null && mapID != null && this.StringLinker.StringMap.TryGetValue(mapID.Value, out sr)) { sb.Append(sr.Name); } else { sb.Append("(null)"); } } sb.Append("]"); //显示bgm名字 sb.Append(" [").Append(this.mapData?.Bgm ?? "(noBgm)").Append("]"); //显示fps sb.AppendFormat(" [fps u:{0:f2} d:{1:f2}]", fpsCounter.UpdatePerSec, fpsCounter.DrawPerSec); //可见性 sb.Append(" ctrl+"); int[] array = new[] { 1, 2, 3, 4, 5, 6, 7, 9, 10 }; for (int i = 0; i < array.Length; i++) { var objType = (Patches.RenderObjectType)array[i]; sb.Append(this.patchVisibility.IsVisible(objType) ? "-" : (i + 1).ToString()); } sb.Append(" Mouse:"); var mouse = this.renderEnv.Input.MousePosition; var mousePos = this.renderEnv.Camera.CameraToWorld(mouse); sb.AppendFormat("{0},{1}", mousePos.X, mousePos.Y); this.ui.TopBar.Text = sb.ToString(); } private void OnSceneItemClick(SceneItem item) { if (item is PortalItem) { var portal = (PortalItem)item; if (portal.ToMap != 999999999) { MoveToPortal(portal.ToMap, portal.ToName, portal.PName); } else if (portal.GraphTargetMap.Count == 1) { MoveToPortal(portal.GraphTargetMap[0], "sp", portal.PName); } else if (portal.GraphTargetMap.Count > 1) { this.ui.Teleport.Sl = this.StringLinker; this.ui.Teleport.CmbMaps.ItemsSource = portal.GraphTargetMap.ToList(); this.ui.Teleport.CmbMaps.SelectedIndex = 0; this.ui.Teleport.Toggle(); } } else if (item is IlluminantClusterItem) { var illuminantCluster = (IlluminantClusterItem)item; this.cm.StartCoroutine(OnCameraMoving(new Point(illuminantCluster.End.X, illuminantCluster.End.Y), 500)); } } private void DrawScene(GameTime gameTime) { if (this.mapData == null) { return; } RenderTarget2D lightMapTexture = null; // draw lightmap if available if (this.mapData?.Light != null) { var viewport = this.GraphicsDevice.Viewport; lightMapTexture = new RenderTarget2D(this.GraphicsDevice, viewport.Width, viewport.Height, false, SurfaceFormat.Color, DepthFormat.None); var oldRenderTarget = this.GraphicsDevice.GetRenderTargets(); this.GraphicsDevice.SetRenderTarget(lightMapTexture); this.PrepareLightMap(); this.GraphicsDevice.SetRenderTargets(oldRenderTarget); } allItems.Clear(); var camera = this.renderEnv.Camera; var origin = camera.Origin; this.batcher.Begin(origin, (float)(gameTime.TotalGameTime.TotalSeconds % 1000)); Rectangle[] rects = null; //绘制场景 foreach (var kv in GetDrawableItems(this.mapData.Scene)) { this.batcher.Draw(kv.Value); //绘制标签 DrawName(kv.Key); //缓存绘图区域 { int rectCount; this.batcher.Measure(kv.Value, ref rects, out rectCount); if (kv.Value.RenderObject is Frame) { var frame = (Frame)kv.Value.RenderObject; } if (rects != null && rectCount > 0) { for (int i = 0; i < rectCount; i++) { rects[i].X -= (int)origin.X; rects[i].Y -= (int)origin.Y; allItems.Add(new ItemRect() { item = kv.Key, rect = rects[i] }); } } } this.batcher.MeshPush(kv.Value); } //在场景之上绘制额外标记 DrawFootholds(gameTime); this.batcher.End(); // apply light map if (lightMapTexture != null) { this.ApplyLightMap(lightMapTexture); lightMapTexture.Dispose(); } } private void DrawTooltipItems(GameTime gameTime) { var pos = renderEnv.Camera.CameraToWorld(renderEnv.Input.MousePosition); var origin = renderEnv.Camera.Origin.ToPoint(); foreach (var item in mapData.Tooltips) { if (item.CharRect.Contains(pos) || item.Rect.Contains(pos)) { var center = new Vector2(item.Rect.Center.X - origin.X, item.Rect.Center.Y - origin.Y); tooltip.Draw(gameTime, renderEnv, item, center); } } } private void DrawFootholds(GameTime gameTime) { var color = MathHelper2.HSVtoColor((float)gameTime.TotalGameTime.TotalSeconds * 100 % 360, 1f, 1f); if (patchVisibility.FootHoldVisible) { var lines = new List(); foreach (LayerNode layer in this.mapData.Scene.Layers.Nodes) { var fhList = layer.Foothold.Nodes.OfType>() .Select(container => container.Item); foreach (var fh in fhList) { lines.Add(new Point(fh.X1, fh.Y1)); lines.Add(new Point(fh.X2, fh.Y2)); } } if (lines.Count > 0) { var meshItem = this.batcher.MeshPop(); meshItem.RenderObject = new LineListMesh(lines.ToArray(), color, 2); this.batcher.Draw(meshItem); this.batcher.MeshPush(meshItem); } } if (patchVisibility.LadderRopeVisible) { var lines = new List(); var ladderList = this.mapData.Scene.Fly.LadderRope.Slots.OfType(); foreach (var item in ladderList) { lines.Add(new Point(item.X, item.Y1)); lines.Add(new Point(item.X, item.Y2)); } if (lines.Count > 0) { var meshItem = this.batcher.MeshPop(); meshItem.RenderObject = new LineListMesh(lines.ToArray(), color, 3); this.batcher.Draw(meshItem); this.batcher.MeshPush(meshItem); } } if (patchVisibility.SkyWhaleVisible) { var lines = new List(); var skyWhaleList = this.mapData.Scene.Fly.SkyWhale.Slots.OfType(); foreach (var item in skyWhaleList) { foreach (var dx in new[] { 0, -item.Width / 2, item.Width / 2 }) { Point start = new Point(item.Start.X + dx, item.Start.Y); Point end = new Point(item.End.X + dx, item.End.Y); //画箭头 lines.Add(start); lines.Add(end); lines.Add(end); lines.Add(new Point(end.X - 5, end.Y + 8)); lines.Add(end); lines.Add(new Point(end.X + 5, end.Y + 8)); } } if (lines.Count > 0) { var meshItem = this.batcher.MeshPop(); meshItem.RenderObject = new LineListMesh(lines.ToArray(), color, 1); this.batcher.Draw(meshItem); this.batcher.MeshPush(meshItem); } } if (patchVisibility.IlluminantClusterPathVisible) { var lines = new List(); var illuminentClusterList = this.mapData.Scene.Fly.IlluminantCluster.Slots.OfType(); foreach (var item in illuminentClusterList) { foreach (var dx in new[] { 0, -item.Size / 2, item.Size / 2 }) { Point start = new Point(item.Start.X + dx, item.Start.Y); Point end = new Point(item.End.X + dx, item.End.Y); //画箭头 lines.Add(start); lines.Add(end); lines.Add(end); lines.Add(new Point(end.X - 5, end.Y + 8)); lines.Add(end); lines.Add(new Point(end.X + 5, end.Y + 8)); } } if (lines.Count > 0) { var meshItem = this.batcher.MeshPop(); meshItem.RenderObject = new LineListMesh(lines.ToArray(), color, 1); this.batcher.Draw(meshItem); this.batcher.MeshPush(meshItem); } } } private void DrawName(SceneItem item) { StringResult sr = null; MeshItem mesh = null; if (item is LifeItem) { var life = (LifeItem)item; switch (life.Type) { case LifeItem.LifeType.Mob: if (this.patchVisibility.MobNameVisible) { string lv = "Lv." + (life.LifeInfo?.level ?? 0); string name; if (this.StringLinker?.StringMob.TryGetValue(life.ID, out sr) ?? false) name = sr.Name; else name = life.ID.ToString(); //绘制怪物名称 mesh = batcher.MeshPop(); mesh.Position = new Vector2(life.X, life.Cy + 4); mesh.RenderObject = new TextMesh() { Align = Alignment.Center, ForeColor = Color.White, BackColor = new Color(Color.Black, 0.7f), Font = renderEnv.Fonts.MobNameFont, Padding = new Margins(2, 2, 2, 2), Text = name }; batcher.Draw(mesh); //绘制怪物等级 var nameRect = batcher.Measure(mesh)[0]; mesh.Position = new Vector2(nameRect.X - 2, nameRect.Y + 3); mesh.RenderObject = new TextMesh() { Align = Alignment.Far, ForeColor = Color.White, BackColor = new Color(Color.Black, 0.7f), Font = renderEnv.Fonts.MobLevelFont, Padding = new Margins(2, 1, 1, 1), Text = lv }; batcher.Draw(mesh); batcher.MeshPush(mesh); } break; case LifeItem.LifeType.Npc: if (this.patchVisibility.NpcNameVisible) { if (life.HideName) break; string name, desc; if (this.StringLinker?.StringNpc.TryGetValue(life.ID, out sr) ?? false) { name = sr.Name; desc = sr.Desc; } else { name = life.ID.ToString(); desc = null; } if (name != null) { mesh = batcher.MeshPop(); mesh.Position = new Vector2(life.X, life.Cy + 4); mesh.RenderObject = new TextMesh() { Align = Alignment.Center, ForeColor = Color.Yellow, BackColor = new Color(Color.Black, 0.7f), Font = renderEnv.Fonts.NpcNameFont, Padding = new Margins(2, 2, 2, 2), Text = name }; batcher.Draw(mesh); batcher.MeshPush(mesh); } if (desc != null) { mesh = batcher.MeshPop(); mesh.Position = new Vector2(life.X, life.Cy + 20); // temporarily ignore font name and size here. mesh.RenderObject = new TextMesh() { Align = Alignment.Center, ForeColor = life.CustomFont?.FontColor ?? Color.Yellow, BackColor = new Color(Color.Black, 0.7f), Font = renderEnv.Fonts.NpcNameFont, Padding = new Margins(2, 2, 2, 2), Text = desc }; batcher.Draw(mesh); batcher.MeshPush(mesh); } } break; } } } private void PrepareLightMap() { var mapLight = this.mapData.Light; var origin = this.renderEnv.Camera.Origin.ToPoint(); this.GraphicsDevice.Clear(mapLight.BackColor); this.lightRenderer.Begin(Matrix.CreateTranslation(new Vector3(-origin.X, -origin.Y, 0))); // render spot light foreach (var light2D in mapLight.Lights) { this.lightRenderer.DrawSpotLight(light2D); } // render texture light foreach(var container in GetSceneContainers(this.mapData.Scene)) { foreach (var item in container.Slots) { if (item is ObjItem obj && obj.Light && obj.View.Animator is FrameAnimator frameAni) { var frame = frameAni.CurrentFrame; this.lightRenderer.DrawTextureLight(frame.Texture, new Vector2(obj.X, obj.Y), frame.AtlasRect, frame.Origin.ToVector2(), obj.View.Flip, new Color(Color.White, frame.A0)); } } } this.lightRenderer.End(); } private void ApplyLightMap(Texture2D lightMapTexture) { this.renderEnv.Sprite.Begin(blendState: this.renderEnv.BlendStateMultiplyRGB); this.renderEnv.Sprite.Draw(lightMapTexture, Vector2.Zero, Color.White); this.renderEnv.Sprite.End(); } private IEnumerable GetSceneContainers(SceneNode node) { /* var container = node as ContainerNode; if (container != null) //暂时不考虑缩进z层递归合并 container下没有子节点 { yield return container; } else { foreach (var mesh in node.Nodes.SelectMany(child => GetSceneContainers(child))) { yield return mesh; } }*/ Stack sceneStack = new Stack(); Stack indices = new Stack(); SceneNode currNode = node; int i = 0; while (currNode != null) { var container = currNode as ContainerNode; if (container != null) { yield return container; goto _pop; } else { if (i < currNode.Nodes.Count) { var child = currNode.Nodes[i]; //push sceneStack.Push(currNode); indices.Push(i + 1); currNode = child; i = 0; continue; } else { goto _pop; } } _pop: if (sceneStack.Count > 0) { currNode = sceneStack.Pop(); i = indices.Pop(); } else { break; } continue; } } private IEnumerable> GetDrawableItems(MapScene scene) { var containers = GetSceneContainers(scene); var kvList = this.drawableItemsCache; foreach (var container in containers) { kvList.Clear(); foreach (var item in container.Slots) { var mesh = GetMesh(item); if (mesh != null) { kvList.Add(new KeyValuePair(item, mesh)); } if (item is IlluminantClusterItem) { if (patchVisibility.IlluminantClusterVisible) { foreach (var meshIlluminantCluster in GetMeshesIlluminantCluster((IlluminantClusterItem)item)) { kvList.Add(new KeyValuePair(item, meshIlluminantCluster)); } } } } kvList.Sort((kv1, kv2) => kv1.Value.CompareTo(kv2.Value)); foreach (var kv in kvList) { yield return kv; } } kvList.Clear(); } private MeshItem GetMesh(SceneItem item) { if (item.Tags != null && item.Tags.Any(tag => !patchVisibility.IsTagVisible(tag))) { return null; } switch (item) { case BackItem back: if (back.Quest.Exists(quest => !patchVisibility.IsQuestVisible(quest.ID, quest.State))) { return null; } if (back.IsFront ? patchVisibility.FrontVisible : patchVisibility.BackVisible) { return GetMeshBack(back); } break; case ObjItem obj: if (patchVisibility.ObjVisible && !obj.Light) { if (obj.Quest.Exists(quest => !patchVisibility.IsQuestVisible(quest.ID, quest.State))) { return null; } if (obj.Questex.Exists(questex => !patchVisibility.IsQuestVisible(questex.ID, questex.Key, questex.State))) { return null; } return GetMeshObj(obj); } break; case TileItem tile: if (patchVisibility.TileVisible) { return GetMeshTile(tile); } break; case LifeItem life: if ((life.Type == LifeItem.LifeType.Mob && patchVisibility.MobVisible) || (life.Type == LifeItem.LifeType.Npc && patchVisibility.NpcVisible)) { return GetMeshLife(life); } break; case PortalItem portal: if (patchVisibility.PortalVisible) { return GetMeshPortal(portal); } break; case ReactorItem reactor: if (patchVisibility.ReactorVisible) { return GetMeshReactor(reactor); } break; case ParticleItem particle: if (particle.Quest.Exists(quest => !patchVisibility.IsQuestVisible(quest.ID, quest.State))) { return null; } if (patchVisibility.EffectVisible) { return GetMeshParticle((ParticleItem)item); } break; } return null; } private MeshItem GetMeshBack(BackItem back) { //计算计算culling if (back.ScreenMode != 0 && back.ScreenMode != renderEnv.Camera.DisplayMode + 1) { return null; } //计算坐标 int cx = back.Cx; int cy = back.Cy; if ((back.TileMode & TileMode.BothTile) != 0 && (cx == 0 || cy == 0)) { Point renderSize = Point.Zero; switch (back.View.Animator) { case FrameAnimator frameAni: renderSize = frameAni.Data.GetBound().Size; break; case AnimationItem aniItem: // For spine animation, we don't know how to calculate the correct cx and cy renderSize = aniItem.Measure().Size; break; case MsCustomSprite msCustomSprite: renderSize = msCustomSprite.Size.ToPoint(); break; } if (cx == 0) cx = renderSize.X; if (cy == 0) cy = renderSize.Y; } Vector2 tileOff = new Vector2(cx, cy); Vector2 position = new Vector2(back.X, back.Y); //计算水平卷动 if ((back.TileMode & TileMode.ScrollHorizontal) != 0) { position.X += ((float)back.Rx * 5 * back.View.Time / 1000) % cx;// +this.Camera.Center.X * (100 - Math.Abs(this.rx)) / 100; } else //镜头移动比率偏移 { position.X += renderEnv.Camera.Center.X * (100 + back.Rx) / 100; } //计算垂直卷动 if ((back.TileMode & TileMode.ScrollVertical) != 0) { position.Y += ((float)back.Ry * 5 * back.View.Time / 1000) % cy;// +this.Camera.Center.Y * (100 - Math.Abs(this.ry)) / 100; } else //镜头移动比率偏移 { position.Y += (renderEnv.Camera.Center.Y) * (100 + back.Ry) / 100; } //y轴镜头调整 //if (back.TileMode == TileMode.None && renderEnv.Camera.WorldRect.Height > 600) // position.Y += (renderEnv.Camera.Height - 600) / 2; //取整 position.X = (float)Math.Floor(position.X); position.Y = (float)Math.Floor(position.Y); //计算tile Rectangle? tileRect = null; if (back.TileMode != TileMode.None) { var cameraRect = renderEnv.Camera.ClipRect; int l, t, r, b; if ((back.TileMode & TileMode.Horizontal) != 0 && cx > 0) { l = (int)Math.Floor((cameraRect.Left - position.X) / cx) - 1; r = (int)Math.Ceiling((cameraRect.Right - position.X) / cx) + 1; } else { l = 0; r = 1; } if ((back.TileMode & TileMode.Vertical) != 0 && cy > 0) { t = (int)Math.Floor((cameraRect.Top - position.Y) / cy) - 1; b = (int)Math.Ceiling((cameraRect.Bottom - position.Y) / cy) + 1; } else { t = 0; b = 1; } tileRect = new Rectangle(l, t, r - l, b - t); } //生成mesh var renderObj = GetRenderObject(back.View.Animator, back.Flip, back.Alpha); if (renderObj == null) { return null; } var mesh = batcher.MeshPop(); mesh.RenderObject = renderObj; mesh.Position = position; mesh.Z0 = 0; mesh.Z1 = back.Index; mesh.FlipX = back.Flip; mesh.TileRegion = tileRect; mesh.TileOffset = tileOff; return mesh; } private MeshItem GetMeshObj(ObjItem obj) { var renderObj = GetRenderObject(obj.View.Animator, flip: obj.View.Flip); if (renderObj == null) { return null; } var mesh = batcher.MeshPop(); mesh.RenderObject = renderObj; mesh.Position = new Vector2(obj.X, obj.Y); mesh.Z0 = obj.Z; mesh.Z1 = obj.Index; if (obj.MoveW != 0 || obj.MoveH != 0) { mesh.Position += GetMovingObjPos(obj); } mesh.FlipX = obj.View.Flip; return mesh; } private MeshItem GetMeshTile(TileItem tile) { var renderObj = GetRenderObject(tile.View.Animator); if (renderObj == null) { return null; } var mesh = batcher.MeshPop(); mesh.RenderObject = renderObj; mesh.Position = new Vector2(tile.X, tile.Y); mesh.Z0 = ((renderObj as Frame)?.Z ?? 0); mesh.Z1 = tile.Index; return mesh; } private MeshItem GetMeshLife(LifeItem life) { var renderObj = GetRenderObject(life.View.Animator); if (renderObj == null) { return null; } var mesh = batcher.MeshPop(); mesh.RenderObject = renderObj; mesh.Position = new Vector2(life.X, life.Cy); mesh.FlipX = life.Flip; mesh.Z0 = ((renderObj as Frame)?.Z ?? 0); mesh.Z1 = life.Index; return mesh; } private MeshItem GetMeshPortal(PortalItem portal) { var renderObj = GetRenderObject(portal.View.IsEditorMode ? portal.View.EditorAnimator : portal.View.Animator); if (renderObj == null) { return null; } var mesh = batcher.MeshPop(); mesh.RenderObject = renderObj; mesh.Position = new Vector2(portal.X, portal.Y); mesh.Z0 = ((renderObj as Frame)?.Z ?? 0); mesh.Z1 = portal.Index; return mesh; } private MeshItem[] GetMeshesIlluminantCluster(IlluminantClusterItem illuminantCluster) { var renderObj = GetRenderObject(illuminantCluster.StartView.Animator); if (renderObj == null) { return null; } var startMesh = batcher.MeshPop(); startMesh.RenderObject = renderObj; startMesh.Position = new Vector2(illuminantCluster.Start.X + illuminantCluster.Size / 2, illuminantCluster.Start.Y - illuminantCluster.Size / 2); startMesh.Z0 = ((renderObj as Frame)?.Z ?? 0); startMesh.Z1 = illuminantCluster.Index; renderObj = GetRenderObject(illuminantCluster.EndView.Animator); if (renderObj == null) { return null; } var endMesh = batcher.MeshPop(); endMesh.RenderObject = renderObj; endMesh.Position = new Vector2(illuminantCluster.End.X + illuminantCluster.Size / 2, illuminantCluster.End.Y - illuminantCluster.Size / 2); endMesh.Z0 = ((renderObj as Frame)?.Z ?? 0); endMesh.Z1 = illuminantCluster.Index; return new MeshItem[] { startMesh, endMesh }; } private MeshItem GetMeshReactor(ReactorItem reactor) { var renderObj = GetRenderObject(reactor.View.Animator); if (renderObj == null) { return null; } var mesh = batcher.MeshPop(); mesh.RenderObject = renderObj; mesh.Position = new Vector2(reactor.X, reactor.Y); mesh.FlipX = reactor.Flip; mesh.Z0 = ((renderObj as Frame)?.Z ?? 0); mesh.Z1 = reactor.Index; return mesh; } private MeshItem GetMeshParticle(ParticleItem particle) { var pSystem = particle.View?.ParticleSystem; if (pSystem == null) { return null; } Vector2 position; position.X = renderEnv.Camera.Center.X * (100 + particle.Rx) / 100; position.Y = renderEnv.Camera.Center.Y * (100 + particle.Ry) / 100; var mesh = batcher.MeshPop(); mesh.RenderObject = pSystem; mesh.Position = position; mesh.Z0 = particle.Z; return mesh; } private object GetRenderObject(object animator, bool flip = false, int alpha = 255) { if (animator is FrameAnimator frameAni) { var frame = frameAni.CurrentFrame; if (frame != null) { if (alpha < 255) //理论上应该返回一个新的实例 { frame.A0 = frame.A0 * alpha / 255; } return frame; } } else if (animator is SpineAnimatorV2 spineAniV2) { var skeleton = spineAniV2.Skeleton; if (skeleton != null) { if (alpha < 255) { skeleton.A = alpha / 255.0f; } return skeleton; } } else if (animator is SpineAnimatorV4 spineAniV4) { var skeleton = spineAniV4.Skeleton; if (skeleton != null) { if (alpha < 255) { skeleton.A = alpha / 255.0f; } return skeleton; } } else if (animator is StateMachineAnimator smAni) { return smAni.Data.GetMesh(); } else if (animator is MsCustomSprite msCustomSprite) { this.UpdateShaderConstant(msCustomSprite.Material); return msCustomSprite; } return null; } private Vector2 GetMovingObjPos(ObjItem obj) { double movingX = 0; double movingY = 0; double time = obj.View.Time; switch (obj.MoveType) { case 1: case 2: // line time *= Math.PI * 2 / obj.MoveP; movingX = obj.MoveW * Math.Cos(time); movingY = obj.MoveH * Math.Cos(time); break; case 3: // circle time *= Math.PI * 2 / obj.MoveP; movingX = obj.MoveW * Math.Cos(time); movingY = obj.MoveH * Math.Sin(time); break; case 6: case 7: case 8: int sign = -1; double freq = (double)(obj.MoveP + obj.MoveDelay) * 2; time = time % freq; if (time >= freq / 2) { time -= freq / 2; if (obj.MoveType == 8) obj.View.Flip = !obj.Flip; if (obj.MoveType != 6) sign = +1; } else { if (obj.MoveType == 8) obj.View.Flip = obj.Flip; } movingX = (Math.Min(1, Math.Max(-1, (time - obj.MoveDelay) * -2 / obj.MoveP + 1)) * sign + 1) / 2 * obj.MoveW; movingY = (Math.Min(1, Math.Max(-1, (time - obj.MoveDelay) * -2 / obj.MoveP + 1)) * sign + 1) / 2 * obj.MoveH; break; default: break; } return new Vector2((float)movingX, (float)movingY); } private void UpdateShaderConstant(ShaderMaterial shaderMaterial) { // we don't know the exact value that being used in the original client switch (shaderMaterial) { case LightPixelShaderMaterial light: light.PlayerPos = renderEnv.Camera.CameraToWorld(renderEnv.Input.MousePosition).ToVector2(); light.LightInnerRadius = 50f; light.LightOuterRadius = 200f; light.PlayerLightColor = Color.White.ToVector4(); light.TopColor = Color.White.ToVector4(); light.BottomColor = new Color(0.2f, 0.2f, 0.2f, 1f).ToVector4(); light.MinY = 4500; light.MaxY = 19500; break; case WaterFrontPixelShaderMaterial waterFront: waterFront.PlayerPos = renderEnv.Camera.CameraToWorld(renderEnv.Input.MousePosition).ToVector2(); waterFront.Factor1 = 1f; waterFront.MinY = 4500; waterFront.MaxY = 19500; waterFront.DistNoiseCenterPos = waterFront.PlayerPos; waterFront.Factor2 = 1f; break; } } } } ================================================ FILE: WzComparerR2.MapRender/FrmMapRender2.cs ================================================ using System; using System.Collections.Generic; using System.Text; using System.Linq; using System.Threading.Tasks; using System.Runtime.InteropServices; using WzComparerR2.WzLib; using WzComparerR2.Common; using WzComparerR2.MapRender.Config; using WzComparerR2.MapRender.Patches2; using WzComparerR2.MapRender.UI; using WzComparerR2.Rendering; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using Form = System.Windows.Forms.Form; using JLChnToZ.IMEHelper; #region USING_EK using EmptyKeys.UserInterface; using KeyBinding = EmptyKeys.UserInterface.Input.KeyBinding; using RelayCommand = EmptyKeys.UserInterface.Input.RelayCommand; using KeyCode = EmptyKeys.UserInterface.Input.KeyCode; using ModifierKeys = EmptyKeys.UserInterface.Input.ModifierKeys; using ServiceManager = EmptyKeys.UserInterface.Mvvm.ServiceManager; using WzComparerR2.MapRender.Effects; #endregion namespace WzComparerR2.MapRender { public partial class FrmMapRender2 : Game { public FrmMapRender2() { graphics = new GraphicsDeviceManager(this); graphics.DeviceCreated += Graphics_DeviceCreated; graphics.DeviceResetting += Graphics_DeviceResetting; graphics.GraphicsProfile = GraphicsProfile.HiDef; this.MaxElapsedTime = TimeSpan.MaxValue; this.IsFixedTimeStep = false; this.TargetElapsedTime = TimeSpan.FromSeconds(1.0 / 60); this.InactiveSleepTime = TimeSpan.FromSeconds(1.0 / 30); this.IsMouseVisible = true; this.Exiting += (o, e) => this.OnExiting(); this.Content = new WcR2ContentManager(this.Services); this.patchVisibility = new PatchVisibility(); this.patchVisibility.FootHoldVisible = false; this.patchVisibility.LadderRopeVisible = false; this.patchVisibility.SkyWhaleVisible = false; this.patchVisibility.IlluminantClusterPathVisible = false; var form = Form.FromHandle(this.Window.Handle) as Form; form.Load += Form_Load; form.GotFocus += Form_GotFocus; form.LostFocus += Form_LostFocus; form.FormClosing += Form_FormClosing; form.FormClosed += Form_FormClosed; this.imeHelper = new IMEHandler(this, true); this.Exiting += (o, e) => this.imeHelper.Dispose(); this.Disposed += (o, e) => this.imeHelper.Dispose(); GameExt.FixKeyboard(this); } private void Form_Load(object sender, EventArgs e) { var form = (Form)sender; form.StartPosition = System.Windows.Forms.FormStartPosition.Manual; form.SetDesktopLocation(0, 0); } private void Form_GotFocus(object sender, EventArgs e) { if (MapRenderConfig.Default.MuteOnLeaveFocus) { if (this.bgm != null) { this.bgm.Volume = 1; } } } private void Form_LostFocus(object sender, EventArgs e) { if (MapRenderConfig.Default.MuteOnLeaveFocus) { if (this.bgm != null) { this.bgm.Volume = 0; } } } private void Form_FormClosing(object sender, System.Windows.Forms.FormClosingEventArgs e) { GameExt.ReleaseKeyboard(this); } private void Form_FormClosed(object sender, System.Windows.Forms.FormClosedEventArgs e) { GameExt.EnsureGameExit(this); } private void Graphics_DeviceCreated(object sender, EventArgs e) { this.engine = new WcR2Engine(this.GraphicsDevice, graphics.PreferredBackBufferWidth, graphics.PreferredBackBufferHeight); WcR2Engine.FixEKBugs(); (this.engine.AssetManager as WcR2AssetManager).DefaultContentManager = this.Content as WcR2ContentManager; } private void Graphics_DeviceResetting(object sender, EventArgs e) { // fix DXGI error 0x887A0001 on gamewindow resizing WzComparerR2.Rendering.D2DFactory.Instance.ReleaseContext(graphics.GraphicsDevice); } public StringLinker StringLinker { get; set; } GraphicsDeviceManager graphics; Wz_Image mapImg; RenderEnv renderEnv; MapData mapData; ResourceLoader resLoader; MeshBatcher batcher; LightRenderer lightRenderer; PatchVisibility patchVisibility; bool prepareCapture; Task captureTask; Resolution resolution; float opacity; List allItems = new List(); List> drawableItemsCache = new List>(); MapRenderUIRoot ui; Tooltip2 tooltip; WcR2Engine engine; Music bgm; CoroutineManager cm; FpsCounter fpsCounter; readonly List attachedEvent = new List(); IMEHandler imeHelper; bool isUnloaded; bool isExiting; protected override void Initialize() { //init services this.Services.AddService(new Random()); this.Services.AddService(new ParticleRandom(this.Services.GetService())); this.Services.AddService(this.imeHelper); ServiceManager.Instance.AddService(this.imeHelper); //init components this.renderEnv = new RenderEnv(this, this.graphics); this.batcher = new MeshBatcher(this.GraphicsDevice) { CullingEnabled = true }; this.lightRenderer = new LightRenderer(this.GraphicsDevice); this.resLoader = new ResourceLoader(this.Services); this.resLoader.PatchVisibility = this.patchVisibility; this.ui = new MapRenderUIRoot(); this.BindingUIInput(); this.tooltip = new Tooltip2(this.Content); this.tooltip.StringLinker = this.StringLinker; this.cm = new CoroutineManager(this); this.cm.StartCoroutine(OnStart()); //entry this.Components.Add(cm); this.fpsCounter = new FpsCounter(this) { UseStopwatch = true }; this.Components.Add(fpsCounter); this.ApplySetting(); SwitchResolution(Resolution.Window_800_600); base.Initialize(); //init UI teleport this.ui.Teleport.Sl = this.StringLinker; } protected override void OnActivated(object sender, EventArgs args) { base.OnActivated(sender, args); } protected override void OnDeactivated(object sender, EventArgs args) { base.OnDeactivated(sender, args); } private void BindingUIInput() { //键盘事件 //切换分辨率 this.ui.InputBindings.Add(new KeyBinding(new RelayCommand(_ => SwitchResolution()), KeyCode.Enter, ModifierKeys.Alt)); //开关小地图 this.ui.InputBindings.Add(new KeyBinding(new RelayCommand(_ => this.ui.Minimap.Toggle()), KeyCode.M, ModifierKeys.None) { IsRepeatEnabled = true }); this.ui.InputBindings.Add(new KeyBinding(new RelayCommand(_ => this.ui.WorldMap.Toggle()), KeyCode.W, ModifierKeys.None) { IsRepeatEnabled = true }); //选项界面 this.ui.InputBindings.Add(new KeyBinding(new RelayCommand(_ => { var uiWnd = this.ui.Windows.OfType().FirstOrDefault(); if (uiWnd == null) { uiWnd = new UIOptions(); uiWnd.DataContext = new UIOptionsDataModel(); uiWnd.OK += UIOption_OK; uiWnd.Cancel += UIOption_Cancel; uiWnd.Visible += UiWnd_Visible; uiWnd.Visibility = EmptyKeys.UserInterface.Visibility.Visible; this.ui.Windows.Add(uiWnd); uiWnd.Parent = this.ui; } else { uiWnd.Toggle(); } }), KeyCode.Escape, ModifierKeys.None)); //截图 this.ui.InputBindings.Add(new KeyBinding(new RelayCommand(_ => { if (CanCapture()) prepareCapture = true; }), KeyCode.Scroll, ModifierKeys.None)); this.ui.InputBindings.Add(new KeyBinding(new RelayCommand(_ => { renderEnv.Camera.AdjustRectEnabled = !renderEnv.Camera.AdjustRectEnabled; }), KeyCode.U, ModifierKeys.Control)); //层隐藏 this.ui.InputBindings.Add(new KeyBinding(new RelayCommand(_ => this.patchVisibility.BackVisible = !this.patchVisibility.BackVisible), KeyCode.D1, ModifierKeys.Control)); this.ui.InputBindings.Add(new KeyBinding(new RelayCommand(_ => this.patchVisibility.ReactorVisible = !this.patchVisibility.ReactorVisible), KeyCode.D2, ModifierKeys.Control)); this.ui.InputBindings.Add(new KeyBinding(new RelayCommand(_ => this.patchVisibility.ObjVisible = !this.patchVisibility.ObjVisible), KeyCode.D3, ModifierKeys.Control)); this.ui.InputBindings.Add(new KeyBinding(new RelayCommand(_ => this.patchVisibility.TileVisible = !this.patchVisibility.TileVisible), KeyCode.D4, ModifierKeys.Control)); this.ui.InputBindings.Add(new KeyBinding(new RelayCommand(_ => this.patchVisibility.NpcVisible = !this.patchVisibility.NpcVisible), KeyCode.D5, ModifierKeys.Control)); this.ui.InputBindings.Add(new KeyBinding(new RelayCommand(_ => this.patchVisibility.MobVisible = !this.patchVisibility.MobVisible), KeyCode.D6, ModifierKeys.Control)); this.ui.InputBindings.Add(new KeyBinding(new RelayCommand(_ => { var visible = this.patchVisibility.FootHoldVisible; this.patchVisibility.FootHoldVisible = !visible; this.patchVisibility.LadderRopeVisible = !visible; this.patchVisibility.SkyWhaleVisible = !visible; this.patchVisibility.IlluminantClusterPathVisible = !visible; }), KeyCode.D7, ModifierKeys.Control)); this.ui.InputBindings.Add(new KeyBinding(new RelayCommand(_ => { var portals = this.mapData.Scene.Portals; if (!this.patchVisibility.PortalVisible) { this.patchVisibility.PortalVisible = true; this.patchVisibility.PortalInEditMode = false; foreach (var item in portals) { item.View.IsEditorMode = false; } this.patchVisibility.IlluminantClusterVisible = true; } else if (!this.patchVisibility.PortalInEditMode) { this.patchVisibility.PortalInEditMode = true; foreach (var item in portals) { item.View.IsEditorMode = true; } this.patchVisibility.IlluminantClusterVisible = false; } else { this.patchVisibility.PortalVisible = false; } }), KeyCode.D8, ModifierKeys.Control)); this.ui.InputBindings.Add(new KeyBinding(new RelayCommand(_ => this.patchVisibility.FrontVisible = !this.patchVisibility.FrontVisible), KeyCode.D9, ModifierKeys.Control)); this.ui.InputBindings.Add(new KeyBinding(new RelayCommand(_ => this.patchVisibility.EffectVisible = !this.patchVisibility.EffectVisible), KeyCode.D0, ModifierKeys.Control)); //移动操作 #region 移动操作 { //键盘移动 int boostMoveFlag = 0; var direction1 = Vector2.Zero; Action calcKeyboardMoveDir = dir => { if (dir.X != 0) { var preMove = dir.X * 10 * (boostMoveFlag != 0 ? 3 : 1); if (Math.Sign(preMove) * Math.Sign(direction1.X) == -1 && Math.Abs(direction1.X) <= Math.Abs(preMove)) { direction1.X = 0; } else { direction1.X += preMove; } } if (dir.Y != 0) { var preMove = dir.Y * 10 * (boostMoveFlag != 0 ? 3 : 1); if (Math.Sign(preMove) * Math.Sign(direction1.Y) == -1 && Math.Abs(direction1.Y) <= Math.Abs(preMove)) { direction1.Y = 0; } else { direction1.Y += preMove; } } }; //键盘动量减速 Action keyboardMoveSlowDown = () => { if (direction1.X > 2 || direction1.X < -2) direction1.X *= 0.98f; else direction1.X = 0; if (direction1.Y > 2 || direction1.Y < 2) direction1.Y *= 0.98f; else direction1.Y = 0; }; EmptyKeys.UserInterface.Input.KeyEventHandler keyEv; var keyApplied = new Dictionary(); keyEv = (o, e) => { if (EmptyKeys.UserInterface.Input.InputManager.Current.FocusedElement is EmptyKeys.UserInterface.Controls.Primitives.TextBoxBase) { return; } switch (e.Key) { case KeyCode.Up: calcKeyboardMoveDir(new Vector2(0, -1)); break; case KeyCode.Down: calcKeyboardMoveDir(new Vector2(0, 1)); break; case KeyCode.Left: calcKeyboardMoveDir(new Vector2(-1, 0)); break; case KeyCode.Right: calcKeyboardMoveDir(new Vector2(1, 0)); break; case KeyCode.LeftControl: boostMoveFlag |= 0x01; break; case KeyCode.RightControl: boostMoveFlag |= 0x02; break; default: return; } keyApplied[e.Key] = true; e.Handled = true; }; this.ui.PreviewKeyDown += keyEv; this.attachedEvent.Add(EventDisposable(keyEv, _ev => this.ui.PreviewKeyDown -= _ev)); keyEv = (o, e) => { switch (e.Key) { case KeyCode.Up: case KeyCode.Down: case KeyCode.Left: case KeyCode.Right: if (keyApplied.TryGetValue(e.Key, out bool pressed) && pressed) { keyApplied[e.Key] = false; e.Handled = true; } break; case KeyCode.LeftControl: boostMoveFlag &= ~0x01; break; case KeyCode.RightControl: boostMoveFlag &= ~0x02; break; } }; this.ui.PreviewKeyUp += keyEv; this.attachedEvent.Add(EventDisposable(keyEv, _ev => this.ui.PreviewKeyUp -= _ev)); //鼠标移动 bool isMouseDown = false; var direction2 = Vector2.Zero; Action calcMouseMoveDir = e => { var mousePos = e.GetPosition(); Vector2 vec = new Vector2(2 * mousePos.X / this.ui.Width - 1, 2 * mousePos.Y / this.ui.Height - 1); var distance = vec.Length(); if (distance >= 0.4f) { vec *= (distance - 0.4f) / distance; } else { vec = Vector2.Zero; } direction2 = vec * 20; }; EmptyKeys.UserInterface.Input.MouseEventHandler mouseEv; EmptyKeys.UserInterface.Input.MouseButtonEventHandler mouseBtnEv; mouseBtnEv = (o, e) => { if (e.ChangedButton == EmptyKeys.UserInterface.Input.MouseButton.Right) { isMouseDown = true; calcMouseMoveDir(e); } }; this.ui.MouseDown += mouseBtnEv; this.attachedEvent.Add(EventDisposable(mouseBtnEv, _ev => this.ui.MouseDown -= _ev)); mouseEv = (o, e) => { if (isMouseDown) { calcMouseMoveDir(e); } }; this.ui.MouseMove += mouseEv; this.attachedEvent.Add(EventDisposable(mouseEv, _ev => this.ui.MouseMove -= _ev)); mouseBtnEv = (o, e) => { if (e.ChangedButton == EmptyKeys.UserInterface.Input.MouseButton.Right) { isMouseDown = false; direction2 = Vector2.Zero; } }; this.ui.MouseUp += mouseBtnEv; this.attachedEvent.Add(EventDisposable(mouseBtnEv, _ev => this.ui.MouseUp -= _ev)); //更新事件 EventHandler ev = (o, e) => { this.renderEnv.Camera.Center += direction1 + direction2 * ((boostMoveFlag != 0) ? 3 : 1); keyboardMoveSlowDown(); }; this.ui.InputUpdated += ev; this.attachedEvent.Add(EventDisposable(ev, _ev => this.ui.InputUpdated -= _ev)); } #endregion //点击事件 var disposable = UIHelper.RegisterClickEvent(this.ui.ContentControl, (sender, point) => { int x = (int)point.X; int y = (int)point.Y; var mouseTarget = this.allItems.Reverse().FirstOrDefault(item => { return item.rect.Contains(x, y) && (item.item is PortalItem || item.item is IlluminantClusterItem || item.item is ReactorItem); }); return mouseTarget.item; }, this.OnSceneItemClick); this.attachedEvent.Add(disposable); this.ui.InputBindings.Add(new KeyBinding(new RelayCommand(_ => { if (this.ui.Visibility == Visibility.Visible) { this.ui.ChatBox.TextBoxChat.Focus(); } }), KeyCode.Enter, ModifierKeys.None)); this.ui.InputBindings.Add(new KeyBinding(new RelayCommand(_ => this.ui.ChatBox.Toggle()), KeyCode.Oem3, ModifierKeys.None)); this.ui.WorldMap.MapSpotClick += WorldMap_MapSpotClick; this.ui.Teleport.SelectedMapGo += Teleport_SelectedMapGo; this.ui.ChatBox.TextBoxChat.TextSubmit += ChatBox_TextSubmit; } private void UIOption_OK(object sender, EventArgs e) { var wnd = sender as UIOptions; var data = wnd.DataContext as UIOptionsDataModel; SaveOptionData(data); wnd.Hide(); ApplySetting(); } private void UIOption_Cancel(object sender, EventArgs e) { var wnd = sender as UIOptions; wnd.Hide(); } private void UiWnd_Visible(object sender, RoutedEventArgs e) { var wnd = sender as UIOptions; var data = wnd.DataContext as UIOptionsDataModel; LoadOptionData(data); } private void WorldMap_MapSpotClick(object sender, UIWorldMap.MapSpotEventArgs e) { int mapID = e.MapID; var callback = new EmptyKeys.UserInterface.Input.RelayCommand(r => { if (r == MessageBoxResult.OK) { this.MoveToPortal(mapID, "sp"); } }); StringResult sr = null; this.StringLinker?.StringMap.TryGetValue(mapID, out sr); var message = string.Format("是否传送到地图\r\n{0} ({1})?", sr?.Name ?? "null", mapID); MessageBox.Show(message, "提示", MessageBoxButton.OKCancel, callback, false); } private void Teleport_SelectedMapGo(object sender, UITeleport.SelectedMapGoEventArgs e) { int mapID = e.MapID; this.MoveToPortal(mapID, "sp"); } private void ChatBox_TextSubmit(object sender, TextEventArgs e) { if (!string.IsNullOrWhiteSpace(e.Text)) { if (e.Text.StartsWith("/")) { ChatCommand(e.Text); } else { this.ui.ChatBox.AppendTextNormal("MapRender: " + e.Text); } } } private async void ChatCommand(string command) { string[] arguments = command.Split((char[])null, StringSplitOptions.RemoveEmptyEntries); if (arguments.Length <= 0) { return; } switch (arguments[0].ToLower()) { case "/help": case "/?": this.ui.ChatBox.AppendTextHelp(@"/help 显示帮助。"); this.ui.ChatBox.AppendTextHelp(@"/map (mapID) 跳转地图。"); this.ui.ChatBox.AppendTextHelp(@"/back 回到上一地图。"); this.ui.ChatBox.AppendTextHelp(@"/home 回城。"); this.ui.ChatBox.AppendTextHelp(@"/history [maxCount] 查看历史地图。"); this.ui.ChatBox.AppendTextHelp(@"/minimap 设置迷你地图状态。"); this.ui.ChatBox.AppendTextHelp(@"/scene 设置地图场景显示状态。"); this.ui.ChatBox.AppendTextHelp(@"/quest 任务设置"); this.ui.ChatBox.AppendTextHelp(@"/questex 设置任务Key的值"); this.ui.ChatBox.AppendTextHelp(@"/multibgm 多重BGM设置"); break; case "/map": int toMapID; if (arguments.Length > 1 && Int32.TryParse(arguments[1], out toMapID) && toMapID > -1) { this.MoveToPortal(toMapID, "sp"); } else { this.ui.ChatBox.AppendTextSystem($"缺少地图ID。"); } break; case "/back": if (this.viewHistory.Count > 0) { this.MoveToLastMap(); } else { this.ui.ChatBox.AppendTextSystem($"前面没有地图。"); } break; case "/home": var retMapID = this.mapData?.ReturnMap; if (retMapID == null || retMapID == 999999999) { this.ui.ChatBox.AppendTextSystem($"回不到那里去。"); } else { this.MoveToPortal(retMapID, "sp"); } break; case "/history": int historyCount; if (!(arguments.Length > 1 && Int32.TryParse(arguments[1], out historyCount) && historyCount > 0)) { historyCount = 5; } this.ui.ChatBox.AppendTextHelp($"历史地图:({this.viewHistory.Count})"); var node = this.viewHistory.Last; while (node != null && historyCount > 0) { StringResult sr = null; if (node.Value != null && this.StringLinker != null) { this.StringLinker.StringMap.TryGetValue(node.Value.MapID, out sr); } this.ui.ChatBox.AppendTextHelp($" {sr?.Name ?? "(null)"}({node.Value.MapID})"); node = node.Previous; historyCount--; } break; case "/minimap": var canvasList = this.mapData?.MiniMap?.ExtraCanvas; switch (arguments.ElementAtOrDefault(1)) { case "list": this.ui.ChatBox.AppendTextHelp($"minimap: {string.Join(", ", canvasList?.Keys)}"); break; case "set": string canvasName = arguments.ElementAtOrDefault(2); if (canvasList != null && canvasList.TryGetValue(canvasName, out Texture2D canvas)) { this.ui.Minimap.MinimapCanvas = engine.Renderer.CreateTexture(canvas); this.ui.ChatBox.AppendTextHelp($"设置迷你地图:{canvasName}"); } else { this.ui.ChatBox.AppendTextSystem($"找不到迷你地图:{canvasName}"); } break; default: this.ui.ChatBox.AppendTextHelp(@"/minimap list 显示所有迷你地图名称。"); this.ui.ChatBox.AppendTextHelp(@"/minimap set (canvasName) 设置迷你地图。"); break; } break; case "/scene": switch (arguments.ElementAtOrDefault(1)) { case "tag": switch (arguments.ElementAtOrDefault(2)) { case "list": var mapTags = GetSceneContainers(this.mapData?.Scene) .SelectMany(container => container.Slots) .Where(sceneItem => sceneItem.Tags != null && sceneItem.Tags.Length > 0) .SelectMany(sceneItem => sceneItem.Tags) .Distinct() .OrderBy(tag => tag) .ToList(); this.ui.ChatBox.AppendTextHelp($"当前地图tags: {string.Join(", ", mapTags)}"); break; case "info": var visibleTags = this.patchVisibility.TagsVisible.Where(kv => kv.Value).Select(kv => kv.Key).ToList(); var hiddenTags = this.patchVisibility.TagsVisible.Where(kv => !kv.Value).Select(kv => kv.Key).ToList(); this.ui.ChatBox.AppendTextHelp($"默认tag显示状态: {this.patchVisibility.DefaultTagVisible}"); this.ui.ChatBox.AppendTextHelp($"显示tags: {string.Join(", ", visibleTags)}"); this.ui.ChatBox.AppendTextHelp($"隐藏tags: {string.Join(", ", hiddenTags)}"); break; case "show": string[] tags = arguments.Skip(3).ToArray(); if (tags.Length > 0) { foreach (var tag in tags) { this.patchVisibility.SetTagVisible(tag, true); } this.ui.ChatBox.AppendTextHelp($"显示tag: {string.Join(", ", tags)}"); } else { this.ui.ChatBox.AppendTextSystem("没有输入tagName。"); } break; case "hide": tags = arguments.Skip(3).ToArray(); if (tags.Length > 0) { foreach (var tag in tags) { this.patchVisibility.SetTagVisible(tag, false); } this.ui.ChatBox.AppendTextHelp($"隐藏tag: {string.Join(", ", tags)}"); } else { this.ui.ChatBox.AppendTextSystem("没有输入tagName。"); } break; case "reset": tags = arguments.Skip(3).ToArray(); if (tags.Length > 0) { this.patchVisibility.ResetTagVisible(tags); this.ui.ChatBox.AppendTextHelp($"重置tag: {string.Join(", ", tags)}"); } else { this.ui.ChatBox.AppendTextSystem("没有输入tagName。"); } break; case "reset-all": this.patchVisibility.ResetTagVisible(); this.ui.ChatBox.AppendTextHelp($"重置已设置tag。"); break; case "set-default": if (bool.TryParse(arguments.ElementAtOrDefault(3), out bool isVisible)) { this.patchVisibility.DefaultTagVisible = isVisible; this.ui.ChatBox.AppendTextHelp($"设置tag默认显示状态:{isVisible}"); } else { this.ui.ChatBox.AppendTextSystem("参数错误。"); } break; default: this.ui.ChatBox.AppendTextHelp(@"/scene tag list 获取场景中所有物体的tag。"); this.ui.ChatBox.AppendTextHelp(@"/scene tag info 获取当前自定义显示状态。"); this.ui.ChatBox.AppendTextHelp(@"/scene tag show (tagName)... 显示tagName的物体。"); this.ui.ChatBox.AppendTextHelp(@"/scene tag hide (tagName)... 隐藏tagName的物体。"); this.ui.ChatBox.AppendTextHelp(@"/scene tag reset (tagName)... 重置指定tagName的显示状态。"); this.ui.ChatBox.AppendTextHelp(@"/scene tag reset-all 重置所有物体为显示状态。"); this.ui.ChatBox.AppendTextHelp(@"/scene tag set-default (true/false) 设置所有tag的默认显示状态。"); break; } break; default: this.ui.ChatBox.AppendTextHelp(@"/scene tag 设置tag相关的显示状态。"); break; } break; case "/multibgm": switch (arguments.ElementAtOrDefault(1)) { case "list": if (!string.IsNullOrEmpty(this.mapData.Bgm)) { var path = new List() { "Sound" }; path.AddRange(this.mapData.Bgm.Split('/')); path[1] += ".img"; var bgmNode = PluginBase.PluginManager.FindWz(string.Join("\\", path)); var subNodes = bgmNode?.Nodes ?? new Wz_Node.WzNodeCollection(null); this.ui.ChatBox.AppendTextHelp($"多重BGM: {subNodes.Count}"); foreach (Wz_Node subNode in subNodes) { this.ui.ChatBox.AppendTextHelp($" {subNode.Text}"); } } else { this.ui.ChatBox.AppendTextHelp($"多重BGM: 0"); } break; case "set": string bgmName = string.Join(" ", arguments.Skip(2)); Music multiBgm = LoadBgm(this.mapData, bgmName); if (multiBgm != null) { this.ui.ChatBox.AppendTextSystem($"多重BGM已更改为{bgmName}。"); Task bgmTask = null; bool willSwitchBgm = this.bgm != multiBgm; if (willSwitchBgm && this.bgm != null) //准备切换 { bgmTask = FadeOut(this.bgm, 1000); } if (bgmTask != null) { await bgmTask; } this.bgm = multiBgm; if (willSwitchBgm && this.bgm != null) { bgmTask = FadeIn(this.bgm, 1000); } } else { this.ui.ChatBox.AppendTextHelp($"请输入正确的多重BGM名称"); } break; default: this.ui.ChatBox.AppendTextHelp(@"/multibgm list 显示多重BGM列表"); this.ui.ChatBox.AppendTextHelp(@"/multibgm set (multiBgm) 播放指定的多重BGM"); break; } break; case "/quest": switch (arguments.ElementAtOrDefault(1)) { case "list": List questList = this?.mapData.Scene.Back.Slots.SelectMany(item => ((BackItem)item).Quest) .Concat(this?.mapData.Scene.Layers.Nodes.SelectMany(l => ((LayerNode)l).Obj.Slots.SelectMany(item => ((ObjItem)item).Quest))) .Concat(this?.mapData.Scene.Npcs.SelectMany(item => item.Quest)) .Concat(this?.mapData.Scene.Front.Slots.SelectMany(item => ((BackItem)item).Quest)) .Concat(this?.mapData.Scene.Effect.Slots.Where(item => item is ParticleItem).SelectMany(item => ((ParticleItem)item).Quest)) .Concat(this?.mapData.Scene.Effect.Slots.Where(item => item is ParticleItem).SelectMany(item => ((ParticleItem)item).SubItems).SelectMany(item => item.Quest)) .Distinct().ToList(); this.ui.ChatBox.AppendTextHelp($"相关任务数量:({questList.Count()})"); foreach (QuestInfo item in questList) { Wz_Node questInfoNode = PluginBase.PluginManager.FindWz($@"Quest\QuestData\{item.ID}.img\QuestInfo") ?? PluginBase.PluginManager.FindWz($@"Quest\QuestInfo.img\{item.ID}"); string questName = questInfoNode?.Nodes["name"].GetValueEx(null) ?? "null"; this.ui.ChatBox.AppendTextHelp($" {questName}({item.ID}) / {item.State}"); } break; case "set": if (Int32.TryParse(arguments.ElementAtOrDefault(2), out int questID) && questID > -1 && Int32.TryParse(arguments.ElementAtOrDefault(3), out int questState) && questState >= -1 && questState <= 2) { this.patchVisibility.SetQuestVisible(questID, questState); this.mapData.PreloadResource(resLoader); Wz_Node questInfoNode = PluginBase.PluginManager.FindWz($@"Quest\QuestData\{questID}.img\QuestInfo") ?? PluginBase.PluginManager.FindWz($@"Quest\QuestInfo.img\{questID}"); string questName = questInfoNode?.Nodes["name"].GetValueEx(null) ?? "null"; this.ui.ChatBox.AppendTextSystem($"{questName}({questID}) 的状态已更改为 {questState}。"); } else { this.ui.ChatBox.AppendTextSystem($"请输入正确的任务状态。"); } break; default: this.ui.ChatBox.AppendTextHelp(@"/quest list 查看相关任务列表"); this.ui.ChatBox.AppendTextHelp(@"/quest set (questID) (questState) 设置任务的状态"); break; } break; case "/questex": switch (arguments.ElementAtOrDefault(1)) { case "list": List questList = this?.mapData.Scene.Layers.Nodes.SelectMany(l => ((LayerNode)l).Obj.Slots.SelectMany(item => ((ObjItem)item).Questex)) .Distinct().ToList(); this.ui.ChatBox.AppendTextHelp($"相关任务Key的数量:({questList.Count()})"); foreach (QuestExInfo item in questList) { Wz_Node questInfoNode = PluginBase.PluginManager.FindWz($@"Quest\QuestData\{item.ID}.img\QuestInfo") ?? PluginBase.PluginManager.FindWz($@"Quest\QuestInfo.img\{item.ID}"); string questName = questInfoNode?.Nodes["name"].GetValueEx(null) ?? "null"; this.ui.ChatBox.AppendTextHelp($" {questName}({item.ID}) / Key:{item.Key}, Value:{item.State}"); } break; case "set": string qkey = arguments.ElementAtOrDefault(3); if (Int32.TryParse(arguments.ElementAtOrDefault(2), out int questID) && questID > -1 && Int32.TryParse(arguments.ElementAtOrDefault(4), out int questState) && questState >= -1 && qkey != null) { this.patchVisibility.SetQuestVisible(questID, qkey, questState); this.mapData.PreloadResource(resLoader); Wz_Node questInfoNode = PluginBase.PluginManager.FindWz($@"Quest\QuestData\{questID}.img\QuestInfo") ?? PluginBase.PluginManager.FindWz($@"Quest\QuestInfo.img\{questID}"); string questName = questInfoNode?.Nodes["name"].GetValueEx(null) ?? "null"; this.ui.ChatBox.AppendTextSystem($"{questName}({questID}, Key={qkey}) 的状态已更改为 {questState}。"); } else { this.ui.ChatBox.AppendTextSystem($"请输入正确的任务状态。"); } break; default: this.ui.ChatBox.AppendTextHelp(@"/questex list 查看相关任务Key列表"); this.ui.ChatBox.AppendTextHelp(@"/questex set (questID) (key) (questState) 设置任务Key的状态"); break; } break; default: this.ui.ChatBox.AppendTextSystem($"不支持{arguments[0]}命令。"); break; } } protected override void LoadContent() { base.LoadContent(); this.ui.LoadContent(this.Content); this.renderEnv.Fonts.LoadContent(this.Content); this.isUnloaded = false; } protected override void Update(GameTime gameTime) { base.Update(gameTime); } protected override void Draw(GameTime gameTime) { opacity = MathHelper.Clamp(opacity, 0f, 1f); if (opacity <= 0) { this.GraphicsDevice.Clear(Color.Black); } else { if (prepareCapture) { Capture(gameTime); } this.GraphicsDevice.Clear(Color.Black); if (this.mapData != null) { DrawScene(gameTime); DrawTooltipItems(gameTime); } this.ui.Draw(gameTime.ElapsedGameTime.TotalMilliseconds); this.tooltip.Draw(gameTime, renderEnv); if (opacity < 1f) { this.renderEnv.Sprite.Begin(blendState: BlendState.NonPremultiplied); var rect = new Rectangle(0, 0, this.renderEnv.Camera.Width, this.renderEnv.Camera.Height); this.renderEnv.Sprite.FillRectangle(rect, new Color(Color.Black, 1 - opacity)); this.renderEnv.Sprite.End(); } } base.Draw(gameTime); } #region 截图相关 private bool CanCapture() { return (captureTask == null || captureTask.IsCompleted) && !prepareCapture; } private void Capture(GameTime gameTime) { if (this.mapData == null) { prepareCapture = false; return; } var oldTarget = GraphicsDevice.GetRenderTargets(); //检查显卡支持纹理大小 var maxTextureWidth = 4096; var maxTextureHeight = 4096; Rectangle oldRect = this.renderEnv.Camera.WorldRect; int width = Math.Min(oldRect.Width, maxTextureWidth); int height = Math.Min(oldRect.Height, maxTextureHeight); this.renderEnv.Camera.UseWorldRect = true; var target2d = new RenderTarget2D(this.GraphicsDevice, width, height, false, SurfaceFormat.Bgra32, DepthFormat.None, 0, RenderTargetUsage.PreserveContents); PngEffect pngEffect = null; Color bgColor = Color.Black; var config = MapRenderConfig.Default; if (ColorWConverter.TryParse(config?.ScreenshotBackgroundColor?.Value, out var colorW)) { bgColor = new Color(colorW.PackedValue); } Color bgColorPMA = bgColor.A switch { 255 => bgColor, 0 => Color.Transparent, _ => Color.FromNonPremultiplied(bgColor.ToVector4()), }; //计算一组截图区 int horizonBlock = (int)Math.Ceiling(1.0 * oldRect.Width / width); int verticalBlock = (int)Math.Ceiling(1.0 * oldRect.Height / height); byte[,][] picBlocks = new byte[horizonBlock, verticalBlock][]; for (int j = 0; j < verticalBlock; j++) { for (int i = 0; i < horizonBlock; i++) { //计算镜头区域 this.renderEnv.Camera.WorldRect = new Rectangle( oldRect.X + i * width, oldRect.Y + j * height, width, height); //绘制 GraphicsDevice.SetRenderTarget(target2d); GraphicsDevice.Clear(bgColorPMA); DrawScene(gameTime); //保存 if (bgColor.A == 255) { Texture2D t2d = target2d; byte[] data = new byte[target2d.Width * target2d.Height * 4]; t2d.GetData(data); picBlocks[i, j] = data; } else { if (pngEffect == null) { pngEffect = new PngEffect(this.GraphicsDevice); pngEffect.AlphaMixEnabled = false; } using var texture = new RenderTarget2D(this.GraphicsDevice, width, height, false, SurfaceFormat.Bgra32, DepthFormat.None); using var sb = new SpriteBatch(this.GraphicsDevice); this.GraphicsDevice.SetRenderTarget(texture); this.GraphicsDevice.Clear(Color.Transparent); sb.Begin(SpriteSortMode.Immediate, BlendState.Opaque, SamplerState.LinearClamp, null, null, pngEffect, null); sb.Draw(target2d, Vector2.Zero, Color.White); sb.End(); byte[] data = new byte[texture.Width * texture.Height * 4]; texture.GetData(data); picBlocks[i, j] = data; } } } pngEffect?.Dispose(); target2d.Dispose(); this.renderEnv.Camera.WorldRect = oldRect; this.renderEnv.Camera.UseWorldRect = false; GraphicsDevice.SetRenderTargets(oldTarget); prepareCapture = false; captureTask = Task.Factory.StartNew(() => SaveTexture(picBlocks, oldRect.Width, oldRect.Height, width, height, bgColor.A == 255) ); } private void SaveTexture(byte[,][] picBlocks, int mapWidth, int mapHeight, int blockWidth, int blockHeight, bool resetAlphaChannel) { //组装 byte[] mapData = new byte[mapWidth * mapHeight * 4]; for (int j = 0; j < picBlocks.GetLength(1); j++) { for (int i = 0; i < picBlocks.GetLength(0); i++) { byte[] data = picBlocks[i, j]; Rectangle blockRect = new Rectangle(); blockRect.X = i * blockWidth; blockRect.Y = j * blockHeight; blockRect.Width = Math.Min(mapWidth - blockRect.X, blockWidth); blockRect.Height = Math.Min(mapHeight - blockRect.Y, blockHeight); int length = blockRect.Width * 4; if (blockRect.X == 0 && blockRect.Width == mapWidth) //整块复制 { int startIndex = mapWidth * 4 * blockRect.Y; Buffer.BlockCopy(data, 0, mapData, startIndex, blockRect.Width * blockRect.Height * 4); } else //逐行扫描 { int offset = 0; for (int y = blockRect.Top, y0 = blockRect.Bottom; y < y0; y++) { int startIndex = (y * mapWidth + blockRect.X) * 4; Buffer.BlockCopy(data, offset, mapData, startIndex, length); offset += blockWidth * 4; } } } } if (resetAlphaChannel) { for (int i = 0; i < mapData.Length; i += 4) { mapData[i + 3] = 255; } } try { using System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap( mapWidth, mapHeight, mapWidth * 4, System.Drawing.Imaging.PixelFormat.Format32bppArgb, Marshal.UnsafeAddrOfPinnedArrayElement(mapData, 0)); string outputFileName = DateTime.Now.ToString("yyyyMMddHHmmssfff") + "_" + (this.mapData?.ID ?? 0).ToString("D9") + ".png"; bitmap.Save(outputFileName, System.Drawing.Imaging.ImageFormat.Png); this.cm.StartCoroutine(cm.Post((v) => { v.ui.ChatBox.AppendTextHelp($"截图保存到文件{v.outputFileName}"); }, new { this.ui, outputFileName })); } catch { } } #endregion #region 配置文件相关 private void ApplySetting() { var config = MapRenderConfig.Default; Music.GlobalVolume = config.Volume; this.renderEnv.Camera.AdjustRectEnabled = config.ClipMapRegion; if (config.TopBarVisible) { this.ui.TopBar.Show(); } else { this.ui.TopBar.Hide(); } this.ui.Minimap.CameraRegionVisible = config.Minimap_CameraRegionVisible; this.ui.WorldMap.UseImageNameAsInfoName = config.WorldMap_UseImageNameAsInfoName; this.batcher.D2DEnabled = config.UseD2dRenderer; (this.Content as WcR2ContentManager).UseD2DFont = config.UseD2dRenderer; } private void LoadOptionData(UIOptionsDataModel model) { var config = MapRenderConfig.Default; model.SelectedFont = config.DefaultFontIndex; model.Volume = Music.GlobalVolume; model.MuteOnLeaveFocus = config.MuteOnLeaveFocus; model.ClipMapRegion = renderEnv.Camera.AdjustRectEnabled; model.UseD2dRenderer = config.UseD2dRenderer; model.NpcNameVisible = this.patchVisibility.NpcNameVisible; model.MobNameVisible = this.patchVisibility.MobNameVisible; model.TopBarVisible = this.ui.TopBar.Visibility == EmptyKeys.UserInterface.Visibility.Visible; model.ScreenshotBackgroundColor = config.ScreenshotBackgroundColor; model.Minimap_CameraRegionVisible = this.ui.Minimap.CameraRegionVisible; model.WorldMap_UseImageNameAsInfoName = this.ui.WorldMap.UseImageNameAsInfoName; } private void SaveOptionData(UIOptionsDataModel model) { WzComparerR2.Config.ConfigManager.Reload(); var config = MapRenderConfig.Default; config.DefaultFontIndex = model.SelectedFont; config.Volume = model.Volume; config.MuteOnLeaveFocus = model.MuteOnLeaveFocus; config.ClipMapRegion = model.ClipMapRegion; config.UseD2dRenderer = model.UseD2dRenderer; this.patchVisibility.NpcNameVisible = model.NpcNameVisible; this.patchVisibility.MobNameVisible = model.MobNameVisible; config.TopBarVisible = model.TopBarVisible; config.ScreenshotBackgroundColor = model.ScreenshotBackgroundColor; config.Minimap_CameraRegionVisible = model.Minimap_CameraRegionVisible; config.WorldMap_UseImageNameAsInfoName = model.WorldMap_UseImageNameAsInfoName; WzComparerR2.Config.ConfigManager.Save(); } #endregion protected override void UnloadContent() { base.UnloadContent(); if (!this.isUnloaded) { this.resLoader?.Unload(); this.ui?.UnloadContents(); this.Content.Unload(); this.imeHelper?.Dispose(); this.bgm = null; this.mapImg = null; this.mapData = null; this.isUnloaded = true; } } private void OnExiting() { if (this.isExiting) { return; } this.batcher?.Dispose(); this.batcher = null; this.renderEnv?.Dispose(); this.renderEnv = null; this.lightRenderer?.Dispose(); this.lightRenderer = null; this.engine = null; foreach (var disposable in this.attachedEvent) { disposable.Dispose(); } this.attachedEvent.Clear(); this.ui?.InputBindings.Clear(); GameExt.RemoveKeyboardEvent(this); GameExt.RemoveMouseStateCache(); WcR2Engine.Unload(); ServiceManager.Instance.RemoveService(); this.isExiting = true; } protected override void Dispose(bool disposing) { if (disposing) { this.UnloadContent(); this.OnExiting(); } base.Dispose(disposing); } private void SwitchResolution() { var r = (Resolution)(((int)this.resolution + 1) % 4); SwitchResolution(r); } private void SwitchResolution(Resolution r) { Form gameWindow = (Form)Form.FromHandle(this.Window.Handle); switch (r) { case Resolution.Window_800_600: case Resolution.Window_1024_768: case Resolution.Window_1366_768: gameWindow.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; break; case Resolution.WindowFullScreen: gameWindow.SetDesktopLocation(0, 0); gameWindow.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None; break; default: r = Resolution.Window_800_600; goto case Resolution.Window_800_600; } this.resolution = r; this.renderEnv.Camera.DisplayMode = (int)r; this.ui.Width = this.renderEnv.Camera.Width; this.ui.Height = this.renderEnv.Camera.Height; engine.Renderer.ResetNativeSize(); } private IDisposable EventDisposable(TDelegate arg, Action action) { return new Disposable(action, arg); } enum Resolution { Window_800_600 = 0, Window_1024_768 = 1, Window_1366_768 = 2, WindowFullScreen = 3, } struct ItemRect { public SceneItem item; public Rectangle rect; } class Disposable : IDisposable { public Disposable(Action action, TDelegate arg) { this.Action = action; this.Arg = arg; } public readonly Action Action; public readonly TDelegate Arg; public void Dispose() { this.Action?.Invoke(this.Arg); } } } } ================================================ FILE: WzComparerR2.MapRender/GameExt.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using SharpDX; using SharpDX.RawInput; using SharpDX.Multimedia; using Microsoft.Xna.Framework; using System.Windows.Forms; using System.Reflection; using System.Runtime.InteropServices; namespace WzComparerR2.MapRender { static class GameExt { public static void FixKeyboard(Game game) { IntPtr hWnd = game.Window.Handle; Device.RegisterDevice(UsagePage.Generic, UsageId.GenericKeyboard, DeviceFlags.None, hWnd, RegisterDeviceOptions.Default); if(!filterCache.ContainsKey(hWnd)) { var filter = new RawInputMessageFilter(); filterCache[hWnd] = filter; MessageFilterHook.AddMessageFilter(hWnd, filter); } } public static void ReleaseKeyboard(Game game) { if (game == null || game.Window == null) { return; } RawInputMessageFilter filter; IntPtr hWnd = game.Window.Handle; if (filterCache.TryGetValue(hWnd, out filter)) { MessageFilterHook.RemoveMessageFilter(hWnd, filter); filterCache.Remove(hWnd); } } public static void RemoveKeyboardEvent(Game game) { if (game == null || game.Window == null) { return; } var fieldInfo = typeof(Device).GetField("KeyboardInput", BindingFlags.Static | BindingFlags.NonPublic); var value = (EventHandler)fieldInfo.GetValue(null); var methodInfo = game.Window.GetType().GetMethod("OnRawKeyEvent", BindingFlags.Instance | BindingFlags.NonPublic); if (value != null) { var fn = (EventHandler)Delegate.CreateDelegate(typeof(EventHandler), game.Window, methodInfo); value -= fn; fieldInfo.SetValue(null, value); } } public static void RemoveMouseStateCache() { var fieldInfo = typeof(Microsoft.Xna.Framework.Input.Mouse).GetField("PrimaryWindow", BindingFlags.Static | BindingFlags.NonPublic); if (fieldInfo != null) { fieldInfo.SetValue(null, null); } fieldInfo = typeof(Microsoft.Xna.Framework.Input.Mouse).GetField("Window", BindingFlags.Static | BindingFlags.NonPublic); if (fieldInfo != null) { fieldInfo.SetValue(null, null); } fieldInfo = typeof(Microsoft.Xna.Framework.Input.Touch.TouchPanel).GetField("PrimaryWindow", BindingFlags.Static | BindingFlags.NonPublic); if (fieldInfo != null) { fieldInfo.SetValue(null, null); } } public static void EnsureGameExit(Game game) { var tid = GetCurrentThreadId(); bool success = PostThreadMessage(tid, WM_QUIT, IntPtr.Zero, IntPtr.Zero); } public static T GetService(this IServiceProvider services) where T : class { return services.GetService(typeof(T)) as T; } private static Dictionary filterCache = new Dictionary(); class RawInputMessageFilter : IMessageFilter { public virtual bool PreFilterMessage(ref Message m) { if (m.Msg == 0xff) Device.HandleMessage(m.LParam, m.HWnd); return false; } } [DllImport("kernel32")] static extern int GetCurrentThreadId(); [DllImport("user32")] static extern bool PostThreadMessage(int tid, int msg, IntPtr wparam, IntPtr lparam); const int WM_QUIT = 0x12; } } ================================================ FILE: WzComparerR2.MapRender/IRandom.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Microsoft.Xna.Framework; namespace WzComparerR2.MapRender { public interface IRandom { float NextVar(float baseValue, float varRange, bool nonNegative = false); int NextVar(int baseValue, int varRange, bool nonNegative = false); Vector2 NextVar(Vector2 baseValue, Vector2 varRange); Color NextVar(Color baseValue, Color varRange); int Next(int maxValue); bool NextPercent(float percent); } } ================================================ FILE: WzComparerR2.MapRender/ITooltip.cs ================================================ #if MapRenderV1 using System; using System.Collections.Generic; using System.Text; using Microsoft.Xna.Framework; namespace WzComparerR2.MapRender { public interface ITooltip { Rectangle TooltipSenseRegion { get; } bool TooltipDisplayed { get; set; } string TooltipTitle { get; } string TooltipContent { get; } } } #endif ================================================ FILE: WzComparerR2.MapRender/IWcR2Font.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Microsoft.Xna.Framework; using WzComparerR2.Rendering; namespace WzComparerR2.MapRender { public interface IWcR2Font { float Size { get; } float LineHeight { get; set; } object BaseFont { get; } Vector2 MeasureString(string text); Vector2 MeasureString(StringBuilder text); } public class D2DFontAdapter : IWcR2Font { public D2DFontAdapter(D2DFont baseFont) { if (baseFont == null) { throw new ArgumentNullException("baseFont"); } this._baseFont = baseFont; } public float Size { get { return this._baseFont.Size; } } public float LineHeight { get { return this._baseFont.Height; } set { this._baseFont.Height = value; } } public object BaseFont { get { return this._baseFont; } } private readonly D2DFont _baseFont; public Vector2 MeasureString(string text) { return this._baseFont.MeasureString(text); } public Vector2 MeasureString(StringBuilder text) { return this._baseFont.MeasureString(text.ToString()); } } public class XnaFontAdapter : IWcR2Font, IDisposable { public XnaFontAdapter(XnaFont baseFont) { if (baseFont == null) { throw new ArgumentNullException("baseFont"); } this._baseFont = baseFont; } public float Size { get { return this._baseFont.Height; } } public float LineHeight { get { return this._baseFont.Height; } set { this._baseFont.Height = (int)value; } } public object BaseFont { get { return this._baseFont; } } private readonly XnaFont _baseFont; public Vector2 MeasureString(string text) { return this._baseFont.MeasureString(text); } public Vector2 MeasureString(StringBuilder text) { return this._baseFont.MeasureString(text); } protected virtual void Dispose(bool disposing) { if (disposing) { this._baseFont.Dispose(); } } public void Dispose() { Dispose(true); } } } ================================================ FILE: WzComparerR2.MapRender/InputState.cs ================================================ using System; using System.Collections.Generic; using System.Text; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Input; using System.Reflection; namespace WzComparerR2.MapRender { public class InputState { public InputState() { } public InputState(Game game) { _gameWindow = game.Window; } private GameWindow _gameWindow; private KeyboardState CurrentKeyboardState; private KeyboardState LastKeyboardState; private MouseState CurrentMouseState; private MouseState LastMouseState; /// /// 更新当前输入设备的信息,这个方法应该在游戏执行Update时调用。 /// public void Update(GameTime gameTime) { LastKeyboardState = CurrentKeyboardState; CurrentKeyboardState = Keyboard.GetState(); LastMouseState = CurrentMouseState; CurrentMouseState = _gameWindow != null ? Mouse.GetState(_gameWindow) : Mouse.GetState(); } /// /// 检查最后一次更新前,指定的key是否被按下。 /// /// 要检查的key。 /// public bool IsKeyDown(Keys key) { return CurrentKeyboardState.IsKeyDown(key) && LastKeyboardState.IsKeyUp(key); } /// /// 检查最后一次更新前,指定的key是否弹起。 /// /// 要检查的key。 /// public bool IsKeyUp(Keys key) { return CurrentKeyboardState.IsKeyUp(key) && LastKeyboardState.IsKeyDown(key); } /// /// 检查当前指定的key是否正在被按下的状态。 /// /// 要检查的key。 /// public bool IsKeyPressing(Keys key) { return CurrentKeyboardState.IsKeyDown(key); } public bool IsCtrlPressing { get { return this.IsKeyPressing(Keys.LeftControl) || this.IsKeyPressing(Keys.RightControl); } } public bool IsAltPressing { get { return this.IsKeyPressing(Keys.LeftAlt) || this.IsKeyPressing(Keys.RightAlt); } } public bool IsShiftPressing { get { return this.IsKeyPressing(Keys.LeftShift) || this.IsKeyPressing(Keys.RightShift); } } /// /// 获取或设置当前的鼠标指针位置。 /// public Point MousePosition { get { return new Point(CurrentMouseState.X, CurrentMouseState.Y); } set { Mouse.SetPosition(value.X, value.Y); CurrentMouseState = Mouse.GetState(); } } public Point MousePositionLast { get { return new Point(LastMouseState.X, LastMouseState.Y); } } /// /// 检查最后一次更新前,指定的鼠标按键是否被按下。 /// /// 要检查的鼠标按键的组合。 /// public bool IsMouseButtonDown(MouseButton button) { MouseButton[] baseButtons = (MouseButton[])Enum.GetValues(typeof(MouseButton)); bool isBtnDown = false; foreach (MouseButton baseBtn in baseButtons) { if ((int)(button & baseBtn) != 0) isBtnDown |= IsSingleMouseButtonDown(baseBtn); if (isBtnDown) break; } return isBtnDown; } private bool IsSingleMouseButtonDown(MouseButton button) { switch (button) { case MouseButton.LeftButton: return CurrentMouseState.LeftButton == ButtonState.Pressed && LastMouseState.LeftButton == ButtonState.Released; case MouseButton.MiddleButton: return CurrentMouseState.MiddleButton == ButtonState.Pressed && LastMouseState.MiddleButton == ButtonState.Released; case MouseButton.RightButton: return CurrentMouseState.RightButton == ButtonState.Pressed && LastMouseState.RightButton == ButtonState.Released; case MouseButton.XButton1: return CurrentMouseState.XButton1 == ButtonState.Pressed && LastMouseState.XButton1 == ButtonState.Released; case MouseButton.XButton2: return CurrentMouseState.XButton2 == ButtonState.Pressed && LastMouseState.XButton2 == ButtonState.Released; default: return false; } } /// /// 检查最后一次更新前,指定的鼠标按键是否弹起. /// /// 要检查的鼠标按键的组合。 /// public bool IsMouseButtonUp(MouseButton button) { MouseButton[] baseButtons = (MouseButton[])Enum.GetValues(typeof(MouseButton)); bool isBtnUp = false; foreach (MouseButton baseBtn in baseButtons) { if ((int)(button & baseBtn) != 0) isBtnUp |= IsSingleMouseButtonUp(baseBtn); if (isBtnUp) break; } return isBtnUp; } private bool IsSingleMouseButtonUp(MouseButton button) { switch (button) { case MouseButton.LeftButton: return CurrentMouseState.LeftButton == ButtonState.Released && LastMouseState.LeftButton == ButtonState.Pressed; case MouseButton.MiddleButton: return CurrentMouseState.MiddleButton == ButtonState.Released && LastMouseState.MiddleButton == ButtonState.Pressed; case MouseButton.RightButton: return CurrentMouseState.RightButton == ButtonState.Released && LastMouseState.RightButton == ButtonState.Pressed; case MouseButton.XButton1: return CurrentMouseState.XButton1 == ButtonState.Released && LastMouseState.XButton1 == ButtonState.Pressed; case MouseButton.XButton2: return CurrentMouseState.XButton2 == ButtonState.Released && LastMouseState.XButton2 == ButtonState.Pressed; default: return false; } } public bool IsMouseButtonPressing(MouseButton button) { MouseButton[] baseButtons = (MouseButton[])Enum.GetValues(typeof(MouseButton)); bool isBtnPressing = false; foreach (MouseButton baseBtn in baseButtons) { if ((int)(button & baseBtn) != 0) isBtnPressing |= IsSingleMouseButtonPressing(baseBtn); if (isBtnPressing) break; } return isBtnPressing; } private bool IsSingleMouseButtonPressing(MouseButton button) { switch (button) { case MouseButton.LeftButton: return CurrentMouseState.LeftButton == ButtonState.Pressed; case MouseButton.MiddleButton: return CurrentMouseState.MiddleButton == ButtonState.Pressed; case MouseButton.RightButton: return CurrentMouseState.RightButton == ButtonState.Pressed; case MouseButton.XButton1: return CurrentMouseState.XButton1 == ButtonState.Pressed; case MouseButton.XButton2: return CurrentMouseState.XButton2 == ButtonState.Pressed; default: return false; } } /// /// 获取最后一次更新前,鼠标滚轮滚动变化值。 /// /// public int GetMouseWheelScrolledValue() { return CurrentMouseState.ScrollWheelValue - LastMouseState.ScrollWheelValue; } } } ================================================ FILE: WzComparerR2.MapRender/LifeInfo.cs ================================================ using System; using System.Collections.Generic; using System.Text; using WzComparerR2.WzLib; namespace WzComparerR2.MapRender { public class LifeInfo { public LifeInfo() { this.speed = -100; } public int level; public long maxHP; public long maxMP; public int speed; public int PADamage; public int PDDamage; public int PDRate; public int MADamage; public int MDDamage; public int MDRate; public int acc; public int eva; public int pushed; public int exp; public ElemAttr elemAttr; public bool undead; public bool boss; public struct ElemAttr { public ElemResistance I; public ElemResistance L; public ElemResistance F; public ElemResistance S; public ElemResistance H; public ElemResistance D; public ElemResistance P; } public enum ElemResistance : byte { Normal = 0, Immune = 1, Resist = 2, Weak = 3, } public static LifeInfo CreateFromNode(Wz_Node mobNode) { if (mobNode == null) { return null; } var lifeInfo = new LifeInfo(); var infoNode = mobNode.Nodes["info"]; if (infoNode != null) { foreach (Wz_Node node in infoNode.Nodes) { switch (node.Text) { case "level": lifeInfo.level = node.GetValueEx(0); break; case "maxHP": lifeInfo.maxHP = node.GetValueEx(0); break; case "maxMP": lifeInfo.maxMP = node.GetValueEx(0); break; case "speed": lifeInfo.speed = node.GetValueEx(0); break; case "PADamage": lifeInfo.PADamage = node.GetValueEx(0); break; case "PDDamage": lifeInfo.PDDamage = node.GetValueEx(0); break; case "PDRate": lifeInfo.PDRate = node.GetValueEx(0); break; case "MADamage": lifeInfo.MADamage = node.GetValueEx(0); break; case "MDDamage": lifeInfo.MDDamage = node.GetValueEx(0); break; case "MDRate": lifeInfo.MDRate = node.GetValueEx(0); break; case "acc": lifeInfo.acc = node.GetValueEx(0); break; case "eva": lifeInfo.eva = node.GetValueEx(0); break; case "pushed": lifeInfo.pushed = node.GetValueEx(0); break; case "exp": lifeInfo.exp = node.GetValueEx(0); break; case "undead": lifeInfo.undead = node.GetValueEx(0) != 0; break; case "boss": lifeInfo.boss = node.GetValueEx(0) != 0; break; case "elemAttr": string elem = node.GetValueEx(string.Empty); for (int i = 0; i < elem.Length; i += 2) { LifeInfo.ElemResistance resist = (LifeInfo.ElemResistance)(elem[i + 1] - 48); switch (elem[i]) { case 'I': lifeInfo.elemAttr.I = resist; break; case 'L': lifeInfo.elemAttr.L = resist; break; case 'F': lifeInfo.elemAttr.F = resist; break; case 'S': lifeInfo.elemAttr.S = resist; break; case 'H': lifeInfo.elemAttr.H = resist; break; case 'D': lifeInfo.elemAttr.D = resist; break; case 'P': lifeInfo.elemAttr.P = resist; break; } } break; } } } return lifeInfo; } } } ================================================ FILE: WzComparerR2.MapRender/LightRenderer.cs ================================================ using System; using System.Runtime.InteropServices; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; namespace WzComparerR2.MapRender { public class LightRenderer : IDisposable { public LightRenderer(GraphicsDevice graphicsDevice) : this(graphicsDevice, 2048) { } public LightRenderer(GraphicsDevice graphicsDevice, int capacity) { this.graphicsDevice = graphicsDevice; this.effect = new BasicEffect(graphicsDevice) { FogEnabled = false, LightingEnabled = false, VertexColorEnabled = true, }; this.blendState = new BlendState() { AlphaSourceBlend = Blend.Zero, AlphaDestinationBlend = Blend.One, AlphaBlendFunction = BlendFunction.Add, ColorSourceBlend = Blend.One, ColorDestinationBlend = Blend.One, ColorBlendFunction = BlendFunction.Add, }; if (capacity > 0) { this.vertices = new VertexPosition2ColorTexture[capacity]; this.indices = new int[capacity * 3]; } } private readonly GraphicsDevice graphicsDevice; private readonly BasicEffect effect; private readonly BlendState blendState; private bool isInBeginEndPair = false; private VertexPosition2ColorTexture[] vertices; private int numVertices; private int[] indices; private int primitiveCount; private Texture2D texture; private Matrix world; private bool isDisposed; public void Begin(Matrix world) { if (this.isInBeginEndPair) { throw new InvalidOperationException("Begin cannot be called again until End has been successfully called."); } this.world = world; this.isInBeginEndPair = true; } public void DrawSpotLight(Light2D light) { if (light == null) { throw new ArgumentNullException(nameof(light)); } this.ValidCheck(); const int arcPoints = 36; float innerAngle = light.InnerAngle; float outerAngle = light.OuterAngle; bool isCircle = (innerAngle == outerAngle); if (innerAngle <= outerAngle) { innerAngle += 360f; } var radStart = MathHelper.ToRadians(outerAngle); var radEnd = MathHelper.ToRadians(innerAngle); float rotationIncrement = (radEnd - radStart) / arcPoints; // angle: up=0, right=90, down=180, left=-90 // x=sin(d), y=-cos(d) var center = new Vector2(light.X, light.Y); var color = light.Color; var radiusSteps = light.InnerRadius == 0 ? new float[] { light.OuterRadius } : new float[] { light.InnerRadius, light.OuterRadius }; var colorSteps = light.InnerRadius == 0 ? new[] { Color.Transparent } : new[] { light.Color, Color.Transparent }; int numPointsPerArc = arcPoints + (isCircle ? 0 : 1); int numVertices = 1 + numPointsPerArc * radiusSteps.Length; int primitiveCount = arcPoints * (radiusSteps.Length * 2 - 1); this.AcquireBuffer(numVertices, primitiveCount, null, out Span vertexBuffer, out Span indexBuffer, out int vertexIndexStart); int indexCur = 0; vertexBuffer[0] = new VertexPosition2ColorTexture(center, color); for (int i = 0; i < arcPoints; i++) { var angle = radStart + rotationIncrement * i; Vector2 direction = new Vector2((float)Math.Sin(angle), -(float)Math.Cos(angle)); vertexBuffer[i + 1] = new VertexPosition2ColorTexture(center + direction * radiusSteps[0], colorSteps[0]); indexBuffer[indexCur++] = 0; indexBuffer[indexCur++] = i + 1; indexBuffer[indexCur++] = i + 2; for (int s = 1; s < radiusSteps.Length; s++) { vertexBuffer[i + 1 + numPointsPerArc] = new VertexPosition2ColorTexture(center + direction * radiusSteps[s], colorSteps[s]); int baseVertexIndex = i + numPointsPerArc * (s - 1) + 1; indexBuffer[indexCur++] = baseVertexIndex; indexBuffer[indexCur++] = baseVertexIndex + numPointsPerArc; indexBuffer[indexCur++] = baseVertexIndex + 1; indexBuffer[indexCur++] = baseVertexIndex + numPointsPerArc; indexBuffer[indexCur++] = baseVertexIndex + 1 + numPointsPerArc; indexBuffer[indexCur++] = baseVertexIndex + 1; } } if (isCircle) { int lastLoopStartIndex = 3 * (arcPoints - 1) * (radiusSteps.Length * 2 - 1); indexBuffer[lastLoopStartIndex + 2] = 1; for (int s = 1; s < radiusSteps.Length; s++) { int baseIndex = lastLoopStartIndex + 3 + (s - 1) * 6; indexBuffer[baseIndex + 2] = 1 + numPointsPerArc * (s - 1); indexBuffer[baseIndex + 4] = 1 + numPointsPerArc * s; indexBuffer[baseIndex + 5] = indexBuffer[baseIndex + 2]; } } else { Vector2 direction = new Vector2((float)Math.Sin(radEnd), -(float)Math.Cos(radEnd)); vertexBuffer[numPointsPerArc] = new VertexPosition2ColorTexture(center + direction * radiusSteps[0], colorSteps[0]); for (int s = 1; s < radiusSteps.Length; s++) { vertexBuffer[numPointsPerArc * (s + 1)] = new VertexPosition2ColorTexture(center + direction * radiusSteps[s], colorSteps[s]); } } // indexBuffer add vertex offset for (int i = 0; i < indexBuffer.Length; i++) { indexBuffer[i] += vertexIndexStart; } } public void DrawTextureLight(Texture2D texture, Vector2 position, Rectangle? srcRect = default, Vector2 origin = default, bool flipX = false, Color? color = default) { if (texture == null) { throw new ArgumentNullException(nameof(texture)); } this.ValidCheck(); this.AcquireBuffer(4, 2, texture, out Span vertexBuffer, out Span indexBuffer, out int vertexIndexStart); var sourceRect = srcRect ?? new Rectangle(0, 0, texture.Width, texture.Height); float srcLeft, srcTop, srcRight, srcBottom; float dstLeft, dstTop, dstRight, dstBottom; if (flipX) { srcLeft = (float)sourceRect.Right / texture.Width; srcRight = (float)sourceRect.Left / texture.Width; dstRight = position.X + origin.X; dstLeft = dstRight - sourceRect.Width; } else { srcLeft = (float)sourceRect.Left / texture.Width; srcRight = (float)sourceRect.Right / texture.Width; dstLeft = position.X - origin.X; dstRight = dstLeft + sourceRect.Width; } srcTop = (float)sourceRect.Top / texture.Height; srcBottom = (float)sourceRect.Bottom / texture.Height; dstTop = position.Y - origin.Y; dstBottom = dstTop + sourceRect.Height; Color vertexColor = color ?? Color.White; vertexBuffer[0] = new VertexPosition2ColorTexture(new Vector2(dstLeft, dstTop), vertexColor, new Vector2(srcLeft, srcTop)); vertexBuffer[1] = new VertexPosition2ColorTexture(new Vector2(dstRight, dstTop), vertexColor, new Vector2(srcRight, srcTop)); vertexBuffer[2] = new VertexPosition2ColorTexture(new Vector2(dstLeft, dstBottom), vertexColor, new Vector2(srcLeft, srcBottom)); vertexBuffer[3] = new VertexPosition2ColorTexture(new Vector2(dstRight, dstBottom), vertexColor, new Vector2(srcRight, srcBottom)); indexBuffer[0] = vertexIndexStart + 0; indexBuffer[1] = vertexIndexStart + 1; indexBuffer[2] = vertexIndexStart + 2; indexBuffer[3] = vertexIndexStart + 2; indexBuffer[4] = vertexIndexStart + 1; indexBuffer[5] = vertexIndexStart + 3; } public void End() { if (!this.isInBeginEndPair) { throw new InvalidOperationException("Begin must be called before calling End."); } this.ValidCheck(); this.Flush(); this.isInBeginEndPair = false; } public void Dispose() { this.Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (!isDisposed) { if (disposing) { this.effect?.Dispose(); this.blendState?.Dispose(); } this.vertices = null; this.indices = null; isDisposed = true; } } private void ValidCheck() { if (this.isDisposed) { throw new ObjectDisposedException(nameof(LightRenderer)); } if (!this.isInBeginEndPair) { throw new InvalidOperationException("Begin must be called successfully before you can call Draw."); } } private void AcquireBuffer(int vertexCount, int primitiveCount, Texture2D texture, out Span vertexBuffer, out Span indexBuffer, out int vertexIndexStart) { int requireIndexBufferSize = primitiveCount * 3; if (this.vertices == null || this.indices == null) { this.vertices = new VertexPosition2ColorTexture[vertexCount]; this.indices = new int[requireIndexBufferSize]; vertexBuffer = this.vertices; indexBuffer = this.indices; vertexIndexStart = 0; this.numVertices = vertexCount; this.primitiveCount = primitiveCount; return; } if (this.vertices.Length - this.numVertices < vertexCount || this.indices.Length - this.primitiveCount * 3 < requireIndexBufferSize || this.texture != texture) { this.Flush(); // if still not enough, resize buffer if (this.vertices.Length < vertexCount) { Array.Resize(ref this.vertices, vertexCount); } if (this.indices.Length < requireIndexBufferSize) { Array.Resize(ref this.indices, requireIndexBufferSize); } } vertexBuffer = this.vertices.AsSpan().Slice(this.numVertices, vertexCount); indexBuffer = this.indices.AsSpan().Slice(this.primitiveCount * 3, requireIndexBufferSize); vertexIndexStart = this.numVertices; this.numVertices += vertexCount; this.primitiveCount += primitiveCount; this.texture = texture; } private void Flush() { if (this.vertices == null || this.indices == null || this.numVertices == 0 || this.primitiveCount == 0) { return; } Rectangle viewPort = this.graphicsDevice.Viewport.Bounds; this.effect.World = this.world; this.effect.Projection = Matrix.CreateOrthographicOffCenter(viewPort, 1, 0); if (this.texture != null) { this.effect.Texture = this.texture; this.effect.TextureEnabled = true; } else { this.effect.Texture = null; this.effect.TextureEnabled = false; } this.graphicsDevice.BlendState = this.blendState; this.graphicsDevice.RasterizerState = RasterizerState.CullNone; foreach (var pass in this.effect.CurrentTechnique.Passes) { pass.Apply(); this.graphicsDevice.DrawUserIndexedPrimitives(PrimitiveType.TriangleList, this.vertices, 0, this.numVertices, this.indices, 0, this.primitiveCount); } // reset buffer Array.Clear(this.vertices, 0, this.numVertices); Array.Clear(this.indices, 0, this.primitiveCount * 3); this.numVertices = 0; this.primitiveCount = 0; this.effect.Texture = null; this.texture = null; } [StructLayout(LayoutKind.Sequential)] internal struct VertexPosition2ColorTexture : IVertexType { public static readonly int Size = 16; public static readonly VertexDeclaration VertexDeclaration = new VertexDeclaration( new VertexElement(0, VertexElementFormat.Vector2, VertexElementUsage.Position, 0), new VertexElement(8, VertexElementFormat.Color, VertexElementUsage.Color, 0), new VertexElement(12, VertexElementFormat.Vector2, VertexElementUsage.TextureCoordinate, 0)); public Vector2 Position; public Color Color; public Vector2 TextureCoordinate; public VertexPosition2ColorTexture(Vector2 position, Color color) : this(position, color, Vector2.Zero) { } public VertexPosition2ColorTexture(Vector2 position, Color color, Vector2 texcoord) { Position = position; Color = color; TextureCoordinate = texcoord; } public override string ToString() => $"{{position: {this.Position}, color: {this.Color}, texcoord: {this.TextureCoordinate}}}"; VertexDeclaration IVertexType.VertexDeclaration => VertexDeclaration; } } } ================================================ FILE: WzComparerR2.MapRender/LineListMesh.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Microsoft.Xna.Framework; namespace WzComparerR2.MapRender { class LineListMesh { public LineListMesh(Point from, Point to, Color color) : this(from, to, color, 1) { } public LineListMesh(Point from, Point to, Color color, int thickness) : this(new Point[] { from, to }, color, thickness) { } public LineListMesh(Point[] lines, Color color) : this(lines, color, 1) { } public LineListMesh(Point[] lines, Color color, int thickness) { this.Lines = lines; this.Color = color; this.Thickness = thickness; } public Point[] Lines { get; set; } public int Thickness { get; set; } = 1; public Color Color { get; set; } } } ================================================ FILE: WzComparerR2.MapRender/MapData.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using WzComparerR2.WzLib; using WzComparerR2.Common; using WzComparerR2.MapRender.Effects; using WzComparerR2.MapRender.Patches2; using WzComparerR2.PluginBase; using WzComparerR2.Animation; using WzComparerR2.Rendering; namespace WzComparerR2.MapRender { public class MapData { public MapData(IRandom random) { this.Scene = new MapScene(); this.MiniMap = new MiniMap(); this.Tooltips = new List(); this.MapEvents = new List(); this.random = random; } #region 基本信息 public int? ID { get; set; } public string Name { get; set; } public int? Link { get; set; } public Rectangle VRect { get; set; } public string MapMark { get; set; } public string Bgm { get; set; } public bool IsTown { get; set; } public bool CanFly { get; set; } public bool CanSwim { get; set; } public int? ReturnMap { get; set; } public bool HideMinimap { get; set; } public int FieldLimit { get; set; } public MiniMap MiniMap { get; private set; } public MapLight Light { get; private set; } #endregion public MapScene Scene { get; private set; } public IList Tooltips { get; private set; } public List MapEvents { get; private set; } private readonly IRandom random; public void Load(Wz_Node mapImgNode, ResourceLoader resLoader) { var infoNode = mapImgNode.Nodes["info"]; if (infoNode == null) { throw new Exception("Cannot find map info node."); } //试图读取ID LoadIDOrName(mapImgNode); //加载基本信息 LoadInfo(infoNode); //读取link if (this.Link != null && !FindMapByID(this.Link.Value, out mapImgNode)) { throw new Exception("Cannot find or extract map link node."); } //加载小地图 Wz_Node node; if (!string.IsNullOrEmpty(this.MapMark)) { node = PluginManager.FindWz("Map\\MapHelper.img\\mark\\" + this.MapMark); if (node != null) { node = node.GetLinkedSourceNode(PluginManager.FindWz); this.MiniMap.MapMark = resLoader.Load(node); } } if ((node = mapImgNode.Nodes["miniMap"]) != null) { LoadMinimap(node, resLoader); } //加载地图元件 if ((node = mapImgNode.Nodes["back"]) != null) { LoadBack(node); } for (int i = 0; i <= 7; i++) { if ((node = mapImgNode.Nodes[i.ToString()]) != null) { LoadLayer(node, i); } } if ((node = mapImgNode.Nodes["foothold"]) != null) { for (int i = 0; i <= 7; i++) { var fhLevel = node.Nodes[i.ToString()]; if (fhLevel != null) { LoadFoothold(fhLevel, i); } } } if ((node = mapImgNode.Nodes["life"]) != null) { LoadLife(node); } if ((node = mapImgNode.Nodes["reactor"]) != null) { LoadReactor(node); } if ((node = mapImgNode.Nodes["portal"]) != null) { LoadPortal(node); } if ((node = mapImgNode.Nodes["ladderRope"]) != null) { LoadLadderRope(node); } if ((node = mapImgNode.Nodes["skyWhale"]) != null) { LoadSkyWhale(node); } if ((node = mapImgNode.Nodes["illuminantCluster"]) != null) { LoadIlluminantCluster(node); } if ((node = mapImgNode.Nodes["ToolTip"]) != null) { LoadTooltip(node); } if ((node = mapImgNode.Nodes["particle"]) != null) { LoadParticle(node); } if ((node = mapImgNode.Nodes["light"]) != null) { LoadLight(node); } if ((node = mapImgNode.Nodes["effect"]) != null) { LoadMapEvents(node); } //计算地图大小 CalcMapSize(); } private void LoadIDOrName(Wz_Node mapImgNode) { var m = Regex.Match(mapImgNode.Text, @"(\d{9})\.img"); if (m.Success) { this.ID = int.Parse(m.Result("$1")); } this.Name = mapImgNode.Text; } private void LoadInfo(Wz_Node infoNode) { int l = infoNode.Nodes["VRLeft"].GetValueEx(0), t = infoNode.Nodes["VRTop"].GetValueEx(0), r = infoNode.Nodes["VRRight"].GetValueEx(0), b = infoNode.Nodes["VRBottom"].GetValueEx(0); this.VRect = new Rectangle(l, t, r - l, b - t); this.Bgm = infoNode.Nodes["bgm"].GetValueEx(null); this.Link = infoNode.Nodes["link"].GetValueEx(); this.MapMark = infoNode.Nodes["mapMark"].GetValueEx(null); this.IsTown = infoNode.Nodes["town"].GetValueEx(false); this.CanFly = infoNode.Nodes["fly"].GetValueEx(false); this.CanSwim = infoNode.Nodes["swim"].GetValueEx(false); this.ReturnMap = infoNode.Nodes["returnMap"].GetValueEx(); this.HideMinimap = infoNode.Nodes["hideMinimap"].GetValueEx(false); this.FieldLimit = infoNode.Nodes["fieldLimit"].GetValueEx(0); } private void LoadMinimap(Wz_Node miniMapNode, ResourceLoader resLoader) { Wz_Node canvas = miniMapNode.FindNodeByPath("canvas"), width = miniMapNode.FindNodeByPath("width"), height = miniMapNode.FindNodeByPath("height"), centerX = miniMapNode.FindNodeByPath("centerX"), centerY = miniMapNode.FindNodeByPath("centerY"), mag = miniMapNode.FindNodeByPath("mag"); this.MiniMap.ExtraCanvas.Clear(); canvas = canvas.GetLinkedSourceNode(PluginManager.FindWz); if (canvas != null) { this.MiniMap.Canvas = resLoader.Load(canvas); this.MiniMap.ExtraCanvas.Add("canvas", this.MiniMap.Canvas); } else { this.MiniMap.Canvas = null; } // example mapID: 993200000, KMST1140 for (int i = 1; ; i++) { string canvasName = $"canvas{i}"; var extraCanvas = miniMapNode.FindNodeByPath(canvasName); if (extraCanvas == null) { break; } extraCanvas = extraCanvas.GetLinkedSourceNode(PluginManager.FindWz); this.MiniMap.ExtraCanvas.Add(canvasName, resLoader.Load(extraCanvas)); } this.MiniMap.Width = width.GetValueEx(0); this.MiniMap.Height = height.GetValueEx(0); this.MiniMap.CenterX = centerX.GetValueEx(0); this.MiniMap.CenterY = centerY.GetValueEx(0); this.MiniMap.Mag = mag.GetValueEx(0); } private void LoadBack(Wz_Node backNode) { foreach (var node in backNode.Nodes) { var item = BackItem.LoadFromNode(node); item.Name = $"back_{node.Text}"; item.Index = int.Parse(node.Text); (item.IsFront ? this.Scene.Front : this.Scene.Back).Slots.Add(item); } } private void LoadLayer(Wz_Node layerNode, int level) { var layerSceneNode = (LayerNode)this.Scene.Layers.Nodes[level]; //读取obj var objNode = layerNode.Nodes["obj"]; if (objNode != null) { foreach (var node in objNode.Nodes) { var item = ObjItem.LoadFromNode(node); item.Name = $"obj_{level}_{node.Text}"; item.Index = int.Parse(node.Text); layerSceneNode.Obj.Slots.Add(item); } } //读取tile string tS = layerNode.Nodes["info"]?.Nodes["tS"].GetValueEx(null); var tileNode = layerNode.Nodes["tile"]; if (tS != null && tileNode != null) { foreach (var node in tileNode.Nodes) { var item = TileItem.LoadFromNode(node); item.TS = tS; item.Name = $"tile_{level}_{node.Text}"; item.Index = int.Parse(node.Text); layerSceneNode.Tile.Slots.Add(item); } } } private void LoadFoothold(Wz_Node fhLayerNode, int level) { var layerSceneNode = (LayerNode)this.Scene.Layers.Nodes[level]; foreach (var group in fhLayerNode.Nodes) { foreach (var node in group.Nodes) { var item = FootholdItem.LoadFromNode(node); item.ID = int.Parse(node.Text); item.Name = $"fh_{level}_{group.Text}_{node.Text}"; var fhSceneNode = new ContainerNode() { Item = item }; layerSceneNode.Foothold.Nodes.Add(fhSceneNode); } } } private void LoadLife(Wz_Node lifeNode) { bool isCategory = lifeNode.Nodes["isCategory"].GetValueEx(0) != 0; var lifeNodeList = !isCategory ? lifeNode.Nodes : lifeNode.Nodes.SelectMany(n => n.Nodes); int i = 0; foreach (var node in lifeNodeList) { var item = LifeItem.LoadFromNode(node); if (isCategory) { item.Name = $"life_{item.Type}_{node.ParentNode.Text}_{node.Text}"; item.Index = i++; } else { item.Name = $"life_{item.Type}_{node.Text}"; item.Index = int.Parse(node.Text); } //直接绑定foothold ContainerNode fhNode; if (item.Fh != 0 && (fhNode = FindFootholdByID(item.Fh)) != null) { fhNode.Slots.Add(item); } else { Scene.Fly.Sky.Slots.Add(item); } } } private void LoadPortal(Wz_Node portalNode) { string mapID = this.ID.ToString().PadLeft(9, '0'); var portalTooltipNode = PluginManager.FindWz("String/ToolTipHelp.img/PortalTooltip/" + this.ID); var graphMapNode = PluginManager.FindWz(string.Format("Map/Map/Graph.img/{0:D2}/{1}/portal", this.ID / 10000000, mapID)); if (graphMapNode == null) { foreach (var graphImgSubNode in PluginManager.FindWz("Map/Map/Graph.img")?.Nodes ?? Enumerable.Empty()) { if (graphImgSubNode.Nodes[mapID] != null) { graphMapNode = graphImgSubNode.Nodes[mapID].Nodes["portal"]; break; } } } foreach (var node in portalNode.Nodes) { var item = PortalItem.LoadFromNode(node); item.Name = $"portal_{node.Text}"; item.Index = int.Parse(node.Text); //加载tooltip if (portalTooltipNode != null && !string.IsNullOrEmpty(item.PName)) { var tooltipNode = portalTooltipNode.Nodes[item.PName]; if (tooltipNode != null) { var tooltip = new PortalItem.ItemTooltip(); if (tooltipNode.Nodes.Count > 0) { tooltip.Title = tooltipNode.Nodes["Title"].GetValueEx(null); } else { tooltip.Title = tooltipNode.GetValue(); } item.Tooltip = tooltip; } } //Graph.img에 따른 이동경로 출력 item.GraphTargetMap = new List(); if (graphMapNode != null) { foreach (var graphPortalNode in graphMapNode.Nodes) { if (item.Index == graphPortalNode.Nodes["portalNum"].GetValueEx()) { var targetMapID = graphPortalNode.Nodes["targetMap"].GetValueEx(); if (targetMapID != null) { item.GraphTargetMap.Add(targetMapID.Value); } } } } Scene.Fly.Portal.Slots.Add(item); } } private void LoadReactor(Wz_Node reactorNode) { //计算reactor所在层 var layer = Scene.Layers.Nodes.OfType() .FirstOrDefault(l => l.Foothold.Nodes.Count > 0) ?? (Scene.Layers.Nodes[0] as LayerNode); foreach (var node in reactorNode.Nodes) { var item = ReactorItem.LoadFromNode(node); item.Name = $"reactor_{node.Text}"; item.Index = int.Parse(node.Text); layer.Reactor.Slots.Add(item); } } private void LoadLadderRope(Wz_Node ladderRopeNode) { foreach (var node in ladderRopeNode.Nodes) { var item = LadderRopeItem.LoadFromNode(node); item.Name = $"ladderRope_{node.Text}"; item.Index = int.Parse(node.Text); Scene.Fly.LadderRope.Slots.Add(item); } } private void LoadSkyWhale(Wz_Node skyWhaleNode) { foreach (var node in skyWhaleNode.Nodes) { var item = SkyWhaleItem.LoadFromNode(node); item.Name = node.Text; Scene.Fly.SkyWhale.Slots.Add(item); } } private void LoadIlluminantCluster(Wz_Node illuminantClusterNode) { foreach (var node in illuminantClusterNode.Nodes) { if (node.Nodes.Count > 0) { var item = IlluminantClusterItem.LoadFromNode(node); item.Name = node.Text; Scene.Fly.IlluminantCluster.Slots.Add(item); } } } private void LoadTooltip(Wz_Node tooltipNode) { Func getRect = (node) => { int x1 = node.Nodes["x1"].GetValueEx(0); int x2 = node.Nodes["x2"].GetValueEx(0); int y1 = node.Nodes["y1"].GetValueEx(0); int y2 = node.Nodes["y2"].GetValueEx(0); return new Rectangle(x1, y1, x2 - x1, y2 - y1); }; var tooltipDescNode = PluginManager.FindWz("String/ToolTipHelp.img/Mapobject/" + this.ID); for (int i = 0; ; i++) { var rectNode = tooltipNode.Nodes[i.ToString()]; var charNode = tooltipNode.Nodes[i + "char"]; if (rectNode != null) { var item = new TooltipItem(); item.Name = i.ToString(); item.Index = i; item.Rect = getRect(rectNode); if (charNode != null) { item.CharRect = getRect(charNode); } var descNode = tooltipDescNode?.Nodes[i.ToString()]; if (descNode != null) { item.Title = descNode.Nodes["Title"].GetValueEx(null); item.Desc = descNode.Nodes["Desc"].GetValueEx(null); item.ItemEU = descNode.Nodes["ItemEU"].GetValueEx(null); } this.Tooltips.Add(item); } else { break; } } } private void LoadParticle(Wz_Node node) { foreach (var particleNode in node.Nodes) { var item = ParticleItem.LoadFromNode(particleNode); item.Name = node.Text; Scene.Effect.Slots.Add(item); } } private void LoadLight(Wz_Node node) { var mapLight = new MapLight() { Mode = node.Nodes["mode"].GetValueEx(0), AmbientColor = node.Nodes["ambient_color"].GetXnaColor(), DirectionalLightColor = node.Nodes["directional_light_color"].GetXnaColor(), LuminanceLimit = node.Nodes["luminance_limit"].GetValueEx(0f), BackColor = node.Nodes["back_color"].GetXnaColor(), }; for (int i=0; ; i++) { var lightNode = node.Nodes[i.ToString()]; if (lightNode == null) { break; } var light = new Light2D() { Type = lightNode.Nodes["type"].GetValueEx(0), X = lightNode.Nodes["x"].GetValueEx(0), Y = lightNode.Nodes["y"].GetValueEx(0), Color = lightNode.Nodes["color"].GetXnaColor(), InnerRadius = lightNode.Nodes["inner_radius"].GetValueEx(0), OuterRadius = lightNode.Nodes["outer_radius"].GetValueEx(0), InnerAngle = lightNode.Nodes["inner_angle"].GetValueEx(0), OuterAngle = lightNode.Nodes["outer_angle"].GetValueEx(0), DirectionAngle = lightNode.Nodes["direction_angle"].GetValueEx(0), }; mapLight.Lights.Add(light); } this.Light = mapLight; } private void LoadMapEvents(Wz_Node effectNode) { foreach (var node in effectNode.Nodes) { var index = node.Text; var type = node.FindNodeByPath("type").GetValueEx(null); var defaultAnimation = node.FindNodeByPath("defaultAnimation").GetValueEx(null); var changedAnimation = node.FindNodeByPath("changedAnimation").GetValueEx(null); var tags = node.FindNodeByPath("tags").GetValueEx(null); var item = new MapEvent(index, type, defaultAnimation, changedAnimation, tags); this.MapEvents.Add(item); } } private void CalcMapSize() { if (!this.VRect.IsEmpty) { return; } var rect = Rectangle.Empty; int xMAX = int.MinValue; foreach (LayerNode layer in this.Scene.Layers.Nodes) { foreach (ContainerNode item in layer.Foothold.Nodes) { var fh = item.Item; var fhRect = new Rectangle( Math.Min(fh.X1, fh.X2), Math.Min(fh.Y1, fh.Y2), Math.Abs(fh.X2 - fh.X1), Math.Abs(fh.Y2 - fh.Y1)); xMAX = Math.Max(fhRect.Right, xMAX); var oldrec = rect; if (rect.IsEmpty) { rect = fhRect; } else { Rectangle newRect; Rectangle.Union(ref rect, ref fhRect, out newRect); rect = newRect; } } } rect.Y -= 250; rect.Height += 450; foreach (LadderRopeItem item in this.Scene.Fly.LadderRope.Slots) { var lrRect = new Rectangle(item.X, Math.Min(item.Y1, item.Y2), 1, Math.Abs(item.Y2 - item.Y1)); if (rect.IsEmpty) { rect = lrRect; } else { Rectangle newRect; Rectangle.Union(ref rect, ref lrRect, out newRect); rect = newRect; } } this.VRect = rect; } private ContainerNode FindFootholdByID(int fhID) { return this.Scene.Layers.Nodes.OfType() .SelectMany(layerNode => layerNode.Foothold.Nodes).OfType>() .FirstOrDefault(fhNode => fhNode.Item.ID == fhID); } /// /// 对场景中所有的物件预加载动画资源。 /// /// public void PreloadResource(ResourceLoader resLoader) { Action loadFunc = null; loadFunc = (node) => { var container = node as ContainerNode; if (container != null) { foreach (var item in container.Slots) { if (item is BackItem) { PreloadResource(resLoader, (BackItem)item); } else if (item is ObjItem) { PreloadResource(resLoader, (ObjItem)item); } else if (item is TileItem) { PreloadResource(resLoader, (TileItem)item); } else if (item is LifeItem) { PreloadResource(resLoader, (LifeItem)item); } else if (item is PortalItem) { PreloadResource(resLoader, (PortalItem)item); } else if (item is IlluminantClusterItem) { PreloadResource(resLoader, (IlluminantClusterItem)item); } else if (item is ReactorItem) { PreloadResource(resLoader, (ReactorItem)item); } else if (item is ParticleItem) { PreloadResource(resLoader, (ParticleItem)item); } } } foreach (var child in node.Nodes) { loadFunc(child); } }; loadFunc(this.Scene); } private void PreloadResource(ResourceLoader resLoader, BackItem back) { string aniDir; switch (back.Ani) { case 0: aniDir = "back"; break; case 1: aniDir = "ani"; break; case 2: aniDir = $"spine{back.SpineNo}"; break; default: throw new Exception($"Unknown back ani value: {back.Ani}."); } string path = $@"Map\Back\{back.BS}.img\{aniDir}\{back.No}"; var aniItem = resLoader.LoadAnimationData(path); back.View = new BackItem.ItemView() { Animator = CreateAnimator(aniItem, back.SpineAni) }; } private void PreloadResource(ResourceLoader resLoader, ObjItem obj) { string path = $@"Map\Obj\{obj.OS}.img\{obj.L0}\{obj.L1}\{obj.L2}"; var aniItem = resLoader.LoadAnimationData(path); obj.View = new ObjItem.ItemView() { Animator = CreateAnimator(aniItem, obj.SpineAni), Flip = obj.Flip }; } private void PreloadResource(ResourceLoader resLoader, TileItem tile) { string path = $@"Map\Tile\{tile.TS}.img\{tile.U}\{tile.No}"; var aniItem = resLoader.LoadAnimationData(path); tile.View = new TileItem.ItemView() { Animator = CreateAnimator(aniItem) }; } private void PreloadResource(ResourceLoader resLoader, LifeItem life) { string path; switch (life.Type) { case LifeItem.LifeType.Mob: path = $@"Mob\{life.ID:D7}.img"; var mobNode = PluginManager.FindWz(path); //加载mob数据 if (mobNode != null) { life.LifeInfo = LifeInfo.CreateFromNode(mobNode); } //获取link int? mobLink = mobNode?.FindNodeByPath(@"info\link").GetValueEx(); if (mobLink != null) { path = $@"Mob\{mobLink.Value:D7}.img"; mobNode = PluginManager.FindWz(path); } //加载动画 if (mobNode != null) { var aniItem = this.CreateSMAnimator(mobNode, resLoader); if (aniItem != null) { AddMobAI(aniItem); life.View = new LifeItem.ItemView() { Animator = aniItem }; } } break; case LifeItem.LifeType.Npc: path = $@"Npc\{life.ID:D7}.img"; var npcNode = PluginManager.FindWz(path); //TODO: 加载npc数据 life.HideName = (npcNode?.FindNodeByPath(@"info\hideName")?.GetValueEx(0) ?? 0) != 0; var customFontNode = npcNode?.FindNodeByPath(@"info\customFont:func"); if (customFontNode != null) { life.CustomFont = LifeItem.LoadCustomFontFunc(customFontNode); } int? npcLink = npcNode?.FindNodeByPath(@"info\link").GetValueEx(); if (npcLink != null) { path = $@"Npc\{npcLink.Value:D7}.img"; npcNode = PluginManager.FindWz(path); } //加载动画 if (npcNode != null) { var aniItem = this.CreateSMAnimator(npcNode, resLoader); if (aniItem != null) { AddNpcAI(aniItem); life.View = new LifeItem.ItemView() { Animator = aniItem }; } } break; } if (life.View == null) //空动画 { life.View = new LifeItem.ItemView(); } } private void PreloadResource(ResourceLoader resLoader, PortalItem portal) { string path; var view = new PortalItem.ItemView(); //加载editor { var typeName = PortalItem.PortalTypes[portal.Type]; path = $@"Map\MapHelper.img\portal\editor\{typeName}"; var aniData = resLoader.LoadAnimationData(path); if (aniData != null) { view.EditorAnimator = CreateAnimator(aniData); } } //加载动画 { string typeName, imgName; switch (portal.Type) { case 7: typeName = PortalItem.PortalTypes[2]; break; default: typeName = PortalItem.PortalTypes[portal.Type]; break; } switch (portal.Image) { case 0: imgName = "default"; break; default: imgName = portal.Image.ToString(); break; } path = $@"Map\MapHelper.img\portal\game\{typeName}\{imgName}"; var aniNode = PluginManager.FindWz(path); if (aniNode != null) { bool useParts = new[] { "portalStart", "portalContinue", "portalExit" } .Any(aniName => aniNode.Nodes[aniName] != null); if (useParts) //加载动作动画 { var animator = CreateSMAnimator(aniNode, resLoader); view.Animator = animator; view.Controller = new PortalItem.Controller(view); } else //加载普通动画 { var aniData = resLoader.LoadAnimationData(aniNode); if (aniData != null) { view.Animator = CreateAnimator(aniData); } } } } portal.View = view; } private void PreloadResource(ResourceLoader resLoader, IlluminantClusterItem illuminantCluster) { string path; var view = new IlluminantClusterItem.ItemView(); path = $@"Map\Obj\sellas.img\fieldGimmick\cluster\{illuminantCluster.StartPoint * 2}"; var aniNode = PluginManager.FindWz(path); if (aniNode != null) { var aniData = resLoader.LoadAnimationData(aniNode); if (aniData != null) { view.Animator = CreateAnimator(aniData); } } illuminantCluster.StartView = view; view = new IlluminantClusterItem.ItemView(); path = $@"Map\Obj\sellas.img\fieldGimmick\cluster\{illuminantCluster.EndPoint * 2 + 1}"; aniNode = PluginManager.FindWz(path); if (aniNode != null) { var aniData = resLoader.LoadAnimationData(aniNode); if (aniData != null) { view.Animator = CreateAnimator(aniData); } } illuminantCluster.EndView = view; } private void PreloadResource(ResourceLoader resLoader, ReactorItem reactor) { string path = $@"Reactor\{reactor.ID:D7}.img"; var reactorNode = PluginManager.FindWz(path); int? reactorLink = reactorNode?.FindNodeByPath(@"info\link").GetValueEx(); if (reactorLink != null) { path = $@"Reactor\{reactorLink.Value:D7}.img"; reactorNode = PluginManager.FindWz(path); } //加载动画 var aniData = new Dictionary(); Wz_Node frameNode; for (int i = 0; (frameNode = reactorNode.Nodes[i.ToString()]) != null; i++) { //加载循环动画 var ani = resLoader.LoadAnimationData(frameNode) as RepeatableFrameAnimationData; if (ani != null) { var ani2 = new RepeatableFrameAnimationData(ani.Frames); ani2.Repeat = ani.Repeat ?? true; //默认循环 aniData.Add(i.ToString(), ani2); } //加载跳转动画 var hitNode = frameNode.Nodes["hit"]; Wz_Uol uol; if ((uol = hitNode?.GetValue()) != null) { hitNode = uol.HandleUol(hitNode); } if (hitNode != null) { var aniHit = resLoader.LoadAnimationData(hitNode) as RepeatableFrameAnimationData; aniData.Add($@"{i}/hit", aniHit); } } var view = new ReactorItem.ItemView(); view.Animator = new StateMachineAnimator(aniData); view.Controller = new ReactorItem.Controller(view); reactor.View = view; } private void PreloadResource(ResourceLoader resLoader, ParticleItem particle) { string path = $@"Effect\particle.img\{particle.ParticleName}"; var particleNode = PluginManager.FindWz(path); if (particleNode == null) { return; } var desc = resLoader.LoadParticleDesc(particleNode); switch (desc) { case ParticleDesc desc0: { var pSystem = new ParticleSystem(this.random); pSystem.LoadDescription(desc0); for (int i = 0; i < particle.SubItems.Length; i++) { var subItem = particle.SubItems[i]; var pGroup = pSystem.CreateGroup(i.ToString()); pGroup.Position = new Vector2(subItem.X, subItem.Y); pGroup.Active(); pSystem.Groups.Add(pGroup); } particle.View = new ParticleItem.ItemView() { ParticleSystem = pSystem }; } break; } } private StateMachineAnimator CreateSMAnimator(Wz_Node node, ResourceLoader resLoader) { var aniData = new Dictionary(); foreach (var actionNode in node.Nodes) { var actName = actionNode.Text; if (actName != "info" && !actName.StartsWith("condition")) { var ani = resLoader.LoadAnimationData(actionNode) as RepeatableFrameAnimationData; if (ani != null) { aniData.Add(actName, ani); } } } if (aniData.Count > 0) { return new StateMachineAnimator(aniData); } else { return null; } } private object CreateAnimator(object animationData, string aniName = null) { switch (animationData) { case RepeatableFrameAnimationData repFrameAni: return new RepeatableFrameAnimator(repFrameAni); case FrameAnimationData frameAni: return new FrameAnimator(frameAni); case ISpineAnimationData spineAniData: var spineAni = spineAniData.CreateAnimator(); if (aniName != null) { spineAni.SelectedAnimationName = aniName; } return spineAni; case MsCustomSpriteData msSpriteData: var defaultTexture = msSpriteData.Textures[0].Texture; return new MsCustomSprite() { Size = new Vector2(defaultTexture.Width, defaultTexture.Height), Material = ShaderMaterialFactory.Create(msSpriteData), }; default: return null; } } private void AddMobAI(StateMachineAnimator ani) { var actions = new[] { "stand", "say", "mouse", "move", "hand", "laugh", "eye" }; ani.AnimationEnd += (o, e) => { switch(e.CurrentState) { case "regen": if (ani.Data.States.Contains("stand")) e.NextState = "stand"; else if (ani.Data.States.Contains("fly")) e.NextState = "fly"; break; case "stand": if (ani.Data.States.Contains("jump") && this.random.NextPercent(0.05f)) { e.NextState = "jump"; } else if (ani.Data.States.Contains("move") && this.random.NextPercent(0.3f)) { e.NextState = "move"; } else { e.NextState = e.CurrentState; } break; default: goto case "regen"; } }; } private void AddNpcAI(StateMachineAnimator ani) { var actions = new[] { "stand", "say", "mouse", "move", "hand", "laugh", "eye" }; var availActions = ani.Data.States.Where(act => actions.Contains(act)).ToArray(); if (availActions.Length > 0) { ani.AnimationEnd += (o, e) => { e.NextState = availActions[this.random.Next(availActions.Length)]; }; } } public static bool FindMapByID(int mapID, out Wz_Node mapImgNode) { string fullPath = string.Format(@"Map\Map\Map{0}\{1:D9}.img", (mapID / 100000000), mapID); mapImgNode = PluginManager.FindWz(fullPath); Wz_Image mapImg; if (mapImgNode != null && (mapImg = mapImgNode.GetValueEx(null)) != null && mapImg.TryExtract()) { mapImgNode = mapImg.Node; return true; } else { mapImgNode = null; return false; } } } } ================================================ FILE: WzComparerR2.MapRender/MapEvent.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace WzComparerR2.MapRender { public class MapEvent { public MapEvent(string index, string type, string defaultAnimation, string changedAnimation, string tags) { if (!Enum.TryParse(type, out var eventType)) { eventType = MapEventType.Unknown; } Index = index; Type = eventType; DefaultAnimation = defaultAnimation; ChangedAnimation = changedAnimation; Tags = tags; } public string Index { get; set; } public MapEventType Type { get; set; } public string DefaultAnimation { get; set; } public string ChangedAnimation { get; set; } public string Tags { get; set; } } public enum MapEventType { Unknown = 0, SetAnimationOnceAndReturn = 1, } } ================================================ FILE: WzComparerR2.MapRender/MapLight.cs ================================================ using System; using System.Collections.Generic; using Microsoft.Xna.Framework; namespace WzComparerR2.MapRender { public class MapLight { public int Mode { get; set; } public Color AmbientColor { get; set; } public Color DirectionalLightColor { get; set; } public float LuminanceLimit { get; set; } public Color BackColor { get; set; } public List Lights { get; private set; } = new List(); } public class Light2D { public int Type { get; set; } public int X { get; set; } public int Y { get; set; } public Color Color { get; set; } public int InnerRadius { get; set; } public int OuterRadius { get; set; } public int InnerAngle { get; set; } public int OuterAngle { get; set; } public int DirectionAngle { get; set; } } } ================================================ FILE: WzComparerR2.MapRender/MapRenderFonts.cs ================================================ using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Text; using System.Drawing; using Microsoft.Xna.Framework.Content; using WzComparerR2.Rendering; using WzComparerR2.MapRender.Config; namespace WzComparerR2.MapRender { public class MapRenderFonts : IDisposable { public static readonly IReadOnlyList DefaultFonts = new ReadOnlyCollection(new[] { "SimSun", "Dotum" }); public static string GetFontResourceKey(string familyName, float size, FontStyle style) { string assetName = string.Join(",", familyName, size, style); return assetName; } public MapRenderFonts() { this.fonts = new Dictionary(); } private Dictionary fonts; public void LoadContent(ContentManager content) { var config = MapRenderConfig.Default; var fontIndex = config.DefaultFontIndex; if (fontIndex < 0 || fontIndex >= DefaultFonts.Count) { fontIndex = 0; } string familyName = DefaultFonts[fontIndex]; fonts["default"] = content.Load(GetFontResourceKey(familyName, 12f, FontStyle.Regular)); fonts["npcName"] = fonts["default"]; fonts["mobName"] = fonts["default"]; fonts["mobLevel"] = content.Load(GetFontResourceKey("Tahoma", 9f, FontStyle.Regular)); fonts["tooltipTitle"] = content.Load(GetFontResourceKey(familyName, 14f, FontStyle.Bold)); fonts["tooltipContent"] = fonts["default"]; } protected IWcR2Font this[string key] { get { IWcR2Font font; this.fonts.TryGetValue(key, out font); return font; } } public IWcR2Font DefaultFont { get { return this["default"]; } } public IWcR2Font NpcNameFont { get { return this["npcName"]; } } public IWcR2Font MobNameFont { get { return this["mobName"]; } } public IWcR2Font MobLevelFont { get { return this["mobLevel"]; } } public IWcR2Font MapNameFont { get { return this["npcName"]; } } public IWcR2Font TooltipTitleFont { get { return this["tooltipTitle"]; } } public IWcR2Font TooltipContentFont { get { return this["tooltipContent"]; } } public void Dispose() { this.Dispose(true); } protected virtual void Dispose(bool disposing) { this.fonts.Clear(); } } } ================================================ FILE: WzComparerR2.MapRender/MapScene.cs ================================================ using System; using System.Collections.Generic; using System.Text; using System.Linq; using WzComparerR2.MapRender.Patches2; namespace WzComparerR2.MapRender { public class MapScene : SceneNode { public MapScene() { this.Nodes.AddRange(new SceneNode[] { Back = new ContainerNode(), Layers = new SceneNode(), Fly = new FlyLayerNode(), Front = new ContainerNode(), Effect = new ContainerNode() }); for (int i = 0; i <= 7; i++) { this.Layers.Nodes.Add(new LayerNode()); } } public ContainerNode Back { get; private set; } public SceneNode Layers { get; private set; } public FlyLayerNode Fly { get; private set; } public ContainerNode Front { get; private set; } public ContainerNode Effect { get; private set; } public IEnumerable Portals => this.Fly.Portal.Slots.OfType(); public IEnumerable IlluminantClusters => this.Fly.IlluminantCluster.Slots.OfType(); public IEnumerable Npcs => this.Layers.Nodes.OfType() .SelectMany(layerNode => layerNode.Foothold.Nodes).OfType>() .SelectMany(fhNode => fhNode.Slots).OfType() .Where(lifeNode => lifeNode.Type == LifeItem.LifeType.Npc && !lifeNode.Hide) .Concat(this.Fly.Sky.Slots.OfType() .Where(lifeNode => lifeNode.Type == LifeItem.LifeType.Npc && !lifeNode.Hide)); public IEnumerable Mobs => this.Layers.Nodes.OfType() .SelectMany(layerNode => layerNode.Foothold.Nodes).OfType>() .SelectMany(fhNode => fhNode.Slots).OfType() .Where(lifeNode => lifeNode.Type == LifeItem.LifeType.Mob && !lifeNode.Hide) .Concat(this.Fly.Sky.Slots.OfType() .Where(lifeNode => lifeNode.Type == LifeItem.LifeType.Mob && !lifeNode.Hide)); public PortalItem FindPortal(string pName) { return this.Portals.FirstOrDefault(_portal => _portal.PName == pName); } } public class LayerNode : SceneNode { public LayerNode() { this.Nodes.AddRange(new SceneNode[] { Obj = new ContainerNode(), Reactor = new ContainerNode(), Tile = new ContainerNode(), Foothold = new SceneNode() }); } public ContainerNode Obj { get; private set; } public ContainerNode Reactor { get; private set; } public ContainerNode Tile { get; private set; } public SceneNode Foothold { get; private set; } } public class FlyLayerNode : SceneNode { public FlyLayerNode() { this.Nodes.AddRange(new SceneNode[] { Portal = new ContainerNode(), LadderRope = new ContainerNode(), Sky = new ContainerNode(), SkyWhale = new ContainerNode(), IlluminantCluster = new ContainerNode(), }); } public ContainerNode Portal { get; private set; } public ContainerNode LadderRope { get; private set; } /// /// 表示不属于任何Foothold的虚拟节点。 /// public ContainerNode Sky { get; private set; } public ContainerNode SkyWhale { get; private set; } public ContainerNode IlluminantCluster { get; private set; } } public class ContainerNode : SceneNode { public ContainerNode() { this.Slots = new List(); } public List Slots { get; private set; } } public class ContainerNode : ContainerNode { public T Item { get; set; } } } ================================================ FILE: WzComparerR2.MapRender/MathHelper2.cs ================================================ using System; using System.Collections.Generic; using System.Text; using Microsoft.Xna.Framework; namespace WzComparerR2.MapRender { public static class MathHelper2 { public static Vector2 Round(Vector2 vector2) { var rounding = MidpointRounding.AwayFromZero; return new Vector2((float)Math.Round(vector2.X, rounding), (float)Math.Round(vector2.Y, rounding)); } public static Rectangle Transform(Rectangle rectangle, Matrix matrix) { Vector2 lt = new Vector2(rectangle.Left, rectangle.Top); Vector2 rb = new Vector2(rectangle.Right, rectangle.Bottom); Vector2.Transform(ref lt, ref matrix, out lt); Vector2.Transform(ref rb, ref matrix, out rb); return new Rectangle((int)lt.X, (int)lt.Y, (int)(rb.X - lt.X), (int)(rb.Y - lt.Y)); } public static float Max(params float[] values) { if (values != null && values.Length > 0) { float maxValue = values[0]; for (int i = 1; i < values.Length; i++) { maxValue = Math.Max(maxValue, values[i]); } return maxValue; } else { return 0; } } public static float Min(params float[] values) { if (values != null && values.Length > 0) { float minValue = values[0]; for (int i = 1; i < values.Length; i++) { minValue = Math.Min(minValue, values[i]); } return minValue; } else { return 0; } } public static Color Lerp(Color[] colors, float amount) { if (colors == null || colors.Length <= 0) { return Color.Transparent; } if (colors.Length == 1) { return colors[0]; } amount = amount % (colors.Length - 1); int index = (int)amount; amount = amount - index; return new Color( (byte)MathHelper.Lerp(colors[index].R, colors[index + 1].R, amount), (byte)MathHelper.Lerp(colors[index].G, colors[index + 1].G, amount), (byte)MathHelper.Lerp(colors[index].B, colors[index + 1].B, amount), (byte)MathHelper.Lerp(colors[index].A, colors[index + 1].A, amount) ); } public static Color HSVtoColor(float hue, float saturation, float value) { Vector3 color = Vector3.Zero; if (saturation == 0) { color = new Vector3(value); } else { hue /= 60; var i = (int)hue; var f = hue - i; var a = value * (1 - saturation); var b = value * (1 - saturation * f); var c = value * (1 - saturation * (1 - f)); switch (i) { case 0: color = new Vector3(value, c, a); break; case 1: color = new Vector3(b, value, a); break; case 2: color = new Vector3(a, value, c); break; case 3: color = new Vector3(a, b, value); break; case 4: color = new Vector3(c, a, value); break; case 5: color = new Vector3(value, a, b); break; } } return new Color(color); } } } ================================================ FILE: WzComparerR2.MapRender/MeshBatcher.cs ================================================ using System; using System.Collections.Generic; using WzComparerR2.Animation; using WzComparerR2.Rendering; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; namespace WzComparerR2.MapRender { sealed class MeshBatcher : IDisposable { public MeshBatcher(GraphicsDevice graphicsDevice) { this.GraphicsDevice = graphicsDevice; this.alphaBlendState = StateEx.NonPremultipled_Hidef(); this.maskState = StateEx.SrcAlphaMask(); this.meshPool = new Stack(); } public GraphicsDevice GraphicsDevice { get; private set; } public bool D2DEnabled { get; set; } public bool CullingEnabled { get; set; } //内部batcher SpriteBatchEx sprite; Spine.SkeletonRenderer spineRender; D2DRenderer d2dRender; MsSpriteRenderer msSpriteRenderer; ItemType lastItem; Stack meshPool; //innerState private readonly BlendState alphaBlendState; private readonly BlendState maskState; //start参数 private Matrix matrix; private Vector2 camaraOriginWorldPosition; private float gameTime; private bool isInBeginEndPair; //culling参数 private bool matrixNoRot; private Rectangle viewport; public void Begin(Vector2 camaraOriginWorldPosition, float gameTime) { this.matrix = Matrix.CreateTranslation(-camaraOriginWorldPosition.X, -camaraOriginWorldPosition.Y, 0); this.camaraOriginWorldPosition = camaraOriginWorldPosition; this.gameTime = gameTime; this.lastItem = ItemType.Unknown; this.isInBeginEndPair = true; this.PrepareCullingParameters(); } private void PrepareCullingParameters() { if (this.matrix.M12 == 0 && this.matrix.M21 == 0) { matrixNoRot = true; } else { matrixNoRot = false; } //重新计算viewport this.viewport = this.GraphicsDevice.Viewport.Bounds; if (matrixNoRot) { var invmt = Matrix.Invert(this.matrix); var lt = Vector2.Transform(this.viewport.Location.ToVector2(), invmt); var rb = Vector2.Transform(new Vector2(this.viewport.Right, this.viewport.Bottom), invmt); int l = (int)Math.Floor(lt.X); int t = (int)Math.Floor(lt.Y); int r = (int)Math.Ceiling(rb.X); int b = (int)Math.Ceiling(rb.Y); this.viewport = new Rectangle(l, t, r - l, b - t); } } public void Draw(MeshItem mesh) { if (mesh.RenderObject is Frame frame) { this.DrawItem(mesh, frame); } else if (mesh.RenderObject is Spine.V2.Skeleton skeletonV2) { this.DrawItem(mesh, skeletonV2); } else if (mesh.RenderObject is Spine.Skeleton skeletonV4) { this.DrawItem(mesh, skeletonV4); } else if (mesh.RenderObject is TextMesh text) { this.DrawItem(mesh, text); } else if (mesh.RenderObject is LineListMesh lineList) { this.DrawItem(lineList); } else if (mesh.RenderObject is ParticleSystem particle) { this.DrawItem(mesh, particle); } else if (mesh.RenderObject is MsCustomSprite msCustomSprite) { this.DrawItem(mesh, msCustomSprite); } } private void DrawItem(MeshItem mesh, Frame frame) { if (frame == null || frame.Texture == null) { return; } var origin = mesh.FlipX ? new Vector2(frame.Rectangle.Width - frame.Origin.X, frame.Origin.Y) : frame.Origin.ToVector2(); var eff = mesh.FlipX ? SpriteEffects.FlipHorizontally : SpriteEffects.None; var rect = frame.Rectangle; if (mesh.FlipX) { rect.X = -rect.Right; } //兼容平铺 if (mesh.TileRegion != null) { var region = mesh.TileRegion.Value; for (int y = region.Top; y < region.Bottom; y++) { for (int x = region.Left; x < region.Right; x++) { Vector2 pos = mesh.Position + mesh.TileOffset * new Vector2(x, y); if (this.CullingEnabled) { var tileRect = rect; tileRect.Offset(pos); if (!this.IntersectsVP(tileRect)) continue; } Prepare(frame.Blend ? ItemType.Sprite_BlendAdditive : ItemType.Sprite); sprite.Draw(frame.Texture, pos, frame.AtlasRect, new Color(Color.White, frame.A0), 0, origin, 1, eff, 0 ); } } } else { rect.Offset(mesh.Position); if (!this.CullingEnabled || this.IntersectsVP(rect)) { Prepare(frame.Blend ? ItemType.Sprite_BlendAdditive : ItemType.Sprite); sprite.Draw(frame.Texture, mesh.Position, frame.AtlasRect, new Color(Color.White, frame.A0), 0, origin, 1, eff, 0 ); } } } private void DrawItem(MeshItem mesh, Spine.V2.Skeleton skeleton) { skeleton.FlipX = mesh.FlipX; //兼容平铺 if (mesh.TileRegion != null) { var region = mesh.TileRegion.Value; for (int y = region.Top; y < region.Bottom; y++) { for (int x = region.Left; x < region.Right; x++) { Vector2 pos = mesh.Position + mesh.TileOffset * new Vector2(x, y); skeleton.X = pos.X; skeleton.Y = pos.Y; skeleton.UpdateWorldTransform(); Prepare(ItemType.Skeleton); this.spineRender.Draw(skeleton); } } } else { skeleton.X = mesh.Position.X; skeleton.Y = mesh.Position.Y; skeleton.UpdateWorldTransform(); Prepare(ItemType.Skeleton); this.spineRender.Draw(skeleton); } } private void DrawItem(MeshItem mesh, Spine.Skeleton skeleton) { skeleton.ScaleX = mesh.FlipX ? -1 : 1; //兼容平铺 if (mesh.TileRegion != null) { var region = mesh.TileRegion.Value; for (int y = region.Top; y < region.Bottom; y++) { for (int x = region.Left; x < region.Right; x++) { Vector2 pos = mesh.Position + mesh.TileOffset * new Vector2(x, y); skeleton.X = pos.X; skeleton.Y = pos.Y; skeleton.UpdateWorldTransform(); Prepare(ItemType.Skeleton); this.spineRender.Draw(skeleton); } } } else { skeleton.X = mesh.Position.X; skeleton.Y = mesh.Position.Y; skeleton.UpdateWorldTransform(); Prepare(ItemType.Skeleton); this.spineRender.Draw(skeleton); } } private void DrawItem(MeshItem mesh, TextMesh text) { if (text.Text != null && text.Font != null) { var size = text.Font.MeasureString(text.Text); var pos = mesh.Position; switch (text.Align) { case Alignment.Near: break; case Alignment.Center: pos.X -= (int)(size.X / 2); break; case Alignment.Far: pos.X -= size.X; break; } var padding = text.Padding; var rect = new Rectangle((int)(pos.X - padding.Left), (int)(pos.Y - padding.Top), (int)(size.X + padding.Left + padding.Right), (int)(size.Y + padding.Top + padding.Bottom) ); if (this.CullingEnabled) { if (!this.IntersectsVP(rect)) { return; } } object baseFont = text.Font.BaseFont ?? text.Font; if (baseFont is XnaFont) { Prepare(ItemType.Sprite); } else if (baseFont is D2DFont) { Prepare(ItemType.D2DObject); } else { return; } if (text.BackColor.A > 0) //绘制背景 { switch (this.lastItem) { case ItemType.Sprite: sprite.FillRoundedRectangle(rect, text.BackColor); break; case ItemType.D2DObject: d2dRender.FillRoundedRectangle(rect, 3, text.BackColor); break; } } if (text.ForeColor.A > 0) //绘制文字 { switch (this.lastItem) { case ItemType.Sprite: sprite.DrawStringEx((XnaFont)baseFont, text.Text, pos, text.ForeColor); break; case ItemType.D2DObject: d2dRender.DrawString((D2DFont)baseFont, text.Text, pos, text.ForeColor); break; } } } } private void DrawItem(LineListMesh lineList) { if (lineList != null && lineList.Lines != null) { var vertices = lineList.Lines; int vertexCount = vertices.Length / 2 * 2; if (this.D2DEnabled) { Prepare(ItemType.D2DObject); for (int i = 0; i < vertexCount; i += 2) { this.d2dRender.DrawLine(vertices[i].ToVector2(), vertices[i + 1].ToVector2(), lineList.Thickness, lineList.Color); } } else { Prepare(ItemType.Sprite); for (int i = 0; i < vertexCount; i += 2) { sprite.DrawLine(vertices[i], vertices[i + 1], lineList.Thickness, lineList.Color); } } } } private void DrawItem(MeshItem mesh, ParticleSystem particleSystem) { if (particleSystem.Texture?.Texture != null) { ItemType itemType = ItemType.Unknown; if (particleSystem.BlendFuncSrc == ParticleBlendFunc.SRC_ALPHA || (int)particleSystem.BlendFuncSrc == 12) //TODO: what's this? KMS v.293, esfera_temple_big { switch (particleSystem.BlendFuncDst) { case ParticleBlendFunc.ONE: //5,2 case ParticleBlendFunc.DEST_ALPHA: //5,7 itemType = ItemType.Sprite_BlendAdditive; break; case ParticleBlendFunc.SRC_COLOR: //12,3 case ParticleBlendFunc.INV_SRC_ALPHA: //5,6 itemType = ItemType.Sprite_BlendNonPremultiplied; break; } } if (particleSystem.BlendFuncSrc == ParticleBlendFunc.ZERO && particleSystem.BlendFuncDst == ParticleBlendFunc.INV_SRC_ALPHA) //1,6 { itemType = ItemType.Sprite_BlendMask; } if (itemType == ItemType.Unknown) { throw new Exception($"Unknown particle blendfunc: {particleSystem.BlendFuncSrc}, {particleSystem.BlendFuncDst}"); } Prepare(itemType); particleSystem.Draw(this.sprite, mesh.Position); } } private void DrawItem(MeshItem mesh, MsCustomSprite msCustomSprite) { Prepare(ItemType.MsSprite); this.msSpriteRenderer.Draw(mesh.Position, msCustomSprite.Size, msCustomSprite.Material); } public Rectangle[] Measure(MeshItem mesh) { Rectangle[] region = null; int count; Measure(mesh, ref region, out count); return region; } public void Measure(MeshItem mesh, ref Rectangle[] region, out int count) { Rectangle rect = Rectangle.Empty; if (mesh.RenderObject is TextMesh) { var textItem = (TextMesh)mesh.RenderObject; var size = textItem.Font.MeasureString(textItem.Text); var pos = mesh.Position; switch (textItem.Align) { case Alignment.Near: break; case Alignment.Center: pos.X -= size.X / 2; break; case Alignment.Far: pos.X -= size.X; break; } var padding = textItem.Padding; var rectBg = new Rectangle((int)(pos.X - padding.Left), (int)(pos.Y - padding.Top), (int)(size.X + padding.Left + padding.Right), (int)(size.Y + padding.Top + padding.Bottom) ); var rectText = new Rectangle((int)pos.X, (int)pos.Y, (int)size.X, (int)size.Y); count = 1; EnsureArraySize(ref region, count); Rectangle.Union(ref rectBg, ref rectText, out region[0]); return; } if (mesh.RenderObject is Frame) { var frame = (Frame)mesh.RenderObject; rect = frame.Rectangle; if (mesh.FlipX) { rect.X = -rect.Right; } } else { count = 0; return; } rect.X += (int)mesh.Position.X; rect.Y += (int)mesh.Position.Y; if (mesh.TileRegion != null) { var tileRegion = mesh.TileRegion.Value; count = tileRegion.Width * tileRegion.Height; EnsureArraySize(ref region, count); Point offset = mesh.TileOffset.ToPoint(); int i = 0; for (int y = tileRegion.Top; y < tileRegion.Bottom; y++) { for (int x = tileRegion.Left; x < tileRegion.Right; x++) { region[i++] = new Rectangle(rect.X + x * offset.X, rect.Y + y * offset.Y, rect.Width, rect.Height); } } } else { count = 1; EnsureArraySize(ref region, count); region[0] = rect; } } private void EnsureArraySize(ref T[] array, int length) { if (array == null) { array = new T[length]; } else if (array.Length < length) { Array.Resize(ref array, length); } } public void End() { InnerFlush(); this.lastItem = ItemType.Unknown; this.isInBeginEndPair = false; } private void Prepare(ItemType itemType) { if (lastItem == itemType) { return; } switch (itemType) { case ItemType.Sprite: case ItemType.Skeleton: case ItemType.D2DObject: case ItemType.Sprite_BlendAdditive: case ItemType.Sprite_BlendNonPremultiplied: case ItemType.Sprite_BlendMask: case ItemType.MsSprite: InnerFlush(); lastItem = itemType; InnerBegin(); break; } } private void InnerBegin() { switch (lastItem) { case ItemType.Sprite: if (this.sprite == null) { this.sprite = new SpriteBatchEx(this.GraphicsDevice); } this.sprite.Begin(SpriteSortMode.Deferred, this.alphaBlendState, transformMatrix: this.matrix); break; case ItemType.Skeleton: if (this.spineRender == null) { this.spineRender = new Spine.SkeletonRenderer(this.GraphicsDevice); } if (this.spineRender.Effect is BasicEffect basicEff) { basicEff.World = this.matrix; basicEff.Projection = Matrix.CreateOrthographicOffCenter(0, this.GraphicsDevice.Viewport.Width, this.GraphicsDevice.Viewport.Height, 0, 1, 0); } this.spineRender.Begin(); break; case ItemType.D2DObject: if (this.d2dRender == null) { this.d2dRender = new D2DRenderer(this.GraphicsDevice); } this.d2dRender.Begin(this.matrix); break; case ItemType.Sprite_BlendAdditive: if (this.sprite == null) { this.sprite = new SpriteBatchEx(this.GraphicsDevice); } this.sprite.Begin(SpriteSortMode.Deferred, BlendState.Additive, transformMatrix: this.matrix); break; case ItemType.Sprite_BlendNonPremultiplied: if (this.sprite == null) { this.sprite = new SpriteBatchEx(this.GraphicsDevice); } this.sprite.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, transformMatrix: this.matrix); break; case ItemType.Sprite_BlendMask: if (this.sprite == null) { this.sprite = new SpriteBatchEx(this.GraphicsDevice); } this.sprite.Begin(SpriteSortMode.Deferred, this.maskState, transformMatrix: this.matrix); break; case ItemType.MsSprite: if (this.msSpriteRenderer == null) { this.msSpriteRenderer = new MsSpriteRenderer(this.GraphicsDevice); } this.msSpriteRenderer.Begin(this.camaraOriginWorldPosition, this.gameTime); break; } } private void InnerFlush() { switch (lastItem) { case ItemType.Sprite: case ItemType.Sprite_BlendAdditive: case ItemType.Sprite_BlendNonPremultiplied: case ItemType.Sprite_BlendMask: this.sprite.End(); break; case ItemType.Skeleton: this.spineRender.End(); break; case ItemType.D2DObject: this.d2dRender.End(); break; case ItemType.MsSprite: this.msSpriteRenderer.End(); break; } } private bool IntersectsVP(Rectangle rect) { if (this.matrixNoRot) { bool isIntersects; this.viewport.Intersects(ref rect, out isIntersects); return isIntersects; } else { var mt = this.matrix; Vector2 lt = new Vector2(rect.Left, rect.Top), rt = new Vector2(rect.Right, rect.Top), lb = new Vector2(rect.Left, rect.Bottom), rb = new Vector2(rect.Right, rect.Bottom); Vector2.Transform(ref lt, ref mt, out lt); Vector2.Transform(ref rt, ref mt, out rt); Vector2.Transform(ref lb, ref mt, out lb); Vector2.Transform(ref rb, ref mt, out rb); int left = (int)Math.Floor(MathHelper2.Min(lt.X, rt.X, lb.X, rb.X)); int top = (int)Math.Floor(MathHelper2.Min(lt.Y, rt.Y, lb.Y, rb.Y)); int right = (int)Math.Ceiling(MathHelper2.Max(lt.X, rt.X, lb.X, rb.X)); int bottom = (int)Math.Ceiling(MathHelper2.Max(lt.Y, rt.Y, lb.Y, rb.Y)); var bound = new Rectangle(left, top, right - left, bottom - top); bool isIntersects; this.viewport.Intersects(ref bound, out isIntersects); return isIntersects; } } public MeshItem MeshPop() { if (this.meshPool.Count > 0) { var mesh = this.meshPool.Pop(); mesh.RenderObject = null; mesh.Position = Vector2.Zero; mesh.Z0 = 0; mesh.Z1 = 0; mesh.FlipX = false; mesh.TileRegion = null; mesh.TileOffset = Vector2.Zero; return mesh; } else { return new MeshItem(); } } public void MeshPush(MeshItem mesh) { mesh.RenderObject = null; this.meshPool.Push(mesh); } public void Dispose() { this.sprite?.Dispose(); this.spineRender?.Dispose(); this.alphaBlendState.Dispose(); this.maskState.Dispose(); this.meshPool.Clear(); } private enum ItemType { Unknown = 0, Sprite = 1, Skeleton = 2, D2DObject = 3, Sprite_AlphaBlend = Sprite, Sprite_BlendAdditive = 4, Sprite_BlendNonPremultiplied = 5, Sprite_BlendMask = 6, MsSprite = 7, } } } ================================================ FILE: WzComparerR2.MapRender/MeshItem.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using Microsoft.Xna.Framework; namespace WzComparerR2.MapRender { class MeshItem : IComparable { public object RenderObject { get; set; } public Vector2 Position { get; set; } public int Z0 { get; set; } public int Z1 { get; set; } //附加信息 public bool FlipX { get; set; } public Rectangle? TileRegion { get; set; } public Vector2 TileOffset { get; set; } public int CompareTo(MeshItem other) { int comp; if ((comp = this.Z0.CompareTo(other.Z0)) != 0 || (comp = this.Z1.CompareTo(other.Z1)) != 0) { return comp; } return 0; } } } ================================================ FILE: WzComparerR2.MapRender/MiniMap.cs ================================================ using System; using System.Collections.Generic; using System.Text; using Microsoft.Xna.Framework.Graphics; namespace WzComparerR2.MapRender { public class MiniMap { public MiniMap() { this.ExtraCanvas = new Dictionary(); } public Texture2D Canvas { get; set; } public Dictionary ExtraCanvas { get; private set; } public int Width { get; set; } public int Height { get; set; } public int CenterX { get; set; } public int CenterY { get; set; } public int Mag { get; set; } public Texture2D MapMark { get; set; } } } ================================================ FILE: WzComparerR2.MapRender/MouseButton.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace WzComparerR2.MapRender { [Flags] public enum MouseButton { LeftButton = 1, MiddleButton = 2, RightButton = 4, XButton1 = 8, XButton2 = 16 } } ================================================ FILE: WzComparerR2.MapRender/MsCustomSprite.cs ================================================ using System; using System.Collections.Generic; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using WzComparerR2.MapRender.Effects; namespace WzComparerR2.MapRender { public class MsCustomSpriteData { public MsCustomTexture[] Textures { get; set; } public MsShader Shader { get; set; } } public class MsShader { public string ID { get; set; } public Dictionary Constants { get; private set; } = new(); } public struct MsCustomTexture { public Texture2D Texture { get; set; } public Texture2D[] Textures { get; set; } public int AddressU { get; set; } public int AddressV { get; set; } } public struct MsShaderConstant { public MsShaderConstant(float value) { this.ColumnCount = 1; this.X = value; this.Y = 0; this.Z = 0; } public MsShaderConstant(float x, float y) { this.ColumnCount = 2; this.X = x; this.Y = y; this.Z = 0; } public MsShaderConstant(float x, float y, float z) { this.ColumnCount = 3; this.X = x; this.Y = y; this.Z = z; } public int ColumnCount { get; set; } public float X { get; set; } public float Y { get; set; } public float Z { get; set; } public float ToScalar() => this.X; public Vector2 ToVector2() => new Vector2(this.X, this.Y); public Vector3 ToVector3() => new Vector3(this.X, this.Y, this.Z); } public class MsCustomSprite { public Vector2 Size { get; set; } public ShaderMaterial Material { get; set; } } } ================================================ FILE: WzComparerR2.MapRender/MsSpriteRenderer.cs ================================================ using System; using System.Buffers; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using WzComparerR2.MapRender.Effects; using WzComparerR2.Rendering; namespace WzComparerR2.MapRender { public class MsSpriteRenderer : IDisposable { public MsSpriteRenderer(GraphicsDevice graphicsDevice) : this(graphicsDevice, 128) { } public MsSpriteRenderer(GraphicsDevice graphicsDevice, int capacity) { this.GraphicsDevice = graphicsDevice; this.loadedPixelShaders = new Dictionary(); this.vertexShader = EffectResources.CreateNativeShader(graphicsDevice, "vs_position_color_texture"); this.capacity = capacity; this.vertices = new VertexPosition4ColorTexture[capacity * 4]; this.indices = new short[capacity * 6]; } public GraphicsDevice GraphicsDevice { get; private set; } private Effect vertexShader; private readonly Dictionary loadedPixelShaders; private Texture2D cachedBackBufferTexture; private ShaderMaterial lastShaderMaterial; private VertexPosition4ColorTexture[] vertices; private short[] indices; private int capacity; private int primitiveCount; private bool isInBeginEndPair; private bool isDisposed; private Matrix vp; private Matrix vp_inv; private Vector4 resolution_time; private Matrix world; public void Begin(Vector2 cameraOrigin, float gameTime) { if (this.isInBeginEndPair) { throw new InvalidOperationException("Begin cannot be called again until End has been successfully called."); } var viewPort = this.GraphicsDevice.Viewport; Matrix.CreateOrthographicOffCenter(cameraOrigin.X, cameraOrigin.X + viewPort.Width, cameraOrigin.Y + viewPort.Height, cameraOrigin.Y, 0, -1, out this.vp); Matrix.Invert(ref this.vp, out this.vp_inv); this.resolution_time = new Vector4(viewPort.Width, viewPort.Height, gameTime, 0); this.world = Matrix.Identity; this.TrySetCommonParameters(this.vertexShader); this.isInBeginEndPair = true; } public void Draw(Vector2 worldPosition, Vector2 size, ShaderMaterial shaderMaterial) { if (this.isDisposed) { throw new ObjectDisposedException(nameof(LightRenderer)); } if (!this.isInBeginEndPair) { throw new InvalidOperationException("Begin must be called successfully before you can call Draw."); } if (this.lastShaderMaterial != null && !this.lastShaderMaterial.Equals(shaderMaterial)) { this.Flush(); } if (this.capacity - this.primitiveCount < 2) { this.Flush(); } this.lastShaderMaterial = shaderMaterial; int vertexOffset = this.primitiveCount * 2; int indexOffset = this.primitiveCount * 3; Vector2 lt = worldPosition; Vector2 rb = worldPosition + size; this.vertices[vertexOffset + 0] = new VertexPosition4ColorTexture(new Vector4(lt.X, lt.Y, 0, 1), Color.White, new Vector2(0, 0)); this.vertices[vertexOffset + 1] = new VertexPosition4ColorTexture(new Vector4(rb.X, lt.Y, 0, 1), Color.White, new Vector2(1, 0)); this.vertices[vertexOffset + 2] = new VertexPosition4ColorTexture(new Vector4(lt.X, rb.Y, 0, 1), Color.White, new Vector2(0, 1)); this.vertices[vertexOffset + 3] = new VertexPosition4ColorTexture(new Vector4(rb.X, rb.Y, 0, 1), Color.White, new Vector2(1, 1)); this.indices[indexOffset + 0] = (short)(vertexOffset + 0); this.indices[indexOffset + 1] = (short)(vertexOffset + 1); this.indices[indexOffset + 2] = (short)(vertexOffset + 2); this.indices[indexOffset + 3] = (short)(vertexOffset + 1); this.indices[indexOffset + 4] = (short)(vertexOffset + 3); this.indices[indexOffset + 5] = (short)(vertexOffset + 2); this.primitiveCount += 2; } public void End() { if (!this.isInBeginEndPair) { throw new InvalidOperationException("Begin must be called before calling End."); } this.Flush(); this.lastShaderMaterial = null; this.isInBeginEndPair = false; } private void Flush() { var shaderMaterial = this.lastShaderMaterial; var primitiveCount = this.primitiveCount; if (shaderMaterial == null || primitiveCount == 0) { return; } if (!this.loadedPixelShaders.TryGetValue(shaderMaterial.ShaderID, out Effect pixelShader)) { pixelShader = EffectResources.CreateNativeShader(this.GraphicsDevice, shaderMaterial.ShaderID); this.loadedPixelShaders.Add(shaderMaterial.ShaderID, pixelShader); } this.vertexShader.CurrentTechnique.Passes[0].Apply(); // pixel shader pre-process if (shaderMaterial is IMaplestoryEffectMatrices m) { m.ViewProjection = this.vp; m.ViewProjectionInverse = this.vp_inv; m.ResolutionTime = this.resolution_time; } IBackgroundCaptureEffect bgTexEffect = shaderMaterial as IBackgroundCaptureEffect; if (bgTexEffect != null) { var pp = this.GraphicsDevice.PresentationParameters; Texture2D bgTex; if (this.cachedBackBufferTexture == null || this.cachedBackBufferTexture.Width != pp.BackBufferWidth || this.cachedBackBufferTexture.Height != pp.BackBufferHeight || this.cachedBackBufferTexture.Format != pp.BackBufferFormat) { bgTex = new Texture2D(this.GraphicsDevice, this.GraphicsDevice.PresentationParameters.BackBufferWidth, this.GraphicsDevice.PresentationParameters.BackBufferHeight, false, this.GraphicsDevice.PresentationParameters.BackBufferFormat); this.cachedBackBufferTexture?.Dispose(); this.cachedBackBufferTexture = bgTex; } else { bgTex = this.cachedBackBufferTexture; } this.GraphicsDevice.CopyBackBuffer(bgTex); bgTexEffect.BackgroundTexture = bgTex; } shaderMaterial.ApplyParameters(pixelShader); pixelShader.CurrentTechnique.Passes[0].Apply(); shaderMaterial.ApplySamplerStates(this.GraphicsDevice); // draw primitives this.GraphicsDevice.RasterizerState = RasterizerState.CullNone; this.GraphicsDevice.BlendState = BlendState.NonPremultiplied; this.GraphicsDevice.DrawUserIndexedPrimitives(PrimitiveType.TriangleList, this.vertices, 0, primitiveCount * 2, this.indices, 0, primitiveCount); this.primitiveCount = 0; // clean-up shader parameters if (bgTexEffect != null) { bgTexEffect.BackgroundTexture = null; } } public void Dispose() { this.Dispose(true); GC.SuppressFinalize(this); } protected void Dispose(bool disposing) { if (disposing) { if (this.vertexShader != null) { this.vertexShader.Dispose(); this.vertexShader = null; } if (this.loadedPixelShaders != null) { foreach (var effect in this.loadedPixelShaders) { effect.Value.Dispose(); } this.loadedPixelShaders.Clear(); } if (this.cachedBackBufferTexture != null) { this.cachedBackBufferTexture.Dispose(); this.cachedBackBufferTexture = null; } } this.lastShaderMaterial = null; this.vertices = null; this.indices = null; this.isDisposed = true; } private void TrySetCommonParameters(Effect effect) { effect.Parameters["vp"]?.SetValue(this.vp); effect.Parameters["vp_inv"]?.SetValue(this.vp_inv); effect.Parameters["resolution_time"]?.SetValue(this.resolution_time); effect.Parameters["world"]?.SetValue(this.world); } public struct VertexPosition4ColorTexture : IVertexType { public static readonly int Size = 28; public static readonly VertexDeclaration VertexDeclaration = new VertexDeclaration(Size, new VertexElement(0, VertexElementFormat.Vector4, VertexElementUsage.Position, 0), new VertexElement(16, VertexElementFormat.Color, VertexElementUsage.Color, 0), new VertexElement(20, VertexElementFormat.Vector2, VertexElementUsage.TextureCoordinate, 0) ); public VertexPosition4ColorTexture(Vector4 position, Color color, Vector2 textureCoordinate) { this.Position = position; this.Color = color; this.TextureCoordinate = textureCoordinate; } public Vector4 Position { get; set; } public Color Color { get; set; } public Vector2 TextureCoordinate { get; set; } public override string ToString() => $"{{position: {this.Position}, color: {this.Color}, texcoord: {this.TextureCoordinate}}}"; VertexDeclaration IVertexType.VertexDeclaration => VertexPosition4ColorTexture.VertexDeclaration; } } } ================================================ FILE: WzComparerR2.MapRender/Music.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using WzComparerR2.WzLib; using System.Runtime.InteropServices; using ManagedBass; namespace WzComparerR2.MapRender { class Music : IDisposable { public Music(Wz_Sound sound) { this.soundData = sound.ExtractSound(); this.pData = GCHandle.Alloc(this.soundData, GCHandleType.Pinned); this.hStream = Bass.CreateStream(pData.AddrOfPinnedObject(), 0, this.soundData.Length, BassFlags.Default); Music.GlobalVolumeChanged += this.OnGlobalVolumeChanged; } private byte[] soundData; private GCHandle pData; private int hStream; private float? vol; public bool IsLoop { get { return (Bass.ChannelFlags(hStream, 0, 0) & BassFlags.Loop) != 0; } set { Bass.ChannelFlags(hStream, value ? BassFlags.Loop : BassFlags.Default, BassFlags.Loop); } } public PlayState State { get { var active = Bass.ChannelIsActive(hStream); switch (active) { case PlaybackState.Stopped: return PlayState.Stopped; case PlaybackState.Playing: return PlayState.Playing; case PlaybackState.Paused: return PlayState.Paused; default: return PlayState.Unknown; } } } public float Volume { get { if (vol == null) { vol = Bass.ChannelGetAttribute(hStream, ChannelAttribute.Volume, out float value) ? value : 0; } return vol.Value; } set { vol = value; Bass.ChannelSetAttribute(hStream, ChannelAttribute.Volume, vol.Value * globalVol); } } public void Play() { Bass.ChannelPlay(hStream, false); } public void Pause() { Bass.ChannelPause(hStream); } public void Stop() { Bass.ChannelStop(hStream); } public void Dispose() { Music.GlobalVolumeChanged -= this.OnGlobalVolumeChanged; Bass.StreamFree(hStream); this.pData.Free(); } public enum PlayState { Stopped = 0, Playing = 1, Paused = 2, Unknown = -1, } private void OnGlobalVolumeChanged(object sender, EventArgs e) { this.Volume = Volume; } #region Global Volume private static float globalVol = 1f; private static event EventHandler GlobalVolumeChanged; public static float GlobalVolume { get { return globalVol; } set { globalVol = value; GlobalVolumeChanged?.Invoke(null, EventArgs.Empty); } } #endregion } } ================================================ FILE: WzComparerR2.MapRender/Particle.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Microsoft.Xna.Framework; namespace WzComparerR2.MapRender { public class Particle { public Vector2 Pos; public float Angle; public Color StartColor; public Color EndColor; public float StartSize; public float EndSize; public float StartSpin; public float EndSpin; public float Life; //gravity public Vector2 Dir; public float RadialAcc; public float TangentialAcc; //radius public float StartRadius; public float EndRadius; public float RotatePerSecond; //lifecycle public float Time; public float NormalizedTime; public void Reset() { this.Pos = Vector2.Zero; this.StartColor = new Color(); this.EndColor = new Color(); this.StartSize = 0; this.EndSize = 0; this.StartSpin = 0; this.EndSpin = 0; this.Life = 0; this.Dir = Vector2.Zero; this.RadialAcc = 0; this.TangentialAcc = 0; this.StartRadius = 0; this.EndRadius = 0; this.RotatePerSecond = 0; this.Time = 0; this.NormalizedTime = 0; } } } ================================================ FILE: WzComparerR2.MapRender/ParticleDesc.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using WzComparerR2.WzLib; using WzComparerR2.Animation; namespace WzComparerR2.MapRender { public class ParticleDesc { public string Name { get; set; } public int TotalParticle { get; set; } public float Angle { get; set; } public float AngleVar { get; set; } public float Duration { get; set; } public ParticleBlendFunc BlendFuncSrc { get; set; } public ParticleBlendFunc BlendFuncDst { get; set; } public Color StartColor { get; set; } public Color StartColorVar { get; set; } public Color EndColor { get; set; } public Color EndColorVar { get; set; } public int MiddlePoint0 { get; set; } public int MiddlePointAlpha0 { get; set; } public int MiddlePoint1 { get; set; } public int MiddlePointAlpha1 { get; set; } public float StartSize { get; set; } public float StartSizeVar { get; set; } public float EndSize { get; set; } public float EndSizeVar { get; set; } public float PosX { get; set; } public float PosY { get; set; } public float PosVarX { get; set; } public float PosVarY { get; set; } public float StartSpin { get; set; } public float StartSpinVar { get; set; } public float EndSpin { get; set; } public float EndSpinVar { get; set; } public ParticleGravityDesc Gravity { get; set; } public ParticleRadiusDesc Radius { get; set; } public float Life { get; set; } public float LifeVar { get; set; } public bool OpacityModifyRGB { get; set; } public int PositionType { get; set; } public Frame Texture { get; set; } } public class ParticleGravityDesc { public float X { get; set; } public float Y { get; set; } public float Speed { get; set; } public float SpeedVar { get; set; } public float RadialAccel { get; set; } public float RadialAccelVar { get; set; } public float TangentialAccel { get; set; } public float TangentialAccelVar { get; set; } public bool RotationIsDir { get; set; } } public class ParticleRadiusDesc { public float StartRadius { get; set; } public float StartRadiusVar { get; set; } public float EndRadius { get; set; } public float EndRadiusVar { get; set; } public float RotatePerSecond { get; set; } public float RotatePerSecondVar { get; set; } } //same as d3d11blend public enum ParticleBlendFunc { ZERO = 1, ONE = 2, SRC_COLOR = 3, INV_SRC_COLOR = 4, SRC_ALPHA = 5, INV_SRC_ALPHA = 6, DEST_ALPHA = 7, INV_DEST_ALPHA = 8, DEST_COLOR = 9, INV_DEST_COLOR = 10, } } ================================================ FILE: WzComparerR2.MapRender/ParticleDesc1.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace WzComparerR2.MapRender { public class ParticleDesc1 { public string Name { get; set; } public int MaxParticleCount { get; set; } } } ================================================ FILE: WzComparerR2.MapRender/ParticleDesc3.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace WzComparerR2.MapRender { public class ParticleDesc3 { } } ================================================ FILE: WzComparerR2.MapRender/ParticleEmitter.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using Microsoft.Xna.Framework; namespace WzComparerR2.MapRender { public class ParticleEmitter { public ParticleEmitter(ParticleSystem particleSystem) { this.Owner = particleSystem; } public ParticleSystem Owner { get; private set; } //初始速度方向 public float Angle { get; set; } public float AngleVar { get; set; } //颜色 public Color StartColor { get; set; } public Color StartColorVar { get; set; } public Color EndColor { get; set; } public Color EndColorVar { get; set; } //大小 public float StartSize { get; set; } public float StartSizeVar { get; set; } public float EndSize { get; set; } public float EndSizeVar { get; set; } //位置 public Vector2 Pos { get; set; } public Vector2 PosVar { get; set; } //旋转 public float StartSpin { get; set; } public float StartSpinVar { get; set; } public float EndSpin { get; set; } public float EndSpinVar { get; set; } //gravity相关 public float Speed { get; set; } public float SpeedVar { get; set; } public float RadialAccel { get; set; } public float RadialAccelVar { get; set; } public float TangentialAccel { get; set; } public float TangentialAccelVar { get; set; } public bool RotationIsDir { get; set; } //radius相关 public float StartRadius { get; set; } public float StartRadiusVar { get; set; } public float EndRadius { get; set; } public float EndRadiusVar { get; set; } public float RotatePerSecond { get; set; } public float RotatePerSecondVar { get; set; } //life public float Life { get; set; } public float LifeVar { get; set; } //发射频率 public float EmissionRate { get; set; } = 200; public Particle Emit() { var particle = this.CreateParticle(); this.InitParticle(particle); return particle; } private Particle CreateParticle() { Particle particle; if (this.Owner.ParticlePool.Count > 0) { particle = this.Owner.ParticlePool.Pop(); particle.Reset(); } else { particle = new Particle(); } return particle; } private void InitParticle(Particle particle) { var r = this.Owner.Rand; //计算位置 大小 颜色 自旋 particle.Pos = r.NextVar(this.Pos, this.PosVar); particle.StartSize = r.NextVar(this.StartSize, this.StartSizeVar, true); particle.EndSize = r.NextVar(this.EndSize, this.EndSizeVar, true); particle.StartColor = r.NextVar(this.StartColor, this.StartColorVar); particle.EndColor = r.NextVar(this.EndColor, this.EndColorVar); particle.StartSpin = r.NextVar(this.StartSpin, this.StartSpinVar); particle.EndSpin = r.NextVar(this.EndSpin, this.EndSpinVar); //计算速度方向 var speed = r.NextVar(this.Speed, this.SpeedVar); var rotAngle = r.NextVar(this.Angle, this.AngleVar); var rot = MathHelper.ToRadians(rotAngle); var dir = new Vector2((float)Math.Cos(rot), -(float)Math.Sin(rot)); particle.Dir = dir * speed; if (this.RotationIsDir) { var deltaSpin = particle.EndSpin - particle.StartSpin; particle.StartSpin = rotAngle; particle.EndSize = rotAngle + deltaSpin; } //计算圆周运动 particle.StartRadius = r.NextVar(this.StartRadius, this.StartRadiusVar, true); particle.EndRadius = r.NextVar(this.EndRadius, this.EndRadiusVar, true); particle.RotatePerSecond = r.NextVar(this.RotatePerSecond, this.RotatePerSecondVar, true); particle.Angle = rotAngle; //初始化加速度 particle.RadialAcc = r.NextVar(this.RadialAccel, this.RadialAccelVar); particle.TangentialAcc = r.NextVar(this.TangentialAccel, this.TangentialAccelVar); //生命周期 particle.Life = r.NextVar(this.Life, this.LifeVar, true); } } } ================================================ FILE: WzComparerR2.MapRender/ParticleRandom.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Microsoft.Xna.Framework; namespace WzComparerR2.MapRender { class ParticleRandom : IRandom { public ParticleRandom() : this(new Random()) { } public ParticleRandom(Random random) { this.Random = random; } public Random Random { get; private set; } private double NextMinOneToOne() { return this.Random.NextDouble() * 2 - 1; } public float NextVar(float baseValue, float varRange, bool nonNegative = false) { if (varRange == 0) { return baseValue; } var val = baseValue + (float)this.NextMinOneToOne() * varRange; if (nonNegative && val < 0) { val = 0; } return val; } public int NextVar(int baseValue, int varRange, bool nonNegative = false) { if (varRange == 0) { return baseValue; } var val = this.Random.Next(baseValue - varRange, baseValue + varRange + 1); if (nonNegative && val < 0) { val = 0; } return val; } public Vector2 NextVar(Vector2 baseValue, Vector2 varRange) { var x = this.NextVar(baseValue.X, varRange.X, false); var y = this.NextVar(baseValue.Y, varRange.Y, false); return new Vector2(x, y); } public Color NextVar(Color baseValue, Color varRange) { var baseColorVec = baseValue.ToVector4(); var varRangeVec = varRange.ToVector4(); var r = this.NextVar(baseColorVec.X, varRangeVec.X, false); var g = this.NextVar(baseColorVec.Y, varRangeVec.Y, false); var b = this.NextVar(baseColorVec.Z, varRangeVec.Z, false); var a = this.NextVar(baseColorVec.W, varRangeVec.W, false); return new Color(r, g, b, a); } public int Next(int maxValue) { return this.Random.Next(maxValue); } public bool NextPercent(float percent) { return this.Random.NextDouble() < percent; } } } ================================================ FILE: WzComparerR2.MapRender/ParticleSystem.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using WzComparerR2.Animation; namespace WzComparerR2.MapRender { public class ParticleSystem { public ParticleSystem(IRandom random) { this.Rand = random; this.ParticlePool = new Stack(); this.Groups = new List(); } public IRandom Rand { get; private set; } public Stack ParticlePool { get; private set; } public ParticleEmitter Emitter { get; private set; } public List Groups { get; private set; } public int ParticleCount { get; set; } public float Duration { get; set; } public Vector2 Gravity { get; set; } public Frame Texture { get; set; } public bool OpacityModifyRGB { get; set; } public ParticleBlendFunc BlendFuncSrc { get; set; } public ParticleBlendFunc BlendFuncDst { get; set; } private AlphaPoint[] alphaPoints; public void LoadDescription(ParticleDesc desc) { this.Texture = desc.Texture; this.ParticleCount = desc.TotalParticle; this.Duration = desc.Duration; this.OpacityModifyRGB = desc.OpacityModifyRGB; if (desc.Gravity != null) { this.Gravity = new Vector2(desc.Gravity.X, desc.Gravity.Y); } var alphaPointList = new List(); if (desc.MiddlePoint0 > 0) { alphaPointList.Add(new AlphaPoint(desc.MiddlePoint0 / 100f, desc.MiddlePointAlpha0)); } if (desc.MiddlePoint1 > 0) { alphaPointList.Add(new AlphaPoint(desc.MiddlePoint1 / 100f, desc.MiddlePointAlpha1)); } this.alphaPoints = alphaPointList.ToArray(); this.BlendFuncSrc = desc.BlendFuncSrc; this.BlendFuncDst = desc.BlendFuncDst; this.CreateEmitter(desc); } private void CreateEmitter(ParticleDesc desc) { var emitter = new ParticleEmitter(this); emitter.Angle = desc.Angle; emitter.AngleVar = desc.AngleVar; emitter.StartColor = desc.StartColor; emitter.StartColorVar = desc.StartColorVar; emitter.EndColor = desc.EndColor; emitter.EndColorVar = desc.EndColorVar; emitter.StartSize = desc.StartSize; emitter.StartSizeVar = desc.StartSizeVar; emitter.EndSize = desc.EndSize; emitter.EndSizeVar = desc.EndSizeVar; emitter.Pos = new Vector2(desc.PosX, desc.PosY); emitter.PosVar = new Vector2(desc.PosVarX, desc.PosVarY); emitter.StartSpin = desc.StartSpin; emitter.StartSpinVar = desc.StartSpinVar; emitter.EndSpin = desc.EndSpin; emitter.EndSpinVar = desc.EndSpinVar; if (desc.Gravity != null) { emitter.Speed = desc.Gravity.Speed; emitter.SpeedVar = desc.Gravity.SpeedVar; emitter.RadialAccel = desc.Gravity.RadialAccel; emitter.RadialAccelVar = desc.Gravity.RadialAccelVar; emitter.TangentialAccel = desc.Gravity.TangentialAccel; emitter.TangentialAccelVar = desc.Gravity.TangentialAccelVar; emitter.RotationIsDir = desc.Gravity.RotationIsDir; } if (desc.Radius != null) { } emitter.Life = desc.Life; emitter.LifeVar = desc.LifeVar; //计算发射速率 if (desc.Duration > 0) { emitter.EmissionRate = desc.TotalParticle / Math.Min(desc.Duration, 1); } else { float lifeMin = Math.Max(0, desc.Life - desc.LifeVar); float lifeMax = Math.Max(0, desc.Life + desc.LifeVar); var life = (lifeMin + lifeMax) / 2; if (life <= 0) { life = 1; } emitter.EmissionRate = desc.TotalParticle / life; } this.Emitter = emitter; } public ParticleGroup CreateGroup(string name = null) { var pGroup = new ParticleGroup(this); pGroup.Name = name; pGroup.Particles.Capacity = this.ParticleCount; return pGroup; } public void Update(TimeSpan elapsed) { foreach (var pGroup in this.Groups) { this.UpdateGroup(pGroup, elapsed); } } public void UpdateGroup(ParticleGroup pGroup, TimeSpan elapsed) { var time = (float)elapsed.TotalSeconds; if (pGroup.IsActive) { if (pGroup.Particles.Count < this.ParticleCount) //生成粒子 { float emitCount = this.Emitter.EmissionRate * time; var count = (int)emitCount + (this.Rand.NextPercent(emitCount % 1) ? 1 : 0); count = Math.Min(count, this.ParticleCount - pGroup.Particles.Count); for (int i = 0; i < count; i++) { var p = this.Emitter.Emit(); if (p.Life <= 0) //fix bug { continue; } pGroup.Particles.Add(p); } } //计算时间 if (this.Duration > 0) { pGroup.Time += time; if (pGroup.Time > this.Duration) { pGroup.IsActive = false; } } } //更新所有粒子 if (pGroup.Particles.Count > 0) { pGroup.Particles.ForEach(p => { p.Time += time; p.NormalizedTime = p.Life <= 0 ? 0 : (p.Time / p.Life); if (p.NormalizedTime >= 1) { return; } //更新粒子 var accDir = p.Pos; if (accDir != Vector2.Zero) //计算方向 { accDir.Normalize(); } var radial = accDir * p.RadialAcc; //法线加速度矢量 //var tangent = new Vector2(-accDir.Y, accDir.X) * p.TangentialAcc; //切线加速度矢量 var tangent = Vector2.Zero; //tangent not works in the game? var acc = this.Gravity + radial + tangent; //总加速度矢量 p.Dir += acc * time; //计算加速度 p.Pos += p.Dir * time; //计算位置 //计算旋转 p.Angle += p.RotatePerSecond * time; var rad = MathHelper.Lerp(p.StartRadius, p.EndRadius, p.NormalizedTime); if (rad > 0) { var radian = MathHelper.ToRadians(p.Angle); accDir = new Vector2((float)Math.Cos(radian), (float)Math.Sin(radian)); p.Pos += accDir * rad * time; } }); //回收粒子 pGroup.Particles.RemoveAll(p => { if (p.NormalizedTime >= 1) { this.CollectParticle(p); return true; } else { return false; } }); } } private void CollectParticle(Particle particle) { this.ParticlePool.Push(particle); } public void Draw(SpriteBatch sprite, Vector2 position) { foreach (var pGroup in this.Groups) { this.DrawGroup(sprite, pGroup, position); } } public void DrawGroup(SpriteBatch sprite, ParticleGroup pGroup, Vector2 position) { if (this.Texture?.Texture == null) { return; } var textureSize = this.Texture.AtlasRect?.Size.ToVector2() ?? new Vector2(this.Texture.Texture.Width, this.Texture.Texture.Height); var rad = textureSize.Length() / (float)Math.Sqrt(2); var origin = this.Texture.Origin.ToVector2(); position += pGroup.Position; foreach (var p in pGroup.Particles) { var color = Lerp(p.StartColor, p.EndColor, p.NormalizedTime); var size = MathHelper.Lerp(p.StartSize, p.EndSize, p.NormalizedTime); var scale = size / rad; var rot = MathHelper.ToRadians(MathHelper.Lerp(p.StartSpin, p.EndSpin, p.NormalizedTime)); //重新计算alpha if (this.alphaPoints.Length > 0) { color.A = (byte)CalcAlphaFromPoints(p); } if (this.OpacityModifyRGB) { if (color.R == 0 && color.G == 0 && color.B == 0) { color.R = color.G = color.B = color.A; } else { //float alpha = color.A / 255.0f; ; //color.R = (byte)(color.R * alpha); //color.G = (byte)(color.G * alpha); //color.B = (byte)(color.B * alpha); } } var pos = p.Pos + position; sprite.Draw(this.Texture.Texture, pos, this.Texture.AtlasRect, color, rot, origin, scale, SpriteEffects.None, 0); } } private int CalcAlphaFromPoints(Particle p) { int? alpha = null; var time = p.NormalizedTime; if (time < this.alphaPoints[0].Time) { alpha = Lerp(p.StartColor.A, this.alphaPoints[0].Alpha, 0, this.alphaPoints[0].Time, time); } for (int i = 1; alpha == null && i < this.alphaPoints.Length; i++) { if (time < this.alphaPoints[i].Time) { alpha = Lerp(this.alphaPoints[i - 1].Alpha, this.alphaPoints[i].Alpha, this.alphaPoints[i - 1].Time, this.alphaPoints[i].Time, time); break; } } if (alpha == null) { alpha = Lerp(this.alphaPoints[this.alphaPoints.Length - 1].Alpha, p.EndColor.A, this.alphaPoints[this.alphaPoints.Length - 1].Time, 1, time); } return alpha.Value; } private Color Lerp(Color color1, Color color2, float amount) { return new Color( (byte)MathHelper.Lerp(color1.R, color2.R, amount), (byte)MathHelper.Lerp(color1.G, color2.G, amount), (byte)MathHelper.Lerp(color1.B, color2.B, amount), (byte)MathHelper.Lerp(color1.A, color2.A, amount) ); } private int Lerp(int int1, int int2, float from, float to, float value) { if (from == to) { return int2; } var amount = (value - from) / (to - from); return (int)MathHelper.Lerp(int1, int2, amount); } private struct AlphaPoint { public AlphaPoint(float time, int alpha) { this.Time = time; this.Alpha = alpha; } public float Time; public int Alpha; } } public class ParticleGroup { public ParticleGroup(ParticleSystem owner) { this.Owner = owner; this.Particles = new List(); } public ParticleSystem Owner { get; private set; } public string Name { get; set; } public List Particles { get; private set; } public Vector2 Position { get; set; } public float Time { get; set; } public bool IsActive { get; set; } public void Active() { this.IsActive = true; this.Time = 0; } } } ================================================ FILE: WzComparerR2.MapRender/PatchVisibility.cs ================================================ using System; using System.Collections.Generic; using System.Text; using WzComparerR2.MapRender.Patches; namespace WzComparerR2.MapRender { public class PatchVisibility { public PatchVisibility() { this.dictVisible = new Dictionary(); this.tagsVisible = new SortedDictionary(); this.questVisible = new Dictionary(); this.questexVisible = new Dictionary, int>(); foreach (RenderObjectType type in Enum.GetValues(typeof(RenderObjectType))) { this.dictVisible[type] = true; } this.PortalInEditMode = false; this.DefaultTagVisible = true; this.IlluminantClusterVisible = true; } public bool BackVisible { get { return IsVisible(RenderObjectType.Back); } set { this.SetVisible(RenderObjectType.Back, value); } } public bool ReactorVisible { get { return IsVisible(RenderObjectType.Reactor); } set { this.SetVisible(RenderObjectType.Reactor, value); } } public bool ObjVisible { get { return IsVisible(RenderObjectType.Obj); } set { this.SetVisible(RenderObjectType.Obj, value); } } public bool TileVisible { get { return IsVisible(RenderObjectType.Tile); } set { this.SetVisible(RenderObjectType.Tile, value); } } public bool NpcVisible { get { return IsVisible(RenderObjectType.Npc); } set { this.SetVisible(RenderObjectType.Npc, value); } } public bool MobVisible { get { return IsVisible(RenderObjectType.Mob); } set { this.SetVisible(RenderObjectType.Mob, value); } } public bool FootHoldVisible { get { return IsVisible(RenderObjectType.Foothold); } set { this.SetVisible(RenderObjectType.Foothold, value); } } public bool LadderRopeVisible { get { return IsVisible(RenderObjectType.LadderRope); } set { this.SetVisible(RenderObjectType.LadderRope, value); } } public bool SkyWhaleVisible { get; set; } public bool IlluminantClusterPathVisible { get; set; } public bool PortalVisible { get { return IsVisible(RenderObjectType.Portal); } set { this.SetVisible(RenderObjectType.Portal, value); } } public bool PortalInEditMode { get; set; } public bool IlluminantClusterVisible { get; set; } public bool FrontVisible { get { return IsVisible(RenderObjectType.Front); } set { this.SetVisible(RenderObjectType.Front, value); } } public bool NpcNameVisible { get { return IsVisible(RenderObjectType.NpcName); } set { this.SetVisible(RenderObjectType.NpcName, value); } } public bool MobNameVisible { get { return IsVisible(RenderObjectType.MobName); } set { this.SetVisible(RenderObjectType.MobName, value); } } public bool EffectVisible { get { return IsVisible(RenderObjectType.Effect); } set { this.SetVisible(RenderObjectType.Effect, value); } } public IReadOnlyDictionary TagsVisible { get { return this.tagsVisible; } } public bool DefaultTagVisible { get; set; } private Dictionary dictVisible; private SortedDictionary tagsVisible; private Dictionary questVisible; private Dictionary, int> questexVisible; public bool IsVisible(RenderObjectType type) { bool visible; dictVisible.TryGetValue(type, out visible); return visible; } public bool IsTagVisible(string tag) { return this.tagsVisible.TryGetValue(tag, out var isVisible) ? isVisible : this.DefaultTagVisible; } public void SetTagVisible(string tag, bool isVisible) { this.tagsVisible[tag] = isVisible; } public void ResetTagVisible() { this.tagsVisible.Clear(); } public void ResetTagVisible(string[] tags) { foreach (var tag in tags) { this.tagsVisible.Remove(tag); } } private void SetVisible(RenderObjectType type, bool visible) { this.dictVisible[type] = visible; } public bool IsQuestVisible(int questID, int questState) { int visible; if (!this.questVisible.TryGetValue(questID, out visible)) { return true; } return visible == -1 || visible == questState; } public bool IsQuestVisibleExact(int questID, int questState) { int visible; if (!this.questVisible.TryGetValue(questID, out visible)) { return false; } return visible == questState; } public void SetQuestVisible(int questID, int questState) { this.questVisible[questID] = questState; } public bool IsQuestVisible(int questID, string qkey, int questState) { int visible; if (!this.questexVisible.TryGetValue(new Tuple(questID, qkey), out visible)) { return true; } return visible == -1 || visible == questState; } public bool IsQuestVisibleExact(int questID, string qkey, int questState) { int visible; if (!this.questexVisible.TryGetValue(new Tuple(questID, qkey), out visible)) { return false; } return visible == questState; } public void SetQuestVisible(int questID, string qkey, int questState) { this.questexVisible[new Tuple(questID, qkey)] = questState; } } } ================================================ FILE: WzComparerR2.MapRender/Patches/BackPatch.cs ================================================ #if MapRenderV1 using System; using System.Collections.Generic; using System.Text; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; namespace WzComparerR2.MapRender.Patches { public class BackPatch : RenderPatch { public BackPatch() { } int cx; int cy; int rx; int ry; int screenMode; TileMode tileMode; // RenderArgs Vector2 origin2; Rectangle tileRect; public int Cx { get { return cx; } set { cx = value; } } public int Cy { get { return cy; } set { cy = value; } } public int Rx { get { return rx; } set { rx = value; } } public int Ry { get { return ry; } set { ry = value; } } public int ScreenMode { get { return screenMode; } set { screenMode = value; } } public TileMode TileMode { get { return tileMode; } set { tileMode = value; } } public override void Update(GameTime gameTime, RenderEnv env) { base.Update(gameTime, env); if (this.RenderArgs.CurrentIndex < 0) return; RenderFrame frame = this.Frames[this.RenderArgs.CurrentIndex]; //计算背景的二次偏移 int cx = (this.cx == 0 ? frame.Texture.Width : this.cx), cy = (this.cy == 0 ? frame.Texture.Height : this.cy); Vector2 origin2 = new Vector2(); double ms = gameTime.TotalGameTime.TotalSeconds; if ((this.TileMode & TileMode.ScrollHorizontial) != 0) { origin2.X = (float)((this.rx * 5 * ms) % cx);// +this.Camera.Center.X * (100 - Math.Abs(this.rx)) / 100; } else { origin2.X = env.Camera.Center.X * (100 + this.rx) / 100; } if ((this.TileMode & TileMode.ScrollVertical) != 0) { origin2.Y = (float)((this.ry * 5 * ms) % cy);// +this.Camera.Center.Y * (100 - Math.Abs(this.ry)) / 100; } else { //origin2.Y = env.Camera.Center.Y * (100 + this.ry) / 100; origin2.Y = (env.Camera.Center.Y) * (100 + this.ry) / 100; //屏幕大小适配补丁 } origin2.Y += (env.Camera.Height - 600); this.origin2 = origin2; //计算平铺绘制参数 if (this.TileMode != TileMode.None) { this.RenderArgs.Culled = false; Rectangle originRect = env.Camera.MeasureDrawingRect(frame.Texture.Width, frame.Texture.Height, this.Position + origin2, frame.Origin, this.Flip); int l, t, r, b; if ((this.TileMode & TileMode.Horizontal) != 0) { l = (int)Math.Floor((double)(env.Camera.ClipRect.Left - originRect.X) / cx); r = (int)Math.Ceiling((double)(env.Camera.ClipRect.Right - originRect.X) / cx); } else { l = 0; r = 1; } if ((this.TileMode & TileMode.Vertical) != 0) { t = (int)Math.Floor((double)(env.Camera.ClipRect.Top - originRect.Y) / cy); b = (int)Math.Ceiling((double)(env.Camera.ClipRect.Bottom - originRect.Y) / cy); } else { t = 0; b = 1; } this.tileRect = new Rectangle(l, t, r - l, b - t); } else { this.RenderArgs.Culled = !env.Camera.CheckSpriteVisible(frame, this.Position + this.origin2, this.Flip); this.tileRect = new Rectangle(0, 0, 1, 1); } if (this.screenMode != 0) { this.RenderArgs.Culled |= (this.screenMode != env.Camera.DisplayMode + 1); } this.RenderArgs.DisplayRectangle = Rectangle.Empty; } public override void Draw(GameTime gameTime, RenderEnv env) { if (this.RenderArgs.CurrentIndex < 0) { return; } RenderFrame frame = this.Frames[this.RenderArgs.CurrentIndex]; float curPer = this.RenderArgs.CurrentPercent; float alpha = (frame.A0 * (1 - curPer) + frame.A1 * curPer) / 255.0f * this.Alpha / 255.0f; Vector2 pos = this.Position + this.origin2 - env.Camera.Origin; pos = new Vector2((float)Math.Floor(pos.X), (float)Math.Floor(pos.Y)); int cx = (this.cx == 0 ? frame.Texture.Width : this.cx), cy = (this.cy == 0 ? frame.Texture.Height : this.cy); for (int j = this.tileRect.Top; j < this.tileRect.Bottom; j++) { for (int i = this.tileRect.Left; i < this.tileRect.Right; i++) { Vector2 pos2 = pos + new Vector2(i * cx, j * cy); env.Sprite.Draw(frame.Texture, pos2, null, new Color(Color.White, alpha), 0f, this.Flip? new Vector2(frame.Texture.Width - frame.Origin.X, frame.Origin.Y) : frame.Origin, 1f, this.Flip ? SpriteEffects.FlipHorizontally : SpriteEffects.None, 0f); } } } } } #endif ================================================ FILE: WzComparerR2.MapRender/Patches/FootholdPatch.cs ================================================ #if MapRenderV1 using System; using System.Collections.Generic; using System.Text; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; namespace WzComparerR2.MapRender.Patches { public class FootholdPatch : RenderPatch { public FootholdPatch() { } private int x1; private int y1; private int x2; private int y2; private int prev; private int next; private int piece; public int X1 { get { return x1; } set { x1 = value; } } public int Y1 { get { return y1; } set { y1 = value; } } public int X2 { get { return x2; } set { x2 = value; } } public int Y2 { get { return y2; } set { y2 = value; } } public int Prev { get { return prev; } set { prev = value; } } public int Next { get { return next; } set { next = value; } } public int Piece { get { return piece; } set { piece = value; } } public override void Update(GameTime gameTime, RenderEnv env) { } public override void Draw(GameTime gameTime, RenderEnv env) { Vector2 origin = env.Camera.Origin; Point p1 = new Point(x1 - (int)origin.X, y1 - (int)origin.Y); Point p2 = new Point(x2 - (int)origin.X, y2 - (int)origin.Y); Color color = MathHelper2.Lerp(colorTable, (float)gameTime.TotalGameTime.TotalMilliseconds % 10000 / 2000); if (x1 != x2 && y1 != y2) { } env.Sprite.DrawLine(p1, p2, 2, color); } private static Color[] colorTable = new Color[] { Color.Red, Color.Yellow, Color.Blue, Color.Purple, Color.Red}; } } #endif ================================================ FILE: WzComparerR2.MapRender/Patches/LadderRopePatch.cs ================================================ #if MapRenderV1 using System; using System.Collections.Generic; using System.Text; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; namespace WzComparerR2.MapRender.Patches { public class LadderRopePatch : RenderPatch { public LadderRopePatch() { } public int X { get; set; } public int Y1 { get; set; } public int Y2 { get; set; } public int L { get; set; } public int Uf { get; set; } public int Page { get; set; } public override void Update(GameTime gameTime, RenderEnv env) { } public override void Draw(GameTime gameTime, RenderEnv env) { Vector2 origin = env.Camera.Origin; Point p1 = new Point(this.X - (int)origin.X, this.Y1 - (int)origin.Y); Point p2 = new Point(this.X - (int)origin.X, this.Y2 - (int)origin.Y); Color color = MathHelper2.Lerp(colorTable, (float)gameTime.TotalGameTime.TotalMilliseconds % 10000 / 2000); env.Sprite.DrawLine(p1, p2, 5, color); } private static Color[] colorTable = new Color[] { Color.Red, Color.Yellow, Color.Blue, Color.Purple, Color.Red}; } } #endif ================================================ FILE: WzComparerR2.MapRender/Patches/LifePatch.cs ================================================ #if MapRenderV1 using System; using System.Collections.Generic; using System.Text; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; namespace WzComparerR2.MapRender.Patches { public class LifePatch : RenderPatch { public LifePatch() { actions = new Dictionary(); lifeInfo = new LifeInfo(); } private Dictionary actions; private int lifeID; private int foothold; private LifeInfo lifeInfo; public int LifeID { get { return lifeID; } set { lifeID = value; } } public int Foothold { get { return foothold; } set { foothold = value; } } public LifeInfo LifeInfo { get { return lifeInfo; } } public Dictionary Actions { get { return actions; } } public void SwitchToDefaultAction() { RenderAnimate action; if (!this.actions.TryGetValue("stand", out action) && !this.actions.TryGetValue("fly", out action)) { foreach (var kv in this.actions) { action = kv.Value; break; } } if (this.Frames != action) { this.Frames = action; } } protected override void Dispose(bool disposing) { if (disposing) { foreach (var kv in this.actions) { foreach (RenderFrame frame in kv.Value) { frame.Texture.Dispose(); } } } } } } #endif ================================================ FILE: WzComparerR2.MapRender/Patches/ObjTilePatch.cs ================================================ #if MapRenderV1 using System; using System.Collections.Generic; using System.Text; using Microsoft.Xna.Framework; namespace WzComparerR2.MapRender.Patches { public class ObjTilePatch : RenderPatch { public override void Update(GameTime gameTime, RenderEnv env) { base.Update(gameTime, env); this.RenderArgs.DisplayRectangle = Rectangle.Empty; } } } #endif ================================================ FILE: WzComparerR2.MapRender/Patches/PortalPatch.cs ================================================ #if MapRenderV1 using System; using System.Collections.Generic; using System.Text; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; namespace WzComparerR2.MapRender.Patches { public class PortalPatch : RenderPatch { public PortalPatch() { this.EditMode = false; } RenderAnimate aniStart; RenderAnimate aniContinue; RenderAnimate aniExit; RenderAnimate aniEditor; string pn; int pt; int tm; string tn; string script; public RenderAnimate AniStart { get { return aniStart; } set { aniStart = value; } } public RenderAnimate AniContinue { get { return aniContinue; } set { aniContinue = value; } } public RenderAnimate AniExit { get { return aniExit; } set { aniExit = value; } } public RenderAnimate AniEditor { get { return aniEditor; } set { aniEditor = value; } } public string PortalName { get { return pn; } set { pn = value; } } public int PortalType { get { return pt; } set { pt = value; } } public int ToMap { get { return tm; } set { tm = value; } } public string ToName { get { return tn; } set { tn = value; } } public string Script { get { return script; } set { script = value; } } public bool EditMode { get; set; } public override void Update(GameTime gameTime, RenderEnv env) { if (this.EditMode) { this.Frames = this.aniEditor; } else { this.Frames = this.aniContinue; } base.Update(gameTime, env); //添加鼠标感应范围 if (this.EditMode && this.RenderArgs.DisplayRectangle.IsEmpty) { Point p = new Point((int)this.Position.X, (int)this.Position.Y); this.RenderArgs.DisplayRectangle = new Rectangle(p.X - 25, p.Y - 50, 50, 50); } } protected override void Dispose(bool disposing) { if (disposing) { RenderAnimate[] animeLst = { this.aniContinue, this.aniEditor, this.aniExit, this.aniStart }; foreach (RenderAnimate ani in animeLst) { if (ani != null) { foreach (RenderFrame frame in ani) { frame.Texture.Dispose(); } } } } } } } #endif ================================================ FILE: WzComparerR2.MapRender/Patches/ReactorPatch.cs ================================================ #if MapRenderV1 using System; using System.Collections.Generic; using System.Text; namespace WzComparerR2.MapRender.Patches { public class ReactorPatch : RenderPatch { public ReactorPatch() { this.stages = new List(); } int reactorID; string reactorName; int reactorTime; List stages; public int ReactorID { get { return reactorID; } set { reactorID = value; } } public string ReactorName { get { return reactorName; } set { reactorName = value; } } public int ReactorTime { get { return reactorTime; } set { reactorTime = value; } } public List Stages { get { return stages; } } protected override void Dispose(bool disposing) { if (disposing) { foreach (var stage in this.stages) { foreach (RenderFrame frame in stage) { frame.Texture.Dispose(); } } } } } } #endif ================================================ FILE: WzComparerR2.MapRender/Patches/RenderObjectType.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace WzComparerR2.MapRender.Patches { public enum RenderObjectType { Back = 1, Reactor, Obj, Tile, Npc, Mob, Foothold, LadderRope, Portal, Front, NpcName, MobName, Effect, } } ================================================ FILE: WzComparerR2.MapRender/Patches/RenderPatch.cs ================================================ #if MapRenderV1 using System; using System.Collections.Generic; using System.Text; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; namespace WzComparerR2.MapRender.Patches { public class RenderPatch : IDisposable { public RenderPatch() { zIndex = new int[8]; flip = false; renderArgs = new RenderArgs(); alpha = 255; } string name; Vector2 position; bool flip; int[] zIndex; RenderAnimate frames; RenderObjectType objectType; TimeSpan playStateChangedTime; RenderArgs renderArgs; int alpha; public string Name { get { return name; } set { name = value; } } public Vector2 Position { get { return position; } set { position = value; } } public bool Flip { get { return flip; } set { flip = value; } } public int[] ZIndex { get { return zIndex; } } public RenderAnimate Frames { get { return frames; } set { frames = value; } } public virtual RenderObjectType ObjectType { get { return objectType; } set { objectType = value; } } public TimeSpan PlayStateChangedTime { get { return playStateChangedTime; } set { playStateChangedTime = value; } } public RenderArgs RenderArgs { get { return renderArgs; } } public int Alpha { get { return alpha; } set { alpha = value; } } public int UpdateCurrentFrameIndex(TimeSpan totalTime) { float framePlayed; return UpdateCurrentFrameIndex(totalTime, out framePlayed); } public virtual int UpdateCurrentFrameIndex(TimeSpan totalTime, out float framePlayed) { framePlayed = 0f; if (this.frames == null || this.frames.Length == 0) { return -1; } int[] timeLine = this.frames.TimeLine; if (timeLine[timeLine.Length - 1] <= 0) { return 0; } double ms = (totalTime.TotalMilliseconds - playStateChangedTime.TotalMilliseconds); int startTimeLine = 1; if (this.frames.Repeat <= 0 || this.frames.Repeat >= this.frames.Length) //使用全帧绘制 { ms %= timeLine[timeLine.Length - 1]; } else { if (ms < timeLine[timeLine.Length - 1]) //首次绘制 { ms %= timeLine[timeLine.Length - 1]; } else //循环段 { startTimeLine = frames.Repeat + 1; ms -= timeLine[timeLine.Length - 1]; int newDelay = timeLine[timeLine.Length - 1] - timeLine[frames.Repeat]; if (newDelay <= 0) return frames.Repeat; ms = ms % newDelay + timeLine[frames.Repeat]; } } for (int i = startTimeLine; i < timeLine.Length; i++) { if (ms < timeLine[i]) { framePlayed = (float)((ms - timeLine[i - 1]) / (timeLine[i] - timeLine[i - 1])); return i - 1; } } return frames.Repeat; } public virtual void Update(GameTime gameTime, RenderEnv env) { float played; int index = this.UpdateCurrentFrameIndex(gameTime.TotalGameTime, out played); if (index > -1) { //获取当前帧绘制参数 RenderFrame frame = this.Frames[index]; this.RenderArgs.CurrentIndex = index; this.RenderArgs.CurrentPercent = played; Rectangle rect; this.RenderArgs.Culled = !env.Camera.CheckSpriteVisible(frame, this.position, this.flip, out rect); this.renderArgs.DisplayRectangle = rect; } else { this.RenderArgs.CurrentIndex = -1; this.RenderArgs.CurrentPercent = 0f; this.RenderArgs.Culled = true; } } public virtual void Draw(GameTime gameTime, RenderEnv env) { if (this.RenderArgs.CurrentIndex < 0) { return; } RenderFrame frame = this.Frames[this.RenderArgs.CurrentIndex]; Vector2 cameraOrigin = env.Camera.Origin; float curPer = this.RenderArgs.CurrentPercent; float alpha = (frame.A0 * (1 - curPer) + frame.A1 * curPer) / 255.0f * this.alpha / 255.0f; env.Sprite.Draw(frame.Texture, this.Position - cameraOrigin, null, new Color(Color.White, alpha), 0f, this.flip ? new Vector2(frame.Texture.Width - frame.Origin.X, frame.Origin.Y) : frame.Origin, 1f, this.flip ? SpriteEffects.FlipHorizontally : SpriteEffects.None, 0f); } public void Dispose() { this.Dispose(true); } protected virtual void Dispose(bool disposing) { if (disposing) { if (this.Frames != null && this.Frames.Length > 0) { foreach (RenderFrame frame in this.Frames) { if (!frame.Texture.IsDisposed) { frame.Texture.Dispose(); } } } } } public override string ToString() { return base.ToString() + " " + this.name; } } } #endif ================================================ FILE: WzComparerR2.MapRender/Patches/TooltipPatch.cs ================================================ #if MapRenderV1 using System; using System.Collections.Generic; using System.Text; using Microsoft.Xna.Framework; namespace WzComparerR2.MapRender.Patches { public class TooltipPatch : RenderPatch { public TooltipPatch() { } public int X1 { get; set; } public int X2 { get; set; } public int Y1 { get; set; } public int Y2 { get; set; } public int CharX1 { get; set; } public int CharX2 { get; set; } public int CharY1 { get; set; } public int CharY2 { get; set; } public string Title { get; set; } public string Desc { get; set; } } } #endif ================================================ FILE: WzComparerR2.MapRender/Patches2/BackItem.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using WzComparerR2.WzLib; namespace WzComparerR2.MapRender.Patches2 { public class BackItem : SceneItem { public string BS { get; set; } public int Ani { get; set; } public string No { get; set; } public string SpineAni { get; set; } public int? SpineNo { get; set; } public int X { get; set; } public int Y { get; set; } public int Cx { get; set; } public int Cy { get; set; } public int Rx { get; set; } public int Ry { get; set; } public int Alpha { get; set; } public TileMode TileMode { get; set; } public int ScreenMode { get; set; } public bool Flip { get; set; } public bool IsFront { get; set; } public List Quest { get; private set; } = new List(); public ItemView View { get; set; } public static BackItem LoadFromNode(Wz_Node node) { var item = new BackItem() { BS = node.Nodes["bS"].GetValueEx(null), Ani = node.Nodes["ani"].GetValueEx(0), No = node.Nodes["no"].GetValueEx(null), SpineAni = node.Nodes["spineAni"].GetValueEx(null), SpineNo = node.Nodes["spineNo"].GetValueEx(null), X = node.Nodes["x"].GetValueEx(0), Y = node.Nodes["y"].GetValueEx(0), Cx = node.Nodes["cx"].GetValueEx(0), Cy = node.Nodes["cy"].GetValueEx(0), Rx = node.Nodes["rx"].GetValueEx(0), Ry = node.Nodes["ry"].GetValueEx(0), Alpha = node.Nodes["a"].GetValueEx(255), TileMode = GetBackTileMode(node.Nodes["type"].GetValueEx(0)), ScreenMode = node.Nodes["screenMode"].GetValueEx(0), Flip = node.Nodes["f"].GetValueEx(false), IsFront = node.Nodes["front"].GetValueEx(false), }; string backTags = node.Nodes["backTags"].GetValueEx(null); if (!string.IsNullOrWhiteSpace(backTags)) { item.Tags = backTags.Split(',').Select(tag => tag.Trim()).ToArray(); if (int.TryParse(backTags, out int questID)) { item.Quest.Add(new QuestInfo(questID, 1)); } } return item; } private static TileMode GetBackTileMode(int type) { switch (type) { case 0: return TileMode.None; case 1: return TileMode.Horizontal; case 2: return TileMode.Vertical; case 3: return TileMode.BothTile; case 4: return TileMode.Horizontal | TileMode.ScrollHorizontal; case 5: return TileMode.Vertical | TileMode.ScrollVertical; case 6: return TileMode.BothTile | TileMode.ScrollHorizontal; case 7: return TileMode.BothTile | TileMode.ScrollVertical; default: return TileMode.None; } } public class ItemView { /// /// 时间关联,单位为毫秒。 /// public int Time { get; set; } /// /// 动画资源。 /// public object Animator { get; set; } } } } ================================================ FILE: WzComparerR2.MapRender/Patches2/FootholdItem.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using WzComparerR2.WzLib; namespace WzComparerR2.MapRender.Patches2 { public class FootholdItem : SceneItem { public int ID { get; set; } public int X1 { get; set; } public int Y1 { get; set; } public int X2 { get; set; } public int Y2 { get; set; } public int Prev { get; set; } public int Next { get; set; } public int Piece { get; set; } public static FootholdItem LoadFromNode(Wz_Node node) { var item = new FootholdItem() { X1 = node.Nodes["x1"].GetValueEx(0), Y1 = node.Nodes["y1"].GetValueEx(0), X2 = node.Nodes["x2"].GetValueEx(0), Y2 = node.Nodes["y2"].GetValueEx(0), Prev = node.Nodes["prev"].GetValueEx(0), Next = node.Nodes["next"].GetValueEx(0), Piece = node.Nodes["piece"].GetValueEx(0), }; return item; } } } ================================================ FILE: WzComparerR2.MapRender/Patches2/IlluminantClusterItem.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Microsoft.Xna.Framework; using WzComparerR2.Rendering; using WzComparerR2.WzLib; using WzComparerR2.Animation; namespace WzComparerR2.MapRender.Patches2 { public class IlluminantClusterItem : SceneItem { public Point Start { get; set; } public Point End { get; set; } public int Size { get; set; } public double Speed { get; set; } public int Particle { get; set; } public int StartPoint { get; set; } public int EndPoint { get; set; } public ItemView StartView { get; set; } public ItemView EndView { get; set; } public static IlluminantClusterItem LoadFromNode(Wz_Node node) { var item = new IlluminantClusterItem() { Start = node.Nodes["start"].GetValueEx(null)?.ToPoint() ?? Point.Zero, End = node.Nodes["end"].GetValueEx(null)?.ToPoint() ?? Point.Zero, Size = node.Nodes["size"].GetValueEx(0), Speed = node.Nodes["speed"].GetValueEx(0), Particle = node.Nodes["particle"].GetValueEx(0), StartPoint = node.Nodes["startPoint"].GetValueEx(0), EndPoint = node.Nodes["endPoint"].GetValueEx(0), }; return item; } public class ItemView { public int Time { get; set; } public object Animator { get; set; } } } } ================================================ FILE: WzComparerR2.MapRender/Patches2/ItemEvent.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace WzComparerR2.MapRender.Patches2 { public class ItemEvent { public ItemEvent(string index, string collision, string animation, string slotName, string target, List actionKeys) { Index = index; Collision = collision; Animation = animation; SlotName = slotName; Target = target; ActionKeys = actionKeys; } public string Index { get; set; } public string Collision { get; set; } public string Animation { get; set; } public string SlotName { get; set; } public string Target { get; set; } public List ActionKeys { get; set; } } } ================================================ FILE: WzComparerR2.MapRender/Patches2/LadderRopeItem.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using WzComparerR2.WzLib; namespace WzComparerR2.MapRender.Patches2 { public class LadderRopeItem : SceneItem { public int X { get; set; } public int Y1 { get; set; } public int Y2 { get; set; } public int L { get; set; } public int Uf { get; set; } public int Page { get; set; } public static LadderRopeItem LoadFromNode(Wz_Node node) { var item = new LadderRopeItem() { X = node.Nodes["x"].GetValueEx(0), Y1 = node.Nodes["y1"].GetValueEx(0), Y2 = node.Nodes["y2"].GetValueEx(0), L = node.Nodes["l"].GetValueEx(0), Uf = node.Nodes["uf"].GetValueEx(0), Page = node.Nodes["page"].GetValueEx(0), }; return item; } } } ================================================ FILE: WzComparerR2.MapRender/Patches2/LifeItem.cs ================================================ using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Text; using Microsoft.Xna.Framework; using WzComparerR2.Rendering; using WzComparerR2.WzLib; namespace WzComparerR2.MapRender.Patches2 { public class LifeItem : SceneItem { public int ID { get; set; } public LifeType Type { get; set; } public int X { get; set; } public int Y { get; set; } public int MobTime { get; set; } public bool Flip { get; set; } public bool Hide { get; set; } public int Fh { get; set; } public int Cy { get; set; } public int Rx0 { get; set; } public int Rx1 { get; set; } public List Quest { get; private set; } = new List(); public List> Date { get; set; } public ItemView View { get; set; } public LifeInfo LifeInfo { get; set; } public bool HideName { get; set; } public CustomFontFunc CustomFont { get; set; } public static LifeItem LoadFromNode(Wz_Node node) { var item = new LifeItem() { ID = node.Nodes["id"].GetValueEx(0), Type = ParseLifeType(node.Nodes["type"].GetValueEx(null)), X = node.Nodes["x"].GetValueEx(0), Y = node.Nodes["y"].GetValueEx(0), MobTime = node.Nodes["mobTime"].GetValueEx(0), Flip = node.Nodes["f"].GetValueEx(false), Hide = node.Nodes["hide"].GetValueEx(false), Fh = node.Nodes["fh"].GetValueEx(0), Cy = node.Nodes["cy"].GetValueEx(0), Rx0 = node.Nodes["rx0"].GetValueEx(0), Rx1 = node.Nodes["rx1"].GetValueEx(0) }; item.Date = new List>(); if (item.Type == LifeType.Npc) { string path = $@"Npc\{item.ID:D7}.img"; var npcNode = PluginBase.PluginManager.FindWz(path); int? npcLink = npcNode?.FindNodeByPath(@"info\link").GetValueEx(); if (npcLink != null) { path = $@"Npc\{npcLink.Value:D7}.img"; npcNode = PluginBase.PluginManager.FindWz(path); } if (npcNode != null) { // TODO: this is totally wrong, we should load this part in StateMachineAnimator foreach (Wz_Node conditionNode in npcNode.Nodes.Where(n => n.Text.StartsWith("condition"))) { foreach (Wz_Node questNode in conditionNode.Nodes) { if (int.TryParse(questNode.Text, out int questID) && questNode.Value != null) { item.Quest.Add(new QuestInfo(questID, Convert.ToInt32(questNode.Value))); } } if (conditionNode.Nodes["dateStart"] != null || conditionNode.Nodes["dateEnd"] != null) { item.Date.Add(Tuple.Create(conditionNode.Nodes["dateStart"].GetValueEx(0), conditionNode.Nodes["dateEnd"].GetValueEx(0))); } } } } return item; } public static CustomFontFunc LoadCustomFontFunc(Wz_Node node) { var customFontFunc = new CustomFontFunc() { Font = node.Nodes["font"].GetValueEx(null), FontSize = node.Nodes["fontSize"]?.GetValue(), }; string fontColor = node.Nodes["fontColor"].GetValueEx(null); if (fontColor != null && int.TryParse(fontColor, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out int argbColor)) { customFontFunc.FontColor = MonogameUtils.ToXnaColor(argbColor); } return customFontFunc; } private static LifeType ParseLifeType(string text) { switch (text) { case "m": return LifeType.Mob; case "n": return LifeType.Npc; default: return LifeType.Unknown; } } public class ItemView { /// /// 时间关联,单位为毫秒。 /// public int Time { get; set; } /// /// 动画资源。 /// public object Animator { get; set; } } public enum LifeType { Unknown = 0, Mob = 1, Npc = 2 } public class CustomFontFunc { public string Font { get; set; } public Color? FontColor { get; set; } public int? FontSize { get; set; } } } } ================================================ FILE: WzComparerR2.MapRender/Patches2/ObjItem.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using WzComparerR2.PluginBase; using WzComparerR2.WzLib; namespace WzComparerR2.MapRender.Patches2 { public class ObjItem : SceneItem { public string OS { get; set; } public string L0 { get; set; } public string L1 { get; set; } public string L2 { get; set; } public int X { get; set; } public int Y { get; set; } public int Z { get; set; } public int MoveType { get; set; } public int MoveW { get; set; } public int MoveH { get; set; } public int MoveP { get; set; } public int MoveDelay { get; set; } public bool Flip { get; set; } public bool Light { get; set; } public string SpineAni { get; set; } public List Quest { get; private set; } = new List(); public List Questex { get; private set; } = new List(); public List Events { get; private set; } = new List(); public ItemView View { get; set; } public static ObjItem LoadFromNode(Wz_Node node) { var item = new ObjItem() { OS = node.Nodes["oS"].GetValueEx(null), L0 = node.Nodes["l0"].GetValueEx(null), L1 = node.Nodes["l1"].GetValueEx(null), L2 = node.Nodes["l2"].GetValueEx(null), X = node.Nodes["x"].GetValueEx(0), Y = node.Nodes["y"].GetValueEx(0), Z = node.Nodes["z"].GetValueEx(0), MoveType = 0, MoveW = 0, MoveH = 0, MoveP = 5000, MoveDelay = 0, Flip = node.Nodes["f"].GetValueEx(false), Light = node.Nodes["light"].GetValueEx(0) != 0, SpineAni = node.Nodes["spineAni"].GetValueEx(null), }; string objTags = node.Nodes["tags"].GetValueEx(null); if (!string.IsNullOrWhiteSpace(objTags)) { item.Tags = objTags.Split(',').Select(tag => tag.Trim()).ToArray(); } if (item.Tags != null) { int questID; foreach (string tag in item.Tags) { if (int.TryParse(tag, out questID) || (tag.StartsWith("q") && int.TryParse(tag.Substring(1), out questID))) { item.Quest.Add(new QuestInfo(questID, 1)); } } } if (node.Nodes["quest"] != null) { foreach (Wz_Node questNode in node.Nodes["quest"].Nodes) { if (int.TryParse(questNode.Text, out int questID)) { item.Quest.Add(new QuestInfo(questID, Convert.ToInt32(questNode.Value))); } } } if (node.Nodes["questex"] != null) { foreach (Wz_Node questNode in node.Nodes["questex"].Nodes) { if (int.TryParse(questNode.Text, out int questID)) { Wz_Node keyNode = questNode.Nodes["key"]; Wz_Node valueNode = questNode.Nodes["value"]; if (keyNode != null && valueNode != null) { item.Questex.Add(new QuestExInfo(questID, keyNode.GetValueEx(null), valueNode.GetValueEx(-1))); } } } } if (node.Nodes["event"] != null) { foreach (Wz_Node eventNode in node.Nodes["event"].Nodes) { var index = eventNode.Text; var condNode = eventNode.Nodes["condition"]; var collision = condNode?.FindNodeByPath("collision").GetValueEx(null); var slotName = condNode?.FindNodeByPath("slotName").GetValueEx(null); var animation = eventNode?.FindNodeByPath("animation").GetValueEx(null); var target = condNode?.FindNodeByPath("target").GetValueEx(null); var actionKeys = (eventNode.FindNodeByPath("action\\playEffect")?.Nodes ?? new Wz_Node.WzNodeCollection(null)).Select(node => node.GetValueEx(null)).ToList(); item.Events.Add(new ItemEvent(index, collision, animation, slotName, target, actionKeys)); } } string path = $@"Map\Obj\{item.OS}.img\{item.L0}\{item.L1}\{item.L2}\0"; var obj_node = PluginManager.FindWz(path); if (obj_node != null) { item.MoveType = obj_node.Nodes["moveType"].GetValueEx(0); item.MoveW = obj_node.Nodes["moveW"].GetValueEx(0); item.MoveH = obj_node.Nodes["moveH"].GetValueEx(0); item.MoveP = obj_node.Nodes["moveP"].GetValueEx(5000); item.MoveDelay = obj_node.Nodes["moveDelay"].GetValueEx(0); } return item; } public class ItemView { /// /// 时间关联,单位为毫秒。 /// public int Time { get; set; } /// /// 动画资源。 /// public object Animator { get; set; } /// /// Flip state of item. /// public bool Flip { get; set; } } } } ================================================ FILE: WzComparerR2.MapRender/Patches2/ParticleItem.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using WzComparerR2.WzLib; namespace WzComparerR2.MapRender.Patches2 { class ParticleItem : SceneItem { public string ParticleName { get; set; } public int Rx { get; set; } public int Ry { get; set; } public int Z { get; set; } public SubParticleItem[] SubItems { get; set; } public List Quest { get; private set; } = new List(); public ItemView View { get; set; } public static ParticleItem LoadFromNode(Wz_Node node) { var item = new ParticleItem() { ParticleName = node.Text, Rx = node.Nodes["rx"].GetValueEx(-100), Ry = node.Nodes["ry"].GetValueEx(-100), Z = node.Nodes["z"].GetValueEx(0) }; if (node.Nodes["quest"] != null) { foreach (Wz_Node questNode in node.Nodes["quest"].Nodes) { if (int.TryParse(questNode.Text, out int questID)) { item.Quest.Add(new QuestInfo(questID, Convert.ToInt32(questNode.Value))); } } } var subItems = new List(); for (int i = 0; ; i++) { var subNode = node.Nodes[i.ToString()]; if (subNode == null) { break; } var subitem = new SubParticleItem() { X = subNode.Nodes["x"].GetValueEx(0), Y = subNode.Nodes["y"].GetValueEx(0), }; if (subNode.Nodes["quest"] != null) { foreach (Wz_Node questNode in subNode.Nodes["quest"].Nodes) { if (int.TryParse(questNode.Text, out int questID)) { subitem.Quest.Add(new QuestInfo(questID, Convert.ToInt32(questNode.Value))); } } } subItems.Add(subitem); } if (subItems.Count <= 0) { subItems.Add(new SubParticleItem() { X = node.Nodes["x"].GetValueEx(0), Y = node.Nodes["y"].GetValueEx(0), }); } item.SubItems = subItems.ToArray(); return item; } public class SubParticleItem { public int X { get; set; } public int Y { get; set; } public List Quest { get; private set; } = new List(); } public class ItemView { public ParticleSystem ParticleSystem { get; set; } } } } ================================================ FILE: WzComparerR2.MapRender/Patches2/PortalItem.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using WzComparerR2.WzLib; using WzComparerR2.Animation; namespace WzComparerR2.MapRender.Patches2 { public class PortalItem : SceneItem { public int Type { get; set; } public string PName { get; set; } public int X { get; set; } public int Y { get; set; } public int? ToMap { get; set; } public string ToName { get; set; } //Graph.img에 따른 이동경로 출력 public List GraphTargetMap { get; set; } public string Script { get; set; } public int Image { get; set; } public bool EnchantPortal { get; set; } public bool ShownAtMinimap { get; set; } public ItemView View { get; set; } public ItemTooltip Tooltip { get; set; } public static PortalItem LoadFromNode(Wz_Node node) { var item = new PortalItem() { PName = node.Nodes["pn"].GetValueEx(null), Type = node.Nodes["pt"].GetValueEx(0), X = node.Nodes["x"].GetValueEx(0), Y = node.Nodes["y"].GetValueEx(0), ToMap = node.Nodes["tm"].GetValueEx(), ToName = node.Nodes["tn"].GetValueEx(null), Script = node.Nodes["script"].GetValueEx(null), Image = node.Nodes["image"].GetValueEx(0), EnchantPortal = node.Nodes["enchantPortal"].GetValueEx(0) != 0, ShownAtMinimap = node.Nodes["shownAtMinimap"].GetValueEx(0) != 0 }; return item; } public static readonly IReadOnlyList PortalTypes = new[] { "sp", "pi", "pv", "pc", "pg", "tp", "ps", "pgi", "psi", "pcs", "ph", "psh", "pcj", "pci", "pci2", "pcig", "pshg", "pcc", "pcir" }; public class ItemView { public bool IsEditorMode { get; set; } public bool IsFocusing { get; set; } public object Animator { get; set; } public object EditorAnimator { get; set; } public Controller Controller { get; set; } } public class ItemTooltip { public string Title { get; set; } } public class Controller : IDisposable { public Controller(ItemView view) { this.View = view; AttachEvent(); } public ItemView View { get; private set; } private StateMachineAnimator animator; public void Update(TimeSpan elapsed) { var ani = this.animator.GetCurrent(); if (ani == null) //隐藏状态 { if (OnStateUpdate(null, out ani) && ani != null) { this.animator.SetAnimation(ani); } } else { animator.Update(elapsed); } } private void AttachEvent() { this.animator = this.View.Animator as StateMachineAnimator; if (animator != null) { animator.AnimationEnd += Animator_AnimationEnd; } } private void Animator_AnimationEnd(object sender, StateMachineAnimator.AnimationEndEventArgs e) { string nextState; if (OnStateUpdate(e.CurrentState, out nextState)) { e.NextState = nextState; } } private bool OnStateUpdate(string curState, out string nextState) { switch (curState) { case null: //初始状态 if (this.View.IsFocusing) { nextState = "portalStart"; } else { nextState = null; } return true; case "portalStart": //开始动画 if (this.View.IsFocusing) { nextState = "portalContinue"; } else { nextState = "portalExit"; } return true; case "portalContinue": //循环动画 if (this.View.IsFocusing) { nextState = "portalContinue"; } else { nextState = "portalExit"; } return true; case "portalExit": //结束动画 if (this.View.IsFocusing) { nextState = "portalStart"; } else { nextState = null; } return true; } nextState = null; return false; } public void Dispose() { if (animator != null) { animator.AnimationEnd -= Animator_AnimationEnd; } } } } } ================================================ FILE: WzComparerR2.MapRender/Patches2/QuestInfo.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace WzComparerR2.MapRender.Patches2 { public struct QuestInfo { public QuestInfo(int id, int state) { this.ID = id; this.State = state; } public int ID { get; set; } public int State { get; set; } } public struct QuestExInfo { public QuestExInfo(int id, string key, int state) { this.ID = id; this.Key = key; this.State = state; } public int ID { get; set; } public string Key { get; set; } public int State { get; set; } } } ================================================ FILE: WzComparerR2.MapRender/Patches2/ReactorItem.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using WzComparerR2.WzLib; using WzComparerR2.Animation; using System.Text.RegularExpressions; namespace WzComparerR2.MapRender.Patches2 { public class ReactorItem : SceneItem { public int ID { get; set; } public int X { get; set; } public int Y { get; set; } public bool Flip { get; set; } public string ReactorName { get; set; } public int ReactorTime { get; set; } public ItemView View { get; set; } public static ReactorItem LoadFromNode(Wz_Node node) { var item = new ReactorItem() { ID = node.Nodes["id"].GetValueEx(0), X = node.Nodes["x"].GetValueEx(0), Y = node.Nodes["y"].GetValueEx(0), Flip = node.Nodes["f"].GetValueEx(false), ReactorName = node.Nodes["name"].GetValueEx(null), ReactorTime = node.Nodes["reactorTime"].GetValueEx(0), }; return item; } public class ItemView { public int Stage { get; set; } public int? NextStage { get; set; } public object Animator { get; set; } public Controller Controller { get; set; } } public class Controller : IDisposable { public Controller(ItemView view) { this.View = view; AttachEvent(); } public ItemView View { get; private set; } private StateMachineAnimator animator; public void Update(TimeSpan elapsed) { var ani = this.animator.GetCurrent(); if (ani == null) //隐藏状态 { if (OnStateUpdate(null, out ani) && ani != null) { this.animator.SetAnimation(ani); } } else { animator.Update(elapsed); } } private void AttachEvent() { this.animator = this.View.Animator as StateMachineAnimator; if (animator != null) { animator.AnimationEnd += Animator_AnimationEnd; } } private void Animator_AnimationEnd(object sender, StateMachineAnimator.AnimationEndEventArgs e) { string nextState; if (OnStateUpdate(e.CurrentState, out nextState)) { e.NextState = nextState; } } private bool OnStateUpdate(string curState, out string nextState) { switch (curState) { case null: //初始状态 if (this.View.Stage > -1) { nextState = this.View.Stage.ToString(); } else { nextState = null; } return true; default: Match m; if ((m = Regex.Match(curState, @"^(\d+)$")).Success) { int curStage = int.Parse(m.Result("$1")); if (this.View.NextStage != null) { string hitAniName = $@"{curStage}/hit"; if (this.animator.Data.States.Contains(hitAniName)) //跳转到hit动作 { nextState = hitAniName; return true; } else { goto _lbl1; } } } else if ((m = Regex.Match(curState, @"^(\d+)/hit$")).Success) { int curStage = int.Parse(m.Result("$1")); if (this.View.NextStage != null) { goto _lbl1; } } break; } nextState = null; return false; _lbl1: { string aniName = this.View.NextStage.Value.ToString(); if (this.animator.Data.States.Contains(aniName)) //动作存在 直接跳转 { nextState = aniName; AfterStageChanged(this.View.NextStage.Value); } else //动作不存在 忽略 { nextState = curState; this.View.NextStage = null; } return true; } } private void AfterStageChanged(int newStage) { this.View.Stage = newStage; this.View.NextStage = null; } public void Dispose() { if (animator != null) { animator.AnimationEnd -= Animator_AnimationEnd; } } } } } ================================================ FILE: WzComparerR2.MapRender/Patches2/SceneItem.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace WzComparerR2.MapRender.Patches2 { public class SceneItem { public string Name { get; set; } public int Index { get; set; } public string[] Tags { get; set; } public override string ToString() { return $"{Name} {GetType().Name}"; } } } ================================================ FILE: WzComparerR2.MapRender/Patches2/SkyWhaleItem.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Microsoft.Xna.Framework; using WzComparerR2.Rendering; using WzComparerR2.WzLib; namespace WzComparerR2.MapRender.Patches2 { class SkyWhaleItem : SceneItem { public Point Start { get; set; } public Point End { get; set; } public int Width { get; set; } public double Speed { get; set; } public double FixSpeedShoe { get; set; } public double VRate { get; set; } public int ApplyTerm { get; set; } public static SkyWhaleItem LoadFromNode(Wz_Node node) { var item = new SkyWhaleItem() { Start = node.Nodes["start"].GetValueEx(null)?.ToPoint()?? Point.Zero, End = node.Nodes["end"].GetValueEx(null)?.ToPoint() ?? Point.Zero, Width = node.Nodes["width"].GetValueEx(0), Speed = node.Nodes["speed"].GetValueEx(0), FixSpeedShoe = node.Nodes["fixSpeedShoe"].GetValueEx(0), VRate = node.Nodes["vrate"].GetValueEx(0), ApplyTerm = node.Nodes["applyTerm"].GetValueEx(0), }; return item; } } } ================================================ FILE: WzComparerR2.MapRender/Patches2/TileItem.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using WzComparerR2.WzLib; namespace WzComparerR2.MapRender.Patches2 { public class TileItem : SceneItem { public string TS { get; set; } public string U { get; set; } public string No { get; set; } public int X { get; set; } public int Y { get; set; } public ItemView View { get; set; } public static TileItem LoadFromNode(Wz_Node node) { var item = new TileItem() { U = node.Nodes["u"].GetValueEx(null), No = node.Nodes["no"].GetValueEx(null), X = node.Nodes["x"].GetValueEx(0), Y = node.Nodes["y"].GetValueEx(0), }; return item; } public class ItemView { /// /// 时间关联,单位为毫秒。 /// public int Time { get; set; } /// /// 动画资源。 /// public object Animator { get; set; } } } } ================================================ FILE: WzComparerR2.MapRender/Patches2/TooltipItem.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Microsoft.Xna.Framework; namespace WzComparerR2.MapRender.Patches2 { public class TooltipItem : SceneItem { public Rectangle Rect { get; set; } public Rectangle CharRect { get; set; } public string Title { get; set; } public string Desc { get; set; } public string ItemEU { get; set; } } } ================================================ FILE: WzComparerR2.MapRender/Properties/AssemblyInfo.cs ================================================ using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // 有关程序集的常规信息通过以下 // 特性集控制。更改这些特性值可修改 // 与程序集关联的信息。 [assembly: AssemblyTitle("WzComparerR2.MapRender")] [assembly: AssemblyDescription("Map Simulator Plugin for WzComparerR2.")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Kagamia Studio")] [assembly: AssemblyProduct("WzComparerR2.MapRender")] [assembly: AssemblyCopyright("Copyright © Kagamia Studio 2015-2025")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] // 将 ComVisible 设置为 false 使此程序集中的类型 // 对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型, // 则将该类型上的 ComVisible 特性设置为 true。 [assembly: ComVisible(false)] // 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID [assembly: Guid("c12049a5-0b6b-4e3d-8cc0-aefa5701dac5")] // 程序集的版本信息由下面四个值组成: // // 主版本 // 次版本 // 生成号 // 修订号 // // 可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值, // 方法是按如下所示使用“*”: // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("2.2.0.0")] [assembly: AssemblyFileVersion("2.2.0.10725")] ================================================ FILE: WzComparerR2.MapRender/Properties/Resources.Designer.cs ================================================ //------------------------------------------------------------------------------ // // 這段程式碼是由工具產生的。 // 執行階段版本:4.0.30319.42000 // // 對這個檔案所做的變更可能會造成錯誤的行為,而且如果重新產生程式碼, // 變更將會遺失。 // //------------------------------------------------------------------------------ namespace WzComparerR2.MapRender.Properties { using System; /// /// 用於查詢當地語系化字串等的強類型資源類別。 /// // 這個類別是自動產生的,是利用 StronglyTypedResourceBuilder // 類別透過 ResGen 或 Visual Studio 這類工具。 // 若要加入或移除成員,請編輯您的 .ResX 檔,然後重新執行 ResGen // (利用 /str 選項),或重建您的 VS 專案。 [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class Resources { private static global::System.Resources.ResourceManager resourceMan; private static global::System.Globalization.CultureInfo resourceCulture; [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal Resources() { } /// /// 傳回這個類別使用的快取的 ResourceManager 執行個體。 /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("WzComparerR2.MapRender.Properties.Resources", typeof(Resources).Assembly); resourceMan = temp; } return resourceMan; } } /// /// 覆寫目前執行緒的 CurrentUICulture 屬性,對象是所有 /// 使用這個強類型資源類別的資源查閱。 /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Globalization.CultureInfo Culture { get { return resourceCulture; } set { resourceCulture = value; } } /// /// 查詢類型 System.Drawing.Bitmap 的當地語系化資源。 /// internal static System.Drawing.Bitmap Basic_img_BtCancel4_disabled_0 { get { object obj = ResourceManager.GetObject("Basic_img_BtCancel4_disabled_0", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// 查詢類型 System.Drawing.Bitmap 的當地語系化資源。 /// internal static System.Drawing.Bitmap Basic_img_BtCancel4_mouseOver_0 { get { object obj = ResourceManager.GetObject("Basic_img_BtCancel4_mouseOver_0", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// 查詢類型 System.Drawing.Bitmap 的當地語系化資源。 /// internal static System.Drawing.Bitmap Basic_img_BtCancel4_normal_0 { get { object obj = ResourceManager.GetObject("Basic_img_BtCancel4_normal_0", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// 查詢類型 System.Drawing.Bitmap 的當地語系化資源。 /// internal static System.Drawing.Bitmap Basic_img_BtCancel4_pressed_0 { get { object obj = ResourceManager.GetObject("Basic_img_BtCancel4_pressed_0", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// 查詢類型 System.Drawing.Bitmap 的當地語系化資源。 /// internal static System.Drawing.Bitmap Basic_img_BtClose3_disabled_0 { get { object obj = ResourceManager.GetObject("Basic_img_BtClose3_disabled_0", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// 查詢類型 System.Drawing.Bitmap 的當地語系化資源。 /// internal static System.Drawing.Bitmap Basic_img_BtClose3_mouseOver_0 { get { object obj = ResourceManager.GetObject("Basic_img_BtClose3_mouseOver_0", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// 查詢類型 System.Drawing.Bitmap 的當地語系化資源。 /// internal static System.Drawing.Bitmap Basic_img_BtClose3_normal_0 { get { object obj = ResourceManager.GetObject("Basic_img_BtClose3_normal_0", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// 查詢類型 System.Drawing.Bitmap 的當地語系化資源。 /// internal static System.Drawing.Bitmap Basic_img_BtClose3_pressed_0 { get { object obj = ResourceManager.GetObject("Basic_img_BtClose3_pressed_0", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// 查詢類型 System.Drawing.Bitmap 的當地語系化資源。 /// internal static System.Drawing.Bitmap Basic_img_BtNo3_disabled_0 { get { object obj = ResourceManager.GetObject("Basic_img_BtNo3_disabled_0", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// 查詢類型 System.Drawing.Bitmap 的當地語系化資源。 /// internal static System.Drawing.Bitmap Basic_img_BtNo3_mouseOver_0 { get { object obj = ResourceManager.GetObject("Basic_img_BtNo3_mouseOver_0", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// 查詢類型 System.Drawing.Bitmap 的當地語系化資源。 /// internal static System.Drawing.Bitmap Basic_img_BtNo3_normal_0 { get { object obj = ResourceManager.GetObject("Basic_img_BtNo3_normal_0", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// 查詢類型 System.Drawing.Bitmap 的當地語系化資源。 /// internal static System.Drawing.Bitmap Basic_img_BtNo3_pressed_0 { get { object obj = ResourceManager.GetObject("Basic_img_BtNo3_pressed_0", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// 查詢類型 System.Drawing.Bitmap 的當地語系化資源。 /// internal static System.Drawing.Bitmap Basic_img_BtOK4_disabled_0 { get { object obj = ResourceManager.GetObject("Basic_img_BtOK4_disabled_0", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// 查詢類型 System.Drawing.Bitmap 的當地語系化資源。 /// internal static System.Drawing.Bitmap Basic_img_BtOK4_mouseOver_0 { get { object obj = ResourceManager.GetObject("Basic_img_BtOK4_mouseOver_0", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// 查詢類型 System.Drawing.Bitmap 的當地語系化資源。 /// internal static System.Drawing.Bitmap Basic_img_BtOK4_normal_0 { get { object obj = ResourceManager.GetObject("Basic_img_BtOK4_normal_0", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// 查詢類型 System.Drawing.Bitmap 的當地語系化資源。 /// internal static System.Drawing.Bitmap Basic_img_BtOK4_pressed_0 { get { object obj = ResourceManager.GetObject("Basic_img_BtOK4_pressed_0", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// 查詢類型 System.Drawing.Bitmap 的當地語系化資源。 /// internal static System.Drawing.Bitmap Basic_img_ComboBox_normal_0 { get { object obj = ResourceManager.GetObject("Basic_img_ComboBox_normal_0", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// 查詢類型 System.Drawing.Bitmap 的當地語系化資源。 /// internal static System.Drawing.Bitmap Basic_img_ComboBox_normal_1 { get { object obj = ResourceManager.GetObject("Basic_img_ComboBox_normal_1", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// 查詢類型 System.Drawing.Bitmap 的當地語系化資源。 /// internal static System.Drawing.Bitmap Basic_img_ComboBox_normal_2 { get { object obj = ResourceManager.GetObject("Basic_img_ComboBox_normal_2", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// 查詢類型 System.Drawing.Bitmap 的當地語系化資源。 /// internal static System.Drawing.Bitmap Basic_img_Notice6_box { get { object obj = ResourceManager.GetObject("Basic_img_Notice6_box", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// 查詢類型 System.Drawing.Bitmap 的當地語系化資源。 /// internal static System.Drawing.Bitmap Basic_img_Notice6_c { get { object obj = ResourceManager.GetObject("Basic_img_Notice6_c", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// 查詢類型 System.Drawing.Bitmap 的當地語系化資源。 /// internal static System.Drawing.Bitmap Basic_img_Notice6_c_box { get { object obj = ResourceManager.GetObject("Basic_img_Notice6_c_box", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// 查詢類型 System.Drawing.Bitmap 的當地語系化資源。 /// internal static System.Drawing.Bitmap Basic_img_Notice6_s { get { object obj = ResourceManager.GetObject("Basic_img_Notice6_s", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// 查詢類型 System.Drawing.Bitmap 的當地語系化資源。 /// internal static System.Drawing.Bitmap Basic_img_Notice6_s_box { get { object obj = ResourceManager.GetObject("Basic_img_Notice6_s_box", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// 查詢類型 System.Drawing.Bitmap 的當地語系化資源。 /// internal static System.Drawing.Bitmap Basic_img_Notice6_t { get { object obj = ResourceManager.GetObject("Basic_img_Notice6_t", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// 查詢類型 System.Drawing.Bitmap 的當地語系化資源。 /// internal static System.Drawing.Bitmap StatusBar3_img_chat_ingame_input_layer_backgrnd { get { object obj = ResourceManager.GetObject("StatusBar3_img_chat_ingame_input_layer_backgrnd", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// 查詢類型 System.Drawing.Bitmap 的當地語系化資源。 /// internal static System.Drawing.Bitmap StatusBar3_img_chat_ingame_input_layer_chatEnter { get { object obj = ResourceManager.GetObject("StatusBar3_img_chat_ingame_input_layer_chatEnter", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// 查詢類型 System.Drawing.Bitmap 的當地語系化資源。 /// internal static System.Drawing.Bitmap StatusBar3_img_chat_ingame_view_max_bottom { get { object obj = ResourceManager.GetObject("StatusBar3_img_chat_ingame_view_max_bottom", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// 查詢類型 System.Drawing.Bitmap 的當地語系化資源。 /// internal static System.Drawing.Bitmap StatusBar3_img_chat_ingame_view_max_center { get { object obj = ResourceManager.GetObject("StatusBar3_img_chat_ingame_view_max_center", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// 查詢類型 System.Drawing.Bitmap 的當地語系化資源。 /// internal static System.Drawing.Bitmap StatusBar3_img_chat_ingame_view_max_top { get { object obj = ResourceManager.GetObject("StatusBar3_img_chat_ingame_view_max_top", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// 查詢類型 System.Drawing.Bitmap 的當地語系化資源。 /// internal static System.Drawing.Bitmap UIWindow_img_ToolTip_WorldMap_ArcaneForce { get { object obj = ResourceManager.GetObject("UIWindow_img_ToolTip_WorldMap_ArcaneForce", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// 查詢類型 System.Drawing.Bitmap 的當地語系化資源。 /// internal static System.Drawing.Bitmap UIWindow_img_ToolTip_WorldMap_AuthenticForce { get { object obj = ResourceManager.GetObject("UIWindow_img_ToolTip_WorldMap_AuthenticForce", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// 查詢類型 System.Drawing.Bitmap 的當地語系化資源。 /// internal static System.Drawing.Bitmap UIWindow_img_ToolTip_WorldMap_enchantMob { get { object obj = ResourceManager.GetObject("UIWindow_img_ToolTip_WorldMap_enchantMob", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// 查詢類型 System.Drawing.Bitmap 的當地語系化資源。 /// internal static System.Drawing.Bitmap UIWindow_img_ToolTip_WorldMap_Line { get { object obj = ResourceManager.GetObject("UIWindow_img_ToolTip_WorldMap_Line", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// 查詢類型 System.Drawing.Bitmap 的當地語系化資源。 /// internal static System.Drawing.Bitmap UIWindow_img_ToolTip_WorldMap_Mob { get { object obj = ResourceManager.GetObject("UIWindow_img_ToolTip_WorldMap_Mob", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// 查詢類型 System.Drawing.Bitmap 的當地語系化資源。 /// internal static System.Drawing.Bitmap UIWindow_img_ToolTip_WorldMap_Npc { get { object obj = ResourceManager.GetObject("UIWindow_img_ToolTip_WorldMap_Npc", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// 查詢類型 System.Drawing.Bitmap 的當地語系化資源。 /// internal static System.Drawing.Bitmap UIWindow_img_ToolTip_WorldMap_StarForce { get { object obj = ResourceManager.GetObject("UIWindow_img_ToolTip_WorldMap_StarForce", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// 查詢類型 System.Drawing.Bitmap 的當地語系化資源。 /// internal static System.Drawing.Bitmap UIWindow2_img_Teleport_customBG { get { object obj = ResourceManager.GetObject("UIWindow2_img_Teleport_customBG", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// 查詢類型 System.Drawing.Bitmap 的當地語系化資源。 /// internal static System.Drawing.Bitmap UIWindow2_img_WorldMap_Border_0 { get { object obj = ResourceManager.GetObject("UIWindow2_img_WorldMap_Border_0", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// 查詢類型 System.Drawing.Bitmap 的當地語系化資源。 /// internal static System.Drawing.Bitmap UIWindow3_img_mirrorFrame_1024 { get { object obj = ResourceManager.GetObject("UIWindow3_img_mirrorFrame_1024", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// 查詢類型 System.Drawing.Bitmap 的當地語系化資源。 /// internal static System.Drawing.Bitmap UIWindow3_img_mirrorFrame_1366 { get { object obj = ResourceManager.GetObject("UIWindow3_img_mirrorFrame_1366", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// 查詢類型 System.Drawing.Bitmap 的當地語系化資源。 /// internal static System.Drawing.Bitmap UIWindow3_img_mirrorFrame_800 { get { object obj = ResourceManager.GetObject("UIWindow3_img_mirrorFrame_800", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } } } ================================================ FILE: WzComparerR2.MapRender/Properties/Resources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ..\resources\basic.img.combobox.normal.0.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\resources\basic.img.combobox.normal.1.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\resources\basic.img.combobox.normal.2.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\UIWindow2.img.WorldMap.Border.0.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\UIWindow3.img.mirrorFrame.800.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\UIWindow3.img.mirrorFrame.1024.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\UIWindow3.img.mirrorFrame.1366.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\UIWindow.img.ToolTip.WorldMap.ArcaneForce.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\UIWindow.img.ToolTip.WorldMap.AuthenticForce.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\UIWindow.img.ToolTip.WorldMap.enchantMob.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\UIWindow.img.ToolTip.WorldMap.Line.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\UIWindow.img.ToolTip.WorldMap.Mob.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\UIWindow.img.ToolTip.WorldMap.Npc.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\UIWindow.img.ToolTip.WorldMap.StarForce.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\Basic.img.Notice6.c.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\Basic.img.Notice6.s.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\Basic.img.Notice6.t.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\Basic.img.BtCancel4.disabled.0.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\Basic.img.BtCancel4.mouseOver.0.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\Basic.img.BtCancel4.normal.0.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\Basic.img.BtCancel4.pressed.0.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\Basic.img.BtClose3.disabled.0.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\Basic.img.BtClose3.mouseOver.0.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\Basic.img.BtClose3.normal.0.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\Basic.img.BtClose3.pressed.0.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\Basic.img.BtNo3.disabled.0.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\Basic.img.BtNo3.mouseOver.0.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\Basic.img.BtNo3.normal.0.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\Basic.img.BtNo3.pressed.0.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\Basic.img.BtOK4.disabled.0.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\Basic.img.BtOK4.mouseOver.0.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\Basic.img.BtOK4.normal.0.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\Basic.img.BtOK4.pressed.0.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\StatusBar3.img.chat.ingame.input.layer_backgrnd.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\StatusBar3.img.chat.ingame.input.layer_chatEnter.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\StatusBar3.img.chat.ingame.view.max.bottom.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\StatusBar3.img.chat.ingame.view.max.center.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\StatusBar3.img.chat.ingame.view.max.top.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\Basic.img.Notice6.box.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\Basic.img.Notice6.c_box.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\Basic.img.Notice6.s_box.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\UIWindow2_img_Teleport_customBG.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ================================================ FILE: WzComparerR2.MapRender/RenderAnimate.cs ================================================ #if MapRenderV1 using System; using System.Collections.Generic; using System.Text; namespace WzComparerR2.MapRender { public class RenderAnimate : IEnumerable { public RenderAnimate(RenderFrame[] frames) { this.frames = frames; UpdateTimeLine(); } RenderFrame[] frames; int repeat; int[] timeLine; public RenderFrame this[int index] { get { return frames[index]; } } public int Length { get { return frames == null ? -1 : frames.Length; } } public int Repeat { get { return repeat; } set { repeat = value; } } public int[] TimeLine { get { return timeLine; } } private void UpdateTimeLine() { if (this.frames == null || this.frames.Length == 0) { timeLine = new int[] { 0 }; return; } if (this.timeLine == null || (this.timeLine.Length != this.frames.Length + 1)) { this.timeLine = new int[this.frames.Length + 1]; } for (int i = 0; i < this.frames.Length; i++) { this.timeLine[i + 1] = this.timeLine[i] + this.frames[i].Delay; } } public IEnumerator GetEnumerator() { return new System.Collections.ObjectModel.ReadOnlyCollection(this.frames).GetEnumerator(); } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return this.GetEnumerator(); } } } #endif ================================================ FILE: WzComparerR2.MapRender/RenderArgs.cs ================================================ #if MapRenderV1 using System; using System.Collections.Generic; using System.Text; using Microsoft.Xna.Framework; namespace WzComparerR2.MapRender { public class RenderArgs { public RenderArgs() { currentIndex = -1; currentPercent = 0f; culled = false; visible = true; } private int currentIndex; private float currentPercent; private bool culled; private bool visible; private Rectangle displayRectangle; public int CurrentIndex { get { return currentIndex; } set { currentIndex = value; } } public float CurrentPercent { get { return currentPercent; } set { currentPercent = value; } } public bool Culled { get { return culled; } set { culled = value; } } public bool Visible { get { return visible; } set { visible = value; } } public Rectangle DisplayRectangle { get { return displayRectangle; } set { displayRectangle = value; } } } } #endif ================================================ FILE: WzComparerR2.MapRender/RenderEnv.cs ================================================ using System; using System.Collections.Generic; using System.Text; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using WzComparerR2.Rendering; namespace WzComparerR2.MapRender { public class RenderEnv : IDisposable { public RenderEnv(Game game, GraphicsDeviceManager graphics) { this.GraphicsDevice = graphics.GraphicsDevice; this.Camera = new Camera(graphics); this.Sprite = new SpriteBatchEx(this.GraphicsDevice); this.D2DRenderer = new D2DRenderer(this.GraphicsDevice); this.Input = new InputState(game); this.Fonts = new MapRenderFonts(); this.BlendStateMultiplyRGB = StateEx.MultiplyRGB(); } public Camera Camera { get; private set; } public SpriteBatchEx Sprite { get; private set; } public D2DRenderer D2DRenderer { get; private set; } public InputState Input { get; private set; } public GraphicsDevice GraphicsDevice { get; private set; } public MapRenderFonts Fonts { get; private set; } public BlendState BlendStateMultiplyRGB { get; private set; } public void Dispose() { this.Dispose(true); } protected virtual void Dispose(bool disposing) { if (disposing) { this.Sprite.Dispose(); this.Fonts.Dispose(); this.BlendStateMultiplyRGB.Dispose(); } } } } ================================================ FILE: WzComparerR2.MapRender/RenderFrame.cs ================================================ #if MapRenderV1 using System; using System.Collections.Generic; using System.Text; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; namespace WzComparerR2.MapRender { public class RenderFrame { public RenderFrame() { } Texture2D texture; Vector2 origin; int z; int delay; int a0; int a1; public Texture2D Texture { get { return texture; } set { texture = value; } } public Vector2 Origin { get { return origin; } set { origin = value; } } public int Z { get { return z; } set { z = value; } } public int Delay { get { return delay; } set { delay = value; } } public int A0 { get { return a0; } set { a0 = value; } } public int A1 { get { return a1; } set { a1 = value; } } } } #endif ================================================ FILE: WzComparerR2.MapRender/ResourceLoader.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using WzComparerR2.Animation; using WzComparerR2.Common; using WzComparerR2.PluginBase; using WzComparerR2.Rendering; using WzComparerR2.WzLib; namespace WzComparerR2.MapRender { public class ResourceLoader : IDisposable { public ResourceLoader(IServiceProvider serviceProvider) { this.Services = serviceProvider; this.loadedItems = new Dictionary(); this.loadedAnimationData = new Dictionary(); } public PatchVisibility PatchVisibility { get; set; } public IServiceProvider Services { get; protected set; } protected Dictionary loadedItems; protected Dictionary loadedAnimationData; private bool isCounting; private GraphicsDevice GraphicsDevice { get { return this.Services.GetService().GraphicsDevice; } } public virtual T Load(string assetName) { return Load(null, assetName); } public virtual T Load(Wz_Node node) { return Load(node, null); } private T Load(Wz_Node node = null, string assetName = null) { if (node == null && assetName == null) throw new ArgumentNullException(); assetName = assetName ?? node.FullPathToFile; ResourceHolder holder; if (!loadedItems.TryGetValue(assetName, out holder)) { var res = InnerLoad(node ?? PluginManager.FindWz(assetName), typeof(T)); if (res == null) { return default(T); } holder = new ResourceHolder(); holder.Resource = res; loadedItems[assetName] = holder; } //结算计数器 if (isCounting) { holder.Count++; } //特殊处理 return (T)holder.Resource; } public object LoadAnimationData(Wz_Node node) { object aniData; string assetName = node.FullPathToFile; if (!loadedAnimationData.TryGetValue(assetName, out aniData)) { aniData = InnerLoadAnimationData(node); if (aniData == null) { return null; } loadedAnimationData[assetName] = aniData; } return aniData; } public object LoadAnimationData(string assetName) { var node = PluginManager.FindWz(assetName); return node != null ? LoadAnimationData(node) : null; } public object LoadParticleDesc(Wz_Node node) { int dataType = node.Nodes["data_type"].GetValueEx(0); switch (dataType) { case 0: return LoadParticleDesc0(node); case 1: return LoadParticleDesc1(node); case 3: return LoadParticleDesc3(node); default: throw new Exception($"Unknown particle data type {dataType}."); } } public ParticleDesc LoadParticleDesc0(Wz_Node node) { var desc = new ParticleDesc(); desc.Name = node.Text; foreach (var pNode in node.Nodes) { switch (pNode.Text) { case "totalParticle": desc.TotalParticle = pNode.GetValue(); break; case "angle": desc.Angle = pNode.GetValue(); break; case "angleVar": desc.AngleVar = pNode.GetValue(); break; case "duration": desc.Duration = pNode.GetValue(); break; case "blendFuncSrc": desc.BlendFuncSrc = (ParticleBlendFunc)pNode.GetValue(); break; case "blendFuncDst": desc.BlendFuncDst = (ParticleBlendFunc)pNode.GetValue(); break; case "startColor": desc.StartColor = pNode.GetXnaColor(); break; case "startColorVar": desc.StartColorVar = pNode.GetXnaColor(); break; case "endColor": desc.EndColor = pNode.GetXnaColor(); break; case "endColorVar": desc.EndColorVar = pNode.GetXnaColor(); break; case "MiddlePoint0": desc.MiddlePoint0 = pNode.GetValue(); break; case "MiddlePointAlpha0": desc.MiddlePointAlpha0 = pNode.GetValue(); break; case "MiddlePoint1": desc.MiddlePoint1 = pNode.GetValue(); break; case "MiddlePointAlpha1": desc.MiddlePointAlpha1 = pNode.GetValue(); break; case "startSize": desc.StartSize = pNode.GetValue(); break; case "startSizeVar": desc.StartSizeVar = pNode.GetValue(); break; case "endSize": desc.EndSize = pNode.GetValue(); break; case "endSizeVar": desc.EndSizeVar = pNode.GetValue(); break; case "posX": desc.PosX = pNode.GetValue(); break; case "posY": desc.PosY = pNode.GetValue(); break; case "posVarX": desc.PosVarX = pNode.GetValue(); break; case "posVarY": desc.PosVarY = pNode.GetValue(); break; case "startSpin": desc.StartSpin = pNode.GetValue(); break; case "startSpinVar": desc.StartSpinVar = pNode.GetValue(); break; case "endSpin": desc.EndSpin = pNode.GetValue(); break; case "endSpinVar": desc.EndSpinVar = pNode.GetValue(); break; case "GRAVITY": { var gravityDesc = new ParticleGravityDesc(); foreach (var pNode2 in pNode.Nodes) { switch (pNode2.Text) { case "x": gravityDesc.X = pNode2.GetValue(); break; case "y": gravityDesc.Y = pNode2.GetValue(); break; case "speed": gravityDesc.Speed = pNode2.GetValue(); break; case "speedVar": gravityDesc.SpeedVar = pNode2.GetValue(); break; case "radialAccel": gravityDesc.RadialAccel = pNode2.GetValue(); break; case "radialAccelVar": gravityDesc.RadialAccelVar = pNode2.GetValue(); break; case "tangentialAccel": gravityDesc.TangentialAccel = pNode2.GetValue(); break; case "tangentialAccelVar": gravityDesc.TangentialAccelVar = pNode2.GetValue(); break; case "rotationIsDir": gravityDesc.RotationIsDir = pNode2.GetValue() != 0; break; } } desc.Gravity = gravityDesc; } break; case "RADIUS": { var radiusDesc = new ParticleRadiusDesc(); foreach (var pNode2 in pNode.Nodes) { switch (pNode2.Text) { case "startRadius": radiusDesc.StartRadius = pNode2.GetValue(); break; case "startRadiusVar": radiusDesc.StartRadiusVar = pNode2.GetValue(); break; case "endRadius": radiusDesc.EndRadius = pNode2.GetValue(); break; case "endRadiusVar": radiusDesc.EndRadiusVar = pNode2.GetValue(); break; case "rotatePerSecond": radiusDesc.RotatePerSecond = pNode2.GetValue(); break; case "rotatePerSecondVar": radiusDesc.RotatePerSecondVar = pNode2.GetValue(); break; } } desc.Radius = radiusDesc; } break; case "life": desc.Life = pNode.GetValue(); break; case "lifeVar": desc.LifeVar = pNode.GetValue(); break; case "opacityModifyRGB": desc.OpacityModifyRGB = pNode.GetValue() != 0; break; case "positionType": desc.PositionType = pNode.GetValue(); break; case "texture": desc.Texture = this.LoadFrame(pNode); break; } } return desc; } public ParticleDesc1 LoadParticleDesc1(Wz_Node node) { return new ParticleDesc1(); } public ParticleDesc3 LoadParticleDesc3(Wz_Node node) { return new ParticleDesc3(); } public void BeginCounting() { foreach (var kv in this.loadedItems) { kv.Value.Count = 0; } isCounting = true; } public void EndCounting() { isCounting = false; } public void ClearAnimationCache() { this.loadedAnimationData.Clear(); } public void Recycle() { var preRemoved = this.loadedItems .Where(kv => kv.Value.Count <= 0) .ToList(); foreach (var kv in preRemoved) { this.loadedItems.Remove(kv.Key); UnloadResource(kv.Value.Resource); } } public void Unload() { foreach (var kv in this.loadedItems) { UnloadResource(kv.Value.Resource); } this.loadedItems.Clear(); this.loadedAnimationData.Clear(); } public void Dispose() { this.Dispose(true); GC.SuppressFinalize(this); } private void UnloadResource(object resource) { if (resource is Texture2D) { ((Texture2D)resource).Dispose(); } else if (resource is TextureAtlas) //回头再考虑回收策略 { ((TextureAtlas)resource).Texture.Dispose(); } else if (resource is Music) { ((Music)resource).Dispose(); } else { (resource as IDisposable)?.Dispose(); } } private object InnerLoad(Wz_Node node, Type assetType) { if (assetType == typeof(TextureAtlas)) //回头再说 { var png = node.GetValue(); if (png != null) { return new TextureAtlas(png.ToTexture(this.GraphicsDevice)); } } else if (assetType == typeof(Texture2D)) { var png = node.GetValue(); if (png != null) { return png.ToTexture(this.GraphicsDevice); } } else if (assetType == typeof(Music)) { var sound = node.GetValue(); if (sound != null) { return new Music(sound); } } else if (assetType == typeof(MsShader)) { return this.LoadMsShader(node); } return null; } private object InnerLoadAnimationData(Wz_Node node) { if (node != null && (node = node.ResolveUol()) != null) { SpineDetectionResult detectionResult; if (node.Value is Wz_Png) //单帧动画 { var aniData = new FrameAnimationData(); var frame = LoadFrame(node); aniData.Frames.Add(frame); return aniData; } else if ((detectionResult = SpineLoader.Detect(node)).Success) { return this.LoadSpineAnimationData(detectionResult); } else if (node.Value == null && node.Nodes.Count > 0) //分析目录 { if (node.Nodes["type"]?.Value is string type) { switch (type) { case "sprite": var msSprite = this.LoadMsSpriteData(node); return msSprite; } } else if (node.Nodes["spine"]?.Value is string spine) { var textureLoader = new SpineTextureLoader(this, node); textureLoader.EnableTextureMissingFallback = true; var atlasNode = node.Nodes[spine + ".atlas"]; detectionResult = SpineLoader.Detect(atlasNode); if (detectionResult.Success) { return this.LoadSpineAnimationData(detectionResult); } } else //读取序列帧动画 { var frames = new List(); Wz_Node frameNode; for (int i = 0; (frameNode = node.Nodes[i.ToString()]) != null; i++) { var frame = LoadFrame(frameNode); frames.Add(frame); } var repeat = node.Nodes["repeat"].GetValueEx(); return new RepeatableFrameAnimationData(frames) { Repeat = repeat }; } } } return null; } private Frame LoadFrame(Wz_Node node) { //处理uol while (node?.Value is Wz_Uol uol) { node = uol.HandleUol(node); } if (node == null) { return new Frame(); } //寻找link var linkNode = node.GetLinkedSourceNode(PluginManager.FindWz); //加载资源 var atlas = Load(linkNode); //读取其他信息 var frame = new Frame() { Texture = atlas.Texture, AtlasRect = atlas.SrcRect, Z = node.Nodes["z"].GetValueEx(0), Delay = node.Nodes["delay"].GetValueEx(120), Blend = node.Nodes["blend"].GetValueEx(0) != 0, Origin = (node.Nodes["origin"]?.Value as Wz_Vector)?.ToPoint() ?? Point.Zero }; frame.A0 = node.Nodes["a0"].GetValueEx(255); frame.A1 = node.Nodes["a1"].GetValueEx(frame.A0); return frame; } private ISpineAnimationData LoadSpineAnimationData(SpineDetectionResult detectionResult) { if (detectionResult.Success) { var textureLoader = new SpineTextureLoader(this, detectionResult.SourceNode.ParentNode); textureLoader.EnableTextureMissingFallback = true; if (detectionResult.Version == SpineVersion.V2) { return SpineAnimationDataV2.Create(detectionResult, textureLoader); } else if (detectionResult.Version == SpineVersion.V4) { return SpineAnimationDataV4.Create(detectionResult, textureLoader); } } return null; } private Texture2D CreateEmptyTexture(string path, int width, int height) { if (!loadedItems.TryGetValue(path, out ResourceHolder holder)) { holder = new ResourceHolder(); holder.Resource = new Texture2D(this.GraphicsDevice, width, height, false, SurfaceFormat.Alpha8); loadedItems[path] = holder; } //结算计数器 if (isCounting) { holder.Count++; } //特殊处理 return (Texture2D)holder.Resource; } private MsCustomSpriteData LoadMsSpriteData(Wz_Node node) { var textures = new List(); // load textures for (int i = 0; ; i++) { Wz_Node textureNode = node.Nodes[i.ToString()]; if (textureNode == null) { break; } var textureArray = new List(); for (int j = 0; ; j++) { Wz_Node textureIndexNode = textureNode.Nodes[j.ToString()]; if (textureIndexNode == null) { break; } var linkNode = textureIndexNode.GetLinkedSourceNode(PluginManager.FindWz); textureArray.Add(this.Load(linkNode)); } var addressU = textureNode.Nodes["address_u"].GetValueEx(0); var addressV = textureNode.Nodes["address_v"].GetValueEx(0); textures.Add(new MsCustomTexture { Texture = textureArray.FirstOrDefault(), Textures = textureArray.ToArray(), AddressU = addressU, AddressV = addressV, }); } // load shader string shaderName = node.Nodes["shader"].GetValueEx(null); MsShader shader = null; if (shaderName != null) { Wz_Node shaderNode = node.GetNodeWzImage()?.Node.FindNodeByPath(false, shaderName.Split('/')); if (shaderNode != null) { shader = this.Load(shaderNode); } } var msSprite = new MsCustomSpriteData() { Textures = textures.ToArray(), Shader = shader, }; return msSprite; } private MsShader LoadMsShader(Wz_Node node) { var shader = new MsShader(); shader.ID = node.Nodes["id"]?.GetValueEx(null); if (node.Nodes["constant"] is Wz_Node constantRoot) { foreach (var constantNode in constantRoot.Nodes) { if (constantNode.Nodes.Count > 0) // read as vector { if (constantNode.Nodes["x"]?.Value is float x) { if (constantNode.Nodes["y"]?.Value is float y) { if (constantNode.Nodes["z"]?.Value is float z) { shader.Constants.Add(constantNode.Text, new MsShaderConstant(x, y, z)); } else { shader.Constants.Add(constantNode.Text, new MsShaderConstant(x, y)); } } else { shader.Constants.Add(constantNode.Text, new MsShaderConstant(x)); } } } else if (constantNode.Value is float x) // read as scalar { shader.Constants.Add(constantNode.Text, new MsShaderConstant(x)); } } } return shader; } protected virtual void Dispose(bool disposing) { if (disposing) { Unload(); } } ~ResourceLoader() { this.Dispose(false); } protected class ResourceHolder { public object Resource { get; set; } public int Count { get; set; } } private class SpineTextureLoader : Spine.TextureLoader, Spine.V2.TextureLoader { public SpineTextureLoader(ResourceLoader resLoader, Wz_Node topNode) { this.BaseLoader = resLoader; this.TopNode = topNode; } public ResourceLoader BaseLoader { get; set; } public Wz_Node TopNode { get; set; } public bool EnableTextureMissingFallback { get; set; } public void Load(Spine.AtlasPage page, string path) { if (this.TryLoadTexture(path, out var texture)) { page.rendererObject = texture; page.width = texture.Width; page.height = texture.Height; } else if (this.EnableTextureMissingFallback && page.width > 0 && page.height > 0) { page.rendererObject = this.CreateEmptyTexture(path, page.width, page.height); } } public void Load(Spine.V2.AtlasPage page, string path) { if (this.TryLoadTexture(path, out var texture)) { page.rendererObject = texture; page.width = texture.Width; page.height = texture.Height; } else if (this.EnableTextureMissingFallback && page.width > 0 && page.height > 0) { page.rendererObject = this.CreateEmptyTexture(path, page.width, page.height); } } public void Unload(object texture) { //什么都不做 } private bool TryLoadTexture(string path, out Texture2D texture) { texture = null; var frameNode = this.TopNode.FindNodeByPath(path); frameNode = frameNode?.ResolveUol(); if (frameNode?.Value is Wz_Png) { var linkNode = frameNode.GetLinkedSourceNode(PluginManager.FindWz); // workaround for KMST 1172, skeleton atlas and other obj may link to the same png node. // TODO: add options to always load into a standalone texture, disable dynamic atlas on this resource, texture = BaseLoader.Load(linkNode).Texture; return true; } return false; } private Texture2D CreateEmptyTexture(string path, int width, int height) => this.BaseLoader.CreateEmptyTexture(path, width, height); } } } ================================================ FILE: WzComparerR2.MapRender/SceneNode.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Collections.ObjectModel; namespace WzComparerR2.MapRender { public class SceneNode { public SceneNode() { this.Nodes = new SceneNodeCollection(this); } public SceneNode Parent { get; private set; } public SceneNodeCollection Nodes { get; private set; } public IEnumerable Descendants() { foreach (var node in this.Nodes) { yield return node; foreach (var subNode in node.Descendants()) { yield return subNode; } } } public class SceneNodeCollection : Collection { public SceneNodeCollection(SceneNode owner) { this._owner = owner; } private SceneNode _owner; public void AddRange(IEnumerable nodes) { foreach (var node in nodes) { this.Add(node); } } protected override void InsertItem(int index, SceneNode item) { if (item.Parent != null) throw new ArgumentException("Item already has parent."); item.Parent = _owner; base.InsertItem(index, item); } protected override void SetItem(int index, SceneNode item) { if (item.Parent != null) throw new ArgumentException("Item already has parent."); this[index].Parent = null; item.Parent = _owner; base.SetItem(index, item); } protected override void RemoveItem(int index) { this[index].Parent = null; base.RemoveItem(index); } protected override void ClearItems() { foreach (var item in this) item.Parent = null; base.ClearItems(); } } } } ================================================ FILE: WzComparerR2.MapRender/TextMesh.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using WzComparerR2.Rendering; using Microsoft.Xna.Framework; namespace WzComparerR2.MapRender { class TextMesh { public string Text { get; set; } public IWcR2Font Font { get; set; } public Color BackColor { get; set; } public Color ForeColor { get; set; } public Margins Padding { get; set; } public Alignment Align { get; set; } } struct Margins { public Margins(int left, int right, int top, int bottom) { this.Left = left; this.Right = right; this.Top = top; this.Bottom = bottom; } public int Left { get; set; } public int Right { get; set; } public int Top { get; set; } public int Bottom { get; set; } } enum Alignment { Near = 0, Center = 1, Far = 2 } } ================================================ FILE: WzComparerR2.MapRender/TextureAtlas.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; namespace WzComparerR2.MapRender { public struct TextureAtlas { public TextureAtlas(Texture2D texture) { this.Texture = texture; this.SrcRect = null; } public TextureAtlas(Texture2D texture, Rectangle rect) { this.Texture = texture; this.SrcRect = rect; } public Texture2D Texture { get; set; } public Rectangle? SrcRect { get; set; } } } ================================================ FILE: WzComparerR2.MapRender/TextureLoader.cs ================================================ using System; using System.Collections.Generic; using System.Collections.Specialized; using System.Text; using System.IO; using System.IO.Compression; using WzComparerR2.WzLib; using WzComparerR2.PluginBase; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; namespace WzComparerR2.MapRender { public class TextureLoader { public TextureLoader(GraphicsDevice graphicsDevice) { this.GraphicsDevice = graphicsDevice; this.loadedTexture = new Dictionary(StringComparer.CurrentCultureIgnoreCase); } public GraphicsDevice GraphicsDevice { get; private set; } public bool IsCounting { get; private set; } private Dictionary loadedTexture; #if MapRenderV1 public RenderFrame CreateFrame(Wz_Node frameNode) { string key = frameNode.FullPathToFile.Replace('\\', '/'); Wz_Png png = null; string source = frameNode.FindNodeByPath("source").GetValueEx(null); if (!string.IsNullOrEmpty(source)) { Wz_Node sourceNode = PluginManager.FindWz(source); if (sourceNode != null) { png = sourceNode.Value as Wz_Png; key = sourceNode.FullPathToFile.Replace('\\', '/'); } } else { png = frameNode.Value as Wz_Png; } if (png == null) return null; RenderFrame frame = new RenderFrame(); Wz_Vector vec = frameNode.FindNodeByPath("origin").GetValueEx(null); frame.Texture = GetTexture(png, key); frame.Origin = (vec == null ? new Vector2() : new Vector2(vec.X, vec.Y)); frame.Delay = frameNode.FindNodeByPath("delay").GetValueEx(100); frame.Z = frameNode.FindNodeByPath("z").GetValueEx(0); frame.A0 = frameNode.FindNodeByPath("a0").GetValueEx(255); frame.A1 = frameNode.FindNodeByPath("a1").GetValueEx(frame.A0); return frame; } #endif private Texture2D GetTexture(Wz_Png png, string key) { TextureItem texItem; if (!this.loadedTexture.TryGetValue(key, out texItem)) { texItem = new TextureItem() { Texture = PngToTexture(this.GraphicsDevice, png) }; this.loadedTexture[key] = texItem; } else { } if (IsCounting) { texItem.counter++; } return texItem.Texture; } public void BeginCounting() { if (!this.IsCounting) { this.IsCounting = true; foreach (var kv in this.loadedTexture) { kv.Value.counter = 0; } } } public void EndCounting() { if (this.IsCounting) { this.IsCounting = false; } } public void ClearUnusedTexture() { List removedKeys = new List(); foreach (var kv in this.loadedTexture) { if (kv.Value.Texture != null && kv.Value.counter <= 0 && !kv.Value.Texture.IsDisposed) { kv.Value.Texture.Dispose(); removedKeys.Add(kv.Key); } } foreach (string key in removedKeys) { this.loadedTexture.Remove(key); } } public static Texture2D PngToTexture(GraphicsDevice device, Wz_Png png) { return WzComparerR2.Rendering.WzLibExtension.ToTexture(png, device); } private class TextureItem { public TextureItem() { } public Texture2D Texture { get; set; } public int counter { get; set; } } } } ================================================ FILE: WzComparerR2.MapRender/TileMode.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace WzComparerR2.MapRender { [Flags] public enum TileMode { None = 0, Horizontal = 1, Vertical = 2, BothTile = Horizontal | Vertical, ScrollHorizontal = 4, ScrollVertical = 8 } } ================================================ FILE: WzComparerR2.MapRender/UI/ColorWConverter.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using EmptyKeys.UserInterface.Media; namespace WzComparerR2.MapRender.UI { public class ColorWConverter { public static bool TryParse(string s, out ColorW colorW) { if (s != null) { if (s.Length == 6) //RGB { if (uint.TryParse(s, System.Globalization.NumberStyles.HexNumber,null, out uint rgb)) { colorW = new ColorW((byte)(rgb >> 16), (byte)(rgb >> 8), (byte)(rgb), 0xff); return true; } } else if (s.Length == 8) //ARGB { if (uint.TryParse(s, System.Globalization.NumberStyles.HexNumber, null, out uint argb)) { colorW = new ColorW((byte)(argb >> 16), (byte)(argb >> 8), (byte)(argb), (byte)(argb>>24)); return true; } } } colorW = ColorW.TransparentBlack; return false; } public static string ToString(ColorW colorW) { return colorW.PackedValue.ToString("x8"); } } } ================================================ FILE: WzComparerR2.MapRender/UI/HitMap.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace WzComparerR2.MapRender.UI { class HitMap { public HitMap(int width, int height) { this.Width = width; this.Height = height; this.bitArray = new byte[this.Stride * this.Height]; } public HitMap(bool defaultHit) { this.DefaultHit = defaultHit; } public int Width { get; private set; } public int Height { get; private set; } public bool DefaultHit { get; set; } public int Stride { get { return (this.Width + 7) / 8; } } private byte[] bitArray; public bool this[int x, int y] { get { if (x < 0 || x >= this.Width || y < 0 || y >= this.Height || this.bitArray == null) { return this.DefaultHit; } return bitArray[this.Stride * y + x / 8] >> (x % 8) != 0; } set { if (x < 0 || x >= this.Width || y < 0 || y >= this.Height || this.bitArray == null) { return; } int i = this.Stride * y + x / 8; if (value) { bitArray[i] |= (byte)(1 << (x % 8)); } else { bitArray[i] &= (byte)~(1 << (x % 8)); } } } public void SetRow(int y, bool[] isHit) { int index = 0; for (int i = 0; i < this.Stride; i++) { int val = 0; for (int j = 0; j < 8; j++) { if (index < isHit.Length) { if (isHit[index]) { val |= 1 << j; } index++; } } this.bitArray[this.Stride * y + i] = (byte)val; } } } } ================================================ FILE: WzComparerR2.MapRender/UI/ITooltipTarget.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using EmptyKeys.UserInterface; namespace WzComparerR2.MapRender.UI { public interface ITooltipTarget { object GetTooltipTarget(PointF mouseLocation); } } ================================================ FILE: WzComparerR2.MapRender/UI/LCRBrush.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using EmptyKeys.UserInterface; using EmptyKeys.UserInterface.Media; using EmptyKeys.UserInterface.Renderers; using Microsoft.Xna.Framework; namespace WzComparerR2.MapRender.UI { class LCRBrush : Brush { public LCRBrush() { } public INinePatchResource Resource { get { return (INinePatchResource)GetValue(ResourceProperty); } set { SetValue(ResourceProperty, value); } } public override void Draw(TextureBase texture, Renderer renderer, double elapsedGameTime, PointF position, Size renderSize, float opacity) { var blocks = UIGraphics.LayoutLCR(this.Resource, new Point((int)renderSize.Width, (int)renderSize.Height)); foreach (var block in blocks) { if (block.Texture != null && block.Rectangle.Width > 0 && block.Rectangle.Height > 0) { PointF pos = new PointF(block.Rectangle.X + position.X, block.Rectangle.Y + position.Y); Size size = new Size(block.Rectangle.Width, block.Rectangle.Height); ColorW color = new ColorW(1f, 1f, 1f, this.Opacity); renderer.Draw(block.Texture, pos, size, color, false); } } } public override void DrawGeometry(GeometryBuffer buffer, TextureBase texture, Renderer renderer, double elapsedGameTime, PointF position, float opacity) { throw new NotImplementedException(); } public static readonly DependencyProperty ResourceProperty = DependencyProperty.Register("Resource", typeof(INinePatchResource), typeof(LCRBrush), new FrameworkPropertyMetadata(null)); } } ================================================ FILE: WzComparerR2.MapRender/UI/MapRenderButtonStyle.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using EmptyKeys.UserInterface; using EmptyKeys.UserInterface.Controls; using EmptyKeys.UserInterface.Data; using EmptyKeys.UserInterface.Themes; using EmptyKeys.UserInterface.Media.Imaging; using MRes = WzComparerR2.MapRender.Properties.Resources; namespace WzComparerR2.MapRender.UI { static class MapRenderButtonStyle { public static Style CreateMapRenderButtonStyle() { var style = ImageButtonStyle.CreateImageButtonStyle(); //btnOK var trigger = new Trigger() { Property = UIElement.NameProperty, Value = "OK" }; trigger.Setters.AddRange(GetMapRenderButtonSetters("OK")); style.Triggers.Add(trigger); //btnYes trigger = new Trigger() { Property = UIElement.NameProperty, Value = "Yes" }; trigger.Setters.AddRange(GetMapRenderButtonSetters("Yes")); style.Triggers.Add(trigger); //btnNo trigger = new Trigger() { Property = UIElement.NameProperty, Value = "No" }; trigger.Setters.AddRange(GetMapRenderButtonSetters("No")); style.Triggers.Add(trigger); //btnCancel trigger = new Trigger() { Property = UIElement.NameProperty, Value = "Cancel" }; trigger.Setters.AddRange(GetMapRenderButtonSetters("Cancel")); style.Triggers.Add(trigger); //btnClose trigger = new Trigger() { Property = UIElement.NameProperty, Value = "Close" }; trigger.Setters.AddRange(GetMapRenderButtonSetters("Close")); style.Triggers.Add(trigger); return style; } public static Setter[] GetMapRenderButtonSetters(string name) { switch (name) { case "OK": case "Yes": return CreateButtonSetters(42, 16, nameof(MRes.Basic_img_BtOK4_normal_0), nameof(MRes.Basic_img_BtOK4_mouseOver_0), nameof(MRes.Basic_img_BtOK4_pressed_0), nameof(MRes.Basic_img_BtOK4_disabled_0)); case "No": return CreateButtonSetters(42, 16, nameof(MRes.Basic_img_BtNo3_normal_0), nameof(MRes.Basic_img_BtNo3_mouseOver_0), nameof(MRes.Basic_img_BtNo3_pressed_0), nameof(MRes.Basic_img_BtNo3_disabled_0)); case "Cancel": return CreateButtonSetters(42, 16, nameof(MRes.Basic_img_BtCancel4_normal_0), nameof(MRes.Basic_img_BtCancel4_mouseOver_0), nameof(MRes.Basic_img_BtCancel4_pressed_0), nameof(MRes.Basic_img_BtCancel4_disabled_0)); case "Close": return CreateButtonSetters(13, 13, nameof(MRes.Basic_img_BtClose3_normal_0), nameof(MRes.Basic_img_BtClose3_mouseOver_0), nameof(MRes.Basic_img_BtClose3_pressed_0), nameof(MRes.Basic_img_BtClose3_disabled_0)); default: return null; } } private static Setter[] CreateButtonSetters(float width, float height, string normalAsset, string mouseOverAsset, string pressedAsset, string disabledAsset) { return new Setter[] { new Setter(UIElement.WidthProperty, width), new Setter(UIElement.HeightProperty, height), new Setter(ImageButton.ImageNormalProperty, new BitmapImage(){ TextureAsset=normalAsset }), new Setter(ImageButton.ImageHoverProperty, new BitmapImage(){ TextureAsset=mouseOverAsset }), new Setter(ImageButton.ImagePressedProperty, new BitmapImage(){ TextureAsset=pressedAsset }), new Setter(ImageButton.ImageDisabledProperty, new BitmapImage(){ TextureAsset=disabledAsset }), }; } } } ================================================ FILE: WzComparerR2.MapRender/UI/MapRenderResourceKey.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace WzComparerR2.MapRender.UI { public enum MapRenderResourceKey { Invalid = 0, TooltipBrush = 1, FontList, DefaultFontFamily, DefaultFontSize, MessageBoxBackgroundBrush, MapRenderButtonStyle, TextBoxExStyle, } } ================================================ FILE: WzComparerR2.MapRender/UI/MapRenderUIRoot.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Reflection; using EmptyKeys.UserInterface; using EmptyKeys.UserInterface.Controls; using EmptyKeys.UserInterface.Input; using EmptyKeys.UserInterface.Media; using EmptyKeys.UserInterface.Media.Effects; using EmptyKeys.UserInterface.Themes; using EmptyKeys.UserInterface.Data; using Res = CharaSimResource.Resource; using MRes = WzComparerR2.MapRender.Properties.Resources; namespace WzComparerR2.MapRender.UI { class MapRenderUIRoot : UIRoot { public MapRenderUIRoot() : base() { InitGlobalResource(); InitializeComponents(); //获取root容器 var propInfo = typeof(Control).GetProperty("VisualTree", BindingFlags.NonPublic | BindingFlags.Instance); var border = propInfo?.GetValue(this) as Border; this.ContentControl = border?.Child as ContentPresenter; } public event EventHandler InputUpdated; public ContentPresenter ContentControl { get; private set; } public UIMirrorFrame MirrorFrame { get; private set; } public UIMinimap2 Minimap { get; private set; } public UIWorldMap WorldMap { get; private set; } public UITopBar TopBar { get; private set; } public UIChatBox ChatBox { get; private set; } public UITeleport Teleport { get; private set; } private void InitializeComponents() { Style style = RootStyle.CreateRootStyle(); style.TargetType = this.GetType(); this.Style = style; this.Background = null; var mirrorFrame = new UIMirrorFrame(); mirrorFrame.Parent = this; mirrorFrame.IsOnTop = false; mirrorFrame.Visibility = Visibility.Collapsed; mirrorFrame.SetBinding(UIMirrorFrame.WidthProperty, new Binding(UIRoot.WidthProperty) { Source = this }); mirrorFrame.SetBinding(UIMirrorFrame.HeightProperty, new Binding(UIRoot.HeightProperty) { Source = this }); this.MirrorFrame = mirrorFrame; this.Windows.Add(mirrorFrame); var minimap = new UIMinimap2(); minimap.Parent = this; this.Minimap = minimap; this.Windows.Add(minimap); var worldmap = new UIWorldMap(); worldmap.Parent = this; worldmap.Hide(); worldmap.Visible += Worldmap_Visible; this.WorldMap = worldmap; this.Windows.Add(worldmap); var topBar = new UITopBar(); topBar.Parent = this; topBar.IsOnTop = false; topBar.SetBinding(UITopBar.WidthProperty, new Binding(UIRoot.WidthProperty) { Source = this }); topBar.SetBinding(UITopBar.PaddingLeftProperty, new Binding(Window.WidthProperty) { Source = minimap }); topBar.SetBinding(UITopBar.IsShortModeProperty, new Binding(Window.VisibilityProperty) { Source = minimap, Converter = UIHelper.CreateConverter((Visibility o) => o == Visibility.Visible) }); this.TopBar = topBar; this.Windows.Add(topBar); var chatBox = new UIChatBox(); chatBox.Parent = this; chatBox.SetBinding(UIChatBox.TopProperty, new Binding(HeightProperty) { Source = this, Converter = UIHelper.CreateConverter((float height) => height - chatBox.Height) }); this.ChatBox = chatBox; this.Windows.Add(chatBox); var teleport = new UITeleport(); teleport.Parent = this; teleport.Hide(); teleport.Visible += Teleport_Visible; this.Teleport = teleport; this.Windows.Add(teleport); ImageManager.Instance.AddImage(nameof(MRes.Basic_img_BtOK4_normal_0)); ImageManager.Instance.AddImage(nameof(MRes.Basic_img_BtOK4_mouseOver_0)); ImageManager.Instance.AddImage(nameof(MRes.Basic_img_BtOK4_pressed_0)); ImageManager.Instance.AddImage(nameof(MRes.Basic_img_BtOK4_disabled_0)); ImageManager.Instance.AddImage(nameof(MRes.Basic_img_BtNo3_normal_0)); ImageManager.Instance.AddImage(nameof(MRes.Basic_img_BtNo3_mouseOver_0)); ImageManager.Instance.AddImage(nameof(MRes.Basic_img_BtNo3_pressed_0)); ImageManager.Instance.AddImage(nameof(MRes.Basic_img_BtNo3_disabled_0)); ImageManager.Instance.AddImage(nameof(MRes.Basic_img_BtCancel4_normal_0)); ImageManager.Instance.AddImage(nameof(MRes.Basic_img_BtCancel4_mouseOver_0)); ImageManager.Instance.AddImage(nameof(MRes.Basic_img_BtCancel4_pressed_0)); ImageManager.Instance.AddImage(nameof(MRes.Basic_img_BtCancel4_disabled_0)); ImageManager.Instance.AddImage(nameof(MRes.Basic_img_BtClose3_normal_0)); ImageManager.Instance.AddImage(nameof(MRes.Basic_img_BtClose3_mouseOver_0)); ImageManager.Instance.AddImage(nameof(MRes.Basic_img_BtClose3_pressed_0)); ImageManager.Instance.AddImage(nameof(MRes.Basic_img_BtClose3_disabled_0)); this.Resources[CommonResourceKeys.MessageBoxWindowStyleKey] = MessageBoxStyle.CreateMessageBoxStyle(); } private void Worldmap_Visible(object sender, RoutedEventArgs e) { UIWorldMap wnd = sender as UIWorldMap; wnd.Left = (int)Math.Max(0, (this.Width - wnd.Width) / 2); wnd.Top = (int)Math.Max(0, (this.Height - wnd.Height) / 2); if (!wnd.IsDataLoaded) { wnd.LoadWzResource(); } else { wnd.JumpToCurrentMap(); } } private void Teleport_Visible(object sender, RoutedEventArgs e) { UITeleport wnd = sender as UITeleport; wnd.Left = (int)Math.Max(0, (this.Width - wnd.Width) / 2); wnd.Top = (int)Math.Max(0, (this.Height - wnd.Height) / 2); } public void LoadContent(object contentManager) { //UI资源 FontManager.DefaultFontFamily = (FontFamily)this.FindResource(MapRenderResourceKey.DefaultFontFamily); FontManager.DefaultFontSize = (float)this.FindResource(MapRenderResourceKey.DefaultFontSize); FontManager.Instance.AddFont(FontManager.DefaultFontFamily.Source, FontManager.DefaultFontSize, FontStyle.Regular); FontManager.Instance.LoadFonts(contentManager); ImageManager.Instance.LoadImages(contentManager); SoundManager.Instance.LoadSounds(contentManager); EffectManager.Instance.LoadEffects(contentManager); FontManager.DefaultFont = FontManager.Instance.GetFont(FontManager.DefaultFontFamily.Source, FontManager.DefaultFontSize, FontStyle.Regular); //其他资源 this.LoadResource(); this.Minimap.MapAreaControl.LoadWzResource(); } private void InitGlobalResource() { //初始化字体 var fontList = MapRenderFonts.DefaultFonts; var config = MapRender.Config.MapRenderConfig.Default; var resDict = ResourceDictionary.DefaultDictionary; var fontIndex = config.DefaultFontIndex; if (fontIndex < 0 || fontIndex >= fontList.Count) { fontIndex = 0; } resDict[MapRenderResourceKey.FontList] = fontList; resDict[MapRenderResourceKey.DefaultFontFamily] = new FontFamily(fontList[fontIndex]); resDict[MapRenderResourceKey.DefaultFontSize] = 12f; //初始化style resDict[MapRenderResourceKey.MapRenderButtonStyle] = MapRenderButtonStyle.CreateMapRenderButtonStyle(); resDict[MapRenderResourceKey.TextBoxExStyle] = TextBoxEx.CreateStyle(); } private void LoadResource() { var assetManager = Engine.Instance.AssetManager; var tooltipBrush = new NinePatchBrush() { Resource = new EKNineFormResource() { NW = assetManager.LoadTexture(null, nameof(Res.UIToolTip_img_Item_Frame2_nw)), N = assetManager.LoadTexture(null, nameof(Res.UIToolTip_img_Item_Frame2_n)), NE = assetManager.LoadTexture(null, nameof(Res.UIToolTip_img_Item_Frame2_ne)), W = assetManager.LoadTexture(null, nameof(Res.UIToolTip_img_Item_Frame2_w)), C = assetManager.LoadTexture(null, nameof(Res.UIToolTip_img_Item_Frame2_c)), E = assetManager.LoadTexture(null, nameof(Res.UIToolTip_img_Item_Frame2_e)), SW = assetManager.LoadTexture(null, nameof(Res.UIToolTip_img_Item_Frame2_sw)), S = assetManager.LoadTexture(null, nameof(Res.UIToolTip_img_Item_Frame2_s)), SE = assetManager.LoadTexture(null, nameof(Res.UIToolTip_img_Item_Frame2_se)), } }; var msgBoxBackgroundBrush = new MessageBoxBackgroundBrush() { Resource = new MessageBoxBackgroundResource() { T = assetManager.LoadTexture(null, nameof(MRes.Basic_img_Notice6_t)), C = assetManager.LoadTexture(null, nameof(MRes.Basic_img_Notice6_c)), C_Box = assetManager.LoadTexture(null, nameof(MRes.Basic_img_Notice6_c_box)), Box = assetManager.LoadTexture(null, nameof(MRes.Basic_img_Notice6_box)), S_Box = assetManager.LoadTexture(null, nameof(MRes.Basic_img_Notice6_s_box)), S = assetManager.LoadTexture(null, nameof(MRes.Basic_img_Notice6_s)), } }; this.Resources[MapRenderResourceKey.TooltipBrush] = tooltipBrush; this.Resources[MapRenderResourceKey.MessageBoxBackgroundBrush] = msgBoxBackgroundBrush; } public void UnloadContents() { FontManager.Instance.ClearCache(); ImageManager.Instance.ClearCache(); SoundManager.Instance.ClearCache(); EffectManager.Instance.ClearCache(); FontManager.DefaultFont = null; } protected override void OnKeyDown(KeyEventArgs e) { var listener = e.Source as KeyInputListener; if (listener != null) { foreach (var binding in this.InputBindings) { if (binding.IsRepeatEnabled && binding.Gesture.Matches(e)) { listener.EnableRepeat(); break; } } } base.OnKeyDown(e); } public new void UpdateInput(double elapsedGameTime) { try { base.UpdateInput(elapsedGameTime); } catch(Exception ex) { } this.OnInputUpdated(EventArgs.Empty); } protected virtual void OnInputUpdated(EventArgs e) { this.InputUpdated?.Invoke(this, e); } } } ================================================ FILE: WzComparerR2.MapRender/UI/MessageBoxBackgroundBrush.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using EmptyKeys.UserInterface; using EmptyKeys.UserInterface.Media; using EmptyKeys.UserInterface.Controls; using EmptyKeys.UserInterface.Renderers; using Microsoft.Xna.Framework; using TextureBlock = WzComparerR2.MapRender.UI.UIGraphics.RenderBlock; namespace WzComparerR2.MapRender.UI { class MessageBoxBackgroundBrush : Brush { public static readonly DependencyProperty ResourceProperty = DependencyProperty.Register("Resource", typeof(MessageBoxBackgroundResource), typeof(MessageBoxBackgroundBrush), new FrameworkPropertyMetadata(null)); public MessageBoxBackgroundBrush() { } public MessageBoxBackgroundResource Resource { get { return (MessageBoxBackgroundResource)this.GetValue(ResourceProperty); } set { this.SetValue(ResourceProperty, value); } } public override void Draw(TextureBase texture, Renderer renderer, double elapsedGameTime, PointF position, Size renderSize, float opacity) { if (this.Resource == null) { return; } IList blocks; var boxOffset = this.GetBoxOffset(); if (boxOffset >= 0) { blocks = this.Layout(this.Resource, new Point((int)renderSize.Width, (int)renderSize.Height), (int)boxOffset); } else //fallback { blocks = UIGraphics.LayoutTCB(this.Resource, new Point((int)renderSize.Width, (int)renderSize.Height)); } foreach (var block in blocks) { if (block.Texture != null && block.Rectangle.Width > 0 && block.Rectangle.Height > 0) { PointF pos = new PointF(block.Rectangle.X + position.X, block.Rectangle.Y + position.Y); Size size = new Size(block.Rectangle.Width, block.Rectangle.Height); ColorW color = new ColorW(1f, 1f, 1f, this.Opacity); renderer.Draw(block.Texture, pos, size, color, false); } } } private float GetBoxOffset() { Grid grid = this.Parent as Grid; if (grid != null) { ContentPresenter pnlContent = VisualTreeHelper.Instance.FindElementByName(grid, "PART_WindowContent") as ContentPresenter; if (pnlContent != null) { var grid2 = pnlContent.Content as Grid; if (grid2 != null) { StackPanel pnlButtons = grid2.Children.OfType().FirstOrDefault(); if (pnlButtons != null) { return pnlButtons.VisualPosition.Y - grid.VisualPosition.Y; } } } } return float.NaN; } private IList Layout(MessageBoxBackgroundResource res, Point size, int boxOffset) { var blocks = new List(6); Point t = res.GetSize(res.T); Point c = res.GetSize(res.C); Point cBox = res.GetSize(res.C_Box); Point box = res.GetSize(res.Box); Point sBox = res.GetSize(res.S_Box); //计算框线 int[] y = new int[6] { 0, t.Y, boxOffset - cBox.Y, boxOffset, size.Y - sBox.Y, size.Y }; //绘制上 blocks.Add(new TextureBlock(res.T, new Rectangle(0, y[0], size.X, y[1] - y[0]))); //绘制中 blocks.Add(new TextureBlock(res.C, new Rectangle(0, y[1], size.X, y[2] - y[1]))); //绘制box blocks.Add(new TextureBlock(res.C_Box, new Rectangle(0, y[2], size.X, y[3] - y[2]))); blocks.Add(new TextureBlock(res.Box, new Rectangle(0, y[3], size.X, y[4] - y[3]))); blocks.Add(new TextureBlock(res.S_Box, new Rectangle(0, y[4], size.X, y[5] - y[4]))); return blocks; } public override void DrawGeometry(GeometryBuffer buffer, TextureBase texture, Renderer renderer, double elapsedGameTime, PointF position, float opacity) { throw new NotImplementedException(); } } class MessageBoxBackgroundResource : INinePatchResource { public TextureBase T { get; set; } public TextureBase C { get; set; } public TextureBase C_Box { get; set; } public TextureBase Box { get; set; } public TextureBase S_Box { get; set; } public TextureBase S { get; set; } TextureBase INinePatchResource.NW { get { return null; } } TextureBase INinePatchResource.N { get { return this.T; } } TextureBase INinePatchResource.NE { get { return null; } } TextureBase INinePatchResource.W { get { return null; } } TextureBase INinePatchResource.C { get { return this.C; } } TextureBase INinePatchResource.E { get { return null; } } TextureBase INinePatchResource.SW { get { return null; } } TextureBase INinePatchResource.S { get { return this.S; } } TextureBase INinePatchResource.SE { get { return null; } } public Point GetSize(TextureBase texture) { return new Point(texture.Width, texture.Height); } } } ================================================ FILE: WzComparerR2.MapRender/UI/MessageBoxStyle.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using EmptyKeys.UserInterface; using EmptyKeys.UserInterface.Controls; using EmptyKeys.UserInterface.Data; using EmptyKeys.UserInterface.Themes; namespace WzComparerR2.MapRender.UI { static class MessageBoxStyle { public static Style CreateMessageBoxStyle() { Style style = new Style(typeof(Window)); style.Setters.Add(new Setter(Control.TemplateProperty, CreateMessageBoxControlTemplate())); return style; } public static ControlTemplate CreateMessageBoxControlTemplate() { ControlTemplate template = new ControlTemplate(typeof(Window), CreateMessageBoxControlFunc); return template; } private static UIElement CreateMessageBoxControlFunc(UIElement parent) { Grid screenBg = new Grid(); screenBg.HorizontalAlignment = HorizontalAlignment.Center; screenBg.ColumnDefinitions.Add(new ColumnDefinition() { Width = GridLength.Auto }); screenBg.SetBinding(Control.BackgroundProperty, new Binding(Control.BackgroundProperty) { Source = parent }); screenBg.Parent = parent; screenBg.Foreground = EmptyKeys.UserInterface.Media.Brushes.White; Grid grid = new Grid(); grid.Width = 260; grid.SetResourceReference(Control.BackgroundProperty, MapRenderResourceKey.MessageBoxBackgroundBrush); grid.ColumnDefinitions.Add(new ColumnDefinition()); grid.RowDefinitions.Add(new RowDefinition()); grid.RowDefinitions.Add(new RowDefinition()); screenBg.Children.Add(grid); Grid.SetColumn(grid, 0); Border border = new Border(); grid.Children.Add(border); Grid.SetRow(border, 0); ContentPresenter pnlTitle = CommonHelpers.CreateContentPresenter(parent, "Title"); pnlTitle.Height = 26; pnlTitle.Margin = new Thickness(0, 4, 0, 0); pnlTitle.HorizontalAlignment = HorizontalAlignment.Center; pnlTitle.VerticalAlignment = VerticalAlignment.Center; pnlTitle.IsHitTestVisible = false; pnlTitle.Name = "PART_WindowTitle"; border.Child = pnlTitle; ContentPresenter pnlContent = CommonHelpers.CreateContentPresenter(parent, "Content"); pnlContent.Margin = new Thickness(20, 8, 20, 0); pnlContent.Name = "PART_WindowContent"; grid.Children.Add(pnlContent); Grid.SetRow(pnlContent, 1); Window msgBox = parent as Window; if (msgBox != null) { msgBox.Visible += MsgBox_Visible; msgBox.SizeChanged += MsgBox_SizeChanged; var style = MapRenderButtonStyle.CreateMapRenderButtonStyle(); style.TargetType = typeof(Button); msgBox.Resources[typeof(Button)] = style; } return screenBg; } private static void MsgBox_Visible(object sender, RoutedEventArgs e) { var wnd = sender as Window; if (wnd != null) { var grid = wnd.Content as Grid; if (grid != null) { var textBlock = grid.Children.OfType().FirstOrDefault(); if (textBlock != null) { textBlock.Margin = new Thickness(0, 0, 0, 8); if (textBlock.Text != null && textBlock.Font != null) { textBlock.TextWrapping = TextWrapping.NoWrap; var size = textBlock.Font.MeasureString(textBlock.Text, new Size(220, 0)); if (size.Height > textBlock.ActualHeight) { textBlock.Height = size.Height; } } } var stackPanel = grid.Children.OfType().FirstOrDefault(); if (stackPanel != null) { stackPanel.HorizontalAlignment = HorizontalAlignment.Right; foreach (var btn in stackPanel.Children.OfType