From bc2ef07de4c0c780d5aece4e70677f19ac742f02 Mon Sep 17 00:00:00 2001 From: Julian Xhokaxhiu Date: Sun, 28 Aug 2022 16:15:21 +0200 Subject: [PATCH] Huge overhaul of core internals - Migrate to .NET 6 - Deprecate EasyHook in favor of a custom CLR + Detours logic ( helps us keeping code scaling in future ) - Use native Powershell instead of the library - .ci/prepare.ps1: Enable vcpkg integration with Visual Studio - .ci/package.ps1: Use the new release path - .ci/azure.yml: Enable vcpkg integration - .ci/azure.yml: Use the latest NuGet 6.x - .ci/azure.yml: Enable parallel builds - .iss/installer.iss: Use the new release path Thanks to Benjamin Moir for helping on this huge milestone! You rock! --- .ci/azure.yml | 32 +- .ci/package.ps1 | 2 +- .ci/prepare.ps1 | 22 + .gitignore | 403 +++++ .iss/installer.iss | 6 +- 7thHeaven.Code/7thHeaven.Code.csproj | 144 +- 7thHeaven.Code/RegistryHelper.cs | 10 +- 7thHeaven.Code/Sys.cs | 10 +- 7thHeaven.sln | 66 +- 7thWrapperLib/7thWrapperLib.csproj | 118 +- 7thWrapperLib/DetourTransaction.cs | 35 + 7thWrapperLib/HexPatch.cs | 3 +- 7thWrapperLib/IrosArc.cs | 3 +- 7thWrapperLib/Overrides.cs | 2 +- 7thWrapperLib/RuntimeVar.cs | 3 +- 7thWrapperLib/Util.cs | 13 + 7thWrapperLib/VFile.cs | 45 +- 7thWrapperLib/VStreamFile.cs | 11 +- 7thWrapperLib/Win32.cs | 84 +- 7thWrapperLib/Wrap.cs | 1331 +++++++++-------- 7thWrapperLoader/7thWrapperLoader.vcxproj | 187 +++ .../7thWrapperLoader.vcxproj.filters | 36 + 7thWrapperLoader/delegates.x.h | 1 + 7thWrapperLoader/dllmain.cpp | 83 + 7thWrapperLoader/host_exports.x.h | 4 + 7thWrapperLoader/hostfxr.x.h | 4 + 7thWrapperLoader/vcpkg.json | 21 + 7thWrapperProxy/7thWrapperProxy.csproj | 20 + 7thWrapperProxy/Main.cs | 34 + README.md | 11 + SeventhHeavenUI/App.xaml.cs | 32 +- .../Classes/ControlConfiguration.cs | 3 +- SeventhHeavenUI/Classes/GameConverter.cs | 3 +- SeventhHeavenUI/Classes/GameDiscMounter.cs | 31 +- SeventhHeavenUI/Classes/GameLauncher.cs | 169 +-- SeventhHeavenUI/Classes/OpenFolderDialog.cs | 59 +- SeventhHeavenUI/Classes/WCF/ServiceHost.cs | 384 ++--- SeventhHeavenUI/EasyHook.dll | Bin 61440 -> 0 bytes SeventhHeavenUI/SeventhHeavenUI.csproj | 1142 +++++--------- .../ViewModels/CatalogViewModel.cs | 5 +- .../ViewModels/ControlMappingViewModel.cs | 4 +- .../ViewModels/GeneralSettingsViewModel.cs | 2 +- .../ViewModels/MainWindowViewModel.cs | 27 +- .../ViewModels/OpenProfileViewModel.cs | 6 +- SeventhHeavenUI/Windows/AboutWindow.xaml.cs | 6 +- SeventhHeavenUI/Windows/MainWindow.xaml.cs | 36 +- SeventhHeavenUI/app.manifest | 15 - TestPlugin/TestPlugin.csproj | 64 +- TurBoLog/TurBoLog.csproj | 89 +- TurBoLog/UI/FMonitor.cs | 101 +- TurBoLog/UI/FMonitor.resx | 60 - TurBoLog/app.config | 3 - 52 files changed, 2561 insertions(+), 2424 deletions(-) create mode 100644 7thWrapperLib/DetourTransaction.cs create mode 100644 7thWrapperLoader/7thWrapperLoader.vcxproj create mode 100644 7thWrapperLoader/7thWrapperLoader.vcxproj.filters create mode 100644 7thWrapperLoader/delegates.x.h create mode 100644 7thWrapperLoader/dllmain.cpp create mode 100644 7thWrapperLoader/host_exports.x.h create mode 100644 7thWrapperLoader/hostfxr.x.h create mode 100644 7thWrapperLoader/vcpkg.json create mode 100644 7thWrapperProxy/7thWrapperProxy.csproj create mode 100644 7thWrapperProxy/Main.cs delete mode 100644 SeventhHeavenUI/EasyHook.dll delete mode 100644 TurBoLog/app.config diff --git a/.ci/azure.yml b/.ci/azure.yml index 5e15d8d1..1cc94512 100644 --- a/.ci/azure.yml +++ b/.ci/azure.yml @@ -1,6 +1,6 @@ # Azure Pipeline YAML file -name: 2.7.1$(Rev:.r) +name: 2.9.9$(Rev:.r) trigger: batch: true @@ -16,21 +16,29 @@ pr: variables: _IS_BUILD_CANARY: false + _IS_GITHUB_RELEASE: false _RELEASE_NAME: 7thHeaven _RELEASE_VERSION: v0 _RELEASE_CONFIGURATION: Release _BUILD_VERSION: $(Build.BuildNumber) _BUILD_BRANCH: $(Build.SourceBranch) + # VCPKG: Enable Binary Caching + VCPKG_BINARY_SOURCES: clear;nuget,https://pkgs.dev.azure.com/julianxhokaxhiu/Github/_packaging/7thHeaven/nuget/v3/index.json,readwrite + # GIT: Fix reporting from stderr to stdout + GIT_REDIRECT_STDERR: 2>&1 pool: vmImage: 'windows-2022' steps: - checkout: self +- task: NuGetAuthenticate@1 - task: PowerShell@2 displayName: 'Prepare $(_RELEASE_NAME) Env' inputs: filePath: $(Build.Repository.LocalPath)\.ci\prepare.ps1 + failOnStderr: true + pwsh: true - task: bleddynrichards.Assembly-Info-Task.Assembly-Info-Task.Assembly-Info-NetFramework@2 displayName: 'Set $(_RELEASE_NAME) Assembly Manifest Data' inputs: @@ -43,7 +51,7 @@ steps: InformationalVersion: $(_BUILD_VERSION) - task: NuGetToolInstaller@1 inputs: - versionSpec: 5.x + versionSpec: 6.x checkLatest: true - task: NuGetCommand@2 displayName: 'Restore $(_RELEASE_NAME) NuGet Packages' @@ -56,12 +64,14 @@ steps: solution: $(Build.Repository.LocalPath)\$(_RELEASE_NAME).sln platform: Any CPU configuration: $(_RELEASE_CONFIGURATION) - msbuildArgs: /p:WindowsTargetPlatformVersion=10.0.19041.0 + msbuildArgs: /p:WindowsTargetPlatformVersion=10.0.19041.0 /m - task: PowerShell@2 condition: and(not(contains(variables._BUILD_BRANCH, 'refs/pull/')), succeeded()) displayName: 'Package $(_RELEASE_NAME)' inputs: filePath: $(Build.Repository.LocalPath)\.ci\package.ps1 + failOnStderr: true + pwsh: true env: buildPath: $(Build.Repository.LocalPath) - task: PowerShell@2 @@ -69,10 +79,18 @@ steps: displayName: 'Prepare $(_RELEASE_NAME) Installer' inputs: filePath: $(Build.Repository.LocalPath)\.iss\build.ps1 + failOnStderr: true + pwsh: true env: buildPath: $(Build.Repository.LocalPath) +- task: PublishPipelineArtifact@1 + displayName: 'Publish $(_RELEASE_NAME) Artifact' + condition: and(eq(variables._IS_GITHUB_RELEASE, 'false'), succeeded()) + inputs: + targetPath: .dist\ + artifactName: $(_RELEASE_NAME)-$(_RELEASE_VERSION) - task: GitHubRelease@0 - condition: and(eq(variables._IS_BUILD_CANARY, 'true'), not(contains(variables._BUILD_BRANCH, 'refs/pull/')), succeeded()) + condition: and(eq(variables._IS_GITHUB_RELEASE, 'true'), eq(variables._IS_BUILD_CANARY, 'true'), succeeded()) displayName: 'Delete $(_RELEASE_NAME) Release (Canary)' continueOnError: true inputs: @@ -81,7 +99,7 @@ steps: tagSource: manual tag: canary - task: GitHubRelease@0 - condition: and(eq(variables._IS_BUILD_CANARY, 'true'), not(contains(variables._BUILD_BRANCH, 'refs/pull/')), succeeded()) + condition: and(eq(variables._IS_GITHUB_RELEASE, 'true'), eq(variables._IS_BUILD_CANARY, 'true'), succeeded()) displayName: 'Create $(_RELEASE_NAME) Release (Canary)' inputs: gitHubConnection: github_ci @@ -94,14 +112,14 @@ steps: releaseNotes: | This is a canary build. Please be aware it may be prone to crashing and is NOT tested by anyone. Use this build AT YOUR OWN RISK! - task: GitHubRelease@0 - condition: and(eq(variables._IS_BUILD_CANARY, 'false'), not(contains(variables._BUILD_BRANCH, 'refs/pull/')), succeeded()) + condition: and(eq(variables._IS_GITHUB_RELEASE, 'true'), eq(variables._IS_BUILD_CANARY, 'false'), succeeded()) displayName: 'Create $(_RELEASE_NAME) Release (Stable)' inputs: gitHubConnection: github_ci assets: $(Build.Repository.LocalPath)\.dist\* title: $(_RELEASE_NAME)-$(_RELEASE_VERSION) - task: PowerShell@2 - condition: and(not(contains(variables._BUILD_BRANCH, 'refs/pull/')), succeeded()) + condition: and(eq(variables._IS_GITHUB_RELEASE, 'true'), succeeded()) displayName: 'Alert the $(_RELEASE_NAME) Release' inputs: filePath: .ci\alert.ps1 diff --git a/.ci/package.ps1 b/.ci/package.ps1 index 590b5723..a550ee67 100644 --- a/.ci/package.ps1 +++ b/.ci/package.ps1 @@ -1,4 +1,4 @@ mkdir ${env:buildPath}\.dist | Out-Null -Set-Location ${env:buildPath}\SeventhHeavenUI\bin\Release +Set-Location "${env:buildPath}\SeventhHeavenUI\bin\Release\net6.0-windows" 7z a "${env:buildPath}\.dist\${env:_RELEASE_NAME}-${env:_RELEASE_VERSION}_${env:_RELEASE_CONFIGURATION}.zip" ".\*" diff --git a/.ci/prepare.ps1 b/.ci/prepare.ps1 index acf0f767..0dc88178 100644 --- a/.ci/prepare.ps1 +++ b/.ci/prepare.ps1 @@ -1,16 +1,38 @@ if ($env:_BUILD_BRANCH -eq "refs/heads/master" -Or $env:_BUILD_BRANCH -eq "refs/tags/canary") { $env:_IS_BUILD_CANARY = "true" + $env:_IS_GITHUB_RELEASE = "true" } elseif ($env:_BUILD_BRANCH -like "refs/tags/*") { $env:_BUILD_VERSION = $env:_BUILD_VERSION.Substring(0, $env:_BUILD_VERSION.LastIndexOf('.')) + ".0" + $env:_IS_GITHUB_RELEASE = "true" } $env:_RELEASE_VERSION = "v${env:_BUILD_VERSION}" +$vcpkgRoot = "C:\vcpkg" +$vcpkgBaseline = [string](jq --arg baseline "builtin-baseline" -r '.[$baseline]' 7thWrapperLoader/vcpkg.json) +$vcpkgOriginUrl = &"git" -C $vcpkgRoot remote get-url origin +$vcpkgBranchName = &"git" -C $vcpkgRoot branch --show-current + Write-Output "--------------------------------------------------" Write-Output "BUILD CONFIGURATION: $env:_RELEASE_CONFIGURATION" Write-Output "RELEASE VERSION: $env:_RELEASE_VERSION" +Write-Output "VCPKG ORIGIN: $vcpkgOriginUrl" +Write-Output "VCPKG BRANCH: $vcpkgBranchName" +Write-Output "VCPKG BASELINE: $vcpkgBaseline" Write-Output "--------------------------------------------------" Write-Host "##vso[task.setvariable variable=_BUILD_VERSION;]${env:_BUILD_VERSION}" Write-Host "##vso[task.setvariable variable=_RELEASE_VERSION;]${env:_RELEASE_VERSION}" Write-Host "##vso[task.setvariable variable=_IS_BUILD_CANARY;]${env:_IS_BUILD_CANARY}" +Write-Host "##vso[task.setvariable variable=_IS_GITHUB_RELEASE;]${env:_IS_GITHUB_RELEASE}" + +git -C $vcpkgRoot pull --all +git -C $vcpkgRoot checkout $vcpkgBaseline +git -C $vcpkgRoot clean -fxd + +cmd.exe /c "call $vcpkgRoot\bootstrap-vcpkg.bat" + +mkdir "C:\vcpkg\downloads\tools\yasm\1.3.0.6" | Out-Null +Invoke-WebRequest -Uri "http://www.tortall.net/projects/yasm/snapshots/v1.3.0.6.g1962/yasm-1.3.0.6.g1962.exe" -SkipCertificateCheck -OutFile "C:\vcpkg\downloads\tools\yasm\1.3.0.6\yasm.exe" + +vcpkg integrate install diff --git a/.gitignore b/.gitignore index e1b6875d..d4b7b55e 100644 --- a/.gitignore +++ b/.gitignore @@ -217,3 +217,406 @@ FakesAssemblies/ _Pvt_Extensions /.dist/Deploy_Zips /SeventhHeavenUI/Resources/Game Driver/hext/ff8 + +############################################################################################### + +## C++ Ignore file + +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*[.json, .xml, .info] + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# 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 +# Note: 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 + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#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 +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +## Custom section + +# Build directory +.build + +# Clion +cmake-build*/ + +# vcpkg +!make.exe +vcpkg_installed/ diff --git a/.iss/installer.iss b/.iss/installer.iss index 270b271a..8266d203 100644 --- a/.iss/installer.iss +++ b/.iss/installer.iss @@ -14,6 +14,10 @@ #define MyAppRelease "Debug" #endif +#ifndef MyAppTargetFramework + #define MyAppTargetFramework "net6.0-windows" +#endif + [Setup] AppId={{E66AE545-C285-4B8C-8BD0-67282E160BF4} AppName={#MyAppName} @@ -37,7 +41,7 @@ UninstallDisplayName={#MyAppName} ArchitecturesInstallIn64BitMode=x64 [Files] -Source: "{#MyAppPath}\bin\{#MyAppRelease}\*"; DestDir: "{app}"; Flags: recursesubdirs +Source: "{#MyAppPath}\bin\{#MyAppRelease}\{#MyAppTargetFramework}\*"; DestDir: "{app}"; Flags: recursesubdirs Source: "{#MyAppPath}\7H.ico"; DestDir: "{app}"; DestName: "uninstall.ico" [Icons] diff --git a/7thHeaven.Code/7thHeaven.Code.csproj b/7thHeaven.Code/7thHeaven.Code.csproj index 589772f8..1ff6e933 100644 --- a/7thHeaven.Code/7thHeaven.Code.csproj +++ b/7thHeaven.Code/7thHeaven.Code.csproj @@ -1,154 +1,46 @@ - - - + - Debug - AnyCPU - {CD2F4CCB-4735-4DEE-8867-B0ED2CA04303} + net6.0-windows Library - Properties _7thHeaven.Code - 7thHeaven.Code - 512 - - v4.8 1.56 - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - false - AnyCPU + false + true + true + true - - .allowedextension + .allowedextension none - true - bin\Release\ - TRACE - prompt - 4 - false - false - AnyCPU + CA1416 - true bin\x86\Debug\ - DEBUG;TRACE - full - x86 - 7.3 - prompt MinimumRecommendedRules.ruleset - - .allowedextension + .allowedextension bin\x86\Release\ - TRACE - true - x86 - 7.3 - prompt MinimumRecommendedRules.ruleset - - true - - true bin\x64\Debug\ - DEBUG;TRACE - full - x64 - 7.3 - prompt bin\x64\Release\ - TRACE - true - x64 - 7.3 - prompt + + + CA1416 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Workshop.resx - True - True - - - - - ResXFileCodeGenerator - Workshop.Designer.cs - - - - - - - - {f80db232-4102-4249-8b31-2ddd5c7fa404} - 7thWrapperLib - + + 0.32.2 @@ -156,12 +48,4 @@ 0.15.0 - - \ No newline at end of file diff --git a/7thHeaven.Code/RegistryHelper.cs b/7thHeaven.Code/RegistryHelper.cs index b59d2a6e..af5384a8 100644 --- a/7thHeaven.Code/RegistryHelper.cs +++ b/7thHeaven.Code/RegistryHelper.cs @@ -1,4 +1,5 @@ -using Microsoft.Win32; +using Iros._7th.Workshop; +using Microsoft.Win32; using System; using System.Collections.Generic; using System.Diagnostics; @@ -221,7 +222,12 @@ public static bool ExportKey(string regKey, string savePath) proc.StartInfo.FileName = "reg.exe"; proc.StartInfo.UseShellExecute = false; proc.StartInfo.CreateNoWindow = true; - proc = Process.Start("reg.exe", $"export \"{regKey}\" \"{savePath}\" /reg:{bitness} /y"); + ProcessStartInfo startInfo = new ProcessStartInfo("reg.exe") + { + Arguments = $"export \"{regKey}\" \"{savePath}\" /reg:{bitness} /y", + UseShellExecute = true + }; + proc = Process.Start(startInfo); if (proc != null) proc.WaitForExit(); return true; diff --git a/7thHeaven.Code/Sys.cs b/7thHeaven.Code/Sys.cs index 414cdbe9..1ddc6a59 100644 --- a/7thHeaven.Code/Sys.cs +++ b/7thHeaven.Code/Sys.cs @@ -495,7 +495,10 @@ public static void OpenAppLog() return; } - ProcessStartInfo startInfo = new ProcessStartInfo(Sys.PathToApplog); + ProcessStartInfo startInfo = new ProcessStartInfo(Sys.PathToApplog) + { + UseShellExecute = true + }; Process.Start(startInfo); } @@ -503,7 +506,10 @@ public static void OpenLibraryFolderInExplorer() { if (Directory.Exists(Settings.LibraryLocation)) { - ProcessStartInfo startInfo = new ProcessStartInfo(Settings.LibraryLocation); + ProcessStartInfo startInfo = new ProcessStartInfo(Settings.LibraryLocation) + { + UseShellExecute = true, + }; Process.Start(startInfo); } } diff --git a/7thHeaven.sln b/7thHeaven.sln index c3d6b8ac..7c57783b 100644 --- a/7thHeaven.sln +++ b/7thHeaven.sln @@ -1,17 +1,21 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.29102.190 +# Visual Studio Version 17 +VisualStudioVersion = 17.3.32819.101 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SeventhHeavenUI", "SeventhHeavenUI\SeventhHeavenUI.csproj", "{5F6C267D-0F93-4C71-BA58-0C1AD6242383}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SeventhHeavenUI", "SeventhHeavenUI\SeventhHeavenUI.csproj", "{5F6C267D-0F93-4C71-BA58-0C1AD6242383}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "7thWrapperLib", "7thWrapperLib\7thWrapperLib.csproj", "{F80DB232-4102-4249-8B31-2DDD5C7FA404}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "7thWrapperLib", "7thWrapperLib\7thWrapperLib.csproj", "{F80DB232-4102-4249-8B31-2DDD5C7FA404}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestPlugin", "TestPlugin\TestPlugin.csproj", "{A64400A1-1E22-4F22-884E-568A776141C5}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestPlugin", "TestPlugin\TestPlugin.csproj", "{A64400A1-1E22-4F22-884E-568A776141C5}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "7thHeaven.Code", "7thHeaven.Code\7thHeaven.Code.csproj", "{CD2F4CCB-4735-4DEE-8867-B0ED2CA04303}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "7thHeaven.Code", "7thHeaven.Code\7thHeaven.Code.csproj", "{CD2F4CCB-4735-4DEE-8867-B0ED2CA04303}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TurBoLog", "TurBoLog\TurBoLog.csproj", "{03EEDE27-0ABB-4CDA-A7F2-F746E2083202}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TurBoLog", "TurBoLog\TurBoLog.csproj", "{03EEDE27-0ABB-4CDA-A7F2-F746E2083202}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "7thWrapperLoader", "7thWrapperLoader\7thWrapperLoader.vcxproj", "{D95622DB-5459-4369-9585-F6457AAF3FC3}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "7thWrapperProxy", "7thWrapperProxy\7thWrapperProxy.csproj", "{F955F161-A47E-4399-9FD2-DB0D83E6461A}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -23,6 +27,18 @@ Global Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution + {5F6C267D-0F93-4C71-BA58-0C1AD6242383}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5F6C267D-0F93-4C71-BA58-0C1AD6242383}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5F6C267D-0F93-4C71-BA58-0C1AD6242383}.Debug|x64.ActiveCfg = Debug|x64 + {5F6C267D-0F93-4C71-BA58-0C1AD6242383}.Debug|x64.Build.0 = Debug|x64 + {5F6C267D-0F93-4C71-BA58-0C1AD6242383}.Debug|x86.ActiveCfg = Debug|x86 + {5F6C267D-0F93-4C71-BA58-0C1AD6242383}.Debug|x86.Build.0 = Debug|x86 + {5F6C267D-0F93-4C71-BA58-0C1AD6242383}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5F6C267D-0F93-4C71-BA58-0C1AD6242383}.Release|Any CPU.Build.0 = Release|Any CPU + {5F6C267D-0F93-4C71-BA58-0C1AD6242383}.Release|x64.ActiveCfg = Release|x64 + {5F6C267D-0F93-4C71-BA58-0C1AD6242383}.Release|x64.Build.0 = Release|x64 + {5F6C267D-0F93-4C71-BA58-0C1AD6242383}.Release|x86.ActiveCfg = Release|x86 + {5F6C267D-0F93-4C71-BA58-0C1AD6242383}.Release|x86.Build.0 = Release|x86 {F80DB232-4102-4249-8B31-2DDD5C7FA404}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F80DB232-4102-4249-8B31-2DDD5C7FA404}.Debug|Any CPU.Build.0 = Debug|Any CPU {F80DB232-4102-4249-8B31-2DDD5C7FA404}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -69,18 +85,30 @@ Global {03EEDE27-0ABB-4CDA-A7F2-F746E2083202}.Release|x64.Build.0 = Release|x64 {03EEDE27-0ABB-4CDA-A7F2-F746E2083202}.Release|x86.ActiveCfg = Release|Any CPU {03EEDE27-0ABB-4CDA-A7F2-F746E2083202}.Release|x86.Build.0 = Release|Any CPU - {5F6C267D-0F93-4C71-BA58-0C1AD6242383}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {5F6C267D-0F93-4C71-BA58-0C1AD6242383}.Debug|Any CPU.Build.0 = Debug|Any CPU - {5F6C267D-0F93-4C71-BA58-0C1AD6242383}.Debug|x64.ActiveCfg = Debug|x64 - {5F6C267D-0F93-4C71-BA58-0C1AD6242383}.Debug|x64.Build.0 = Debug|x64 - {5F6C267D-0F93-4C71-BA58-0C1AD6242383}.Debug|x86.ActiveCfg = Debug|x86 - {5F6C267D-0F93-4C71-BA58-0C1AD6242383}.Debug|x86.Build.0 = Debug|x86 - {5F6C267D-0F93-4C71-BA58-0C1AD6242383}.Release|Any CPU.ActiveCfg = Release|Any CPU - {5F6C267D-0F93-4C71-BA58-0C1AD6242383}.Release|Any CPU.Build.0 = Release|Any CPU - {5F6C267D-0F93-4C71-BA58-0C1AD6242383}.Release|x64.ActiveCfg = Release|x64 - {5F6C267D-0F93-4C71-BA58-0C1AD6242383}.Release|x64.Build.0 = Release|x64 - {5F6C267D-0F93-4C71-BA58-0C1AD6242383}.Release|x86.ActiveCfg = Release|x86 - {5F6C267D-0F93-4C71-BA58-0C1AD6242383}.Release|x86.Build.0 = Release|x86 + {D95622DB-5459-4369-9585-F6457AAF3FC3}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {D95622DB-5459-4369-9585-F6457AAF3FC3}.Debug|Any CPU.Build.0 = Debug|Win32 + {D95622DB-5459-4369-9585-F6457AAF3FC3}.Debug|x64.ActiveCfg = Debug|x64 + {D95622DB-5459-4369-9585-F6457AAF3FC3}.Debug|x64.Build.0 = Debug|x64 + {D95622DB-5459-4369-9585-F6457AAF3FC3}.Debug|x86.ActiveCfg = Debug|Win32 + {D95622DB-5459-4369-9585-F6457AAF3FC3}.Debug|x86.Build.0 = Debug|Win32 + {D95622DB-5459-4369-9585-F6457AAF3FC3}.Release|Any CPU.ActiveCfg = Release|Win32 + {D95622DB-5459-4369-9585-F6457AAF3FC3}.Release|Any CPU.Build.0 = Release|Win32 + {D95622DB-5459-4369-9585-F6457AAF3FC3}.Release|x64.ActiveCfg = Release|x64 + {D95622DB-5459-4369-9585-F6457AAF3FC3}.Release|x64.Build.0 = Release|x64 + {D95622DB-5459-4369-9585-F6457AAF3FC3}.Release|x86.ActiveCfg = Release|Win32 + {D95622DB-5459-4369-9585-F6457AAF3FC3}.Release|x86.Build.0 = Release|Win32 + {F955F161-A47E-4399-9FD2-DB0D83E6461A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F955F161-A47E-4399-9FD2-DB0D83E6461A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F955F161-A47E-4399-9FD2-DB0D83E6461A}.Debug|x64.ActiveCfg = Debug|Any CPU + {F955F161-A47E-4399-9FD2-DB0D83E6461A}.Debug|x64.Build.0 = Debug|Any CPU + {F955F161-A47E-4399-9FD2-DB0D83E6461A}.Debug|x86.ActiveCfg = Debug|Any CPU + {F955F161-A47E-4399-9FD2-DB0D83E6461A}.Debug|x86.Build.0 = Debug|Any CPU + {F955F161-A47E-4399-9FD2-DB0D83E6461A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F955F161-A47E-4399-9FD2-DB0D83E6461A}.Release|Any CPU.Build.0 = Release|Any CPU + {F955F161-A47E-4399-9FD2-DB0D83E6461A}.Release|x64.ActiveCfg = Release|Any CPU + {F955F161-A47E-4399-9FD2-DB0D83E6461A}.Release|x64.Build.0 = Release|Any CPU + {F955F161-A47E-4399-9FD2-DB0D83E6461A}.Release|x86.ActiveCfg = Release|Any CPU + {F955F161-A47E-4399-9FD2-DB0D83E6461A}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/7thWrapperLib/7thWrapperLib.csproj b/7thWrapperLib/7thWrapperLib.csproj index 6c71db6a..16ad2276 100644 --- a/7thWrapperLib/7thWrapperLib.csproj +++ b/7thWrapperLib/7thWrapperLib.csproj @@ -1,148 +1,48 @@ - - + - Debug - AnyCPU - 8.0.30703 - 2.0 - {F80DB232-4102-4249-8B31-2DDD5C7FA404} + net6.0-windows Library - Properties _7thWrapperLib - 7thWrapperLib - 512 - - v4.8 1.56 + false + true + true + true - true - full - false - bin\Release\ - DEBUG;TRACE - prompt - 4 true - false - AnyCPU + CA1416 - .allowedextension none - true - bin\Release\ - TRACE - prompt - 4 - false true - AnyCPU - - - true + CA1416 - x64 bin\x64\Debug\ true - AnyCPU bin\x64\Release\ true - true - true bin\x86\Debug\ - DEBUG;TRACE true - full - x86 - 7.3 - prompt bin\x86\Release\ - TRACE true - true - x86 - 7.3 - prompt - - - - - - - - - - - - - - - - - - - - - - - - True - True - Settings.settings - - - - - - - - - - - - - SettingsSingleFileGenerator - Settings.Designer.cs - - - - - 1.2.1 - - - 2.7.7097 - - - 2.1.0 - + 0.32.2 - - \ No newline at end of file diff --git a/7thWrapperLib/DetourTransaction.cs b/7thWrapperLib/DetourTransaction.cs new file mode 100644 index 00000000..888069c5 --- /dev/null +++ b/7thWrapperLib/DetourTransaction.cs @@ -0,0 +1,35 @@ +using System; +using static _7thWrapperLib.Wrap; + +namespace _7thWrapperLib +{ + unsafe struct DetourTransaction : IDisposable + { + static HostExports* s_Exports; + + internal static void Initialize(HostExports* exports) + { + s_Exports = exports; + } + + public DetourTransaction() + { + s_Exports->DetourTransactionBegin(); + } + + public void Attach(void** target, void* detour) + { + s_Exports->DetourAttach(target, detour); + } + + public void Detach(void** target, void* detour) + { + s_Exports->DetourDetach(target, detour); + } + + public void Dispose() + { + s_Exports->DetourTransactionCommit(); + } + } +} \ No newline at end of file diff --git a/7thWrapperLib/HexPatch.cs b/7thWrapperLib/HexPatch.cs index 6d6260e1..ed3088d2 100644 --- a/7thWrapperLib/HexPatch.cs +++ b/7thWrapperLib/HexPatch.cs @@ -3,6 +3,7 @@ The original developer is Iros */ +using Iros._7th; using System; using System.Collections.Generic; using System.Linq; @@ -98,7 +99,7 @@ private static void Apply(IEnumerable lines) { { if (prot == Protection.PAGE_EXECUTE || prot == Protection.PAGE_EXECUTE_READ) VirtualProtect(addr, (uint)bytes.Length, Protection.PAGE_EXECUTE_READWRITE, out prot); - System.Runtime.InteropServices.Marshal.Copy(bytes, 0, addr, bytes.Length); + Util.CopyToIntPtr(bytes, addr, bytes.Length); } } else if (instruct.Contains(':')) diff --git a/7thWrapperLib/IrosArc.cs b/7thWrapperLib/IrosArc.cs index 1e300950..c42b12a8 100644 --- a/7thWrapperLib/IrosArc.cs +++ b/7thWrapperLib/IrosArc.cs @@ -3,6 +3,7 @@ The original developer is Iros */ +using Iros._7th; using Microsoft.Win32.SafeHandles; using System; using System.Collections.Generic; @@ -534,7 +535,7 @@ public void RawRead(string file, uint offset, uint length, IntPtr dest, ref uint if ((e.Flags & FileFlags.COMPRESSION_FLAGS) != 0) { var cache = GetCache(e); uint readLen = Math.Min(length, (uint)cache.Data.Length - offset); - System.Runtime.InteropServices.Marshal.Copy(cache.Data, (int)offset, dest, (int)readLen); + Util.CopyToIntPtr(cache.Data, dest, (int)readLen, (int)offset); bytesRead = readLen; if (readLen == 0) { DebugLogger.DetailedWriteLine($"IrosArc RawRead file {file} offset {offset} length {length} read {readLen} bytes - cache data size {cache.Data.Length}"); diff --git a/7thWrapperLib/Overrides.cs b/7thWrapperLib/Overrides.cs index 5ca5e3a2..2a4bfb67 100644 --- a/7thWrapperLib/Overrides.cs +++ b/7thWrapperLib/Overrides.cs @@ -28,7 +28,7 @@ private LoadedFile GetLF(string path, string name) { string file = System.IO.Path.Combine(_base, path, name); if (!_files.TryGetValue(file, out lf)) { lf = new LoadedFile(); - lf.Handle = Wrap.CreateFileW(file.Replace('/', '\\'), System.IO.FileAccess.Read, System.IO.FileShare.Read, IntPtr.Zero, System.IO.FileMode.Open, System.IO.FileAttributes.Normal, IntPtr.Zero); + lf.Handle = Win32.CreateFileW(file.Replace('/', '\\'), System.IO.FileAccess.Read, System.IO.FileShare.Read, IntPtr.Zero, System.IO.FileMode.Open, System.IO.FileAttributes.Normal, IntPtr.Zero); if (lf.Handle == IntPtr.Zero || lf.Handle == new IntPtr(-1)) { lf.Missing = true; lf.Tag = DateTime.MaxValue; diff --git a/7thWrapperLib/RuntimeVar.cs b/7thWrapperLib/RuntimeVar.cs index 94681751..fd4b6c67 100644 --- a/7thWrapperLib/RuntimeVar.cs +++ b/7thWrapperLib/RuntimeVar.cs @@ -3,6 +3,7 @@ The original developer is Iros */ +using Iros._7th; using System; using System.Collections.Generic; using System.Linq; @@ -142,7 +143,7 @@ public static Func MakeRuntimeVar(string spec, string value) { IntPtr address; address = new IntPtr(Parse(parts[1])); return () => { - System.Runtime.InteropServices.Marshal.Copy(address, data, 0, size); + Util.CopyFromIntPtr(address, data, size); string s = FFText.Translate(data); if (!s.Equals(last)) DebugLogger.WriteLine($"RuntimeVar {spec} became {s}"); diff --git a/7thWrapperLib/Util.cs b/7thWrapperLib/Util.cs index 99a64c6b..b7dda912 100644 --- a/7thWrapperLib/Util.cs +++ b/7thWrapperLib/Util.cs @@ -3,9 +3,12 @@ The original developer is Iros */ +using _7thWrapperLib; using System; using System.Collections.Generic; using System.Linq; +using System.Net; +using System.Security.Cryptography; using System.Text; namespace Iros._7th { @@ -45,6 +48,16 @@ public static void Serialize(object o, System.IO.Stream s) { var ser = new System.Xml.Serialization.XmlSerializer(o.GetType()); ser.Serialize(s, o); } + + public unsafe static void CopyFromIntPtr(IntPtr source, byte[] dest, int size, int offset = 0) + { + new Span(source.ToPointer(), size).CopyTo(new Span(dest, offset, size)); + } + + public unsafe static void CopyToIntPtr(byte[] source, IntPtr dest, int size, int offset = 0) + { + source.AsSpan(offset).CopyTo(new Span((void*)dest, size)); + } } public static class XmlUtil { diff --git a/7thWrapperLib/VFile.cs b/7thWrapperLib/VFile.cs index 692a4eaa..5744c41f 100644 --- a/7thWrapperLib/VFile.cs +++ b/7thWrapperLib/VFile.cs @@ -9,7 +9,8 @@ The original developer is Iros using System.Text; using System.IO; using System.Runtime.InteropServices; - +using Iros._7th; + namespace _7thWrapperLib { class VFileException : Exception { public VFileException(string msg) : base(msg) { } @@ -122,7 +123,7 @@ class VRangeInline : VRange { public byte[] Data { get; set; } public override void Read(uint offset, uint length, IntPtr dest, ref uint bytesRead) { - System.Runtime.InteropServices.Marshal.Copy(Data, (int)offset, dest, (int)length); + Util.CopyToIntPtr(Data, dest, (int)length, (int)offset); bytesRead = length; } } @@ -133,7 +134,7 @@ class VRangeNull : VRange { public override void Read(uint offset, uint length, IntPtr dest, ref uint bytesRead) { while(length > 0) { uint len = Math.Min(length, 16384); - System.Runtime.InteropServices.Marshal.Copy(_zero, 0, dest, (int)len); + Util.CopyToIntPtr(_zero, dest, (int)len); length -= len; bytesRead += len; } @@ -220,7 +221,7 @@ public override void Read(uint offset, uint length, IntPtr dest, ref uint bytesR if (offset < 24) { length = Math.Min(length, 24 - offset); - System.Runtime.InteropServices.Marshal.Copy(_header, (int)offset, dest, (int)length); + Util.CopyToIntPtr(_header, dest, (int)length, (int)offset); bytesRead = length; DebugLogger.WriteLine($"Chunked {Name} reading from cheader - {bytesRead} bytes read [current size is {BitConverter.ToInt32(_header, 20)}]"); return; @@ -230,7 +231,7 @@ public override void Read(uint offset, uint length, IntPtr dest, ref uint bytesR bytesRead = length; return; //leave with garbage... } int len = Math.Min((int)length, _calculated.Length - (int)offset); - System.Runtime.InteropServices.Marshal.Copy(_calculated, (int)offset, dest, len); + Util.CopyToIntPtr(_calculated, dest, len, (int)offset); bytesRead = (uint)len; } } @@ -287,7 +288,7 @@ public override void Read(uint offset, uint length, IntPtr dest, ref uint bytesR _lastInit = init; } if (_current != null && _handle.Equals(IntPtr.Zero) && _current.Archive == null) { - _handle = Wrap.CreateFileW(_current.File, System.IO.FileAccess.Read, System.IO.FileShare.Read, IntPtr.Zero, System.IO.FileMode.Open, System.IO.FileAttributes.Normal, IntPtr.Zero); + _handle = Win32.CreateFileW(_current.File, System.IO.FileAccess.Read, System.IO.FileShare.Read, IntPtr.Zero, System.IO.FileMode.Open, System.IO.FileAttributes.Normal, IntPtr.Zero); } _lastHeader = header; _access = DateTime.Now; @@ -306,14 +307,14 @@ public override void Read(uint offset, uint length, IntPtr dest, ref uint bytesR } else { if (offset < 24) { length = Math.Min(length, 24 - offset); - System.Runtime.InteropServices.Marshal.Copy(_header, (int)offset, dest, (int)length); + Util.CopyToIntPtr(_header, dest, (int)length, (int)offset); bytesRead = length; //DebugLogger.WriteLine("Conditional {2} reading from cheader - {0} bytes read [current size is {1}]", bytesRead, BitConverter.ToInt32(_header, 20), Name); return; } else { offset -= 24; if (_current.Archive == null) { - Wrap.SetFilePointer(_handle, (int)offset, IntPtr.Zero, Wrap.EMoveMethod.Begin); + Win32.SetFilePointer(_handle, (int)offset, IntPtr.Zero, Win32.EMoveMethod.Begin); Win32.ReadFile(_handle, dest, length, ref bytesRead, IntPtr.Zero); //DebugLogger.WriteLine("Conditional {1} reading from cfile - {0} bytes read", bytesRead, Name); } else { @@ -350,9 +351,9 @@ class VRangeFile : VRangeCleanup { private DateTime _access; public override void Read(uint offset, uint length, IntPtr dest, ref uint bytesRead) { - if (_handle == IntPtr.Zero) _handle = Wrap.CreateFileW(Filename, System.IO.FileAccess.Read, System.IO.FileShare.Read, IntPtr.Zero, System.IO.FileMode.Open, System.IO.FileAttributes.Normal, IntPtr.Zero); + if (_handle == IntPtr.Zero) _handle = Win32.CreateFileW(Filename, System.IO.FileAccess.Read, System.IO.FileShare.Read, IntPtr.Zero, System.IO.FileMode.Open, System.IO.FileAttributes.Normal, IntPtr.Zero); _access = DateTime.Now; - Wrap.SetFilePointer(_handle, (int)offset, IntPtr.Zero, Wrap.EMoveMethod.Begin); + Win32.SetFilePointer(_handle, (int)offset, IntPtr.Zero, Win32.EMoveMethod.Begin); Win32.ReadFile(_handle, dest, length, ref bytesRead, IntPtr.Zero); } @@ -583,15 +584,15 @@ public VArchiveData(byte[] data) { _position = 0; } - public int SetFilePointer(long offset, Wrap.EMoveMethod method) { + public int SetFilePointer(long offset, Win32.EMoveMethod method) { switch (method) { - case Wrap.EMoveMethod.Begin: + case Win32.EMoveMethod.Begin: _position = offset; break; - case Wrap.EMoveMethod.End: + case Win32.EMoveMethod.End: _position = _size + offset; break; - case Wrap.EMoveMethod.Current: + case Win32.EMoveMethod.Current: _position += offset; break; } @@ -599,11 +600,11 @@ public int SetFilePointer(long offset, Wrap.EMoveMethod method) { if (_position > _size) return -1; return (int)_position; } - public bool SetFilePointerEx(IntPtr hFile, long liDistanceToMove, IntPtr lpNewFilePointer, uint dwMoveMethod) { - SetFilePointer(liDistanceToMove, (Wrap.EMoveMethod)dwMoveMethod); + public ulong SetFilePointerEx(IntPtr hFile, long liDistanceToMove, IntPtr lpNewFilePointer, uint dwMoveMethod) { + SetFilePointer(liDistanceToMove, (Win32.EMoveMethod)dwMoveMethod); if (lpNewFilePointer != IntPtr.Zero) System.Runtime.InteropServices.Marshal.WriteInt64(lpNewFilePointer, _position); - return true; + return 1; } public unsafe int ReadFile(IntPtr bytes, uint numBytesToRead, ref uint numBytesRead) { numBytesRead = Math.Min(numBytesToRead, (uint)(_size - _position)); @@ -636,16 +637,16 @@ public VArchiveFile(IrosArc arc, string filename) { _size = arc.GetFileSize(filename); } - public int SetFilePointer(long offset, Wrap.EMoveMethod method) { + public int SetFilePointer(long offset, Win32.EMoveMethod method) { DebugLogger.WriteLine($"VArchive SetFilePointer on {_filename} to {offset} from {method}"); switch (method) { - case Wrap.EMoveMethod.Begin: + case Win32.EMoveMethod.Begin: _position = offset; break; - case Wrap.EMoveMethod.End: + case Win32.EMoveMethod.End: _position = _size + offset; break; - case Wrap.EMoveMethod.Current: + case Win32.EMoveMethod.Current: _position += offset; break; } @@ -656,7 +657,7 @@ public int SetFilePointer(long offset, Wrap.EMoveMethod method) { public bool SetFilePointerEx(IntPtr hFile, long liDistanceToMove, IntPtr lpNewFilePointer, uint dwMoveMethod) { DebugLogger.WriteLine("VArchive SetFilePointerEx"); - SetFilePointer(liDistanceToMove, (Wrap.EMoveMethod)dwMoveMethod); + SetFilePointer(liDistanceToMove, (Win32.EMoveMethod)dwMoveMethod); if (lpNewFilePointer != IntPtr.Zero) System.Runtime.InteropServices.Marshal.WriteInt64(lpNewFilePointer, _position); return true; diff --git a/7thWrapperLib/VStreamFile.cs b/7thWrapperLib/VStreamFile.cs index e9c89065..ec9d852e 100644 --- a/7thWrapperLib/VStreamFile.cs +++ b/7thWrapperLib/VStreamFile.cs @@ -3,6 +3,7 @@ The original developer is Iros */ +using Iros._7th; using System; using System.Collections.Generic; using System.Linq; @@ -17,15 +18,15 @@ public VStreamFile(byte[] source) { _source = source; } - public long SetPosition(long pos, Wrap.EMoveMethod type) { + public long SetPosition(long pos, Win32.EMoveMethod type) { switch (type) { - case Wrap.EMoveMethod.Begin: + case Win32.EMoveMethod.Begin: _position = pos; break; - case Wrap.EMoveMethod.Current: + case Win32.EMoveMethod.Current: _position += pos; break; - case Wrap.EMoveMethod.End: + case Win32.EMoveMethod.End: _position = _source.Length + pos; break; } @@ -38,7 +39,7 @@ public int Read(IntPtr buffer, uint toRead, ref uint read) { if (willRead == 0) { read = 0; return 1; } - System.Runtime.InteropServices.Marshal.Copy(_source, (int)_position, buffer, willRead); + Util.CopyToIntPtr(_source, buffer, willRead, (int)_position); _position += willRead; //DebugLogger.DetailedWriteLine("VStream reading {willRead} bytes, new position {_position}"); read = (uint)willRead; diff --git a/7thWrapperLib/Win32.cs b/7thWrapperLib/Win32.cs index 179c8053..79743aee 100644 --- a/7thWrapperLib/Win32.cs +++ b/7thWrapperLib/Win32.cs @@ -5,22 +5,43 @@ The original developer is Iros using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Runtime.InteropServices; using System.Text; namespace _7thWrapperLib { static class Win32 { - [DllImport("kernel32.dll", SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - internal static extern bool DuplicateHandle(IntPtr hSourceProcessHandle, - IntPtr hSourceHandle, IntPtr hTargetProcessHandle, out IntPtr lpTargetHandle, - uint dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, uint dwOptions); - [DllImport("kernel32.dll")] - internal static extern IntPtr GetCurrentProcess(); + public enum EMoveMethod : uint + { + Begin = 0, + Current = 1, + End = 2 + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe struct FILETIME + { + public uint dwLowDateTime; + public uint dwHighDateTime; + }; + [StructLayout(LayoutKind.Sequential)] + public unsafe struct BY_HANDLE_FILE_INFORMATION + { + public uint FileAttributes; + public FILETIME CreationTime; + public FILETIME LastAccessTime; + public FILETIME LastWriteTime; + public uint VolumeSerialNumber; + public uint FileSizeHigh; + public uint FileSizeLow; + public uint NumberOfLinks; + public uint FileIndexHigh; + public uint FileIndexLow; + } - internal struct OVERLAPPED { + public struct OVERLAPPED { public UIntPtr Internal; public UIntPtr InternalHigh; public uint Offset; @@ -28,25 +49,54 @@ internal struct OVERLAPPED { public IntPtr EventHandle; } + // ------------------------------------------------------------------------------------------------------------------------------------------- + + [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)] + internal delegate void ReadFileCompletionDelegate(int dwErrorCode, int dwNumBytesTransferred, ref OVERLAPPED lpOverlapped); + + // ------------------------------------------------------------------------------------------------------------------------------------------- + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + static internal extern bool CloseHandle(IntPtr hObject); + + [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] + public static extern IntPtr CreateFileW( + [MarshalAs(UnmanagedType.LPWStr)] string lpFileName, + [MarshalAs(UnmanagedType.U4)] FileAccess dwDesiredAccess, + [MarshalAs(UnmanagedType.U4)] FileShare dwShareMode, + IntPtr lpSecurityAttributes, + [MarshalAs(UnmanagedType.U4)] FileMode dwCreationDisposition, + [MarshalAs(UnmanagedType.U4)] FileAttributes dwFlagsAndAttributes, + IntPtr hTemplateFile); + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + internal static extern bool DuplicateHandle(IntPtr hSourceProcessHandle, + IntPtr hSourceHandle, IntPtr hTargetProcessHandle, out IntPtr lpTargetHandle, + uint dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, uint dwOptions); + + [DllImport("kernel32.dll")] + internal static extern IntPtr GetCurrentProcess(); + [DllImport("kernel32.dll", SetLastError = true)] static internal extern unsafe int ReadFile(IntPtr handle, IntPtr bytes, uint numBytesToRead, ref uint numBytesRead, IntPtr overlapped); + [DllImport("kernel32.dll", SetLastError = true)] static internal extern unsafe int ReadFile(IntPtr handle, IntPtr bytes, uint numBytesToRead, ref uint numBytesRead, ref OVERLAPPED overlapped); - [DllImport("kernel32.dll", SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - static internal extern bool WriteFile(IntPtr hFile, IntPtr lpBuffer, uint nNumberOfBytesToWrite, - out uint lpNumberOfBytesWritten, [In] ref System.Threading.NativeOverlapped lpOverlapped); - [DllImport("kernel32.dll", SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - static internal extern bool CloseHandle(IntPtr hObject); - [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)] - internal delegate void ReadFileCompletionDelegate(int dwErrorCode, int dwNumBytesTransferred, ref OVERLAPPED lpOverlapped); [DllImport("kernel32.dll")] static extern bool ReadFileEx(IntPtr hFile, [Out] byte[] lpBuffer, uint nNumberOfBytesToRead, [In] ref System.Threading.NativeOverlapped lpOverlapped, ReadFileCompletionDelegate lpCompletionRoutine); + [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] + static internal extern unsafe int SetFilePointer( + [In] IntPtr hFile, + [In] int lDistanceToMove, + [In] IntPtr lpDistanceToMoveHigh, + [In] EMoveMethod dwMoveMethod); + } } diff --git a/7thWrapperLib/Wrap.cs b/7thWrapperLib/Wrap.cs index bf848336..81995e1b 100644 --- a/7thWrapperLib/Wrap.cs +++ b/7thWrapperLib/Wrap.cs @@ -1,656 +1,703 @@ -/* - This source is subject to the Microsoft Public License. See LICENSE.TXT for details. - The original developer is Iros -*/ - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using EasyHook; -using System.Runtime.InteropServices; -using Microsoft.Win32.SafeHandles; -using System.IO; - -namespace _7thWrapperLib { - public class Wrap : IEntryPoint { - [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] - public static extern IntPtr CreateFileW( - [MarshalAs(UnmanagedType.LPWStr)] string lpFileName, - [MarshalAs(UnmanagedType.U4)] FileAccess dwDesiredAccess, - [MarshalAs(UnmanagedType.U4)] FileShare dwShareMode, - IntPtr lpSecurityAttributes, - [MarshalAs(UnmanagedType.U4)] FileMode dwCreationDisposition, - [MarshalAs(UnmanagedType.U4)] FileAttributes dwFlagsAndAttributes, - IntPtr hTemplateFile); - - [DllImport("kernel32.dll", CharSet = CharSet.Unicode)] - public static extern IntPtr FindFirstFileW(string lpFileName, out WIN32_FIND_DATA lpFindFileData); - - [DllImport("kernel32.dll", CharSet = CharSet.Ansi)] - public static extern IntPtr FindFirstFileA(string lpFileName, out WIN32_FIND_DATA lpFindFileData); - - [DllImport("kernel32.dll", CharSet = CharSet.Ansi)] - public static extern IntPtr CreateEventA(IntPtr lpEventAttributes, bool bManualReset, bool bInitialState, IntPtr lpname); - - [DllImport("kernel32.dll", CharSet = CharSet.Ansi)] - public static extern int GetFileType(IntPtr hFile); - - [DllImport("kernel32.dll", CharSet=CharSet.Unicode)] - internal static extern bool CreateProcessW(string lpApplicationName, - string lpCommandLine, ref SECURITY_ATTRIBUTES lpProcessAttributes, - ref SECURITY_ATTRIBUTES lpThreadAttributes, bool bInheritHandles, - uint dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory, - [In] ref STARTUPINFO lpStartupInfo, - out PROCESS_INFORMATION lpProcessInformation); - - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] - internal struct STARTUPINFO { - public Int32 cb; - public string lpReserved; - public string lpDesktop; - public string lpTitle; - public Int32 dwX; - public Int32 dwY; - public Int32 dwXSize; - public Int32 dwYSize; - public Int32 dwXCountChars; - public Int32 dwYCountChars; - public Int32 dwFillAttribute; - public Int32 dwFlags; - public Int16 wShowWindow; - public Int16 cbReserved2; - public IntPtr lpReserved2; - public IntPtr hStdInput; - public IntPtr hStdOutput; - public IntPtr hStdError; - } - [StructLayout(LayoutKind.Sequential)] - internal struct PROCESS_INFORMATION { - public IntPtr hProcess; - public IntPtr hThread; - public int dwProcessId; - public int dwThreadId; - } - [StructLayout(LayoutKind.Sequential)] - public struct SECURITY_ATTRIBUTES { - public int nLength; - public IntPtr lpSecurityDescriptor; - public int bInheritHandle; - } - - [StructLayout(LayoutKind.Sequential)] - public struct FILETIME { - public uint dwLowDateTime; - public uint dwHighDateTime; - }; - - public const int MAX_PATH = 260; - public const int MAX_ALTERNATE = 14; - - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] - public struct WIN32_FIND_DATA { - public FileAttributes dwFileAttributes; - public FILETIME ftCreationTime; - public FILETIME ftLastAccessTime; - public FILETIME ftLastWriteTime; - public uint nFileSizeHigh; //changed all to uint, otherwise you run into unexpected overflow - public uint nFileSizeLow; //| - public uint dwReserved0; //| - public uint dwReserved1; //v - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAX_PATH)] - public string cFileName; - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAX_ALTERNATE)] - public string cAlternate; - } - - [StructLayout(LayoutKind.Sequential)] - struct BY_HANDLE_FILE_INFORMATION { - public uint FileAttributes; - public FILETIME CreationTime; - public FILETIME LastAccessTime; - public FILETIME LastWriteTime; - public uint VolumeSerialNumber; - public uint FileSizeHigh; - public uint FileSizeLow; - public uint NumberOfLinks; - public uint FileIndexHigh; - public uint FileIndexLow; - } - [DllImport("kernel32.dll", SetLastError = true)] - static extern bool GetFileInformationByHandle( - IntPtr hFile, - out BY_HANDLE_FILE_INFORMATION lpFileInformation - ); - - - public enum EMoveMethod : uint { - Begin = 0, - Current = 1, - End = 2 - } - - [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] - static internal extern unsafe int SetFilePointer( - [In] IntPtr hFile, - [In] int lDistanceToMove, - [In] IntPtr lpDistanceToMoveHigh, - [In] EMoveMethod dwMoveMethod); - - [DllImport("kernel32.dll")] - static extern bool SetFilePointerEx(IntPtr hFile, long liDistanceToMove, IntPtr lpNewFilePointer, uint dwMoveMethod); - - [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)] - private delegate IntPtr DCreateFile( - [MarshalAs(UnmanagedType.LPWStr)] string filename, - [MarshalAs(UnmanagedType.U4)] FileAccess access, - [MarshalAs(UnmanagedType.U4)] FileShare share, - IntPtr securityAttributes, - [MarshalAs(UnmanagedType.U4)] FileMode creationDisposition, - [MarshalAs(UnmanagedType.U4)] FileAttributes flagsAndAttributes, - IntPtr templateFile); - - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - private delegate int DReadFile(IntPtr handle, IntPtr bytes, uint numBytesToRead, ref uint numBytesRead, IntPtr overlapped); - - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - [return: MarshalAs(UnmanagedType.Bool)] - private delegate bool DWriteFile(IntPtr hFile, IntPtr lpBuffer, uint nNumberOfBytesToWrite, - out uint lpNumberOfBytesWritten, [In] ref System.Threading.NativeOverlapped lpOverlapped); - - - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - [return: MarshalAs(UnmanagedType.Bool)] - private delegate bool DCloseHandle(IntPtr hObject); - - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - private delegate int DGetFileType(IntPtr hFile); - - [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)] - private delegate IntPtr DFindFirstFileW(string lpFileName, out WIN32_FIND_DATA lpFindFileData); - - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - private delegate int DSetFilePointer(IntPtr handle, int lDistanceTomove, IntPtr lpDistanceToMoveHigh, EMoveMethod dwMoveMethod); - - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - private delegate bool DSetFilePointerEx(IntPtr hFile, long liDistanceToMove, IntPtr lpNewFilePointer, uint dwMoveMethod); - - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - private delegate bool DDuplicateHandle(IntPtr hSourceProcessHandle, - IntPtr hSourceHandle, IntPtr hTargetProcessHandle, out IntPtr lpTargetHandle, - uint dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, uint dwOptions); - - [DllImport("kernel32.dll")] - static extern uint GetFileSize(IntPtr hFile, IntPtr lpFileSizeHigh); - - [DllImport("kernel32.dll")] - static extern bool GetFileSizeEx(IntPtr hFile, ref long lpFileSize); - - [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)] - private delegate bool DCreateProcessW(string lpApplicationName, - string lpCommandLine, ref SECURITY_ATTRIBUTES lpProcessAttributes, - ref SECURITY_ATTRIBUTES lpThreadAttributes, bool bInheritHandles, - uint dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory, - [In] ref STARTUPINFO lpStartupInfo, - out PROCESS_INFORMATION lpProcessInformation); - - [DllImport("kernel32", SetLastError = true, CharSet = CharSet.Auto)] - static extern IntPtr LoadLibrary(string lpFileName); - - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - private delegate bool DGetFileInformationByHandle(IntPtr hFile, out BY_HANDLE_FILE_INFORMATION lpFileInformation); - - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - private delegate bool DReadFileEx(IntPtr hFile, [Out] byte[] lpBuffer, - uint nNumberOfBytesToRead, [In] ref System.Threading.NativeOverlapped lpOverlapped, - Win32.ReadFileCompletionDelegate lpCompletionRoutine); - - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - private delegate uint DGetFileSize(IntPtr hFile, IntPtr lpFileSizeHigh); - - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - private delegate bool DGetFileSizeEx(IntPtr hFile, ref long lpFileSize); - - private Dictionary _hMap = new Dictionary(); - private Dictionary _hNames = new Dictionary(); - private Dictionary _streamFiles = new Dictionary(); - private Dictionary _saveFiles = new Dictionary(); - private RuntimeProfile _profile; - - LocalHook _hCreateFileW, _hReadFile, _hFindFirstFile, _hSetFilePointer, _hCloseHandle, - _hGetFileType, _hCreateProcessW, _hGetFileInformationByHandle, _hDuplicateHandle, - _hGetFileSize, _hGetFileSizeEx, _hSetFilePointerEx, _hWriteFile; - - public Wrap(RemoteHooking.IContext context, RuntimeParams parms) { - DebugLogger.WriteLine("Wrap created"); - } - - public void Run(RemoteHooking.IContext context, RuntimeParams parms) { - System.Threading.Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo("en-US", false); - try { - - RuntimeProfile profile; - using (var fs = new FileStream(parms.ProfileFile, FileMode.Open)) +/* + This source is subject to the Microsoft Public License. See LICENSE.TXT for details. + The original developer is Iros + Heavy rework was done by Julian Xhokaxhiu + Additional help and support on .NET internals + low level wiring by Benjamin Moir +*/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Runtime.InteropServices; +using Microsoft.Win32.SafeHandles; +using System.IO; +using System.Diagnostics; + +namespace _7thWrapperLib { + public static unsafe class Wrap { + private static Dictionary _hMap = new Dictionary(); + private static Dictionary _hNames = new Dictionary(); + private static Dictionary _streamFiles = new Dictionary(); + //private static Dictionary _saveFiles = new Dictionary(); + private static Dictionary _varchives = new Dictionary(); + private static RuntimeProfile _profile; + + [StructLayout(LayoutKind.Sequential)] + public unsafe struct HostExports + { + public delegate* unmanaged DetourTransactionBegin; + public delegate* unmanaged DetourAttach; + public delegate* unmanaged DetourDetach; + public delegate* unmanaged DetourTransactionCommit; + } + private static unsafe HostExports* _exports; + + [StructLayout(LayoutKind.Sequential)] + struct Methods + { + public delegate* unmanaged CreateFileW; + public delegate* unmanaged ReadFile; + //public delegate* unmanaged WriteFile; + public delegate* unmanaged FindFirstFileW; + public delegate* unmanaged SetFilePointer; + public delegate* unmanaged SetFilePointerEx; + public delegate* unmanaged CloseHandle; + public delegate* unmanaged GetFileType; + public delegate* unmanaged GetFileInformationByHandle; + public delegate* unmanaged DuplicateHandle; + //public delegate* unmanaged CreateProcessW; + public delegate* unmanaged GetFileSize; + public delegate* unmanaged GetFileSizeEx; + } + + static int s_MainThreadId; + static Methods s_Trampolines; + static Methods s_Detours; + + private static void MonitorThread(object rpo) + { + RuntimeProfile rp = (RuntimeProfile)rpo; + var accessors = rp.MonitorVars + .Select(t => new { Name = t.Item1, Data = t.Item2.Split(':') }) + .Select(a => new { Type = (VarType)Enum.Parse(typeof(VarType), a.Data[0]), Addr = new IntPtr(RuntimeVar.Parse(a.Data[1])), Name = a.Name, Mask = a.Data.Length < 3 ? -1 : (int)RuntimeVar.Parse(a.Data[2]) }) + .Where(a => (int)a.Type <= 2) + .ToList(); + int[] values = accessors.Select(_ => 247834893).ToArray(); + + do + { + DebugLogger.WriteLine("MONITOR:"); + for (int i = 0; i < accessors.Count; i++) + { + int value; + switch (accessors[i].Type) + { + case VarType.Int: + value = System.Runtime.InteropServices.Marshal.ReadInt32(accessors[i].Addr); + break; + case VarType.Short: + value = System.Runtime.InteropServices.Marshal.ReadInt16(accessors[i].Addr); + break; + case VarType.Byte: + value = System.Runtime.InteropServices.Marshal.ReadByte(accessors[i].Addr); + break; + default: + continue; + } + value = value & accessors[i].Mask; + if (value != values[i]) + { + values[i] = value; + DebugLogger.WriteLine($" {accessors[i].Name} = {value}"); + } + } + } while (true); + } + + public unsafe static void Run(IntPtr exports, Process currentProcess, string profileFile = ".7thWrapperProfile") + { + System.Threading.Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo("en-US", false); + try { + _exports = (HostExports*)exports; + + RuntimeProfile profile; + using (var fs = new FileStream(profileFile, FileMode.Open)) { - profile = Iros._7th.Util.DeserializeBinary(fs); - } - - File.Delete(parms.ProfileFile); - - if (!String.IsNullOrWhiteSpace(profile.LogFile)) { - try { - try { File.Delete(profile.LogFile); } catch { } // ensure old log is deleted since new run - + profile = Iros._7th.Util.DeserializeBinary(fs); + } + + File.Delete(profileFile); + + if (!String.IsNullOrWhiteSpace(profile.LogFile)) { + try { + try { File.Delete(profile.LogFile); } catch { } // ensure old log is deleted since new run + DebugLogger.Init(profile.LogFile); - DebugLogger.IsDetailedLogging = profile.Options.HasFlag(RuntimeOptions.DetailedLog); - - DebugLogger.WriteLine("Logging debug output to " + profile.LogFile); - } catch (Exception ex) { - DebugLogger.WriteLine("Failed to log debug output: " + ex.ToString()); - } - } - - DebugLogger.WriteLine($"Wrap run... Host: {context.HostPID} PID: {RemoteHooking.GetCurrentProcessId()} TID: {RemoteHooking.GetCurrentThreadId()} Path: {profile.ModPath} Capture: {String.Join(", ", profile.MonitorPaths)}"); - _profile = profile; - for (int i = _profile.MonitorPaths.Count - 1; i >= 0; i--) { - if (!_profile.MonitorPaths[i].EndsWith(System.IO.Path.DirectorySeparatorChar.ToString())) - _profile.MonitorPaths[i] += System.IO.Path.DirectorySeparatorChar; - if (String.IsNullOrWhiteSpace(_profile.MonitorPaths[i])) _profile.MonitorPaths.RemoveAt(i); - } - - foreach (var item in profile.Mods) { - DebugLogger.WriteLine($" Mod: {item.BaseFolder} has {item.Conditionals.Count} conditionals"); - DebugLogger.WriteLine(" Additional paths: " + String.Join(", ", item.ExtraFolders)); - item.Startup(); - } - - _hCreateFileW = LocalHook.Create(LocalHook.GetProcAddress("kernel32.dll", "CreateFileW"), new DCreateFile(HCreateFileW), this); - _hCreateFileW.ThreadACL.SetExclusiveACL(new[] { 0 }); - - _hReadFile = LocalHook.Create(LocalHook.GetProcAddress("kernel32.dll", "ReadFile"), new DReadFile(HReadFile), this); - _hReadFile.ThreadACL.SetExclusiveACL(new[] { 0 }); - - _hWriteFile = LocalHook.Create(LocalHook.GetProcAddress("kernel32.dll", "WriteFile"), new DWriteFile(HWriteFile), this); - _hWriteFile.ThreadACL.SetExclusiveACL(new[] { 0 }); - - _hFindFirstFile = LocalHook.Create(LocalHook.GetProcAddress("kernel32.dll", "FindFirstFileW"), new DFindFirstFileW(HFindFirstFile), this); - _hFindFirstFile.ThreadACL.SetExclusiveACL(new[] { 0 }); - - _hSetFilePointer = LocalHook.Create(LocalHook.GetProcAddress("kernel32.dll", "SetFilePointer"), new DSetFilePointer(HSetFilePointer), this); - _hSetFilePointer.ThreadACL.SetExclusiveACL(new[] { 0 }); - - _hSetFilePointerEx = LocalHook.Create(LocalHook.GetProcAddress("kernel32.dll", "SetFilePointerEx"), new DSetFilePointerEx(HSetFilePointerEx), this); - _hSetFilePointerEx.ThreadACL.SetExclusiveACL(new[] { 0 }); - - _hCloseHandle = LocalHook.Create(LocalHook.GetProcAddress("kernel32.dll", "CloseHandle"), new DCloseHandle(HCloseHandle), this); - _hCloseHandle.ThreadACL.SetExclusiveACL(new[] { 0 }); - - _hGetFileType = LocalHook.Create(LocalHook.GetProcAddress("kernel32.dll", "GetFileType"), new DGetFileType(HGetFileType), this); - _hGetFileType.ThreadACL.SetExclusiveACL(new[] { 0 }); - - _hGetFileInformationByHandle = LocalHook.Create(LocalHook.GetProcAddress("kernel32.dll", "GetFileInformationByHandle"), new DGetFileInformationByHandle(HGetFileInformationByHandle), this); - _hGetFileInformationByHandle.ThreadACL.SetExclusiveACL(new[] { 0 }); - - _hDuplicateHandle = LocalHook.Create(LocalHook.GetProcAddress("kernel32.dll", "DuplicateHandle"), new DDuplicateHandle(HDuplicateHandle), this); - _hDuplicateHandle.ThreadACL.SetExclusiveACL(new[] { 0 }); - - _hCreateProcessW = LocalHook.Create(LocalHook.GetProcAddress("kernel32.dll", "CreateProcessW"), new DCreateProcessW(HCreateProcessW), this); - _hCreateProcessW.ThreadACL.SetExclusiveACL(new[] { 0 }); - - _hGetFileSize = LocalHook.Create(LocalHook.GetProcAddress("kernel32.dll", "GetFileSize"), new DGetFileSize(HGetFileSize), this); - _hGetFileSize.ThreadACL.SetExclusiveACL(new[] { 0 }); - - _hGetFileSizeEx = LocalHook.Create(LocalHook.GetProcAddress("kernel32.dll", "GetFileSizeEx"), new DGetFileSizeEx(HGetFileSizeEx), this); - _hGetFileSizeEx.ThreadACL.SetExclusiveACL(new[] { 0 }); - - if (profile.MonitorVars != null) - new System.Threading.Thread(MonitorThread) { IsBackground = true }.Start(profile); - - RemoteHooking.WakeUpProcess(); - - System.Threading.Thread.Sleep(1000); - foreach (string LL in profile.Mods.SelectMany(m => m.GetLoadLibraries())) { - DebugLogger.WriteLine($"Loading library DLL {LL}"); - LoadLibrary(LL); - } - foreach (var mod in profile.Mods) { - foreach (string LA in mod.GetLoadAssemblies()) { - DebugLogger.WriteLine($"Loading assembly DLL {LA}"); - var asm = System.Reflection.Assembly.LoadFrom(LA); - try { - string path = mod.BaseFolder; - asm.GetType("_7thHeaven.Main") - .GetMethod("Init", new[] { typeof(RuntimeMod) }) - .Invoke(null, new object[] { mod }); - } catch { } - } - } - - foreach (var mod in profile.Mods.AsEnumerable().Reverse()) { - foreach (string file in mod.GetPathOverrideNames("hext")) { - foreach (var of in mod.GetOverrides("hext\\" + file)) { - System.IO.Stream s; - if (of.Archive == null) { - s = new System.IO.FileStream(of.File, FileMode.Open, FileAccess.Read); - } else { - s = of.Archive.GetData(of.File); - } - DebugLogger.WriteLine($"Applying hext patch {file} from mod {mod.BaseFolder}"); - try { - HexPatch.Apply(s); - } catch (Exception ex) { - DebugLogger.WriteLine("Error applying patch: " + ex.Message); - } - } - } - } - } catch (Exception e) { - DebugLogger.WriteLine(e.ToString()); - return; - } - while (true) { - System.Threading.Thread.Sleep(500); - } - } - - private void MonitorThread(object rpo) { - RuntimeProfile rp = (RuntimeProfile)rpo; - var accessors = rp.MonitorVars - .Select(t => new { Name = t.Item1, Data = t.Item2.Split(':') }) - .Select(a => new { Type = (VarType)Enum.Parse(typeof(VarType), a.Data[0]), Addr = new IntPtr(RuntimeVar.Parse(a.Data[1])), Name = a.Name, Mask = a.Data.Length < 3 ? -1 : (int)RuntimeVar.Parse(a.Data[2]) }) - .Where(a => (int)a.Type <= 2) - .ToList(); - int[] values = accessors.Select(_ => 247834893).ToArray(); - - do { - System.Threading.Thread.Sleep(5000); - DebugLogger.WriteLine("MONITOR:"); - for (int i = 0; i < accessors.Count; i++) { - int value; - switch (accessors[i].Type) { - case VarType.Int: - value = System.Runtime.InteropServices.Marshal.ReadInt32(accessors[i].Addr); - break; - case VarType.Short: - value = System.Runtime.InteropServices.Marshal.ReadInt16(accessors[i].Addr); - break; - case VarType.Byte: - value = System.Runtime.InteropServices.Marshal.ReadByte(accessors[i].Addr); - break; - default: - continue; - } - value = value & accessors[i].Mask; - if (value != values[i]) { - values[i] = value; - DebugLogger.WriteLine($" {accessors[i].Name} = {value}"); - } - } - } while (true); - } - - private Dictionary _varchives = new Dictionary(); - - private bool HCloseHandle(IntPtr hObject) { - VArchiveData va; - - if (_varchives.TryGetValue(hObject, out va)) { - _varchives.Remove(hObject); - DebugLogger.WriteLine($"Closing dummy handle {hObject}"); - } - - if (_streamFiles.ContainsKey(hObject)) - _streamFiles.Remove(hObject); - - if (_saveFiles.ContainsKey(hObject)) - _saveFiles.Remove(hObject); - - return Win32.CloseHandle(hObject); - } - - private int HGetFileType(IntPtr hFile) { - DebugLogger.DetailedWriteLine($"GetFileType on {hFile}"); - VArchiveData va; - if (_varchives.TryGetValue(hFile, out va)) { - //DebugLogger.WriteLine(" ---faking dummy file"); - return 1; - } else - return GetFileType(hFile); - } - - private int HSetFilePointer(IntPtr handle, int lDistanceTomove, IntPtr lpDistanceToMoveHigh, EMoveMethod dwMoveMethod) { - //DebugLogger.WriteLine("SetFilePointer on {0} to {1} by {2}", handle, lDistanceTomove, dwMoveMethod); - VArchiveData va; - VStreamFile vsf; - long offset = lDistanceTomove; - if (!lpDistanceToMoveHigh.Equals(IntPtr.Zero)) - offset |= ((long)Marshal.ReadInt32(lpDistanceToMoveHigh) << 32); - if (_varchives.TryGetValue(handle, out va)) { - return va.SetFilePointer(offset, dwMoveMethod); - } else if (_streamFiles.TryGetValue(handle, out vsf)) { - return (int)_streamFiles[handle].SetPosition(offset, dwMoveMethod); - } else { - return SetFilePointer(handle, lDistanceTomove, lpDistanceToMoveHigh, dwMoveMethod); - } - } - - private bool HWriteFile(IntPtr hFile, IntPtr lpBuffer, uint nNumberOfBytesToWrite, - out uint lpNumberOfBytesWritten, [In] ref System.Threading.NativeOverlapped lpOverlapped) { - - - bool result = Win32.WriteFile(hFile, lpBuffer, nNumberOfBytesToWrite, out lpNumberOfBytesWritten, ref lpOverlapped); - //DebugLogger.WriteLine(String.Format("Write {0} bytes on {1}", lpNumberOfBytesWritten, hFile.ToInt32())); - - if (_saveFiles.ContainsKey(hFile)) { - int offset = SetFilePointer(hFile, 0, IntPtr.Zero, EMoveMethod.Current); - //DebugLogger.WriteLine(String.Format("Write {0} bytes to {1} at offset {2}", lpNumberOfBytesWritten, _saveFiles[hFile], offset)); - } - - return result; - } - - private int HReadFile(IntPtr handle, IntPtr bytes, uint numBytesToRead, ref uint numBytesRead, IntPtr overlapped) { - VArchiveData va; - if (_varchives.TryGetValue(handle, out va)) { - return va.ReadFile(bytes, numBytesToRead, ref numBytesRead); - } - VStreamFile vsf; - if (_streamFiles.TryGetValue(handle, out vsf)) { - return vsf.Read(bytes, numBytesRead, ref numBytesRead); - } - - //DebugLogger.WriteLine("Hooked ReadFile on {0} for {1} bytes", handle.ToInt32(), numBytesToRead); - //if (overlapped != IntPtr.Zero) DebugLogger.WriteLine("(is overlapped)"); - - LGPWrapper lgp; - if (_hMap.TryGetValue(handle, out lgp)) { - try { - int pos = SetFilePointer(handle, 0, IntPtr.Zero, EMoveMethod.Current); - //DebugLogger.WriteLine("Hooked ReadFile on {0} for {1} bytes at {2}", handle.ToInt32(), numBytesToRead, pos); - lgp.VFile.Read((uint)pos, numBytesToRead, bytes, ref numBytesRead); - //DebugLogger.WriteLine("--{0} bytes read", numBytesRead); - SetFilePointer(handle, (int)(pos + numBytesRead), IntPtr.Zero, EMoveMethod.Begin); - lgp.Ping(); - return -1; - } catch (Exception e) { - DebugLogger.WriteLine("ERROR: " + e.ToString()); - throw; - } - } - - return Win32.ReadFile(handle, bytes, numBytesToRead, ref numBytesRead, overlapped); - } - - private IntPtr CreateVA(OverrideFile of) { - VArchiveData va = new VArchiveData(of.Archive.GetBytes(of.File)); - IntPtr dummy = of.Archive.GetDummyHandle(); - DebugLogger.WriteLine($"Creating dummy file handle {dummy} to access {of.Archive}{of.File}"); - _varchives[dummy] = va; - return dummy; - } - - private IntPtr HCreateFileW( - [MarshalAs(UnmanagedType.LPWStr)] string lpFileName, - [MarshalAs(UnmanagedType.U4)] FileAccess dwDesiredAccess, - [MarshalAs(UnmanagedType.U4)] FileShare dwShareMode, - IntPtr lpSecurityAttributes, - [MarshalAs(UnmanagedType.U4)] FileMode dwCreationDisposition, - [MarshalAs(UnmanagedType.U4)] FileAttributes dwFlagsAndAttributes, - IntPtr hTemplateFile) { - - // Usually this check should be enough... - bool isFF7GameFile = lpFileName.StartsWith(_profile.FF7Path, StringComparison.InvariantCultureIgnoreCase); - // ...but if it fails, last resort is to check if the file exists in the game directory - if (!isFF7GameFile && !lpFileName.StartsWith("\\", StringComparison.InvariantCultureIgnoreCase) && !Path.IsPathRooted(lpFileName)) + DebugLogger.IsDetailedLogging = profile.Options.HasFlag(RuntimeOptions.DetailedLog); + + DebugLogger.WriteLine("Logging debug output to " + profile.LogFile); + } catch (Exception ex) { + DebugLogger.WriteLine("Failed to log debug output: " + ex.ToString()); + } + } + + DebugLogger.WriteLine($"Wrap run... PName: {currentProcess.ProcessName} PID: {currentProcess.Id} Path: {profile.ModPath} Capture: {String.Join(", ", profile.MonitorPaths)}"); + _profile = profile; + for (int i = _profile.MonitorPaths.Count - 1; i >= 0; i--) { + if (!_profile.MonitorPaths[i].EndsWith(System.IO.Path.DirectorySeparatorChar.ToString())) + _profile.MonitorPaths[i] += System.IO.Path.DirectorySeparatorChar; + if (String.IsNullOrWhiteSpace(_profile.MonitorPaths[i])) _profile.MonitorPaths.RemoveAt(i); + } + + foreach (var item in profile.Mods) { + DebugLogger.WriteLine($" Mod: {item.BaseFolder} has {item.Conditionals.Count} conditionals"); + DebugLogger.WriteLine(" Additional paths: " + String.Join(", ", item.ExtraFolders)); + item.Startup(); + } + + try + { + DetourTransaction.Initialize(_exports); + InitializeHooks(Environment.CurrentManagedThreadId); + } + catch (Exception ex) + { + DebugLogger.WriteLine(ex.ToString()); + } + + if (profile.MonitorVars != null) + new System.Threading.Thread(MonitorThread) { IsBackground = true }.Start(profile); + + foreach (string LL in profile.Mods.SelectMany(m => m.GetLoadLibraries())) { + DebugLogger.WriteLine($"Loading library DLL {LL}"); + NativeLibrary.Load(LL); + } + + foreach (var mod in profile.Mods) { + foreach (string LA in mod.GetLoadAssemblies()) { + DebugLogger.WriteLine($"Loading assembly DLL {LA}"); + var asm = System.Reflection.Assembly.LoadFrom(LA); + try { + string path = mod.BaseFolder; + asm.GetType("_7thHeaven.Main") + .GetMethod("Init", new[] { typeof(RuntimeMod) }) + .Invoke(null, new object[] { mod }); + } catch { } + } + } + + foreach (var mod in profile.Mods.AsEnumerable().Reverse()) { + foreach (string file in mod.GetPathOverrideNames("hext")) { + foreach (var of in mod.GetOverrides("hext\\" + file)) { + System.IO.Stream s; + if (of.Archive == null) { + s = new System.IO.FileStream(of.File, FileMode.Open, FileAccess.Read); + } else { + s = of.Archive.GetData(of.File); + } + DebugLogger.WriteLine($"Applying hext patch {file} from mod {mod.BaseFolder}"); + try { + HexPatch.Apply(s); + } catch (Exception ex) { + DebugLogger.WriteLine("Error applying patch: " + ex.Message); + } + } + } + } + } catch (Exception e) { + DebugLogger.WriteLine(e.ToString()); + return; + } + } + + public static void InitializeHooks(int mainThreadId) + { + s_MainThreadId = mainThreadId; + s_Detours = new() + { + CreateFileW = &HCreateFileW, + ReadFile = &HReadFile, + //WriteFile = &HWriteFile, + FindFirstFileW = &HFindFirstFileW, + SetFilePointer = &HSetFilePointer, + SetFilePointerEx = &HSetFilePointerEx, + CloseHandle = &HCloseHandle, + GetFileType = &HGetFileType, + GetFileInformationByHandle = &HGetFileInformationByHandle, + DuplicateHandle = &HDuplicateHandle, + //CreateProcessW = &HCreateProcessW, + GetFileSize = &HGetFileSize, + GetFileSizeEx = &HGetFileSizeEx, + }; + + // Ensure console is initialized before we hook WinAPI. + _ = Console.In; + _ = Console.Out; + _ = Console.Error; + + fixed (Methods* targets = &s_Trampolines) { - isFF7GameFile = _profile.gameFiles.Any(s => s.EndsWith(lpFileName, StringComparison.InvariantCultureIgnoreCase)); - } - - // If a game file is found, process with replacing its content with relative mod file - if (isFF7GameFile) + var kernel32 = NativeLibrary.Load("kernel32"); + using var transaction = new DetourTransaction(); + + // Locate addresses + *(void**)&targets->CreateFileW = (void*)NativeLibrary.GetExport(kernel32, "CreateFileW"); + *(void**)&targets->ReadFile = (void*)NativeLibrary.GetExport(kernel32, "ReadFile"); + //*(void**)&targets->WriteFile = (void*)NativeLibrary.GetExport(kernel32, "WriteFile"); + *(void**)&targets->FindFirstFileW = (void*)NativeLibrary.GetExport(kernel32, "FindFirstFileW"); + *(void**)&targets->SetFilePointer = (void*)NativeLibrary.GetExport(kernel32, "SetFilePointer"); + *(void**)&targets->SetFilePointerEx = (void*)NativeLibrary.GetExport(kernel32, "SetFilePointerEx"); + *(void**)&targets->CloseHandle = (void*)NativeLibrary.GetExport(kernel32, "CloseHandle"); + *(void**)&targets->GetFileType = (void*)NativeLibrary.GetExport(kernel32, "GetFileType"); + *(void**)&targets->GetFileInformationByHandle = (void*)NativeLibrary.GetExport(kernel32, "GetFileInformationByHandle"); + *(void**)&targets->DuplicateHandle = (void*)NativeLibrary.GetExport(kernel32, "DuplicateHandle"); + //*(void**)&targets->CreateProcessW = (void*)NativeLibrary.GetExport(kernel32, "CreateProcessW"); + *(void**)&targets->GetFileSize = (void*)NativeLibrary.GetExport(kernel32, "GetFileSize"); + *(void**)&targets->GetFileSizeEx = (void*)NativeLibrary.GetExport(kernel32, "GetFileSizeEx"); + + // Attach detours + transaction.Attach((void**)&targets->CreateFileW, s_Detours.CreateFileW); + transaction.Attach((void**)&targets->ReadFile, s_Detours.ReadFile); + //transaction.Attach((void**)&targets->WriteFile, s_Detours.WriteFile); + transaction.Attach((void**)&targets->FindFirstFileW, s_Detours.FindFirstFileW); + transaction.Attach((void**)&targets->SetFilePointer, s_Detours.SetFilePointer); + transaction.Attach((void**)&targets->SetFilePointerEx, s_Detours.SetFilePointerEx); + transaction.Attach((void**)&targets->CloseHandle, s_Detours.CloseHandle); + transaction.Attach((void**)&targets->GetFileType, s_Detours.GetFileType); + transaction.Attach((void**)&targets->GetFileInformationByHandle, s_Detours.GetFileInformationByHandle); + transaction.Attach((void**)&targets->DuplicateHandle, s_Detours.DuplicateHandle); + //transaction.Attach((void**)&targets->CreateProcessW, s_Detours.CreateProcessW); + transaction.Attach((void**)&targets->GetFileSize, s_Detours.GetFileSize); + transaction.Attach((void**)&targets->GetFileSizeEx, s_Detours.GetFileSizeEx); + } + } + + // ------------------------------------------------------------------------------------------------------ + [UnmanagedCallersOnly] + static int HCloseHandle(void* hObject) + { + int ret = 0; + + if (Environment.CurrentManagedThreadId != s_MainThreadId) + return s_Trampolines.CloseHandle(hObject); + + try { - lpFileName = lpFileName.Replace("\\/", "\\").Replace("/", "\\").Replace("\\\\", "\\"); - DebugLogger.DetailedWriteLine($"CreateFileW for {lpFileName}..."); - if (lpFileName.IndexOf('\\') < 0) - { - //DebugLogger.WriteLine("No path: curdir is {0}", System.IO.Directory.GetCurrentDirectory(), 0); - lpFileName = Path.Combine(Directory.GetCurrentDirectory(), lpFileName); - } - - foreach (string path in _profile.MonitorPaths) + VArchiveData va; + IntPtr _hObject = new IntPtr(hObject); + + if (_varchives.TryGetValue(_hObject, out va)) { - if (lpFileName.StartsWith(path, StringComparison.InvariantCultureIgnoreCase)) - { - string match = lpFileName.Substring(path.Length); - OverrideFile mapped = LGPWrapper.MapFile(match, _profile); + _varchives.Remove(_hObject); + DebugLogger.WriteLine($"Closing dummy handle {_hObject}"); + } - //DebugLogger.WriteLine($"Attempting match '{match}' for {lpFileName}..."); + if (_streamFiles.ContainsKey(_hObject)) + _streamFiles.Remove(_hObject); - if (mapped == null) - { - // Attempt a second round, this time relaxing the path match replacing only the game folder path. - match = lpFileName.Substring(_profile.FF7Path.Length + 1); - mapped = LGPWrapper.MapFile(match, _profile); + //if (_saveFiles.ContainsKey(_hObject)) + // _saveFiles.Remove(_hObject); - //DebugLogger.WriteLine($"Attempting match '{match}' for {lpFileName}..."); - } + ret = s_Trampolines.CloseHandle(hObject); + } + catch (Exception ex) + { + Debug.WriteLine(ex.ToString()); + } + + return ret; + } + + [UnmanagedCallersOnly] + static ulong HGetFileType(void* hFile) + { + ulong ret = 0; + + try + { + IntPtr _hFile = new IntPtr(hFile); + DebugLogger.DetailedWriteLine($"GetFileType on {_hFile}"); + VArchiveData va; + if (_varchives.TryGetValue(_hFile, out va)) + { + //DebugLogger.WriteLine(" ---faking dummy file"); + ret = 1; + } + else + { + ret = s_Trampolines.GetFileType(hFile); + } + } + catch (Exception ex) + { + Debug.WriteLine(ex.ToString()); + } + + return ret; + } + + [UnmanagedCallersOnly] + static ulong HSetFilePointer(void* handle, long lDistanceTomove, long* lpDistanceToMoveHigh, ulong dwMoveMethod) + { + //DebugLogger.WriteLine("SetFilePointer on {0} to {1} by {2}", handle, lDistanceTomove, dwMoveMethod); + ulong ret = 0; + + try + { + VArchiveData va; + VStreamFile vsf; + long offset = lDistanceTomove; + if (lpDistanceToMoveHigh != null) + offset |= ((long)lpDistanceToMoveHigh << 32); + + IntPtr _handle = new IntPtr(handle); + if (_varchives.TryGetValue(_handle, out va)) + { + ret = (ulong)va.SetFilePointer(offset, (Win32.EMoveMethod)dwMoveMethod); + } + else if (_streamFiles.TryGetValue(_handle, out vsf)) + { + ret = (ulong)_streamFiles[_handle].SetPosition(offset, (Win32.EMoveMethod)dwMoveMethod); + } + else + { + ret = s_Trampolines.SetFilePointer(handle, lDistanceTomove, lpDistanceToMoveHigh, dwMoveMethod); + } + } + catch (Exception ex) + { + Debug.WriteLine(ex.ToString()); + } + + return ret; + } - if (mapped != null) + //[UnmanagedCallersOnly] + //static unsafe int HWriteFile(void* hFile, void* lpBuffer, ulong nNumberOfBytesToWrite, ulong* lpNumberOfBytesWritten, void* lpOverlapped) + //{ + // int result = 0; + // *lpNumberOfBytesWritten = 0; + // try + // { + // result = s_Trampolines.WriteFile(hFile, lpBuffer, nNumberOfBytesToWrite, lpNumberOfBytesWritten, lpOverlapped); + // //DebugLogger.WriteLine(String.Format("Write {0} bytes on {1}", lpNumberOfBytesWritten, hFile.ToInt32())); + + // IntPtr _hFile = new IntPtr(hFile); + // if (_saveFiles.ContainsKey(_hFile)) + // { + // ulong offset = s_Trampolines.SetFilePointer(hFile, 0, null, (ulong)Win32.EMoveMethod.Current); + // //DebugLogger.WriteLine(String.Format("Write {0} bytes to {1} at offset {2}", lpNumberOfBytesWritten, _saveFiles[hFile], offset)); + // } + // } + // catch (Exception ex) + // { + // Debug.WriteLine(ex.ToString()); + // } + + // return result; + //} + + [UnmanagedCallersOnly] + static int HReadFile(void* handle, void* bytes, uint numBytesToRead, uint* numBytesRead, void* overlapped) + { + int ret = 0; + try + { + VArchiveData va; + VStreamFile vsf; + LGPWrapper lgp; + IntPtr _handle = new IntPtr(handle); + IntPtr _bytes = new IntPtr(bytes); + + if (_varchives.TryGetValue(_handle, out va)) + ret = va.ReadFile(_handle, numBytesToRead, ref *numBytesRead); + else if (_streamFiles.TryGetValue(_handle, out vsf)) + ret = vsf.Read(_bytes, *numBytesRead, ref *numBytesRead); + else if (_hMap.TryGetValue(_handle, out lgp)) + { + //DebugLogger.WriteLine("Hooked ReadFile on {0} for {1} bytes", handle.ToInt32(), numBytesToRead); + //if (overlapped != IntPtr.Zero) DebugLogger.WriteLine("(is overlapped)"); + + ulong pos = s_Trampolines.SetFilePointer(handle, 0, null, (ulong)Win32.EMoveMethod.Current); + //DebugLogger.WriteLine("Hooked ReadFile on {0} for {1} bytes at {2}", handle.ToInt32(), numBytesToRead, pos); + lgp.VFile.Read((uint)pos, numBytesToRead, _bytes, ref *numBytesRead); + //DebugLogger.WriteLine("--{0} bytes read", numBytesRead); + s_Trampolines.SetFilePointer(handle, (int)(pos + numBytesRead), null, (ulong)Win32.EMoveMethod.Begin); + lgp.Ping(); + ret = -1; + } + else + ret = s_Trampolines.ReadFile(handle, bytes, numBytesToRead, numBytesRead, overlapped); + } + catch (Exception ex) + { + Debug.WriteLine(ex.ToString()); + } + + return ret; + } + + static IntPtr CreateVA(OverrideFile of) { + IntPtr dummy = IntPtr.Zero; + + try + { + VArchiveData va = new VArchiveData(of.Archive.GetBytes(of.File)); + dummy = of.Archive.GetDummyHandle(); + DebugLogger.WriteLine($"Creating dummy file handle {dummy} to access {of.Archive}{of.File}"); + _varchives[dummy] = va; + } + catch (Exception ex) + { + Debug.WriteLine(ex.ToString()); + } + + return dummy; + } + + // ushort*, uint, uint, void*, uint, uint, void*, void* + [UnmanagedCallersOnly] + static void* HCreateFileW(ushort* lpFileName, uint dwDesiredAccess, uint dwShareMode, void*lpSecurityAttributes, uint dwCreationDisposition, uint dwFlagsAndAttributes, void* hTemplateFile) + { + void* ret = null; + try + { + string _lpFileName = new string((char*)lpFileName); + + // Usually this check should be enough... + bool isFF7GameFile = _lpFileName.StartsWith(_profile.FF7Path, StringComparison.InvariantCultureIgnoreCase); + // ...but if it fails, last resort is to check if the file exists in the game directory + if (!isFF7GameFile && !_lpFileName.StartsWith("\\", StringComparison.InvariantCultureIgnoreCase) && !Path.IsPathRooted(_lpFileName)) + { + isFF7GameFile = _profile.gameFiles.Any(s => s.EndsWith(_lpFileName, StringComparison.InvariantCultureIgnoreCase)); + } + + // If a game file is found, process with replacing its content with relative mod file + if (isFF7GameFile) + { + _lpFileName = _lpFileName.Replace("\\/", "\\").Replace("/", "\\").Replace("\\\\", "\\"); + DebugLogger.DetailedWriteLine($"CreateFileW for {_lpFileName}..."); + if (_lpFileName.IndexOf('\\') < 0) + { + //DebugLogger.WriteLine("No path: curdir is {0}", System.IO.Directory.GetCurrentDirectory(), 0); + _lpFileName = Path.Combine(Directory.GetCurrentDirectory(), _lpFileName); + } + + foreach (string path in _profile.MonitorPaths) + { + if (_lpFileName.StartsWith(path, StringComparison.InvariantCultureIgnoreCase)) { - DebugLogger.WriteLine($"Remapping {lpFileName} to {mapped.File} [ Matched: '{match}' ]"); + string match = _lpFileName.Substring(path.Length); + OverrideFile mapped = LGPWrapper.MapFile(match, _profile); + + //DebugLogger.WriteLine($"Attempting match '{match}' for {_lpFileName}..."); + + if (mapped == null) + { + // Attempt a second round, this time relaxing the path match replacing only the game folder path. + match = _lpFileName.Substring(_profile.FF7Path.Length + 1); + mapped = LGPWrapper.MapFile(match, _profile); + + //DebugLogger.WriteLine($"Attempting match '{match}' for {_lpFileName}..."); + } + + if (mapped != null) + { + DebugLogger.WriteLine($"Remapping {_lpFileName} to {mapped.File} [ Matched: '{match}' ]"); - if (mapped.Archive == null) - lpFileName = mapped.File; - else - return CreateVA(mapped); + if (mapped.Archive == null) + _lpFileName = mapped.File; + else + { + ret = CreateVA(mapped).ToPointer(); + break; + } + } } } - } - } else - DebugLogger.WriteLine($"Skipped file {lpFileName}"); - - IntPtr handle = CreateFileW(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile); - //DebugLogger.WriteLine("Hooked CreateFileW for {0} under {1}", lpFileName, handle.ToInt32()); - - if (isFF7GameFile && handle.ToInt32() != -1) - { - if (System.IO.Path.GetExtension(lpFileName).Equals(".ff7", StringComparison.InvariantCultureIgnoreCase)) - { - _saveFiles.Add(handle, lpFileName); } - - DebugLogger.DetailedWriteLine($"CreateFileW: {lpFileName} -> {handle}"); - } - - return handle; - } - - private IntPtr HFindFirstFile(string lpFileName, out WIN32_FIND_DATA lpFindFileData) { - DebugLogger.WriteLine("FindFirstFile for " + lpFileName); - return FindFirstFileW(lpFileName, out lpFindFileData); - } - - private bool HCreateProcessW(string lpApplicationName, - string lpCommandLine, ref SECURITY_ATTRIBUTES lpProcessAttributes, - ref SECURITY_ATTRIBUTES lpThreadAttributes, bool bInheritHandles, - uint dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory, - [In] ref STARTUPINFO lpStartupInfo, - out PROCESS_INFORMATION lpProcessInformation) { - DebugLogger.WriteLine($"CreateProcessW for {lpApplicationName}, {lpCommandLine}"); - string exe = lpApplicationName; - if (String.IsNullOrWhiteSpace(exe)) exe = lpCommandLine; - exe = exe.Replace('/', '\\'); - if (System.IO.Path.GetFileName(exe).IndexOf("FF7", StringComparison.InvariantCultureIgnoreCase) >= 0) { - if (System.IO.File.Exists(exe + ".exe")) exe += ".exe"; - //int pid; - try { - string lib = System.Reflection.Assembly.GetExecutingAssembly().Location; - DebugLogger.WriteLine("--Injecting into " + exe + " with library " + lib); - - if (CreateProcessW(lpApplicationName, lpCommandLine, ref lpProcessAttributes, ref lpThreadAttributes, - bInheritHandles, dwCreationFlags, lpEnvironment, lpCurrentDirectory, ref lpStartupInfo, out lpProcessInformation)) { - EasyHook.RemoteHooking.Inject(lpProcessInformation.dwProcessId, lib, null, _profile); - - return true; - } else - return false; - } catch (Exception ex) { - DebugLogger.WriteLine(ex.ToString()); - throw; - } - } else { - return CreateProcessW(lpApplicationName, lpCommandLine, ref lpProcessAttributes, ref lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment, lpCurrentDirectory, ref lpStartupInfo, out lpProcessInformation); - } - } - - private bool HGetFileInformationByHandle(IntPtr hFile, out BY_HANDLE_FILE_INFORMATION lpFileInformation) { - bool result = GetFileInformationByHandle(hFile, out lpFileInformation); - VArchiveData va; - if (result && _varchives.TryGetValue(hFile, out va)) { - DebugLogger.DetailedWriteLine($"Overriding GetFileInformationByHandle for dummy file {hFile}"); - lpFileInformation.FileSizeHigh = (uint)(va.Size >> 32); - lpFileInformation.FileSizeLow = (uint)(va.Size & 0xffffffff); - } - return result; - } - - private bool HDuplicateHandle(IntPtr hSourceProcessHandle, - IntPtr hSourceHandle, IntPtr hTargetProcessHandle, out IntPtr lpTargetHandle, - uint dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, uint dwOptions) { - // DebugLogger.DetailedWriteLine("DuplicateHandle on {0}", hSourceHandle); - bool result = Win32.DuplicateHandle(hSourceProcessHandle, hSourceHandle, hTargetProcessHandle, out lpTargetHandle, dwDesiredAccess, bInheritHandle, dwOptions); - if (result && _varchives.ContainsKey(hSourceHandle)) { - _varchives[lpTargetHandle] = _varchives[hSourceHandle]; - DebugLogger.DetailedWriteLine($"Duplicating dummy handle {hSourceHandle} to {lpTargetHandle}"); - } - return result; - } - - private uint HGetFileSize(IntPtr hFile, IntPtr lpFileSizeHigh) { - VArchiveData va; - if (_varchives.TryGetValue(hFile, out va)) { - return va.GetFileSize(lpFileSizeHigh); - } - return GetFileSize(hFile, lpFileSizeHigh); - } - - private bool HGetFileSizeEx(IntPtr hFile, ref long lpFileSize) { - VArchiveData va; - if (_varchives.TryGetValue(hFile, out va)) { - DebugLogger.WriteLine($"GetFileSizeEx on dummy handle {hFile}"); - lpFileSize = va.Size; - return true; - } - return GetFileSizeEx(hFile, ref lpFileSize); - } - - - private bool HSetFilePointerEx(IntPtr hFile, long liDistanceToMove, IntPtr lpNewFilePointer, uint dwMoveMethod) { - VArchiveData va; - if (_varchives.TryGetValue(hFile, out va)) { - return va.SetFilePointerEx(hFile, liDistanceToMove, lpNewFilePointer, dwMoveMethod); - } - return SetFilePointerEx(hFile, liDistanceToMove, lpNewFilePointer, dwMoveMethod); - } - } -} + else + DebugLogger.DetailedWriteLine($"Skipped file {_lpFileName}"); + + if (ret == null) + ret = s_Trampolines.CreateFileW((ushort*)Marshal.StringToHGlobalAuto(_lpFileName).ToPointer(), dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile); + + //DebugLogger.WriteLine("Hooked CreateFileW for {0} under {1}", _lpFileName, handle.ToInt32()); + + //if (isFF7GameFile && ret != null) + //{ + // IntPtr _ret = new IntPtr(ret); + + // if (System.IO.Path.GetExtension(_lpFileName).Equals(".ff7", StringComparison.InvariantCultureIgnoreCase)) + // { + // _saveFiles.Add(_ret, _lpFileName); + // } + + // DebugLogger.DetailedWriteLine($"CreateFileW: {_lpFileName} -> {_ret}"); + //} + } + catch (Exception ex) + { + Debug.WriteLine(ex.ToString()); + } + + return ret; + } + + [UnmanagedCallersOnly] + static void* HFindFirstFileW(ushort* lpFileName, void* lpFindFileData) { + void* ret = null; + + try + { + DebugLogger.WriteLine("FindFirstFile for " + new string((char*)lpFileName)); + ret = s_Trampolines.FindFirstFileW(lpFileName, lpFindFileData); + } + catch (Exception ex) + { + Debug.WriteLine(ex.ToString()); + } + + return ret; + } + + //[UnmanagedCallersOnly] + //private static bool HCreateProcessW(string lpApplicationName, + // string lpCommandLine, ref SECURITY_ATTRIBUTES lpProcessAttributes, + // ref SECURITY_ATTRIBUTES lpThreadAttributes, bool bInheritHandles, + // uint dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory, + // [In] ref STARTUPINFO lpStartupInfo, + // out PROCESS_INFORMATION lpProcessInformation) + //{ + + // DebugLogger.WriteLine($"CreateProcessW for {lpApplicationName}, {lpCommandLine}"); + // string exe = lpApplicationName; + // if (String.IsNullOrWhiteSpace(exe)) exe = lpCommandLine; + // exe = exe.Replace('/', '\\'); + + // bool ret; + // if (System.IO.Path.GetFileName(exe).IndexOf("FF7", StringComparison.InvariantCultureIgnoreCase) >= 0) + // { + // if (System.IO.File.Exists(exe + ".exe")) exe += ".exe"; + // //int pid; + // try + // { + // string lib = System.Reflection.Assembly.GetExecutingAssembly().Location; + // DebugLogger.WriteLine("--Injecting into " + exe + " with library " + lib); + + // ret = CreateProcessW(lpApplicationName, lpCommandLine, ref lpProcessAttributes, ref lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment, lpCurrentDirectory, ref lpStartupInfo, out lpProcessInformation); + // } + // catch (Exception ex) + // { + // DebugLogger.WriteLine(ex.ToString()); + // throw; + // } + // } + // else + // ret = CreateProcessW(lpApplicationName, lpCommandLine, ref lpProcessAttributes, ref lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment, lpCurrentDirectory, ref lpStartupInfo, out lpProcessInformation); + + // return ret; + //} + + [UnmanagedCallersOnly] + static int HGetFileInformationByHandle(void* hFile, Win32.BY_HANDLE_FILE_INFORMATION* lpFileInformation) { + int result = 0; + try + { + result = s_Trampolines.GetFileInformationByHandle(hFile, lpFileInformation); + VArchiveData va; + IntPtr _hFile = new IntPtr(hFile); + if (result > 0 && _varchives.TryGetValue(_hFile, out va)) + { + DebugLogger.DetailedWriteLine($"Overriding GetFileInformationByHandle for dummy file {_hFile}"); + lpFileInformation->FileSizeHigh = (uint)(va.Size >> 32); + lpFileInformation->FileSizeLow = (uint)(va.Size & 0xffffffff); + } + } + catch (Exception ex) + { + Debug.WriteLine(ex.ToString()); + } + + return result; + } + + [UnmanagedCallersOnly] + static int HDuplicateHandle(void* hSourceProcessHandle, void* hSourceHandle, void* hTargetProcessHandle, void** lpTargetHandle, ulong dwDesiredAccess, int bInheritHandle, ulong dwOptions) + { + // DebugLogger.DetailedWriteLine("DuplicateHandle on {0}", hSourceHandle); + + int result = 0; + try + { + IntPtr _hSourceHandle = new IntPtr(hSourceHandle); + IntPtr _lpTargetHandle = new IntPtr(lpTargetHandle); + result = s_Trampolines.DuplicateHandle(hSourceProcessHandle, hSourceHandle, hTargetProcessHandle, lpTargetHandle, dwDesiredAccess, bInheritHandle, dwOptions); + if (result > 0 && _varchives.ContainsKey(_hSourceHandle)) + { + _varchives[_lpTargetHandle] = _varchives[_hSourceHandle]; + DebugLogger.DetailedWriteLine($"Duplicating dummy handle {_hSourceHandle} to {_lpTargetHandle}"); + } + } + catch (Exception ex) + { + Debug.WriteLine(ex.ToString()); + } + + return result; + } + + [UnmanagedCallersOnly] + static ulong HGetFileSize(void* hFile, ulong* lpFileSizeHigh) + { + VArchiveData va; + + ulong ret = 0; + try + { + IntPtr _hFile = new IntPtr(hFile); + IntPtr _lpFileSizeHigh = new IntPtr(lpFileSizeHigh); + if (_varchives.TryGetValue(_hFile, out va)) + ret = va.GetFileSize(_lpFileSizeHigh); + else + ret = s_Trampolines.GetFileSize(hFile, lpFileSizeHigh); + } + catch (Exception ex) + { + Debug.WriteLine(ex.ToString()); + } + + return ret; + } + + [UnmanagedCallersOnly] + static ulong HGetFileSizeEx(void* hFile, ulong* lpFileSize) + { + ulong ret = 0; + try + { + VArchiveData va; + IntPtr _hFile = new IntPtr(hFile); + if (_varchives.TryGetValue(_hFile, out va)) + { + DebugLogger.WriteLine($"GetFileSizeEx on dummy handle {_hFile}"); + *lpFileSize = (ulong)va.Size; + ret = 1; + } + else + ret = s_Trampolines.GetFileSizeEx(hFile, lpFileSize); + } + catch (Exception ex) + { + Debug.WriteLine(ex.ToString()); + } + + return ret; + } + + [UnmanagedCallersOnly] + static ulong HSetFilePointerEx(void* hFile, long liDistanceToMove, long* lpNewFilePointer, ulong dwMoveMethod) + { + VArchiveData va; + + ulong ret = 0; + try + { + IntPtr _hFile = new IntPtr(hFile); + IntPtr _lpNewFilePointer = new IntPtr(lpNewFilePointer); + + if (_varchives.TryGetValue(_hFile, out va)) + ret = va.SetFilePointerEx(_hFile, liDistanceToMove, _lpNewFilePointer, (uint)dwMoveMethod); + else + ret = s_Trampolines.SetFilePointerEx(hFile, liDistanceToMove, lpNewFilePointer, dwMoveMethod); + } + catch (Exception ex) + { + Debug.WriteLine(ex.ToString()); + } + + return ret; + } + } +} diff --git a/7thWrapperLoader/7thWrapperLoader.vcxproj b/7thWrapperLoader/7thWrapperLoader.vcxproj new file mode 100644 index 00000000..7dd4a304 --- /dev/null +++ b/7thWrapperLoader/7thWrapperLoader.vcxproj @@ -0,0 +1,187 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + Win32Proj + {d95622db-5459-4369-9585-f6457aaf3fc3} + My7thWrapperLoader + 10.0 + + + + DynamicLibrary + true + v143 + Unicode + + + DynamicLibrary + false + v143 + true + Unicode + + + DynamicLibrary + true + v143 + Unicode + + + DynamicLibrary + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + $(SolutionDir)\SeventhHeavenUI\bin\$(Configuration)\net6.0-windows\ + + + $(SolutionDir)\SeventhHeavenUI\bin\$(Configuration)\net6.0-windows\ + + + $(SolutionDir)\SeventhHeavenUI\bin\$(Configuration)\net6.0-windows\ + + + $(SolutionDir)\SeventhHeavenUI\bin\$(Configuration)\net6.0-windows\ + + + true + + + + Level3 + true + WIN32;_DEBUG;MY7THWRAPPERLOADER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + NotUsing + + + + + Windows + true + false + + + + + + + Level3 + true + true + true + WIN32;NDEBUG;MY7THWRAPPERLOADER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + NotUsing + + + + + Windows + true + true + true + false + + + + + + + Level3 + true + _DEBUG;MY7THWRAPPERLOADER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + NotUsing + + + + + Windows + true + false + + + + + + + Level3 + true + true + true + NDEBUG;MY7THWRAPPERLOADER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + NotUsing + + + + + Windows + true + true + true + false + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/7thWrapperLoader/7thWrapperLoader.vcxproj.filters b/7thWrapperLoader/7thWrapperLoader.vcxproj.filters new file mode 100644 index 00000000..4448b379 --- /dev/null +++ b/7thWrapperLoader/7thWrapperLoader.vcxproj.filters @@ -0,0 +1,36 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + + + + + + Header Files + + + Header Files + + + Header Files + + + \ No newline at end of file diff --git a/7thWrapperLoader/delegates.x.h b/7thWrapperLoader/delegates.x.h new file mode 100644 index 00000000..f78e50f4 --- /dev/null +++ b/7thWrapperLoader/delegates.x.h @@ -0,0 +1 @@ +X(load_assembly_and_get_function_pointer) diff --git a/7thWrapperLoader/dllmain.cpp b/7thWrapperLoader/dllmain.cpp new file mode 100644 index 00000000..8087fe61 --- /dev/null +++ b/7thWrapperLoader/dllmain.cpp @@ -0,0 +1,83 @@ +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#include +#include +#include + +#pragma comment(linker, "/export:DirectInputCreateA=C:\\Windows\\System32\\dinput.DirectInputCreateA,@1") + +#define X(n) n##_fn n; +#include "hostfxr.x.h" +#include "delegates.x.h" +#undef X + +#define MAIN_ASM_NAME L"7thWrapperProxy" +#define MAIN_TYP_NAME L"_7thWrapperProxy.Proxy" +#define MAIN_FUN_NAME L"Main" + +// APIs to export from the host to managed code +struct host_exports +{ +#define X(n) decltype(&n) n; +#include "host_exports.x.h" +#undef X +} exports = +{ + #define X(n) &n, + #include "host_exports.x.h" + #undef X +}; + +// API to initialize 7th Heaven. +HRESULT(WINAPI* HostInitialize)(const host_exports*); + +BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved) +{ + if (fdwReason != DLL_PROCESS_ATTACH) + return TRUE; + + static auto target = &GetCommandLineA; + static decltype(target) detour = []() + { + DetourTransactionBegin(); + DetourDetach((void**)&target, detour); + DetourTransactionCommit(); + + size_t buffer_size = 0; + get_hostfxr_path(nullptr, &buffer_size, nullptr); + + auto buffer = new char_t[buffer_size]; + get_hostfxr_path(buffer, &buffer_size, nullptr); + + auto hostfxr = LoadLibraryW(buffer); + delete[] buffer; + +#define X(n) *(void**)&n = GetProcAddress(hostfxr, #n); +#include "hostfxr.x.h" +#undef X + + hostfxr_handle context = nullptr; + hostfxr_set_error_writer([](auto message) { OutputDebugString(message); }); + hostfxr_initialize_for_runtime_config(MAIN_ASM_NAME L".runtimeconfig.json", nullptr, &context); + +#define X(n) hostfxr_get_runtime_delegate(context, hdt_##n, (void**)&n); +#include "delegates.x.h" +#undef X + + hostfxr_close(context); + + load_assembly_and_get_function_pointer(MAIN_ASM_NAME L".dll", MAIN_TYP_NAME L", " MAIN_ASM_NAME, MAIN_FUN_NAME, UNMANAGEDCALLERSONLY_METHOD, nullptr, (void**)&HostInitialize); + HostInitialize(&exports); + + return target(); + }; + + DisableThreadLibraryCalls(hinstDLL); + DetourTransactionBegin(); + DetourAttach((void**)&target, detour); + DetourTransactionCommit(); + + return TRUE; +} diff --git a/7thWrapperLoader/host_exports.x.h b/7thWrapperLoader/host_exports.x.h new file mode 100644 index 00000000..4ed602af --- /dev/null +++ b/7thWrapperLoader/host_exports.x.h @@ -0,0 +1,4 @@ +X(DetourTransactionBegin) +X(DetourAttach) +X(DetourDetach) +X(DetourTransactionCommit) \ No newline at end of file diff --git a/7thWrapperLoader/hostfxr.x.h b/7thWrapperLoader/hostfxr.x.h new file mode 100644 index 00000000..9ffaa1fa --- /dev/null +++ b/7thWrapperLoader/hostfxr.x.h @@ -0,0 +1,4 @@ +X(hostfxr_initialize_for_runtime_config) +X(hostfxr_get_runtime_delegate) +X(hostfxr_set_error_writer) +X(hostfxr_close) \ No newline at end of file diff --git a/7thWrapperLoader/vcpkg.json b/7thWrapperLoader/vcpkg.json new file mode 100644 index 00000000..73b9e723 --- /dev/null +++ b/7thWrapperLoader/vcpkg.json @@ -0,0 +1,21 @@ +{ + "name": "7th-wrapper-loader", + "version": "1.0.0", + "builtin-baseline": "acc3bcf76b84ae5041c86ab55fe138ae7b8255c7", + "dependencies": [ + "detours", + "nethost" + ], + "overrides": [ + { + "name": "detours", + "version": "4.0.1", + "port-version": 5 + }, + { + "name": "nethost", + "version": "6.0.5", + "port-version": 0 + } + ] +} diff --git a/7thWrapperProxy/7thWrapperProxy.csproj b/7thWrapperProxy/7thWrapperProxy.csproj new file mode 100644 index 00000000..1a813c1f --- /dev/null +++ b/7thWrapperProxy/7thWrapperProxy.csproj @@ -0,0 +1,20 @@ + + + + net6.0-windows + _7thWrapperProxy + enable + enable + true + true + + + + CA1416 + + + + CA1416 + + + diff --git a/7thWrapperProxy/Main.cs b/7thWrapperProxy/Main.cs new file mode 100644 index 00000000..13fa2d1b --- /dev/null +++ b/7thWrapperProxy/Main.cs @@ -0,0 +1,34 @@ +using System.Diagnostics; +using System.Reflection; +using System.Runtime.InteropServices; + +namespace _7thWrapperProxy +{ + static unsafe class Proxy + { + private static Assembly? lib; + private static Type? t; + private static MethodInfo? m; + private static IntPtr _exports; + + [UnmanagedCallersOnly] + public static int Main(void* exports) + { + try + { + _exports = new IntPtr(exports); + lib = Assembly.LoadFrom(Path.Combine(Directory.GetCurrentDirectory(), "7thWrapperLib.dll")); + t = lib.GetType("_7thWrapperLib.Wrap"); + + if (t != null) m = t.GetMethod("Run", BindingFlags.Static | BindingFlags.Public); + if (m != null) m.Invoke(null, new object[] { _exports, Process.GetCurrentProcess(), Type.Missing }); + } + catch (Exception ex) + { + Debug.WriteLine(ex.ToString()); + } + + return 0; + } + } +} \ No newline at end of file diff --git a/README.md b/README.md index c0db1677..f7524791 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,17 @@ This is a fork of the original [7th Heaven 2.x](https://github.com/unab0mb/7h) r 1. Run the installer and import this [.vsconfig](.vsconfig) file in the installer to pick the required components to build this project 2. Once installed, open the file [`7thHeaven.sln`](7thHeaven.sln) in Visual Studio and click the build button +## Special thanks + +The .NET 6 migration would not have been possibile without the help of these people. The order is purely Alphabetical. + +These people are: + +- [Benjamin Moir](https://github.com/DaZombieKiller): + - For figuring out various .NET internals + - For the Detours examples and logics to be used in C# + - For the patience to guide through nitty gritty low level details + ## License See [LICENSE.txt](LICENSE.txt) diff --git a/SeventhHeavenUI/App.xaml.cs b/SeventhHeavenUI/App.xaml.cs index e26100c2..9382a690 100644 --- a/SeventhHeavenUI/App.xaml.cs +++ b/SeventhHeavenUI/App.xaml.cs @@ -2,7 +2,7 @@ using Iros._7th; using Iros._7th.Workshop; using SeventhHeaven.Classes; -using SeventhHeaven.Classes.WCF; +//using SeventhHeaven.Classes.WCF; using SeventhHeaven.ViewModels; using SeventhHeaven.Windows; using System; @@ -40,15 +40,16 @@ public App() uniqueMutex = new Mutex(true, uniqueAppGuid, out bool gotMutex); GC.KeepAlive(App.uniqueMutex); - if (SingleInstance.IsFirstInstance(uniqueAppGuid, true)) - { - SingleInstance.OnSecondInstanceStarted += SingleInstance_OnSecondInstanceStarted; - } - else - { - // second instance so notify first instance - SingleInstance.NotifyFirstInstance(uniqueAppGuid); - } + //TODO: Add support for .NET 6.0 + //if (SingleInstance.IsFirstInstance(uniqueAppGuid, true)) + //{ + // SingleInstance.OnSecondInstanceStarted += SingleInstance_OnSecondInstanceStarted; + //} + //else + //{ + // // second instance so notify first instance + // SingleInstance.NotifyFirstInstance(uniqueAppGuid); + //} if (!gotMutex) { @@ -57,11 +58,12 @@ public App() } } - private void SingleInstance_OnSecondInstanceStarted(object sender, SecondInstanceStartedEventArgs e) - { - // e.Args[0] = path to 7th Heaven .exe - ProcessCommandLineArgs(e.Args); - } + //TODO: Add support for .NET 6.0 + //private void SingleInstance_OnSecondInstanceStarted(object sender, SecondInstanceStartedEventArgs e) + //{ + // // e.Args[0] = path to 7th Heaven .exe + // ProcessCommandLineArgs(e.Args); + //} internal static void ProcessCommandLineArgs(string[] args, bool closeAfterProcessing = false) { diff --git a/SeventhHeavenUI/Classes/ControlConfiguration.cs b/SeventhHeavenUI/Classes/ControlConfiguration.cs index a216a5ae..e9fc5daa 100644 --- a/SeventhHeavenUI/Classes/ControlConfiguration.cs +++ b/SeventhHeavenUI/Classes/ControlConfiguration.cs @@ -1,5 +1,4 @@ -using EasyHook; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using System.Windows.Input; diff --git a/SeventhHeavenUI/Classes/GameConverter.cs b/SeventhHeavenUI/Classes/GameConverter.cs index 259abe89..8d7d4054 100644 --- a/SeventhHeavenUI/Classes/GameConverter.cs +++ b/SeventhHeavenUI/Classes/GameConverter.cs @@ -6,7 +6,6 @@ using System.Diagnostics; using System.IO; using System.Linq; -using System.Management.Automation; using System.Security.Cryptography; using System.Text; using System.Text.RegularExpressions; @@ -1074,7 +1073,7 @@ private void RunUlgp(string sourcePath, string destPath) WorkingDirectory = Path.GetDirectoryName(Sys.PathToUlgpExe), Arguments = $"\"{sourcePath}\" \"{destPath}\"", CreateNoWindow = true, - UseShellExecute = false, + UseShellExecute = true, Verb = "runas", // ensures the process is started as Admin }; diff --git a/SeventhHeavenUI/Classes/GameDiscMounter.cs b/SeventhHeavenUI/Classes/GameDiscMounter.cs index dd94a25e..fd225859 100644 --- a/SeventhHeavenUI/Classes/GameDiscMounter.cs +++ b/SeventhHeavenUI/Classes/GameDiscMounter.cs @@ -3,7 +3,6 @@ using System.Diagnostics; using System.IO; using System.Linq; -using System.Management.Automation; using System.Threading; namespace _7thHeaven.Code @@ -127,13 +126,15 @@ private bool MountWithPowershell(string isoPath) return false; } - using (PowerShell ps = PowerShell.Create()) - { - ps.AddCommand("Mount-DiskImage"); - ps.AddParameter("ImagePath", isoPath); - - System.Collections.ObjectModel.Collection result = ps.Invoke(); - } + ProcessStartInfo startInfo = new ProcessStartInfo("PowerShell.exe") + { + CreateNoWindow = false, + UseShellExecute = true, + WindowStyle = ProcessWindowStyle.Hidden, + Arguments = $"Mount-DiskImage -ImagePath '{isoPath}'" + + }; + Process.Start(startInfo); return true; } @@ -156,10 +157,14 @@ private bool UnmountWithPowershell(string isoPath) return false; } - using (PowerShell ps = PowerShell.Create()) - { - System.Collections.ObjectModel.Collection result = ps.AddCommand("Dismount-DiskImage").AddParameter("ImagePath", isoPath).Invoke(); - } + ProcessStartInfo startInfo = new ProcessStartInfo("PowerShell.exe") + { + CreateNoWindow = false, + UseShellExecute = true, + WindowStyle = ProcessWindowStyle.Hidden, + Arguments = $"Dismount-DiskImage -ImagePath '{isoPath}'", + }; + Process.Start(startInfo); return true; } @@ -240,7 +245,7 @@ private void RunWinCDEmuWithArguments(string arguments, int timeoutInSeconds = 1 ProcessStartInfo startInfo = new ProcessStartInfo(Sys.PathToWinCDEmuExe, arguments) { WorkingDirectory = Path.GetDirectoryName(Sys.PathToWinCDEmuExe), - UseShellExecute = false, + UseShellExecute = true, CreateNoWindow = true, WindowStyle = ProcessWindowStyle.Normal }; diff --git a/SeventhHeavenUI/Classes/GameLauncher.cs b/SeventhHeavenUI/Classes/GameLauncher.cs index 55a6cf55..4ae75417 100644 --- a/SeventhHeavenUI/Classes/GameLauncher.cs +++ b/SeventhHeavenUI/Classes/GameLauncher.cs @@ -476,10 +476,10 @@ public static bool LaunchGame(bool varDump, bool debug, bool launchWithNoMods = // - // Copy EasyHook.dll to FF7 + // Copy 7thWrapper* dlls to FF7 // Instance.RaiseProgressChanged(ResourceHelper.Get(StringKey.CopyingEasyHookToFf7PathIfNotFoundOrOlder)); - CopyEasyHookDlls(); + Copy7thWrapperDlls(); // // Inherit FFNx Config keys from each mod @@ -618,7 +618,7 @@ public static bool LaunchGame(bool varDump, bool debug, bool launchWithNoMods = RuntimeParams parms = new RuntimeParams { - ProfileFile = Path.GetTempFileName() + ProfileFile = Path.Combine(Path.GetDirectoryName(Sys.Settings.FF7Exe), ".7thWrapperProfile") }; Instance.RaiseProgressChanged($"{ResourceHelper.Get(StringKey.WritingTemporaryRuntimeProfileFileTo)} {parms.ProfileFile} ..."); @@ -628,64 +628,16 @@ public static bool LaunchGame(bool varDump, bool debug, bool launchWithNoMods = // attempt to launch the game a few times in the case of an ApplicationException that can be thrown by EasyHook it seems randomly at times // ... The error tends to go away the second time trying but we will try multiple times before failing - string lib = Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "7thWrapperLib.dll"); - bool didInject = false; - int attemptCount = 0; - int totalAttempts = 5; Instance.RaiseProgressChanged(ResourceHelper.Get(StringKey.AttemptingToInjectWithEasyHook)); - while (!didInject && attemptCount < totalAttempts) + while (!didInject) { try { - - // attempt to inject on background thread so we can have a timeout if the process does not return in 10 seconds - // a successful injection should only take ~3 seconds - var waitTask = Task.Factory.StartNew(() => - { - LaunchFF7Exe(); - EasyHook.RemoteHooking.Inject(GameLauncher.ff7Proc.Id, lib, lib, parms); - }).ContinueWith((taskResult) => - { - if (taskResult.IsFaulted) - { - // an error occurred when injecting so concatenate all errors to display in output - didInject = false; - string errors = ""; - foreach (Exception ex in taskResult.Exception.InnerExceptions) - { - if (ex is AggregateException) - { - errors += string.Join("; ", (ex as AggregateException).InnerExceptions.Select(s => s.Message)); - } - else - { - errors += $"{ex.Message}; "; - } - } - - Logger.Warn($"\t{ResourceHelper.Get(StringKey.ReceivedErrors)}: {errors} ..."); - } - else - { - didInject = true; - } - }).ConfigureAwait(false); - - // Wait 10 seconds for the injection to complete - DateTime startTime = DateTime.Now; - while (!waitTask.GetAwaiter().IsCompleted) - { - TimeSpan elapsed = DateTime.Now.Subtract(startTime); - if (elapsed.TotalSeconds > 10) - { - Instance.RaiseProgressChanged($"\t{ResourceHelper.Get(StringKey.ReachedTimeoutWaitingForInjection)}", NLog.LogLevel.Warn); - didInject = false; - break; - } - } + LaunchFF7Exe(); + didInject = true; } catch (Exception e) { @@ -695,17 +647,10 @@ public static bool LaunchGame(bool varDump, bool debug, bool launchWithNoMods = }catch { } Instance.RaiseProgressChanged($"\t{ResourceHelper.Get(StringKey.ReceivedUnknownError)}: {e.Message} ...", NLog.LogLevel.Warn); } - finally - { - attemptCount++; - } } if (!didInject) { - Instance.RaiseProgressChanged($"{ResourceHelper.Get(StringKey.FailedToInjectAfterMaxAmountOfTries)} ({totalAttempts}) ...", NLog.LogLevel.Error); - Instance.RaiseProgressChanged("NativeAPI:"+EasyHook.NativeAPI.RtlGetLastErrorString(),NLog.LogLevel.Error); - return false; } @@ -732,7 +677,8 @@ public static bool LaunchGame(bool varDump, bool debug, bool launchWithNoMods = ProcessStartInfo debugTxtProc = new ProcessStartInfo() { WorkingDirectory = Path.GetDirectoryName(runtimeProfile.LogFile), - FileName = runtimeProfile.LogFile + FileName = runtimeProfile.LogFile, + UseShellExecute = true }; Process.Start(debugTxtProc); } @@ -826,7 +772,7 @@ public static bool LaunchGame(bool varDump, bool debug, bool launchWithNoMods = // Did the game crash? If yes, generate a report if (ff7Proc.ExitCode < 0) { - Logger.Error($"FF7.exe Crashed while exiting with code {ff7Proc.ExitCode}. Generating report..."); + Logger.Error($"FF7.exe Crashed while exiting with code 0x{ff7Proc.ExitCode.ToString("X8")}. Generating report..."); Instance.CollectCrashReport(); } @@ -957,70 +903,29 @@ internal static RuntimeProfile CreateRuntimeProfile() return runtimeProfiles; } - private static void CopyEasyHookDlls() + private static void Copy7thWrapperDlls() { - string source = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location); - - string dir = Path.GetDirectoryName(Sys.Settings.FF7Exe); - string pathToTargetEasyHook = Path.Combine(dir, "EasyHook.dll"); - string pathToSourceEasyHook = Path.Combine(source, "EasyHook.dll"); - - // compare file versions to determine if file can be skipped - bool hasOldVersion = false; - - if (File.Exists(pathToTargetEasyHook)) - { - FileVersionInfo sourceVersion = FileVersionInfo.GetVersionInfo(pathToSourceEasyHook); - FileVersionInfo targetVersion = FileVersionInfo.GetVersionInfo(pathToTargetEasyHook); - - if (new Version(targetVersion.FileVersion).CompareTo(new Version(sourceVersion.FileVersion)) < 0) - { - Instance.RaiseProgressChanged($"\tEasyHook.dll {ResourceHelper.Get(StringKey.DetectedToBeOlderVersion)} ..."); - hasOldVersion = true; - } - } - - - - if (!File.Exists(pathToTargetEasyHook) || hasOldVersion) - { - File.Copy(pathToSourceEasyHook, pathToTargetEasyHook, true); - Instance.RaiseProgressChanged($"\tEasyHook.dll {ResourceHelper.Get(StringKey.Copied)} ..."); - } - else - { - Instance.RaiseProgressChanged($"\t{ResourceHelper.Get(StringKey.SkippedCopying)} EasyHook.dll ..."); - - } - - - - string pathToTargetEasyHook32 = Path.Combine(dir, "EasyHook32.dll"); - string pathToSourceEasyHook32 = Path.Combine(source, "EasyHook32.dll"); - hasOldVersion = false; - - if (File.Exists(pathToTargetEasyHook32)) - { - FileVersionInfo sourceVersion = FileVersionInfo.GetVersionInfo(pathToSourceEasyHook32); - FileVersionInfo targetVersion = FileVersionInfo.GetVersionInfo(pathToTargetEasyHook32); - - if (new Version(targetVersion.FileVersion).CompareTo(new Version(sourceVersion.FileVersion)) < 0) - { - Instance.RaiseProgressChanged($"\tEasyHook32.dll {ResourceHelper.Get(StringKey.DetectedToBeOlderVersion)} ..."); - hasOldVersion = true; - } - } - + string src = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location); + string dest = Path.GetDirectoryName(Sys.Settings.FF7Exe); + + File.Copy(Path.Combine(src, "7thWrapperProxy.runtimeconfig.json"), Path.Combine(dest, "7thWrapperProxy.runtimeconfig.json"), true); + File.Copy(Path.Combine(src, "7thWrapperProxy.dll"), Path.Combine(dest, "7thWrapperProxy.dll"), true); + File.Copy(Path.Combine(src, "SharpCompress.dll"), Path.Combine(dest, "SharpCompress.dll"), true); + File.Copy(Path.Combine(src, "7thWrapperLib.dll"), Path.Combine(dest, "7thWrapperLib.dll"), true); + File.Copy(Path.Combine(src, "7thWrapperLoader.dll"), Path.Combine(dest, "dinput.dll"), true); + File.Copy(Path.Combine(src, "nethost.dll"), Path.Combine(dest, "nethost.dll"), true); + } - if (!File.Exists(pathToTargetEasyHook32) || hasOldVersion) - { - File.Copy(pathToSourceEasyHook32, pathToTargetEasyHook32, true); - Instance.RaiseProgressChanged($"\tEasyHook32.dll {ResourceHelper.Get(StringKey.Copied)} ..."); - } - else - { - Instance.RaiseProgressChanged($"\t{ResourceHelper.Get(StringKey.SkippedCopying)} EasyHook32.dll ..."); - } + private static void Delete7thWrapperDlls() + { + string dest = Path.GetDirectoryName(Sys.Settings.FF7Exe); + + File.Delete(Path.Combine(dest, "7thWrapperProxy.runtimeconfig.json")); + File.Delete(Path.Combine(dest, "7thWrapperProxy.dll")); + File.Delete(Path.Combine(dest, "SharpCompress.dll")); + File.Delete(Path.Combine(dest, "7thWrapperLib.dll")); + File.Delete(Path.Combine(dest, "dinput.dll")); + File.Delete(Path.Combine(dest, "nethost.dll")); } private static void StartTurboLogForVariableDump(RuntimeProfile runtimeProfiles) @@ -1041,7 +946,8 @@ private static void StartTurboLogForVariableDump(RuntimeProfile runtimeProfiles) ProcessStartInfo psi = new ProcessStartInfo(turboLogProcName) { - WorkingDirectory = Path.GetDirectoryName(Sys.Settings.FF7Exe) + WorkingDirectory = Path.GetDirectoryName(Sys.Settings.FF7Exe), + UseShellExecute = true, }; Process aproc = Process.Start(psi); @@ -1060,7 +966,8 @@ internal static bool LaunchFF7Exe() { ProcessStartInfo startInfo = new ProcessStartInfo(Sys.Settings.FF7Exe) { - WorkingDirectory = Path.GetDirectoryName(Sys.Settings.FF7Exe) + WorkingDirectory = Path.GetDirectoryName(Sys.Settings.FF7Exe), + UseShellExecute = true, }; ff7Proc = Process.Start(startInfo); ff7Proc.EnableRaisingEvents = true; @@ -1086,6 +993,9 @@ internal static bool LaunchFF7Exe() { EnableOrDisableReunionMod(doEnable: true); } + + // cleanup + Delete7thWrapperDlls(); } catch (Exception ex) { @@ -1801,7 +1711,7 @@ internal void LaunchProgramsForMod(RuntimeMod mod) WorkingDirectory = Path.GetDirectoryName(program.PathToProgram), FileName = program.PathToProgram, Arguments = program.ProgramArgs, - UseShellExecute = false, + UseShellExecute = true, }; Process aproc = Process.Start(psi); @@ -1876,7 +1786,8 @@ internal void LaunchAdditionalProgramsToRunPrior() { WorkingDirectory = Path.GetDirectoryName(al.PathToProgram), FileName = al.PathToProgram, - Arguments = al.ProgramArgs + Arguments = al.ProgramArgs, + UseShellExecute = true, }; Process aproc = Process.Start(psi); diff --git a/SeventhHeavenUI/Classes/OpenFolderDialog.cs b/SeventhHeavenUI/Classes/OpenFolderDialog.cs index a766f4fa..22f11b94 100644 --- a/SeventhHeavenUI/Classes/OpenFolderDialog.cs +++ b/SeventhHeavenUI/Classes/OpenFolderDialog.cs @@ -69,60 +69,9 @@ private OpenFileDialog Dialog /// true if the user clicks OK public DialogResult ShowDialog(IntPtr hWndOwner) { - if (Environment.OSVersion.Version.Major >= 6) - { - OpenFileDialog dialog = Dialog; - DialogResult result = VistaDialog.Show(hWndOwner, dialog) != 0 ? DialogResult.Cancel : DialogResult.OK; - SelectedPath = dialog.FileName; - SelectedPaths = dialog.FileNames; - return result; - } - else - { - FolderBrowserDialog XPDialog = FolderBrowser; - DialogResult result = XPDialog.ShowDialog(); - SelectedPath = XPDialog.SelectedPath; - return result; - } - } - - private static class VistaDialog - { - private const BindingFlags c_flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; - private readonly static Assembly s_windowsFormsAssembly = typeof(FileDialog).Assembly; - private readonly static Type s_iFileDialogType = s_windowsFormsAssembly.GetType("System.Windows.Forms.FileDialogNative+IFileDialog"); - private readonly static MethodInfo s_createVistaDialogMethodInfo = typeof(OpenFileDialog).GetMethod("CreateVistaDialog", c_flags); - private readonly static MethodInfo s_onBeforeVistaDialogMethodInfo = typeof(OpenFileDialog).GetMethod("OnBeforeVistaDialog", c_flags); - private readonly static MethodInfo s_getOptionsMethodInfo = typeof(FileDialog).GetMethod("GetOptions", c_flags); - private readonly static MethodInfo s_setOptionsMethodInfo = s_iFileDialogType.GetMethod("SetOptions", c_flags); - private readonly static uint s_fosPickFoldersBitFlag = (uint)s_windowsFormsAssembly - .GetType("System.Windows.Forms.FileDialogNative+FOS") - .GetField("FOS_PICKFOLDERS") - .GetValue(null); - private readonly static ConstructorInfo s_vistaDialogEventsConstructorInfo = s_windowsFormsAssembly - .GetType("System.Windows.Forms.FileDialog+VistaDialogEvents") - .GetConstructor(c_flags, null, new[] { typeof(FileDialog) }, null); - private readonly static MethodInfo s_adviseMethodInfo = s_iFileDialogType.GetMethod("Advise"); - private readonly static MethodInfo s_unAdviseMethodInfo = s_iFileDialogType.GetMethod("Unadvise"); - private readonly static MethodInfo s_showMethodInfo = s_iFileDialogType.GetMethod("Show"); - - public static int Show(IntPtr ownerHandle, OpenFileDialog dialog) - { - var iFileDialog = s_createVistaDialogMethodInfo.Invoke(dialog, new object[] { }); - s_onBeforeVistaDialogMethodInfo.Invoke(dialog, new[] { iFileDialog }); - s_setOptionsMethodInfo.Invoke(iFileDialog, new object[] { (uint)s_getOptionsMethodInfo.Invoke(dialog, new object[] { }) | s_fosPickFoldersBitFlag }); - var adviseParametersWithOutputConnectionToken = new[] { s_vistaDialogEventsConstructorInfo.Invoke(new object[] { dialog }), 0U }; - s_adviseMethodInfo.Invoke(iFileDialog, adviseParametersWithOutputConnectionToken); - - try - { - return (int)s_showMethodInfo.Invoke(iFileDialog, new object[] { ownerHandle }); - - } - finally - { - s_unAdviseMethodInfo.Invoke(iFileDialog, new[] { adviseParametersWithOutputConnectionToken[1] }); - } - } + FolderBrowserDialog XPDialog = FolderBrowser; + DialogResult result = XPDialog.ShowDialog(); + SelectedPath = XPDialog.SelectedPath; + return result; } } \ No newline at end of file diff --git a/SeventhHeavenUI/Classes/WCF/ServiceHost.cs b/SeventhHeavenUI/Classes/WCF/ServiceHost.cs index 48b3c7e4..42a68895 100644 --- a/SeventhHeavenUI/Classes/WCF/ServiceHost.cs +++ b/SeventhHeavenUI/Classes/WCF/ServiceHost.cs @@ -1,191 +1,193 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.ServiceModel; -using System.Text; -using System.Threading.Tasks; -using System.Windows; - -/// -/// Implementation reference: https://www.codeproject.com/Articles/1186738/A-Robust-and-Elegant-Single-Instance-WPF-Applicati -/// -namespace SeventhHeaven.Classes.WCF -{ - /// The WCF interface for passing the startup parameters - [ServiceContract (ProtectionLevel = System.Net.Security.ProtectionLevel.None)] - public interface ISingleInstance - { - /// - /// Notifies the first instance that another instance of the application attempted to start. - /// - /// The other instance's command-line arguments. - [OperationContract] - void PassStartupArgs(string[] args); - } - - /// - /// Event declaration for the startup of a second instance - /// - public class SecondInstanceStartedEventArgs : EventArgs - { - /// - /// The event method declaration for the startup of a second instance - /// - /// The other instance's command-line arguments. - public SecondInstanceStartedEventArgs(string[] args) - { Args = args; } - - /// - /// Property containing the second instance's command-line arguments - /// - public string[] Args { get; set; } - } - - /// - /// A class to allow for a single-instance of an application. - /// - public class SingleInstance : ISingleInstance - { - /// - /// Is raised when another instance attempts to start up. - /// - public static event EventHandler OnSecondInstanceStarted; - - private static ServiceHost NamedPipeHost = null; - - /// - /// Interface: Notifies the first instance that another instance of the application attempted to start. - /// - /// The other instance's command-line arguments. - public void PassStartupArgs(string[] args) - { - //check if an event is registered for when a second instance is started - OnSecondInstanceStarted?.Invoke(this, new SecondInstanceStartedEventArgs(args)); - } - - /// - /// Checks to see if this instance is the first instance of this application on the local machine. If it is not, this method will - /// send the first instance this instance's command-line arguments. - /// - /// The application's unique identifier. - /// Should the main window become active on a second instance launch - /// True if this instance is the first instance. - public static bool IsFirstInstance(string uid, bool activatewindow) - { - //attempt to open the service, should succeed if this is the first instance - if (OpenServiceHost(uid)) - { - if (activatewindow) - OnSecondInstanceStarted += ActivateMainWindow; - return true; - } - - return false; - } - - /// - /// Attempts to create the named pipe service. - /// - /// The application's unique identifier. - /// True if the service was published successfully. - private static bool OpenServiceHost(string uid) - { - try - { - //hook the application exit event to avoid race condition when messages flow while the application is disposing of the channel during shutdown - Application.Current.Exit += new ExitEventHandler(OnAppExit); - - //for any unhandled exception we need to ensure NamedPipeHost is disposed - AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(OnUnhandledException); - - - //setup the WCF service using a NamedPipe - NamedPipeHost = new ServiceHost(typeof(SingleInstance), new Uri("net.pipe://localhost/7thHeavenApp")); - NamedPipeHost.AddServiceEndpoint(typeof(ISingleInstance), new NetNamedPipeBinding(), uid); - - //if the service is already open (i.e. another instance is already running) this will cause an exception - NamedPipeHost.Open(); - - //success and we are first instance - return true; - } - catch (AddressAlreadyInUseException) - { - //failed to open the service so must be a second instance - NamedPipeHost.Abort(); - NamedPipeHost = null; - return false; - } - catch (CommunicationObjectFaultedException) - { - //failed to open the service so must be a second instance - NamedPipeHost.Abort(); - NamedPipeHost = null; - return false; - } - catch (Exception) - { - // an uknown error occurred maybe invalid endpoint? - return false; - } - } - - /// - /// Ensures that the named pipe service host is closed on the application exit - /// - private static void OnAppExit(object sender, EventArgs e) - { - if (NamedPipeHost != null) - { - NamedPipeHost.Close(); - NamedPipeHost = null; - } - } - - /// - /// ensure host is disposed of if there is an unhandled exception - /// - private static void OnUnhandledException(object sender, UnhandledExceptionEventArgs e) - { - if (NamedPipeHost != null) - { - if (NamedPipeHost.State == CommunicationState.Faulted) - NamedPipeHost.Abort(); - else - NamedPipeHost.Close(); - NamedPipeHost = null; - } - } - - /// - /// Notifies the main instance that this instance is attempting to start up. - /// - /// The application's unique identifier. - public static void NotifyFirstInstance(string uid) - { - //create channel with first instance interface - using (ChannelFactory factory = new ChannelFactory( - new NetNamedPipeBinding(), new EndpointAddress("net.pipe://localhost/7thHeavenApp/" + uid))) - { - ISingleInstance singleInstanceInterface = factory.CreateChannel(); - //pass the command-line args to the first interface - singleInstanceInterface.PassStartupArgs(Environment.GetCommandLineArgs()); - } - } - - /// - /// Activate the first instance's main window - /// - private static void ActivateMainWindow(object sender, SecondInstanceStartedEventArgs e) - { - //activate first instance window - Application.Current.Dispatcher.Invoke(new Action(() => - { - //check if window state is minimized then restore - if (Application.Current.MainWindow.WindowState == WindowState.Minimized) - Application.Current.MainWindow.WindowState = WindowState.Normal; //despite saying Normal this actually will restore - Application.Current.MainWindow.Activate(); //now activate window - })); - } - } -} +//TODO: Add support for .NET 6.0 + +//using System; +//using System.Collections.Generic; +//using System.Linq; +//using System.ServiceModel; +//using System.Text; +//using System.Threading.Tasks; +//using System.Windows; + +///// +///// Implementation reference: https://www.codeproject.com/Articles/1186738/A-Robust-and-Elegant-Single-Instance-WPF-Applicati +///// +//namespace SeventhHeaven.Classes.WCF +//{ +// /// The WCF interface for passing the startup parameters +// [ServiceContract (ProtectionLevel = System.Net.Security.ProtectionLevel.None)] +// public interface ISingleInstance +// { +// /// +// /// Notifies the first instance that another instance of the application attempted to start. +// /// +// /// The other instance's command-line arguments. +// [OperationContract] +// void PassStartupArgs(string[] args); +// } + +// /// +// /// Event declaration for the startup of a second instance +// /// +// public class SecondInstanceStartedEventArgs : EventArgs +// { +// /// +// /// The event method declaration for the startup of a second instance +// /// +// /// The other instance's command-line arguments. +// public SecondInstanceStartedEventArgs(string[] args) +// { Args = args; } + +// /// +// /// Property containing the second instance's command-line arguments +// /// +// public string[] Args { get; set; } +// } + +// /// +// /// A class to allow for a single-instance of an application. +// /// +// public class SingleInstance : ISingleInstance +// { +// /// +// /// Is raised when another instance attempts to start up. +// /// +// public static event EventHandler OnSecondInstanceStarted; + +// private static ServiceHost NamedPipeHost = null; + +// /// +// /// Interface: Notifies the first instance that another instance of the application attempted to start. +// /// +// /// The other instance's command-line arguments. +// public void PassStartupArgs(string[] args) +// { +// //check if an event is registered for when a second instance is started +// OnSecondInstanceStarted?.Invoke(this, new SecondInstanceStartedEventArgs(args)); +// } + +// /// +// /// Checks to see if this instance is the first instance of this application on the local machine. If it is not, this method will +// /// send the first instance this instance's command-line arguments. +// /// +// /// The application's unique identifier. +// /// Should the main window become active on a second instance launch +// /// True if this instance is the first instance. +// public static bool IsFirstInstance(string uid, bool activatewindow) +// { +// //attempt to open the service, should succeed if this is the first instance +// if (OpenServiceHost(uid)) +// { +// if (activatewindow) +// OnSecondInstanceStarted += ActivateMainWindow; +// return true; +// } + +// return false; +// } + +// /// +// /// Attempts to create the named pipe service. +// /// +// /// The application's unique identifier. +// /// True if the service was published successfully. +// private static bool OpenServiceHost(string uid) +// { +// try +// { +// //hook the application exit event to avoid race condition when messages flow while the application is disposing of the channel during shutdown +// Application.Current.Exit += new ExitEventHandler(OnAppExit); + +// //for any unhandled exception we need to ensure NamedPipeHost is disposed +// AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(OnUnhandledException); + + +// //setup the WCF service using a NamedPipe +// NamedPipeHost = new ServiceHost(typeof(SingleInstance), new Uri("net.pipe://localhost/7thHeavenApp")); +// NamedPipeHost.AddServiceEndpoint(typeof(ISingleInstance), new NetNamedPipeBinding(), uid); + +// //if the service is already open (i.e. another instance is already running) this will cause an exception +// NamedPipeHost.Open(); + +// //success and we are first instance +// return true; +// } +// catch (AddressAlreadyInUseException) +// { +// //failed to open the service so must be a second instance +// NamedPipeHost.Abort(); +// NamedPipeHost = null; +// return false; +// } +// catch (CommunicationObjectFaultedException) +// { +// //failed to open the service so must be a second instance +// NamedPipeHost.Abort(); +// NamedPipeHost = null; +// return false; +// } +// catch (Exception) +// { +// // an uknown error occurred maybe invalid endpoint? +// return false; +// } +// } + +// /// +// /// Ensures that the named pipe service host is closed on the application exit +// /// +// private static void OnAppExit(object sender, EventArgs e) +// { +// if (NamedPipeHost != null) +// { +// NamedPipeHost.Close(); +// NamedPipeHost = null; +// } +// } + +// /// +// /// ensure host is disposed of if there is an unhandled exception +// /// +// private static void OnUnhandledException(object sender, UnhandledExceptionEventArgs e) +// { +// if (NamedPipeHost != null) +// { +// if (NamedPipeHost.State == CommunicationState.Faulted) +// NamedPipeHost.Abort(); +// else +// NamedPipeHost.Close(); +// NamedPipeHost = null; +// } +// } + +// /// +// /// Notifies the main instance that this instance is attempting to start up. +// /// +// /// The application's unique identifier. +// public static void NotifyFirstInstance(string uid) +// { +// //create channel with first instance interface +// using (ChannelFactory factory = new ChannelFactory( +// new NetNamedPipeBinding(), new EndpointAddress("net.pipe://localhost/7thHeavenApp/" + uid))) +// { +// ISingleInstance singleInstanceInterface = factory.CreateChannel(); +// //pass the command-line args to the first interface +// singleInstanceInterface.PassStartupArgs(Environment.GetCommandLineArgs()); +// } +// } + +// /// +// /// Activate the first instance's main window +// /// +// private static void ActivateMainWindow(object sender, SecondInstanceStartedEventArgs e) +// { +// //activate first instance window +// Application.Current.Dispatcher.Invoke(new Action(() => +// { +// //check if window state is minimized then restore +// if (Application.Current.MainWindow.WindowState == WindowState.Minimized) +// Application.Current.MainWindow.WindowState = WindowState.Normal; //despite saying Normal this actually will restore +// Application.Current.MainWindow.Activate(); //now activate window +// })); +// } +// } +//} diff --git a/SeventhHeavenUI/EasyHook.dll b/SeventhHeavenUI/EasyHook.dll deleted file mode 100644 index 68aedc18a8738bf806fa2490757be4430b358d1a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 61440 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 - - + - Debug - AnyCPU - {5F6C267D-0F93-4C71-BA58-0C1AD6242383} + net6.0-windows WinExe SeventhHeaven 7th Heaven - v4.8 - 512 - {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - 4 - true - true publish\ true Disk @@ -29,40 +19,25 @@ false false true - + false + true + true + true - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - .allowedextension - AnyCPU none - true - bin\Release\ - TRACE - prompt - 4 + CA1416 SeventhHeavenUI.App - app.manifest @@ -70,896 +45,546 @@ 7H.ico - true bin\x86\Debug\ - DEBUG;TRACE - full - x86 - 7.3 - prompt MinimumRecommendedRules.ruleset - true bin\x86\Release\ - TRACE - true - pdbonly - x86 - 7.3 - prompt MinimumRecommendedRules.ruleset - true - x64 bin\x64\Debug\ - x64 bin\x64\Release\ .\DS4WindowsCore.dll - - - - - - - - - - - - - - - - - - - - 4.0 - - - - - - MSBuild:Compile - Designer - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CatalogUserControl.xaml - - - CreateCatalogUserControl.xaml - - - CreateModUserControl.xaml - - - MegaLinkGenUserControl.xaml - - - MyModsUserControl.xaml - - - PatchIroAdvancedUserControl.xaml - - - PatchIroUserControl.xaml - - - UnpackIroUserControl.xaml - - - PackIroUserControl.xaml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CatalogCreationToolWindow.xaml - - - ChunkToolWindow.xaml - - - ConfigureGLWindow.xaml - - - ConfigureModWindow.xaml - - - ControlMappingWindow.xaml - - - GameLaunchSettingsWindow.xaml - - - GameLaunchWindow.xaml - - - ImportModWindow.xaml - - - AllowModToRunWindow.xaml - - - AboutWindow.xaml - - - MessageDialogWindow.xaml - - - IroCreateWindow.xaml - - - MovieImportWindow.xaml - - - OpenProfileWindow.xaml - - - SetLanguageWindow.xaml - - - ThemeSettingsWindow.xaml - - - UnhandledErrorWindow.xaml - - - - - - - - - - - - - - - - - - - - - - + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - - Always - - + + PreserveNewest - - - Designer + + PreserveNewest - - - Designer + + PreserveNewest - - + + PreserveNewest - - - Always - - + + PreserveNewest - - + + PreserveNewest - - - MSBuild:Compile - Designer - Always - - - MSBuild:Compile - Designer - Always - - - MSBuild:Compile - Designer - Always - - + + PreserveNewest - - + + PreserveNewest - - - + + PreserveNewest - - - MSBuild:Compile - Designer + + PreserveNewest - - - MSBuild:Compile - Designer - Always - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + + + PreserveNewest - + PreserveNewest - + PreserveNewest - - MSBuild:Compile - Designer - Always + + PreserveNewest - - Designer - MSBuild:Compile - - - Designer - MSBuild:Compile - - - Designer - MSBuild:Compile - - - Designer - MSBuild:Compile - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - Designer - MSBuild:Compile - - - Designer - MSBuild:Compile - - - Designer - MSBuild:Compile - - - Designer - MSBuild:Compile - - - Designer - MSBuild:Compile - - - Designer - MSBuild:Compile - - - Designer - MSBuild:Compile - - - Designer - MSBuild:Compile - - - Designer - MSBuild:Compile - - - Designer - MSBuild:Compile - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - Designer - MSBuild:Compile - - - Designer - MSBuild:Compile - - - MSBuild:Compile - Designer - - - App.xaml - Code - - - GeneralSettingsWindow.xaml - - - InputTextWindow.xaml - - - MainWindow.xaml - Code - - - Designer - MSBuild:Compile - - - Designer - MSBuild:Compile - - - Designer - MSBuild:Compile - - - Designer - MSBuild:Compile - - - Designer - MSBuild:Compile - - - Designer - MSBuild:Compile - - - Designer - MSBuild:Compile - - - Designer - MSBuild:Compile - - - Designer - MSBuild:Compile - - - - - - True - True - Resources.resx - - - True - Settings.settings - True - - + PreserveNewest - - Always + + PreserveNewest - + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + Always - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - - ResXFileCodeGenerator - Resources.Designer.cs - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - - - - Always - - - - Always - - SettingsSingleFileGenerator - Settings.Designer.cs + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest - - {cd2f4ccb-4735-4dee-8867-b0ed2ca04303} - 7thHeaven.Code - - - {f80db232-4102-4249-8b31-2ddd5c7fa404} - 7thWrapperLib - - - {03eede27-0abb-4cda-a7f2-f746e2083202} - TurBoLog - + + + + - - Designer - Always - PreserveNewest @@ -977,18 +602,13 @@ - - 2.7.7097 - 4.4.0 1.10.2 - - 1.1.0 - + 2.1.0 @@ -1001,25 +621,19 @@ 5.0.4 - - 0.10.4 - - - 4.2.0 - 4.2.0 - - 4.5.0 - + 2.0.3 + - - - - + + CA1416 + + + \ No newline at end of file diff --git a/SeventhHeavenUI/ViewModels/CatalogViewModel.cs b/SeventhHeavenUI/ViewModels/CatalogViewModel.cs index d5eb8744..bd99b3c0 100644 --- a/SeventhHeavenUI/ViewModels/CatalogViewModel.cs +++ b/SeventhHeavenUI/ViewModels/CatalogViewModel.cs @@ -857,7 +857,10 @@ public void Download(IEnumerable links, DownloadItem downloadInfo) if (dialogViewModel?.Result == MessageBoxResult.Yes) { Sys.Message(new WMessage($"{ResourceHelper.Get(StringKey.OpeningExternalUrlInBrowserFor)} {downloadInfo.ItemName} - {location}")); - ProcessStartInfo startInfo = new ProcessStartInfo(location); + ProcessStartInfo startInfo = new ProcessStartInfo(location) + { + UseShellExecute = true, + }; Process.Start(startInfo); } diff --git a/SeventhHeavenUI/ViewModels/ControlMappingViewModel.cs b/SeventhHeavenUI/ViewModels/ControlMappingViewModel.cs index 663c7c42..88d3ecf7 100644 --- a/SeventhHeavenUI/ViewModels/ControlMappingViewModel.cs +++ b/SeventhHeavenUI/ViewModels/ControlMappingViewModel.cs @@ -1368,7 +1368,8 @@ internal void LaunchControlPanelGameControllersWindow() { Process.Start(new ProcessStartInfo("control.exe") { - Arguments = "joy.cpl" + Arguments = "joy.cpl", + UseShellExecute = true, }); } @@ -1418,6 +1419,7 @@ private Task InstallDriverAsync() { ProcessStartInfo startInfo = new ProcessStartInfo(Sys.PathToScpDriverExe, "si") { + UseShellExecute = true, Verb = "runas", WorkingDirectory = Sys.PathToVBusDriver // ensure the working directory is where the .exe is located }; diff --git a/SeventhHeavenUI/ViewModels/GeneralSettingsViewModel.cs b/SeventhHeavenUI/ViewModels/GeneralSettingsViewModel.cs index 5206a831..b203b8d6 100644 --- a/SeventhHeavenUI/ViewModels/GeneralSettingsViewModel.cs +++ b/SeventhHeavenUI/ViewModels/GeneralSettingsViewModel.cs @@ -437,7 +437,7 @@ public static void AutoDetectSystemPaths(Settings settings) { Logger.Info("FF7 Exe path is empty or ff7.exe is missing. Auto detecting paths ..."); - string registry_path = @"HKEY_LOCAL_MACHINE\SOFTWARE\Square Soft, Inc.\Final Fantasy VII"; + string registry_path = $"{RegistryHelper.GetKeyPath(FF7RegKey.SquareSoftKeyPath)}\\Final Fantasy VII"; string ff7 = null; FF7Version foundVersion = FF7Version.Unknown; diff --git a/SeventhHeavenUI/ViewModels/MainWindowViewModel.cs b/SeventhHeavenUI/ViewModels/MainWindowViewModel.cs index fe8d21a1..1988a45c 100644 --- a/SeventhHeavenUI/ViewModels/MainWindowViewModel.cs +++ b/SeventhHeavenUI/ViewModels/MainWindowViewModel.cs @@ -1297,9 +1297,9 @@ internal void OpenPreviewModLink() try { - ProcessStartInfo startInfo = new ProcessStartInfo() + ProcessStartInfo startInfo = new ProcessStartInfo(PreviewModLink) { - FileName = PreviewModLink + UseShellExecute = true, }; Process.Start(startInfo); @@ -1321,9 +1321,9 @@ internal void OpenDonationModLink() try { - ProcessStartInfo startInfo = new ProcessStartInfo() + ProcessStartInfo startInfo = new ProcessStartInfo(PreviewDonationModLink) { - FileName = PreviewDonationModLink + UseShellExecute = true, }; Process.Start(startInfo); @@ -1398,18 +1398,11 @@ internal void OpenPreviewModReadMe() } } - if (pathToTempFile.EndsWith(".txt")) + ProcessStartInfo startInfo = new ProcessStartInfo(pathToTempFile) { - Process.Start("notepad.exe", pathToTempFile); - } - else - { - ProcessStartInfo startInfo = new ProcessStartInfo() - { - FileName = pathToTempFile - }; - Process.Start(startInfo); - } + UseShellExecute = true, + }; + Process.Start(startInfo); } catch (Exception e) { @@ -1597,9 +1590,9 @@ internal void LaunchHelpPage() return; } - ProcessStartInfo startInfo = new ProcessStartInfo() + ProcessStartInfo startInfo = new ProcessStartInfo(helpFile) { - FileName = helpFile + UseShellExecute = true, }; Process.Start(startInfo); diff --git a/SeventhHeavenUI/ViewModels/OpenProfileViewModel.cs b/SeventhHeavenUI/ViewModels/OpenProfileViewModel.cs index fd35fd71..ae585492 100644 --- a/SeventhHeavenUI/ViewModels/OpenProfileViewModel.cs +++ b/SeventhHeavenUI/ViewModels/OpenProfileViewModel.cs @@ -208,7 +208,11 @@ public void ViewProfileDetails(string name) File.WriteAllLines(tempFile, profileDetails); - Process.Start("notepad.exe", tempFile); + ProcessStartInfo startInfo = new ProcessStartInfo(tempFile) + { + UseShellExecute = true, + }; + Process.Start(startInfo); } catch (Exception e) { diff --git a/SeventhHeavenUI/Windows/AboutWindow.xaml.cs b/SeventhHeavenUI/Windows/AboutWindow.xaml.cs index 6ec28c19..484479d8 100644 --- a/SeventhHeavenUI/Windows/AboutWindow.xaml.cs +++ b/SeventhHeavenUI/Windows/AboutWindow.xaml.cs @@ -27,7 +27,11 @@ private void btnClose_Click(object sender, RoutedEventArgs e) private void Hyperlink_RequestNavigate(object sender, RequestNavigateEventArgs e) { - Process.Start(new ProcessStartInfo(e.Uri.AbsoluteUri)); + ProcessStartInfo startInfo = new ProcessStartInfo(e.Uri.AbsoluteUri) + { + UseShellExecute = true, + }; + Process.Start(startInfo); e.Handled = true; } diff --git a/SeventhHeavenUI/Windows/MainWindow.xaml.cs b/SeventhHeavenUI/Windows/MainWindow.xaml.cs index 89a9d98b..2d744273 100644 --- a/SeventhHeavenUI/Windows/MainWindow.xaml.cs +++ b/SeventhHeavenUI/Windows/MainWindow.xaml.cs @@ -7,7 +7,7 @@ using SeventhHeavenUI.ViewModels; using System; using System.ComponentModel; -using System.Diagnostics; +using System.Diagnostics; using System.Linq; using System.Windows; using System.Windows.Controls; @@ -97,10 +97,10 @@ private void SetWindowSizeAndLocation() private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e) { - if (GameLauncher.IsFF7Running()) - { - e.Cancel = true; - return; + if (GameLauncher.IsFF7Running()) + { + e.Cancel = true; + return; } if (ViewModel.CatalogMods.DownloadList.Count > 0) @@ -342,9 +342,13 @@ private void btnOpenAppLog_Click(object sender, RoutedEventArgs e) Sys.OpenAppLog(); } - private void btnOpenAppLog_PreviewMouseRightButtonUp(object sender, MouseButtonEventArgs e) - { - Process.Start(Sys.PathToCrashReports); + private void btnOpenAppLog_PreviewMouseRightButtonUp(object sender, MouseButtonEventArgs e) + { + ProcessStartInfo startInfo = new ProcessStartInfo(Sys.PathToCrashReports) + { + UseShellExecute = true, + }; + Process.Start(startInfo); } private void MyModsTabItem_MouseRightButtonUp(object sender, MouseButtonEventArgs e) @@ -424,11 +428,15 @@ private void menuItemMovieImport_Click(object sender, RoutedEventArgs e) private void menuItemAbout_Click(object sender, RoutedEventArgs e) { new AboutWindow().ShowDialog(); - } - - private void SupportUkraine_MouseUp(object sender, MouseButtonEventArgs e) - { - System.Diagnostics.Process.Start("https://war.ukraine.ua/support-ukraine/"); - } + } + + private void SupportUkraine_MouseUp(object sender, MouseButtonEventArgs e) + { + ProcessStartInfo startInfo = new ProcessStartInfo("https://war.ukraine.ua/support-ukraine/") + { + UseShellExecute = true, + }; + Process.Start(startInfo); + } } } diff --git a/SeventhHeavenUI/app.manifest b/SeventhHeavenUI/app.manifest index d72e7501..b64f6271 100644 --- a/SeventhHeavenUI/app.manifest +++ b/SeventhHeavenUI/app.manifest @@ -3,21 +3,6 @@ - - - - diff --git a/TestPlugin/TestPlugin.csproj b/TestPlugin/TestPlugin.csproj index 73c55ca9..4329735e 100644 --- a/TestPlugin/TestPlugin.csproj +++ b/TestPlugin/TestPlugin.csproj @@ -1,74 +1,28 @@ - - - + - Debug - AnyCPU - {A64400A1-1E22-4F22-884E-568A776141C5} + net6.0-windows Library - Properties - TestPlugin - TestPlugin - v4.8 - 512 - 1.56 - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - AnyCPU + false none - true - bin\Release\ - TRACE - prompt - 4 x86 - - - true + CA1416 - x64 bin\x64\Debug\ - x64 bin\x64\Release\ + + CA1416 + - - - - - - - - - - - + - - {f80db232-4102-4249-8b31-2ddd5c7fa404} - 7thWrapperLib - + - - \ No newline at end of file diff --git a/TurBoLog/TurBoLog.csproj b/TurBoLog/TurBoLog.csproj index 6594e14a..0281c678 100644 --- a/TurBoLog/TurBoLog.csproj +++ b/TurBoLog/TurBoLog.csproj @@ -1,124 +1,55 @@ - - + + net6.0-windows Local - 8.0.30703 - 2.0 - {03EEDE27-0ABB-4CDA-A7F2-F746E2083202} - Debug - AnyCPU debug.ico - TurBoLog JScript Grid IE50 false WinExe - TurBoLog OnBuildSuccess TurBoLog.UI.FMonitor - - - - - 0.0 - v4.8 - + false + True + true - bin\Debug\ - false 285212672 - false - DEBUG;TRACE - true 4096 false - - false + CA1416 false false - false - 4 - full - prompt - false - bin\Release\ - false 285212672 - false - TRACE - - false 4096 false - - true + CA1416 false false - false - 4 none - prompt - false - - - true - x64 bin\x64\Debug\ - x64 bin\x64\Release\ - + System - + System.Drawing - - System.Windows.Forms - - - - - Code - - - Form - - - Form - - - FMonitor.cs - - - FPrompt.cs - - - - - - - - - - - - - + \ No newline at end of file diff --git a/TurBoLog/UI/FMonitor.cs b/TurBoLog/UI/FMonitor.cs index ee5c45d7..7f812914 100644 --- a/TurBoLog/UI/FMonitor.cs +++ b/TurBoLog/UI/FMonitor.cs @@ -18,15 +18,14 @@ public class FMonitor : System.Windows.Forms.Form private System.Windows.Forms.ColumnHeader colTimeStamp; private System.Windows.Forms.ColumnHeader colPid; private System.Windows.Forms.ColumnHeader colText; - private System.Windows.Forms.MainMenu mnuMain; - private System.Windows.Forms.MenuItem mnuFile; - private System.Windows.Forms.MenuItem mnuFileExit; + private System.Windows.Forms.MenuStrip mnuMain; + private System.Windows.Forms.ToolStripMenuItem mnuFile; + private System.Windows.Forms.ToolStripMenuItem mnuFileExit; private System.Windows.Forms.ColumnHeader colProcessName; - private System.Windows.Forms.MenuItem mnuDebug; - private System.Windows.Forms.MenuItem mnuDebugCapture; - private MenuItem mnuFileExport; - private MenuItem menuItem2; - private IContainer components; + private System.Windows.Forms.ToolStripMenuItem mnuDebug; + private System.Windows.Forms.ToolStripMenuItem mnuDebugCapture; + private ToolStripMenuItem mnuFileExport; + private ToolStripSeparator menuItem2; public FMonitor() { @@ -105,13 +104,6 @@ public static void Main(string[] args) { /// protected override void Dispose( bool disposing ) { - if( disposing ) - { - if(components != null) - { - components.Dispose(); - } - } base.Dispose( disposing ); DebugMonitor.Stop(); @@ -124,39 +116,40 @@ protected override void Dispose( bool disposing ) /// private void InitializeComponent() { - this.components = new System.ComponentModel.Container(); System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FMonitor)); this.lsvOutput = new System.Windows.Forms.ListView(); - this.colTimeStamp = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); - this.colPid = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); - this.colProcessName = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); - this.colText = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); - this.mnuMain = new System.Windows.Forms.MainMenu(this.components); - this.mnuFile = new System.Windows.Forms.MenuItem(); - this.mnuFileExport = new System.Windows.Forms.MenuItem(); - this.menuItem2 = new System.Windows.Forms.MenuItem(); - this.mnuFileExit = new System.Windows.Forms.MenuItem(); - this.mnuDebug = new System.Windows.Forms.MenuItem(); - this.mnuDebugCapture = new System.Windows.Forms.MenuItem(); + this.colTimeStamp = new System.Windows.Forms.ColumnHeader(); + this.colPid = new System.Windows.Forms.ColumnHeader(); + this.colProcessName = new System.Windows.Forms.ColumnHeader(); + this.colText = new System.Windows.Forms.ColumnHeader(); + this.mnuMain = new System.Windows.Forms.MenuStrip(); + this.mnuFile = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuFileExport = new System.Windows.Forms.ToolStripMenuItem(); + this.menuItem2 = new System.Windows.Forms.ToolStripSeparator(); + this.mnuFileExit = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuDebug = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuDebugCapture = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuMain.SuspendLayout(); this.SuspendLayout(); // // lsvOutput // + this.lsvOutput.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.lsvOutput.BorderStyle = System.Windows.Forms.BorderStyle.None; this.lsvOutput.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { this.colTimeStamp, this.colPid, this.colProcessName, this.colText}); - this.lsvOutput.Dock = System.Windows.Forms.DockStyle.Fill; - this.lsvOutput.Font = new System.Drawing.Font("Courier New", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.lsvOutput.Font = new System.Drawing.Font("Courier New", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point); this.lsvOutput.FullRowSelect = true; this.lsvOutput.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.Nonclickable; - this.lsvOutput.HideSelection = false; this.lsvOutput.HoverSelection = true; - this.lsvOutput.Location = new System.Drawing.Point(0, 0); + this.lsvOutput.Location = new System.Drawing.Point(0, 30); this.lsvOutput.Name = "lsvOutput"; - this.lsvOutput.Size = new System.Drawing.Size(880, 382); + this.lsvOutput.Size = new System.Drawing.Size(880, 352); this.lsvOutput.TabIndex = 0; this.lsvOutput.UseCompatibleStateImageBehavior = false; this.lsvOutput.View = System.Windows.Forms.View.Details; @@ -184,63 +177,83 @@ private void InitializeComponent() // // mnuMain // - this.mnuMain.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { + this.mnuMain.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { this.mnuFile, this.mnuDebug}); + this.mnuMain.Location = new System.Drawing.Point(0, 0); + this.mnuMain.Name = "mnuMain"; + this.mnuMain.Size = new System.Drawing.Size(880, 24); + this.mnuMain.TabIndex = 0; // // mnuFile // - this.mnuFile.Index = 0; - this.mnuFile.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { + this.mnuFile.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.mnuFileExport, this.menuItem2, this.mnuFileExit}); + this.mnuFile.MergeIndex = 0; + this.mnuFile.Name = "mnuFile"; + this.mnuFile.Size = new System.Drawing.Size(37, 20); this.mnuFile.Text = "&File"; // // mnuFileExport // - this.mnuFileExport.Index = 0; + this.mnuFileExport.MergeIndex = 0; + this.mnuFileExport.Name = "mnuFileExport"; + this.mnuFileExport.Size = new System.Drawing.Size(108, 22); this.mnuFileExport.Text = "Export"; this.mnuFileExport.Click += new System.EventHandler(this.mnuFileExport_Click); // // menuItem2 // - this.menuItem2.Index = 1; - this.menuItem2.Text = "-"; + this.menuItem2.MergeIndex = 1; + this.menuItem2.Name = "menuItem2"; + this.menuItem2.Size = new System.Drawing.Size(105, 6); // // mnuFileExit // - this.mnuFileExit.Index = 2; + this.mnuFileExit.MergeIndex = 2; + this.mnuFileExit.Name = "mnuFileExit"; + this.mnuFileExit.Size = new System.Drawing.Size(108, 22); this.mnuFileExit.Text = "&Exit"; this.mnuFileExit.Click += new System.EventHandler(this.mnuFileExit_Click); // // mnuDebug // - this.mnuDebug.Index = 1; - this.mnuDebug.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { + this.mnuDebug.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.mnuDebugCapture}); + this.mnuDebug.MergeIndex = 1; + this.mnuDebug.Name = "mnuDebug"; + this.mnuDebug.Size = new System.Drawing.Size(54, 20); this.mnuDebug.Text = "&Debug"; // // mnuDebugCapture // this.mnuDebugCapture.Checked = true; - this.mnuDebugCapture.Index = 0; + this.mnuDebugCapture.CheckState = System.Windows.Forms.CheckState.Checked; + this.mnuDebugCapture.MergeIndex = 0; + this.mnuDebugCapture.Name = "mnuDebugCapture"; + this.mnuDebugCapture.Size = new System.Drawing.Size(116, 22); this.mnuDebugCapture.Text = "&Capture"; this.mnuDebugCapture.Click += new System.EventHandler(this.mnuDebugCapture_Click); // // FMonitor // - this.AutoScaleBaseSize = new System.Drawing.Size(5, 13); + this.AutoScaleBaseSize = new System.Drawing.Size(6, 16); this.AutoValidate = System.Windows.Forms.AutoValidate.EnableAllowFocusChange; this.ClientSize = new System.Drawing.Size(880, 382); + this.Controls.Add(this.mnuMain); this.Controls.Add(this.lsvOutput); this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); - this.Menu = this.mnuMain; + this.MainMenuStrip = this.mnuMain; this.Name = "FMonitor"; this.Text = "FF7 Debug Log"; this.SizeChanged += new System.EventHandler(this.FMonitor_SizeChanged); this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.FMonitor_KeyDown); + this.mnuMain.ResumeLayout(false); + this.mnuMain.PerformLayout(); this.ResumeLayout(false); + this.PerformLayout(); } #endregion diff --git a/TurBoLog/UI/FMonitor.resx b/TurBoLog/UI/FMonitor.resx index e0c00fff..268aea8e 100644 --- a/TurBoLog/UI/FMonitor.resx +++ b/TurBoLog/UI/FMonitor.resx @@ -1,64 +1,4 @@ - - diff --git a/TurBoLog/app.config b/TurBoLog/app.config deleted file mode 100644 index 2c0f5590..00000000 --- a/TurBoLog/app.config +++ /dev/null @@ -1,3 +0,0 @@ - - -