commit 6fb9b12a1c8a86485b2e58872df08f32ec03be18 Author: nastys <@> Date: Sat Jul 6 22:51:56 2019 +0200 First commit diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..1ff0c42 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,63 @@ +############################################################################### +# Set default behavior to automatically normalize line endings. +############################################################################### +* text=auto + +############################################################################### +# Set default behavior for command prompt diff. +# +# This is need for earlier builds of msysgit that does not have it on by +# default for csharp files. +# Note: This is only used by command line +############################################################################### +#*.cs diff=csharp + +############################################################################### +# Set the merge driver for project and solution files +# +# Merging from the command prompt will add diff markers to the files if there +# are conflicts (Merging from VS is not affected by the settings below, in VS +# the diff markers are never inserted). Diff markers may cause the following +# file extensions to fail to load in VS. An alternative would be to treat +# these files as binary and thus will always conflict and require user +# intervention with every merge. To do so, just uncomment the entries below +############################################################################### +#*.sln merge=binary +#*.csproj merge=binary +#*.vbproj merge=binary +#*.vcxproj merge=binary +#*.vcproj merge=binary +#*.dbproj merge=binary +#*.fsproj merge=binary +#*.lsproj merge=binary +#*.wixproj merge=binary +#*.modelproj merge=binary +#*.sqlproj merge=binary +#*.wwaproj merge=binary + +############################################################################### +# behavior for image files +# +# image files are treated as binary by default. +############################################################################### +#*.jpg binary +#*.png binary +#*.gif binary + +############################################################################### +# diff behavior for common document formats +# +# Convert binary document formats to text before diffing them. This feature +# is only available from the command line. Turn it on by uncommenting the +# entries below. +############################################################################### +#*.doc diff=astextplain +#*.DOC diff=astextplain +#*.docx diff=astextplain +#*.DOCX diff=astextplain +#*.dot diff=astextplain +#*.DOT diff=astextplain +#*.pdf diff=astextplain +#*.PDF diff=astextplain +#*.rtf diff=astextplain +#*.RTF diff=astextplain diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..59cd594 --- /dev/null +++ b/.gitignore @@ -0,0 +1,344 @@ +## 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 + +# 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/ + +# 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 + +# 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 + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# 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 +# 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 + +# 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 +*- Backup*.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/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# 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 + +# Exclude data folder +!data/* +!data/debug/* \ No newline at end of file diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..4fb8b5e --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "dependencies\\MemoryModule"] + path = dependencies\\MemoryModule + url = https://github.com/fancycode/MemoryModule.git diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..e0ca0e8 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,76 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, sex characteristics, gender identity and expression, +level of experience, education, socio-economic status, nationality, personal +appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or + advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at hatsune@miku.cl. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see +https://www.contributor-covenant.org/faq diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..7141856 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,3 @@ +Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change. + +Please make sure to update tests as appropriate. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..e0eb4dd --- /dev/null +++ b/LICENSE @@ -0,0 +1,25 @@ +MIT License + +Copyright (c) Project DIVA Modding 2nd Community +Copyright (c) samyuu (nop) +Copyright (c) ThirteenAG +Copyright (c) Microsoft Corporation +Copyright (c) FreeGLUT team + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/PD-Loader.sln b/PD-Loader.sln new file mode 100644 index 0000000..9f87970 --- /dev/null +++ b/PD-Loader.sln @@ -0,0 +1,49 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.28922.388 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PD-Loader", "source\PD-Loader\PD-Loader.vcxproj", "{CA479467-D518-46A2-AC86-3098ADA99FE5}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TLAC", "source\plugins\TLAC\TLAC.vcxproj", "{2B5533BB-04A1-424F-9BCA-1CA963B46B7F}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Patches", "source\plugins\Patches\Patches.vcxproj", "{3FD6ACA9-E613-4FD6-BDA2-55A91C2CF65C}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Launcher", "source\plugins\Launcher\Launcher.vcxproj", "{60D5E9F4-335F-402B-9A07-D78674DFFC9B}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Render", "source\plugins\Render\Render.vcxproj", "{89F87459-768F-4638-9267-0F90CD74452D}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {CA479467-D518-46A2-AC86-3098ADA99FE5}.Debug|x64.ActiveCfg = Debug|x64 + {CA479467-D518-46A2-AC86-3098ADA99FE5}.Debug|x64.Build.0 = Debug|x64 + {CA479467-D518-46A2-AC86-3098ADA99FE5}.Release|x64.ActiveCfg = Release|x64 + {CA479467-D518-46A2-AC86-3098ADA99FE5}.Release|x64.Build.0 = Release|x64 + {2B5533BB-04A1-424F-9BCA-1CA963B46B7F}.Debug|x64.ActiveCfg = Debug|x64 + {2B5533BB-04A1-424F-9BCA-1CA963B46B7F}.Debug|x64.Build.0 = Debug|x64 + {2B5533BB-04A1-424F-9BCA-1CA963B46B7F}.Release|x64.ActiveCfg = Release|x64 + {2B5533BB-04A1-424F-9BCA-1CA963B46B7F}.Release|x64.Build.0 = Release|x64 + {3FD6ACA9-E613-4FD6-BDA2-55A91C2CF65C}.Debug|x64.ActiveCfg = Debug|x64 + {3FD6ACA9-E613-4FD6-BDA2-55A91C2CF65C}.Debug|x64.Build.0 = Debug|x64 + {3FD6ACA9-E613-4FD6-BDA2-55A91C2CF65C}.Release|x64.ActiveCfg = Release|x64 + {3FD6ACA9-E613-4FD6-BDA2-55A91C2CF65C}.Release|x64.Build.0 = Release|x64 + {60D5E9F4-335F-402B-9A07-D78674DFFC9B}.Debug|x64.ActiveCfg = Debug|x64 + {60D5E9F4-335F-402B-9A07-D78674DFFC9B}.Debug|x64.Build.0 = Debug|x64 + {60D5E9F4-335F-402B-9A07-D78674DFFC9B}.Release|x64.ActiveCfg = Release|x64 + {60D5E9F4-335F-402B-9A07-D78674DFFC9B}.Release|x64.Build.0 = Release|x64 + {89F87459-768F-4638-9267-0F90CD74452D}.Debug|x64.ActiveCfg = Debug|x64 + {89F87459-768F-4638-9267-0F90CD74452D}.Debug|x64.Build.0 = Debug|x64 + {89F87459-768F-4638-9267-0F90CD74452D}.Release|x64.ActiveCfg = Release|x64 + {89F87459-768F-4638-9267-0F90CD74452D}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {F5AD7464-338F-4150-9DDC-85AD115BA616} + EndGlobalSection +EndGlobal diff --git a/README.md b/README.md new file mode 100644 index 0000000..b18b8ce --- /dev/null +++ b/README.md @@ -0,0 +1,106 @@ +# PD Loader + +**PD Loader** lets you load plugins into **PDAFT** by placing them into a **"plugins"** folder, it includes useful ones such as input emulation and a graphical launcher. + +## License and Legal + +The code included in this repository is intended for the solely usage of investigative reasons for the **PDAFT** videogame.\ +Based on your current jurisdiction, it may be illegal to tamper with an arcade cabinet or its hardware and so it would be the usage of this software.\ +Protection circumvention is not provided by this software.\ +Additionally, a MIT License is provided. + +## Installation + +* Have a **full clean** game version **"7.10"** decompressed without any modifications. +* Download the latest release of **PD Loader**. +* Decompress everything into the **root folder** of the game. + +* Install the official **"mdata"**. +**M215** to **M270** are supported, custom ones are also supported. + +To install them you have to drop each **"mdata"** update decompressed into the **"mdata\"** folder in the root folder of the game (create it if it doesn't exists). + +**e.g.** For all the official **"mdata"** you should have the following folders inside: + * M215 + * M220 + * M230 + * M240 + * M250 + * M260 + * M270 + +Files required to make **"mdata"** available in the game are included (**"ram\databank\PvList*"** files). + +## Usage + +**IMPORTANT:** Do *not* edit any of the *.ini* configuration files with Notepad. Use [Notepad++](https://notepad-plus-plus.org/download) (or WordPad) with UTF-8 encoding instead, otherwise your game may crash. + +Use **"plugins\components.ini"** to enable or disable TLAC components.\ +Use **"plugins\config.ini"** to enable or disable plugins loading along other useful settings.\ +Use **"plugins\keyconfig.ini"** to change your game keybinds.\ +Use **"plugins\playerdata.ini"** to change your playername and other player customization. + +Place any **.dva** plugin you want to use inside the **"plugins"** folder.\ + +Launch **"diva.exe"**. +Use **"--launch"** argument to bypass the launcher and to be able to use other arguments. + +## Screenshots + +Your **root** game folder should look like this: + +![root game folder](https://i.imgur.com/c57lQPH.png) + +Your **"mdata"** folder should look like this: + +![mdata folder](https://i.imgur.com/2jUjosM.png) + +## Contributing + +Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change. + +Please make sure to update tests as appropriate. + +## License + +MIT (see "LICENSE"). + +## Third party plugins + +[DiscordDiva](https://github.com/bela333/DiscordDiva) - Discord integration by **bela333**.\ +[DivaSound](https://github.com/somewhatlurker/DivaSound) - Replacement audio output by **somewhatlurker**, this plugin is a must if you have audio issues or no audio at all. + +## FAQ + +Only **NVIDIA** videocards are supported for now, AMD and Intel GPU support is possible but further research needs to be done. + +**Linux** can run the game with **Wine**, for audio you must use [DivaSound](https://github.com/somewhatlurker/DivaSound).\ +WMV movie videos are not supported and it is recommended to set **No_Movies** to **1** to prevent crashes when the game attempts to load a movie. + +**Windows 7** is supported except for WMV movie videos. + +Previous versions used **dinput8.dll** in the game root folder, if you still have it you must delete it. + +Always use the original game executable. + +Special thanks to **Brolijah**, **Skyth**, **korenkonder**, **Samyuu**, **somewhatlurker**, **Cainan**, **crash5band**, **ano**, **lybxlpsv** and everyone else who helped at the [Project DIVA Modding 2nd Community Discord](https://discord.gg/cvBVGDZ), come join us if you want to help us to improve this project. + +## Developer FAQ + +To enable logs and dumps create a folder called "logs" in the root folder, each dump size is 200MB so watch your disk space. + +This is a stripdown of [**ThirteenAG's** Ultimate ASI Loader v4.47](https://github.com/ThirteenAG/Ultimate-ASI-Loader/) for **"dnsapi.dll"** usage only. + +Solution for **Visual Studio 2019**.\ +Requires platform toolset **v142**.\ +Requires **Windows 10 SDK (10.0.17763.0)**. + +Requires **Desktop development with C++**.\ +Requires **Windows Universal CRT SDK**. + +Requires **.NET desktop development** (Launcher plugin).\ +Requires **.NET Framework 4 targeting pack** (Launcher plugin).\ +Requires **C++/CLI support for v142 build tools (14.21)** (Launcher plugin) + +Requires [**Microsoft Research Detours Package**](https://github.com/microsoft/Detours) (included in the solution).\ +Requires [**FreeGLUT 3.0.0 PD**](https://github.com/Rayduxz/FreeGLUT) (included in the solution). diff --git a/data/glut32.dll b/data/glut32.dll new file mode 100644 index 0000000..f0fdac1 Binary files /dev/null and b/data/glut32.dll differ diff --git a/data/plugins/components.ini b/data/plugins/components.ini new file mode 100644 index 0000000..beaf33b --- /dev/null +++ b/data/plugins/components.ini @@ -0,0 +1,48 @@ +# components.ini +# defines which components will be enabled + +# emulates game input through mouse and keyboard. +# controls can be set inside the keyconfig.ini file. +input_emulator = true +touch_slider_emulator = true +touch_panel_emulator = true + +# freezes the PV select timer at 39 seconds. +sys_timer = true + +# loads user defined const values from the playerdata.ini config +# and writes them to the game's PlayerData struct. +# required for modules selection +player_data_manager = true + +# adjusts 2D and 3D animations to retain their original speed at different frame rates. +# only use this component if you are running this game at higher than 60 FPS. +frame_rate_manager = false + +# sets an unlimited per session play count for the normal game mode. +# this means the player won't be sent back to the title screen after 2 plays. +stage_manager = true + +# skip unnecessary DATA_INITIALIZE loading time, speed up SYSTEM_STARTUP and skip the startup WARNING message. +# further improvements can be achieved by shortening the duration of the pv_999 script file as it's test loaded during the SYSTEM_STARTUP. +fast_loader = true + +# allows for a user controllable camera which can be toggled using F3, +# can be controlled using W/A/S/D to move, SPACE/CTRL to ascend/descend, Q/E to rotate, R/F to zoom in/out +# and holding SHIFT/ALT to speed up or slow down these controls. +camera_controller = false + +# scales the framebuffer so that it will fit the current screen or window +# required to set resolutions in config.ini +scale_component = true + +# lets the user change the current game state using the F4 - F8 keys, enable the dw_gui / DATA_TESTs +# and speed up 2D animations / menu navigation by holding SHIFT + TAB. +# enabling this component is not recommended for most users. +debug_component = false + +# enables the ability to set a cap on the framerate by setting FPS.Limit in config.ini +fps_limiter = false + +# allows the usage of hold transfer +target_inspector = true \ No newline at end of file diff --git a/data/plugins/config.ini b/data/plugins/config.ini new file mode 100644 index 0000000..963b7a7 --- /dev/null +++ b/data/plugins/config.ini @@ -0,0 +1,55 @@ +[Global] +# Load plugins including below options +Enable=1 + +[Launcher] +# Forces the launcher to be skipped, you can use the --launch parameter instead of this. +Skip=0 + +[Patches] +#Enable or disable mouse cursor +Cursor=1 +#Hide Freeplay text +Hide_Freeplay=0 +#Set the card reader and network status icons -- 0=default, 1=hidden, 2=error, 3=OK, 4=partial OK +Status_Icons=3 +#Hide the watermark that's usually shown in PV viewing mode +Hide_PV_Watermark=0 +#Permanently the photo controls during PV playback +No_PV_UI=0 +#Disables showing lyrics +No_Lyrics=0 +#Disables movies (enable this if the game hangs when loading certain PVs) +No_Movies=0 +#Hide the volume and SE control buttons +Hide_Volume=0 +#Disables the error banner +No_Error=1 + +[Graphics] +#FPS Limiter requires "fps_limiter" component to be enabled in "components.ini" +FPS.Limit=60 +# Temporal Anti-Aliasing +TAA=1 +# Morphological Anti-Aliasing +MLAA=1 + +[Resolution] +# 0 = Windowed, 1 = Borderless (Forced to desktop resolution), 2 = Fullscreen +Display=1 +Width=1280 +Height=720 +# Fullscreen only options +BitDepth=32 +RefreshRate=60 + +# Set a custom internal render resolution +# HD : 1280x720 +# FHD : 1920x1080 +# WQHD : 2560x1440 +# 4K UHD: 3840x2160 +# 5K UHD: 5120x2880 +# 8K UHD: 7680x4320 +r.Enable=0 +r.Width=3840 +r.Height=2160 diff --git a/data/plugins/keyconfig.ini b/data/plugins/keyconfig.ini new file mode 100644 index 0000000..f001c72 --- /dev/null +++ b/data/plugins/keyconfig.ini @@ -0,0 +1,78 @@ +# keyconfig.ini +# defines the input_emulator key bindings +# multiple keys are to be specified comma separated + +# Options: + +# allows the user to mouse wheel scroll through the PV selection list +mouse_scroll_pv_selection = true +# the interval it takes for the emulated slider touch points to move from one contact point to another. +# in most cases this does not need editing. +touch_slider_emulation_speed = 750.0 + +# JVS Buttons: +JVS_TEST = F1 +JVS_SERVICE = F2 + +JVS_START = Enter, Ds4_Options, XINPUT_START, MouseMiddle +JVS_TRIANGLE = W, I, Ds4_Triangle, Ds4_DPad_Up, Ds4_L_Trigger, Ds4_R_Trigger, XINPUT_Y, XINPUT_UP, XINPUT_LT, XINPUT_RT +JVS_SQUARE = A, J, Ds4_Square, Ds4_DPad_Left, Ds4_L_Trigger, Ds4_R_Trigger, XINPUT_X, XINPUT_LEFT, XINPUT_LT, XINPUT_RT, MouseX2 +JVS_CROSS = S, K, Ds4_Cross, Ds4_DPad_Down, Ds4_L_Trigger, Ds4_R_Trigger, XINPUT_A, XINPUT_DOWN, XINPUT_LT, XINPUT_RT, MouseX1 +JVS_CIRCLE = D, L, Ds4_Circle, Ds4_DPad_Right, Ds4_L_Trigger, Ds4_R_Trigger, XINPUT_B, XINPUT_RIGHT, XINPUT_LT, XINPUT_RT + +JVS_LEFT = Left, Up, Ds4_L1, Ds4_L_Stick_Up, Ds4_R_Stick_Up, XINPUT_LS +JVS_RIGHT = Right, Down, Ds4_R1, Ds4_L_Stick_Down, Ds4_R_Stick_Down, XINPUT_RS + +# Touch Slider: +LEFT_SIDE_SLIDE_LEFT = Q, Ds4_L_Stick_Left, XINPUT_LLEFT +LEFT_SIDE_SLIDE_RIGHT = E, Ds4_L_Stick_Right, XINPUT_LRIGHT + +RIGHT_SIDE_SLIDE_LEFT = U, Ds4_R_Stick_Left, XINPUT_RLEFT +RIGHT_SIDE_SLIDE_RIGHT = O, Ds4_R_Stick_Right, XINPUT_RRIGHT + +# --- KEYBOARD KEY NAMES: --- + +# - Standard Keys +# uppercase "A" to "Z", "0" to "9" + +# - NumPad Keys +# "NumPad0" to "NumPad9" +# "Plus", "Minus", "Divide", "Multiply" + +# - Function Keys +# "F1" to "F24" + +# - Shift Keys +# "LeftShift", "RightShift" + +# - Control Keys +# "LeftControl", "RightControl" + +# - Arrow Keys +# "Up", "Down", "Left", "Right" + +# - Special Keys +# "Enter", "Tab", "Backspace", "Insert", "Delete", "Home", "End", "PageUp", "PageDown", "Escape" + +# --- MOUSE BUTTON NAMES: --- +# "MouseLeft", "MouseMiddle", "MouseRight", "MouseX1", "MouseX2" + +# --- DUALSHOCK 4 BUTTON NAMES: --- + +# - Face Buttons +# "Ds4_Square", "Ds4_Cross", "Ds4_Circle", "Ds4_Triangle" + +# - Standard Buttons +# "Ds4_Share", "Ds4_Options", "Ds4_PS", "Ds4_Touch", "Ds4_L1", "Ds4_R1" + +# - D-Pad Directions +# "Ds4_DPad_Up", "Ds4_DPad_Right", "Ds4_DPad_Down", "Ds4_DPad_Left" + +# - Trigger Buttons +# "Ds4_L_Trigger", "Ds4_R_Trigger" + +# - Left Joystick +# "Ds4_L_Stick_Up", "Ds4_L_Stick_Right", "Ds4_L_Stick_Down", "Ds4_L_Stick_Left", "Ds4_L3" + +# - Right Joystick +# "Ds4_R_Stick_Up", "Ds4_R_Stick_Right", "Ds4_R_Stick_Down", "Ds4_R_Stick_Left", "Ds4_R3" \ No newline at end of file diff --git a/data/plugins/playerdata.ini b/data/plugins/playerdata.ini new file mode 100644 index 0000000..2115eeb --- /dev/null +++ b/data/plugins/playerdata.ini @@ -0,0 +1,33 @@ +# playerdata.ini +# defines constant values to be loaded into the PlayerData struct +# this file has to be encoded using utf-8 + +player_name = NO-NAME +level_name = 忘れないでね私の声を + +# IDs defined in rom/gm_plate_tbl.farc/gm_plate_id.bin +level_plate_id = 0 + +# IDs same as gam_skin%03d +skin_equip = 0 + +# IDs defined in rom/gm_btn_se_tbl.farc/gm_btn_se_id.bin +btn_se_equip = -1 + +# IDs defined in rom/gm_slide_se_tbl.farc/gm_slide_se_id.bin +slide_se_equip = -1 + +# IDs defined in rom/gm_chainslide_se_tbl.farc/gm_chainslide_se_id.bin +chainslide_se_equip = -1 + +# Show clear borders on Progress Bar for: +# Great +border_great = true +# Excellent +border_excellent = true + +# Allows module selection (WARNING: It may cause lag in the main menu and other places) +use_card = false + +# Allows use of game mode modifiers (hi-speed, hidden, and sudden) +gamemode_options = true \ No newline at end of file diff --git a/data/ram/databank/PvList0_SBZV_7.10_26176_1558979190.dat b/data/ram/databank/PvList0_SBZV_7.10_26176_1558979190.dat new file mode 100644 index 0000000..d07a62c --- /dev/null +++ b/data/ram/databank/PvList0_SBZV_7.10_26176_1558979190.dat @@ -0,0 +1 @@ +7%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2009-01-01%252C2029-01-01%2C10%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2009-01-01%252C2029-01-01%2C16%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2009-01-01%252C2029-01-01%2C20%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2009-01-01%252C2029-01-01%2C23%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2009-01-01%252C2029-01-01%2C27%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2009-01-01%252C2029-01-01%2C30%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2009-01-01%252C2029-01-01%2C41%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2011-01-27%252C2029-01-01%2C42%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2012-02-23%252C2029-01-01%2C44%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2011-08-04%252C2029-01-01%2C46%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2012-03-27%252C2029-01-01%2C48%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2011-02-01%252C2029-01-01%2C49%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2011-02-01%252C2029-01-01%2C51%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2011-08-04%252C2029-01-01%2C52%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2012-03-27%252C2029-01-01%2C55%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2011-06-23%252C2029-01-01%2C57%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2012-02-23%252C2029-01-01%2C86%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2012-05-15%252C2029-01-01%2C96%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2012-07-19%252C2029-01-01%2C204%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2010-07-16%252C2029-01-01%2C213%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2011-12-15%252C2029-01-01%2C216%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2013-03-19%252C2029-01-01%2C219%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2013-08-06%252C2029-01-01%2C220%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2012-12-18%252C2029-01-01%2C221%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2013-04-26%252C2029-01-01%2C225%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2013-02-05%252C2029-01-01%2C232%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2013-03-19%252C2029-01-01%2C249%252C1%252C0%252C2016-02-26%252C2016-04-27%252C2016-02-26%252C2029-01-01%2C251%252C1%252C0%252C2015-10-08%252C2015-11-05%252C2015-10-08%252C2029-01-01%2C253%252C1%252C0%252C2016-08-31%252C2016-10-27%252C2016-08-31%252C2029-01-01%2C254%252C1%252C0%252C2016-12-21%252C2017-12-13%252C2016-12-21%252C2029-01-01%2C255%252C1%252C0%252C2016-06-30%252C2016-08-31%252C2016-06-30%252C2029-01-01%2C257%252C1%252C0%252C2016-04-27%252C2016-06-30%252C2016-04-27%252C2029-01-01%2C259%252C1%252C0%252C2016-04-27%252C2016-06-30%252C2016-04-27%252C2029-01-01%2C260%252C1%252C0%252C2016-04-27%252C2016-06-30%252C2016-04-27%252C2029-01-01%2C261%252C1%252C0%252C2016-06-30%252C2016-08-31%252C2016-06-30%252C2029-01-01%2C262%252C1%252C0%252C2016-10-27%252C2016-12-21%252C2016-10-27%252C2029-01-01%2C263%252C1%252C0%252C2016-12-21%252C2017-12-13%252C2016-12-21%252C2029-01-01%2C265%252C1%252C0%252C2016-10-27%252C2016-12-21%252C2016-10-27%252C2029-01-01%2C266%252C1%252C0%252C2017-12-13%252C2029-01-01%252C2017-12-13%252C2029-01-01%2C267%252C1%252C0%252C2017-12-13%252C2029-01-01%252C2017-12-13%252C2029-01-01%2C402%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2009-01-02%252C2029-01-01%2C405%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2009-01-02%252C2029-01-01%2C410%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2010-06-01%252C2029-01-01%2C427%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2012-01-12%252C2029-01-01%2C600%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2013-11-21%252C2029-01-01%2C602%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2013-11-21%252C2029-01-01%2C605%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2015-04-23%252C2029-01-01%2C615%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2014-01-28%252C2029-01-01%2C616%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2013-11-21%252C2029-01-01%2C637%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2015-03-26%252C2029-01-01%2C638%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2013-12-17%252C2029-01-01%2C725%252C1%252C0%252C2015-11-05%252C2015-12-22%252C2015-11-05%252C2029-01-01%2C726%252C1%252C0%252C2015-11-05%252C2015-12-22%252C2015-11-05%252C2029-01-01%2C727%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2015-06-26%252C2029-01-01%2C728%252C1%252C0%252C2016-01-28%252C2016-02-26%252C2016-01-28%252C2029-01-01%2C729%252C1%252C0%252C2015-10-08%252C2015-11-05%252C2015-10-08%252C2029-01-01%2C731%252C1%252C0%252C2015-12-22%252C2016-01-28%252C2015-12-22%252C2029-01-01%2C733%252C1%252C0%252C2016-01-28%252C2016-02-26%252C2016-01-28%252C2029-01-01%2C734%252C1%252C0%252C2015-11-05%252C2015-12-22%252C2015-11-05%252C2029-01-01%2C736%252C1%252C0%252C2015-10-08%252C2015-11-05%252C2015-10-08%252C2029-01-01%2C737%252C1%252C0%252C2015-12-22%252C2016-01-28%252C2015-12-22%252C2029-01-01%2C738%252C1%252C0%252C2016-01-28%252C2016-02-26%252C2016-01-28%252C2029-01-01%2C832%252C1%252C0%252C2016-08-31%252C2016-10-27%252C2016-08-31%252C2029-01-01 \ No newline at end of file diff --git a/data/ram/databank/PvList1_SBZV_7.10_48303_1558979196.dat b/data/ram/databank/PvList1_SBZV_7.10_48303_1558979196.dat new file mode 100644 index 0000000..bbf9ee5 --- /dev/null +++ b/data/ram/databank/PvList1_SBZV_7.10_48303_1558979196.dat @@ -0,0 +1 @@ +12%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2009-01-01%252C2029-01-01%2C20%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2009-01-01%252C2029-01-01%2C23%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2009-01-01%252C2029-01-01%2C27%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2009-01-01%252C2029-01-01%2C29%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2009-01-01%252C2029-01-01%2C30%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2009-01-01%252C2029-01-01%2C31%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2009-01-01%252C2029-01-01%2C41%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2011-01-27%252C2029-01-01%2C42%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2012-02-23%252C2029-01-01%2C44%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2011-08-04%252C2029-01-01%2C46%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2012-03-27%252C2029-01-01%2C48%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2011-02-01%252C2029-01-01%2C49%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2011-02-01%252C2029-01-01%2C51%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2011-08-04%252C2029-01-01%2C52%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2012-03-27%252C2029-01-01%2C55%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2011-06-23%252C2029-01-01%2C57%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2012-02-23%252C2029-01-01%2C82%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2012-05-15%252C2029-01-01%2C84%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2012-10-10%252C2029-01-01%2C85%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2012-12-18%252C2029-01-01%2C86%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2012-05-15%252C2029-01-01%2C89%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2012-09-06%252C2029-01-01%2C91%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2012-11-13%252C2029-01-01%2C96%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2012-07-19%252C2029-01-01%2C202%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2009-01-02%252C2029-01-01%2C204%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2010-07-16%252C2029-01-01%2C210%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2010-11-17%252C2029-01-01%2C211%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2010-11-17%252C2029-01-01%2C212%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2011-06-23%252C2029-01-01%2C213%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2011-12-15%252C2029-01-01%2C215%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2012-05-15%252C2029-01-01%2C216%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2013-03-19%252C2029-01-01%2C218%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2013-02-05%252C2029-01-01%2C219%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2013-08-06%252C2029-01-01%2C220%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2012-12-18%252C2029-01-01%2C221%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2013-04-26%252C2029-01-01%2C222%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2013-04-26%252C2029-01-01%2C224%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2013-06-27%252C2029-01-01%2C225%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2013-02-05%252C2029-01-01%2C227%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2012-12-18%252C2029-01-01%2C232%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2013-03-19%252C2029-01-01%2C249%252C1%252C0%252C2016-02-26%252C2016-04-27%252C2016-02-26%252C2029-01-01%2C251%252C1%252C0%252C2015-10-08%252C2015-11-05%252C2015-10-08%252C2029-01-01%2C253%252C1%252C0%252C2016-08-31%252C2016-10-27%252C2016-08-31%252C2029-01-01%2C254%252C1%252C0%252C2016-12-21%252C2017-12-13%252C2016-12-21%252C2029-01-01%2C255%252C1%252C0%252C2016-06-30%252C2016-08-31%252C2016-06-30%252C2029-01-01%2C257%252C1%252C0%252C2016-04-27%252C2016-06-30%252C2016-04-27%252C2029-01-01%2C259%252C1%252C0%252C2016-04-27%252C2016-06-30%252C2016-04-27%252C2029-01-01%2C260%252C1%252C0%252C2016-04-27%252C2016-06-30%252C2016-04-27%252C2029-01-01%2C261%252C1%252C0%252C2016-06-30%252C2016-08-31%252C2016-06-30%252C2029-01-01%2C262%252C1%252C0%252C2016-10-27%252C2016-12-21%252C2016-10-27%252C2029-01-01%2C263%252C1%252C0%252C2016-12-21%252C2017-12-13%252C2016-12-21%252C2029-01-01%2C265%252C1%252C0%252C2016-10-27%252C2016-12-21%252C2016-10-27%252C2029-01-01%2C266%252C1%252C0%252C2017-12-13%252C2029-01-01%252C2017-12-13%252C2029-01-01%2C267%252C1%252C0%252C2017-12-13%252C2029-01-01%252C2017-12-13%252C2029-01-01%2C402%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2009-01-02%252C2029-01-01%2C405%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2009-01-02%252C2029-01-01%2C407%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2010-06-01%252C2029-01-01%2C410%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2010-06-01%252C2029-01-01%2C412%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2010-06-01%252C2029-01-01%2C413%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2011-05-24%252C2029-01-01%2C417%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2011-05-24%252C2029-01-01%2C423%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2012-01-12%252C2029-01-01%2C425%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2012-01-12%252C2029-01-01%2C427%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2012-01-12%252C2029-01-01%2C428%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2012-03-27%252C2029-01-01%2C431%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2012-06-14%252C2029-01-01%2C435%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2014-07-01%252C2029-01-01%2C441%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2015-01-27%252C2029-01-01%2C600%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2013-11-21%252C2029-01-01%2C602%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2013-11-21%252C2029-01-01%2C605%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2015-04-23%252C2029-01-01%2C615%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2014-01-28%252C2029-01-01%2C616%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2013-11-21%252C2029-01-01%2C617%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2014-07-29%252C2029-01-01%2C625%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2014-09-17%252C2029-01-01%2C637%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2015-03-26%252C2029-01-01%2C638%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2013-12-17%252C2029-01-01%2C639%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2014-07-01%252C2029-01-01%2C725%252C1%252C0%252C2015-11-05%252C2015-12-22%252C2015-11-05%252C2029-01-01%2C726%252C1%252C0%252C2015-11-05%252C2015-12-22%252C2015-11-05%252C2029-01-01%2C727%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2015-06-26%252C2029-01-01%2C728%252C1%252C0%252C2016-01-28%252C2016-02-26%252C2016-01-28%252C2029-01-01%2C729%252C1%252C0%252C2015-10-08%252C2015-11-05%252C2015-10-08%252C2029-01-01%2C731%252C1%252C0%252C2015-12-22%252C2016-01-28%252C2015-12-22%252C2029-01-01%2C733%252C1%252C0%252C2016-01-28%252C2016-02-26%252C2016-01-28%252C2029-01-01%2C734%252C1%252C0%252C2015-11-05%252C2015-12-22%252C2015-11-05%252C2029-01-01%2C736%252C1%252C0%252C2015-10-08%252C2015-11-05%252C2015-10-08%252C2029-01-01%2C737%252C1%252C0%252C2015-12-22%252C2016-01-28%252C2015-12-22%252C2029-01-01%2C738%252C1%252C0%252C2016-01-28%252C2016-02-26%252C2016-01-28%252C2029-01-01%2C832%252C1%252C0%252C2016-08-31%252C2016-10-27%252C2016-08-31%252C2029-01-01 \ No newline at end of file diff --git a/data/ram/databank/PvList2_SBZV_7.10_25220_1558979199.dat b/data/ram/databank/PvList2_SBZV_7.10_25220_1558979199.dat new file mode 100644 index 0000000..a0cf749 --- /dev/null +++ b/data/ram/databank/PvList2_SBZV_7.10_25220_1558979199.dat @@ -0,0 +1 @@ +7%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2009-01-01%252C2029-01-01%2C10%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2009-01-01%252C2029-01-01%2C12%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2009-01-01%252C2029-01-01%2C16%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2009-01-01%252C2029-01-01%2C20%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2009-01-01%252C2029-01-01%2C23%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2009-01-01%252C2029-01-01%2C27%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2009-01-01%252C2029-01-01%2C29%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2009-01-01%252C2029-01-01%2C30%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2009-01-01%252C2029-01-01%2C31%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2009-01-01%252C2029-01-01%2C41%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2011-01-27%252C2029-01-01%2C42%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2012-02-23%252C2029-01-01%2C44%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2011-08-04%252C2029-01-01%2C46%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2012-03-27%252C2029-01-01%2C48%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2011-02-01%252C2029-01-01%2C49%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2011-02-01%252C2029-01-01%2C51%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2011-08-04%252C2029-01-01%2C52%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2012-03-27%252C2029-01-01%2C55%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2011-06-23%252C2029-01-01%2C57%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2012-02-23%252C2029-01-01%2C82%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2012-05-15%252C2029-01-01%2C84%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2012-10-10%252C2029-01-01%2C85%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2012-12-18%252C2029-01-01%2C86%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2012-05-15%252C2029-01-01%2C89%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2012-09-06%252C2029-01-01%2C91%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2012-11-13%252C2029-01-01%2C96%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2012-07-19%252C2029-01-01%2C202%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2009-01-02%252C2029-01-01%2C204%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2010-07-16%252C2029-01-01%2C210%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2010-11-17%252C2029-01-01%2C211%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2010-11-17%252C2029-01-01%2C212%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2011-06-23%252C2029-01-01%2C213%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2011-12-15%252C2029-01-01%2C215%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2012-05-15%252C2029-01-01%2C216%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2013-03-19%252C2029-01-01%2C218%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2013-02-05%252C2029-01-01%2C219%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2013-08-06%252C2029-01-01%2C220%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2012-12-18%252C2029-01-01%2C221%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2013-04-26%252C2029-01-01%2C222%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2013-04-26%252C2029-01-01%2C224%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2013-06-27%252C2029-01-01%2C225%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2013-02-05%252C2029-01-01%2C227%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2012-12-18%252C2029-01-01%2C232%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2013-03-19%252C2029-01-01%2C249%252C1%252C0%252C2016-02-26%252C2016-04-27%252C2016-02-26%252C2029-01-01%2C251%252C1%252C0%252C2015-10-08%252C2015-11-05%252C2015-10-08%252C2029-01-01%2C253%252C1%252C0%252C2016-08-31%252C2016-10-27%252C2016-08-31%252C2029-01-01%2C254%252C1%252C0%252C2016-12-21%252C2017-12-13%252C2016-12-21%252C2029-01-01%2C255%252C1%252C0%252C2016-06-30%252C2016-08-31%252C2016-06-30%252C2029-01-01%2C257%252C1%252C0%252C2016-04-27%252C2016-06-30%252C2016-04-27%252C2029-01-01%2C259%252C1%252C0%252C2016-04-27%252C2016-06-30%252C2016-04-27%252C2029-01-01%2C260%252C1%252C0%252C2016-04-27%252C2016-06-30%252C2016-04-27%252C2029-01-01%2C261%252C1%252C0%252C2016-06-30%252C2016-08-31%252C2016-06-30%252C2029-01-01%2C262%252C1%252C0%252C2016-10-27%252C2016-12-21%252C2016-10-27%252C2029-01-01%2C263%252C1%252C0%252C2016-12-21%252C2017-12-13%252C2016-12-21%252C2029-01-01%2C265%252C1%252C0%252C2016-10-27%252C2016-12-21%252C2016-10-27%252C2029-01-01%2C266%252C1%252C0%252C2017-12-13%252C2029-01-01%252C2017-12-13%252C2029-01-01%2C267%252C1%252C0%252C2017-12-13%252C2029-01-01%252C2017-12-13%252C2029-01-01%2C402%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2009-01-02%252C2029-01-01%2C405%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2009-01-02%252C2029-01-01%2C407%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2010-06-01%252C2029-01-01%2C412%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2010-06-01%252C2029-01-01%2C412%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2010-06-01%252C2029-01-01%2C413%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2011-05-24%252C2029-01-01%2C417%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2011-05-24%252C2029-01-01%2C423%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2012-01-12%252C2029-01-01%2C425%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2012-01-12%252C2029-01-01%2C427%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2012-01-12%252C2029-01-01%2C428%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2012-03-27%252C2029-01-01%2C431%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2012-06-14%252C2029-01-01%2C435%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2014-07-01%252C2029-01-01%2C441%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2015-01-27%252C2029-01-01%2C600%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2013-11-21%252C2029-01-01%2C602%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2013-11-21%252C2029-01-01%2C605%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2015-04-23%252C2029-01-01%2C615%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2014-01-28%252C2029-01-01%2C616%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2013-11-21%252C2029-01-01%2C617%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2014-07-29%252C2029-01-01%2C625%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2014-09-17%252C2029-01-01%2C637%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2015-03-26%252C2029-01-01%2C638%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2013-12-17%252C2029-01-01%2C639%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2014-07-01%252C2029-01-01%2C725%252C1%252C0%252C2015-11-05%252C2015-12-22%252C2015-11-05%252C2029-01-01%2C726%252C1%252C0%252C2015-11-05%252C2015-12-22%252C2015-11-05%252C2029-01-01%2C727%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2015-06-26%252C2029-01-01%2C728%252C1%252C0%252C2016-01-28%252C2016-02-26%252C2016-01-28%252C2029-01-01%2C729%252C1%252C0%252C2015-10-08%252C2015-11-05%252C2015-10-08%252C2029-01-01%2C731%252C1%252C0%252C2015-12-22%252C2016-01-28%252C2015-12-22%252C2029-01-01%2C733%252C1%252C0%252C2016-01-28%252C2016-02-26%252C2016-01-28%252C2029-01-01%2C734%252C1%252C0%252C2015-11-05%252C2015-12-22%252C2015-11-05%252C2029-01-01%2C736%252C1%252C0%252C2015-10-08%252C2015-11-05%252C2015-10-08%252C2029-01-01%2C737%252C1%252C0%252C2015-12-22%252C2016-01-28%252C2015-12-22%252C2029-01-01%2C738%252C1%252C0%252C2016-01-28%252C2016-02-26%252C2016-01-28%252C2029-01-01%2C832%252C1%252C0%252C2016-08-31%252C2016-10-27%252C2016-08-31%252C2029-01-01 \ No newline at end of file diff --git a/data/ram/databank/PvList3_SBZV_7.10_51748_1558979202.dat b/data/ram/databank/PvList3_SBZV_7.10_51748_1558979202.dat new file mode 100644 index 0000000..e644dec --- /dev/null +++ b/data/ram/databank/PvList3_SBZV_7.10_51748_1558979202.dat @@ -0,0 +1 @@ +7%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2009-01-01%252C2029-01-01%2C7%252C1%252C1%252C2029-01-01%252C2000-01-01%252C2016-01-14%252C2029-01-01%2C10%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2009-01-01%252C2029-01-01%2C10%252C1%252C1%252C2029-01-01%252C2000-01-01%252C2016-02-10%252C2029-01-01%2C12%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2009-01-01%252C2029-01-01%2C12%252C1%252C1%252C2029-01-01%252C2000-01-01%252C2018-04-25%252C2029-01-01%2C16%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2009-01-01%252C2029-01-01%2C16%252C1%252C1%252C2019-05-22%252C2029-01-01%252C2019-05-22%252C2029-01-01%2C20%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2009-01-01%252C2029-01-01%2C20%252C1%252C1%252C2019-04-24%252C2019-05-22%252C2019-04-24%252C2029-01-01%2C23%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2009-01-01%252C2029-01-01%2C23%252C1%252C1%252C2029-01-01%252C2000-01-01%252C2016-09-15%252C2029-01-01%2C27%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2009-01-01%252C2029-01-01%2C27%252C1%252C1%252C2029-01-01%252C2000-01-01%252C2016-04-07%252C2029-01-01%2C29%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2009-01-01%252C2029-01-01%2C29%252C1%252C1%252C2029-01-01%252C2000-01-01%252C2016-12-08%252C2029-01-01%2C30%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2009-01-01%252C2029-01-01%2C30%252C1%252C1%252C2029-01-01%252C2000-01-01%252C2015-11-26%252C2029-01-01%2C31%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2009-01-01%252C2029-01-01%2C31%252C1%252C1%252C2029-01-01%252C2000-01-01%252C2016-05-12%252C2029-01-01%2C41%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2011-01-27%252C2029-01-01%2C41%252C1%252C1%252C2029-01-01%252C2000-01-01%252C2017-02-09%252C2029-01-01%2C42%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2012-02-23%252C2029-01-01%2C42%252C1%252C1%252C2029-01-01%252C2000-01-01%252C2018-03-20%252C2029-01-01%2C44%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2011-08-04%252C2029-01-01%2C44%252C1%252C1%252C2029-01-01%252C2000-01-01%252C2017-10-25%252C2029-01-01%2C46%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2012-03-27%252C2029-01-01%2C46%252C1%252C1%252C2029-01-01%252C2000-01-01%252C2016-06-09%252C2029-01-01%2C48%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2011-02-01%252C2029-01-01%2C48%252C1%252C1%252C2029-01-01%252C2000-01-01%252C2018-03-20%252C2029-01-01%2C49%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2011-02-01%252C2029-01-01%2C49%252C1%252C1%252C2029-01-01%252C2000-01-01%252C2016-07-28%252C2029-01-01%2C51%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2011-08-04%252C2029-01-01%2C51%252C1%252C1%252C2029-01-01%252C2000-01-01%252C2016-04-07%252C2029-01-01%2C52%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2012-03-27%252C2029-01-01%2C52%252C1%252C1%252C2029-01-01%252C2000-01-01%252C2016-06-09%252C2029-01-01%2C55%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2011-06-23%252C2029-01-01%2C55%252C1%252C1%252C2029-01-01%252C2000-01-01%252C2016-10-13%252C2029-01-01%2C57%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2012-02-23%252C2029-01-01%2C57%252C1%252C1%252C2029-01-01%252C2000-01-01%252C2016-01-14%252C2029-01-01%2C82%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2012-05-15%252C2029-01-01%2C82%252C1%252C1%252C2029-01-01%252C2000-01-01%252C2016-03-10%252C2029-01-01%2C84%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2012-10-10%252C2029-01-01%2C84%252C1%252C1%252C2029-01-01%252C2000-01-01%252C2016-12-08%252C2029-01-01%2C85%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2012-12-18%252C2029-01-01%2C85%252C1%252C1%252C2029-01-01%252C2000-01-01%252C2016-01-14%252C2029-01-01%2C86%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2012-05-15%252C2029-01-01%2C86%252C1%252C1%252C2019-05-22%252C2029-01-01%252C2019-05-22%252C2029-01-01%2C89%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2012-09-06%252C2029-01-01%2C89%252C1%252C1%252C2029-01-01%252C2000-01-01%252C2016-11-10%252C2029-01-01%2C91%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2012-11-13%252C2029-01-01%2C91%252C1%252C1%252C2029-01-01%252C2000-01-01%252C2017-11-22%252C2029-01-01%2C96%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2012-07-19%252C2029-01-01%2C96%252C1%252C1%252C2029-01-01%252C2000-01-01%252C2016-04-07%252C2029-01-01%2C202%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2009-01-02%252C2029-01-01%2C202%252C1%252C1%252C2029-01-01%252C2000-01-01%252C2016-03-10%252C2029-01-01%2C204%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2010-07-16%252C2029-01-01%2C204%252C1%252C1%252C2029-01-01%252C2000-01-01%252C2018-02-21%252C2029-01-01%2C210%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2010-11-17%252C2029-01-01%2C210%252C1%252C1%252C2029-01-01%252C2000-01-01%252C2016-10-13%252C2029-01-01%2C211%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2010-11-17%252C2029-01-01%2C211%252C1%252C1%252C2029-01-01%252C2000-01-01%252C2016-09-15%252C2029-01-01%2C212%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2011-06-23%252C2029-01-01%2C212%252C1%252C1%252C2029-01-01%252C2000-01-01%252C2015-11-26%252C2029-01-01%2C213%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2011-12-15%252C2029-01-01%2C213%252C1%252C1%252C2029-01-01%252C2000-01-01%252C2018-09-26%252C2029-01-01%2C215%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2012-05-15%252C2029-01-01%2C215%252C1%252C1%252C2029-01-01%252C2000-01-01%252C2018-01-24%252C2029-01-01%2C216%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2013-03-19%252C2029-01-01%2C218%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2013-02-05%252C2029-01-01%2C218%252C1%252C1%252C2029-01-01%252C2000-01-01%252C2017-01-12%252C2029-01-01%2C219%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2013-08-06%252C2029-01-01%2C219%252C1%252C1%252C2029-01-01%252C2000-01-01%252C2015-07-09%252C2029-01-01%2C220%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2012-12-18%252C2029-01-01%2C220%252C1%252C1%252C2029-01-01%252C2000-01-01%252C2018-04-25%252C2029-01-01%2C221%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2013-04-26%252C2029-01-01%2C221%252C1%252C1%252C2029-01-01%252C2000-01-01%252C2016-11-10%252C2029-01-01%2C222%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2013-04-26%252C2029-01-01%2C222%252C1%252C1%252C2029-01-01%252C2000-01-01%252C2017-01-12%252C2029-01-01%2C224%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2013-06-27%252C2029-01-01%2C224%252C1%252C1%252C2029-01-01%252C2000-01-01%252C2016-02-10%252C2029-01-01%2C225%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2013-02-05%252C2029-01-01%2C225%252C1%252C1%252C2019-03-20%252C2019-04-24%252C2019-03-20%252C2029-01-01%2C227%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2012-12-18%252C2029-01-01%2C227%252C1%252C1%252C2029-01-01%252C2000-01-01%252C2018-02-21%252C2029-01-01%2C232%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2013-03-19%252C2029-01-01%2C232%252C1%252C1%252C2029-01-01%252C2000-01-01%252C2016-07-28%252C2029-01-01%2C249%252C1%252C0%252C2016-02-26%252C2016-04-27%252C2016-02-26%252C2029-01-01%2C251%252C1%252C0%252C2015-10-08%252C2015-11-05%252C2015-10-08%252C2029-01-01%2C253%252C1%252C0%252C2016-08-31%252C2016-10-27%252C2016-08-31%252C2029-01-01%2C254%252C1%252C0%252C2016-12-21%252C2017-12-13%252C2016-12-21%252C2029-01-01%2C255%252C1%252C0%252C2016-06-30%252C2016-08-31%252C2016-06-30%252C2029-01-01%2C257%252C1%252C0%252C2016-04-27%252C2016-06-30%252C2016-04-27%252C2029-01-01%2C259%252C1%252C0%252C2016-04-27%252C2016-06-30%252C2016-04-27%252C2029-01-01%2C260%252C1%252C0%252C2016-04-27%252C2016-06-30%252C2016-04-27%252C2029-01-01%2C261%252C1%252C0%252C2016-06-30%252C2016-08-31%252C2016-06-30%252C2029-01-01%2C262%252C1%252C0%252C2016-10-27%252C2016-12-21%252C2016-10-27%252C2029-01-01%2C263%252C1%252C0%252C2016-12-21%252C2017-12-13%252C2016-12-21%252C2029-01-01%2C265%252C1%252C0%252C2016-10-27%252C2016-12-21%252C2016-10-27%252C2029-01-01%2C266%252C1%252C0%252C2017-12-13%252C2029-01-01%252C2017-12-13%252C2029-01-01%2C267%252C1%252C0%252C2017-12-13%252C2029-01-01%252C2017-12-13%252C2029-01-01%2C402%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2009-01-02%252C2029-01-01%2C402%252C1%252C1%252C2029-01-01%252C2000-01-01%252C2016-03-10%252C2029-01-01%2C405%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2009-01-02%252C2029-01-01%2C405%252C1%252C1%252C2029-01-01%252C2000-01-01%252C2016-02-10%252C2029-01-01%2C407%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2010-06-01%252C2029-01-01%2C407%252C1%252C1%252C2029-01-01%252C2000-01-01%252C2015-11-26%252C2029-01-01%2C410%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2010-06-01%252C2029-01-01%2C410%252C1%252C1%252C2019-05-22%252C2029-01-01%252C2019-05-22%252C2029-01-01%2C412%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2010-06-01%252C2029-01-01%2C412%252C1%252C1%252C2029-01-01%252C2000-01-01%252C2018-11-21%252C2029-01-01%2C413%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2011-05-24%252C2029-01-01%2C413%252C1%252C1%252C2029-01-01%252C2000-01-01%252C2016-07-28%252C2029-01-01%2C417%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2011-05-24%252C2029-01-01%2C417%252C1%252C1%252C2029-01-01%252C2000-01-01%252C2017-10-25%252C2029-01-01%2C423%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2012-01-12%252C2029-01-01%2C423%252C1%252C1%252C2029-01-01%252C2000-01-01%252C2016-05-12%252C2029-01-01%2C425%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2012-01-12%252C2029-01-01%2C425%252C1%252C1%252C2029-01-01%252C2000-01-01%252C2017-11-22%252C2029-01-01%2C427%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2012-01-12%252C2029-01-01%2C427%252C1%252C1%252C2029-01-01%252C2000-01-01%252C2018-01-24%252C2029-01-01%2C428%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2012-03-27%252C2029-01-01%2C428%252C1%252C1%252C2029-01-01%252C2000-01-01%252C2016-05-12%252C2029-01-01%2C431%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2012-06-14%252C2029-01-01%2C431%252C1%252C1%252C2029-01-01%252C2000-01-01%252C2017-02-09%252C2029-01-01%2C435%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2014-07-01%252C2029-01-01%2C435%252C1%252C1%252C2029-01-01%252C2000-01-01%252C2018-07-25%252C2029-01-01%2C441%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2015-01-27%252C2029-01-01%2C441%252C1%252C1%252C2019-05-22%252C2029-01-01%252C2019-05-22%252C2029-01-01%2C600%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2013-11-21%252C2029-01-01%2C600%252C1%252C1%252C2029-01-01%252C2000-01-01%252C2018-06-20%252C2029-01-01%2C602%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2013-11-21%252C2029-01-01%2C602%252C1%252C1%252C2029-01-01%252C2000-01-01%252C2018-12-26%252C2029-01-01%2C605%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2015-04-23%252C2029-01-01%2C605%252C1%252C1%252C2029-01-01%252C2000-01-01%252C2018-08-22%252C2029-01-01%2C613%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2013-12-17%252C2029-01-01%2C613%252C1%252C1%252C2029-01-01%252C2019-03-20%252C2019-02-27%252C2029-01-01%2C615%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2014-01-28%252C2029-01-01%2C615%252C1%252C1%252C2029-01-01%252C2000-01-01%252C2018-05-23%252C2029-01-01%2C616%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2013-11-21%252C2029-01-01%2C616%252C1%252C1%252C2029-01-01%252C2000-01-01%252C2018-05-23%252C2029-01-01%2C617%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2014-07-29%252C2029-01-01%2C617%252C1%252C1%252C2029-01-01%252C2000-01-01%252C2018-07-25%252C2029-01-01%2C625%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2014-09-17%252C2029-01-01%2C625%252C1%252C1%252C2029-01-01%252C2000-01-01%252C2018-08-22%252C2029-01-01%2C637%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2015-03-26%252C2029-01-01%2C638%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2013-12-17%252C2029-01-01%2C638%252C1%252C1%252C2029-01-01%252C2000-01-01%252C2018-10-24%252C2029-01-01%2C639%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2014-07-01%252C2029-01-01%2C639%252C1%252C1%252C2029-01-01%252C2000-01-01%252C2018-06-20%252C2029-01-01%2C725%252C1%252C0%252C2015-11-05%252C2015-12-22%252C2015-11-05%252C2029-01-01%2C726%252C1%252C0%252C2015-11-05%252C2015-12-22%252C2015-11-05%252C2029-01-01%2C727%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2015-06-26%252C2029-01-01%2C728%252C1%252C0%252C2016-01-28%252C2016-02-26%252C2016-01-28%252C2029-01-01%2C729%252C1%252C0%252C2015-10-08%252C2015-11-05%252C2015-10-08%252C2029-01-01%2C730%252C1%252C0%252C2029-01-01%252C2000-01-01%252C2015-01-27%252C2029-01-01%2C730%252C1%252C1%252C2029-01-01%252C2019-02-27%252C2019-01-23%252C2029-01-01%2C731%252C1%252C0%252C2015-12-22%252C2016-01-28%252C2015-12-22%252C2029-01-01%2C733%252C1%252C0%252C2016-01-28%252C2016-02-26%252C2016-01-28%252C2029-01-01%2C734%252C1%252C0%252C2015-11-05%252C2015-12-22%252C2015-11-05%252C2029-01-01%2C736%252C1%252C0%252C2015-10-08%252C2015-11-05%252C2015-10-08%252C2029-01-01%2C737%252C1%252C0%252C2015-12-22%252C2016-01-28%252C2015-12-22%252C2029-01-01%2C738%252C1%252C0%252C2016-01-28%252C2016-02-26%252C2016-01-28%252C2029-01-01%2C832%252C1%252C0%252C2016-08-31%252C2016-10-27%252C2016-08-31%252C2029-01-01 \ No newline at end of file diff --git a/data/ram/databank/PvList4_SBZV_7.10_48392_1558979205.dat b/data/ram/databank/PvList4_SBZV_7.10_48392_1558979205.dat new file mode 100644 index 0000000..5926586 --- /dev/null +++ b/data/ram/databank/PvList4_SBZV_7.10_48392_1558979205.dat @@ -0,0 +1 @@ +%2A%2A%$A \ No newline at end of file diff --git a/dependencies/detours/include/detours.h b/dependencies/detours/include/detours.h new file mode 100644 index 0000000..0aade5c --- /dev/null +++ b/dependencies/detours/include/detours.h @@ -0,0 +1,1108 @@ +///////////////////////////////////////////////////////////////////////////// +// +// Core Detours Functionality (detours.h of detours.lib) +// +// Microsoft Research Detours Package, Version 4.0.1 +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +#pragma once +#ifndef _DETOURS_H_ +#define _DETOURS_H_ + +#define DETOURS_VERSION 0x4c0c1 // 0xMAJORcMINORcPATCH + +////////////////////////////////////////////////////////////////////////////// +// + +#ifdef DETOURS_INTERNAL + +#define _CRT_STDIO_ARBITRARY_WIDE_SPECIFIERS 1 +#define _ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE 1 + +#pragma warning(disable:4068) // unknown pragma (suppress) + +#if _MSC_VER >= 1900 +#pragma warning(push) +#pragma warning(disable:4091) // empty typedef +#endif + +#include +#if (_MSC_VER < 1310) +#else +#pragma warning(push) +#if _MSC_VER > 1400 +#pragma warning(disable:6102 6103) // /analyze warnings +#endif +#include +#pragma warning(pop) +#endif + +#endif // DETOURS_INTERNAL + +////////////////////////////////////////////////////////////////////////////// +// + +#undef DETOURS_X64 +#undef DETOURS_X86 +#undef DETOURS_IA64 +#undef DETOURS_ARM +#undef DETOURS_ARM64 +#undef DETOURS_BITS +#undef DETOURS_32BIT +#undef DETOURS_64BIT + +#if defined(_X86_) +#define DETOURS_X86 +#define DETOURS_OPTION_BITS 64 + +#elif defined(_AMD64_) +#define DETOURS_X64 +#define DETOURS_OPTION_BITS 32 + +#elif defined(_IA64_) +#define DETOURS_IA64 +#define DETOURS_OPTION_BITS 32 + +#elif defined(_ARM_) +#define DETOURS_ARM + +#elif defined(_ARM64_) +#define DETOURS_ARM64 + +#else +#error Unknown architecture (x86, amd64, ia64, arm, arm64) +#endif + +#ifdef _WIN64 +#undef DETOURS_32BIT +#define DETOURS_64BIT 1 +#define DETOURS_BITS 64 +// If all 64bit kernels can run one and only one 32bit architecture. +//#define DETOURS_OPTION_BITS 32 +#else +#define DETOURS_32BIT 1 +#undef DETOURS_64BIT +#define DETOURS_BITS 32 +// If all 64bit kernels can run one and only one 32bit architecture. +//#define DETOURS_OPTION_BITS 32 +#endif + +#define VER_DETOURS_BITS DETOUR_STRINGIFY(DETOURS_BITS) + +////////////////////////////////////////////////////////////////////////////// +// + +#if (_MSC_VER < 1299) +typedef LONG LONG_PTR; +typedef ULONG ULONG_PTR; +#endif + +///////////////////////////////////////////////// SAL 2.0 Annotations w/o SAL. +// +// These definitions are include so that Detours will build even if the +// compiler doesn't have full SAL 2.0 support. +// +#ifndef DETOURS_DONT_REMOVE_SAL_20 + +#ifdef DETOURS_TEST_REMOVE_SAL_20 +#undef _Analysis_assume_ +#undef _Benign_race_begin_ +#undef _Benign_race_end_ +#undef _Field_range_ +#undef _Field_size_ +#undef _In_ +#undef _In_bytecount_ +#undef _In_count_ +#undef _In_opt_ +#undef _In_opt_bytecount_ +#undef _In_opt_count_ +#undef _In_opt_z_ +#undef _In_range_ +#undef _In_reads_ +#undef _In_reads_bytes_ +#undef _In_reads_opt_ +#undef _In_reads_opt_bytes_ +#undef _In_reads_or_z_ +#undef _In_z_ +#undef _Inout_ +#undef _Inout_opt_ +#undef _Inout_z_count_ +#undef _Out_ +#undef _Out_opt_ +#undef _Out_writes_ +#undef _Outptr_result_maybenull_ +#undef _Readable_bytes_ +#undef _Success_ +#undef _Writable_bytes_ +#undef _Pre_notnull_ +#endif + +#if defined(_Deref_out_opt_z_) && !defined(_Outptr_result_maybenull_) +#define _Outptr_result_maybenull_ _Deref_out_opt_z_ +#endif + +#if defined(_In_count_) && !defined(_In_reads_) +#define _In_reads_(x) _In_count_(x) +#endif + +#if defined(_In_opt_count_) && !defined(_In_reads_opt_) +#define _In_reads_opt_(x) _In_opt_count_(x) +#endif + +#if defined(_In_opt_bytecount_) && !defined(_In_reads_opt_bytes_) +#define _In_reads_opt_bytes_(x) _In_opt_bytecount_(x) +#endif + +#if defined(_In_bytecount_) && !defined(_In_reads_bytes_) +#define _In_reads_bytes_(x) _In_bytecount_(x) +#endif + +#ifndef _In_ +#define _In_ +#endif + +#ifndef _In_bytecount_ +#define _In_bytecount_(x) +#endif + +#ifndef _In_count_ +#define _In_count_(x) +#endif + +#ifndef _In_opt_ +#define _In_opt_ +#endif + +#ifndef _In_opt_bytecount_ +#define _In_opt_bytecount_(x) +#endif + +#ifndef _In_opt_count_ +#define _In_opt_count_(x) +#endif + +#ifndef _In_opt_z_ +#define _In_opt_z_ +#endif + +#ifndef _In_range_ +#define _In_range_(x,y) +#endif + +#ifndef _In_reads_ +#define _In_reads_(x) +#endif + +#ifndef _In_reads_bytes_ +#define _In_reads_bytes_(x) +#endif + +#ifndef _In_reads_opt_ +#define _In_reads_opt_(x) +#endif + +#ifndef _In_reads_opt_bytes_ +#define _In_reads_opt_bytes_(x) +#endif + +#ifndef _In_reads_or_z_ +#define _In_reads_or_z_ +#endif + +#ifndef _In_z_ +#define _In_z_ +#endif + +#ifndef _Inout_ +#define _Inout_ +#endif + +#ifndef _Inout_opt_ +#define _Inout_opt_ +#endif + +#ifndef _Inout_z_count_ +#define _Inout_z_count_(x) +#endif + +#ifndef _Out_ +#define _Out_ +#endif + +#ifndef _Out_opt_ +#define _Out_opt_ +#endif + +#ifndef _Out_writes_ +#define _Out_writes_(x) +#endif + +#ifndef _Outptr_result_maybenull_ +#define _Outptr_result_maybenull_ +#endif + +#ifndef _Writable_bytes_ +#define _Writable_bytes_(x) +#endif + +#ifndef _Readable_bytes_ +#define _Readable_bytes_(x) +#endif + +#ifndef _Success_ +#define _Success_(x) +#endif + +#ifndef _Pre_notnull_ +#define _Pre_notnull_ +#endif + +#ifdef DETOURS_INTERNAL + +#pragma warning(disable:4615) // unknown warning type (suppress with older compilers) + +#ifndef _Benign_race_begin_ +#define _Benign_race_begin_ +#endif + +#ifndef _Benign_race_end_ +#define _Benign_race_end_ +#endif + +#ifndef _Field_size_ +#define _Field_size_(x) +#endif + +#ifndef _Field_range_ +#define _Field_range_(x,y) +#endif + +#ifndef _Analysis_assume_ +#define _Analysis_assume_(x) +#endif + +#endif // DETOURS_INTERNAL +#endif // DETOURS_DONT_REMOVE_SAL_20 + +////////////////////////////////////////////////////////////////////////////// +// +#ifndef GUID_DEFINED +#define GUID_DEFINED +typedef struct _GUID +{ + DWORD Data1; + WORD Data2; + WORD Data3; + BYTE Data4[ 8 ]; +} GUID; + +#ifdef INITGUID +#define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ + const GUID name \ + = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } } +#else +#define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ + const GUID name +#endif // INITGUID +#endif // !GUID_DEFINED + +#if defined(__cplusplus) +#ifndef _REFGUID_DEFINED +#define _REFGUID_DEFINED +#define REFGUID const GUID & +#endif // !_REFGUID_DEFINED +#else // !__cplusplus +#ifndef _REFGUID_DEFINED +#define _REFGUID_DEFINED +#define REFGUID const GUID * const +#endif // !_REFGUID_DEFINED +#endif // !__cplusplus + +#ifndef ARRAYSIZE +#define ARRAYSIZE(x) (sizeof(x)/sizeof(x[0])) +#endif + +// +////////////////////////////////////////////////////////////////////////////// + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +/////////////////////////////////////////////////// Instruction Target Macros. +// +#define DETOUR_INSTRUCTION_TARGET_NONE ((PVOID)0) +#define DETOUR_INSTRUCTION_TARGET_DYNAMIC ((PVOID)(LONG_PTR)-1) +#define DETOUR_SECTION_HEADER_SIGNATURE 0x00727444 // "Dtr\0" + +extern const GUID DETOUR_EXE_RESTORE_GUID; +extern const GUID DETOUR_EXE_HELPER_GUID; + +#define DETOUR_TRAMPOLINE_SIGNATURE 0x21727444 // Dtr! +typedef struct _DETOUR_TRAMPOLINE DETOUR_TRAMPOLINE, *PDETOUR_TRAMPOLINE; + +/////////////////////////////////////////////////////////// Binary Structures. +// +#pragma pack(push, 8) +typedef struct _DETOUR_SECTION_HEADER +{ + DWORD cbHeaderSize; + DWORD nSignature; + DWORD nDataOffset; + DWORD cbDataSize; + + DWORD nOriginalImportVirtualAddress; + DWORD nOriginalImportSize; + DWORD nOriginalBoundImportVirtualAddress; + DWORD nOriginalBoundImportSize; + + DWORD nOriginalIatVirtualAddress; + DWORD nOriginalIatSize; + DWORD nOriginalSizeOfImage; + DWORD cbPrePE; + + DWORD nOriginalClrFlags; + DWORD reserved1; + DWORD reserved2; + DWORD reserved3; + + // Followed by cbPrePE bytes of data. +} DETOUR_SECTION_HEADER, *PDETOUR_SECTION_HEADER; + +typedef struct _DETOUR_SECTION_RECORD +{ + DWORD cbBytes; + DWORD nReserved; + GUID guid; +} DETOUR_SECTION_RECORD, *PDETOUR_SECTION_RECORD; + +typedef struct _DETOUR_CLR_HEADER +{ + // Header versioning + ULONG cb; + USHORT MajorRuntimeVersion; + USHORT MinorRuntimeVersion; + + // Symbol table and startup information + IMAGE_DATA_DIRECTORY MetaData; + ULONG Flags; + + // Followed by the rest of the IMAGE_COR20_HEADER +} DETOUR_CLR_HEADER, *PDETOUR_CLR_HEADER; + +typedef struct _DETOUR_EXE_RESTORE +{ + DWORD cb; + DWORD cbidh; + DWORD cbinh; + DWORD cbclr; + + PBYTE pidh; + PBYTE pinh; + PBYTE pclr; + + IMAGE_DOS_HEADER idh; + union { + IMAGE_NT_HEADERS inh; // all environments have this +#ifdef IMAGE_NT_OPTIONAL_HDR32_MAGIC // some environments do not have this + IMAGE_NT_HEADERS32 inh32; +#endif +#ifdef IMAGE_NT_OPTIONAL_HDR64_MAGIC // some environments do not have this + IMAGE_NT_HEADERS64 inh64; +#endif +#ifdef IMAGE_NT_OPTIONAL_HDR64_MAGIC // some environments do not have this + BYTE raw[sizeof(IMAGE_NT_HEADERS64) + + sizeof(IMAGE_SECTION_HEADER) * 32]; +#else + BYTE raw[0x108 + sizeof(IMAGE_SECTION_HEADER) * 32]; +#endif + }; + DETOUR_CLR_HEADER clr; + +} DETOUR_EXE_RESTORE, *PDETOUR_EXE_RESTORE; + +#ifdef IMAGE_NT_OPTIONAL_HDR64_MAGIC +C_ASSERT(sizeof(IMAGE_NT_HEADERS64) == 0x108); +#endif + +// The size can change, but assert for clarity due to the muddying #ifdefs. +#ifdef _WIN64 +C_ASSERT(sizeof(DETOUR_EXE_RESTORE) == 0x688); +#else +C_ASSERT(sizeof(DETOUR_EXE_RESTORE) == 0x678); +#endif + +typedef struct _DETOUR_EXE_HELPER +{ + DWORD cb; + DWORD pid; + DWORD nDlls; + CHAR rDlls[4]; +} DETOUR_EXE_HELPER, *PDETOUR_EXE_HELPER; + +#pragma pack(pop) + +#define DETOUR_SECTION_HEADER_DECLARE(cbSectionSize) \ +{ \ + sizeof(DETOUR_SECTION_HEADER),\ + DETOUR_SECTION_HEADER_SIGNATURE,\ + sizeof(DETOUR_SECTION_HEADER),\ + (cbSectionSize),\ + \ + 0,\ + 0,\ + 0,\ + 0,\ + \ + 0,\ + 0,\ + 0,\ + 0,\ +} + +/////////////////////////////////////////////////////////////// Helper Macros. +// +#define DETOURS_STRINGIFY(x) DETOURS_STRINGIFY_(x) +#define DETOURS_STRINGIFY_(x) #x + +///////////////////////////////////////////////////////////// Binary Typedefs. +// +typedef BOOL (CALLBACK *PF_DETOUR_BINARY_BYWAY_CALLBACK)( + _In_opt_ PVOID pContext, + _In_opt_ LPCSTR pszFile, + _Outptr_result_maybenull_ LPCSTR *ppszOutFile); + +typedef BOOL (CALLBACK *PF_DETOUR_BINARY_FILE_CALLBACK)( + _In_opt_ PVOID pContext, + _In_ LPCSTR pszOrigFile, + _In_ LPCSTR pszFile, + _Outptr_result_maybenull_ LPCSTR *ppszOutFile); + +typedef BOOL (CALLBACK *PF_DETOUR_BINARY_SYMBOL_CALLBACK)( + _In_opt_ PVOID pContext, + _In_ ULONG nOrigOrdinal, + _In_ ULONG nOrdinal, + _Out_ ULONG *pnOutOrdinal, + _In_opt_ LPCSTR pszOrigSymbol, + _In_opt_ LPCSTR pszSymbol, + _Outptr_result_maybenull_ LPCSTR *ppszOutSymbol); + +typedef BOOL (CALLBACK *PF_DETOUR_BINARY_COMMIT_CALLBACK)( + _In_opt_ PVOID pContext); + +typedef BOOL (CALLBACK *PF_DETOUR_ENUMERATE_EXPORT_CALLBACK)(_In_opt_ PVOID pContext, + _In_ ULONG nOrdinal, + _In_opt_ LPCSTR pszName, + _In_opt_ PVOID pCode); + +typedef BOOL (CALLBACK *PF_DETOUR_IMPORT_FILE_CALLBACK)(_In_opt_ PVOID pContext, + _In_opt_ HMODULE hModule, + _In_opt_ LPCSTR pszFile); + +typedef BOOL (CALLBACK *PF_DETOUR_IMPORT_FUNC_CALLBACK)(_In_opt_ PVOID pContext, + _In_ DWORD nOrdinal, + _In_opt_ LPCSTR pszFunc, + _In_opt_ PVOID pvFunc); + +// Same as PF_DETOUR_IMPORT_FUNC_CALLBACK but extra indirection on last parameter. +typedef BOOL (CALLBACK *PF_DETOUR_IMPORT_FUNC_CALLBACK_EX)(_In_opt_ PVOID pContext, + _In_ DWORD nOrdinal, + _In_opt_ LPCSTR pszFunc, + _In_opt_ PVOID* ppvFunc); + +typedef VOID * PDETOUR_BINARY; +typedef VOID * PDETOUR_LOADED_BINARY; + +//////////////////////////////////////////////////////////// Transaction APIs. +// +LONG WINAPI DetourTransactionBegin(VOID); +LONG WINAPI DetourTransactionAbort(VOID); +LONG WINAPI DetourTransactionCommit(VOID); +LONG WINAPI DetourTransactionCommitEx(_Out_opt_ PVOID **pppFailedPointer); + +LONG WINAPI DetourUpdateThread(_In_ HANDLE hThread); + +LONG WINAPI DetourAttach(_Inout_ PVOID *ppPointer, + _In_ PVOID pDetour); + +LONG WINAPI DetourAttachEx(_Inout_ PVOID *ppPointer, + _In_ PVOID pDetour, + _Out_opt_ PDETOUR_TRAMPOLINE *ppRealTrampoline, + _Out_opt_ PVOID *ppRealTarget, + _Out_opt_ PVOID *ppRealDetour); + +LONG WINAPI DetourDetach(_Inout_ PVOID *ppPointer, + _In_ PVOID pDetour); + +BOOL WINAPI DetourSetIgnoreTooSmall(_In_ BOOL fIgnore); +BOOL WINAPI DetourSetRetainRegions(_In_ BOOL fRetain); +PVOID WINAPI DetourSetSystemRegionLowerBound(_In_ PVOID pSystemRegionLowerBound); +PVOID WINAPI DetourSetSystemRegionUpperBound(_In_ PVOID pSystemRegionUpperBound); + +////////////////////////////////////////////////////////////// Code Functions. +// +PVOID WINAPI DetourFindFunction(_In_ LPCSTR pszModule, + _In_ LPCSTR pszFunction); +PVOID WINAPI DetourCodeFromPointer(_In_ PVOID pPointer, + _Out_opt_ PVOID *ppGlobals); +PVOID WINAPI DetourCopyInstruction(_In_opt_ PVOID pDst, + _Inout_opt_ PVOID *ppDstPool, + _In_ PVOID pSrc, + _Out_opt_ PVOID *ppTarget, + _Out_opt_ LONG *plExtra); +BOOL WINAPI DetourSetCodeModule(_In_ HMODULE hModule, + _In_ BOOL fLimitReferencesToModule); +PVOID WINAPI DetourAllocateRegionWithinJumpBounds(_In_ LPCVOID pbTarget, + _Out_ PDWORD pcbAllocatedSize); + +///////////////////////////////////////////////////// Loaded Binary Functions. +// +HMODULE WINAPI DetourGetContainingModule(_In_ PVOID pvAddr); +HMODULE WINAPI DetourEnumerateModules(_In_opt_ HMODULE hModuleLast); +PVOID WINAPI DetourGetEntryPoint(_In_opt_ HMODULE hModule); +ULONG WINAPI DetourGetModuleSize(_In_opt_ HMODULE hModule); +BOOL WINAPI DetourEnumerateExports(_In_ HMODULE hModule, + _In_opt_ PVOID pContext, + _In_ PF_DETOUR_ENUMERATE_EXPORT_CALLBACK pfExport); +BOOL WINAPI DetourEnumerateImports(_In_opt_ HMODULE hModule, + _In_opt_ PVOID pContext, + _In_opt_ PF_DETOUR_IMPORT_FILE_CALLBACK pfImportFile, + _In_opt_ PF_DETOUR_IMPORT_FUNC_CALLBACK pfImportFunc); + +BOOL WINAPI DetourEnumerateImportsEx(_In_opt_ HMODULE hModule, + _In_opt_ PVOID pContext, + _In_opt_ PF_DETOUR_IMPORT_FILE_CALLBACK pfImportFile, + _In_opt_ PF_DETOUR_IMPORT_FUNC_CALLBACK_EX pfImportFuncEx); + +_Writable_bytes_(*pcbData) +_Readable_bytes_(*pcbData) +_Success_(return != NULL) +PVOID WINAPI DetourFindPayload(_In_opt_ HMODULE hModule, + _In_ REFGUID rguid, + _Out_ DWORD *pcbData); + +_Writable_bytes_(*pcbData) +_Readable_bytes_(*pcbData) +_Success_(return != NULL) +PVOID WINAPI DetourFindPayloadEx(_In_ REFGUID rguid, + _Out_ DWORD * pcbData); + +DWORD WINAPI DetourGetSizeOfPayloads(_In_opt_ HMODULE hModule); + +///////////////////////////////////////////////// Persistent Binary Functions. +// + +PDETOUR_BINARY WINAPI DetourBinaryOpen(_In_ HANDLE hFile); + +_Writable_bytes_(*pcbData) +_Readable_bytes_(*pcbData) +_Success_(return != NULL) +PVOID WINAPI DetourBinaryEnumeratePayloads(_In_ PDETOUR_BINARY pBinary, + _Out_opt_ GUID *pGuid, + _Out_ DWORD *pcbData, + _Inout_ DWORD *pnIterator); + +_Writable_bytes_(*pcbData) +_Readable_bytes_(*pcbData) +_Success_(return != NULL) +PVOID WINAPI DetourBinaryFindPayload(_In_ PDETOUR_BINARY pBinary, + _In_ REFGUID rguid, + _Out_ DWORD *pcbData); + +PVOID WINAPI DetourBinarySetPayload(_In_ PDETOUR_BINARY pBinary, + _In_ REFGUID rguid, + _In_reads_opt_(cbData) PVOID pData, + _In_ DWORD cbData); +BOOL WINAPI DetourBinaryDeletePayload(_In_ PDETOUR_BINARY pBinary, _In_ REFGUID rguid); +BOOL WINAPI DetourBinaryPurgePayloads(_In_ PDETOUR_BINARY pBinary); +BOOL WINAPI DetourBinaryResetImports(_In_ PDETOUR_BINARY pBinary); +BOOL WINAPI DetourBinaryEditImports(_In_ PDETOUR_BINARY pBinary, + _In_opt_ PVOID pContext, + _In_opt_ PF_DETOUR_BINARY_BYWAY_CALLBACK pfByway, + _In_opt_ PF_DETOUR_BINARY_FILE_CALLBACK pfFile, + _In_opt_ PF_DETOUR_BINARY_SYMBOL_CALLBACK pfSymbol, + _In_opt_ PF_DETOUR_BINARY_COMMIT_CALLBACK pfCommit); +BOOL WINAPI DetourBinaryWrite(_In_ PDETOUR_BINARY pBinary, _In_ HANDLE hFile); +BOOL WINAPI DetourBinaryClose(_In_ PDETOUR_BINARY pBinary); + +/////////////////////////////////////////////////// Create Process & Load Dll. +// +typedef BOOL (WINAPI *PDETOUR_CREATE_PROCESS_ROUTINEA)( + _In_opt_ LPCSTR lpApplicationName, + _Inout_opt_ LPSTR lpCommandLine, + _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes, + _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, + _In_ BOOL bInheritHandles, + _In_ DWORD dwCreationFlags, + _In_opt_ LPVOID lpEnvironment, + _In_opt_ LPCSTR lpCurrentDirectory, + _In_ LPSTARTUPINFOA lpStartupInfo, + _Out_ LPPROCESS_INFORMATION lpProcessInformation); + +typedef BOOL (WINAPI *PDETOUR_CREATE_PROCESS_ROUTINEW)( + _In_opt_ LPCWSTR lpApplicationName, + _Inout_opt_ LPWSTR lpCommandLine, + _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes, + _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, + _In_ BOOL bInheritHandles, + _In_ DWORD dwCreationFlags, + _In_opt_ LPVOID lpEnvironment, + _In_opt_ LPCWSTR lpCurrentDirectory, + _In_ LPSTARTUPINFOW lpStartupInfo, + _Out_ LPPROCESS_INFORMATION lpProcessInformation); + +BOOL WINAPI DetourCreateProcessWithDllA(_In_opt_ LPCSTR lpApplicationName, + _Inout_opt_ LPSTR lpCommandLine, + _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes, + _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, + _In_ BOOL bInheritHandles, + _In_ DWORD dwCreationFlags, + _In_opt_ LPVOID lpEnvironment, + _In_opt_ LPCSTR lpCurrentDirectory, + _In_ LPSTARTUPINFOA lpStartupInfo, + _Out_ LPPROCESS_INFORMATION lpProcessInformation, + _In_ LPCSTR lpDllName, + _In_opt_ PDETOUR_CREATE_PROCESS_ROUTINEA pfCreateProcessA); + +BOOL WINAPI DetourCreateProcessWithDllW(_In_opt_ LPCWSTR lpApplicationName, + _Inout_opt_ LPWSTR lpCommandLine, + _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes, + _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, + _In_ BOOL bInheritHandles, + _In_ DWORD dwCreationFlags, + _In_opt_ LPVOID lpEnvironment, + _In_opt_ LPCWSTR lpCurrentDirectory, + _In_ LPSTARTUPINFOW lpStartupInfo, + _Out_ LPPROCESS_INFORMATION lpProcessInformation, + _In_ LPCSTR lpDllName, + _In_opt_ PDETOUR_CREATE_PROCESS_ROUTINEW pfCreateProcessW); + +#ifdef UNICODE +#define DetourCreateProcessWithDll DetourCreateProcessWithDllW +#define PDETOUR_CREATE_PROCESS_ROUTINE PDETOUR_CREATE_PROCESS_ROUTINEW +#else +#define DetourCreateProcessWithDll DetourCreateProcessWithDllA +#define PDETOUR_CREATE_PROCESS_ROUTINE PDETOUR_CREATE_PROCESS_ROUTINEA +#endif // !UNICODE + +BOOL WINAPI DetourCreateProcessWithDllExA(_In_opt_ LPCSTR lpApplicationName, + _Inout_opt_ LPSTR lpCommandLine, + _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes, + _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, + _In_ BOOL bInheritHandles, + _In_ DWORD dwCreationFlags, + _In_opt_ LPVOID lpEnvironment, + _In_opt_ LPCSTR lpCurrentDirectory, + _In_ LPSTARTUPINFOA lpStartupInfo, + _Out_ LPPROCESS_INFORMATION lpProcessInformation, + _In_ LPCSTR lpDllName, + _In_opt_ PDETOUR_CREATE_PROCESS_ROUTINEA pfCreateProcessA); + +BOOL WINAPI DetourCreateProcessWithDllExW(_In_opt_ LPCWSTR lpApplicationName, + _Inout_opt_ LPWSTR lpCommandLine, + _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes, + _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, + _In_ BOOL bInheritHandles, + _In_ DWORD dwCreationFlags, + _In_opt_ LPVOID lpEnvironment, + _In_opt_ LPCWSTR lpCurrentDirectory, + _In_ LPSTARTUPINFOW lpStartupInfo, + _Out_ LPPROCESS_INFORMATION lpProcessInformation, + _In_ LPCSTR lpDllName, + _In_opt_ PDETOUR_CREATE_PROCESS_ROUTINEW pfCreateProcessW); + +#ifdef UNICODE +#define DetourCreateProcessWithDllEx DetourCreateProcessWithDllExW +#else +#define DetourCreateProcessWithDllEx DetourCreateProcessWithDllExA +#endif // !UNICODE + +BOOL WINAPI DetourCreateProcessWithDllsA(_In_opt_ LPCSTR lpApplicationName, + _Inout_opt_ LPSTR lpCommandLine, + _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes, + _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, + _In_ BOOL bInheritHandles, + _In_ DWORD dwCreationFlags, + _In_opt_ LPVOID lpEnvironment, + _In_opt_ LPCSTR lpCurrentDirectory, + _In_ LPSTARTUPINFOA lpStartupInfo, + _Out_ LPPROCESS_INFORMATION lpProcessInformation, + _In_ DWORD nDlls, + _In_reads_(nDlls) LPCSTR *rlpDlls, + _In_opt_ PDETOUR_CREATE_PROCESS_ROUTINEA pfCreateProcessA); + +BOOL WINAPI DetourCreateProcessWithDllsW(_In_opt_ LPCWSTR lpApplicationName, + _Inout_opt_ LPWSTR lpCommandLine, + _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes, + _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, + _In_ BOOL bInheritHandles, + _In_ DWORD dwCreationFlags, + _In_opt_ LPVOID lpEnvironment, + _In_opt_ LPCWSTR lpCurrentDirectory, + _In_ LPSTARTUPINFOW lpStartupInfo, + _Out_ LPPROCESS_INFORMATION lpProcessInformation, + _In_ DWORD nDlls, + _In_reads_(nDlls) LPCSTR *rlpDlls, + _In_opt_ PDETOUR_CREATE_PROCESS_ROUTINEW pfCreateProcessW); + +#ifdef UNICODE +#define DetourCreateProcessWithDlls DetourCreateProcessWithDllsW +#else +#define DetourCreateProcessWithDlls DetourCreateProcessWithDllsA +#endif // !UNICODE + +BOOL WINAPI DetourProcessViaHelperA(_In_ DWORD dwTargetPid, + _In_ LPCSTR lpDllName, + _In_ PDETOUR_CREATE_PROCESS_ROUTINEA pfCreateProcessA); + +BOOL WINAPI DetourProcessViaHelperW(_In_ DWORD dwTargetPid, + _In_ LPCSTR lpDllName, + _In_ PDETOUR_CREATE_PROCESS_ROUTINEW pfCreateProcessW); + +#ifdef UNICODE +#define DetourProcessViaHelper DetourProcessViaHelperW +#else +#define DetourProcessViaHelper DetourProcessViaHelperA +#endif // !UNICODE + +BOOL WINAPI DetourProcessViaHelperDllsA(_In_ DWORD dwTargetPid, + _In_ DWORD nDlls, + _In_reads_(nDlls) LPCSTR *rlpDlls, + _In_ PDETOUR_CREATE_PROCESS_ROUTINEA pfCreateProcessA); + +BOOL WINAPI DetourProcessViaHelperDllsW(_In_ DWORD dwTargetPid, + _In_ DWORD nDlls, + _In_reads_(nDlls) LPCSTR *rlpDlls, + _In_ PDETOUR_CREATE_PROCESS_ROUTINEW pfCreateProcessW); + +#ifdef UNICODE +#define DetourProcessViaHelperDlls DetourProcessViaHelperDllsW +#else +#define DetourProcessViaHelperDlls DetourProcessViaHelperDllsA +#endif // !UNICODE + +BOOL WINAPI DetourUpdateProcessWithDll(_In_ HANDLE hProcess, + _In_reads_(nDlls) LPCSTR *rlpDlls, + _In_ DWORD nDlls); + +BOOL WINAPI DetourUpdateProcessWithDllEx(_In_ HANDLE hProcess, + _In_ HMODULE hImage, + _In_ BOOL bIs32Bit, + _In_reads_(nDlls) LPCSTR *rlpDlls, + _In_ DWORD nDlls); + +BOOL WINAPI DetourCopyPayloadToProcess(_In_ HANDLE hProcess, + _In_ REFGUID rguid, + _In_reads_bytes_(cbData) PVOID pvData, + _In_ DWORD cbData); +BOOL WINAPI DetourRestoreAfterWith(VOID); +BOOL WINAPI DetourRestoreAfterWithEx(_In_reads_bytes_(cbData) PVOID pvData, + _In_ DWORD cbData); +BOOL WINAPI DetourIsHelperProcess(VOID); +VOID CALLBACK DetourFinishHelperProcess(_In_ HWND, + _In_ HINSTANCE, + _In_ LPSTR, + _In_ INT); + +// +////////////////////////////////////////////////////////////////////////////// +#ifdef __cplusplus +} +#endif // __cplusplus + +//////////////////////////////////////////////// Detours Internal Definitions. +// +#ifdef __cplusplus +#ifdef DETOURS_INTERNAL + +#define NOTHROW +// #define NOTHROW (nothrow) + +////////////////////////////////////////////////////////////////////////////// +// +#if (_MSC_VER < 1299) +#include +typedef IMAGEHLP_MODULE IMAGEHLP_MODULE64; +typedef PIMAGEHLP_MODULE PIMAGEHLP_MODULE64; +typedef IMAGEHLP_SYMBOL SYMBOL_INFO; +typedef PIMAGEHLP_SYMBOL PSYMBOL_INFO; + +static inline +LONG InterlockedCompareExchange(_Inout_ LONG *ptr, _In_ LONG nval, _In_ LONG oval) +{ + return (LONG)::InterlockedCompareExchange((PVOID*)ptr, (PVOID)nval, (PVOID)oval); +} +#else +#pragma warning(push) +#pragma warning(disable:4091) // empty typedef +#include +#pragma warning(pop) +#endif + +#ifdef IMAGEAPI // defined by DBGHELP.H +typedef LPAPI_VERSION (NTAPI *PF_ImagehlpApiVersionEx)(_In_ LPAPI_VERSION AppVersion); + +typedef BOOL (NTAPI *PF_SymInitialize)(_In_ HANDLE hProcess, + _In_opt_ LPCSTR UserSearchPath, + _In_ BOOL fInvadeProcess); +typedef DWORD (NTAPI *PF_SymSetOptions)(_In_ DWORD SymOptions); +typedef DWORD (NTAPI *PF_SymGetOptions)(VOID); +typedef DWORD64 (NTAPI *PF_SymLoadModule64)(_In_ HANDLE hProcess, + _In_opt_ HANDLE hFile, + _In_ LPSTR ImageName, + _In_opt_ LPSTR ModuleName, + _In_ DWORD64 BaseOfDll, + _In_opt_ DWORD SizeOfDll); +typedef BOOL (NTAPI *PF_SymGetModuleInfo64)(_In_ HANDLE hProcess, + _In_ DWORD64 qwAddr, + _Out_ PIMAGEHLP_MODULE64 ModuleInfo); +typedef BOOL (NTAPI *PF_SymFromName)(_In_ HANDLE hProcess, + _In_ LPSTR Name, + _Out_ PSYMBOL_INFO Symbol); + +typedef struct _DETOUR_SYM_INFO +{ + HANDLE hProcess; + HMODULE hDbgHelp; + PF_ImagehlpApiVersionEx pfImagehlpApiVersionEx; + PF_SymInitialize pfSymInitialize; + PF_SymSetOptions pfSymSetOptions; + PF_SymGetOptions pfSymGetOptions; + PF_SymLoadModule64 pfSymLoadModule64; + PF_SymGetModuleInfo64 pfSymGetModuleInfo64; + PF_SymFromName pfSymFromName; +} DETOUR_SYM_INFO, *PDETOUR_SYM_INFO; + +PDETOUR_SYM_INFO DetourLoadImageHlp(VOID); + +#endif // IMAGEAPI + +#if defined(_INC_STDIO) && !defined(_CRT_STDIO_ARBITRARY_WIDE_SPECIFIERS) +#error detours.h must be included before stdio.h (or at least define _CRT_STDIO_ARBITRARY_WIDE_SPECIFIERS earlier) +#endif +#define _CRT_STDIO_ARBITRARY_WIDE_SPECIFIERS 1 + +#ifndef DETOUR_TRACE +#if DETOUR_DEBUG +#define DETOUR_TRACE(x) printf x +#define DETOUR_BREAK() __debugbreak() +#include +#include +#else +#define DETOUR_TRACE(x) +#define DETOUR_BREAK() +#endif +#endif + +#if 1 || defined(DETOURS_IA64) + +// +// IA64 instructions are 41 bits, 3 per bundle, plus 5 bit bundle template => 128 bits per bundle. +// + +#define DETOUR_IA64_INSTRUCTIONS_PER_BUNDLE (3) + +#define DETOUR_IA64_TEMPLATE_OFFSET (0) +#define DETOUR_IA64_TEMPLATE_SIZE (5) + +#define DETOUR_IA64_INSTRUCTION_SIZE (41) +#define DETOUR_IA64_INSTRUCTION0_OFFSET (DETOUR_IA64_TEMPLATE_SIZE) +#define DETOUR_IA64_INSTRUCTION1_OFFSET (DETOUR_IA64_TEMPLATE_SIZE + DETOUR_IA64_INSTRUCTION_SIZE) +#define DETOUR_IA64_INSTRUCTION2_OFFSET (DETOUR_IA64_TEMPLATE_SIZE + DETOUR_IA64_INSTRUCTION_SIZE + DETOUR_IA64_INSTRUCTION_SIZE) + +C_ASSERT(DETOUR_IA64_TEMPLATE_SIZE + DETOUR_IA64_INSTRUCTIONS_PER_BUNDLE * DETOUR_IA64_INSTRUCTION_SIZE == 128); + +__declspec(align(16)) struct DETOUR_IA64_BUNDLE +{ + public: + union + { + BYTE data[16]; + UINT64 wide[2]; + }; + + enum { + A_UNIT = 1u, + I_UNIT = 2u, + M_UNIT = 3u, + B_UNIT = 4u, + F_UNIT = 5u, + L_UNIT = 6u, + X_UNIT = 7u, + }; + struct DETOUR_IA64_METADATA + { + ULONG nTemplate : 8; // Instruction template. + ULONG nUnit0 : 4; // Unit for slot 0 + ULONG nUnit1 : 4; // Unit for slot 1 + ULONG nUnit2 : 4; // Unit for slot 2 + }; + + protected: + static const DETOUR_IA64_METADATA s_rceCopyTable[33]; + + UINT RelocateBundle(_Inout_ DETOUR_IA64_BUNDLE* pDst, _Inout_opt_ DETOUR_IA64_BUNDLE* pBundleExtra) const; + + bool RelocateInstruction(_Inout_ DETOUR_IA64_BUNDLE* pDst, + _In_ BYTE slot, + _Inout_opt_ DETOUR_IA64_BUNDLE* pBundleExtra) const; + + // 120 112 104 96 88 80 72 64 56 48 40 32 24 16 8 0 + // f. e. d. c. b. a. 9. 8. 7. 6. 5. 4. 3. 2. 1. 0. + + // 00 + // f.e. d.c. b.a. 9.8. 7.6. 5.4. 3.2. 1.0. + // 0000 0000 0000 0000 0000 0000 0000 001f : Template [4..0] + // 0000 0000 0000 0000 0000 03ff ffff ffe0 : Zero [ 41.. 5] + // 0000 0000 0000 0000 0000 3c00 0000 0000 : Zero [ 45.. 42] + // 0000 0000 0007 ffff ffff c000 0000 0000 : One [ 82.. 46] + // 0000 0000 0078 0000 0000 0000 0000 0000 : One [ 86.. 83] + // 0fff ffff ff80 0000 0000 0000 0000 0000 : Two [123.. 87] + // f000 0000 0000 0000 0000 0000 0000 0000 : Two [127..124] + BYTE GetTemplate() const; + // Get 4 bit opcodes. + BYTE GetInst0() const; + BYTE GetInst1() const; + BYTE GetInst2() const; + BYTE GetUnit(BYTE slot) const; + BYTE GetUnit0() const; + BYTE GetUnit1() const; + BYTE GetUnit2() const; + // Get 37 bit data. + UINT64 GetData0() const; + UINT64 GetData1() const; + UINT64 GetData2() const; + + // Get/set the full 41 bit instructions. + UINT64 GetInstruction(BYTE slot) const; + UINT64 GetInstruction0() const; + UINT64 GetInstruction1() const; + UINT64 GetInstruction2() const; + void SetInstruction(BYTE slot, UINT64 instruction); + void SetInstruction0(UINT64 instruction); + void SetInstruction1(UINT64 instruction); + void SetInstruction2(UINT64 instruction); + + // Get/set bitfields. + static UINT64 GetBits(UINT64 Value, UINT64 Offset, UINT64 Count); + static UINT64 SetBits(UINT64 Value, UINT64 Offset, UINT64 Count, UINT64 Field); + + // Get specific read-only fields. + static UINT64 GetOpcode(UINT64 instruction); // 4bit opcode + static UINT64 GetX(UINT64 instruction); // 1bit opcode extension + static UINT64 GetX3(UINT64 instruction); // 3bit opcode extension + static UINT64 GetX6(UINT64 instruction); // 6bit opcode extension + + // Get/set specific fields. + static UINT64 GetImm7a(UINT64 instruction); + static UINT64 SetImm7a(UINT64 instruction, UINT64 imm7a); + static UINT64 GetImm13c(UINT64 instruction); + static UINT64 SetImm13c(UINT64 instruction, UINT64 imm13c); + static UINT64 GetSignBit(UINT64 instruction); + static UINT64 SetSignBit(UINT64 instruction, UINT64 signBit); + static UINT64 GetImm20a(UINT64 instruction); + static UINT64 SetImm20a(UINT64 instruction, UINT64 imm20a); + static UINT64 GetImm20b(UINT64 instruction); + static UINT64 SetImm20b(UINT64 instruction, UINT64 imm20b); + + static UINT64 SignExtend(UINT64 Value, UINT64 Offset); + + BOOL IsMovlGp() const; + + VOID SetInst(BYTE Slot, BYTE nInst); + VOID SetInst0(BYTE nInst); + VOID SetInst1(BYTE nInst); + VOID SetInst2(BYTE nInst); + VOID SetData(BYTE Slot, UINT64 nData); + VOID SetData0(UINT64 nData); + VOID SetData1(UINT64 nData); + VOID SetData2(UINT64 nData); + BOOL SetNop(BYTE Slot); + BOOL SetNop0(); + BOOL SetNop1(); + BOOL SetNop2(); + + public: + BOOL IsBrl() const; + VOID SetBrl(); + VOID SetBrl(UINT64 target); + UINT64 GetBrlTarget() const; + VOID SetBrlTarget(UINT64 target); + VOID SetBrlImm(UINT64 imm); + UINT64 GetBrlImm() const; + + UINT64 GetMovlGp() const; + VOID SetMovlGp(UINT64 gp); + + VOID SetStop(); + + UINT Copy(_Out_ DETOUR_IA64_BUNDLE *pDst, _Inout_opt_ DETOUR_IA64_BUNDLE* pBundleExtra = NULL) const; +}; +#endif // DETOURS_IA64 + +#ifdef DETOURS_ARM + +#define DETOURS_PFUNC_TO_PBYTE(p) ((PBYTE)(((ULONG_PTR)(p)) & ~(ULONG_PTR)1)) +#define DETOURS_PBYTE_TO_PFUNC(p) ((PBYTE)(((ULONG_PTR)(p)) | (ULONG_PTR)1)) + +#endif // DETOURS_ARM + +////////////////////////////////////////////////////////////////////////////// + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +#define DETOUR_OFFLINE_LIBRARY(x) \ +PVOID WINAPI DetourCopyInstruction##x(_In_opt_ PVOID pDst, \ + _Inout_opt_ PVOID *ppDstPool, \ + _In_ PVOID pSrc, \ + _Out_opt_ PVOID *ppTarget, \ + _Out_opt_ LONG *plExtra); \ + \ +BOOL WINAPI DetourSetCodeModule##x(_In_ HMODULE hModule, \ + _In_ BOOL fLimitReferencesToModule); \ + +DETOUR_OFFLINE_LIBRARY(X86) +DETOUR_OFFLINE_LIBRARY(X64) +DETOUR_OFFLINE_LIBRARY(ARM) +DETOUR_OFFLINE_LIBRARY(ARM64) +DETOUR_OFFLINE_LIBRARY(IA64) + +#undef DETOUR_OFFLINE_LIBRARY + +////////////////////////////////////////////////////////////////////////////// +// +// Helpers for manipulating page protection. +// + +_Success_(return != FALSE) +BOOL WINAPI DetourVirtualProtectSameExecuteEx(_In_ HANDLE hProcess, + _In_ PVOID pAddress, + _In_ SIZE_T nSize, + _In_ DWORD dwNewProtect, + _Out_ PDWORD pdwOldProtect); + +_Success_(return != FALSE) +BOOL WINAPI DetourVirtualProtectSameExecute(_In_ PVOID pAddress, + _In_ SIZE_T nSize, + _In_ DWORD dwNewProtect, + _Out_ PDWORD pdwOldProtect); +#ifdef __cplusplus +} +#endif // __cplusplus + +////////////////////////////////////////////////////////////////////////////// + +#define MM_ALLOCATION_GRANULARITY 0x10000 + +////////////////////////////////////////////////////////////////////////////// + +#endif // DETOURS_INTERNAL +#endif // __cplusplus + +#endif // _DETOURS_H_ +// +//////////////////////////////////////////////////////////////// End of File. diff --git a/dependencies/detours/include/detver.h b/dependencies/detours/include/detver.h new file mode 100644 index 0000000..f0aae9b --- /dev/null +++ b/dependencies/detours/include/detver.h @@ -0,0 +1,27 @@ +////////////////////////////////////////////////////////////////////////////// +// +// Common version parameters. +// +// Microsoft Research Detours Package, Version 4.0.1 +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +#define _USING_V110_SDK71_ 1 +#include "winver.h" +#if 0 +#include +#include +#else +#ifndef DETOURS_STRINGIFY +#define DETOURS_STRINGIFY(x) DETOURS_STRINGIFY_(x) +#define DETOURS_STRINGIFY_(x) #x +#endif + +#define VER_FILEFLAGSMASK 0x3fL +#define VER_FILEFLAGS 0x0L +#define VER_FILEOS 0x00040004L +#define VER_FILETYPE 0x00000002L +#define VER_FILESUBTYPE 0x00000000L +#endif +#define VER_DETOURS_BITS DETOUR_STRINGIFY(DETOURS_BITS) diff --git a/dependencies/detours/include/syelog.h b/dependencies/detours/include/syelog.h new file mode 100644 index 0000000..7cfa9f3 --- /dev/null +++ b/dependencies/detours/include/syelog.h @@ -0,0 +1,89 @@ +////////////////////////////////////////////////////////////////////////////// +// +// Detours Test Program (syelog.h of syelog.lib) +// +// Microsoft Research Detours Package +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +#pragma once +#ifndef _SYELOGD_H_ +#define _SYELOGD_H_ +#include + +#pragma pack(push, 1) +#pragma warning(push) +#pragma warning(disable: 4200) + +////////////////////////////////////////////////////////////////////////////// +// +// +#define SYELOG_PIPE_NAMEA "\\\\.\\pipe\\syelog" +#define SYELOG_PIPE_NAMEW L"\\\\.\\pipe\\syelog" +#ifdef UNICODE +#define SYELOG_PIPE_NAME SYELOG_PIPE_NAMEW +#else +#define SYELOG_PIPE_NAME SYELOG_PIPE_NAMEA +#endif + +////////////////////////////////////////////////////////////////////////////// +// +#define SYELOG_MAXIMUM_MESSAGE 4086 // 4096 - sizeof(header stuff) + +typedef struct _SYELOG_MESSAGE +{ + USHORT nBytes; + BYTE nFacility; + BYTE nSeverity; + DWORD nProcessId; + FILETIME ftOccurance; + BOOL fTerminate; + CHAR szMessage[SYELOG_MAXIMUM_MESSAGE]; +} SYELOG_MESSAGE, *PSYELOG_MESSAGE; + + +// Facility Codes. +// +#define SYELOG_FACILITY_KERNEL 0x10 // OS Kernel +#define SYELOG_FACILITY_SECURITY 0x20 // OS Security +#define SYELOG_FACILITY_LOGGING 0x30 // OS Logging-internal +#define SYELOG_FACILITY_SERVICE 0x40 // User-mode system daemon +#define SYELOG_FACILITY_APPLICATION 0x50 // User-mode application +#define SYELOG_FACILITY_USER 0x60 // User self-generated. +#define SYELOG_FACILITY_LOCAL0 0x70 // Locally defined. +#define SYELOG_FACILITY_LOCAL1 0x71 // Locally defined. +#define SYELOG_FACILITY_LOCAL2 0x72 // Locally defined. +#define SYELOG_FACILITY_LOCAL3 0x73 // Locally defined. +#define SYELOG_FACILITY_LOCAL4 0x74 // Locally defined. +#define SYELOG_FACILITY_LOCAL5 0x75 // Locally defined. +#define SYELOG_FACILITY_LOCAL6 0x76 // Locally defined. +#define SYELOG_FACILITY_LOCAL7 0x77 // Locally defined. +#define SYELOG_FACILITY_LOCAL8 0x78 // Locally defined. +#define SYELOG_FACILITY_LOCAL9 0x79 // Locally defined. + +// Severity Codes. +// +#define SYELOG_SEVERITY_FATAL 0x00 // System is dead. +#define SYELOG_SEVERITY_ALERT 0x10 // Take action immediately. +#define SYELOG_SEVERITY_CRITICAL 0x20 // Critical condition. +#define SYELOG_SEVERITY_ERROR 0x30 // Error +#define SYELOG_SEVERITY_WARNING 0x40 // Warning +#define SYELOG_SEVERITY_NOTICE 0x50 // Significant condition. +#define SYELOG_SEVERITY_INFORMATION 0x60 // Informational +#define SYELOG_SEVERITY_AUDIT_FAIL 0x66 // Audit Failed +#define SYELOG_SEVERITY_AUDIT_PASS 0x67 // Audit Succeeeded +#define SYELOG_SEVERITY_DEBUG 0x70 // Debugging + +// Logging Functions. +// +VOID SyelogOpen(PCSTR pszIdentifier, BYTE nFacility); +VOID Syelog(BYTE nSeverity, PCSTR pszMsgf, ...); +VOID SyelogV(BYTE nSeverity, PCSTR pszMsgf, va_list args); +VOID SyelogClose(BOOL fTerminate); + +#pragma warning(pop) +#pragma pack(pop) + +#endif // _SYELOGD_H_ +// +///////////////////////////////////////////////////////////////// End of File. diff --git a/dependencies/detours/lib/detours.lib b/dependencies/detours/lib/detours.lib new file mode 100644 index 0000000..13ac97b Binary files /dev/null and b/dependencies/detours/lib/detours.lib differ diff --git a/dependencies/detours/lib/syelog.lib b/dependencies/detours/lib/syelog.lib new file mode 100644 index 0000000..215789f Binary files /dev/null and b/dependencies/detours/lib/syelog.lib differ diff --git a/dependencies/freeglut/include/GL/freeglut.h b/dependencies/freeglut/include/GL/freeglut.h new file mode 100644 index 0000000..0e6f8c6 --- /dev/null +++ b/dependencies/freeglut/include/GL/freeglut.h @@ -0,0 +1,22 @@ +#ifndef __FREEGLUT_H__ +#define __FREEGLUT_H__ + +/* + * freeglut.h + * + * The freeglut library include file + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PAWEL W. OLSZTA BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "freeglut_std.h" +#include "freeglut_ext.h" + +/*** END OF FILE ***/ + +#endif /* __FREEGLUT_H__ */ diff --git a/dependencies/freeglut/include/GL/freeglut_ext.h b/dependencies/freeglut/include/GL/freeglut_ext.h new file mode 100644 index 0000000..932a16b --- /dev/null +++ b/dependencies/freeglut/include/GL/freeglut_ext.h @@ -0,0 +1,288 @@ +#ifndef __FREEGLUT_EXT_H__ +#define __FREEGLUT_EXT_H__ + +/* + * freeglut_ext.h + * + * The non-GLUT-compatible extensions to the freeglut library include file + * + * Copyright (c) 1999-2000 Pawel W. Olszta. All Rights Reserved. + * Written by Pawel W. Olszta, + * Creation date: Thu Dec 2 1999 + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PAWEL W. OLSZTA BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifdef __cplusplus + extern "C" { +#endif + +/* + * Additional GLUT Key definitions for the Special key function + */ +#define GLUT_KEY_NUM_LOCK 0x006D +#define GLUT_KEY_BEGIN 0x006E +#define GLUT_KEY_DELETE 0x006F +#define GLUT_KEY_SHIFT_L 0x0070 +#define GLUT_KEY_SHIFT_R 0x0071 +#define GLUT_KEY_CTRL_L 0x0072 +#define GLUT_KEY_CTRL_R 0x0073 +#define GLUT_KEY_ALT_L 0x0074 +#define GLUT_KEY_ALT_R 0x0075 + +/* + * GLUT API Extension macro definitions -- behaviour when the user clicks on an "x" to close a window + */ +#define GLUT_ACTION_EXIT 0 +#define GLUT_ACTION_GLUTMAINLOOP_RETURNS 1 +#define GLUT_ACTION_CONTINUE_EXECUTION 2 + +/* + * Create a new rendering context when the user opens a new window? + */ +#define GLUT_CREATE_NEW_CONTEXT 0 +#define GLUT_USE_CURRENT_CONTEXT 1 + +/* + * Direct/Indirect rendering context options (has meaning only in Unix/X11) + */ +#define GLUT_FORCE_INDIRECT_CONTEXT 0 +#define GLUT_ALLOW_DIRECT_CONTEXT 1 +#define GLUT_TRY_DIRECT_CONTEXT 2 +#define GLUT_FORCE_DIRECT_CONTEXT 3 + +/* + * GLUT API Extension macro definitions -- the glutGet parameters + */ +#define GLUT_INIT_STATE 0x007C + +#define GLUT_ACTION_ON_WINDOW_CLOSE 0x01F9 + +#define GLUT_WINDOW_BORDER_WIDTH 0x01FA +#define GLUT_WINDOW_BORDER_HEIGHT 0x01FB +#define GLUT_WINDOW_HEADER_HEIGHT 0x01FB /* Docs say it should always have been GLUT_WINDOW_BORDER_HEIGHT, keep this for backward compatibility */ + +#define GLUT_VERSION 0x01FC + +#define GLUT_RENDERING_CONTEXT 0x01FD +#define GLUT_DIRECT_RENDERING 0x01FE + +#define GLUT_FULL_SCREEN 0x01FF + +#define GLUT_SKIP_STALE_MOTION_EVENTS 0x0204 + +#define GLUT_GEOMETRY_VISUALIZE_NORMALS 0x0205 + +#define GLUT_STROKE_FONT_DRAW_JOIN_DOTS 0x0206 /* Draw dots between line segments of stroke fonts? */ + +#define GLUT_ALLOW_NEGATIVE_WINDOW_POSITION 0x0207 /* GLUT doesn't allow negative window positions by default */ + +#define GLUT_WINDOW_SRGB 0x007D + +/* + * New tokens for glutInitDisplayMode. + * Only one GLUT_AUXn bit may be used at a time. + * Value 0x0400 is defined in OpenGLUT. + */ +#define GLUT_AUX 0x1000 + +#define GLUT_AUX1 0x1000 +#define GLUT_AUX2 0x2000 +#define GLUT_AUX3 0x4000 +#define GLUT_AUX4 0x8000 + +/* + * Context-related flags, see fg_state.c + * Set the requested OpenGL version + */ +#define GLUT_INIT_MAJOR_VERSION 0x0200 +#define GLUT_INIT_MINOR_VERSION 0x0201 +#define GLUT_INIT_FLAGS 0x0202 +#define GLUT_INIT_PROFILE 0x0203 + +/* + * Flags for glutInitContextFlags, see fg_init.c + */ +#define GLUT_DEBUG 0x0001 +#define GLUT_FORWARD_COMPATIBLE 0x0002 + + +/* + * Flags for glutInitContextProfile, see fg_init.c + */ +#define GLUT_CORE_PROFILE 0x0001 +#define GLUT_COMPATIBILITY_PROFILE 0x0002 + +/* +* GLUT API Extension macro definitions -- Spaceball button definitions +*/ + +#define GLUT_SPACEBALL_BUTTON_A 0x0001 +#define GLUT_SPACEBALL_BUTTON_B 0x0002 +#define GLUT_SPACEBALL_BUTTON_C 0x0004 +#define GLUT_SPACEBALL_BUTTON_D 0x0008 +#define GLUT_SPACEBALL_BUTTON_E 0x0010 + +/* + * Process loop function, see fg_main.c + */ +FGAPI void FGAPIENTRY glutMainLoopEvent( void ); +FGAPI void FGAPIENTRY glutLeaveMainLoop( void ); +FGAPI void FGAPIENTRY glutExit ( void ); + +/* + * Window management functions, see fg_window.c + */ +FGAPI void FGAPIENTRY glutFullScreenToggle( void ); +FGAPI void FGAPIENTRY glutLeaveFullScreen( void ); + +/* + * Menu functions + */ +FGAPI void FGAPIENTRY glutSetMenuFont( int menuID, void* font ); + +/* + * Window-specific callback functions, see fg_callbacks.c + */ +FGAPI void FGAPIENTRY glutMouseWheelFunc( void (* callback)( int, int, int, int ) ); +FGAPI void FGAPIENTRY glutPositionFunc( void (* callback)( int, int ) ); +FGAPI void FGAPIENTRY glutCloseFunc( void (* callback)( void ) ); +FGAPI void FGAPIENTRY glutWMCloseFunc( void (* callback)( void ) ); +/* And also a destruction callback for menus */ +FGAPI void FGAPIENTRY glutMenuDestroyFunc( void (* callback)( void ) ); + +/* + * State setting and retrieval functions, see fg_state.c + */ +FGAPI void FGAPIENTRY glutSetOption ( GLenum option_flag, int value ); +FGAPI int * FGAPIENTRY glutGetModeValues(GLenum mode, int * size); +/* A.Donev: User-data manipulation */ +FGAPI void* FGAPIENTRY glutGetWindowData( void ); +FGAPI void FGAPIENTRY glutSetWindowData(void* data); +FGAPI void* FGAPIENTRY glutGetMenuData( void ); +FGAPI void FGAPIENTRY glutSetMenuData(void* data); + +/* + * Font stuff, see fg_font.c + */ +FGAPI int FGAPIENTRY glutBitmapHeight( void* font ); +FGAPI GLfloat FGAPIENTRY glutStrokeHeight( void* font ); +FGAPI void FGAPIENTRY glutBitmapString( void* font, const unsigned char *string ); +FGAPI void FGAPIENTRY glutStrokeString( void* font, const unsigned char *string ); + +/* + * Geometry functions, see fg_geometry.c + */ +FGAPI void FGAPIENTRY glutWireRhombicDodecahedron( void ); +FGAPI void FGAPIENTRY glutSolidRhombicDodecahedron( void ); +FGAPI void FGAPIENTRY glutWireSierpinskiSponge ( int num_levels, double offset[3], double scale ); +FGAPI void FGAPIENTRY glutSolidSierpinskiSponge ( int num_levels, double offset[3], double scale ); +FGAPI void FGAPIENTRY glutWireCylinder( double radius, double height, GLint slices, GLint stacks); +FGAPI void FGAPIENTRY glutSolidCylinder( double radius, double height, GLint slices, GLint stacks); + +/* + * Rest of functions for rendering Newell's teaset, found in fg_teapot.c + * NB: front facing polygons have clockwise winding, not counter clockwise + */ +FGAPI void FGAPIENTRY glutWireTeacup( double size ); +FGAPI void FGAPIENTRY glutSolidTeacup( double size ); +FGAPI void FGAPIENTRY glutWireTeaspoon( double size ); +FGAPI void FGAPIENTRY glutSolidTeaspoon( double size ); + +/* + * Extension functions, see fg_ext.c + */ +typedef void (*GLUTproc)(); +FGAPI GLUTproc FGAPIENTRY glutGetProcAddress( const char *procName ); + +/* + * Multi-touch/multi-pointer extensions + */ + +#define GLUT_HAS_MULTI 1 + +/* TODO: add device_id parameter, + cf. http://sourceforge.net/mailarchive/forum.php?thread_name=20120518071314.GA28061%40perso.beuc.net&forum_name=freeglut-developer */ +FGAPI void FGAPIENTRY glutMultiEntryFunc( void (* callback)( int, int ) ); +FGAPI void FGAPIENTRY glutMultiButtonFunc( void (* callback)( int, int, int, int, int ) ); +FGAPI void FGAPIENTRY glutMultiMotionFunc( void (* callback)( int, int, int ) ); +FGAPI void FGAPIENTRY glutMultiPassiveFunc( void (* callback)( int, int, int ) ); + +/* + * Joystick functions, see fg_joystick.c + */ +/* USE OF THESE FUNCTIONS IS DEPRECATED !!!!! */ +/* If you have a serious need for these functions in your application, please either + * contact the "freeglut" developer community at freeglut-developer@lists.sourceforge.net, + * switch to the OpenGLUT library, or else port your joystick functionality over to PLIB's + * "js" library. + */ +int glutJoystickGetNumAxes( int ident ); +int glutJoystickGetNumButtons( int ident ); +int glutJoystickNotWorking( int ident ); +float glutJoystickGetDeadBand( int ident, int axis ); +void glutJoystickSetDeadBand( int ident, int axis, float db ); +float glutJoystickGetSaturation( int ident, int axis ); +void glutJoystickSetSaturation( int ident, int axis, float st ); +void glutJoystickSetMinRange( int ident, float *axes ); +void glutJoystickSetMaxRange( int ident, float *axes ); +void glutJoystickSetCenter( int ident, float *axes ); +void glutJoystickGetMinRange( int ident, float *axes ); +void glutJoystickGetMaxRange( int ident, float *axes ); +void glutJoystickGetCenter( int ident, float *axes ); + +/* + * Initialization functions, see fg_init.c + */ +/* to get the typedef for va_list */ +#include +FGAPI void FGAPIENTRY glutInitContextVersion( int majorVersion, int minorVersion ); +FGAPI void FGAPIENTRY glutInitContextFlags( int flags ); +FGAPI void FGAPIENTRY glutInitContextProfile( int profile ); +FGAPI void FGAPIENTRY glutInitErrorFunc( void (* callback)( const char *fmt, va_list ap ) ); +FGAPI void FGAPIENTRY glutInitWarningFunc( void (* callback)( const char *fmt, va_list ap ) ); + +/* OpenGL >= 2.0 support */ +FGAPI void FGAPIENTRY glutSetVertexAttribCoord3( GLint attrib ); +FGAPI void FGAPIENTRY glutSetVertexAttribNormal( GLint attrib ); +FGAPI void FGAPIENTRY glutSetVertexAttribTexCoord2( GLint attrib ); + +/* Mobile platforms lifecycle */ +FGAPI void FGAPIENTRY glutInitContextFunc( void (* callback)( void ) ); +FGAPI void FGAPIENTRY glutAppStatusFunc( void (* callback)( int ) ); +/* state flags that can be passed to callback set by glutAppStatusFunc */ +#define GLUT_APPSTATUS_PAUSE 0x0001 +#define GLUT_APPSTATUS_RESUME 0x0002 + +/* + * GLUT API macro definitions -- the display mode definitions + */ +#define GLUT_CAPTIONLESS 0x0400 +#define GLUT_BORDERLESS 0x0800 +#define GLUT_SRGB 0x1000 + +/* User-argument callbacks and implementation */ +#include "freeglut_ucall.h" + +#ifdef __cplusplus + } +#endif + +/*** END OF FILE ***/ + +#endif /* __FREEGLUT_EXT_H__ */ diff --git a/dependencies/freeglut/include/GL/freeglut_std.h b/dependencies/freeglut/include/GL/freeglut_std.h new file mode 100644 index 0000000..aa4795a --- /dev/null +++ b/dependencies/freeglut/include/GL/freeglut_std.h @@ -0,0 +1,654 @@ +#ifndef __FREEGLUT_STD_H__ +#define __FREEGLUT_STD_H__ + +/* + * freeglut_std.h + * + * The GLUT-compatible part of the freeglut library include file + * + * Copyright (c) 1999-2000 Pawel W. Olszta. All Rights Reserved. + * Written by Pawel W. Olszta, + * Creation date: Thu Dec 2 1999 + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PAWEL W. OLSZTA BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifdef __cplusplus + extern "C" { +#endif + +/* + * Under windows, we have to differentiate between static and dynamic libraries + */ +#ifdef _WIN32 +/* #pragma may not be supported by some compilers. + * Discussion by FreeGLUT developers suggests that + * Visual C++ specific code involving pragmas may + * need to move to a separate header. 24th Dec 2003 + */ + +/* Define FREEGLUT_LIB_PRAGMAS to 1 to include library + * pragmas or to 0 to exclude library pragmas. + * The default behavior depends on the compiler/platform. + */ +# ifndef FREEGLUT_LIB_PRAGMAS +# if ( defined(_MSC_VER) || defined(__WATCOMC__) ) && !defined(_WIN32_WCE) +# define FREEGLUT_LIB_PRAGMAS 1 +# else +# define FREEGLUT_LIB_PRAGMAS 0 +# endif +# endif + +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN 1 +# endif +# ifndef NOMINMAX +# define NOMINMAX +# endif +# include + +/* Windows static library */ +# ifdef FREEGLUT_STATIC + +# define FGAPI +# define FGAPIENTRY + + /* Link with Win32 static freeglut lib */ +# if FREEGLUT_LIB_PRAGMAS +# ifdef NDEBUG +# pragma comment (lib, "freeglut_static.lib") +# else +# pragma comment (lib, "freeglut_staticd.lib") +# endif +# endif + +/* Windows shared library (DLL) */ +# else + +# define FGAPIENTRY __stdcall +# if defined(FREEGLUT_EXPORTS) +# define FGAPI __declspec(dllexport) +# else +# define FGAPI __declspec(dllimport) + + /* Link with Win32 shared freeglut lib */ +# if FREEGLUT_LIB_PRAGMAS +# ifdef NDEBUG +# pragma comment (lib, "freeglut.lib") +# else +# pragma comment (lib, "freeglutd.lib") +# endif +# endif + +# endif + +# endif + +/* Drag in other Windows libraries as required by FreeGLUT */ +# if FREEGLUT_LIB_PRAGMAS +# pragma comment (lib, "glu32.lib") /* link OpenGL Utility lib */ +# pragma comment (lib, "opengl32.lib") /* link Microsoft OpenGL lib */ +# pragma comment (lib, "gdi32.lib") /* link Windows GDI lib */ +# pragma comment (lib, "winmm.lib") /* link Windows MultiMedia lib */ +# pragma comment (lib, "user32.lib") /* link Windows user lib */ +# endif + +#else + +/* Non-Windows definition of FGAPI and FGAPIENTRY */ +# define FGAPI +# define FGAPIENTRY + +#endif + +/* + * The freeglut and GLUT API versions + */ +#define FREEGLUT 1 +#define GLUT_API_VERSION 4 +#define GLUT_XLIB_IMPLEMENTATION 13 +/* Deprecated: + cf. http://sourceforge.net/mailarchive/forum.php?thread_name=CABcAi1hw7cr4xtigckaGXB5X8wddLfMcbA_rZ3NAuwMrX_zmsw%40mail.gmail.com&forum_name=freeglut-developer */ +#define FREEGLUT_VERSION_2_0 1 + +/* + * Always include OpenGL and GLU headers + */ +/* Note: FREEGLUT_GLES is only used to cleanly bootstrap headers + inclusion here; use GLES constants directly + (e.g. GL_ES_VERSION_2_0) for all other needs */ +#ifdef FREEGLUT_GLES +# include +# include +# include +#elif __APPLE__ +# include +# include +#else +# include +# include +#endif + +/* + * GLUT API macro definitions -- the special key codes: + */ +#define GLUT_KEY_F1 0x0001 +#define GLUT_KEY_F2 0x0002 +#define GLUT_KEY_F3 0x0003 +#define GLUT_KEY_F4 0x0004 +#define GLUT_KEY_F5 0x0005 +#define GLUT_KEY_F6 0x0006 +#define GLUT_KEY_F7 0x0007 +#define GLUT_KEY_F8 0x0008 +#define GLUT_KEY_F9 0x0009 +#define GLUT_KEY_F10 0x000A +#define GLUT_KEY_F11 0x000B +#define GLUT_KEY_F12 0x000C +#define GLUT_KEY_LEFT 0x0064 +#define GLUT_KEY_UP 0x0065 +#define GLUT_KEY_RIGHT 0x0066 +#define GLUT_KEY_DOWN 0x0067 +#define GLUT_KEY_PAGE_UP 0x0068 +#define GLUT_KEY_PAGE_DOWN 0x0069 +#define GLUT_KEY_HOME 0x006A +#define GLUT_KEY_END 0x006B +#define GLUT_KEY_INSERT 0x006C + +/* + * GLUT API macro definitions -- mouse state definitions + */ +#define GLUT_LEFT_BUTTON 0x0000 +#define GLUT_MIDDLE_BUTTON 0x0001 +#define GLUT_RIGHT_BUTTON 0x0002 +#define GLUT_DOWN 0x0000 +#define GLUT_UP 0x0001 +#define GLUT_LEFT 0x0000 +#define GLUT_ENTERED 0x0001 + +/* + * GLUT API macro definitions -- the display mode definitions + */ +#define GLUT_RGB 0x0000 +#define GLUT_RGBA 0x0000 +#define GLUT_INDEX 0x0001 +#define GLUT_SINGLE 0x0000 +#define GLUT_DOUBLE 0x0002 +#define GLUT_ACCUM 0x0004 +#define GLUT_ALPHA 0x0008 +#define GLUT_DEPTH 0x0010 +#define GLUT_STENCIL 0x0020 +#define GLUT_MULTISAMPLE 0x0080 +#define GLUT_STEREO 0x0100 +#define GLUT_LUMINANCE 0x0200 + +/* + * GLUT API macro definitions -- windows and menu related definitions + */ +#define GLUT_MENU_NOT_IN_USE 0x0000 +#define GLUT_MENU_IN_USE 0x0001 +#define GLUT_NOT_VISIBLE 0x0000 +#define GLUT_VISIBLE 0x0001 +#define GLUT_HIDDEN 0x0000 +#define GLUT_FULLY_RETAINED 0x0001 +#define GLUT_PARTIALLY_RETAINED 0x0002 +#define GLUT_FULLY_COVERED 0x0003 + +/* + * GLUT API macro definitions -- fonts definitions + * + * Steve Baker suggested to make it binary compatible with GLUT: + */ +#if defined(_MSC_VER) || defined(__CYGWIN__) || defined(__MINGW32__) || defined(__WATCOMC__) +# define GLUT_STROKE_ROMAN ((void *)0x0000) +# define GLUT_STROKE_MONO_ROMAN ((void *)0x0001) +# define GLUT_BITMAP_9_BY_15 ((void *)0x0002) +# define GLUT_BITMAP_8_BY_13 ((void *)0x0003) +# define GLUT_BITMAP_TIMES_ROMAN_10 ((void *)0x0004) +# define GLUT_BITMAP_TIMES_ROMAN_24 ((void *)0x0005) +# define GLUT_BITMAP_HELVETICA_10 ((void *)0x0006) +# define GLUT_BITMAP_HELVETICA_12 ((void *)0x0007) +# define GLUT_BITMAP_HELVETICA_18 ((void *)0x0008) +#else + /* + * I don't really know if it's a good idea... But here it goes: + */ + extern void* glutStrokeRoman; + extern void* glutStrokeMonoRoman; + extern void* glutBitmap9By15; + extern void* glutBitmap8By13; + extern void* glutBitmapTimesRoman10; + extern void* glutBitmapTimesRoman24; + extern void* glutBitmapHelvetica10; + extern void* glutBitmapHelvetica12; + extern void* glutBitmapHelvetica18; + + /* + * Those pointers will be used by following definitions: + */ +# define GLUT_STROKE_ROMAN ((void *) &glutStrokeRoman) +# define GLUT_STROKE_MONO_ROMAN ((void *) &glutStrokeMonoRoman) +# define GLUT_BITMAP_9_BY_15 ((void *) &glutBitmap9By15) +# define GLUT_BITMAP_8_BY_13 ((void *) &glutBitmap8By13) +# define GLUT_BITMAP_TIMES_ROMAN_10 ((void *) &glutBitmapTimesRoman10) +# define GLUT_BITMAP_TIMES_ROMAN_24 ((void *) &glutBitmapTimesRoman24) +# define GLUT_BITMAP_HELVETICA_10 ((void *) &glutBitmapHelvetica10) +# define GLUT_BITMAP_HELVETICA_12 ((void *) &glutBitmapHelvetica12) +# define GLUT_BITMAP_HELVETICA_18 ((void *) &glutBitmapHelvetica18) +#endif + +/* + * GLUT API macro definitions -- the glutGet parameters + */ +#define GLUT_WINDOW_X 0x0064 +#define GLUT_WINDOW_Y 0x0065 +#define GLUT_WINDOW_WIDTH 0x0066 +#define GLUT_WINDOW_HEIGHT 0x0067 +#define GLUT_WINDOW_BUFFER_SIZE 0x0068 +#define GLUT_WINDOW_STENCIL_SIZE 0x0069 +#define GLUT_WINDOW_DEPTH_SIZE 0x006A +#define GLUT_WINDOW_RED_SIZE 0x006B +#define GLUT_WINDOW_GREEN_SIZE 0x006C +#define GLUT_WINDOW_BLUE_SIZE 0x006D +#define GLUT_WINDOW_ALPHA_SIZE 0x006E +#define GLUT_WINDOW_ACCUM_RED_SIZE 0x006F +#define GLUT_WINDOW_ACCUM_GREEN_SIZE 0x0070 +#define GLUT_WINDOW_ACCUM_BLUE_SIZE 0x0071 +#define GLUT_WINDOW_ACCUM_ALPHA_SIZE 0x0072 +#define GLUT_WINDOW_DOUBLEBUFFER 0x0073 +#define GLUT_WINDOW_RGBA 0x0074 +#define GLUT_WINDOW_PARENT 0x0075 +#define GLUT_WINDOW_NUM_CHILDREN 0x0076 +#define GLUT_WINDOW_COLORMAP_SIZE 0x0077 +#define GLUT_WINDOW_NUM_SAMPLES 0x0078 +#define GLUT_WINDOW_STEREO 0x0079 +#define GLUT_WINDOW_CURSOR 0x007A + +#define GLUT_SCREEN_WIDTH 0x00C8 +#define GLUT_SCREEN_HEIGHT 0x00C9 +#define GLUT_SCREEN_WIDTH_MM 0x00CA +#define GLUT_SCREEN_HEIGHT_MM 0x00CB +#define GLUT_MENU_NUM_ITEMS 0x012C +#define GLUT_DISPLAY_MODE_POSSIBLE 0x0190 +#define GLUT_INIT_WINDOW_X 0x01F4 +#define GLUT_INIT_WINDOW_Y 0x01F5 +#define GLUT_INIT_WINDOW_WIDTH 0x01F6 +#define GLUT_INIT_WINDOW_HEIGHT 0x01F7 +#define GLUT_INIT_DISPLAY_MODE 0x01F8 +#define GLUT_ELAPSED_TIME 0x02BC +#define GLUT_WINDOW_FORMAT_ID 0x007B + +/* + * GLUT API macro definitions -- the glutDeviceGet parameters + */ +#define GLUT_HAS_KEYBOARD 0x0258 +#define GLUT_HAS_MOUSE 0x0259 +#define GLUT_HAS_SPACEBALL 0x025A +#define GLUT_HAS_DIAL_AND_BUTTON_BOX 0x025B +#define GLUT_HAS_TABLET 0x025C +#define GLUT_NUM_MOUSE_BUTTONS 0x025D +#define GLUT_NUM_SPACEBALL_BUTTONS 0x025E +#define GLUT_NUM_BUTTON_BOX_BUTTONS 0x025F +#define GLUT_NUM_DIALS 0x0260 +#define GLUT_NUM_TABLET_BUTTONS 0x0261 +#define GLUT_DEVICE_IGNORE_KEY_REPEAT 0x0262 +#define GLUT_DEVICE_KEY_REPEAT 0x0263 +#define GLUT_HAS_JOYSTICK 0x0264 +#define GLUT_OWNS_JOYSTICK 0x0265 +#define GLUT_JOYSTICK_BUTTONS 0x0266 +#define GLUT_JOYSTICK_AXES 0x0267 +#define GLUT_JOYSTICK_POLL_RATE 0x0268 + +/* + * GLUT API macro definitions -- the glutLayerGet parameters + */ +#define GLUT_OVERLAY_POSSIBLE 0x0320 +#define GLUT_LAYER_IN_USE 0x0321 +#define GLUT_HAS_OVERLAY 0x0322 +#define GLUT_TRANSPARENT_INDEX 0x0323 +#define GLUT_NORMAL_DAMAGED 0x0324 +#define GLUT_OVERLAY_DAMAGED 0x0325 + +/* + * GLUT API macro definitions -- the glutVideoResizeGet parameters + */ +#define GLUT_VIDEO_RESIZE_POSSIBLE 0x0384 +#define GLUT_VIDEO_RESIZE_IN_USE 0x0385 +#define GLUT_VIDEO_RESIZE_X_DELTA 0x0386 +#define GLUT_VIDEO_RESIZE_Y_DELTA 0x0387 +#define GLUT_VIDEO_RESIZE_WIDTH_DELTA 0x0388 +#define GLUT_VIDEO_RESIZE_HEIGHT_DELTA 0x0389 +#define GLUT_VIDEO_RESIZE_X 0x038A +#define GLUT_VIDEO_RESIZE_Y 0x038B +#define GLUT_VIDEO_RESIZE_WIDTH 0x038C +#define GLUT_VIDEO_RESIZE_HEIGHT 0x038D + +/* + * GLUT API macro definitions -- the glutUseLayer parameters + */ +#define GLUT_NORMAL 0x0000 +#define GLUT_OVERLAY 0x0001 + +/* + * GLUT API macro definitions -- the glutGetModifiers parameters + */ +#define GLUT_ACTIVE_SHIFT 0x0001 +#define GLUT_ACTIVE_CTRL 0x0002 +#define GLUT_ACTIVE_ALT 0x0004 + +/* + * GLUT API macro definitions -- the glutSetCursor parameters + */ +#define GLUT_CURSOR_RIGHT_ARROW 0x0000 +#define GLUT_CURSOR_LEFT_ARROW 0x0001 +#define GLUT_CURSOR_INFO 0x0002 +#define GLUT_CURSOR_DESTROY 0x0003 +#define GLUT_CURSOR_HELP 0x0004 +#define GLUT_CURSOR_CYCLE 0x0005 +#define GLUT_CURSOR_SPRAY 0x0006 +#define GLUT_CURSOR_WAIT 0x0007 +#define GLUT_CURSOR_TEXT 0x0008 +#define GLUT_CURSOR_CROSSHAIR 0x0009 +#define GLUT_CURSOR_UP_DOWN 0x000A +#define GLUT_CURSOR_LEFT_RIGHT 0x000B +#define GLUT_CURSOR_TOP_SIDE 0x000C +#define GLUT_CURSOR_BOTTOM_SIDE 0x000D +#define GLUT_CURSOR_LEFT_SIDE 0x000E +#define GLUT_CURSOR_RIGHT_SIDE 0x000F +#define GLUT_CURSOR_TOP_LEFT_CORNER 0x0010 +#define GLUT_CURSOR_TOP_RIGHT_CORNER 0x0011 +#define GLUT_CURSOR_BOTTOM_RIGHT_CORNER 0x0012 +#define GLUT_CURSOR_BOTTOM_LEFT_CORNER 0x0013 +#define GLUT_CURSOR_INHERIT 0x0064 +#define GLUT_CURSOR_NONE 0x0065 +#define GLUT_CURSOR_FULL_CROSSHAIR 0x0066 + +/* + * GLUT API macro definitions -- RGB color component specification definitions + */ +#define GLUT_RED 0x0000 +#define GLUT_GREEN 0x0001 +#define GLUT_BLUE 0x0002 + +/* + * GLUT API macro definitions -- additional keyboard and joystick definitions + */ +#define GLUT_KEY_REPEAT_OFF 0x0000 +#define GLUT_KEY_REPEAT_ON 0x0001 +#define GLUT_KEY_REPEAT_DEFAULT 0x0002 + +#define GLUT_JOYSTICK_BUTTON_A 0x0001 +#define GLUT_JOYSTICK_BUTTON_B 0x0002 +#define GLUT_JOYSTICK_BUTTON_C 0x0004 +#define GLUT_JOYSTICK_BUTTON_D 0x0008 + +/* + * GLUT API macro definitions -- game mode definitions + */ +#define GLUT_GAME_MODE_ACTIVE 0x0000 +#define GLUT_GAME_MODE_POSSIBLE 0x0001 +#define GLUT_GAME_MODE_WIDTH 0x0002 +#define GLUT_GAME_MODE_HEIGHT 0x0003 +#define GLUT_GAME_MODE_PIXEL_DEPTH 0x0004 +#define GLUT_GAME_MODE_REFRESH_RATE 0x0005 +#define GLUT_GAME_MODE_DISPLAY_CHANGED 0x0006 + +/* + * Initialization functions, see fglut_init.c + */ +FGAPI void FGAPIENTRY glutInit( int* pargc, char** argv ); +FGAPI void FGAPIENTRY glutInitWindowPosition( int x, int y ); +FGAPI void FGAPIENTRY glutInitWindowSize( int width, int height ); +FGAPI void FGAPIENTRY glutInitDisplayMode( unsigned int displayMode ); +FGAPI void FGAPIENTRY glutInitDisplayString( const char* displayMode ); + +/* + * Process loop function, see fg_main.c + */ +FGAPI void FGAPIENTRY glutMainLoop( void ); + +/* + * Window management functions, see fg_window.c + */ +FGAPI int FGAPIENTRY glutCreateWindow( const char* title ); +FGAPI int FGAPIENTRY glutCreateSubWindow( int window, int x, int y, int width, int height ); +FGAPI void FGAPIENTRY glutDestroyWindow( int window ); +FGAPI void FGAPIENTRY glutSetWindow( int window ); +FGAPI int FGAPIENTRY glutGetWindow( void ); +FGAPI void FGAPIENTRY glutSetWindowTitle( const char* title ); +FGAPI void FGAPIENTRY glutSetIconTitle( const char* title ); +FGAPI void FGAPIENTRY glutReshapeWindow( int width, int height ); +FGAPI void FGAPIENTRY glutFitWindowSizeToDesktop(int width, int height); +FGAPI void FGAPIENTRY glutPositionWindow( int x, int y ); +FGAPI void FGAPIENTRY glutShowWindow( void ); +FGAPI void FGAPIENTRY glutHideWindow( void ); +FGAPI void FGAPIENTRY glutIconifyWindow( void ); +FGAPI void FGAPIENTRY glutPushWindow( void ); +FGAPI void FGAPIENTRY glutPopWindow( void ); +FGAPI void FGAPIENTRY glutFullScreen( void ); + +/* + * Display-related functions, see fg_display.c + */ +FGAPI void FGAPIENTRY glutPostWindowRedisplay( int window ); +FGAPI void FGAPIENTRY glutPostRedisplay( void ); +FGAPI void FGAPIENTRY glutSwapBuffers( void ); + +/* + * Mouse cursor functions, see fg_cursor.c + */ +FGAPI void FGAPIENTRY glutWarpPointer( int x, int y ); +FGAPI void FGAPIENTRY glutSetCursor( int cursor ); + +/* + * Overlay stuff, see fg_overlay.c + */ +FGAPI void FGAPIENTRY glutEstablishOverlay( void ); +FGAPI void FGAPIENTRY glutRemoveOverlay( void ); +FGAPI void FGAPIENTRY glutUseLayer( GLenum layer ); +FGAPI void FGAPIENTRY glutPostOverlayRedisplay( void ); +FGAPI void FGAPIENTRY glutPostWindowOverlayRedisplay( int window ); +FGAPI void FGAPIENTRY glutShowOverlay( void ); +FGAPI void FGAPIENTRY glutHideOverlay( void ); + +/* + * Menu stuff, see fg_menu.c + */ +FGAPI int FGAPIENTRY glutCreateMenu( void (* callback)( int menu ) ); +FGAPI void FGAPIENTRY glutDestroyMenu( int menu ); +FGAPI int FGAPIENTRY glutGetMenu( void ); +FGAPI void FGAPIENTRY glutSetMenu( int menu ); +FGAPI void FGAPIENTRY glutAddMenuEntry( const char* label, int value ); +FGAPI void FGAPIENTRY glutAddSubMenu( const char* label, int subMenu ); +FGAPI void FGAPIENTRY glutChangeToMenuEntry( int item, const char* label, int value ); +FGAPI void FGAPIENTRY glutChangeToSubMenu( int item, const char* label, int value ); +FGAPI void FGAPIENTRY glutRemoveMenuItem( int item ); +FGAPI void FGAPIENTRY glutAttachMenu( int button ); +FGAPI void FGAPIENTRY glutDetachMenu( int button ); + +/* + * Global callback functions, see fg_callbacks.c + */ +FGAPI void FGAPIENTRY glutTimerFunc( unsigned int time, void (* callback)( int ), int value ); +FGAPI void FGAPIENTRY glutIdleFunc( void (* callback)( void ) ); + +/* + * Window-specific callback functions, see fg_callbacks.c + */ +FGAPI void FGAPIENTRY glutKeyboardFunc( void (* callback)( unsigned char, int, int ) ); +FGAPI void FGAPIENTRY glutSpecialFunc( void (* callback)( int, int, int ) ); +FGAPI void FGAPIENTRY glutReshapeFunc( void (* callback)( int, int ) ); +FGAPI void FGAPIENTRY glutVisibilityFunc( void (* callback)( int ) ); +FGAPI void FGAPIENTRY glutDisplayFunc( void (* callback)( void ) ); +FGAPI void FGAPIENTRY glutMouseFunc( void (* callback)( int, int, int, int ) ); +FGAPI void FGAPIENTRY glutMotionFunc( void (* callback)( int, int ) ); +FGAPI void FGAPIENTRY glutPassiveMotionFunc( void (* callback)( int, int ) ); +FGAPI void FGAPIENTRY glutEntryFunc( void (* callback)( int ) ); + +FGAPI void FGAPIENTRY glutKeyboardUpFunc( void (* callback)( unsigned char, int, int ) ); +FGAPI void FGAPIENTRY glutSpecialUpFunc( void (* callback)( int, int, int ) ); +FGAPI void FGAPIENTRY glutJoystickFunc( void (* callback)( unsigned int, int, int, int ), int pollInterval ); +FGAPI void FGAPIENTRY glutMenuStateFunc( void (* callback)( int ) ); +FGAPI void FGAPIENTRY glutMenuStatusFunc( void (* callback)( int, int, int ) ); +FGAPI void FGAPIENTRY glutOverlayDisplayFunc( void (* callback)( void ) ); +FGAPI void FGAPIENTRY glutWindowStatusFunc( void (* callback)( int ) ); + +FGAPI void FGAPIENTRY glutSpaceballMotionFunc( void (* callback)( int, int, int ) ); +FGAPI void FGAPIENTRY glutSpaceballRotateFunc( void (* callback)( int, int, int ) ); +FGAPI void FGAPIENTRY glutSpaceballButtonFunc( void (* callback)( int, int ) ); +FGAPI void FGAPIENTRY glutButtonBoxFunc( void (* callback)( int, int ) ); +FGAPI void FGAPIENTRY glutDialsFunc( void (* callback)( int, int ) ); +FGAPI void FGAPIENTRY glutTabletMotionFunc( void (* callback)( int, int ) ); +FGAPI void FGAPIENTRY glutTabletButtonFunc( void (* callback)( int, int, int, int ) ); + +/* + * State setting and retrieval functions, see fg_state.c + */ +FGAPI int FGAPIENTRY glutGet( GLenum query ); +FGAPI int FGAPIENTRY glutDeviceGet( GLenum query ); +FGAPI int FGAPIENTRY glutGetModifiers( void ); +FGAPI int FGAPIENTRY glutLayerGet( GLenum query ); + +/* + * Font stuff, see fg_font.c + */ +FGAPI void FGAPIENTRY glutBitmapCharacter( void* font, int character ); +FGAPI int FGAPIENTRY glutBitmapWidth( void* font, int character ); +FGAPI void FGAPIENTRY glutStrokeCharacter( void* font, int character ); +FGAPI int FGAPIENTRY glutStrokeWidth( void* font, int character ); +FGAPI GLfloat FGAPIENTRY glutStrokeWidthf( void* font, int character ); /* GLUT 3.8 */ +FGAPI int FGAPIENTRY glutBitmapLength( void* font, const unsigned char* string ); +FGAPI int FGAPIENTRY glutStrokeLength( void* font, const unsigned char* string ); +FGAPI GLfloat FGAPIENTRY glutStrokeLengthf( void* font, const unsigned char *string ); /* GLUT 3.8 */ + +/* + * Geometry functions, see fg_geometry.c + */ + +FGAPI void FGAPIENTRY glutWireCube( double size ); +FGAPI void FGAPIENTRY glutSolidCube( double size ); +FGAPI void FGAPIENTRY glutWireSphere( double radius, GLint slices, GLint stacks ); +FGAPI void FGAPIENTRY glutSolidSphere( double radius, GLint slices, GLint stacks ); +FGAPI void FGAPIENTRY glutWireCone( double base, double height, GLint slices, GLint stacks ); +FGAPI void FGAPIENTRY glutSolidCone( double base, double height, GLint slices, GLint stacks ); +FGAPI void FGAPIENTRY glutWireTorus( double innerRadius, double outerRadius, GLint sides, GLint rings ); +FGAPI void FGAPIENTRY glutSolidTorus( double innerRadius, double outerRadius, GLint sides, GLint rings ); +FGAPI void FGAPIENTRY glutWireDodecahedron( void ); +FGAPI void FGAPIENTRY glutSolidDodecahedron( void ); +FGAPI void FGAPIENTRY glutWireOctahedron( void ); +FGAPI void FGAPIENTRY glutSolidOctahedron( void ); +FGAPI void FGAPIENTRY glutWireTetrahedron( void ); +FGAPI void FGAPIENTRY glutSolidTetrahedron( void ); +FGAPI void FGAPIENTRY glutWireIcosahedron( void ); +FGAPI void FGAPIENTRY glutSolidIcosahedron( void ); + +/* + * Teapot rendering functions, found in fg_teapot.c + * NB: front facing polygons have clockwise winding, not counter clockwise + */ +FGAPI void FGAPIENTRY glutWireTeapot( double size ); +FGAPI void FGAPIENTRY glutSolidTeapot( double size ); + +/* + * Game mode functions, see fg_gamemode.c + */ +FGAPI void FGAPIENTRY glutGameModeString( const char* string ); +FGAPI int FGAPIENTRY glutEnterGameMode( void ); +FGAPI void FGAPIENTRY glutLeaveGameMode( void ); +FGAPI int FGAPIENTRY glutGameModeGet( GLenum query ); + +/* + * Video resize functions, see fg_videoresize.c + */ +FGAPI int FGAPIENTRY glutVideoResizeGet( GLenum query ); +FGAPI void FGAPIENTRY glutSetupVideoResizing( void ); +FGAPI void FGAPIENTRY glutStopVideoResizing( void ); +FGAPI void FGAPIENTRY glutVideoResize( int x, int y, int width, int height ); +FGAPI void FGAPIENTRY glutVideoPan( int x, int y, int width, int height ); + +/* + * Colormap functions, see fg_misc.c + */ +FGAPI void FGAPIENTRY glutSetColor( int color, GLfloat red, GLfloat green, GLfloat blue ); +FGAPI GLfloat FGAPIENTRY glutGetColor( int color, int component ); +FGAPI void FGAPIENTRY glutCopyColormap( int window ); + +/* + * Misc keyboard and joystick functions, see fg_misc.c + */ +FGAPI void FGAPIENTRY glutIgnoreKeyRepeat( int ignore ); +FGAPI void FGAPIENTRY glutSetKeyRepeat( int repeatMode ); +FGAPI void FGAPIENTRY glutForceJoystickFunc( void ); + +/* + * Misc functions, see fg_misc.c + */ +FGAPI int FGAPIENTRY glutExtensionSupported( const char* extension ); +FGAPI void FGAPIENTRY glutReportErrors( void ); + +/* Comment from glut.h of classic GLUT: + + Win32 has an annoying issue where there are multiple C run-time + libraries (CRTs). If the executable is linked with a different CRT + from the GLUT DLL, the GLUT DLL will not share the same CRT static + data seen by the executable. In particular, atexit callbacks registered + in the executable will not be called if GLUT calls its (different) + exit routine). GLUT is typically built with the + "/MD" option (the CRT with multithreading DLL support), but the Visual + C++ linker default is "/ML" (the single threaded CRT). + + One workaround to this issue is requiring users to always link with + the same CRT as GLUT is compiled with. That requires users supply a + non-standard option. GLUT 3.7 has its own built-in workaround where + the executable's "exit" function pointer is covertly passed to GLUT. + GLUT then calls the executable's exit function pointer to ensure that + any "atexit" calls registered by the application are called if GLUT + needs to exit. + + Note that the __glut*WithExit routines should NEVER be called directly. + To avoid the atexit workaround, #define GLUT_DISABLE_ATEXIT_HACK. */ + +/* to get the prototype for exit() */ +#include + +#if defined(_WIN32) && !defined(GLUT_DISABLE_ATEXIT_HACK) && !defined(__WATCOMC__) +FGAPI void FGAPIENTRY __glutInitWithExit(int *argcp, char **argv, void (__cdecl *exitfunc)(int)); +FGAPI int FGAPIENTRY __glutCreateWindowWithExit(const char *title, void (__cdecl *exitfunc)(int)); +FGAPI int FGAPIENTRY __glutCreateMenuWithExit(void (* func)(int), void (__cdecl *exitfunc)(int)); +#ifndef FREEGLUT_BUILDING_LIB +#if defined(__GNUC__) +#define FGUNUSED __attribute__((unused)) +#else +#define FGUNUSED +#endif +static void FGAPIENTRY FGUNUSED glutInit_ATEXIT_HACK(int *argcp, char **argv) { __glutInitWithExit(argcp, argv, exit); } +#define glutInit glutInit_ATEXIT_HACK +static int FGAPIENTRY FGUNUSED glutCreateWindow_ATEXIT_HACK(const char *title) { return __glutCreateWindowWithExit(title, exit); } +#define glutCreateWindow glutCreateWindow_ATEXIT_HACK +static int FGAPIENTRY FGUNUSED glutCreateMenu_ATEXIT_HACK(void (* func)(int)) { return __glutCreateMenuWithExit(func, exit); } +#define glutCreateMenu glutCreateMenu_ATEXIT_HACK +#endif +#endif + +#ifdef __cplusplus + } +#endif + +/*** END OF FILE ***/ + +#endif /* __FREEGLUT_STD_H__ */ + diff --git a/dependencies/freeglut/include/GL/freeglut_ucall.h b/dependencies/freeglut/include/GL/freeglut_ucall.h new file mode 100644 index 0000000..1ef2ae7 --- /dev/null +++ b/dependencies/freeglut/include/GL/freeglut_ucall.h @@ -0,0 +1,113 @@ +#ifndef __FREEGLUT_UCALL_H__ +#define __FREEGLUT_UCALL_H__ + +/* + * freeglut_ucall.h + * + * Callbacks with user data arguments. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PAWEL W. OLSZTA BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifdef __cplusplus + extern "C" { +#endif + +/* + * Menu stuff, see fg_menu.c + */ +FGAPI int FGAPIENTRY glutCreateMenuUcall( void (* callback)( int menu, void* user_data ), void* user_data ); + +/* + * Global callback functions, see fg_callbacks.c + */ +FGAPI void FGAPIENTRY glutTimerFuncUcall( unsigned int time, void (* callback)( int, void* ), int value, void* user_data ); +FGAPI void FGAPIENTRY glutIdleFuncUcall( void (* callback)( void* ), void* user_data ); + +/* + * Window-specific callback functions, see fg_callbacks.c + */ +FGAPI void FGAPIENTRY glutKeyboardFuncUcall( void (* callback)( unsigned char, int, int, void* ), void* user_data ); +FGAPI void FGAPIENTRY glutSpecialFuncUcall( void (* callback)( int, int, int, void* ), void* user_data ); +FGAPI void FGAPIENTRY glutReshapeFuncUcall( void (* callback)( int, int, void* ), void* user_data ); +FGAPI void FGAPIENTRY glutVisibilityFuncUcall( void (* callback)( int, void* ), void* user_data ); +FGAPI void FGAPIENTRY glutDisplayFuncUcall( void (* callback)( void* ), void* user_data ); +FGAPI void FGAPIENTRY glutMouseFuncUcall( void (* callback)( int, int, int, int, void* ), void* user_data ); +FGAPI void FGAPIENTRY glutMotionFuncUcall( void (* callback)( int, int, void* ), void* user_data ); +FGAPI void FGAPIENTRY glutPassiveMotionFuncUcall( void (* callback)( int, int, void* ), void* user_data ); +FGAPI void FGAPIENTRY glutEntryFuncUcall( void (* callback)( int, void* ), void* user_data ); + +FGAPI void FGAPIENTRY glutKeyboardUpFuncUcall( void (* callback)( unsigned char, int, int, void* ), void* user_data ); +FGAPI void FGAPIENTRY glutSpecialUpFuncUcall( void (* callback)( int, int, int, void* ), void* user_data ); +FGAPI void FGAPIENTRY glutJoystickFuncUcall( void (* callback)( unsigned int, int, int, int, void* ), int pollInterval, void* user_data ); +FGAPI void FGAPIENTRY glutMenuStatusFuncUcall( void (* callback)( int, int, int, void* ), void* user_data ); +FGAPI void FGAPIENTRY glutOverlayDisplayFuncUcall( void (* callback)( void* ), void* user_data ); +FGAPI void FGAPIENTRY glutWindowStatusFuncUcall( void (* callback)( int, void* ), void* user_data ); + +FGAPI void FGAPIENTRY glutSpaceballMotionFuncUcall( void (* callback)( int, int, int, void* ), void* user_data ); +FGAPI void FGAPIENTRY glutSpaceballRotateFuncUcall( void (* callback)( int, int, int, void* ), void* user_data ); +FGAPI void FGAPIENTRY glutSpaceballButtonFuncUcall( void (* callback)( int, int, void* ), void* user_data ); +FGAPI void FGAPIENTRY glutButtonBoxFuncUcall( void (* callback)( int, int, void* ), void* user_data ); +FGAPI void FGAPIENTRY glutDialsFuncUcall( void (* callback)( int, int, void* ), void* user_data ); +FGAPI void FGAPIENTRY glutTabletMotionFuncUcall( void (* callback)( int, int, void* ), void* user_data ); +FGAPI void FGAPIENTRY glutTabletButtonFuncUcall( void (* callback)( int, int, int, int, void* ), void* user_data ); + +FGAPI void FGAPIENTRY glutMouseWheelFuncUcall( void (* callback)( int, int, int, int, void* ), void* user_data ); +FGAPI void FGAPIENTRY glutPositionFuncUcall( void (* callback)( int, int, void* ), void* user_data ); +FGAPI void FGAPIENTRY glutCloseFuncUcall( void (* callback)( void* ), void* user_data ); +FGAPI void FGAPIENTRY glutWMCloseFuncUcall( void (* callback)( void* ), void* user_data ); +FGAPI void FGAPIENTRY glutMenuDestroyFuncUcall( void (* callback)( void* ), void* user_data ); + +/* + * Multi-touch/multi-pointer extensions + */ +FGAPI void FGAPIENTRY glutMultiEntryFuncUcall( void (* callback)( int, int, void* ), void* user_data ); +FGAPI void FGAPIENTRY glutMultiButtonFuncUcall( void (* callback)( int, int, int, int, int, void* ), void* user_data ); +FGAPI void FGAPIENTRY glutMultiMotionFuncUcall( void (* callback)( int, int, int, void* ), void* user_data ); +FGAPI void FGAPIENTRY glutMultiPassiveFuncUcall( void (* callback)( int, int, int, void* ), void* user_data ); + +/* + * Initialization functions, see fg_init.c + */ +#include +FGAPI void FGAPIENTRY glutInitErrorFuncUcall( void (* callback)( const char *fmt, va_list ap, void* user_data ), void* user_data ); +FGAPI void FGAPIENTRY glutInitWarningFuncUcall( void (* callback)( const char *fmt, va_list ap, void* user_data ), void* user_data ); + +/* Mobile platforms lifecycle */ +FGAPI void FGAPIENTRY glutInitContextFuncUcall( void (* callback)( void* ), void* user_data ); +FGAPI void FGAPIENTRY glutAppStatusFuncUcall( void (* callback)( int, void* ), void* user_data ); + +/* + * Continued "hack" from GLUT applied to Ucall functions. + * For more info, see bottom of freeglut_std.h + */ + +/* to get the prototype for exit() */ +#include + +#if defined(_WIN32) && !defined(GLUT_DISABLE_ATEXIT_HACK) && !defined(__WATCOMC__) +FGAPI int FGAPIENTRY __glutCreateMenuUcallWithExit(void(*func)(int, void*), void(__cdecl *exitfunc)(int), void* user_data); +#ifndef FREEGLUT_BUILDING_LIB +#if defined(__GNUC__) +#define FGUNUSED __attribute__((unused)) +#else +#define FGUNUSED +#endif +static int FGAPIENTRY FGUNUSED glutCreateMenuUcall_ATEXIT_HACK(void(*func)(int, void*), void* user_data) { return __glutCreateMenuUcallWithExit(func, exit, user_data); } +#define glutCreateMenuUcall glutCreateMenuUcall_ATEXIT_HACK +#endif +#endif + +#ifdef __cplusplus + } +#endif + +/*** END OF FILE ***/ + +#endif /* __FREEGLUT_UCALL_H__ */ + diff --git a/dependencies/freeglut/include/GL/glut.h b/dependencies/freeglut/include/GL/glut.h new file mode 100644 index 0000000..6191f77 --- /dev/null +++ b/dependencies/freeglut/include/GL/glut.h @@ -0,0 +1,21 @@ +#ifndef __GLUT_H__ +#define __GLUT_H__ + +/* + * glut.h + * + * The freeglut library include file + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PAWEL W. OLSZTA BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "freeglut_std.h" + +/*** END OF FILE ***/ + +#endif /* __GLUT_H__ */ diff --git a/dependencies/freeglut/lib/freeglut.exp b/dependencies/freeglut/lib/freeglut.exp new file mode 100644 index 0000000..e8c01a8 Binary files /dev/null and b/dependencies/freeglut/lib/freeglut.exp differ diff --git a/dependencies/freeglut/lib/freeglut.lib b/dependencies/freeglut/lib/freeglut.lib new file mode 100644 index 0000000..1756a00 Binary files /dev/null and b/dependencies/freeglut/lib/freeglut.lib differ diff --git a/dependencies/freeglut/lib/freeglutd.exp b/dependencies/freeglut/lib/freeglutd.exp new file mode 100644 index 0000000..670dec4 Binary files /dev/null and b/dependencies/freeglut/lib/freeglutd.exp differ diff --git a/dependencies/freeglut/lib/freeglutd.lib b/dependencies/freeglut/lib/freeglutd.lib new file mode 100644 index 0000000..c9c53f9 Binary files /dev/null and b/dependencies/freeglut/lib/freeglutd.lib differ diff --git a/source/PD-Loader/ModuleList.h b/source/PD-Loader/ModuleList.h new file mode 100644 index 0000000..72c79e3 --- /dev/null +++ b/source/PD-Loader/ModuleList.h @@ -0,0 +1,186 @@ +#pragma once + +#include +#include +#include +#include +#include + +std::wstring GetModuleFileNameW(HMODULE hModule) +{ + static constexpr auto INITIAL_BUFFER_SIZE = MAX_PATH; + static constexpr auto MAX_ITERATIONS = 7; + std::wstring ret; + auto bufferSize = INITIAL_BUFFER_SIZE; + for (size_t iterations = 0; iterations < MAX_ITERATIONS; ++iterations) + { + ret.resize(bufferSize); + auto charsReturned = GetModuleFileNameW(hModule, &ret[0], bufferSize); + if (charsReturned < ret.length()) + { + ret.resize(charsReturned); + return ret; + } + else + { + bufferSize *= 2; + } + } + return L""; +} + +auto starts_with = [](const std::wstring& big_str, const std::wstring& small_str) -> auto +{ + return big_str.compare(0, small_str.length(), small_str) == 0; +}; + +// Stores a list of loaded modules with their names, WITHOUT extension +class ModuleList +{ +public: + enum class SearchLocation + { + All, + LocalOnly, + SystemOnly, + }; + + // Initializes module list + // Needs to be called before any calls to Get or GetAll + void Enumerate(SearchLocation location = SearchLocation::All) + { + constexpr size_t INITIAL_SIZE = sizeof(HMODULE) * 256; + HMODULE* modules = static_cast(malloc(INITIAL_SIZE)); + if (modules != nullptr) + { + typedef BOOL(WINAPI * Func)(HANDLE hProcess, HMODULE * lphModule, DWORD cb, LPDWORD lpcbNeeded); + + HMODULE hLib = LoadLibrary(TEXT("kernel32")); + assert(hLib != nullptr); // If this fails then everything is probably broken anyway + + Func pEnumProcessModules = reinterpret_cast(GetProcAddress(hLib, "K32EnumProcessModules")); + if (pEnumProcessModules == nullptr) + { + // Try psapi + FreeLibrary(hLib); + hLib = LoadLibrary(TEXT("psapi")); + if (hLib != nullptr) + { + pEnumProcessModules = reinterpret_cast(GetProcAddress(hLib, "EnumProcessModules")); + } + } + + if (pEnumProcessModules != nullptr) + { + const HANDLE currentProcess = GetCurrentProcess(); + DWORD cbNeeded = 0; + if (pEnumProcessModules(currentProcess, modules, INITIAL_SIZE, &cbNeeded) != 0) + { + if (cbNeeded > INITIAL_SIZE) + { + HMODULE* newModules = static_cast(realloc(modules, cbNeeded)); + if (newModules != nullptr) + { + modules = newModules; + + if (pEnumProcessModules(currentProcess, modules, cbNeeded, &cbNeeded) != 0) + { + EnumerateInternal(modules, location, cbNeeded / sizeof(HMODULE)); + } + } + } + else + { + EnumerateInternal(modules, location, cbNeeded / sizeof(HMODULE)); + } + } + } + + if (hLib != nullptr) + { + FreeLibrary(hLib); + } + + free(modules); + } + } + + // Recreates module list + void ReEnumerate(SearchLocation location = SearchLocation::All) + { + Clear(); + Enumerate(location); + } + + // Clears module list + void Clear() + { + m_moduleList.clear(); + } + + // Gets handle of a loaded module with given name, NULL otherwise + HMODULE Get(const wchar_t* moduleName) const + { + // If vector is empty then we're trying to call it without calling Enumerate first + assert(m_moduleList.size() != 0); + + auto it = std::find_if(m_moduleList.begin(), m_moduleList.end(), [&](const auto& e) { + return _wcsicmp(moduleName, std::get<1>(e).c_str()) == 0; + }); + return it != m_moduleList.end() ? std::get<0>(*it) : nullptr; + } + + // Gets handles to all loaded modules with given name + std::vector GetAll(const wchar_t* moduleName) const + { + // If vector is empty then we're trying to call it without calling Enumerate first + assert(m_moduleList.size() != 0); + + std::vector results; + for (auto& e : m_moduleList) + { + if (_wcsicmp(moduleName, std::get<1>(e).c_str()) == 0) + { + results.push_back(std::get<0>(e)); + } + } + + return results; + } + +private: + void EnumerateInternal(HMODULE* modules, SearchLocation location, size_t numModules) + { + const auto exeModulePath = GetModuleFileNameW(NULL).substr(0, GetModuleFileNameW(NULL).find_last_of(L"/\\")); + + m_moduleList.reserve(numModules); + for (size_t i = 0; i < numModules; i++) + { + // Obtain module name, with resizing if necessary + auto moduleName = GetModuleFileNameW(*modules); + + if (!moduleName.empty()) + { + const wchar_t* nameBegin = wcsrchr(moduleName.c_str(), '\\') + 1; + const wchar_t* dotPos = wcsrchr(nameBegin, '.'); + bool isLocal = starts_with(std::wstring(moduleName), exeModulePath); + + if ((isLocal && location != SearchLocation::SystemOnly) || (!isLocal && location != SearchLocation::LocalOnly)) + { + if (dotPos != nullptr) + { + m_moduleList.emplace_back(*modules, std::wstring(nameBegin, dotPos), isLocal); + } + else + { + m_moduleList.emplace_back(*modules, nameBegin, isLocal); + } + } + } + + modules++; + } + } + +public: std::vector< std::tuple > m_moduleList; +}; \ No newline at end of file diff --git a/source/PD-Loader/PD-Loader.vcxproj b/source/PD-Loader/PD-Loader.vcxproj new file mode 100644 index 0000000..3bf8727 --- /dev/null +++ b/source/PD-Loader/PD-Loader.vcxproj @@ -0,0 +1,186 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + {CA479467-D518-46A2-AC86-3098ADA99FE5} + Win32Proj + PDLoader + 10.0 + + + + + + DynamicLibrary + true + v142 + Unicode + + + DynamicLibrary + false + v142 + true + Unicode + + + DynamicLibrary + true + v142 + Unicode + + + DynamicLibrary + false + v142 + true + Unicode + false + + + + + + + + + + + + + + + + + + + + + true + + + true + dnsapi + + + false + + + false + dnsapi + + + + NotUsing + Level3 + Disabled + true + X64;%(PreprocessorDefinitions) + true + pch.h + stdcpp17 + ..\MemoryModule;%(AdditionalIncludeDirectories) + + + Windows + true + false + x64.def + + + + + NotUsing + Level3 + Disabled + true + _CRT_SECURE_NO_WARNINGS;_DEBUG;PDLOADER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + pch.h + ..\dependencies\MemoryModule;%(AdditionalIncludeDirectories) + stdcpp17 + + + Windows + true + false + x64.def + + + + + Use + Level3 + MaxSpeed + true + true + true + WIN32;NDEBUG;PDLOADER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + pch.h + + + Windows + true + true + true + false + x64.def + + + + + NotUsing + Level3 + MaxSpeed + true + true + true + _CRT_SECURE_NO_WARNINGS;NDEBUG;PDLOADER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + pch.h + ..\dependencies\MemoryModule;%(AdditionalIncludeDirectories) + stdcpp17 + + + Windows + true + true + true + false + x64.def + + + + + + + + + + + + + + + + + diff --git a/source/PD-Loader/PD-Loader.vcxproj.filters b/source/PD-Loader/PD-Loader.vcxproj.filters new file mode 100644 index 0000000..046fb1b --- /dev/null +++ b/source/PD-Loader/PD-Loader.vcxproj.filters @@ -0,0 +1,38 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;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 + + + + + Header Files + + + Header Files + + + Source Files + + + + + Source Files + + + + + Source Files + + + \ No newline at end of file diff --git a/source/PD-Loader/dllmain.cpp b/source/PD-Loader/dllmain.cpp new file mode 100644 index 0000000..e26843a --- /dev/null +++ b/source/PD-Loader/dllmain.cpp @@ -0,0 +1,710 @@ +#include "framework.h" +#include "exception.hpp" + +HMODULE hm; +std::vector iniPaths; + +bool iequals(std::wstring_view s1, std::wstring_view s2) +{ + std::wstring str1(std::move(s1)); + std::wstring str2(std::move(s2)); + std::transform(str1.begin(), str1.end(), str1.begin(), [](wchar_t c) { return ::towlower(c); }); + std::transform(str2.begin(), str2.end(), str2.begin(), [](wchar_t c) { return ::towlower(c); }); + return (str1 == str2); +} + +std::wstring to_wstring(std::string_view cstr) +{ + std::string str(std::move(cstr)); + auto charsReturned = MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), NULL, 0); + std::wstring wstrTo(charsReturned, 0); + MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), &wstrTo[0], charsReturned); + return wstrTo; +} + +std::wstring SHGetKnownFolderPath(REFKNOWNFOLDERID rfid, DWORD dwFlags, HANDLE hToken) +{ + std::wstring r; + WCHAR* szSystemPath = nullptr; + if (SUCCEEDED(SHGetKnownFolderPath(rfid, dwFlags, hToken, &szSystemPath))) + { + r = szSystemPath; + } + CoTaskMemFree(szSystemPath); + return r; +}; + +HMODULE LoadLibraryW(const std::wstring& lpLibFileName) +{ + return LoadLibraryW(lpLibFileName.c_str()); +} + +std::wstring GetCurrentDirectoryW() +{ + static constexpr auto INITIAL_BUFFER_SIZE = MAX_PATH; + static constexpr auto MAX_ITERATIONS = 7; + std::wstring ret; + auto bufferSize = INITIAL_BUFFER_SIZE; + for (size_t iterations = 0; iterations < MAX_ITERATIONS; ++iterations) + { + ret.resize(bufferSize); + auto charsReturned = GetCurrentDirectoryW(bufferSize, &ret[0]); + if (charsReturned < ret.length()) + { + ret.resize(charsReturned); + return ret; + } + else + { + bufferSize *= 2; + } + } + return L""; +} + +UINT GetPrivateProfileIntW(LPCWSTR lpAppName, LPCWSTR lpKeyName, INT nDefault, const std::vector& fileNames) +{ + for (const auto& file : fileNames) + { + nDefault = GetPrivateProfileIntW(lpAppName, lpKeyName, nDefault, file.c_str()); + } + return nDefault; +} + +std::wstring GetSelfName() +{ + const std::wstring moduleFileName = GetModuleFileNameW(hm); + return moduleFileName.substr(moduleFileName.find_last_of(L"/\\") + 1); +} + +template +void GetSections(T&& h, Args... args) +{ + const std::set< std::string_view, std::less<> > s = { args... }; + size_t dwLoadOffset = (size_t)GetModuleHandle(NULL); + BYTE* pImageBase = reinterpret_cast(dwLoadOffset); + PIMAGE_DOS_HEADER pDosHeader = reinterpret_cast(dwLoadOffset); + PIMAGE_NT_HEADERS pNtHeader = reinterpret_cast(pImageBase + pDosHeader->e_lfanew); + PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(pNtHeader); + for (int iSection = 0; iSection < pNtHeader->FileHeader.NumberOfSections; ++iSection, ++pSection) + { + auto pszSectionName = reinterpret_cast(pSection->Name); + if (s.find(pszSectionName) != s.end()) + { + DWORD dwPhysSize = (pSection->Misc.VirtualSize + 4095) & ~4095; + std::forward(h)(pSection, dwLoadOffset, dwPhysSize); + } + } +} + +enum Kernel32ExportsNames +{ + eGetStartupInfoA, + eGetStartupInfoW, + eGetModuleHandleA, + eGetModuleHandleW, + eGetProcAddress, + eGetShortPathNameA, + eFindNextFileA, + eFindNextFileW, + eLoadLibraryA, + eLoadLibraryW, + eFreeLibrary, + eCreateEventA, + eCreateEventW, + eGetSystemInfo, + eInterlockedCompareExchange, + eSleep, + + Kernel32ExportsNamesCount +}; + +enum Kernel32ExportsData +{ + IATPtr, + ProcAddress, + + Kernel32ExportsDataCount +}; + +size_t Kernel32Data[Kernel32ExportsNamesCount][Kernel32ExportsDataCount]; + +static LONG OriginalLibraryLoaded = 0; +void LoadOriginalLibrary() +{ + if (_InterlockedCompareExchange(&OriginalLibraryLoaded, 1, 0) != 0) return; + + auto szSelfName = GetSelfName(); + auto szSystemPath = SHGetKnownFolderPath(FOLDERID_System, 0, nullptr) + L'\\' + szSelfName; + auto szLocalPath = GetModuleFileNameW(hm); szLocalPath = szLocalPath.substr(0, szLocalPath.find_last_of(L"/\\") + 1); + + if (iequals(szSelfName, L"dnsapi.dll")) { + dnsapi.LoadOriginalLibrary(LoadLibraryW(szSystemPath)); + } +} + +void FindFiles(WIN32_FIND_DATAW* fd) +{ + auto dir = GetCurrentDirectoryW(); + + HANDLE dvaFile = FindFirstFileW(L"*.dva", fd); + if (dvaFile != INVALID_HANDLE_VALUE) + { + do { + if (!(fd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) + { + auto pos = wcslen(fd->cFileName); + + if (fd->cFileName[pos - 4] == '.' && + (fd->cFileName[pos - 3] == 'd' || fd->cFileName[pos - 3] == 'D') && + (fd->cFileName[pos - 2] == 'v' || fd->cFileName[pos - 2] == 'V') && + (fd->cFileName[pos - 1] == 'a' || fd->cFileName[pos - 1] == 'A')) + { + auto path = dir + L'\\' + fd->cFileName; + + if (GetModuleHandle(path.c_str()) == NULL) + { + auto h = LoadLibraryW(path); + SetCurrentDirectoryW(dir.c_str()); //in case dva switched it + + if (h == NULL) + { + auto e = GetLastError(); + if (e != ERROR_DLL_INIT_FAILED) // in case dllmain returns false + { + std::wstring msg = L"Unable to load " + std::wstring(fd->cFileName) + L". Error: " + std::to_wstring(e); + MessageBoxW(0, msg.c_str(), L"PD Loader", MB_ICONERROR); + } + } + else + { + auto procedure = (void(*)())GetProcAddress(h, "InitializeDVA"); + + if (procedure != NULL) + { + procedure(); + } + } + } + } + } + } while (FindNextFileW(dvaFile, fd)); + FindClose(dvaFile); + } +} + +void LoadPlugins() +{ + auto oldDir = GetCurrentDirectoryW(); // store the current directory + + auto szSelfPath = GetModuleFileNameW(hm).substr(0, GetModuleFileNameW(hm).find_last_of(L"/\\") + 1); + SetCurrentDirectoryW(szSelfPath.c_str()); + + auto nWantsToLoadPlugins = GetPrivateProfileIntW(L"global", L"enable", TRUE, iniPaths); + + if (nWantsToLoadPlugins) + { + WIN32_FIND_DATAW fd; + + SetCurrentDirectoryW(szSelfPath.c_str()); + + if (SetCurrentDirectoryW(L"plugins\\")) + FindFiles(&fd); + } + + SetCurrentDirectoryW(oldDir.c_str()); // Reset the current directory +} + +static LONG LoadedPluginsYet = 0; +void LoadEverything() +{ + if (_InterlockedCompareExchange(&LoadedPluginsYet, 1, 0) != 0) return; + + LoadOriginalLibrary(); + LoadPlugins(); +} + +static LONG RestoredOnce = 0; +void LoadPluginsAndRestoreIAT(uintptr_t retaddr) +{ + bool calledFromBind = false; + + //steam drm check + GetSections([&](PIMAGE_SECTION_HEADER pSection, size_t dwLoadOffset, DWORD dwPhysSize) { + auto dwStart = static_cast(dwLoadOffset + pSection->VirtualAddress); + auto dwEnd = dwStart + dwPhysSize; + if (retaddr >= dwStart && retaddr <= dwEnd) + calledFromBind = true; + }, ".bind"); + + if (calledFromBind) return; + + if (_InterlockedCompareExchange(&RestoredOnce, 1, 0) != 0) return; + + LoadEverything(); + + for (size_t i = 0; i < Kernel32ExportsNamesCount; i++) + { + if (Kernel32Data[i][IATPtr] && Kernel32Data[i][ProcAddress]) + { + auto ptr = (size_t*)Kernel32Data[i][IATPtr]; + DWORD dwProtect[2]; + VirtualProtect(ptr, sizeof(size_t), PAGE_EXECUTE_READWRITE, &dwProtect[0]); + *ptr = Kernel32Data[i][ProcAddress]; + VirtualProtect(ptr, sizeof(size_t), dwProtect[0], &dwProtect[1]); + } + } +} + +void WINAPI CustomGetStartupInfoA(LPSTARTUPINFOA lpStartupInfo) +{ + LoadPluginsAndRestoreIAT((uintptr_t)_ReturnAddress()); + return GetStartupInfoA(lpStartupInfo); +} + +void WINAPI CustomGetStartupInfoW(LPSTARTUPINFOW lpStartupInfo) +{ + LoadPluginsAndRestoreIAT((uintptr_t)_ReturnAddress()); + return GetStartupInfoW(lpStartupInfo); +} + +HMODULE WINAPI CustomGetModuleHandleA(LPCSTR lpModuleName) +{ + LoadPluginsAndRestoreIAT((uintptr_t)_ReturnAddress()); + return GetModuleHandleA(lpModuleName); +} + +HMODULE WINAPI CustomGetModuleHandleW(LPCWSTR lpModuleName) +{ + LoadPluginsAndRestoreIAT((uintptr_t)_ReturnAddress()); + return GetModuleHandleW(lpModuleName); +} + +FARPROC WINAPI CustomGetProcAddress(HMODULE hModule, LPCSTR lpProcName) +{ + LoadPluginsAndRestoreIAT((uintptr_t)_ReturnAddress()); + return GetProcAddress(hModule, lpProcName); +} + +DWORD WINAPI CustomGetShortPathNameA(LPCSTR lpszLongPath, LPSTR lpszShortPath, DWORD cchBuffer) +{ + LoadPluginsAndRestoreIAT((uintptr_t)_ReturnAddress()); + return GetShortPathNameA(lpszLongPath, lpszShortPath, cchBuffer); +} + +BOOL WINAPI CustomFindNextFileA(HANDLE hFindFile, LPWIN32_FIND_DATAA lpFindFileData) +{ + LoadPluginsAndRestoreIAT((uintptr_t)_ReturnAddress()); + return FindNextFileA(hFindFile, lpFindFileData); +} + +BOOL WINAPI CustomFindNextFileW(HANDLE hFindFile, LPWIN32_FIND_DATAW lpFindFileData) +{ + LoadPluginsAndRestoreIAT((uintptr_t)_ReturnAddress()); + return FindNextFileW(hFindFile, lpFindFileData); +} + +HMODULE WINAPI CustomLoadLibraryA(LPCSTR lpLibFileName) +{ + LoadOriginalLibrary(); + + return LoadLibraryA(lpLibFileName); +} + +HMODULE WINAPI CustomLoadLibraryW(LPCWSTR lpLibFileName) +{ + LoadOriginalLibrary(); + + return LoadLibraryW(lpLibFileName); +} + +BOOL WINAPI CustomFreeLibrary(HMODULE hLibModule) +{ + if (hLibModule != hm) + return FreeLibrary(hLibModule); + else + return !NULL; +} + +HANDLE WINAPI CustomCreateEventA(LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset, BOOL bInitialState, LPCSTR lpName) +{ + LoadPluginsAndRestoreIAT((uintptr_t)_ReturnAddress()); + return CreateEventA(lpEventAttributes, bManualReset, bInitialState, lpName); +} + +HANDLE WINAPI CustomCreateEventW(LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset, BOOL bInitialState, LPCWSTR lpName) +{ + LoadPluginsAndRestoreIAT((uintptr_t)_ReturnAddress()); + return CreateEventW(lpEventAttributes, bManualReset, bInitialState, lpName); +} + +void WINAPI CustomGetSystemInfo(LPSYSTEM_INFO lpSystemInfo) +{ + LoadPluginsAndRestoreIAT((uintptr_t)_ReturnAddress()); + return GetSystemInfo(lpSystemInfo); +} + +LONG WINAPI CustomInterlockedCompareExchange(LONG volatile* Destination, LONG ExChange, LONG Comperand) +{ + LoadPluginsAndRestoreIAT((uintptr_t)_ReturnAddress()); + return _InterlockedCompareExchange(Destination, ExChange, Comperand); +} + +void WINAPI CustomSleep(DWORD dwMilliseconds) +{ + LoadPluginsAndRestoreIAT((uintptr_t)_ReturnAddress()); + return Sleep(dwMilliseconds); +} + +bool HookKernel32IAT(HMODULE mod, bool exe) +{ + auto hExecutableInstance = (size_t)mod; + IMAGE_NT_HEADERS* ntHeader = (IMAGE_NT_HEADERS*)(hExecutableInstance + ((IMAGE_DOS_HEADER*)hExecutableInstance)->e_lfanew); + IMAGE_IMPORT_DESCRIPTOR* pImports = (IMAGE_IMPORT_DESCRIPTOR*)(hExecutableInstance + ntHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress); + size_t nNumImports = ntHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size / sizeof(IMAGE_IMPORT_DESCRIPTOR) - 1; + + if (exe) + { + Kernel32Data[eGetStartupInfoA][ProcAddress] = (size_t)GetProcAddress(GetModuleHandle(TEXT("KERNEL32.DLL")), "GetStartupInfoA"); + Kernel32Data[eGetStartupInfoW][ProcAddress] = (size_t)GetProcAddress(GetModuleHandle(TEXT("KERNEL32.DLL")), "GetStartupInfoW"); + Kernel32Data[eGetModuleHandleA][ProcAddress] = (size_t)GetProcAddress(GetModuleHandle(TEXT("KERNEL32.DLL")), "GetModuleHandleA"); + Kernel32Data[eGetModuleHandleW][ProcAddress] = (size_t)GetProcAddress(GetModuleHandle(TEXT("KERNEL32.DLL")), "GetModuleHandleW"); + Kernel32Data[eGetProcAddress][ProcAddress] = (size_t)GetProcAddress(GetModuleHandle(TEXT("KERNEL32.DLL")), "GetProcAddress"); + Kernel32Data[eGetShortPathNameA][ProcAddress] = (size_t)GetProcAddress(GetModuleHandle(TEXT("KERNEL32.DLL")), "GetShortPathNameA"); + Kernel32Data[eFindNextFileA][ProcAddress] = (size_t)GetProcAddress(GetModuleHandle(TEXT("KERNEL32.DLL")), "FindNextFileA"); + Kernel32Data[eFindNextFileW][ProcAddress] = (size_t)GetProcAddress(GetModuleHandle(TEXT("KERNEL32.DLL")), "FindNextFileW"); + Kernel32Data[eLoadLibraryA][ProcAddress] = (size_t)GetProcAddress(GetModuleHandle(TEXT("KERNEL32.DLL")), "LoadLibraryA"); + Kernel32Data[eLoadLibraryW][ProcAddress] = (size_t)GetProcAddress(GetModuleHandle(TEXT("KERNEL32.DLL")), "LoadLibraryW"); + Kernel32Data[eFreeLibrary][ProcAddress] = (size_t)GetProcAddress(GetModuleHandle(TEXT("KERNEL32.DLL")), "FreeLibrary"); + Kernel32Data[eCreateEventA][ProcAddress] = (size_t)GetProcAddress(GetModuleHandle(TEXT("KERNEL32.DLL")), "CreateEventA"); + Kernel32Data[eCreateEventW][ProcAddress] = (size_t)GetProcAddress(GetModuleHandle(TEXT("KERNEL32.DLL")), "CreateEventW"); + Kernel32Data[eGetSystemInfo][ProcAddress] = (size_t)GetProcAddress(GetModuleHandle(TEXT("KERNEL32.DLL")), "GetSystemInfo"); + Kernel32Data[eInterlockedCompareExchange][ProcAddress] = (size_t)GetProcAddress(GetModuleHandle(TEXT("KERNEL32.DLL")), "InterlockedCompareExchange"); + Kernel32Data[eSleep][ProcAddress] = (size_t)GetProcAddress(GetModuleHandle(TEXT("KERNEL32.DLL")), "Sleep"); + } + + uint32_t matchedImports = 0; + + auto PatchIAT = [&](size_t start, size_t end, size_t exe_end) + { + for (size_t i = 0; i < nNumImports; i++) + { + if (hExecutableInstance + (pImports + i)->FirstThunk > start && !(end && hExecutableInstance + (pImports + i)->FirstThunk > end)) + end = hExecutableInstance + (pImports + i)->FirstThunk; + } + + if (!end) { end = start + 0x100; } + if (end > exe_end) //for very broken exes + { + start = hExecutableInstance; + end = exe_end; + } + + for (auto i = start; i < end; i += sizeof(size_t)) + { + DWORD dwProtect[2]; + VirtualProtect((size_t*)i, sizeof(size_t), PAGE_EXECUTE_READWRITE, &dwProtect[0]); + + auto ptr = *(size_t*)i; + if (!ptr) + continue; + + if (ptr == Kernel32Data[eGetStartupInfoA][ProcAddress]) + { + if (exe) Kernel32Data[eGetStartupInfoA][IATPtr] = i; + *(size_t*)i = (size_t)CustomGetStartupInfoA; + matchedImports++; + } + else if (ptr == Kernel32Data[eGetStartupInfoW][ProcAddress]) + { + if (exe) Kernel32Data[eGetStartupInfoW][IATPtr] = i; + *(size_t*)i = (size_t)CustomGetStartupInfoW; + matchedImports++; + } + else if (ptr == Kernel32Data[eGetModuleHandleA][ProcAddress]) + { + if (exe) Kernel32Data[eGetModuleHandleA][IATPtr] = i; + *(size_t*)i = (size_t)CustomGetModuleHandleA; + matchedImports++; + } + else if (ptr == Kernel32Data[eGetModuleHandleW][ProcAddress]) + { + if (exe) Kernel32Data[eGetModuleHandleW][IATPtr] = i; + *(size_t*)i = (size_t)CustomGetModuleHandleW; + matchedImports++; + } + else if (ptr == Kernel32Data[eGetProcAddress][ProcAddress]) + { + if (exe) Kernel32Data[eGetProcAddress][IATPtr] = i; + *(size_t*)i = (size_t)CustomGetProcAddress; + matchedImports++; + } + else if (ptr == Kernel32Data[eGetShortPathNameA][ProcAddress]) + { + if (exe) Kernel32Data[eGetShortPathNameA][IATPtr] = i; + *(size_t*)i = (size_t)CustomGetShortPathNameA; + matchedImports++; + } + else if (ptr == Kernel32Data[eFindNextFileA][ProcAddress]) + { + if (exe) Kernel32Data[eFindNextFileA][IATPtr] = i; + *(size_t*)i = (size_t)CustomFindNextFileA; + matchedImports++; + } + else if (ptr == Kernel32Data[eFindNextFileW][ProcAddress]) + { + if (exe) Kernel32Data[eFindNextFileW][IATPtr] = i; + *(size_t*)i = (size_t)CustomFindNextFileW; + matchedImports++; + } + else if (ptr == Kernel32Data[eLoadLibraryA][ProcAddress]) + { + if (exe) Kernel32Data[eLoadLibraryA][IATPtr] = i; + *(size_t*)i = (size_t)CustomLoadLibraryA; + matchedImports++; + } + else if (ptr == Kernel32Data[eLoadLibraryW][ProcAddress]) + { + if (exe) Kernel32Data[eLoadLibraryW][IATPtr] = i; + *(size_t*)i = (size_t)CustomLoadLibraryW; + matchedImports++; + } + else if (ptr == Kernel32Data[eFreeLibrary][ProcAddress]) + { + if (exe) Kernel32Data[eFreeLibrary][IATPtr] = i; + *(size_t*)i = (size_t)CustomFreeLibrary; + matchedImports++; + } + else if (ptr == Kernel32Data[eCreateEventA][ProcAddress]) + { + if (exe) Kernel32Data[eCreateEventA][IATPtr] = i; + *(size_t*)i = (size_t)CustomCreateEventA; + matchedImports++; + } + else if (ptr == Kernel32Data[eCreateEventW][ProcAddress]) + { + if (exe) Kernel32Data[eCreateEventW][IATPtr] = i; + *(size_t*)i = (size_t)CustomCreateEventW; + matchedImports++; + } + else if (ptr == Kernel32Data[eGetSystemInfo][ProcAddress]) + { + if (exe) Kernel32Data[eGetSystemInfo][IATPtr] = i; + *(size_t*)i = (size_t)CustomGetSystemInfo; + matchedImports++; + } + else if (ptr == Kernel32Data[eInterlockedCompareExchange][ProcAddress]) + { + if (exe) Kernel32Data[eInterlockedCompareExchange][IATPtr] = i; + *(size_t*)i = (size_t)CustomInterlockedCompareExchange; + matchedImports++; + } + else if (ptr == Kernel32Data[eSleep][ProcAddress]) + { + if (exe) Kernel32Data[eSleep][IATPtr] = i; + *(size_t*)i = (size_t)CustomSleep; + matchedImports++; + } + + VirtualProtect((size_t*)i, sizeof(size_t), dwProtect[0], &dwProtect[1]); + } + }; + + static auto getSection = [](const PIMAGE_NT_HEADERS nt_headers, unsigned section) -> PIMAGE_SECTION_HEADER + { + return reinterpret_cast( + (UCHAR*)nt_headers->OptionalHeader.DataDirectory + + nt_headers->OptionalHeader.NumberOfRvaAndSizes * sizeof(IMAGE_DATA_DIRECTORY) + + section * sizeof(IMAGE_SECTION_HEADER)); + }; + + static auto getSectionEnd = [](IMAGE_NT_HEADERS* ntHeader, size_t inst) -> auto + { + auto sec = getSection(ntHeader, ntHeader->FileHeader.NumberOfSections - 1); + auto secSize = max(sec->SizeOfRawData, sec->Misc.VirtualSize); + auto end = inst + max(sec->PointerToRawData, sec->VirtualAddress) + secSize; + return end; + }; + + auto hExecutableInstance_end = getSectionEnd(ntHeader, hExecutableInstance); + + // Find kernel32.dll + for (size_t i = 0; i < nNumImports; i++) + { + if ((size_t)(hExecutableInstance + (pImports + i)->Name) < hExecutableInstance_end) + { + if (!_stricmp((const char*)(hExecutableInstance + (pImports + i)->Name), "KERNEL32.DLL")) + PatchIAT(hExecutableInstance + (pImports + i)->FirstThunk, 0, hExecutableInstance_end); + } + } + + // Fixing ordinals + auto szSelfName = GetSelfName(); + + static auto PatchOrdinals = [&szSelfName](size_t hInstance) + { + IMAGE_NT_HEADERS* ntHeader = (IMAGE_NT_HEADERS*)(hInstance + ((IMAGE_DOS_HEADER*)hInstance)->e_lfanew); + IMAGE_IMPORT_DESCRIPTOR* pImports = (IMAGE_IMPORT_DESCRIPTOR*)(hInstance + ntHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress); + size_t nNumImports = ntHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size / sizeof(IMAGE_IMPORT_DESCRIPTOR) - 1; + + for (size_t i = 0; i < nNumImports; i++) + { + if ((size_t)(hInstance + (pImports + i)->Name) < getSectionEnd(ntHeader, (size_t)hInstance)) + { + if (iequals(szSelfName, (to_wstring((const char*)(hInstance + (pImports + i)->Name))))) + { + PIMAGE_THUNK_DATA thunk = (PIMAGE_THUNK_DATA)(hInstance + (pImports + i)->OriginalFirstThunk); + size_t j = 0; + while (thunk->u1.Function) + { + if (thunk->u1.Ordinal & IMAGE_ORDINAL_FLAG) + { + PIMAGE_IMPORT_BY_NAME import = (PIMAGE_IMPORT_BY_NAME)(hInstance + thunk->u1.AddressOfData); + void** p = (void**)(hInstance + (pImports + i)->FirstThunk); + } + ++thunk; + } + } + } + } + }; + + ModuleList dlls; + dlls.Enumerate(ModuleList::SearchLocation::LocalOnly); + for (auto& e : dlls.m_moduleList) + { + PatchOrdinals((size_t)std::get(e)); + } + return matchedImports > 0; +} + +LONG WINAPI CustomUnhandledExceptionFilter(LPEXCEPTION_POINTERS ExceptionInfo) +{ + // step 1: write minidump + wchar_t modulename[MAX_PATH]; + wchar_t filename[MAX_PATH]; + wchar_t timestamp[128]; + __time64_t time; + struct tm ltime; + HANDLE hFile; + HWND hWnd; + + wchar_t* modulenameptr = NULL; + if (GetModuleFileNameW(GetModuleHandle(NULL), modulename, _countof(modulename)) != 0) + { + modulenameptr = wcsrchr(modulename, '\\'); + *modulenameptr = L'\0'; + modulenameptr += 1; + } + else + { + *modulenameptr = L'err.err'; + } + + _time64(&time); + _localtime64_s(<ime, &time); + wcsftime(timestamp, _countof(timestamp), L"%Y%m%d%H%M%S", <ime); + swprintf_s(filename, L"%s\\%s\\%s.%s.dmp", modulename, L"logs", modulenameptr, timestamp); + + hFile = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + + if (hFile != INVALID_HANDLE_VALUE) + { + MINIDUMP_EXCEPTION_INFORMATION ex; + memset(&ex, 0, sizeof(ex)); + ex.ThreadId = GetCurrentThreadId(); + ex.ExceptionPointers = ExceptionInfo; + ex.ClientPointers = TRUE; + + if (FAILED(MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile, MiniDumpWithDataSegs, &ex, NULL, NULL))) + { + } + + CloseHandle(hFile); + } + + // step 2: write log + // Logs exception into buffer and writes to file + swprintf_s(filename, L"%s\\%s\\%s.%s.log", modulename, L"logs", modulenameptr, timestamp); + hFile = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + + if (hFile != INVALID_HANDLE_VALUE) + { + auto Log = [ExceptionInfo, hFile](char* buffer, size_t size, bool reg, bool stack, bool trace) + { + if (LogException(buffer, size, (LPEXCEPTION_POINTERS)ExceptionInfo, reg, stack, trace)) + { + DWORD NumberOfBytesWritten = 0; + WriteFile(hFile, buffer, strlen(buffer), &NumberOfBytesWritten, NULL); + } + }; + + // Try to make a very descriptive exception, for that we need to malloc a huge buffer... + if (auto buffer = (char*)malloc(max_logsize_ever)) + { + Log(buffer, max_logsize_ever, true, true, true); + free(buffer); + } + else + { + // Use a static buffer, no need for any allocation + static const auto size = max_logsize_basic + max_logsize_regs + max_logsize_stackdump; + static char static_buf[size]; + static_assert(size <= max_static_buffer, "Static buffer is too big"); + + Log(buffer = static_buf, sizeof(static_buf), true, true, false); + } + + CloseHandle(hFile); + } + + // step 3: exit the application + ShowCursor(TRUE); + hWnd = FindWindowW(0, L""); + SetForegroundWindow(hWnd); + + return EXCEPTION_CONTINUE_SEARCH; +} + +void Init() +{ + std::wstring modulePath = GetModuleFileNameW(hm); + std::wstring moduleName = modulePath.substr(modulePath.find_last_of(L"/\\") + 1); + moduleName.resize(moduleName.find_last_of(L'.')); + modulePath.resize(modulePath.find_last_of(L"/\\") + 1); + iniPaths.emplace_back(modulePath + moduleName + L".ini"); + iniPaths.emplace_back(modulePath + L"plugins\\config.ini"); + + std::wstring m = GetModuleFileNameW(NULL); + m = m.substr(0, m.find_last_of(L"/\\") + 1) + L"logs"; + + auto FolderExists = [](LPCWSTR szPath) -> BOOL + { + DWORD dwAttrib = GetFileAttributes(szPath); + return (dwAttrib != INVALID_FILE_ATTRIBUTES && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY)); + }; + + if (FolderExists(m.c_str())) + { + SetUnhandledExceptionFilter(CustomUnhandledExceptionFilter); + // Now stub out CustomUnhandledExceptionFilter so NO ONE ELSE can set it! + uint32_t ret = 0x909090C3; //ret + DWORD protect[2]; + VirtualProtect(&SetUnhandledExceptionFilter, sizeof(ret), PAGE_EXECUTE_READWRITE, &protect[0]); + memcpy(&SetUnhandledExceptionFilter, &ret, sizeof(ret)); + VirtualProtect(&SetUnhandledExceptionFilter, sizeof(ret), protect[0], &protect[1]); + } + + LoadEverything(); + +} + +BOOL APIENTRY DllMain(HMODULE hModule, DWORD reason, LPVOID /*lpReserved*/) +{ + if (reason == DLL_PROCESS_ATTACH) + { + hm = hModule; + Init(); + } + return TRUE; +} diff --git a/source/PD-Loader/exception.hpp b/source/PD-Loader/exception.hpp new file mode 100644 index 0000000..ff7958c --- /dev/null +++ b/source/PD-Loader/exception.hpp @@ -0,0 +1,678 @@ +#pragma once +/* + * Unhandled Exception Tracer + * by LINK/2012 + * + * This source code is offered for use in the public domain. You may + * use, modify or distribute it freely. + * + * This code is distributed in the hope that it will be useful but + * WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY + * DISCLAIMED. This includes but is not limited to warranties of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + */ + +#include +#include +#include +#include +#include +#include +#pragma comment(lib, "Dbghelp.lib") + + /* + * Special Note: + * Try not to allocate any memory in this file! + * Allocation after a exception may not be a good idea... + */ + +#define LODWORD(_qw) ((DWORD)(_qw)) +#define HIDWORD(_qw) ((DWORD)(((_qw) >> 32) & 0xffffffff)) + + // General constants +static const int sizeof_word = sizeof(void*); // Size of a CPU word (4 bytes on x86) +static const int max_chars_per_print = MAX_PATH + 256; // Max characters per Print() call +static const int symbol_max = 256; // Max size of a symbol (func symbol, var symbol, etc) +static const int max_static_buffer = 4096; // Max static buffer for logging + +// Stackdump constants +static const int stackdump_max_words = 60; // max number of CPU words that the stackdump should dump +static const int stackdump_words_per_line = 6; // max CPU words in a single line +static const int stackdump_line_count = (stackdump_max_words / stackdump_words_per_line) + 1; + +// Backtrace constants +static const int max_backtrace_ever = 100; +static const int max_backtrace = 20; + +// Maximum log size constants +static const int max_logsize_basic = (MAX_PATH + 200); // module path + other text +static const int max_logsize_regs = 32 + (4 * 4 * 28); // info + (regsPerLine * numLines * charsPerReg) +static const int max_logsize_stackdump = 32 + 80 + (stackdump_line_count * 32) + (10 * stackdump_words_per_line * stackdump_line_count); +static const int max_logsize_backtrace = 32 + max_backtrace_ever * (MAX_PATH + symbol_max + 90); +static const int max_logsize_ever = 32 + max_logsize_basic + max_logsize_regs + max_logsize_stackdump + max_logsize_backtrace; + +// Internal +class ExceptionTracer; +class StackTrace; +static HMODULE GetModuleFromAddress(LPVOID address); +static const char* GetExceptionCodeString(unsigned int code); +static const char* FindModuleName(HMODULE module, char* output, DWORD size); +static int LogException(char* buffer, size_t max, LPEXCEPTION_POINTERS pException, bool bLogRegisters, bool bLogStack, bool bLogBacktrace); +static LPTOP_LEVEL_EXCEPTION_FILTER PrevFilter = nullptr; +static void(*ExceptionCallback)(const char* buffer) = nullptr; + +// Exportable +int InstallExceptionCatcher(void(*OnException)(const char* log)); + +/* + * ExceptionTrace + * This class is responssible for tracing all possible informations about an LPEXCEPTION_POINTER + */ +class ExceptionTracer +{ +public: + ExceptionTracer(char* buffer, size_t max, LPEXCEPTION_POINTERS pException); + void PrintUnhandledException(); + void PrintRegisters(); + void PrintStackdump(); + void PrintBacktrace(); + + void EnterScope(); + void LeaveScope(); + void Print(const char* fmt, ...); + void NewLine() { Print("\n%s", spc); } + +protected: + EXCEPTION_POINTERS& exception; + EXCEPTION_RECORD& record; + CONTEXT& context; + HMODULE module; + + char* buffer; // Logging buffer + size_t len; // Logged length + size_t max; // Maximum we can log in that buffer + + char spc[(10 * 4) + 1]; // Scope/spacing buffer, 4 spaces per scope, max 10 scopes + size_t nspc; // Number spaces used up there +}; + +/* + * StackTracer + * Responssible for backtracing an stack from a context + */ +class StackTracer +{ +public: + struct Trace + { + // The following values may be null (any) + HMODULE module; // The module the func related to this frame is located + void* pc; // Program counter at func related to this frame (EIP) + void* ret; // Return address for the frame + void* frame; // The frame address (EBP) + void* stack; // The stack pointer at the frame (ESP) + }; + + StackTracer(const CONTEXT& context); + Trace* Walk(); + +private: + Trace trace; + DWORD old_options; + CONTEXT context; + STACKFRAME64 frame; +}; + +/* + * TheUnhandledExceptionFilter + * Logs an unhandled exception + */ +static LONG CALLBACK TheUnhandledExceptionFilter(LPEXCEPTION_POINTERS pException) +{ + // Logs exception into buffer and calls the callback + auto Log = [pException](char* buffer, size_t size, bool reg, bool stack, bool trace) + { + if (LogException(buffer, size, (LPEXCEPTION_POINTERS)pException, reg, stack, trace)) + ExceptionCallback(buffer); + }; + + // Try to make a very descriptive exception, for that we need to malloc a huge buffer... + if (auto buffer = (char*)malloc(max_logsize_ever)) + { + Log(buffer, max_logsize_ever, true, true, true); + free(buffer); + } + else + { + // Use a static buffer, no need for any allocation + static const auto size = max_logsize_basic + max_logsize_regs + max_logsize_stackdump; + static char static_buf[size]; + static_assert(size <= max_static_buffer, "Static buffer is too big"); + + Log(buffer = static_buf, sizeof(static_buf), true, true, false); + } + + // Continue exception propagation + return (PrevFilter ? PrevFilter(pException) : EXCEPTION_CONTINUE_SEARCH); // I'm not really sure about this return +} + +/* + * InstallExceptionCatcher + * Installs a exception handler to call the specified callback when it happens with human readalbe information. + */ +int InstallExceptionCatcher(void(*cb)(const char* log)) +{ + PrevFilter = SetUnhandledExceptionFilter(TheUnhandledExceptionFilter); + ExceptionCallback = cb; + return 1; +} + +/* + * LogException + * Takes an LPEXCEPTION_POINTERS and transforms in a string that is put in the logging steam + */ +static int LogException(char* buffer, size_t max, LPEXCEPTION_POINTERS pException, bool bLogRegisters, bool bLogStack, bool bLogBacktrace) +{ + ExceptionTracer trace(buffer, max, pException); + trace.PrintUnhandledException(); + trace.EnterScope(); + if (bLogRegisters) trace.PrintRegisters(); + if (bLogStack) trace.PrintStackdump(); + if (bLogBacktrace) trace.PrintBacktrace(); + trace.LeaveScope(); + return 1; +} + +/* + * ExceptionTracer + * Contructs a exception trace object, responssible for tracing informations about an exception + */ +ExceptionTracer::ExceptionTracer(char* buffer, size_t max, LPEXCEPTION_POINTERS pException) : + buffer(buffer), exception(*pException), record(*pException->ExceptionRecord), context(*pException->ContextRecord) +{ + this->buffer = buffer; + this->buffer[this->len = 0] = 0; + this->spc[this->nspc = 0] = 0; + this->max = max; + + // Acquiere common information that we'll access + this->module = GetModuleFromAddress(record.ExceptionAddress); +} + +/* + * Print + * Prints some formated text into the logging buffer + */ +void ExceptionTracer::Print(const char* fmt, ...) +{ + va_list va; + va_start(va, fmt); + if ((this->max - this->len) > max_chars_per_print) + this->len += vsprintf(&this->buffer[len], fmt, va); + va_end(va); +} + +/* + * EnterScope + * Enters a new scope in the logging buffer (scope is related to indentation) + * This also prints a new line + */ +void ExceptionTracer::EnterScope() +{ + nspc += 4; + spc[nspc - 4] = ' '; + spc[nspc - 3] = ' '; + spc[nspc - 2] = ' '; + spc[nspc - 1] = ' '; + spc[nspc - 0] = 0; + NewLine(); +} + +/* + * LeaveScope + * Leaves the scope + */ +void ExceptionTracer::LeaveScope() +{ + assert(nspc > 0); + nspc -= 4; + spc[nspc] = 0; + NewLine(); +} + +/* + * PrintUnhandledException + * Prints the well known "Unhandled exception at ..." into the logging buffer + */ +void ExceptionTracer::PrintUnhandledException() +{ + char module_name[MAX_PATH]; + auto dwExceptionCode = record.ExceptionCode; + uintptr_t address = (uintptr_t)record.ExceptionAddress; + + // Find out our module name for logging + if (!this->module || !GetModuleFileNameA(this->module, module_name, sizeof(module_name))) + strcpy(module_name, "unknown"); + + // Log the exception in a similar format similar to debuggers format + Print("Unhandled exception at 0x%p in %s", address, FindModuleName(module, module_name, sizeof(module_name))); + if (module) Print(" (+0x%x)", address - (uintptr_t)(module)); + Print(": 0x%X: %s", dwExceptionCode, GetExceptionCodeString(dwExceptionCode)); + + // If exception is IN_PAGE_ERROR or ACCESS_VIOLATION, we have additional information such as an address + if (dwExceptionCode == EXCEPTION_IN_PAGE_ERROR || dwExceptionCode == EXCEPTION_ACCESS_VIOLATION) + { + auto rw = (DWORD)record.ExceptionInformation[0]; // read or write? + auto addr = (ULONG_PTR)record.ExceptionInformation[1]; // which address? + + Print(" %s 0x%p", + rw == 0 ? "reading location" : rw == 1 ? "writing location" : rw == 8 ? "DEP at" : "", + addr); + + // IN_PAGE_ERROR have another information... + if (dwExceptionCode == EXCEPTION_IN_PAGE_ERROR) + { + NewLine(); + Print("Underlying NTSTATUS code that resulted in the exception is 0x%p", + record.ExceptionInformation[2]); + } + } + + Print("."); +} + +/* + * PrintRegisters + * Prints the content of the assembly registers into the logging buffer + */ +void ExceptionTracer::PrintRegisters() +{ + int regs_in_line = 0; // Amount of registers currently printed on this line + + // Prints a register, followed by spaces + auto PrintRegister = [this, ®s_in_line](const char* reg_name, size_t reg_value, const char* spaces) + { + Print("%s: 0x%p%s", reg_name, reg_value, spaces); + if (++regs_in_line >= 4) { this->NewLine(); regs_in_line = 0; } + }; + + auto PrintFloatRegister = [this, ®s_in_line](const char* reg_name, int reg_num, uint32_t reg_value1, uint32_t reg_value2, uint32_t reg_value3, uint32_t reg_value4) + { + Print("%s%02d: 0x%08X 0x%08X 0x%08X 0x%08X [ %f %f %f %f ]", reg_name, reg_num, reg_value1, reg_value2, reg_value3, reg_value4, + *(float*)& reg_value1, *(float*)& reg_value2, *(float*)& reg_value3, *(float*)& reg_value4); + if (++regs_in_line >= 1) { this->NewLine(); regs_in_line = 0; } + }; + + // Prints a general purposes register + auto PrintIntRegister = [PrintRegister](const char* reg_name, size_t reg_value) + { + PrintRegister(reg_name, reg_value, " "); + }; + + // Prints a segment register + auto PrintSegRegister = [PrintRegister](const char* reg_name, size_t reg_value) + { + PrintRegister(reg_name, reg_value, " "); + }; + + Print("Register dump:"); + EnterScope(); + { + // Print main general purposes registers + if (context.ContextFlags & CONTEXT_INTEGER) + { +#if !_M_X64 + PrintIntRegister("EAX", context.Eax); + PrintIntRegister("EBX", context.Ebx); + PrintIntRegister("ECX", context.Ecx); + PrintIntRegister("EDX", context.Edx); + PrintIntRegister("EDI", context.Edi); + PrintIntRegister("ESI", context.Esi); +#else + PrintIntRegister("RAX", context.Rax); + PrintIntRegister("RCX", context.Rcx); + PrintIntRegister("RDX", context.Rdx); + PrintIntRegister("RBX", context.Rbx); + PrintIntRegister("RBP", context.Rbp); + PrintIntRegister("RSI", context.Rsi); + PrintIntRegister("RDI", context.Rdi); + PrintIntRegister("R08", context.R8); + PrintIntRegister("R09", context.R9); + PrintIntRegister("R10", context.R10); + PrintIntRegister("R11", context.R11); + PrintIntRegister("R12", context.R12); + PrintIntRegister("R13", context.R13); + PrintIntRegister("R14", context.R14); + PrintIntRegister("R15", context.R15); +#endif + } + + // Print control registers + if (context.ContextFlags & CONTEXT_CONTROL) + { +#if !_M_X64 + PrintIntRegister("EBP", context.Ebp); + PrintIntRegister("EIP", context.Eip); + PrintIntRegister("ESP", context.Esp); + PrintIntRegister("EFL", context.EFlags); + this->NewLine(); this->NewLine(); regs_in_line = 0; + PrintSegRegister("CS", context.SegCs); + PrintSegRegister("SS", context.SegSs); +#else + PrintIntRegister("RIP", context.Rip); + PrintIntRegister("RSP", context.Rsp); + PrintIntRegister("EFL", context.EFlags); + this->NewLine(); this->NewLine(); regs_in_line = 0; + PrintSegRegister("CS", context.SegCs); + PrintSegRegister("SS", context.SegSs); +#endif + } + + this->NewLine(); regs_in_line = 0; + + // Print segment registers + if (context.ContextFlags & CONTEXT_SEGMENTS) + { + PrintSegRegister("GS", context.SegGs); + PrintSegRegister("FS", context.SegFs); + this->NewLine(); regs_in_line = 0; + PrintSegRegister("ES", context.SegEs); + PrintSegRegister("DS", context.SegDs); + } + + this->NewLine(); this->NewLine(); regs_in_line = 0; + + // Print floating point registers + if (context.ContextFlags & CONTEXT_FLOATING_POINT) + { + for (int i = 0; i < 8; i++) + { +#if !_M_X64 + auto f = *(M128A*) & (context.FloatSave.RegisterArea[i * 10]); + PrintFloatRegister("ST", i, LODWORD(f.Low), HIDWORD(f.Low), LODWORD(f.High), HIDWORD(f.High)); +#else + PrintFloatRegister("ST", i, + LODWORD(context.FltSave.FloatRegisters[i].Low), HIDWORD(context.FltSave.FloatRegisters[i].Low), + LODWORD(context.FltSave.FloatRegisters[i].High), HIDWORD(context.FltSave.FloatRegisters[i].High)); +#endif + } + + this->NewLine(); + + for (int i = 0; i < 16; i++) + { +#if !_M_X64 + auto f = *(M128A*) & (context.ExtendedRegisters[(i + 10) * 16]); + PrintFloatRegister("XMM", i, LODWORD(f.Low), HIDWORD(f.Low), LODWORD(f.High), HIDWORD(f.High)); + + if (i >= 7) + break; +#else + PrintFloatRegister("XMM", i, + LODWORD(context.FltSave.XmmRegisters[i].Low), HIDWORD(context.FltSave.XmmRegisters[i].Low), + LODWORD(context.FltSave.XmmRegisters[i].High), HIDWORD(context.FltSave.XmmRegisters[i].High)); +#endif + } + } + } + LeaveScope(); +} + +/* + * PrintStackdump + * Prints the content of the stack into the logging buffer + */ +void ExceptionTracer::PrintStackdump() +{ + // We need the ESP of the exception context to execute a stack dump, make sure we have access to it + if ((context.ContextFlags & CONTEXT_CONTROL) == 0) + return; + + static const auto align = sizeof_word; // Stack aligment + static const auto max_words_in_line_magic = stackdump_words_per_line + 10; + + MEMORY_BASIC_INFORMATION mbi; +#if !_M_X64 + uintptr_t base, bottom, top = (uintptr_t)context.Esp; +#else + uintptr_t base, bottom, top = (uintptr_t)context.Rsp; +#endif + auto words_in_line = max_words_in_line_magic; + + // Finds the bottom of the stack from it's base pointer + // Note: mbi will get overriden on this function + auto GetStackBottom = [&mbi](uintptr_t base) + { + VirtualQuery((void*)base, &mbi, sizeof(mbi)); // Find uncommited region of the stack + VirtualQuery((char*)mbi.BaseAddress + mbi.RegionSize, &mbi, sizeof(mbi)); // Find guard page + VirtualQuery((char*)mbi.BaseAddress + mbi.RegionSize, &mbi, sizeof(mbi)); // Find commited region of the stack + auto last = (uintptr_t)mbi.BaseAddress; + return (base + (last - base) + mbi.RegionSize); // base + distanceToLastRegion + lastRegionSize + }; + + // Prints an CPU word at the specified stack address + auto PrintWord = [this, &words_in_line](uintptr_t addr) + { + if (words_in_line++ >= stackdump_words_per_line) + { + // Print new line only if it's not the first time we enter here (i.e. words_in_line has magical value) + if (words_in_line != max_words_in_line_magic + 1) NewLine(); + words_in_line = 1; + Print("0x%p: ", addr); + } + Print(" %p", *(size_t*)addr); + }; + + Print("Stack dump:"); + EnterScope(); + { + // Makes sure the pointer at top (ESP) is valid and readable memory + if (VirtualQuery((void*)(top), &mbi, sizeof(mbi)) + && (mbi.State & MEM_COMMIT) + && (mbi.Protect & (PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_READWRITE | PAGE_READONLY)) != 0) + { + base = (uintptr_t)mbi.AllocationBase; // Base of the stack (uncommited) + bottom = GetStackBottom(base); // Bottom of the stack (commited) + + // Align the stack top (esp) in a 4 bytes boundary + auto remainder = top % align; + uintptr_t current = remainder ? top + (align - remainder) : top; + + // on x86 stack grows downward! (i.e. from bottom to base) + for (int n = 0; n < stackdump_max_words && current < bottom; ++n, current += align) + PrintWord(current); + + NewLine(); + Print("base: 0x%p top: 0x%p bottom: 0x%p", base, top, bottom); + NewLine(); + } + } + LeaveScope(); +} + +/* + * PrintBacktrace + * Prints a call backtrace into the logging buffer + */ +void ExceptionTracer::PrintBacktrace() +{ + StackTracer tracer(this->context); + + char module_name[MAX_PATH]; + char sym_buffer[sizeof(SYMBOL_INFO) + symbol_max]; + + int backtrace_count = 0; // Num of frames traced + bool has_symbol_api = false; // True if we have the symbol API available for use + DWORD old_options; // Saves old symbol API options + + SYMBOL_INFO& symbol = *(SYMBOL_INFO*)sym_buffer; + symbol.SizeOfStruct = sizeof(SYMBOL_INFO); + symbol.MaxNameLen = symbol_max; + + // Tries to get the symbol api + if (SymInitialize(GetCurrentProcess(), 0, TRUE)) + { + has_symbol_api = true; + old_options = SymSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_LOAD_LINES | SYMOPT_NO_PROMPTS | SYMOPT_FAIL_CRITICAL_ERRORS); + } + + Print("Backtrace (may be wrong):"); + EnterScope(); + { + // Walks on the stack until there's no frame to trace or we traced 'max_backtrace' frames + while (auto trace = tracer.Walk()) + { + if (++backtrace_count >= max_backtrace) + break; + + bool has_sym = false; // This EIP has a symbol associated with it? + DWORD64 displacement; // EIP displacement relative to symbol + + // If we have access to the symbol api, try to get symbol name from pc (eip) + if (has_symbol_api) + has_sym = trace->pc ? !!SymFromAddr(GetCurrentProcess(), (DWORD64)trace->pc, &displacement, &symbol) : false; + + // Print everything up, this.... Ew, this looks awful! + Print(backtrace_count == 1 ? "=>" : " "); // First line should have '=>' to specify where it crashed + Print("0x%p ", trace->pc); // Print EIP at frame + if (has_sym) Print("%s+0x%x ", symbol.Name, (DWORD)displacement); // Print frame func symbol + Print("in %s (+0x%x) ", // Print module + trace->module ? FindModuleName(trace->module, module_name, sizeof(module_name)) : "unknown", + (uintptr_t)(trace->pc) - (uintptr_t)(trace->module) // Module displacement + ); + if (trace->frame) Print("(0x%p) ", trace->frame); // Print frame pointer + + NewLine(); + } + } + LeaveScope(); + + // Cleanup the symbol api + if (has_symbol_api) + { + SymSetOptions(old_options); + SymCleanup(GetCurrentProcess()); + } +} + +/* + * GetExceptionCodeString + * Returns an description by an exception code + */ +static const char* GetExceptionCodeString(unsigned int code) +{ + switch (code) + { + case EXCEPTION_ACCESS_VIOLATION: return "Access violation"; + case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: return "Array bounds exceeded"; + case EXCEPTION_BREAKPOINT: return "Breakpoint exception"; + case EXCEPTION_DATATYPE_MISALIGNMENT: return "Data type misalignment exception"; + case EXCEPTION_FLT_DENORMAL_OPERAND: return "Denormal float operand"; + case EXCEPTION_FLT_DIVIDE_BY_ZERO: return "Floating-point division by zero"; + case EXCEPTION_FLT_INEXACT_RESULT: return "Floating-point inexact result"; + case EXCEPTION_FLT_INVALID_OPERATION: return "Floating-point invalid operation"; + case EXCEPTION_FLT_OVERFLOW: return "Floating-point overflow"; + case EXCEPTION_FLT_STACK_CHECK: return "Floating-point stack check"; + case EXCEPTION_FLT_UNDERFLOW: return "Floating-point underflow"; + case EXCEPTION_ILLEGAL_INSTRUCTION: return "Illegal instruction."; + case EXCEPTION_IN_PAGE_ERROR: return "In page error"; + case EXCEPTION_INT_DIVIDE_BY_ZERO: return "Integer division by zero"; + case EXCEPTION_INT_OVERFLOW: return "Integer overflow"; + case EXCEPTION_INVALID_DISPOSITION: return "Invalid disposition"; + case EXCEPTION_NONCONTINUABLE_EXCEPTION: return "Non-continuable exception"; + case EXCEPTION_PRIV_INSTRUCTION: return "Privileged instruction"; + case EXCEPTION_SINGLE_STEP: return "Single step exception"; + case EXCEPTION_STACK_OVERFLOW: return "Stack overflow"; + default: return "NO_DESCRIPTION"; + } +} + +/* + * FindModuleName + * Finds module filename or "unknown" + */ +static const char* FindModuleName(HMODULE module, char* output, DWORD maxsize) +{ + if (GetModuleFileNameA(module, output, maxsize)) + { + // Finds the filename part in the output string + char* filename = strrchr(output, '\\'); + if (!filename) filename = strrchr(output, '/'); + + // If filename found (i.e. output isn't already a filename but full path), make output be filename + if (filename) + { + size_t size = strlen(++filename); + memmove(output, filename, size); + output[size] = 0; + } + } + else + { + // Unknown module + strcpy(output, "unknown"); + } + return output; +} + +/* + * GetModuleFromAddress + * Finds module handle from some address inside it + */ +static HMODULE GetModuleFromAddress(LPVOID address) +{ + HMODULE module; + if (GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, + (char*)address, &module)) + return module; + return nullptr; +} + +/* +* StackTracer +* Constructs the tracer, we basically need to initialize the symbol api +*/ +StackTracer::StackTracer(const CONTEXT& context) +{ + // Initialise basic values + memset(&this->frame, 0, sizeof(frame)); + memcpy(&this->context, &context, sizeof(context)); + + // Setup the initial frame context +#if !_M_X64 + frame.AddrPC.Mode = AddrModeFlat; + frame.AddrPC.Offset = context.Eip; + frame.AddrFrame.Mode = AddrModeFlat; + frame.AddrFrame.Offset = context.Ebp; + frame.AddrStack.Mode = AddrModeFlat; + frame.AddrStack.Offset = context.Esp; +#else + frame.AddrPC.Mode = AddrModeFlat; + frame.AddrPC.Offset = context.Rip; + frame.AddrFrame.Mode = AddrModeFlat; + frame.AddrFrame.Offset = context.Rbp; + frame.AddrStack.Mode = AddrModeFlat; + frame.AddrStack.Offset = context.Rsp; +#endif +} + +/* + * StackTracer::Walk + * Walks on the stack, each walk is one frame of backtrace + * Returns a frame or null if the walk on the park is not possible anymore + */ +StackTracer::Trace* StackTracer::Walk() +{ + if (StackWalk64(IMAGE_FILE_MACHINE_I386, GetCurrentProcess(), GetCurrentThread(), + &frame, &context, NULL, NULL, NULL, NULL)) + { + trace.module = GetModuleFromAddress((void*)frame.AddrPC.Offset); + trace.frame = (void*)frame.AddrFrame.Offset; + trace.stack = (void*)frame.AddrStack.Offset; + trace.pc = (void*)frame.AddrPC.Offset; + trace.ret = (void*)frame.AddrReturn.Offset; + return &trace; + } + return nullptr; +} \ No newline at end of file diff --git a/source/PD-Loader/framework.h b/source/PD-Loader/framework.h new file mode 100644 index 0000000..97f1cea --- /dev/null +++ b/source/PD-Loader/framework.h @@ -0,0 +1,89 @@ +#pragma once + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +// Windows Header Files +#include +#pragma once +#include +#include +#include +#include "ModuleList.h" +#include +#pragma intrinsic(_ReturnAddress) + +struct shared +{ + FARPROC DllCanUnloadNow; + FARPROC DllGetClassObject; + FARPROC DllRegisterServer; + FARPROC DllUnregisterServer; + FARPROC DebugSetMute; + + void LoadOriginalLibrary(HMODULE dll) + { + DllCanUnloadNow = GetProcAddress(dll, "DllCanUnloadNow"); + DllGetClassObject = GetProcAddress(dll, "DllGetClassObject"); + DllRegisterServer = GetProcAddress(dll, "DllRegisterServer"); + DllUnregisterServer = GetProcAddress(dll, "DllUnregisterServer"); + DebugSetMute = GetProcAddress(dll, "DebugSetMute"); + } +} shared; + +struct dnsapi_dll +{ + HMODULE dll; + + // only some functions are implemented. + // PDAFT doesn't use many, so this should hopefully be fine + FARPROC DnsFree; + FARPROC DnsQuery_A; + FARPROC DnsQueryEx; + FARPROC DnsCancelQuery; + + // DnsQueryEx and DnsCancelQuery take pointers to structs as parameters + // (three for DnsQueryEx and one for DnsCancelQuery) + // fortunately they should fit in registers so the stack doesn't matter + // hopefully this works fine... I have no clue what I'm doing + static LONG WINAPI DnsQueryExStub() + { + return 9004; // DNS_ERROR_RCODE_NOT_IMPLEMENTED + } + static LONG WINAPI DnsCancelQueryStub() + { + return 9004; // DNS_ERROR_RCODE_NOT_IMPLEMENTED + } + + void LoadOriginalLibrary(HMODULE module) + { + dll = module; + shared.LoadOriginalLibrary(dll); + DnsFree = GetProcAddress(dll, "DnsFree"); + DnsQuery_A = GetProcAddress(dll, "DnsQuery_A"); + DnsQueryEx = GetProcAddress(dll, "DnsQueryEx"); + DnsCancelQuery = GetProcAddress(dll, "DnsCancelQuery"); + + // if entry points aren't found, GetProcAddress should return null + // I hope this is correct + // Thanks to somewhatlurker + if (DnsQueryEx == NULL) { DnsQueryEx = (FARPROC)& DnsQueryExStub; }; + if (DnsCancelQuery == NULL) { DnsCancelQuery = (FARPROC)& DnsCancelQueryStub; }; + } +} dnsapi; + +void _DnsFree() { dnsapi.DnsFree(); } +void _DnsQuery_A() { dnsapi.DnsQuery_A(); } +void _DnsQueryEx() { dnsapi.DnsQueryEx(); } +void _DnsCancelQuery() { dnsapi.DnsCancelQuery(); } + +#pragma runtime_checks( "", off ) + +#ifdef _DEBUG +#pragma message ("You are compiling the code in Debug - be warned that wrappers for export functions may not have correct code generated") +#endif + +void _DllRegisterServer() { shared.DllRegisterServer(); } +void _DllUnregisterServer() { shared.DllUnregisterServer(); } +void _DllCanUnloadNow() { shared.DllCanUnloadNow(); } +void _DllGetClassObject() { shared.DllGetClassObject(); } + +#pragma runtime_checks( "", restore ) \ No newline at end of file diff --git a/source/PD-Loader/x64.def b/source/PD-Loader/x64.def new file mode 100644 index 0000000..9fa3e38 --- /dev/null +++ b/source/PD-Loader/x64.def @@ -0,0 +1,10 @@ +LIBRARY "dnsapi" +EXPORTS +DnsFree = _DnsFree +DnsQuery_A = _DnsQuery_A +DnsQueryEx = _DnsQueryEx +DnsCancelQuery = _DnsCancelQuery +DllCanUnloadNow = _DllCanUnloadNow PRIVATE +DllGetClassObject = _DllGetClassObject PRIVATE +DllRegisterServer = _DllRegisterServer PRIVATE +DllUnregisterServer = _DllUnregisterServer PRIVATE \ No newline at end of file diff --git a/source/plugins/Launcher/Launcher.filters b/source/plugins/Launcher/Launcher.filters new file mode 100644 index 0000000..64d26db --- /dev/null +++ b/source/plugins/Launcher/Launcher.filters @@ -0,0 +1,30 @@ + + + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;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 + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + + + Source Files + + + + + Header Files + + + Header Files + + + \ No newline at end of file diff --git a/source/plugins/Launcher/Launcher.vcxproj b/source/plugins/Launcher/Launcher.vcxproj new file mode 100644 index 0000000..c698cb9 --- /dev/null +++ b/source/plugins/Launcher/Launcher.vcxproj @@ -0,0 +1,182 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + {60d5e9f4-335f-402b-9a07-d78674dffc9b} + v4.5 + ManagedCProj + Launcher + 10.0 + Launcher + + + + Application + true + v142 + true + Unicode + + + Application + false + v142 + true + Unicode + + + DynamicLibrary + true + v142 + true + Unicode + + + DynamicLibrary + false + v142 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + true + .dva + + + false + + + false + .dva + $(ProjectName) + + + + Use + pch.h + Level3 + Disabled + WIN32;_DEBUG;%(PreprocessorDefinitions) + + + + Windows + main + + + + + NotUsing + + + Level3 + Disabled + _DEBUG;%(PreprocessorDefinitions) + ..\..\..\dependencies\detours\include;..\..\..\dependencies\freeglut\include;%(AdditionalIncludeDirectories) + stdcpp17 + + + User32.lib + Windows + + + ..\..\..\dependencies\detours\lib;..\..\..\dependencies\freeglut\lib;%(AdditionalLibraryDirectories) + + + + + Use + pch.h + Level3 + WIN32;NDEBUG;%(PreprocessorDefinitions) + + + + Windows + main + + + + + NotUsing + + + Level3 + NDEBUG;%(PreprocessorDefinitions) + ..\..\..\dependencies\detours\include;..\..\..\dependencies\freeglut\include;%(AdditionalIncludeDirectories) + stdcpp17 + + + User32.lib + Windows + + + ..\..\..\dependencies\detours\lib;..\..\..\dependencies\freeglut\lib;%(AdditionalLibraryDirectories) + + + + + + + + + + + + + + + + + + CppForm + + + + + ui.h + + + + + + \ No newline at end of file diff --git a/source/plugins/Launcher/TabPadding.cpp b/source/plugins/Launcher/TabPadding.cpp new file mode 100644 index 0000000..ddf136b --- /dev/null +++ b/source/plugins/Launcher/TabPadding.cpp @@ -0,0 +1,44 @@ +#include "TabPadding.h" +//#include + +// ported from https://stackoverflow.com/a/7785745 +// original code by user LarsTech https://stackoverflow.com/users/719186/larstech + +TabPadding::TabPadding(TabControl^ tc) +{ + tabControl = tc; + tabControl->Selected += gcnew TabControlEventHandler(this, &TabPadding::tabControl_Selected); + AssignHandle(tc->Handle); +} + +void TabPadding::tabControl_Selected(Object^ sender, TabControlEventArgs^ e) +{ + tabControl->Invalidate(); +} + +void TabPadding::WndProc(Message %m) { + NativeWindow::WndProc(m); + + if (m.Msg == 15) { //WM_PAINT + Graphics^ g = Graphics::FromHwnd(m.HWnd); + + //Replace the outside white borders: + if (tabControl->Parent) { + g->SetClip(System::Drawing::Rectangle(0, 0, tabControl->Width - 2, tabControl->Height - 1), Drawing2D::CombineMode::Exclude); + g->FillRectangle(gcnew SolidBrush(tabControl->Parent->BackColor), + System::Drawing::Rectangle(0, tabControl->ItemSize.Height + 2, tabControl->Width, tabControl->Height - (tabControl->ItemSize.Height + 2))); + } + + + //Replace the inside white borders: + if (tabControl->SelectedTab) { + g->ResetClip(); + System::Drawing::Rectangle r = tabControl->SelectedTab->Bounds; + g->SetClip(r, Drawing2D::CombineMode::Exclude); + g->FillRectangle(gcnew SolidBrush(tabControl->SelectedTab->BackColor), + System::Drawing::Rectangle(r.Left - 3, r.Top - 1, r.Width + 4, r.Height + 3)); + } + + delete g; + } +} \ No newline at end of file diff --git a/source/plugins/Launcher/TabPadding.h b/source/plugins/Launcher/TabPadding.h new file mode 100644 index 0000000..3e02c1e --- /dev/null +++ b/source/plugins/Launcher/TabPadding.h @@ -0,0 +1,19 @@ +#pragma once +using namespace System::Windows::Forms; +using namespace System::Drawing; + +// ported from https://stackoverflow.com/a/7785745 +// original code by user LarsTech https://stackoverflow.com/users/719186/larstech + +ref class TabPadding : public NativeWindow +{ +public: + TabPadding(TabControl^ tc); + +private: + TabControl^ tabControl; + void tabControl_Selected(Object^ sender, TabControlEventArgs^ e); + +protected: + virtual void WndProc(Message %m) override; +}; \ No newline at end of file diff --git a/source/plugins/Launcher/background.png b/source/plugins/Launcher/background.png new file mode 100644 index 0000000..eef80fb Binary files /dev/null and b/source/plugins/Launcher/background.png differ diff --git a/source/plugins/Launcher/dllmain.cpp b/source/plugins/Launcher/dllmain.cpp new file mode 100644 index 0000000..25b1e82 --- /dev/null +++ b/source/plugins/Launcher/dllmain.cpp @@ -0,0 +1,45 @@ +#include "ui.h" +#include "framework.h" +#include +#pragma comment(lib, "detours.lib") + +using namespace System; +using namespace System::Windows::Forms; + +[STAThread] +int showUI() { + if (Environment::OSVersion->Version->Major >= 6) + SetProcessDPIAware(); + + Application::EnableVisualStyles(); + Application::SetCompatibleTextRenderingDefault(false); + Application::Run(gcnew Launcher::ui()); + return 0; +} + +int hookedMain(int argc, const char** argv, const char** envp) +{ + for (int i = 0; i < argc; ++i) + { + arg = argv[i]; + if (arg == "--launch" || nSkipLauncher) + return divaMain(argc, argv, envp); + } + return showUI(); +} + +BOOL APIENTRY DllMain(HMODULE hModule, + DWORD ul_reason_for_call, + LPVOID lpReserved +) +{ + if (ul_reason_for_call == DLL_PROCESS_ATTACH) + { + DisableThreadLibraryCalls(hModule); + DetourTransactionBegin(); + DetourUpdateThread(GetCurrentThread()); + DetourAttach(&(PVOID&)divaMain, hookedMain); + DetourTransactionCommit(); + } + return TRUE; +} \ No newline at end of file diff --git a/source/plugins/Launcher/framework.h b/source/plugins/Launcher/framework.h new file mode 100644 index 0000000..d4f639f --- /dev/null +++ b/source/plugins/Launcher/framework.h @@ -0,0 +1,60 @@ +#pragma once + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +// Windows Header Files +#include +#include + +int (__cdecl* divaMain)(int argc, const char** argv, const char** envp) = (int(__cdecl*)(int argc, const char** argv, const char** envp))0x140194D90; + +using namespace std; + +string arg; + +wstring ExePath() { + WCHAR buffer[MAX_PATH]; + GetModuleFileNameW(NULL, buffer, MAX_PATH); + return wstring(buffer); +} + +wstring DirPath() { + wstring exepath = ExePath(); + wstring::size_type pos = exepath.find_last_of(L"\\/"); + return exepath.substr(0, pos); +} + +wstring DIVA_EXECUTABLE_STRING = ExePath(); +LPCWSTR DIVA_EXECUTABLE = DIVA_EXECUTABLE_STRING.c_str(); + +wstring DIVA_EXECUTABLE_LAUNCH_STRING = DIVA_EXECUTABLE_STRING + L" --launch"; +LPWSTR DIVA_EXECUTABLE_LAUNCH = const_cast(DIVA_EXECUTABLE_LAUNCH_STRING.c_str()); + +wstring CONFIG_FILE_STRING = DirPath() + L"\\plugins\\config.ini"; +LPCWSTR CONFIG_FILE = CONFIG_FILE_STRING.c_str(); + +int nDisplay = GetPrivateProfileIntW(L"resolution", L"display", 0, CONFIG_FILE); +int nWidth = GetPrivateProfileIntW(L"resolution", L"width", 1280, CONFIG_FILE); +int nHeight = GetPrivateProfileIntW(L"resolution", L"height", 720, CONFIG_FILE); + +int nIntRes = GetPrivateProfileIntW(L"resolution", L"r.enable", FALSE, CONFIG_FILE); +int nIntResWidth = GetPrivateProfileIntW(L"resolution", L"r.width", 1280, CONFIG_FILE); +int nIntResHeight = GetPrivateProfileIntW(L"resolution", L"r.height", 720, CONFIG_FILE); + +int nBitDepth = GetPrivateProfileIntW(L"resolution", L"bitdepth", 32, CONFIG_FILE); +int nRefreshRate = GetPrivateProfileIntW(L"resolution", L"refreshrate", 60, CONFIG_FILE); + +int nCursor = GetPrivateProfileIntW(L"patches", L"cursor", TRUE, CONFIG_FILE); +int nHideFreeplay = GetPrivateProfileIntW(L"patches", L"hide_freeplay", FALSE, CONFIG_FILE); +int nStatusIcons = GetPrivateProfileIntW(L"patches", L"status_icons", 0, CONFIG_FILE); +int nHidePVWatermark = GetPrivateProfileIntW(L"patches", L"hide_pv_watermark", FALSE, CONFIG_FILE); +int nNoPVUi = GetPrivateProfileIntW(L"patches", L"no_pv_ui", FALSE, CONFIG_FILE); +int nHideVolCtrl = GetPrivateProfileIntW(L"patches", L"hide_volume", FALSE, CONFIG_FILE); +int nNoLyrics = GetPrivateProfileIntW(L"patches", L"no_lyrics", FALSE, CONFIG_FILE); +int nNoMovies = GetPrivateProfileIntW(L"patches", L"no_movies", FALSE, CONFIG_FILE); +int nNoError = GetPrivateProfileIntW(L"patches", L"no_error", TRUE, CONFIG_FILE); + +int nTAA = GetPrivateProfileIntW(L"graphics", L"TAA", TRUE, CONFIG_FILE); +int nMLAA = GetPrivateProfileIntW(L"graphics", L"MLAA", TRUE, CONFIG_FILE); +int nFPSLimit = GetPrivateProfileIntW(L"graphics", L"FPS.Limit", 0, CONFIG_FILE); + +int nSkipLauncher = GetPrivateProfileIntW(L"launcher", L"skip", FALSE, CONFIG_FILE); diff --git a/source/plugins/Launcher/ui.h b/source/plugins/Launcher/ui.h new file mode 100644 index 0000000..53bcadf --- /dev/null +++ b/source/plugins/Launcher/ui.h @@ -0,0 +1,1187 @@ +#pragma once +#include "framework.h" +#include +#include +#include "TabPadding.h" + +namespace Launcher { + + using namespace System; + using namespace System::ComponentModel; + using namespace System::Collections; + using namespace System::Windows::Forms; + using namespace System::Data; + using namespace System::Drawing; + using namespace msclr::interop; + + /// + /// Summary for ui + /// + public ref class ui : public System::Windows::Forms::Form + { + public: + ui(void) + { + InitializeComponent(); + this->button_Discord->Cursor = Cursors::Hand; + this->ClientSize = Drawing::Size(444, 323); + TabPadding^ tabpad = gcnew TabPadding(tabControl); + + // Beta version, components not working yet and tab disabled + tabPage_Components->Enabled = false; + + comboBox_Display->SelectedIndex = nDisplay; + + DEVMODEW dm = { 0 }; + dm.dmSize = sizeof(dm); + for (int iModeNum = 0; EnumDisplaySettingsW(NULL, iModeNum, &dm) != 0; iModeNum++) + { + if (!comboBox_Resolution->Items->Contains(dm.dmPelsWidth + "x" + dm.dmPelsHeight)) + { + comboBox_Resolution->Items->Add(dm.dmPelsWidth + "x" + dm.dmPelsHeight); + } + if (!comboBox_BitDepth->Items->Contains(dm.dmBitsPerPel)) + { + comboBox_BitDepth->Items->Add(dm.dmBitsPerPel); + } + if (!comboBox_RefreshRate->Items->Contains(dm.dmDisplayFrequency)) + { + comboBox_RefreshRate->Items->Add(dm.dmDisplayFrequency); + } + } + + comboBox_intResolution->Items->Add("640x480"); + comboBox_intResolution->Items->Add("960x720"); + comboBox_intResolution->Items->Add("1280x720"); + comboBox_intResolution->Items->Add("1920x1080"); + comboBox_intResolution->Items->Add("2560x1440"); + comboBox_intResolution->Items->Add("3840x2160"); + comboBox_intResolution->Items->Add("5120x2880"); + comboBox_intResolution->Items->Add("7680x4320"); + + comboBox_Resolution->Text = nWidth.ToString() + "x" + nHeight.ToString(); + checkBox_InternalRes->Checked = nIntRes; + comboBox_intResolution->Text = nIntResWidth.ToString() + "x" + nIntResHeight.ToString(); + checkBox_Cursor->Checked = nCursor; + checkBox_HideFreeplay->Checked = nHideFreeplay; + checkBox_HidePVWatermark->Checked = nHidePVWatermark; + checkBox_NoPVUi->Checked = nNoPVUi; + checkBox_NoLyrics->Checked = nNoLyrics; + checkBox_NoMovies->Checked = nNoMovies; + checkBox_HideVolCtrl->Checked = nHideVolCtrl; + comboBox_StatusIcons->SelectedIndex = nStatusIcons; + checkBox_TAA->Checked = nTAA; + checkBox_MLAA->Checked = nMLAA; + checkBox_DisableErrorBanner->Checked = nNoError; + textBox_FPSLimit->Text = nFPSLimit.ToString(); + checkBox_SkipLauncher->Checked = nSkipLauncher; + comboBox_BitDepth->Text = nBitDepth.ToString(); + comboBox_RefreshRate->Text = nRefreshRate.ToString(); + + if (!nIntRes) + { + comboBox_intResolution->Enabled = false; + } + if (nDisplay == 0 || nDisplay == 2) + { + comboBox_Resolution->Enabled = true; + } + else + { + comboBox_Resolution->Enabled = false; + } + if (comboBox_Display->SelectedIndex == 2) + { + comboBox_BitDepth->Enabled = true; + comboBox_RefreshRate->Enabled = true; + } + else + { + comboBox_BitDepth->Enabled = false; + comboBox_RefreshRate->Enabled = false; + } + } + + protected: + /// + /// Clean up any resources being used. + /// + ~ui() + { + if (components) + { + delete components; + } + } + private: System::Windows::Forms::Button^ button_Launch; + private: System::Windows::Forms::Button^ button_Exit; + private: System::Windows::Forms::Label^ label_Resolution; + + protected: + + + + private: System::Windows::Forms::GroupBox^ groupBox_ScreenRes; + + + private: System::Windows::Forms::TabControl^ tabControl; + private: System::Windows::Forms::TabPage^ tabPage_Resolution; + private: System::Windows::Forms::GroupBox^ groupBox_InternalRes; + private: System::Windows::Forms::CheckBox^ checkBox_InternalRes; + private: System::Windows::Forms::TabPage^ tabPage_Components; + private: System::Windows::Forms::CheckBox^ checkBox_sys_timer; + private: System::Windows::Forms::CheckBox^ checkBox_touch_panel_emulator; + private: System::Windows::Forms::CheckBox^ checkBox_touch_slider_emulator; + private: System::Windows::Forms::CheckBox^ checkBox_input_emulator; + private: System::Windows::Forms::CheckBox^ checkBox_player_data_manager; + + private: System::Windows::Forms::CheckBox^ checkBox_frame_rate_manager; + private: System::Windows::Forms::CheckBox^ checkBox_stage_manager; + private: System::Windows::Forms::CheckBox^ checkBox_fast_loader; + private: System::Windows::Forms::CheckBox^ checkBox_camera_controller; + private: System::Windows::Forms::Panel^ panel_Components; + + private: System::Windows::Forms::CheckBox^ checkBox_scale_component; + private: System::Windows::Forms::CheckBox^ checkBox_fps_limiter; + private: System::Windows::Forms::TabPage^ tabPage_Patches; + private: System::Windows::Forms::Panel^ panel_Patches; + + + + private: System::Windows::Forms::Label^ label_FPSLimit; + + + + private: System::Windows::Forms::CheckBox^ checkBox_Cursor; + + private: System::Windows::Forms::Label^ label_intResolution; + + + + + + + private: System::Windows::Forms::TextBox^ textBox_FPSLimit; + private: System::Windows::Forms::CheckBox^ checkBox_HideFreeplay; + private: System::Windows::Forms::CheckBox^ checkBox_MLAA; + private: System::Windows::Forms::CheckBox^ checkBox_TAA; + + private: System::Windows::Forms::CheckBox^ checkBox_HidePVWatermark; + private: System::Windows::Forms::CheckBox^ checkBox_NoLyrics; + private: System::Windows::Forms::CheckBox^ checkBox_NoPVUi; + private: System::Windows::Forms::CheckBox^ checkBox_HideVolCtrl; + private: System::Windows::Forms::ComboBox^ comboBox_Display; + private: System::Windows::Forms::Label^ label_Display; +private: System::Windows::Forms::ComboBox^ comboBox_Resolution; +private: System::Windows::Forms::ComboBox^ comboBox_intResolution; + +private: System::Windows::Forms::ComboBox^ comboBox_StatusIcons; +private: System::Windows::Forms::Label^ label_StatusIcons; +private: System::Windows::Forms::Button^ button_Discord; + +private: System::Windows::Forms::Panel^ panel_innerPatches; + + +private: System::Windows::Forms::TableLayoutPanel^ tableLayoutPanel_FPSLimit; + +private: System::Windows::Forms::TableLayoutPanel^ tableLayoutPanel_Status; + + + + + + +private: System::Windows::Forms::CheckBox^ checkBox_NoMovies; +private: System::Windows::Forms::Label^ label_Soon; +private: System::Windows::Forms::Button^ button_github; +private: System::Windows::Forms::CheckBox^ checkBox_DisableErrorBanner; +private: System::Windows::Forms::CheckBox^ checkBox_SkipLauncher; +private: System::Windows::Forms::TableLayoutPanel^ tableLayoutPanel_ScreenRes; +private: System::Windows::Forms::TableLayoutPanel^ tableLayoutPanel_IntRes; +private: System::Windows::Forms::Label^ label_RefreshRate; +private: System::Windows::Forms::ComboBox^ comboBox_BitDepth; +private: System::Windows::Forms::ComboBox^ comboBox_RefreshRate; +private: System::Windows::Forms::Label^ label_BitDepth; + + + + + + private: + /// + /// Required designer variable. + /// + System::ComponentModel::Container ^components; + +#pragma region Windows Form Designer generated code + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + void InitializeComponent(void) + { + System::ComponentModel::ComponentResourceManager^ resources = (gcnew System::ComponentModel::ComponentResourceManager(ui::typeid)); + this->button_Launch = (gcnew System::Windows::Forms::Button()); + this->button_Exit = (gcnew System::Windows::Forms::Button()); + this->label_Resolution = (gcnew System::Windows::Forms::Label()); + this->groupBox_ScreenRes = (gcnew System::Windows::Forms::GroupBox()); + this->tableLayoutPanel_ScreenRes = (gcnew System::Windows::Forms::TableLayoutPanel()); + this->label_RefreshRate = (gcnew System::Windows::Forms::Label()); + this->label_Display = (gcnew System::Windows::Forms::Label()); + this->comboBox_Resolution = (gcnew System::Windows::Forms::ComboBox()); + this->comboBox_Display = (gcnew System::Windows::Forms::ComboBox()); + this->comboBox_BitDepth = (gcnew System::Windows::Forms::ComboBox()); + this->comboBox_RefreshRate = (gcnew System::Windows::Forms::ComboBox()); + this->label_BitDepth = (gcnew System::Windows::Forms::Label()); + this->tabControl = (gcnew System::Windows::Forms::TabControl()); + this->tabPage_Resolution = (gcnew System::Windows::Forms::TabPage()); + this->groupBox_InternalRes = (gcnew System::Windows::Forms::GroupBox()); + this->tableLayoutPanel_IntRes = (gcnew System::Windows::Forms::TableLayoutPanel()); + this->comboBox_intResolution = (gcnew System::Windows::Forms::ComboBox()); + this->checkBox_InternalRes = (gcnew System::Windows::Forms::CheckBox()); + this->label_intResolution = (gcnew System::Windows::Forms::Label()); + this->tabPage_Patches = (gcnew System::Windows::Forms::TabPage()); + this->panel_Patches = (gcnew System::Windows::Forms::Panel()); + this->panel_innerPatches = (gcnew System::Windows::Forms::Panel()); + this->checkBox_SkipLauncher = (gcnew System::Windows::Forms::CheckBox()); + this->checkBox_DisableErrorBanner = (gcnew System::Windows::Forms::CheckBox()); + this->checkBox_NoMovies = (gcnew System::Windows::Forms::CheckBox()); + this->checkBox_Cursor = (gcnew System::Windows::Forms::CheckBox()); + this->tableLayoutPanel_FPSLimit = (gcnew System::Windows::Forms::TableLayoutPanel()); + this->textBox_FPSLimit = (gcnew System::Windows::Forms::TextBox()); + this->label_FPSLimit = (gcnew System::Windows::Forms::Label()); + this->checkBox_TAA = (gcnew System::Windows::Forms::CheckBox()); + this->tableLayoutPanel_Status = (gcnew System::Windows::Forms::TableLayoutPanel()); + this->label_StatusIcons = (gcnew System::Windows::Forms::Label()); + this->comboBox_StatusIcons = (gcnew System::Windows::Forms::ComboBox()); + this->checkBox_MLAA = (gcnew System::Windows::Forms::CheckBox()); + this->checkBox_HideFreeplay = (gcnew System::Windows::Forms::CheckBox()); + this->checkBox_HidePVWatermark = (gcnew System::Windows::Forms::CheckBox()); + this->checkBox_HideVolCtrl = (gcnew System::Windows::Forms::CheckBox()); + this->checkBox_NoLyrics = (gcnew System::Windows::Forms::CheckBox()); + this->checkBox_NoPVUi = (gcnew System::Windows::Forms::CheckBox()); + this->tabPage_Components = (gcnew System::Windows::Forms::TabPage()); + this->panel_Components = (gcnew System::Windows::Forms::Panel()); + this->label_Soon = (gcnew System::Windows::Forms::Label()); + this->checkBox_fps_limiter = (gcnew System::Windows::Forms::CheckBox()); + this->checkBox_scale_component = (gcnew System::Windows::Forms::CheckBox()); + this->checkBox_camera_controller = (gcnew System::Windows::Forms::CheckBox()); + this->checkBox_input_emulator = (gcnew System::Windows::Forms::CheckBox()); + this->checkBox_fast_loader = (gcnew System::Windows::Forms::CheckBox()); + this->checkBox_touch_slider_emulator = (gcnew System::Windows::Forms::CheckBox()); + this->checkBox_stage_manager = (gcnew System::Windows::Forms::CheckBox()); + this->checkBox_touch_panel_emulator = (gcnew System::Windows::Forms::CheckBox()); + this->checkBox_frame_rate_manager = (gcnew System::Windows::Forms::CheckBox()); + this->checkBox_sys_timer = (gcnew System::Windows::Forms::CheckBox()); + this->checkBox_player_data_manager = (gcnew System::Windows::Forms::CheckBox()); + this->button_Discord = (gcnew System::Windows::Forms::Button()); + this->button_github = (gcnew System::Windows::Forms::Button()); + this->groupBox_ScreenRes->SuspendLayout(); + this->tableLayoutPanel_ScreenRes->SuspendLayout(); + this->tabControl->SuspendLayout(); + this->tabPage_Resolution->SuspendLayout(); + this->groupBox_InternalRes->SuspendLayout(); + this->tableLayoutPanel_IntRes->SuspendLayout(); + this->tabPage_Patches->SuspendLayout(); + this->panel_Patches->SuspendLayout(); + this->panel_innerPatches->SuspendLayout(); + this->tableLayoutPanel_FPSLimit->SuspendLayout(); + this->tableLayoutPanel_Status->SuspendLayout(); + this->tabPage_Components->SuspendLayout(); + this->panel_Components->SuspendLayout(); + this->SuspendLayout(); + // + // button_Launch + // + this->button_Launch->FlatAppearance->BorderColor = System::Drawing::SystemColors::Control; + this->button_Launch->FlatStyle = System::Windows::Forms::FlatStyle::Flat; + this->button_Launch->Location = System::Drawing::Point(18, 267); + this->button_Launch->Name = L"button_Launch"; + this->button_Launch->Size = System::Drawing::Size(75, 23); + this->button_Launch->TabIndex = 20; + this->button_Launch->Text = L"Launch"; + this->button_Launch->Click += gcnew System::EventHandler(this, &ui::Button_Launch_Click); + // + // button_Exit + // + this->button_Exit->DialogResult = System::Windows::Forms::DialogResult::Cancel; + this->button_Exit->FlatAppearance->BorderColor = System::Drawing::SystemColors::Control; + this->button_Exit->FlatStyle = System::Windows::Forms::FlatStyle::Flat; + this->button_Exit->Location = System::Drawing::Point(139, 267); + this->button_Exit->Name = L"button_Exit"; + this->button_Exit->Size = System::Drawing::Size(75, 23); + this->button_Exit->TabIndex = 21; + this->button_Exit->Text = L"Exit"; + this->button_Exit->Click += gcnew System::EventHandler(this, &ui::Button_Exit_Click); + // + // label_Resolution + // + this->label_Resolution->Anchor = System::Windows::Forms::AnchorStyles::Left; + this->label_Resolution->AutoSize = true; + this->label_Resolution->FlatStyle = System::Windows::Forms::FlatStyle::Flat; + this->label_Resolution->Location = System::Drawing::Point(3, 32); + this->label_Resolution->Name = L"label_Resolution"; + this->label_Resolution->Size = System::Drawing::Size(60, 13); + this->label_Resolution->TabIndex = 11; + this->label_Resolution->Text = L"Resolution:"; + // + // groupBox_ScreenRes + // + this->groupBox_ScreenRes->Controls->Add(this->tableLayoutPanel_ScreenRes); + this->groupBox_ScreenRes->FlatStyle = System::Windows::Forms::FlatStyle::Flat; + this->groupBox_ScreenRes->ForeColor = System::Drawing::Color::White; + this->groupBox_ScreenRes->Location = System::Drawing::Point(8, 6); + this->groupBox_ScreenRes->Name = L"groupBox_ScreenRes"; + this->groupBox_ScreenRes->Size = System::Drawing::Size(208, 133); + this->groupBox_ScreenRes->TabIndex = 10; + this->groupBox_ScreenRes->TabStop = false; + this->groupBox_ScreenRes->Text = L"Screen Resolution"; + // + // tableLayoutPanel_ScreenRes + // + this->tableLayoutPanel_ScreenRes->ColumnCount = 2; + this->tableLayoutPanel_ScreenRes->ColumnStyles->Add((gcnew System::Windows::Forms::ColumnStyle(System::Windows::Forms::SizeType::Percent, + 50))); + this->tableLayoutPanel_ScreenRes->ColumnStyles->Add((gcnew System::Windows::Forms::ColumnStyle(System::Windows::Forms::SizeType::Percent, + 50))); + this->tableLayoutPanel_ScreenRes->Controls->Add(this->label_RefreshRate, 0, 3); + this->tableLayoutPanel_ScreenRes->Controls->Add(this->label_Display, 0, 0); + this->tableLayoutPanel_ScreenRes->Controls->Add(this->comboBox_Resolution, 1, 1); + this->tableLayoutPanel_ScreenRes->Controls->Add(this->label_Resolution, 0, 1); + this->tableLayoutPanel_ScreenRes->Controls->Add(this->comboBox_Display, 1, 0); + this->tableLayoutPanel_ScreenRes->Controls->Add(this->comboBox_BitDepth, 1, 2); + this->tableLayoutPanel_ScreenRes->Controls->Add(this->comboBox_RefreshRate, 1, 3); + this->tableLayoutPanel_ScreenRes->Controls->Add(this->label_BitDepth, 0, 2); + this->tableLayoutPanel_ScreenRes->Location = System::Drawing::Point(9, 19); + this->tableLayoutPanel_ScreenRes->Name = L"tableLayoutPanel_ScreenRes"; + this->tableLayoutPanel_ScreenRes->RowCount = 4; + this->tableLayoutPanel_ScreenRes->RowStyles->Add((gcnew System::Windows::Forms::RowStyle(System::Windows::Forms::SizeType::Percent, + 47.36842F))); + this->tableLayoutPanel_ScreenRes->RowStyles->Add((gcnew System::Windows::Forms::RowStyle(System::Windows::Forms::SizeType::Percent, + 52.63158F))); + this->tableLayoutPanel_ScreenRes->RowStyles->Add((gcnew System::Windows::Forms::RowStyle(System::Windows::Forms::SizeType::Absolute, + 26))); + this->tableLayoutPanel_ScreenRes->RowStyles->Add((gcnew System::Windows::Forms::RowStyle(System::Windows::Forms::SizeType::Absolute, + 28))); + this->tableLayoutPanel_ScreenRes->Size = System::Drawing::Size(193, 107); + this->tableLayoutPanel_ScreenRes->TabIndex = 15; + // + // label_RefreshRate + // + this->label_RefreshRate->Anchor = System::Windows::Forms::AnchorStyles::Left; + this->label_RefreshRate->AutoSize = true; + this->label_RefreshRate->FlatStyle = System::Windows::Forms::FlatStyle::Flat; + this->label_RefreshRate->Location = System::Drawing::Point(3, 86); + this->label_RefreshRate->Name = L"label_RefreshRate"; + this->label_RefreshRate->Size = System::Drawing::Size(73, 13); + this->label_RefreshRate->TabIndex = 105; + this->label_RefreshRate->Text = L"Refresh Rate:"; + // + // label_Display + // + this->label_Display->Anchor = System::Windows::Forms::AnchorStyles::Left; + this->label_Display->AutoSize = true; + this->label_Display->FlatStyle = System::Windows::Forms::FlatStyle::Flat; + this->label_Display->Location = System::Drawing::Point(3, 6); + this->label_Display->Name = L"label_Display"; + this->label_Display->Size = System::Drawing::Size(44, 13); + this->label_Display->TabIndex = 13; + this->label_Display->Text = L"Display:"; + // + // comboBox_Resolution + // + this->comboBox_Resolution->Anchor = System::Windows::Forms::AnchorStyles::Left; + this->comboBox_Resolution->FlatStyle = System::Windows::Forms::FlatStyle::Flat; + this->comboBox_Resolution->FormattingEnabled = true; + this->comboBox_Resolution->Location = System::Drawing::Point(99, 28); + this->comboBox_Resolution->Name = L"comboBox_Resolution"; + this->comboBox_Resolution->Size = System::Drawing::Size(85, 21); + this->comboBox_Resolution->TabIndex = 12; + // + // comboBox_Display + // + this->comboBox_Display->Anchor = System::Windows::Forms::AnchorStyles::Left; + this->comboBox_Display->DropDownStyle = System::Windows::Forms::ComboBoxStyle::DropDownList; + this->comboBox_Display->FlatStyle = System::Windows::Forms::FlatStyle::Flat; + this->comboBox_Display->FormattingEnabled = true; + this->comboBox_Display->Items->AddRange(gcnew cli::array< System::Object^ >(3) { L"Windowed", L"Borderless", L"Fullscreen" }); + this->comboBox_Display->Location = System::Drawing::Point(99, 3); + this->comboBox_Display->Name = L"comboBox_Display"; + this->comboBox_Display->Size = System::Drawing::Size(85, 21); + this->comboBox_Display->TabIndex = 14; + this->comboBox_Display->SelectedIndexChanged += gcnew System::EventHandler(this, &ui::ComboBox_Display_SelectedIndexChanged); + // + // comboBox_BitDepth + // + this->comboBox_BitDepth->FlatStyle = System::Windows::Forms::FlatStyle::Flat; + this->comboBox_BitDepth->FormattingEnabled = true; + this->comboBox_BitDepth->Location = System::Drawing::Point(99, 55); + this->comboBox_BitDepth->Name = L"comboBox_BitDepth"; + this->comboBox_BitDepth->Size = System::Drawing::Size(57, 21); + this->comboBox_BitDepth->TabIndex = 102; + // + // comboBox_RefreshRate + // + this->comboBox_RefreshRate->FlatStyle = System::Windows::Forms::FlatStyle::Flat; + this->comboBox_RefreshRate->FormattingEnabled = true; + this->comboBox_RefreshRate->Location = System::Drawing::Point(99, 81); + this->comboBox_RefreshRate->Name = L"comboBox_RefreshRate"; + this->comboBox_RefreshRate->Size = System::Drawing::Size(57, 21); + this->comboBox_RefreshRate->TabIndex = 103; + // + // label_BitDepth + // + this->label_BitDepth->Anchor = System::Windows::Forms::AnchorStyles::Left; + this->label_BitDepth->AutoSize = true; + this->label_BitDepth->FlatStyle = System::Windows::Forms::FlatStyle::Flat; + this->label_BitDepth->Location = System::Drawing::Point(3, 58); + this->label_BitDepth->Name = L"label_BitDepth"; + this->label_BitDepth->Size = System::Drawing::Size(54, 13); + this->label_BitDepth->TabIndex = 104; + this->label_BitDepth->Text = L"Bit Depth:"; + // + // tabControl + // + this->tabControl->Appearance = System::Windows::Forms::TabAppearance::FlatButtons; + this->tabControl->Controls->Add(this->tabPage_Resolution); + this->tabControl->Controls->Add(this->tabPage_Patches); + this->tabControl->Controls->Add(this->tabPage_Components); + this->tabControl->Location = System::Drawing::Point(0, 0); + this->tabControl->Name = L"tabControl"; + this->tabControl->SelectedIndex = 0; + this->tabControl->Size = System::Drawing::Size(233, 261); + this->tabControl->TabIndex = 10; + // + // tabPage_Resolution + // + this->tabPage_Resolution->BackColor = System::Drawing::Color::FromArgb(static_cast(static_cast(64)), + static_cast(static_cast(64)), static_cast(static_cast(64))); + this->tabPage_Resolution->Controls->Add(this->groupBox_InternalRes); + this->tabPage_Resolution->Controls->Add(this->groupBox_ScreenRes); + this->tabPage_Resolution->Location = System::Drawing::Point(4, 25); + this->tabPage_Resolution->Name = L"tabPage_Resolution"; + this->tabPage_Resolution->Padding = System::Windows::Forms::Padding(3); + this->tabPage_Resolution->Size = System::Drawing::Size(225, 232); + this->tabPage_Resolution->TabIndex = 0; + this->tabPage_Resolution->Text = L"Resolution"; + // + // groupBox_InternalRes + // + this->groupBox_InternalRes->Controls->Add(this->tableLayoutPanel_IntRes); + this->groupBox_InternalRes->FlatStyle = System::Windows::Forms::FlatStyle::Flat; + this->groupBox_InternalRes->ForeColor = System::Drawing::Color::White; + this->groupBox_InternalRes->Location = System::Drawing::Point(8, 145); + this->groupBox_InternalRes->Name = L"groupBox_InternalRes"; + this->groupBox_InternalRes->Size = System::Drawing::Size(208, 83); + this->groupBox_InternalRes->TabIndex = 20; + this->groupBox_InternalRes->TabStop = false; + this->groupBox_InternalRes->Text = L"Internal Resolution"; + // + // tableLayoutPanel_IntRes + // + this->tableLayoutPanel_IntRes->ColumnCount = 2; + this->tableLayoutPanel_IntRes->ColumnStyles->Add((gcnew System::Windows::Forms::ColumnStyle(System::Windows::Forms::SizeType::Percent, + 50))); + this->tableLayoutPanel_IntRes->ColumnStyles->Add((gcnew System::Windows::Forms::ColumnStyle(System::Windows::Forms::SizeType::Percent, + 50))); + this->tableLayoutPanel_IntRes->Controls->Add(this->comboBox_intResolution, 1, 1); + this->tableLayoutPanel_IntRes->Controls->Add(this->checkBox_InternalRes, 0, 0); + this->tableLayoutPanel_IntRes->Controls->Add(this->label_intResolution, 0, 1); + this->tableLayoutPanel_IntRes->Location = System::Drawing::Point(6, 19); + this->tableLayoutPanel_IntRes->Name = L"tableLayoutPanel_IntRes"; + this->tableLayoutPanel_IntRes->RowCount = 2; + this->tableLayoutPanel_IntRes->RowStyles->Add((gcnew System::Windows::Forms::RowStyle(System::Windows::Forms::SizeType::Percent, + 50))); + this->tableLayoutPanel_IntRes->RowStyles->Add((gcnew System::Windows::Forms::RowStyle(System::Windows::Forms::SizeType::Percent, + 50))); + this->tableLayoutPanel_IntRes->Size = System::Drawing::Size(196, 57); + this->tableLayoutPanel_IntRes->TabIndex = 24; + // + // comboBox_intResolution + // + this->comboBox_intResolution->FlatStyle = System::Windows::Forms::FlatStyle::Flat; + this->comboBox_intResolution->FormattingEnabled = true; + this->comboBox_intResolution->Location = System::Drawing::Point(101, 31); + this->comboBox_intResolution->Name = L"comboBox_intResolution"; + this->comboBox_intResolution->Size = System::Drawing::Size(85, 21); + this->comboBox_intResolution->TabIndex = 23; + // + // checkBox_InternalRes + // + this->checkBox_InternalRes->Anchor = System::Windows::Forms::AnchorStyles::Left; + this->checkBox_InternalRes->AutoSize = true; + this->checkBox_InternalRes->FlatStyle = System::Windows::Forms::FlatStyle::Flat; + this->checkBox_InternalRes->Location = System::Drawing::Point(3, 5); + this->checkBox_InternalRes->Name = L"checkBox_InternalRes"; + this->checkBox_InternalRes->Size = System::Drawing::Size(56, 17); + this->checkBox_InternalRes->TabIndex = 21; + this->checkBox_InternalRes->Text = L"Enable"; + this->checkBox_InternalRes->CheckedChanged += gcnew System::EventHandler(this, &ui::CheckBox_InternalRes_CheckedChanged); + // + // label_intResolution + // + this->label_intResolution->Anchor = System::Windows::Forms::AnchorStyles::Left; + this->label_intResolution->AutoSize = true; + this->label_intResolution->FlatStyle = System::Windows::Forms::FlatStyle::Flat; + this->label_intResolution->Location = System::Drawing::Point(3, 36); + this->label_intResolution->Name = L"label_intResolution"; + this->label_intResolution->Size = System::Drawing::Size(60, 13); + this->label_intResolution->TabIndex = 22; + this->label_intResolution->Text = L"Resolution:"; + // + // tabPage_Patches + // + this->tabPage_Patches->BackColor = System::Drawing::Color::FromArgb(static_cast(static_cast(64)), static_cast(static_cast(64)), + static_cast(static_cast(64))); + this->tabPage_Patches->Controls->Add(this->panel_Patches); + this->tabPage_Patches->Location = System::Drawing::Point(4, 25); + this->tabPage_Patches->Name = L"tabPage_Patches"; + this->tabPage_Patches->Size = System::Drawing::Size(225, 232); + this->tabPage_Patches->TabIndex = 1; + this->tabPage_Patches->Text = L"Options"; + // + // panel_Patches + // + this->panel_Patches->AutoScroll = true; + this->panel_Patches->Controls->Add(this->panel_innerPatches); + this->panel_Patches->Location = System::Drawing::Point(0, 0); + this->panel_Patches->Name = L"panel_Patches"; + this->panel_Patches->Size = System::Drawing::Size(225, 232); + this->panel_Patches->TabIndex = 9; + // + // panel_innerPatches + // + this->panel_innerPatches->Controls->Add(this->checkBox_SkipLauncher); + this->panel_innerPatches->Controls->Add(this->checkBox_DisableErrorBanner); + this->panel_innerPatches->Controls->Add(this->checkBox_NoMovies); + this->panel_innerPatches->Controls->Add(this->checkBox_Cursor); + this->panel_innerPatches->Controls->Add(this->tableLayoutPanel_FPSLimit); + this->panel_innerPatches->Controls->Add(this->checkBox_TAA); + this->panel_innerPatches->Controls->Add(this->tableLayoutPanel_Status); + this->panel_innerPatches->Controls->Add(this->checkBox_MLAA); + this->panel_innerPatches->Controls->Add(this->checkBox_HideFreeplay); + this->panel_innerPatches->Controls->Add(this->checkBox_HidePVWatermark); + this->panel_innerPatches->Controls->Add(this->checkBox_HideVolCtrl); + this->panel_innerPatches->Controls->Add(this->checkBox_NoLyrics); + this->panel_innerPatches->Controls->Add(this->checkBox_NoPVUi); + this->panel_innerPatches->Location = System::Drawing::Point(2, 2); + this->panel_innerPatches->Margin = System::Windows::Forms::Padding(2); + this->panel_innerPatches->Name = L"panel_innerPatches"; + this->panel_innerPatches->Size = System::Drawing::Size(204, 311); + this->panel_innerPatches->TabIndex = 115; + // + // checkBox_SkipLauncher + // + this->checkBox_SkipLauncher->AutoSize = true; + this->checkBox_SkipLauncher->FlatStyle = System::Windows::Forms::FlatStyle::Flat; + this->checkBox_SkipLauncher->Location = System::Drawing::Point(3, 227); + this->checkBox_SkipLauncher->Name = L"checkBox_SkipLauncher"; + this->checkBox_SkipLauncher->Size = System::Drawing::Size(92, 17); + this->checkBox_SkipLauncher->TabIndex = 116; + this->checkBox_SkipLauncher->Text = L"Skip Launcher"; + // + // checkBox_DisableErrorBanner + // + this->checkBox_DisableErrorBanner->AutoSize = true; + this->checkBox_DisableErrorBanner->FlatStyle = System::Windows::Forms::FlatStyle::Flat; + this->checkBox_DisableErrorBanner->Location = System::Drawing::Point(3, 204); + this->checkBox_DisableErrorBanner->Name = L"checkBox_DisableErrorBanner"; + this->checkBox_DisableErrorBanner->Size = System::Drawing::Size(120, 17); + this->checkBox_DisableErrorBanner->TabIndex = 115; + this->checkBox_DisableErrorBanner->Text = L"Disable Error Banner"; + // + // checkBox_NoMovies + // + this->checkBox_NoMovies->AutoSize = true; + this->checkBox_NoMovies->FlatStyle = System::Windows::Forms::FlatStyle::Flat; + this->checkBox_NoMovies->Location = System::Drawing::Point(3, 117); + this->checkBox_NoMovies->Margin = System::Windows::Forms::Padding(2); + this->checkBox_NoMovies->Name = L"checkBox_NoMovies"; + this->checkBox_NoMovies->Size = System::Drawing::Size(95, 17); + this->checkBox_NoMovies->TabIndex = 15; + this->checkBox_NoMovies->Text = L"Disable Movies"; + // + // checkBox_Cursor + // + this->checkBox_Cursor->AutoSize = true; + this->checkBox_Cursor->FlatStyle = System::Windows::Forms::FlatStyle::Flat; + this->checkBox_Cursor->Location = System::Drawing::Point(3, 3); + this->checkBox_Cursor->Name = L"checkBox_Cursor"; + this->checkBox_Cursor->Size = System::Drawing::Size(53, 17); + this->checkBox_Cursor->TabIndex = 21; + this->checkBox_Cursor->Text = L"Cursor"; + // + // tableLayoutPanel_FPSLimit + // + this->tableLayoutPanel_FPSLimit->ColumnCount = 2; + this->tableLayoutPanel_FPSLimit->ColumnStyles->Add((gcnew System::Windows::Forms::ColumnStyle(System::Windows::Forms::SizeType::Percent, + 41.4966F))); + this->tableLayoutPanel_FPSLimit->ColumnStyles->Add((gcnew System::Windows::Forms::ColumnStyle(System::Windows::Forms::SizeType::Percent, + 58.5034F))); + this->tableLayoutPanel_FPSLimit->Controls->Add(this->textBox_FPSLimit, 1, 0); + this->tableLayoutPanel_FPSLimit->Controls->Add(this->label_FPSLimit, 0, 0); + this->tableLayoutPanel_FPSLimit->Location = System::Drawing::Point(2, 281); + this->tableLayoutPanel_FPSLimit->Margin = System::Windows::Forms::Padding(2); + this->tableLayoutPanel_FPSLimit->Name = L"tableLayoutPanel_FPSLimit"; + this->tableLayoutPanel_FPSLimit->RowCount = 1; + this->tableLayoutPanel_FPSLimit->RowStyles->Add((gcnew System::Windows::Forms::RowStyle(System::Windows::Forms::SizeType::Percent, + 50))); + this->tableLayoutPanel_FPSLimit->Size = System::Drawing::Size(147, 28); + this->tableLayoutPanel_FPSLimit->TabIndex = 114; + // + // textBox_FPSLimit + // + this->textBox_FPSLimit->BorderStyle = System::Windows::Forms::BorderStyle::FixedSingle; + this->textBox_FPSLimit->Location = System::Drawing::Point(64, 3); + this->textBox_FPSLimit->MaxLength = 3; + this->textBox_FPSLimit->Name = L"textBox_FPSLimit"; + this->textBox_FPSLimit->Size = System::Drawing::Size(79, 20); + this->textBox_FPSLimit->TabIndex = 111; + this->textBox_FPSLimit->TextChanged += gcnew System::EventHandler(this, &ui::TextBox_FPSLimit_TextChanged); + this->textBox_FPSLimit->KeyPress += gcnew System::Windows::Forms::KeyPressEventHandler(this, &ui::TextBox_FPSLimit_KeyPress); + // + // label_FPSLimit + // + this->label_FPSLimit->Anchor = System::Windows::Forms::AnchorStyles::Left; + this->label_FPSLimit->AutoSize = true; + this->label_FPSLimit->FlatStyle = System::Windows::Forms::FlatStyle::Flat; + this->label_FPSLimit->Location = System::Drawing::Point(3, 7); + this->label_FPSLimit->Name = L"label_FPSLimit"; + this->label_FPSLimit->Size = System::Drawing::Size(54, 13); + this->label_FPSLimit->TabIndex = 110; + this->label_FPSLimit->Text = L"FPS Limit:"; + this->label_FPSLimit->TextAlign = System::Drawing::ContentAlignment::MiddleLeft; + // + // checkBox_TAA + // + this->checkBox_TAA->AutoSize = true; + this->checkBox_TAA->FlatStyle = System::Windows::Forms::FlatStyle::Flat; + this->checkBox_TAA->Location = System::Drawing::Point(3, 26); + this->checkBox_TAA->Name = L"checkBox_TAA"; + this->checkBox_TAA->Size = System::Drawing::Size(44, 17); + this->checkBox_TAA->TabIndex = 31; + this->checkBox_TAA->Text = L"TAA"; + // + // tableLayoutPanel_Status + // + this->tableLayoutPanel_Status->ColumnCount = 2; + this->tableLayoutPanel_Status->ColumnStyles->Add((gcnew System::Windows::Forms::ColumnStyle(System::Windows::Forms::SizeType::Percent, + 42.85714F))); + this->tableLayoutPanel_Status->ColumnStyles->Add((gcnew System::Windows::Forms::ColumnStyle(System::Windows::Forms::SizeType::Percent, + 57.14286F))); + this->tableLayoutPanel_Status->Controls->Add(this->label_StatusIcons, 0, 0); + this->tableLayoutPanel_Status->Controls->Add(this->comboBox_StatusIcons, 1, 0); + this->tableLayoutPanel_Status->Location = System::Drawing::Point(2, 249); + this->tableLayoutPanel_Status->Margin = System::Windows::Forms::Padding(2); + this->tableLayoutPanel_Status->Name = L"tableLayoutPanel_Status"; + this->tableLayoutPanel_Status->RowCount = 1; + this->tableLayoutPanel_Status->RowStyles->Add((gcnew System::Windows::Forms::RowStyle(System::Windows::Forms::SizeType::Percent, + 50))); + this->tableLayoutPanel_Status->Size = System::Drawing::Size(147, 28); + this->tableLayoutPanel_Status->TabIndex = 113; + // + // label_StatusIcons + // + this->label_StatusIcons->Anchor = System::Windows::Forms::AnchorStyles::Left; + this->label_StatusIcons->AutoSize = true; + this->label_StatusIcons->FlatStyle = System::Windows::Forms::FlatStyle::Flat; + this->label_StatusIcons->Location = System::Drawing::Point(3, 1); + this->label_StatusIcons->Name = L"label_StatusIcons"; + this->label_StatusIcons->Size = System::Drawing::Size(40, 26); + this->label_StatusIcons->TabIndex = 80; + this->label_StatusIcons->Text = L"Status Icons:"; + this->label_StatusIcons->TextAlign = System::Drawing::ContentAlignment::MiddleLeft; + // + // comboBox_StatusIcons + // + this->comboBox_StatusIcons->DropDownStyle = System::Windows::Forms::ComboBoxStyle::DropDownList; + this->comboBox_StatusIcons->FlatStyle = System::Windows::Forms::FlatStyle::Flat; + this->comboBox_StatusIcons->FormattingEnabled = true; + this->comboBox_StatusIcons->Items->AddRange(gcnew cli::array< System::Object^ >(5) { + L"Default", L"Hidden", L"Error", L"OK", + L"Partial OK" + }); + this->comboBox_StatusIcons->Location = System::Drawing::Point(65, 3); + this->comboBox_StatusIcons->Name = L"comboBox_StatusIcons"; + this->comboBox_StatusIcons->Size = System::Drawing::Size(79, 21); + this->comboBox_StatusIcons->TabIndex = 101; + // + // checkBox_MLAA + // + this->checkBox_MLAA->AutoSize = true; + this->checkBox_MLAA->FlatStyle = System::Windows::Forms::FlatStyle::Flat; + this->checkBox_MLAA->Location = System::Drawing::Point(3, 49); + this->checkBox_MLAA->Name = L"checkBox_MLAA"; + this->checkBox_MLAA->Size = System::Drawing::Size(52, 17); + this->checkBox_MLAA->TabIndex = 41; + this->checkBox_MLAA->Text = L"MLAA"; + // + // checkBox_HideFreeplay + // + this->checkBox_HideFreeplay->AutoSize = true; + this->checkBox_HideFreeplay->FlatStyle = System::Windows::Forms::FlatStyle::Flat; + this->checkBox_HideFreeplay->Location = System::Drawing::Point(3, 72); + this->checkBox_HideFreeplay->Name = L"checkBox_HideFreeplay"; + this->checkBox_HideFreeplay->Size = System::Drawing::Size(101, 17); + this->checkBox_HideFreeplay->TabIndex = 51; + this->checkBox_HideFreeplay->Text = L"Hide Freeplay"; + // + // checkBox_HidePVWatermark + // + this->checkBox_HidePVWatermark->AutoSize = true; + this->checkBox_HidePVWatermark->FlatStyle = System::Windows::Forms::FlatStyle::Flat; + this->checkBox_HidePVWatermark->Location = System::Drawing::Point(3, 181); + this->checkBox_HidePVWatermark->Name = L"checkBox_HidePVWatermark"; + this->checkBox_HidePVWatermark->Size = System::Drawing::Size(117, 17); + this->checkBox_HidePVWatermark->TabIndex = 91; + this->checkBox_HidePVWatermark->Text = L"Hide PV Watermark"; + // + // checkBox_HideVolCtrl + // + this->checkBox_HideVolCtrl->AutoSize = true; + this->checkBox_HideVolCtrl->FlatStyle = System::Windows::Forms::FlatStyle::Flat; + this->checkBox_HideVolCtrl->Location = System::Drawing::Point(3, 95); + this->checkBox_HideVolCtrl->Name = L"checkBox_HideVolCtrl"; + this->checkBox_HideVolCtrl->Size = System::Drawing::Size(122, 17); + this->checkBox_HideVolCtrl->TabIndex = 61; + this->checkBox_HideVolCtrl->Text = L"Hide Volume Buttons"; + // + // checkBox_NoLyrics + // + this->checkBox_NoLyrics->AutoSize = true; + this->checkBox_NoLyrics->FlatStyle = System::Windows::Forms::FlatStyle::Flat; + this->checkBox_NoLyrics->Location = System::Drawing::Point(3, 159); + this->checkBox_NoLyrics->Margin = System::Windows::Forms::Padding(2); + this->checkBox_NoLyrics->Name = L"checkBox_NoLyrics"; + this->checkBox_NoLyrics->Size = System::Drawing::Size(88, 17); + this->checkBox_NoLyrics->TabIndex = 81; + this->checkBox_NoLyrics->Text = L"Disable Lyrics"; + // + // checkBox_NoPVUi + // + this->checkBox_NoPVUi->AutoSize = true; + this->checkBox_NoPVUi->FlatStyle = System::Windows::Forms::FlatStyle::Flat; + this->checkBox_NoPVUi->Location = System::Drawing::Point(3, 138); + this->checkBox_NoPVUi->Margin = System::Windows::Forms::Padding(2); + this->checkBox_NoPVUi->Name = L"checkBox_NoPVUi"; + this->checkBox_NoPVUi->Size = System::Drawing::Size(89, 17); + this->checkBox_NoPVUi->TabIndex = 71; + this->checkBox_NoPVUi->Text = L"Disable PV UI"; + // + // tabPage_Components + // + this->tabPage_Components->BackColor = System::Drawing::Color::FromArgb(static_cast(static_cast(64)), + static_cast(static_cast(64)), static_cast(static_cast(64))); + this->tabPage_Components->Controls->Add(this->panel_Components); + this->tabPage_Components->Location = System::Drawing::Point(4, 25); + this->tabPage_Components->Name = L"tabPage_Components"; + this->tabPage_Components->Padding = System::Windows::Forms::Padding(3); + this->tabPage_Components->Size = System::Drawing::Size(225, 232); + this->tabPage_Components->TabIndex = 2; + this->tabPage_Components->Text = L"Components"; + // + // panel_Components + // + this->panel_Components->AutoScroll = true; + this->panel_Components->Controls->Add(this->label_Soon); + this->panel_Components->Controls->Add(this->checkBox_fps_limiter); + this->panel_Components->Controls->Add(this->checkBox_scale_component); + this->panel_Components->Controls->Add(this->checkBox_camera_controller); + this->panel_Components->Controls->Add(this->checkBox_input_emulator); + this->panel_Components->Controls->Add(this->checkBox_fast_loader); + this->panel_Components->Controls->Add(this->checkBox_touch_slider_emulator); + this->panel_Components->Controls->Add(this->checkBox_stage_manager); + this->panel_Components->Controls->Add(this->checkBox_touch_panel_emulator); + this->panel_Components->Controls->Add(this->checkBox_frame_rate_manager); + this->panel_Components->Controls->Add(this->checkBox_sys_timer); + this->panel_Components->Controls->Add(this->checkBox_player_data_manager); + this->panel_Components->Location = System::Drawing::Point(0, 0); + this->panel_Components->Name = L"panel_Components"; + this->panel_Components->Size = System::Drawing::Size(225, 232); + this->panel_Components->TabIndex = 0; + // + // label_Soon + // + this->label_Soon->AutoSize = true; + this->label_Soon->FlatStyle = System::Windows::Forms::FlatStyle::Flat; + this->label_Soon->Location = System::Drawing::Point(3, 3); + this->label_Soon->Name = L"label_Soon"; + this->label_Soon->Size = System::Drawing::Size(143, 52); + this->label_Soon->TabIndex = 12; + this->label_Soon->Text = L"Options not yet available\r\nthrough the launcher.\r\nPlease use \"components.ini\"\r\nin" + L"side the \"plugins\" folder."; + // + // checkBox_fps_limiter + // + this->checkBox_fps_limiter->AutoSize = true; + this->checkBox_fps_limiter->FlatStyle = System::Windows::Forms::FlatStyle::Flat; + this->checkBox_fps_limiter->Location = System::Drawing::Point(6, 277); + this->checkBox_fps_limiter->Name = L"checkBox_fps_limiter"; + this->checkBox_fps_limiter->Size = System::Drawing::Size(76, 17); + this->checkBox_fps_limiter->TabIndex = 10; + this->checkBox_fps_limiter->Text = L"FPS Limiter"; + // + // checkBox_scale_component + // + this->checkBox_scale_component->AutoSize = true; + this->checkBox_scale_component->FlatStyle = System::Windows::Forms::FlatStyle::Flat; + this->checkBox_scale_component->Location = System::Drawing::Point(6, 253); + this->checkBox_scale_component->Name = L"checkBox_scale_component"; + this->checkBox_scale_component->Size = System::Drawing::Size(107, 17); + this->checkBox_scale_component->TabIndex = 9; + this->checkBox_scale_component->Text = L"Scale Component"; + // + // checkBox_camera_controller + // + this->checkBox_camera_controller->AutoSize = true; + this->checkBox_camera_controller->FlatStyle = System::Windows::Forms::FlatStyle::Flat; + this->checkBox_camera_controller->Location = System::Drawing::Point(6, 230); + this->checkBox_camera_controller->Name = L"checkBox_camera_controller"; + this->checkBox_camera_controller->Size = System::Drawing::Size(106, 17); + this->checkBox_camera_controller->TabIndex = 8; + this->checkBox_camera_controller->Text = L"Camera Controller"; + // + // checkBox_input_emulator + // + this->checkBox_input_emulator->AutoSize = true; + this->checkBox_input_emulator->FlatStyle = System::Windows::Forms::FlatStyle::Flat; + this->checkBox_input_emulator->Location = System::Drawing::Point(6, 35); + this->checkBox_input_emulator->Name = L"checkBox_input_emulator"; + this->checkBox_input_emulator->Size = System::Drawing::Size(91, 17); + this->checkBox_input_emulator->TabIndex = 0; + this->checkBox_input_emulator->Text = L"Input Emulator"; + // + // checkBox_fast_loader + // + this->checkBox_fast_loader->AutoSize = true; + this->checkBox_fast_loader->FlatStyle = System::Windows::Forms::FlatStyle::Flat; + this->checkBox_fast_loader->Location = System::Drawing::Point(6, 207); + this->checkBox_fast_loader->Name = L"checkBox_fast_loader"; + this->checkBox_fast_loader->Size = System::Drawing::Size(79, 17); + this->checkBox_fast_loader->TabIndex = 7; + this->checkBox_fast_loader->Text = L"Fast Loader"; + // + // checkBox_touch_slider_emulator + // + this->checkBox_touch_slider_emulator->AutoSize = true; + this->checkBox_touch_slider_emulator->FlatStyle = System::Windows::Forms::FlatStyle::Flat; + this->checkBox_touch_slider_emulator->Location = System::Drawing::Point(6, 69); + this->checkBox_touch_slider_emulator->Name = L"checkBox_touch_slider_emulator"; + this->checkBox_touch_slider_emulator->Size = System::Drawing::Size(128, 17); + this->checkBox_touch_slider_emulator->TabIndex = 1; + this->checkBox_touch_slider_emulator->Text = L"Touch Panel Emulator"; + // + // checkBox_stage_manager + // + this->checkBox_stage_manager->AutoSize = true; + this->checkBox_stage_manager->FlatStyle = System::Windows::Forms::FlatStyle::Flat; + this->checkBox_stage_manager->Location = System::Drawing::Point(6, 184); + this->checkBox_stage_manager->Name = L"checkBox_stage_manager"; + this->checkBox_stage_manager->Size = System::Drawing::Size(96, 17); + this->checkBox_stage_manager->TabIndex = 6; + this->checkBox_stage_manager->Text = L"Stage Manager"; + // + // checkBox_touch_panel_emulator + // + this->checkBox_touch_panel_emulator->AutoSize = true; + this->checkBox_touch_panel_emulator->FlatStyle = System::Windows::Forms::FlatStyle::Flat; + this->checkBox_touch_panel_emulator->Location = System::Drawing::Point(6, 92); + this->checkBox_touch_panel_emulator->Name = L"checkBox_touch_panel_emulator"; + this->checkBox_touch_panel_emulator->Size = System::Drawing::Size(127, 17); + this->checkBox_touch_panel_emulator->TabIndex = 2; + this->checkBox_touch_panel_emulator->Text = L"Touch Slider Emulator"; + // + // checkBox_frame_rate_manager + // + this->checkBox_frame_rate_manager->AutoSize = true; + this->checkBox_frame_rate_manager->FlatStyle = System::Windows::Forms::FlatStyle::Flat; + this->checkBox_frame_rate_manager->Location = System::Drawing::Point(6, 161); + this->checkBox_frame_rate_manager->Name = L"checkBox_frame_rate_manager"; + this->checkBox_frame_rate_manager->Size = System::Drawing::Size(123, 17); + this->checkBox_frame_rate_manager->TabIndex = 5; + this->checkBox_frame_rate_manager->Text = L"Frame Rate Manager"; + // + // checkBox_sys_timer + // + this->checkBox_sys_timer->AutoSize = true; + this->checkBox_sys_timer->FlatStyle = System::Windows::Forms::FlatStyle::Flat; + this->checkBox_sys_timer->Location = System::Drawing::Point(6, 115); + this->checkBox_sys_timer->Name = L"checkBox_sys_timer"; + this->checkBox_sys_timer->Size = System::Drawing::Size(117, 17); + this->checkBox_sys_timer->TabIndex = 3; + this->checkBox_sys_timer->Text = L"Freeze Select Timer"; + // + // checkBox_player_data_manager + // + this->checkBox_player_data_manager->AutoSize = true; + this->checkBox_player_data_manager->FlatStyle = System::Windows::Forms::FlatStyle::Flat; + this->checkBox_player_data_manager->Location = System::Drawing::Point(6, 138); + this->checkBox_player_data_manager->Name = L"checkBox_player_data_manager"; + this->checkBox_player_data_manager->Size = System::Drawing::Size(123, 17); + this->checkBox_player_data_manager->TabIndex = 4; + this->checkBox_player_data_manager->Text = L"Player Data Manager"; + // + // button_Discord + // + this->button_Discord->BackColor = System::Drawing::Color::Transparent; + this->button_Discord->FlatAppearance->BorderSize = 0; + this->button_Discord->FlatAppearance->MouseDownBackColor = System::Drawing::Color::Transparent; + this->button_Discord->FlatAppearance->MouseOverBackColor = System::Drawing::Color::Transparent; + this->button_Discord->FlatStyle = System::Windows::Forms::FlatStyle::Flat; + this->button_Discord->Image = (cli::safe_cast(resources->GetObject(L"button_Discord.Image"))); + this->button_Discord->Location = System::Drawing::Point(239, 12); + this->button_Discord->Name = L"button_Discord"; + this->button_Discord->Size = System::Drawing::Size(42, 42); + this->button_Discord->TabIndex = 31; + this->button_Discord->UseVisualStyleBackColor = false; + this->button_Discord->Click += gcnew System::EventHandler(this, &ui::button_Discord_Click); + // + // button_github + // + this->button_github->BackColor = System::Drawing::Color::Transparent; + this->button_github->FlatAppearance->BorderSize = 0; + this->button_github->FlatAppearance->MouseDownBackColor = System::Drawing::Color::Transparent; + this->button_github->FlatAppearance->MouseOverBackColor = System::Drawing::Color::Transparent; + this->button_github->FlatStyle = System::Windows::Forms::FlatStyle::Flat; + this->button_github->Image = (cli::safe_cast(resources->GetObject(L"button_github.Image"))); + this->button_github->Location = System::Drawing::Point(239, 60); + this->button_github->Name = L"button_github"; + this->button_github->Size = System::Drawing::Size(42, 42); + this->button_github->TabIndex = 32; + this->button_github->UseVisualStyleBackColor = false; + this->button_github->Click += gcnew System::EventHandler(this, &ui::button_github_Click); + // + // ui + // + this->AcceptButton = this->button_Launch; + this->AutoScaleDimensions = System::Drawing::SizeF(96, 96); + this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Dpi; + this->AutoSize = true; + this->AutoSizeMode = System::Windows::Forms::AutoSizeMode::GrowAndShrink; + this->BackColor = System::Drawing::Color::FromArgb(static_cast(static_cast(64)), static_cast(static_cast(64)), + static_cast(static_cast(64))); + this->BackgroundImage = (cli::safe_cast(resources->GetObject(L"$this.BackgroundImage"))); + this->BackgroundImageLayout = System::Windows::Forms::ImageLayout::Stretch; + this->CancelButton = this->button_Exit; + this->ClientSize = System::Drawing::Size(288, 297); + this->Controls->Add(this->button_github); + this->Controls->Add(this->button_Discord); + this->Controls->Add(this->tabControl); + this->Controls->Add(this->button_Exit); + this->Controls->Add(this->button_Launch); + this->DoubleBuffered = true; + this->ForeColor = System::Drawing::Color::White; + this->FormBorderStyle = System::Windows::Forms::FormBorderStyle::FixedDialog; + this->MaximizeBox = false; + this->MinimizeBox = false; + this->Name = L"ui"; + this->StartPosition = System::Windows::Forms::FormStartPosition::CenterScreen; + this->Text = L"PD Launcher"; + this->FormClosing += gcnew System::Windows::Forms::FormClosingEventHandler(this, &ui::Ui_FormClosing); + this->FormClosed += gcnew System::Windows::Forms::FormClosedEventHandler(this, &ui::Ui_FormClosed); + this->Load += gcnew System::EventHandler(this, &ui::Ui_Load); + this->groupBox_ScreenRes->ResumeLayout(false); + this->tableLayoutPanel_ScreenRes->ResumeLayout(false); + this->tableLayoutPanel_ScreenRes->PerformLayout(); + this->tabControl->ResumeLayout(false); + this->tabPage_Resolution->ResumeLayout(false); + this->groupBox_InternalRes->ResumeLayout(false); + this->tableLayoutPanel_IntRes->ResumeLayout(false); + this->tableLayoutPanel_IntRes->PerformLayout(); + this->tabPage_Patches->ResumeLayout(false); + this->panel_Patches->ResumeLayout(false); + this->panel_innerPatches->ResumeLayout(false); + this->panel_innerPatches->PerformLayout(); + this->tableLayoutPanel_FPSLimit->ResumeLayout(false); + this->tableLayoutPanel_FPSLimit->PerformLayout(); + this->tableLayoutPanel_Status->ResumeLayout(false); + this->tableLayoutPanel_Status->PerformLayout(); + this->tabPage_Components->ResumeLayout(false); + this->panel_Components->ResumeLayout(false); + this->panel_Components->PerformLayout(); + this->ResumeLayout(false); + + } +#pragma endregion +private: System::Void SaveSettings() { + String^ userInput = Convert::ToInt32(comboBox_Display->SelectedIndex).ToString(); + wstring input = msclr::interop::marshal_as(userInput); + WritePrivateProfileStringW(L"resolution", L"display", input.c_str(), CONFIG_FILE); + + userInput = comboBox_Resolution->Text; + cli::array^ ResolutionArray = userInput->Split('x'); + userInput = ResolutionArray[0]; + input = msclr::interop::marshal_as(userInput); + WritePrivateProfileStringW(L"resolution", L"width", input.c_str(), CONFIG_FILE); + + userInput = comboBox_Resolution->Text; + ResolutionArray = userInput->Split('x'); + userInput = ResolutionArray[1]; + input = msclr::interop::marshal_as(userInput); + WritePrivateProfileStringW(L"resolution", L"height", input.c_str(), CONFIG_FILE); + + userInput = Convert::ToInt32(checkBox_InternalRes->Checked).ToString(); + input = msclr::interop::marshal_as(userInput); + WritePrivateProfileStringW(L"resolution", L"r.enable", input.c_str(), CONFIG_FILE); + + userInput = comboBox_intResolution->Text; + cli::array^ intResolutionArray = userInput->Split('x'); + userInput = intResolutionArray[0]; + input = msclr::interop::marshal_as(userInput); + WritePrivateProfileStringW(L"resolution", L"r.width", input.c_str(), CONFIG_FILE); + + userInput = comboBox_BitDepth->Text; + input = msclr::interop::marshal_as(userInput); + WritePrivateProfileStringW(L"resolution", L"bitdepth", input.c_str(), CONFIG_FILE); + + userInput = comboBox_RefreshRate->Text; + input = msclr::interop::marshal_as(userInput); + WritePrivateProfileStringW(L"resolution", L"refreshrate", input.c_str(), CONFIG_FILE); + + userInput = comboBox_intResolution->Text; + intResolutionArray = userInput->Split('x'); + userInput = intResolutionArray[1]; + input = msclr::interop::marshal_as(userInput); + WritePrivateProfileStringW(L"resolution", L"r.height", input.c_str(), CONFIG_FILE); + + userInput = Convert::ToInt32(checkBox_Cursor->Checked).ToString(); + input = msclr::interop::marshal_as(userInput); + WritePrivateProfileStringW(L"patches", L"cursor", input.c_str(), CONFIG_FILE); + + userInput = Convert::ToInt32(checkBox_HideFreeplay->Checked).ToString(); + input = msclr::interop::marshal_as(userInput); + WritePrivateProfileStringW(L"patches", L"hide_freeplay", input.c_str(), CONFIG_FILE); + + userInput = Convert::ToInt32(comboBox_StatusIcons->SelectedIndex).ToString(); + input = msclr::interop::marshal_as(userInput); + WritePrivateProfileStringW(L"patches", L"status_icons", input.c_str(), CONFIG_FILE); + + userInput = Convert::ToInt32(checkBox_HidePVWatermark->Checked).ToString(); + input = msclr::interop::marshal_as(userInput); + WritePrivateProfileStringW(L"patches", L"hide_pv_watermark", input.c_str(), CONFIG_FILE); + + userInput = Convert::ToInt32(checkBox_NoPVUi->Checked).ToString(); + input = msclr::interop::marshal_as(userInput); + WritePrivateProfileStringW(L"patches", L"no_pv_ui", input.c_str(), CONFIG_FILE); + + userInput = Convert::ToInt32(checkBox_NoLyrics->Checked).ToString(); + input = msclr::interop::marshal_as(userInput); + WritePrivateProfileStringW(L"patches", L"no_lyrics", input.c_str(), CONFIG_FILE); + + userInput = Convert::ToInt32(checkBox_NoMovies->Checked).ToString(); + input = msclr::interop::marshal_as(userInput); + WritePrivateProfileStringW(L"patches", L"no_movies", input.c_str(), CONFIG_FILE); + + userInput = Convert::ToInt32(checkBox_HideVolCtrl->Checked).ToString(); + input = msclr::interop::marshal_as(userInput); + WritePrivateProfileStringW(L"patches", L"hide_volume", input.c_str(), CONFIG_FILE); + + userInput = Convert::ToInt32(checkBox_DisableErrorBanner->Checked).ToString(); + input = msclr::interop::marshal_as(userInput); + WritePrivateProfileStringW(L"patches", L"no_error", input.c_str(), CONFIG_FILE); + + userInput = Convert::ToInt32(checkBox_TAA->Checked).ToString(); + input = msclr::interop::marshal_as(userInput); + WritePrivateProfileStringW(L"graphics", L"TAA", input.c_str(), CONFIG_FILE); + + userInput = Convert::ToInt32(checkBox_MLAA->Checked).ToString(); + input = msclr::interop::marshal_as(userInput); + WritePrivateProfileStringW(L"graphics", L"MLAA", input.c_str(), CONFIG_FILE); + + userInput = textBox_FPSLimit->Text; + input = msclr::interop::marshal_as(userInput); + WritePrivateProfileStringW(L"graphics", L"FPS.Limit", input.c_str(), CONFIG_FILE); + + userInput = Convert::ToInt32(checkBox_SkipLauncher->Checked).ToString(); + input = msclr::interop::marshal_as(userInput); + WritePrivateProfileStringW(L"launcher", L"skip", input.c_str(), CONFIG_FILE); +} +private: System::Void Ui_Load(System::Object^ sender, System::EventArgs^ e){ +} +private: System::Void Button_Exit_Click(System::Object^ sender, System::EventArgs^ e) { + this->Close(); +} +private: System::Void Button_Launch_Click(System::Object^ sender, System::EventArgs^ e) { + + SaveSettings(); + + STARTUPINFOW si; + PROCESS_INFORMATION pi; + + ZeroMemory(&si, sizeof(si)); + si.cb = sizeof(si); + ZeroMemory(&pi, sizeof(pi)); + CreateProcessW(DIVA_EXECUTABLE, DIVA_EXECUTABLE_LAUNCH, NULL, NULL, false, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi); + + // this->Close won't work in here since it will prompt the user to save the settings + TerminateProcess(GetCurrentProcess(), EXIT_SUCCESS); +} +private: System::Void CheckBox_InternalRes_CheckedChanged(System::Object^ sender, System::EventArgs^ e) { + if (checkBox_InternalRes->Checked) + { + comboBox_intResolution->Enabled = true; + } + else + { + comboBox_intResolution->Enabled = false; + } +} +private: System::Void ComboBox_Display_SelectedIndexChanged(System::Object^ sender, System::EventArgs^ e) { + if (comboBox_Display->SelectedIndex == 0 || comboBox_Display->SelectedIndex == 2) + { + comboBox_Resolution->Enabled = true; + } + else + { + comboBox_Resolution->Enabled = false; + } + if (comboBox_Display->SelectedIndex == 2) + { + comboBox_BitDepth->Enabled = true; + comboBox_RefreshRate->Enabled = true; + } + else + { + comboBox_BitDepth->Enabled = false; + comboBox_RefreshRate->Enabled = false; + } +} +private: System::Void Ui_FormClosing(System::Object^ sender, System::Windows::Forms::FormClosingEventArgs^ e) { + switch (MessageBox::Show("Do you want to save your settings?", "PD Launcher", MessageBoxButtons::YesNoCancel, MessageBoxIcon::Question)) + { + case System::Windows::Forms::DialogResult::Yes: + SaveSettings(); + break; + + case System::Windows::Forms::DialogResult::No: + break; + + case System::Windows::Forms::DialogResult::Cancel: + e->Cancel = true; + break; + } +} +private: System::Void Ui_FormClosed(System::Object^ sender, System::Windows::Forms::FormClosedEventArgs^ e) { + // Prevents abnormal termination messages, remember that the game is still technically running and must be killed! + TerminateProcess(GetCurrentProcess(), EXIT_SUCCESS); +} +private: System::Void button_Discord_Click(System::Object^ sender, System::EventArgs^ e) { + System::Diagnostics::Process::Start("https://discord.gg/cvBVGDZ"); +} +private: System::Void TextBox_FPSLimit_TextChanged(System::Object^ sender, System::EventArgs^ e) { + if (System::Text::RegularExpressions::Regex::IsMatch(textBox_FPSLimit->Text, " ^ [0-9]")) + { + textBox_FPSLimit->Text = ""; + } +} +private: System::Void TextBox_FPSLimit_KeyPress(System::Object^ sender, System::Windows::Forms::KeyPressEventArgs^ e) { + if (!Char::IsControl(e->KeyChar) && !Char::IsDigit(e->KeyChar) && (e->KeyChar != '.')) + { + e->Handled = true; + } +} +private: System::Void button_github_Click(System::Object^ sender, System::EventArgs^ e) { + System::Diagnostics::Process::Start("https://notabug.org/nastys/PD-Loader"); +} +}; +} diff --git a/source/plugins/Launcher/ui.resx b/source/plugins/Launcher/ui.resx new file mode 100644 index 0000000..641132d --- /dev/null +++ b/source/plugins/Launcher/ui.resx @@ -0,0 +1,4415 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + + iVBORw0KGgoAAAANSUhEUgAAACoAAAApCAYAAABDV7v1AAAABGdBTUEAALGPC/xhBQAAA7dJREFUWEft + mElME2EUxwdDgh6NVw8e9OzZGwcv3ryZePKmUQodREAN1Ah0BmQTVJZgQFABE1kEl7iAEQWjAiIEOtMW + aJGyFWgpbWG6PL+vnSmFDrL0KyHaf/LPtPNm3vtN37dMSkUVlYwuqMYOJmRz52g1n0UzfF4knKTmmERG + p1CotEfFsjsTTU8colmuKTmXh70wzfL6S6qxY2L57SsxZ0SBEng2JoyklSzXp1Bo40SE7Qm1xSaXLOJm + uZMiwtaKj++MlU2yB1aqtadEjK0VBd2G/2/QtAItFFSPQ2HNOKQX+s9dzeMh5Y7/c2o+D8W141BWb4Rb + 93Xr7t3MxEFLHxugd8gCtS2T0PJhBj73LsCwfhkMJicYp5y+Y9+wFd53m6Gh3QSD/BJ0fJ2HG0Va2XyS + iYLWtZpgbn4FBjkrCIIHtpLb7QXNKH4IB3oYm2xOyURBdQY7rKxuDbhRHo8XvF4vlKBuyOXFJgaaXa4X + y+5eliXBN5bl8hMDHUDtDleCywO5VWOy+YmAXkcTYWPLcSttdhcsLbt8rQ2W3ekCq83lA8PXScKf2z7O + huTHJgJaXGsAFyoqCRcc0FjRcBiFzFIdfPqxIEYA5i0C5FePwc1iLTS8nl53H1YXWiXkahABbe+cC/ll + 1BVrLax89luMANQ0TwbOq+7pYcEqiBG/8CogxYNNBPTtF7NYxi8MipcqKf7w+RpoT/9i4PztB6Ngd7jE + iF+rgjsQDzYR0K6g1mJhUDz+Wjtm4enLKVhddYsRf6y7f9Fb0zLpnTE713VCUlpBaA0ioD0DFrEEoEnl + BtOMM2QCbSbHitu34OPFX1JGSei2SgT0TdfaGPWg4y/eCnUvTGgDWN4UGK8SPT8XoR5to0vLQuB+86IA + 19C7wMYaREBTUas6vy2AxeYviD1jXoHiR+OQiSZMeeMENL6agqZ30/CkzQR364yQjl5c8Nh2OP3DwuX2 + gH7CDiV18rsTEVDJORWj0IpeREbQvv0dvZjgtyi56yQX1RhAb7T7JlhZg9G3ZMldh00UNJKOgpL2vwlK + UaoDNMs75RKFYzqX96K8NnyUi2PvEJSiaDVXKJcoXCsZTaWS5Zo3g90x6Pl0w2Ga5Yb+9vS7Mc1q6uNV + nbFKhm+Qy71jUKyLWboTKGFGMstXo6S1RMxyVTj3FfXIkSRGU4bOCWGDBimGooCgA4pRMhyLAAN/xoUL + GjGdUWjj0JhNQZNsfl+DSkpQc2fpXM6x70GxFAx3+jIzdFz8GlVUFEVRfwDiv42+8CmffAAAAABJRU5E + rkJggg== + + + + + iVBORw0KGgoAAAANSUhEUgAAACoAAAAqCAYAAADFw8lbAAAABGdBTUEAALGPC/xhBQAABKdJREFUWEft + mF1IZGUYx/3WcKXygxVFUboxMEQNRSI/UhGEAqEC77ILg6JoLu3ORL0Q68JYEbxRb1Zc0L00xMBQoUX8 + KkNGKcHByHT9WMk1d97+/9fjzpnjMzPnzM7UQv3hdzHjeZ7nP6/ved6PmP/1nCkJvALeAG+DD8DHoB28 + B94CxeAl8K/oFvgCrIE98AhcxsbGqmvw2Qv+BH8ANxgBr4OoKxaUgK/BI7MpuyDuAkyDd0AiiIo6wUPg + lUw4ATk40j+AQhAxvQq+Bc9s0Apy/gLeBQngmZQDfgURN3kNcnM6uEDYKgceKXmkQR1OBZp1PLLx4GcQ + tZG0glqPAaeBbaWC+1KyaIO6nLO2XzA26gspkZnExEQVHx8v/s1KXFycfj4hIUH8uxnUfgBCtq4U8KOU + wEx2drba3NxU09PTqq2tTZtISkpSeXl5qqioSJOTk6O/r6ioUENDQ2pra0sNDw+HNIv654B9NqhcUrCV + pqYmZdb29rY6PT01Pvl0fn6uLi8vjU9Kzc3NqZSUFDGnGfhgOwworsc/SYFW6urqjNLONDMzo5KTk8Wc + ZuDjLxBwua0Cp1KglfLycqO0M62tramMjAwxpxV4GdWuBH0KQraj3Nxctbu7a5R2rtHRUTGvFXjZAi/T + mFX3pAAr7e3tRsnwlZ+fL+Y2Az8H4DXtzCK3FGClp6fHKBe+GhoaxNxm4Idvf712ZlIcCNk7SW9vr1Eu + fDU3N4u5zcAP97Ps6X5Klx6WcLlcRrnwVVVVJea2Al8fXdnz6bb0oERNTY1RLjyxr2ZlZYm5rcDXJ1f2 + fLolPSjBPriwsGCUda6RkRExrwR8fXhlz18n0sMSbFErKytGaftiTGZmppjTCvw8AeJS+kAKsHJdiOt9 + V1eXOjo6MmwE1vHxserr69PrvzVfIOCHB0aebm/ojhRghrslbiympqaeGk5LS1P19fVqfn7esOXT7Oys + qq6uVqmpqTdyhQJ+fgM8it8QW8ETKcgMNxU0MDAw8PS7wsJCdXBwYNjzaX9/XxUUFPjF2wVe1gHvDW6I + h7h9KchKcXGxNra6uqq3ejs7O+ri4kLvjvjdxsaG3oB4vV5VWVkp5ggFvPDeQBSPH3NSkAS3euvr63p7 + x5Hb29vTo7y0tKThCnZ2dqanhRQfDPjg/OQlR0BVg8dScDBo2uPx+Bnt7u5WJycnjo2iPlekr2gmlO5K + CYLR2NiojQ4ODqrFxUW1vLysjR4eHqra2loxJhCofwR4IxNSPCY/lJJI8GjR2dmpe+TY2JianJzU87O/ + v1/P1Y6ODn1mkmKtoC5H80vA6yNb+hyE7ACE//bx8XFVUlKi3G63amlpUa2trfoF45o+MTGhl10p1gzq + 0STvpRzrG2DLLGFfLSsr0yPMfltaWqrS09PFZyVQawew8zjWC+A74HedGGmQnyPpAbw+Cls062hknYLc + vJHhexERcc7afsHsgHy8HLsP2L8jKv7qu8DWSSAYyMHl8X3A66Oo6U3wPfgd2JoSeI7z8ARsAN7a8Ubm + HxHPWHxDuZG5A3hfdGwxx38t7+7vgc8A7w1eBM+FuD7fBjyP88f81xQT8zfAJCInz8zAaAAAAABJRU5E + rkJggg== + + + + + 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 + + + \ No newline at end of file diff --git a/source/plugins/Patches/Patches.vcxproj b/source/plugins/Patches/Patches.vcxproj new file mode 100644 index 0000000..578fd8a --- /dev/null +++ b/source/plugins/Patches/Patches.vcxproj @@ -0,0 +1,172 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + {3FD6ACA9-E613-4FD6-BDA2-55A91C2CF65C} + Win32Proj + Patches + 10.0 + + + + DynamicLibrary + true + v142 + Unicode + + + DynamicLibrary + false + v142 + true + Unicode + + + DynamicLibrary + true + v142 + Unicode + + + DynamicLibrary + false + v142 + true + Unicode + + + + + + + + + + + + + + + + + + + + + false + .dva + + + true + + + true + .dva + + + false + + + + NotUsing + Level3 + MaxSpeed + true + true + true + NDEBUG;PATCHES_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + pch.h + stdcpp17 + ..\..\..\dependencies\freeglut\include;%(AdditionalIncludeDirectories) + + + Windows + true + true + true + false + ..\..\..\dependencies\freeglut\lib;%(AdditionalLibraryDirectories) + + + + + Use + Level3 + Disabled + true + WIN32;_DEBUG;PATCHES_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + pch.h + + + Windows + true + false + + + + + NotUsing + Level3 + Disabled + true + _DEBUG;PATCHES_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + + + ..\..\..\dependencies\freeglut\include;%(AdditionalIncludeDirectories) + stdcpp17 + + + Windows + true + false + ..\..\..\dependencies\freeglut\lib;%(AdditionalLibraryDirectories) + + + + + Use + Level3 + MaxSpeed + true + true + true + WIN32;NDEBUG;PATCHES_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + pch.h + + + Windows + true + true + true + false + + + + + + + + + \ No newline at end of file diff --git a/source/plugins/Patches/Patches.vcxproj.filters b/source/plugins/Patches/Patches.vcxproj.filters new file mode 100644 index 0000000..1f5bfee --- /dev/null +++ b/source/plugins/Patches/Patches.vcxproj.filters @@ -0,0 +1,22 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;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 + + + \ No newline at end of file diff --git a/source/plugins/Patches/dllmain.cpp b/source/plugins/Patches/dllmain.cpp new file mode 100644 index 0000000..fe8aaa8 --- /dev/null +++ b/source/plugins/Patches/dllmain.cpp @@ -0,0 +1,211 @@ +#include "windows.h" +#include "vector" +#include +#include + +void InjectCode(void* address, const std::vector data); +void ApplyPatches(); + +const LPCWSTR CONFIG_FILE = L".\\config.ini"; + +BOOL APIENTRY DllMain(HMODULE hModule, + DWORD ul_reason_for_call, + LPVOID lpReserved +) +{ + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + ApplyPatches(); + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + break; + } + return TRUE; +} + +void ApplyPatches() { + + const struct { void* Address; std::vector Data; } patches[] = + { + // Always return true for the SelCredit enter SelPv check + { (void*)0x0000000140393610, { 0xB0, 0x01, 0xC3, 0x90, 0x90, 0x90 } }, + // Just completely ignore all SYSTEM_STARTUP errors + { (void*)0x00000001403F5080, { 0xC3 } }, + // Always exit TASK_MODE_APP_ERROR on the first frame + { (void*)0x00000001403F73A7, { 0x90, 0x90 } }, + { (void*)0x00000001403F73C3, { 0x89, 0xD1, 0x90 } }, + // Ignore the EngineClear variable to clear the framebuffer at all resolutions + { (void*)0x0000000140501480, { 0x90, 0x90 } }, + { (void*)0x0000000140501515, { 0x90, 0x90 } }, + // Don't update the touch slider state so we can write our own + { (void*)0x000000014061579B, { 0x90, 0x90, 0x90, 0x8B, 0x42, 0xE0, 0x90, 0x90, 0x90 } }, + // Write ram files to the current directory instead of Y : / SBZV / ram + { (void*)0x000000014066CF09, { 0xE9, 0xD8, 0x00 } }, + // Change mdata path from "C:/Mount/Option" to "mdata/" + { (void*)0x0000000140A8CA18, { 0x6D, 0x64, 0x61, 0x74, 0x61, 0x2F, 0x00 } }, + { (void*)0x000000014066CEAE, { 0x06 } }, + // Skip parts of the network check state + { (void*)0x00000001406717B1, { 0xE9, 0x22, 0x03, 0x00 } }, + // Set the initial DHCP WAIT timer value to 0 + { (void*)0x00000001406724E7, { 0x00, 0x00 } }, + // Ignore SYSTEM_STARTUP Location Server checks + { (void*)0x00000001406732A2, { 0x90, 0x90 } }, + // Toon Shader Fix by lybxlpsv + { (void*)0x000000014050214F, { 0x90 } }, + { (void*)0x0000000140502150, { 0x90 } }, + // Toon Shader Outline Fix by lybxlpsv + { (void*)0x0000000140641102, { 0x01 } }, + // Disables call to glutFitWindowSizeToDesktop, prevents window automatic resize + { (void*)0x0000000140194E06, { 0x90, 0x90, 0x90, 0x90, 0x90 } }, + // Allow modifier mode selection (by Team Shimapan) + { (void*)0x00000001405CB1B3, { 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90 } }, + { (void*)0x00000001405CA0F5, { 0x90, 0x90 } }, + // allow modifier modes to work without use_card + { (void*)0x00000001405CB14A,{ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90 } }, + { (void*)0x0000000140136CFA,{ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90 } }, + // enable module selector without use_card (Modules are not getting saved upon selection, commented until a fix is found) + //{ (void*)0x00000001405C513B, { 0x01 } }, + // Show Freeplay instead + { (void*)0x00000001403BABEA, { 0x75 } }, + // Force Hide IDs + { (void*)0x00000001409A5918, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } }, + { (void*)0x00000001409A5928, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } + }; + printf("[Patches] Patches loaded\n"); + + for (size_t i = 0; i < _countof(patches); i++) + InjectCode(patches[i].Address, patches[i].Data); + + auto nCursor = GetPrivateProfileIntW(L"patches", L"cursor", TRUE, CONFIG_FILE); + auto nHideFreeplay = GetPrivateProfileIntW(L"patches", L"hide_freeplay", FALSE, CONFIG_FILE); + auto nStatusIcons = GetPrivateProfileIntW(L"patches", L"status_icons", 0, CONFIG_FILE); + auto nHidePVWatermark = GetPrivateProfileIntW(L"patches", L"hide_pv_watermark", FALSE, CONFIG_FILE); + auto nNoPVUi = GetPrivateProfileIntW(L"patches", L"no_pv_ui", FALSE, CONFIG_FILE); + auto nHideVolCtrl = GetPrivateProfileIntW(L"patches", L"hide_volume", FALSE, CONFIG_FILE); + auto nNoLyrics = GetPrivateProfileIntW(L"patches", L"no_lyrics", FALSE, CONFIG_FILE); + auto nNoMovies = GetPrivateProfileIntW(L"patches", L"no_movies", FALSE, CONFIG_FILE); + auto nNoError = GetPrivateProfileIntW(L"patches", L"no_error", FALSE, CONFIG_FILE); + // Hides the Freeplay text + if (nHideFreeplay) + { + InjectCode((void*)0x00000001403BABEF, { 0x06, 0xB6 }); + printf("[Patches] Hide Freeplay enabled\n"); + } + // Use GLUT_CURSOR_RIGHT_ARROW instead of GLUT_CURSOR_NONE + if (nCursor) + { + InjectCode((void*)0x000000014019341B, { 0x00 }); + printf("[Patches] Cursor enabled\n"); + } + // Override status icon states to be invalid (hides them) + if (nStatusIcons > 0) + { + std::vector cardIcon = { 0xFD, 0x0A }; + std::vector networkIcon = { 0x9E, 0x1E }; + + if (nStatusIcons == 2) // 2 for error icons + { + cardIcon = { 0xFA, 0x0A }; + networkIcon = { 0x9F, 0x1E }; + printf("[Patches] Status icons set to error state\n"); + } + else if (nStatusIcons == 3) // 3 for OK icons + { + cardIcon = { 0xFC, 0x0A }; + networkIcon = { 0xA0, 0x1E }; + printf("[Patches] Status icons set to OK state\n"); + } + else if (nStatusIcons == 4) // 4 for partial OK icons + { + cardIcon = { 0xFB, 0x0A }; + networkIcon = { 0xA1, 0x1E }; + printf("[Patches] Status icons set to partial OK state\n"); + } + else // 1 or invalid for hidden + { + cardIcon = { 0xFD, 0x0A }; + networkIcon = { 0x9E, 0x1E }; + printf("[Patches] Status icons hidden\n"); + } + + // card icon + InjectCode((void*)0x00000001403B9D6E, cardIcon); // error state + InjectCode((void*)0x00000001403B9D73, cardIcon); // OK state + + // network icon + InjectCode((void*)0x00000001403BA14B, networkIcon); // error state + InjectCode((void*)0x00000001403BA155, networkIcon); // OK state + InjectCode((void*)0x00000001403BA16B, networkIcon); // partial state + + InjectCode((void*)0x00000001403BA1A5, { 0x48, 0xE9 }); // never show the error code for partial connection + + // I was going to use this with a string, but the assignment wasn't behaving well and making separate prints was easier than figuring it out + // printf("[Patches] Status icons %s\n", iconType); + } + // Removes PV watermark + if (nHidePVWatermark) + { + InjectCode((void*)0x0000000140A13A88, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }); + printf("[Patches] PV watermark hidden\n"); + } + // Disable the PV screen photo UI + if (nNoPVUi) + { + InjectCode((void*)0x000000014048FA91, { 0xEB, 0x6F }); // skip button panel image (JMP 0x14048FB02) + + // patch minimum PV UI state to 1 instead of 0 + // hook check for lyrics enabled (UI state < 2) to change UI state 0 into 1 + // dump new code in the skipped button panel condition + InjectCode((void*)0x000000014048FA93, { 0xC7, 0x83, 0x58, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 }); // MOV dword ptr [0x158 + RBX],0x1 + InjectCode((void*)0x000000014048FA9D, { 0xC6, 0x80, 0x3A, 0xD1, 0x02, 0x00, 0x01 }); // MOV byte ptr [0x2d13a + RAX],0x1 + InjectCode((void*)0x000000014048FAA4, { 0xE9, 0x8B, 0xFB, 0xFF, 0xFF }); // JMP 0x14048F634 + + InjectCode((void*)0x000000014048F62D, { 0xE9, 0x61, 0x04, 0x00, 0x00 }); // JMP 0x14048FA93 + + printf("[Patches] PV UI disabled\n"); + } + // Don't show volume control + if (nHideVolCtrl) + { + // skip SE button + InjectCode((void*)0x00000001409A4D60, { 0xC0, 0xD3 }); + + // skip volume sliders button + InjectCode((void*)0x0000000140A85F10, { 0xE0, 0x50 }); + + printf("[Patches] Volume control hidden\n"); + } + // Skip loading (and therefore displaying) song lyrics + if (nNoLyrics) + { + InjectCode((void*)0x00000001404E7A25, { 0x00, 0x00 }); + InjectCode((void*)0x00000001404E7950, { 0x48, 0xE9 }); // ensure first iteration doesn't run + printf("[Patches] Lyrics disabled\n"); + } + // Skip loading (and therefore displaying) song movies + if (nNoMovies) + { + InjectCode((void*)0x00000001404EB584, { 0x48, 0xE9 }); + InjectCode((void*)0x00000001404EB471, { 0x48, 0xE9 }); + printf("[Patches] Movies disabled\n"); + } + // Disable error banner + if (nNoError) + { + // Disable Errors Banner + InjectCode((void*)0x00000001403B9E9B, { 0x90, 0x90 }); + printf("[Patches] Errors Banner disabled\n"); + } +} + +void InjectCode(void* address, const std::vector data) +{ + const size_t byteCount = data.size() * sizeof(uint8_t); + + DWORD oldProtect; + VirtualProtect(address, byteCount, PAGE_EXECUTE_READWRITE, &oldProtect); + memcpy(address, data.data(), byteCount); + VirtualProtect(address, byteCount, oldProtect, nullptr); +} diff --git a/source/plugins/Render/Render.vcxproj b/source/plugins/Render/Render.vcxproj new file mode 100644 index 0000000..32edb89 --- /dev/null +++ b/source/plugins/Render/Render.vcxproj @@ -0,0 +1,176 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + {89F87459-768F-4638-9267-0F90CD74452D} + Win32Proj + Render + 10.0 + + + + DynamicLibrary + true + v142 + Unicode + + + DynamicLibrary + false + v142 + true + Unicode + + + DynamicLibrary + true + v142 + Unicode + + + DynamicLibrary + false + v142 + true + Unicode + + + + + + + + + + + + + + + + + + + + + false + .dva + + + true + + + true + .dva + + + false + + + + NotUsing + Level3 + MaxSpeed + true + true + true + NDEBUG;RENDER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + + + ..\..\..\dependencies\freeglut\include;..\..\..\dependencies\detours\include;%(AdditionalIncludeDirectories) + stdcpp17 + + + Windows + true + true + true + false + ..\..\..\dependencies\detours\lib;..\..\..\dependencies\freeglut\lib;%(AdditionalLibraryDirectories) + + + + + Use + Level3 + Disabled + true + WIN32;_DEBUG;RENDER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + pch.h + + + Windows + true + false + + + + + NotUsing + Level3 + Disabled + true + _DEBUG;RENDER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + + + stdcpp17 + ..\..\..\dependencies\freeglut\include;..\..\..\dependencies\detours\include;%(AdditionalIncludeDirectories) + + + Windows + true + false + ..\..\..\dependencies\detours\lib;..\..\..\dependencies\freeglut\lib;%(AdditionalLibraryDirectories) + + + + + Use + Level3 + MaxSpeed + true + true + true + WIN32;NDEBUG;RENDER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + pch.h + + + Windows + true + true + true + false + + + + + + + + + + + + \ No newline at end of file diff --git a/source/plugins/Render/Render.vcxproj.filters b/source/plugins/Render/Render.vcxproj.filters new file mode 100644 index 0000000..94f2bbe --- /dev/null +++ b/source/plugins/Render/Render.vcxproj.filters @@ -0,0 +1,27 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;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 + + + + + Header Files + + + + + Source Files + + + \ No newline at end of file diff --git a/source/plugins/Render/dllmain.cpp b/source/plugins/Render/dllmain.cpp new file mode 100644 index 0000000..e135be3 --- /dev/null +++ b/source/plugins/Render/dllmain.cpp @@ -0,0 +1,80 @@ +#include "framework.h" +#include +#pragma comment(lib, "detours.lib") +#include +#include +#include +#include + +int hookedCreateWindow(const char* title, void(__cdecl* exit_function)(int)) +{ + if (nDisplay == 1) + { + *fullScreenFlag = 0; + glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH | GLUT_BORDERLESS); + glutInitWindowSize(glutGet(GLUT_SCREEN_WIDTH), glutGet(GLUT_SCREEN_HEIGHT)); + glutInitWindowPosition(0, 0); + glutCreateWindow(title); + printf("[Render Manager] Borderless mode.\n"); + } + else if (nDisplay == 2) + { + // Crashes, no idea why yet + if (glutGameModeGet(GLUT_GAME_MODE_POSSIBLE)) { + char GameModeString[24]; + sprintf_s(GameModeString, sizeof(GameModeString),"%dx%d:%d@%d", nWidth, nHeight, nBitDepth ,nRefreshRate); + printf("[Render Manager] Game Mode supported and enabled in replacement of Fullscreen.\n"); + printf(GameModeString); + printf("\n"); + glutGameModeString(GameModeString); + glutEnterGameMode(); + } + else { + *fullScreenFlag = 1; + glutCreateWindow(title); + printf("[Render Manager] Fullscreen enabled.\n"); + } + } + else + { + *fullScreenFlag = 0; + glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH); + glutInitWindowSize(nWidth, nHeight); + glutInitWindowPosition((glutGet(GLUT_SCREEN_WIDTH) - nWidth) / 2, (glutGet(GLUT_SCREEN_HEIGHT) - nHeight) / 2); // Center to the middle of the screen when windowed + glutCreateWindow(title); + printf("[Render Manager] Windowed mode.\n"); + } + return true; +} + +__int64 hookedParseParameters(int a1, __int64* a2) +{ + // Force -wqhd if Custom Internal Resolution is enabled + if (nIntRes) + *resolutionType = 15; + // Return to the original function + return divaParseParameters(a1, a2); +} + + +BOOL APIENTRY DllMain(HMODULE hModule, + DWORD ul_reason_for_call, + LPVOID lpReserved +) +{ + if (ul_reason_for_call == DLL_PROCESS_ATTACH) + { + DisableThreadLibraryCalls(hModule); + DetourTransactionBegin(); + DetourUpdateThread(GetCurrentThread()); + DetourAttach(&(PVOID&)divaCreateWindow, hookedCreateWindow); + DetourTransactionCommit(); + + DetourTransactionBegin(); + DetourUpdateThread(GetCurrentThread()); + DetourAttach(&(PVOID&)divaParseParameters, hookedParseParameters); + DetourTransactionCommit(); + } + return TRUE; +} + diff --git a/source/plugins/Render/framework.h b/source/plugins/Render/framework.h new file mode 100644 index 0000000..42237fd --- /dev/null +++ b/source/plugins/Render/framework.h @@ -0,0 +1,36 @@ +#pragma once + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +// Windows Header Files +#include +#include + +static int(__cdecl* divaCreateWindow)(const char* title, void(__cdecl* exitfunc)(int)) = (int(__cdecl*)(const char* title, void(__cdecl * exitfunc)(int)))0x140194D00; +__int64 (__fastcall* divaParseParameters)(int a1, __int64* a2) = (__int64(__fastcall*)(int a1, __int64* a2))0x140193630; + +uint8_t* fullScreenFlag = (uint8_t*)0x140EDA5D1; +DWORD* resolutionType = (DWORD*)0x140EDA5D4; + +using namespace std; + +wstring DirPath() { + WCHAR buffer[MAX_PATH]; + GetModuleFileNameW(NULL, buffer, MAX_PATH); + wstring::size_type pos = wstring(buffer).find_last_of(L"\\/"); + return wstring(buffer).substr(0, pos); +} + +wstring CONFIG_FILE_STRING = DirPath() + L"\\plugins\\config.ini"; +LPCWSTR CONFIG_FILE = CONFIG_FILE_STRING.c_str(); + +int nDisplay = GetPrivateProfileIntW(L"resolution", L"display", 0, CONFIG_FILE); +int nWidth = GetPrivateProfileIntW(L"resolution", L"width", 1280, CONFIG_FILE); +int nHeight = GetPrivateProfileIntW(L"resolution", L"height", 720, CONFIG_FILE); + +int nIntRes = GetPrivateProfileIntW(L"resolution", L"r.enable", FALSE, CONFIG_FILE); +int nIntResWidth = GetPrivateProfileIntW(L"resolution", L"r.width", 1280, CONFIG_FILE); +int nIntResHeight = GetPrivateProfileIntW(L"resolution", L"r.height", 720, CONFIG_FILE); + +int nBitDepth = GetPrivateProfileIntW(L"resolution", L"bitdepth", 32, CONFIG_FILE); +int nRefreshRate = GetPrivateProfileIntW(L"resolution", L"refreshrate", 60, CONFIG_FILE); + diff --git a/source/plugins/TLAC/Components/CameraController.cpp b/source/plugins/TLAC/Components/CameraController.cpp new file mode 100644 index 0000000..3ef3b70 --- /dev/null +++ b/source/plugins/TLAC/Components/CameraController.cpp @@ -0,0 +1,231 @@ +#include "CameraController.h" +#include +#include "Input/InputState.h" +#include "ComponentsManager.h" +#include "../framework.h" +#include "../Input/Mouse/Mouse.h" +#include "../Input/Keyboard/Keyboard.h" +#include "../Input/Bindings/KeyboardBinding.h" + +#define GLUT_CURSOR_RIGHT_ARROW 0x0000 +#define GLUT_CURSOR_NONE 0x0065 + +using namespace TLAC::Input; +using namespace TLAC::Utilities; + +namespace TLAC::Components +{ + CameraController::CameraController() + { + } + + CameraController::~CameraController() + { + delete ToggleBinding; + + delete ForwardBinding; + delete BackwardBinding; + delete LeftBinding; + delete RightBinding; + + delete UpBinding; + delete DownBinding; + delete ClockwiseBinding; + delete CounterClockwiseBinding; + delete ZoomInBinding; + delete ZoomOutBinding; + + delete FastBinding; + delete SlowBinding; + } + + const char* CameraController::GetDisplayName() + { + return "camera_controller"; + } + + void CameraController::Initialize(ComponentsManager* manager) + { + componentsManager = manager; + + printf("[TLAC] CameraController::Initialize(): Initialized\n"); + + for (int i = 0; i < sizeof(cameraSetterAddresses) / sizeof(void*); i++) + { + DWORD oldProtect; + VirtualProtect((void*)cameraSetterAddresses[i], sizeof(uint8_t), PAGE_EXECUTE_READWRITE, &oldProtect); + + originalSetterBytes[i] = *(uint8_t*)cameraSetterAddresses[i]; + } + + ToggleBinding = new Binding(); + ToggleBinding->AddBinding(new KeyboardBinding(VK_F3)); + + ForwardBinding = new Binding(); + ForwardBinding->AddBinding(new KeyboardBinding('W')); + BackwardBinding = new Binding(); + BackwardBinding->AddBinding(new KeyboardBinding('S')); + LeftBinding = new Binding(); + LeftBinding->AddBinding(new KeyboardBinding('A')); + RightBinding = new Binding(); + RightBinding->AddBinding(new KeyboardBinding('D')); + + UpBinding = new Binding(); + UpBinding->AddBinding(new KeyboardBinding(VK_SPACE)); + DownBinding = new Binding(); + DownBinding->AddBinding(new KeyboardBinding(VK_CONTROL)); + + ClockwiseBinding = new Binding(); + ClockwiseBinding->AddBinding(new KeyboardBinding('E')); + CounterClockwiseBinding = new Binding(); + CounterClockwiseBinding->AddBinding(new KeyboardBinding('Q')); + + ZoomInBinding = new Binding(); + ZoomInBinding->AddBinding(new KeyboardBinding('R')); + ZoomOutBinding = new Binding(); + ZoomOutBinding->AddBinding(new KeyboardBinding('F')); + + FastBinding = new Binding(); + FastBinding->AddBinding(new KeyboardBinding(VK_SHIFT)); + SlowBinding = new Binding(); + SlowBinding->AddBinding(new KeyboardBinding(VK_MENU)); + + camera = (Camera*)CAMERA_ADDRESS; + } + + void CameraController::Update() + { + return; + } + + void CameraController::UpdateInput() + { + if (ToggleBinding->AnyTapped()) + { + SetControls(!GetIsEnabled()); + return; + } + + if (!GetIsEnabled()) + return; + + auto keyboard = Keyboard::GetInstance(); + auto mouse = Mouse::GetInstance(); + + bool forward = ForwardBinding->AnyDown(); + bool backward = BackwardBinding->AnyDown(); + bool left = LeftBinding->AnyDown(); + bool right = RightBinding->AnyDown(); + + bool up = UpBinding->AnyDown(); + bool down = DownBinding->AnyDown(); + + bool fast = FastBinding->AnyDown(); + bool slow = SlowBinding->AnyDown(); + + bool clockwise = ClockwiseBinding->AnyDown(); + bool counterclockwise = CounterClockwiseBinding->AnyDown(); + + bool zoomin = ZoomInBinding->AnyDown(); + bool zoomout = ZoomOutBinding->AnyDown(); + + float speed = GetElapsedTime() * (fast ? fastSpeed : slow ? slowSpeed : normalSpeed); + + if (forward ^ backward) + camera->Position += PointFromAngle(verticalRotation + (forward ? +0.0f : -180.0f), speed); + + if (left ^ right) + camera->Position += PointFromAngle(verticalRotation + (right ? +90.0f : -90.0f), speed); + + if (up ^ down) + camera->Position.Y += speed * (up ? +0.25f : -0.25f); + + if (clockwise ^ counterclockwise) + camera->Rotation += speed * (clockwise ? -1.0f : +1.0f); + + if (zoomin ^ zoomout) + { + camera->HorizontalFov += speed * (zoomin ? -1.0f : +1.0f); + camera->HorizontalFov = std::clamp(camera->HorizontalFov, +1.0f, +170.0f); + } + + if (mouse->HasMoved()) + { + SetMouseWindowCenter(); + + auto delta = mouse->GetDeltaPosition(); + + verticalRotation += delta.x * sensitivity; + horizontalRotation -= delta.y * (sensitivity / 5.0f); + + horizontalRotation = std::clamp(horizontalRotation, -75.0f, +75.0f); + } + + ((InputState*)*(uint64_t*)INPUT_STATE_PTR_ADDRESS)->HideCursor(); + + Vec2 focus = PointFromAngle(verticalRotation, 1.0f); + camera->Focus.X = camera->Position.X + focus.X; + camera->Focus.Z = camera->Position.Z + focus.Y; + + camera->Focus.Y = camera->Position.Y + PointFromAngle(horizontalRotation, 5.0f).X; + } + + void CameraController::SetControls(bool value) + { + if (GetIsEnabled() == value) + return; + + SetIsEnabled(value); + componentsManager->SetUpdateGameInput(!value); + + printf("[TLAC] CameraController::SetControls(): enabled = %s\n", GetIsEnabled() ? "true" : "false"); + + typedef void __stdcall _glutSetCursor(int); + auto glutSetCursor = (_glutSetCursor*)GLUT_SET_CURSOR_ADDRESS; + + // hide cursor + glutSetCursor(value ? GLUT_CURSOR_NONE : GLUT_CURSOR_RIGHT_ARROW); + + if (value) + { + // disable camera setters + for (int i = 0; i < sizeof(cameraSetterAddresses) / sizeof(void*); i++) + *(uint8_t*)cameraSetterAddresses[i] = RET_OPCODE; + + // set initial camera angle + Vec2 camXz = Vec2(camera->Position.X, camera->Position.Z); + Vec2 focusXz = Vec2(camera->Focus.X, camera->Focus.Z); + verticalRotation = AngleFromPoints(camXz, focusXz); + + horizontalRotation = 0; + camera->Rotation = defaultRotation; + camera->HorizontalFov = defaultFov; + } + else + { + // restore camera setters + for (int i = 0; i < sizeof(cameraSetterAddresses) / sizeof(void*); i++) + *(uint8_t*)cameraSetterAddresses[i] = originalSetterBytes[i]; + } + } + + void CameraController::SetMouseWindowCenter() + { + RECT windowRect = framework::GetWindowBounds(); + + int centerX = windowRect.left + (windowRect.right - windowRect.left) / 2; + int centerY = windowRect.top + (windowRect.bottom - windowRect.top) / 2; + + Mouse::GetInstance()->SetPosition(centerX, centerY); + } + + bool CameraController::GetIsEnabled() + { + return isEnabled; + } + + void CameraController::SetIsEnabled(bool value) + { + isEnabled = value; + } +} \ No newline at end of file diff --git a/source/plugins/TLAC/Components/CameraController.h b/source/plugins/TLAC/Components/CameraController.h new file mode 100644 index 0000000..0d5cfbc --- /dev/null +++ b/source/plugins/TLAC/Components/CameraController.h @@ -0,0 +1,80 @@ +#pragma once +#include "EmulatorComponent.h" +#include "../Constants.h" +#include "../Input/Bindings/Binding.h" +#include "../Utilities/Math.h" + +namespace TLAC::Components +{ + struct Camera + { + Utilities::Vec3 Position; + Utilities::Vec3 Focus; + float Rotation; + float HorizontalFov; + float VerticalFov; + }; + + class CameraController : public EmulatorComponent + { + public: + Input::Binding* ToggleBinding; + + Input::Binding* ForwardBinding; + Input::Binding* BackwardBinding; + Input::Binding* LeftBinding; + Input::Binding* RightBinding; + + Input::Binding* UpBinding; + Input::Binding* DownBinding; + Input::Binding* FastBinding; + Input::Binding* SlowBinding; + + Input::Binding* ClockwiseBinding; + Input::Binding* CounterClockwiseBinding; + + Input::Binding* ZoomInBinding; + Input::Binding* ZoomOutBinding; + + CameraController(); + ~CameraController(); + + virtual const char* GetDisplayName() override; + + virtual void Initialize(ComponentsManager*) override; + virtual void Update() override; + virtual void UpdateInput() override; + + void SetControls(bool value); + + bool GetIsEnabled(); + + private: + const float fastSpeed = 0.1f; + const float slowSpeed = 0.0005f; + const float normalSpeed = 0.005f; + + const float defaultRotation = 0.0f; + const float defaultFov = 70.0f; + const float sensitivity = 0.25f; + + ComponentsManager* componentsManager; + float verticalRotation; + float horizontalRotation; + + bool isEnabled; + Camera* camera; + + uint8_t originalSetterBytes[4]; + void* cameraSetterAddresses[4] = + { + (void*)CAMERA_POS_SETTER_ADDRESS, + (void*)CAMERA_INTR_SETTER_ADDRESS, + (void*)CAMERA_ROT_SETTER_ADDRESS, + (void*)CAMERA_PERS_SETTER_ADDRESS, + }; + + void SetMouseWindowCenter(); + void SetIsEnabled(bool value); + }; +} diff --git a/source/plugins/TLAC/Components/ComponentsManager.cpp b/source/plugins/TLAC/Components/ComponentsManager.cpp new file mode 100644 index 0000000..1ad90b3 --- /dev/null +++ b/source/plugins/TLAC/Components/ComponentsManager.cpp @@ -0,0 +1,155 @@ +#include "ComponentsManager.h" +#include "../FileSystem/ConfigFile.h" +#include "../framework.h" +#include "Input/InputEmulator.h" +#include "Input/TouchSliderEmulator.h" +#include "Input/TouchPanelEmulator.h" +#include "SysTimer.h" +#include "PlayerDataManager.h" +#include "FrameRateManager.h" +#include "FastLoader.h" +#include "StageManager.h" +#include "CameraController.h" +#include "DebugComponent.h" +#include "ScaleComponent.h" +#include "FPSLimiter.h" +#include "GameTargets/TargetInspector.h" + +using ConfigFile = TLAC::FileSystem::ConfigFile; + +namespace TLAC::Components +{ + typedef void EngineUpdateInput(void*); + + ComponentsManager::ComponentsManager() + { + } + + ComponentsManager::~ComponentsManager() + { + } + + void ComponentsManager::ParseAddComponents() + { + EmulatorComponent* allComponents[] + { + new TargetInspector(), + new InputEmulator(), + new TouchSliderEmulator(), + new TouchPanelEmulator(), + new SysTimer(), + new PlayerDataManager(), + new FrameRateManager(), + new FastLoader(), + new ScaleComponent(), + new FPSLimiter(), + new StageManager(), + new CameraController(), + new DebugComponent(), + }; + + ConfigFile componentsConfig(framework::GetModuleDirectory(), COMPONENTS_CONFIG_FILE_NAME); + bool success = componentsConfig.OpenRead(); + + if (!success) + { + printf("ComponentsManager::ParseAddComponents(): Unable to parse %s\n", COMPONENTS_CONFIG_FILE_NAME.c_str()); + return; + } + + size_t componentCount = sizeof(allComponents) / sizeof(EmulatorComponent*); + components.reserve(componentCount); + + std::string trueString = "true", falseString = "false"; + + for (int i = 0; i < componentCount; i++) + { + std::string* value; + + auto name = allComponents[i]->GetDisplayName(); + //printf("ComponentsManager::ParseAddComponents(): searching name: %s\n", name); + + if (componentsConfig.TryGetValue(name, &value)) + { + //printf("ComponentsManager::ParseAddComponents(): %s found\n", name); + + if (*value == trueString) + { + //printf("ComponentsManager::ParseAddComponents(): enabling %s...\n", name); + components.push_back(allComponents[i]); + } + else if (*value == falseString) + { + //printf("ComponentsManager::ParseAddComponents(): disabling %s...\n", name); + } + else + { + //printf("ComponentsManager::ParseAddComponents(): invalid value %s for component %s\n", value, name); + } + + delete value; + } + else + { + //printf("ParseAddComponents(): component %s not found\n", name); + delete allComponents[i]; + } + } + } + + + void ComponentsManager::Initialize() + { + dwGuiDisplay = (DwGuiDisplay*) * (uint64_t*)DW_GUI_DISPLAY_INSTANCE_PTR_ADDRESS; + + ParseAddComponents(); + updateStopwatch.Start(); + + for (auto& component : components) + component->Initialize(this); + } + + void ComponentsManager::Update() + { + elpasedTime = updateStopwatch.Restart(); + + for (auto& component : components) + { + component->SetElapsedTime(elpasedTime); + component->Update(); + } + } + + void ComponentsManager::UpdateInput() + { + if (!GetIsInputEmulatorUsed()) + { + uint64_t* inputStatePtr = (uint64_t*)INPUT_STATE_PTR_ADDRESS; + + // poll input using the original PollInput function we overwrote with the update hook instead + if (inputStatePtr != nullptr) + ((EngineUpdateInput*)ENGINE_UPDATE_INPUT_ADDRESS)((void*)* inputStatePtr); + } + + for (auto& component : components) + component->UpdateInput(); + } + + void ComponentsManager::OnFocusGain() + { + for (auto& component : components) + component->OnFocusGain(); + } + + void ComponentsManager::OnFocusLost() + { + for (auto& component : components) + component->OnFocusLost(); + } + + void ComponentsManager::Dispose() + { + for (auto& component : components) + delete component; + } +} \ No newline at end of file diff --git a/source/plugins/TLAC/Components/ComponentsManager.h b/source/plugins/TLAC/Components/ComponentsManager.h new file mode 100644 index 0000000..0c20c0e --- /dev/null +++ b/source/plugins/TLAC/Components/ComponentsManager.h @@ -0,0 +1,55 @@ +#pragma once +#include "EmulatorComponent.h" +#include "../Utilities/Stopwatch.h" +#include +#include + +namespace TLAC::Components +{ + const std::string COMPONENTS_CONFIG_FILE_NAME = "components.ini"; + + // Incomplete type + struct DwGuiDisplay + { + void* vftable; + void* active; + void* cap; + void* on; + void* move; + void* widget; + }; + + class ComponentsManager + { + public: + ComponentsManager(); + ~ComponentsManager(); + void Initialize(); + void Update(); + void UpdateInput(); + void OnFocusGain(); + void OnFocusLost(); + void Dispose(); + + inline bool GetIsInputEmulatorUsed() { return isInputEmulatorUsed; }; + inline void SetIsInputEmulatorUsed(bool value) { isInputEmulatorUsed = value; }; + + inline bool GetUpdateGameInput() { return updateGameInput; }; + inline void SetUpdateGameInput(bool value) { updateGameInput = value; } + + inline bool IsDwGuiActive() { return dwGuiDisplay->active != nullptr; }; + inline bool IsDwGuiHovered() { return dwGuiDisplay->on != nullptr; }; + + private: + DwGuiDisplay* dwGuiDisplay; + + bool isInputEmulatorUsed = false; + bool updateGameInput = true; + + float elpasedTime; + Utilities::Stopwatch updateStopwatch; + std::vector components; + + void ParseAddComponents(); + }; +} \ No newline at end of file diff --git a/source/plugins/TLAC/Components/CustomPlayerData.h b/source/plugins/TLAC/Components/CustomPlayerData.h new file mode 100644 index 0000000..c8a156e --- /dev/null +++ b/source/plugins/TLAC/Components/CustomPlayerData.h @@ -0,0 +1,20 @@ +#pragma once +#include + +namespace TLAC::Components +{ + struct CustomPlayerData + { + std::string *PlayerName; + std::string* LevelName; + int LevelPlateId; + int SkinEquip; + int BtnSeEquip; + int SlideSeEquip; + int ChainslideSeEquip; + bool ShowGreatClearBorder; + bool ShowExcellentClearBorder; + bool UseCard; + bool GameModifierOptions; + }; +} diff --git a/source/plugins/TLAC/Components/DebugComponent.cpp b/source/plugins/TLAC/Components/DebugComponent.cpp new file mode 100644 index 0000000..1719596 --- /dev/null +++ b/source/plugins/TLAC/Components/DebugComponent.cpp @@ -0,0 +1,263 @@ +#include "DebugComponent.h" +#include "../Constants.h" + +namespace TLAC::Components +{ + const char* GameStateNames[] = + { + "STARTUP", + "ADVERTISE", + "GAME", + "DATA_TEST", + "TEST_MODE", + "APP_ERROR", + "MAX", + }; + + const char* SubGameStateNames[] = + { + "DATA_INITIALIZE", + "SYSTEM_STARTUP", + "SYSTEM_STARTUP_ERROR", + "WARNING", + "LOGO", + "RATING", + "DEMO", + "TITLE", + "RANKING", + "SCORE_RANKING", + "CM", + "PHOTO_MODE_DEMO", + "SELECTOR", + "GAME_MAIN", + "GAME_SEL", + "STAGE_RESULT", + "SCREEN_SHOT_SEL", + "SCREEN_SHOT_RESULT", + "GAME_OVER", + "DATA_TEST_MAIN", + "DATA_TEST_MISC", + "DATA_TEST_OBJ", + "DATA_TEST_STG", + "DATA_TEST_MOT", + "DATA_TEST_COLLISION", + "DATA_TEST_SPR", + "DATA_TEST_AET", + "DATA_TEST_AUTH_3D", + "DATA_TEST_CHR", + "DATA_TEST_ITEM", + "DATA_TEST_PERF", + "DATA_TEST_PVSCRIPT", + "DATA_TEST_PRINT", + "DATA_TEST_CARD", + "DATA_TEST_OPD", + "DATA_TEST_SLIDER", + "DATA_TEST_GLITTER", + "DATA_TEST_GRAPHICS", + "DATA_TEST_COLLECTION_CARD", + "TEST_MODE_MAIN", + "APP_ERROR", + "MAX", + }; + + const char* DataTestNames[] = + { + "MAIN TEST", + "MISC TEST", + "OBJECT TEST", + "STAGE TEST", + "MOTION TEST", + "COLLISION TEST", + "SPRITE TEST", + "2DAUTH TEST", + "3DAUTH TEST", + "CHARA TEST", + "ITEM TEST", + "PERFORMANCE TEST", + "PVSCRIPT TEST", + "PRINT TEST", + "CARD TEST", + "OPD TEST", + "SLIDER TEST", + "GLITTER TEST", + "GRAPHICS TEST", + "COLLECTION CARD TEST", + }; + + typedef void ChangeGameState(GameState); + ChangeGameState* changeGameState = (ChangeGameState*)CHANGE_MODE_ADDRESS; + + typedef void ChangeSubState(GameState, SubGameState); + ChangeSubState* changeSubState = (ChangeSubState*)CHANGE_SUB_MODE_ADDRESS; + + DebugComponent::DebugComponent() + { + } + + DebugComponent::~DebugComponent() + { + } + + const char* DebugComponent::GetDisplayName() + { + return "debug_component"; + } + + void DebugComponent::Initialize(ComponentsManager*) + { + printf("[TLAC] DebugComponent::Initialize(): Initialized\n"); + + InjectPatches(); + + HWND consoleHandle = GetConsoleWindow(); + ShowWindow(consoleHandle, SW_SHOW); + + // In case the FrameRateManager isn't enabled + DWORD oldProtect; + VirtualProtect((void*)AET_FRAME_DURATION_ADDRESS, sizeof(float), PAGE_EXECUTE_READWRITE, &oldProtect); + } + + void DebugComponent::Update() + { + if (dataTestMain) + { + Input::Keyboard::GetInstance()->PollInput(); + UpdateDataTestMain(); + } + } + + void DebugComponent::UpdateInput() + { + auto keyboard = Input::Keyboard::GetInstance(); + + // fast forward menus + if (keyboard->IsDown(VK_SHIFT)) + { + float* frameDuration = (float*)AET_FRAME_DURATION_ADDRESS; + + if (keyboard->IsDown(VK_TAB)) + *frameDuration = 1.0f / (GetGameFrameRate() / aetSpeedUpFactor); + else if (keyboard->IsReleased(VK_TAB)) + *frameDuration = 1.0f / 60.0f; + } + + for (size_t i = 0; i < _countof(gameStateKeyMappings); i++) + { + if (keyboard->IsTapped(gameStateKeyMappings[i].KeyCode)) + InternalChangeGameState(gameStateKeyMappings[i].State); + } + } + + void DebugComponent::InjectPatches() + { + const struct { void* Address; std::initializer_list Data; } patches[] = + { + // Prevent the DATA_TEST game state from exiting on the first frame + { (void*)0x0000000140284B01, { 0x00 } }, + // Enable dw_gui sprite draw calls + { (void*)0x0000000140192601, { 0x00 } }, + // Update the dw_gui display + { (void*)0x0000000140302600, { 0xB0, 0x01 } }, + // Draw the dw_gui display + { (void*)0x0000000140302610, { 0xB0, 0x01 } }, + // Enable the dw_gui widgets + { (void*)0x0000000140192D00, { 0xB8, 0x01, 0x00, 0x00, 0x00, 0xC3 } }, + }; + + for (size_t i = 0; i < _countof(patches); i++) + InjectCode(patches[i].Address, patches[i].Data); + } + + void DebugComponent::SetConsoleForeground() + { + HWND consoleHandle = GetConsoleWindow(); + ShowWindow(consoleHandle, SW_SHOW); + + if (consoleHandle == NULL) + return; + + WINDOWPLACEMENT place = { sizeof(WINDOWPLACEMENT) }; + GetWindowPlacement(consoleHandle, &place); + + switch (place.showCmd) + { + case SW_SHOWMAXIMIZED: + ShowWindow(consoleHandle, SW_SHOWMAXIMIZED); + break; + case SW_SHOWMINIMIZED: + ShowWindow(consoleHandle, SW_RESTORE); + break; + default: + ShowWindow(consoleHandle, SW_NORMAL); + break; + } + + SetWindowPos(0, HWND_TOP, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE); + SetForegroundWindow(consoleHandle); + } + + void DebugComponent::PrintDataTestMain() + { + system("cls"); + printf(" DATA TEST MAIN:\n\n"); + + for (int i = SUB_DATA_TEST_MISC; i <= SUB_DATA_TEST_COLLECTION_CARD; i++) + printf("%s %s\n", i == selectionIndex ? "->" : " ", DataTestNames[i - SUB_DATA_TEST_MAIN]); + + printf("\n"); + SetConsoleForeground(); + } + + void DebugComponent::InternalChangeGameState(GameState state) + { + changeGameState(state); + printDataTestMain = dataTestMain = (state == GS_DATA_TEST); + } + + void DebugComponent::UpdateDataTestMain() + { + auto keyboard = Input::Keyboard::GetInstance(); + + if (keyboard->IsIntervalTapped(VK_UP)) + { + selectionIndex--; + printDataTestMain = true; + } + + if (keyboard->IsIntervalTapped(VK_DOWN)) + { + selectionIndex++; + printDataTestMain = true; + } + + if (selectionIndex > SUB_DATA_TEST_COLLECTION_CARD) + selectionIndex = SUB_DATA_TEST_MISC; + + if (selectionIndex < SUB_DATA_TEST_MISC) + selectionIndex = SUB_DATA_TEST_COLLECTION_CARD; + + if (keyboard->IsTapped(VK_RETURN)) + { + dataTestMain = false; + + printf("[%s] -> [%s]\n", SubGameStateNames[SUB_DATA_TEST_MAIN], SubGameStateNames[selectionIndex]); + changeSubState(GS_DATA_TEST, (SubGameState)selectionIndex); + } + + if (printDataTestMain) + { + PrintDataTestMain(); + printDataTestMain = false; + } + } + + void DebugComponent::InjectCode(void* address, const std::initializer_list &data) + { + const size_t byteCount = data.size() * sizeof(uint8_t); + + DWORD oldProtect; + VirtualProtect(address, byteCount, PAGE_EXECUTE_READWRITE, &oldProtect); + + memcpy(address, data.begin(), byteCount); + } +} diff --git a/source/plugins/TLAC/Components/DebugComponent.h b/source/plugins/TLAC/Components/DebugComponent.h new file mode 100644 index 0000000..480ee71 --- /dev/null +++ b/source/plugins/TLAC/Components/DebugComponent.h @@ -0,0 +1,44 @@ +#pragma once +#include "EmulatorComponent.h" +#include "GameState.h" +#include "../Input/Keyboard/Keyboard.h" + +namespace TLAC::Components +{ + class DebugComponent : public EmulatorComponent + { + public: + DebugComponent(); + ~DebugComponent(); + + virtual const char* GetDisplayName() override; + + virtual void Initialize(ComponentsManager*) override; + virtual void Update() override; + virtual void UpdateInput() override; + + private: + const float aetSpeedUpFactor = 4.0f; + + bool dataTestMain = false; + bool printDataTestMain = false; + int selectionIndex = SUB_DATA_TEST_MISC; + + const struct { BYTE KeyCode; GameState State; } gameStateKeyMappings[5] = + { + { VK_F4, GS_ADVERTISE }, + { VK_F5, GS_GAME }, + { VK_F6, GS_DATA_TEST }, + { VK_F7, GS_TEST_MODE }, + { VK_F8, GS_APP_ERROR }, + }; + + void InjectPatches(); + void SetConsoleForeground(); + void PrintDataTestMain(); + void InternalChangeGameState(GameState state); + void UpdateDataTestMain(); + void InjectCode(void* address, const std::initializer_list &data); + }; +} + diff --git a/source/plugins/TLAC/Components/EmulatorComponent.cpp b/source/plugins/TLAC/Components/EmulatorComponent.cpp new file mode 100644 index 0000000..8482ed9 --- /dev/null +++ b/source/plugins/TLAC/Components/EmulatorComponent.cpp @@ -0,0 +1,33 @@ +#include "EmulatorComponent.h" +#include "../Constants.h" + +namespace TLAC::Components +{ + EmulatorComponent::EmulatorComponent() + { + } + + EmulatorComponent::~EmulatorComponent() + { + } + + void EmulatorComponent::SetElapsedTime(float value) + { + elapsedTime = value; + } + + float EmulatorComponent::GetElapsedTime() + { + return elapsedTime == 0.0f ? (1000.0f / 60.0f) : elapsedTime; + } + + float EmulatorComponent::GetFrameRate() + { + return 1000.0f / GetElapsedTime(); + } + + float EmulatorComponent::GetGameFrameRate() + { + return *(float*)FRAME_RATE_ADDRESS; + } +} diff --git a/source/plugins/TLAC/Components/EmulatorComponent.h b/source/plugins/TLAC/Components/EmulatorComponent.h new file mode 100644 index 0000000..48b6175 --- /dev/null +++ b/source/plugins/TLAC/Components/EmulatorComponent.h @@ -0,0 +1,30 @@ +#pragma once + +namespace TLAC::Components +{ + class ComponentsManager; + + class EmulatorComponent + { + public: + EmulatorComponent(); + ~EmulatorComponent(); + + virtual const char* GetDisplayName() = 0; + + virtual void Initialize(ComponentsManager*) = 0; + virtual void Update() = 0; + + virtual void UpdateInput() {}; + virtual void OnFocusGain() {}; + virtual void OnFocusLost() {}; + + void SetElapsedTime(float value); + float GetElapsedTime(); + float GetFrameRate(); + float GetGameFrameRate(); + + private: + float elapsedTime; + }; +} diff --git a/source/plugins/TLAC/Components/FPSLimiter.cpp b/source/plugins/TLAC/Components/FPSLimiter.cpp new file mode 100644 index 0000000..54b4d1c --- /dev/null +++ b/source/plugins/TLAC/Components/FPSLimiter.cpp @@ -0,0 +1,67 @@ +#include "FPSLimiter.h" +#include +#include +#include +#include +#include +#include + +using dsec = std::chrono::duration; +using seconds = std::chrono::seconds; + +// this is pretty much just lybxlpsv's limiter, but removed from GLComponent + +namespace TLAC::Components +{ + using namespace std::chrono; + + static std::chrono::time_point mBeginFrame = system_clock::now(); + static std::chrono::time_point prevTimeInSeconds = time_point_cast(mBeginFrame); + + const LPCTSTR config_file_name = _T(".\\config.ini"); + int fpsLimit = GetPrivateProfileIntW(L"graphics", L"fps.limit", 0, config_file_name); + + FPSLimiter::FPSLimiter() + { + } + + FPSLimiter::~FPSLimiter() + { + } + + const char* FPSLimiter::GetDisplayName() + { + return "fps_limiter"; + } + + void FPSLimiter::Initialize(ComponentsManager* manager) + { + mBeginFrame = std::chrono::system_clock::now(); + prevTimeInSeconds = std::chrono::time_point_cast(mBeginFrame); + frameCountPerSecond = 0; + if(fpsLimit != 0) + printf("FPSLimiter fpsLimit: %d\n", fpsLimit); + } + + // I assume this gets called once per frame... if not this won't work + void FPSLimiter::Update() + { + auto invFpsLimit = round(dsec{ 1. / fpsLimit }); + auto mEndFrame = mBeginFrame + invFpsLimit; + auto timeInSeconds = std::chrono::time_point_cast(std::chrono::system_clock::now()); + + ++frameCountPerSecond; + if (timeInSeconds > prevTimeInSeconds) + { + //printf("FPSLimiter::Update(): FPS: %d\n", frameCountPerSecond); + frameCountPerSecond = 0; + prevTimeInSeconds = timeInSeconds; + } + + // This part keeps the frame rate. + if (fpsLimit > 19) // not sure why lyb used 19 here + std::this_thread::sleep_until(mEndFrame); + mBeginFrame = mEndFrame; + mEndFrame = mBeginFrame + invFpsLimit; + } +} \ No newline at end of file diff --git a/source/plugins/TLAC/Components/FPSLimiter.h b/source/plugins/TLAC/Components/FPSLimiter.h new file mode 100644 index 0000000..d975a53 --- /dev/null +++ b/source/plugins/TLAC/Components/FPSLimiter.h @@ -0,0 +1,23 @@ +#pragma once +#include "EmulatorComponent.h" +#include + +using seconds = std::chrono::seconds; + +namespace TLAC::Components +{ + class FPSLimiter : public EmulatorComponent + { + public: + FPSLimiter(); + ~FPSLimiter(); + + virtual const char* GetDisplayName() override; + + virtual void Initialize(ComponentsManager*) override; + virtual void Update() override; + + private: + int frameCountPerSecond; + }; +} \ No newline at end of file diff --git a/source/plugins/TLAC/Components/FastLoader.cpp b/source/plugins/TLAC/Components/FastLoader.cpp new file mode 100644 index 0000000..6832907 --- /dev/null +++ b/source/plugins/TLAC/Components/FastLoader.cpp @@ -0,0 +1,60 @@ +#include "FastLoader.h" +#include "../Constants.h" +#include + +namespace TLAC::Components +{ + FastLoader::FastLoader() + { + } + + FastLoader::~FastLoader() + { + } + + const char* FastLoader::GetDisplayName() + { + return "fast_loader"; + } + + void FastLoader::Initialize(ComponentsManager*) + { + } + + void FastLoader::Update() + { + if (dataInitialized) + return; + + previousGameState = currentGameState; + currentGameState = *(GameState*)CURRENT_GAME_STATE_ADDRESS; + + if (currentGameState == GS_STARTUP) + { + typedef void UpdateTask(); + UpdateTask* updateTask = (UpdateTask*)UPDATE_TASKS_ADDRESS; + + // speed up TaskSystemStartup + for (int i = 0; i < updatesPerFrame; i++) + updateTask(); + + constexpr int DATA_INITIALIZED = 3; + + // skip TaskDataInit + *(int*)(DATA_INIT_STATE_ADDRESS) = DATA_INITIALIZED; + + // skip TaskWarning + *(int*)(SYSTEM_WARNING_ELAPSED_ADDRESS) = 3939; + } + else if (previousGameState == GS_STARTUP) + { + dataInitialized = true; + printf("[TLAC] FastLoader::Update(): Data Initialized\n"); + } + } + + void FastLoader::UpdateInput() + { + return; + } +} diff --git a/source/plugins/TLAC/Components/FastLoader.h b/source/plugins/TLAC/Components/FastLoader.h new file mode 100644 index 0000000..1319cbc --- /dev/null +++ b/source/plugins/TLAC/Components/FastLoader.h @@ -0,0 +1,26 @@ +#pragma once +#include "EmulatorComponent.h" +#include "GameState.h" + +namespace TLAC::Components +{ + class FastLoader : public EmulatorComponent + { + public: + FastLoader(); + ~FastLoader(); + + virtual const char* GetDisplayName() override; + + virtual void Initialize(ComponentsManager*) override; + virtual void Update() override; + virtual void UpdateInput() override; + + private: + const int updatesPerFrame = 39; + + GameState currentGameState; + GameState previousGameState; + bool dataInitialized = false; + }; +} diff --git a/source/plugins/TLAC/Components/FrameRateManager.cpp b/source/plugins/TLAC/Components/FrameRateManager.cpp new file mode 100644 index 0000000..c902a2f --- /dev/null +++ b/source/plugins/TLAC/Components/FrameRateManager.cpp @@ -0,0 +1,86 @@ +#include "FrameRateManager.h" +#include "../Constants.h" +#include "GameState.h" +#include +#include +#include "../framework.h" + +namespace TLAC::Components +{ + FrameRateManager::FrameRateManager() + { + } + + FrameRateManager::~FrameRateManager() + { + } + + const char* FrameRateManager::GetDisplayName() + { + return "frame_rate_manager"; + } + + void FrameRateManager::Initialize(ComponentsManager*) + { + pvFrameRate = (float*)PV_FRAME_RATE_ADDRESS; + frameSpeed = (float*)FRAME_SPEED_ADDRESS; + aetFrameDuration = (float*)AET_FRAME_DURATION_ADDRESS; + + // The default is expected to be 1.0 / 60.0 + defaultAetFrameDuration = *aetFrameDuration; + + // This const variable is stored inside a data segment so we don't want to throw any access violations + DWORD oldProtect; + VirtualProtect((void*)AET_FRAME_DURATION_ADDRESS, sizeof(float), PAGE_EXECUTE_READWRITE, &oldProtect); + } + + void FrameRateManager::Update() + { + float frameRate = 0.0f; + frameRate = RoundFrameRate(GetGameFrameRate()); + + *aetFrameDuration = 1.0f / frameRate; + *pvFrameRate = frameRate; + + bool inGame = *(GameState*)CURRENT_GAME_STATE_ADDRESS == GS_GAME; + + if (inGame) + { + // During the GAME state the frame rate will be handled by the PvFrameRate instead + + constexpr float defaultFrameSpeed = 1.0f; + constexpr float defaultFrameRate = 60.0f; + + // This PV struct creates a copy of the PvFrameRate & PvFrameSpeed during the loading screen + // so we'll make sure to keep updating it as well. + // Each new motion also creates its own copy of these values but keeping track of the active motions is annoying + // and they usually change multiple times per PV anyway so this should suffice for now + float* pvStructPvFrameRate = (float*)(0x0000000140CDD978 + 0x2BF98); + float* pvStructPvFrameSpeed = (float*)(0x0000000140CDD978 + 0x2BF9C); + + *pvStructPvFrameRate = *pvFrameRate; + *pvStructPvFrameSpeed = (defaultFrameRate / *pvFrameRate); + + *frameSpeed = defaultFrameSpeed; + } + else + { + *frameSpeed = *aetFrameDuration / defaultAetFrameDuration; + } + } + + float FrameRateManager::RoundFrameRate(float frameRate) + { + constexpr float roundingThreshold = 4.0f; + + for (int i = 0; i < sizeof(commonRefreshRates) / sizeof(float); i++) + { + float refreshRate = commonRefreshRates[i]; + + if (frameRate > refreshRate - roundingThreshold && frameRate < refreshRate + roundingThreshold) + frameRate = refreshRate; + } + + return frameRate; + } +} diff --git a/source/plugins/TLAC/Components/FrameRateManager.h b/source/plugins/TLAC/Components/FrameRateManager.h new file mode 100644 index 0000000..f6a6600 --- /dev/null +++ b/source/plugins/TLAC/Components/FrameRateManager.h @@ -0,0 +1,34 @@ +#pragma once +#include "EmulatorComponent.h" + +namespace TLAC::Components +{ + class FrameRateManager : public EmulatorComponent + { + public: + FrameRateManager(); + ~FrameRateManager(); + + virtual const char* GetDisplayName() override; + + virtual void Initialize(ComponentsManager*) override; + virtual void Update() override; + + private: + float *pvFrameRate; + float *frameSpeed; + float *aetFrameDuration; + float defaultAetFrameDuration; + + float commonRefreshRates[5] + { + 60.0f, + 75.0f, + 120.0f, + 144.0f, + 240.0f, + }; + + float RoundFrameRate(float frameRate); + }; +} diff --git a/source/plugins/TLAC/Components/GameState.h b/source/plugins/TLAC/Components/GameState.h new file mode 100644 index 0000000..cb86647 --- /dev/null +++ b/source/plugins/TLAC/Components/GameState.h @@ -0,0 +1,62 @@ +#pragma once +#include + +namespace TLAC::Components +{ + enum GameState : uint32_t + { + GS_STARTUP, + GS_ADVERTISE, + GS_GAME, + GS_DATA_TEST, + GS_TEST_MODE, + GS_APP_ERROR, + GS_MAX, + }; + + enum SubGameState : uint32_t + { + SUB_DATA_INITIALIZE, + SUB_SYSTEM_STARTUP, + SUB_SYSTEM_STARTUP_ERROR, + SUB_WARNING, + SUB_LOGO, + SUB_RATING, + SUB_DEMO, + SUB_TITLE, + SUB_RANKING, + SUB_SCORE_RANKING, + SUB_CM, + SUB_PHOTO_MODE_DEMO, + SUB_SELECTOR, + SUB_GAME_MAIN, + SUB_GAME_SEL, + SUB_STAGE_RESULT, + SUB_SCREEN_SHOT_SEL, + SUB_SCREEN_SHOT_RESULT, + SUB_GAME_OVER, + SUB_DATA_TEST_MAIN, + SUB_DATA_TEST_MISC, + SUB_DATA_TEST_OBJ, + SUB_DATA_TEST_STG, + SUB_DATA_TEST_MOT, + SUB_DATA_TEST_COLLISION, + SUB_DATA_TEST_SPR, + SUB_DATA_TEST_AET, + SUB_DATA_TEST_AUTH_3D, + SUB_DATA_TEST_CHR, + SUB_DATA_TEST_ITEM, + SUB_DATA_TEST_PERF, + SUB_DATA_TEST_PVSCRIPT, + SUB_DATA_TEST_PRINT, + SUB_DATA_TEST_CARD, + SUB_DATA_TEST_OPD, + SUB_DATA_TEST_SLIDER, + SUB_DATA_TEST_GLITTER, + SUB_DATA_TEST_GRAPHICS, + SUB_DATA_TEST_COLLECTION_CARD, + SUB_TEST_MODE_MAIN, + SUB_APP_ERROR, + SUB_MAX, + }; +} \ No newline at end of file diff --git a/source/plugins/TLAC/Components/GameTargets/HoldState.h b/source/plugins/TLAC/Components/GameTargets/HoldState.h new file mode 100644 index 0000000..974fe1d --- /dev/null +++ b/source/plugins/TLAC/Components/GameTargets/HoldState.h @@ -0,0 +1,13 @@ +#pragma once + +namespace TLAC::Components +{ + enum HoldState + { + HOLD_NONE, + HOLD_SANKAKU = 64, + HOLD_MARU = 128, + HOLD_BATSU = 256, + HOLD_SHIKAKU = 512, + }; +} \ No newline at end of file diff --git a/source/plugins/TLAC/Components/GameTargets/TargetHitStates.h b/source/plugins/TLAC/Components/GameTargets/TargetHitStates.h new file mode 100644 index 0000000..2dbe423 --- /dev/null +++ b/source/plugins/TLAC/Components/GameTargets/TargetHitStates.h @@ -0,0 +1,18 @@ +#pragma once + +namespace TLAC::Components +{ + enum TargetHitStates + { + COOL, + FINE, + SAFE, + SAD, + COOL_WRONG, // unsure + FINE_WRONG, // unsure + SAFE_WRONG, // unsure + SAD_WRONG, // unsure + WORST, + NONE = 21, + }; +} \ No newline at end of file diff --git a/source/plugins/TLAC/Components/GameTargets/TargetInspector.cpp b/source/plugins/TLAC/Components/GameTargets/TargetInspector.cpp new file mode 100644 index 0000000..803670a --- /dev/null +++ b/source/plugins/TLAC/Components/GameTargets/TargetInspector.cpp @@ -0,0 +1,77 @@ +#include "TargetInspector.h" + +namespace TLAC::Components +{ + bool TargetInspector::repressTbl[maxTargetSlots]; + + TargetInspector::TargetInspector() + { + } + + TargetInspector::~TargetInspector() + { + } + + void TargetInspector::Initialize(ComponentsManager*) + { + tgtTypePtr = (int*)TGT_TYPE_BASE_ADDRESS; + tgtHitStatePtr = (int*)TGT_HIT_STATE_BASE_ADDRESS; + tgtRemainingTimePtr = (float*)TGT_REMAINING_DURATION_BASE_ADDRESS; + } + + void TargetInspector::Update() + { + GetTargetStates(); + UpdateRepressTbl(); + } + + const char* TargetInspector::GetDisplayName() + { + return "target_inspector"; + } + + void TargetInspector::GetTargetStates() + { + for (int i = 0; i < maxTargetSlots; ++i) + { + tgtStates[i].tgtType = *((int*)((char*)tgtTypePtr + (i * offset))); + tgtStates[i].tgtHitState = *((int*)((char*)tgtHitStatePtr + (i * offset))); + tgtStates[i].tgtRemainingTime = *((float*)((char*)tgtRemainingTimePtr + (i * offset))); + } + } + + void TargetInspector::UpdateRepressTbl() + { + for (int i = 0; i < maxTargetSlots; ++i) + { + repressTbl[i] = IsWithinRange(tgtStates[i].tgtRemainingTime) + && HasNotBeenHit(tgtStates[i].tgtHitState) + && !IsSlide(tgtStates[i].tgtType); + } + } + + bool TargetInspector::IsWithinRange(float time) + { + return time < timingThreshold && time > -timingThreshold && time != 0; + } + + bool TargetInspector::HasNotBeenHit(int hitState) + { + return hitState == NONE; + } + + bool TargetInspector::IsSlide(int type) + { + return (type >= SLIDE_L && type <= SLIDE_LONG_R) || type >= SLIDE_L_CH; + } + + bool TargetInspector::IsAnyRepress() + { + for (int i = 0; i < maxTargetSlots; ++i) + { + if (repressTbl[i]) + return true; + } + return false; + } +} diff --git a/source/plugins/TLAC/Components/GameTargets/TargetInspector.h b/source/plugins/TLAC/Components/GameTargets/TargetInspector.h new file mode 100644 index 0000000..87fb103 --- /dev/null +++ b/source/plugins/TLAC/Components/GameTargets/TargetInspector.h @@ -0,0 +1,43 @@ +#pragma once +#include "../EmulatorComponent.h" +#include "../Input/InputEmulator.h" +#include "../../Constants.h" +#include "TargetHitStates.h" +#include "TargetState.h" + +namespace TLAC::Components +{ + const int maxTargetSlots = 32; + + class TargetInspector : public EmulatorComponent + { + public: + TargetInspector(); + ~TargetInspector(); + + static bool repressTbl[maxTargetSlots]; + + virtual void Initialize(ComponentsManager*) override; + virtual void Update() override; + virtual const char* GetDisplayName() override; + + void GetTargetStates(); + static bool IsAnyRepress(); + + private: + const uint64_t offset = 0x4A8; + const float timingThreshold = 0.13f; // PS4 estimate + + TargetState tgtStates[maxTargetSlots]; + + int* tgtTypePtr; + int* tgtHitStatePtr; + float* tgtRemainingTimePtr; + + bool IsSlide(int); + bool IsWithinRange(float); + bool HasNotBeenHit(int); + void UpdateRepressTbl(); + + }; +} \ No newline at end of file diff --git a/source/plugins/TLAC/Components/GameTargets/TargetState.h b/source/plugins/TLAC/Components/GameTargets/TargetState.h new file mode 100644 index 0000000..7ea6cff --- /dev/null +++ b/source/plugins/TLAC/Components/GameTargets/TargetState.h @@ -0,0 +1,11 @@ +#pragma once + +namespace TLAC::Components +{ + struct TargetState + { + int tgtType; + int tgtHitState; + float tgtRemainingTime; + }; +} \ No newline at end of file diff --git a/source/plugins/TLAC/Components/GameTargets/TargetTypes.h b/source/plugins/TLAC/Components/GameTargets/TargetTypes.h new file mode 100644 index 0000000..f152417 --- /dev/null +++ b/source/plugins/TLAC/Components/GameTargets/TargetTypes.h @@ -0,0 +1,26 @@ +#pragma once + +namespace TLAC::Components +{ + enum TargetTypes + { + SANKAKU, + MARU, + BATSU, + SHIKAKU, + SANKAKU_H, + MARU_H, + BATSU_H, + SHIKAKU_H, + SLIDE_L = 12, + SLIDE_R, + SLIDE_LONG_L = 15, + SLIDE_LONG_R, + SANKAKU_CH = 18, + MARU_CH, + BATSU_CH, + SHIKAKU_CH, + SLIDE_L_CH = 23, + SLIDE_R_CH, + }; +} \ No newline at end of file diff --git a/source/plugins/TLAC/Components/Input/InputBufferType.h b/source/plugins/TLAC/Components/Input/InputBufferType.h new file mode 100644 index 0000000..06c4e3f --- /dev/null +++ b/source/plugins/TLAC/Components/Input/InputBufferType.h @@ -0,0 +1,14 @@ +#pragma once + +namespace TLAC::Components +{ + enum InputBufferType + { + InputBufferType_Tapped, + InputBufferType_Released, + InputBufferType_Down, + InputBufferType_DoubleTapped, + InputBufferType_IntervalTapped, + InputBufferType_Max, + }; +} \ No newline at end of file diff --git a/source/plugins/TLAC/Components/Input/InputEmulator.cpp b/source/plugins/TLAC/Components/Input/InputEmulator.cpp new file mode 100644 index 0000000..6c69a19 --- /dev/null +++ b/source/plugins/TLAC/Components/Input/InputEmulator.cpp @@ -0,0 +1,305 @@ +#include +#include "windows.h" +#include "InputEmulator.h" +#include "../ComponentsManager.h" +#include "../../Constants.h" +#include "../../framework.h" +#include "../../Input/Bindings/KeyboardBinding.h" +#include "../../Input/Bindings/XinputBinding.h" +#include "../../Input/Bindings/MouseBinding.h" +#include "../../Input/Bindings/Ds4Binding.h" +#include "../../Input/KeyConfig/Config.h" +#include "../../Utilities/Operations.h" +#include "../../Utilities/EnumBitwiseOperations.h" +#include "../../FileSystem/ConfigFile.h" + +const std::string KEY_CONFIG_FILE_NAME = "keyconfig.ini"; + +using namespace TLAC::Input; +using namespace TLAC::Input::KeyConfig; +using namespace TLAC::Utilities; + +namespace TLAC::Components +{ + + InputEmulator::InputEmulator() + { + + } + + InputEmulator::~InputEmulator() + { + delete TestBinding; + delete ServiceBinding; + + delete StartBinding; + delete SankakuBinding; + delete ShikakuBinding; + delete BatsuBinding; + delete MaruBinding; + + delete LeftBinding; + delete RightBinding; + } + + const char* InputEmulator::GetDisplayName() + { + return "input_emulator"; + } + + void InputEmulator::Initialize(ComponentsManager* manager) + { + componentsManager = manager; + componentsManager->SetIsInputEmulatorUsed(true); + + inputState = GetInputStatePtr((void*)INPUT_STATE_PTR_ADDRESS); + inputState->HideCursor(); + + TestBinding = new Binding(); + ServiceBinding = new Binding(); + StartBinding = new Binding(); + + SankakuBinding = new Binding(); + ShikakuBinding = new Binding(); + BatsuBinding = new Binding(); + MaruBinding = new Binding(); + + LeftBinding = new Binding(); + RightBinding = new Binding(); + + FileSystem::ConfigFile configFile(framework::GetModuleDirectory(), KEY_CONFIG_FILE_NAME); + configFile.OpenRead(); + + Config::BindConfigKeys(configFile.ConfigMap, "JVS_TEST", *TestBinding, { "F1" }); + Config::BindConfigKeys(configFile.ConfigMap, "JVS_SERVICE", *ServiceBinding, { "F2" }); + Config::BindConfigKeys(configFile.ConfigMap, "JVS_START", *StartBinding, { "Enter" }); + Config::BindConfigKeys(configFile.ConfigMap, "JVS_TRIANGLE", *SankakuBinding, { "W", "I" }); + Config::BindConfigKeys(configFile.ConfigMap, "JVS_SQUARE", *ShikakuBinding, { "A", "J" }); + Config::BindConfigKeys(configFile.ConfigMap, "JVS_CROSS", *BatsuBinding, { "S", "K" }); + Config::BindConfigKeys(configFile.ConfigMap, "JVS_CIRCLE", *MaruBinding, { "D", "L" }); + Config::BindConfigKeys(configFile.ConfigMap, "JVS_LEFT", *LeftBinding, { "Q", "U" }); + Config::BindConfigKeys(configFile.ConfigMap, "JVS_RIGHT", *RightBinding, { "E", "O" }); + + mouseScrollPvSelection = configFile.GetBooleanValue("mouse_scroll_pv_selection"); + } + + void InputEmulator::Update() + { + return; + } + + void InputEmulator::OnFocusLost() + { + // to prevent buttons from being "stuck" + inputState->ClearState(); + inputState->HideCursor(); + } + + void InputEmulator::UpdateInput() + { + if (!componentsManager->GetUpdateGameInput()) + return; + + if (!componentsManager->IsDwGuiActive()) + { + UpdateJvsInput(); + + if (mouseScrollPvSelection && !componentsManager->IsDwGuiHovered()) + UpdateMousePvScroll(); + } + + UpdateDwGuiInput(); + } + + void InputEmulator::UpdateJvsInput() + { + auto tappedFunc = [](void* binding) { return ((Binding*)binding)->AnyTapped(); }; + auto releasedFunc = [](void* binding) { return ((Binding*)binding)->AnyReleased(); }; + auto downFunc = [](void* binding) { return ((Binding*)binding)->AnyDown(); }; + + lastDownState = inputState->Down.Buttons; + + inputState->Tapped.Buttons = GetJvsButtonsState(tappedFunc); + inputState->Released.Buttons = GetJvsButtonsState(releasedFunc); + inputState->Down.Buttons = GetJvsButtonsState(downFunc); + inputState->DoubleTapped.Buttons = GetJvsButtonsState(tappedFunc); + inputState->IntervalTapped.Buttons = GetJvsButtonsState(tappedFunc); + + UpdateHoldState(); + heldButtons = GetButtonFromHold(); + + if ((lastDownState &= inputState->Tapped.Buttons) != 0) + { + inputState->Down.Buttons ^= inputState->Tapped.Buttons; + if (IsHold() && !TargetInspector::IsAnyRepress()) + inputState->Down.Buttons |= heldButtons; + } + + // repress held down buttons to not block input + //inputState->Down.Buttons ^= inputState->Tapped.Buttons; + } + + HoldState InputEmulator::GetHoldState() + { + return (HoldState) * ((int*)HOLD_STATE_ADDRESS); + } + + int InputEmulator::GetMaxHoldState() + { + return *(int*)MAX_HOLD_STATE_ADDRESS; + } + + bool InputEmulator::IsHold() + { + return ((holdState != HOLD_NONE) && (GetMaxHoldState() != 1)); + } + + void InputEmulator::UpdateHoldState() + { + holdState = GetHoldState(); + + for (int i = 0; i < 4; ++i) + { + int holdId = 1 << (i + 6); + + if ((holdId & holdState) != 0) + holdTbl[i] = 1; + else + holdTbl[i] = 0; + } + } + + void InputEmulator::UpdateDwGuiInput() + { + auto keyboard = Keyboard::GetInstance(); + auto mouse = Mouse::GetInstance(); + + auto pos = mouse->GetRelativePosition(); + inputState->MouseX = (int)pos.x; + inputState->MouseY = (int)pos.y; + + auto deltaPos = mouse->GetDeltaPosition(); + inputState->MouseDeltaX = (int)deltaPos.x; + inputState->MouseDeltaY = (int)deltaPos.y; + + inputState->Key = GetKeyState(); + + for (int i = 0; i < sizeof(keyBits) / sizeof(KeyBit); i++) + UpdateInputBit(keyBits[i].Bit, keyBits[i].KeyCode); + + for (int i = InputBufferType_Tapped; i < InputBufferType_Max; i++) + { + inputState->SetBit(scrollUpBit, mouse->GetIsScrolledUp(), (InputBufferType)i); + inputState->SetBit(scrollDownBit, mouse->GetIsScrolledDown(), (InputBufferType)i); + } + } + + void InputEmulator::UpdateMousePvScroll() + { + // I originally wanted to use a MouseBinding set to JVS_LEFT / JVS_RIGHT + // but that ended up being too slow because a PV slot can only be scrolled to once the scroll animation has finished playing + int* slotsToScroll = (int*)PV_SEL_SLOTS_TO_SCROLL; + int* modulesToScroll = (int*)MODULE_SEL_SLOTS_TO_SCROLL; + + auto mouse = Mouse::GetInstance(); + if (mouse->GetIsScrolledUp()) + { + if (*(int*)PV_SEL_SLOTS_CONST < 26) *slotsToScroll -= 1; + if (*(int*)MODULE_IS_RECOMMENDED == 0) *modulesToScroll -= 1; + } + if (mouse->GetIsScrolledDown()) + { + if (*(int*)PV_SEL_SLOTS_CONST < 26) *slotsToScroll += 1; + if (*(int*)MODULE_IS_RECOMMENDED == 0) *modulesToScroll += 1; + } + } + + InputState* InputEmulator::GetInputStatePtr(void* address) + { + return (InputState*)(*(uint64_t*)address); + } + + JvsButtons InputEmulator::GetJvsButtonsState(bool(*buttonTestFunc)(void*)) + { + JvsButtons buttons = JVS_NONE; + + if (buttonTestFunc(TestBinding)) + buttons |= JVS_TEST; + if (buttonTestFunc(ServiceBinding)) + buttons |= JVS_SERVICE; + + if (buttonTestFunc(StartBinding)) + buttons |= JVS_START; + + if (buttonTestFunc(SankakuBinding)) + buttons |= JVS_TRIANGLE; + if (buttonTestFunc(ShikakuBinding)) + buttons |= JVS_SQUARE; + if (buttonTestFunc(BatsuBinding)) + buttons |= JVS_CROSS; + if (buttonTestFunc(MaruBinding)) + buttons |= JVS_CIRCLE; + + if (buttonTestFunc(LeftBinding)) + buttons |= JVS_L; + if (buttonTestFunc(RightBinding)) + buttons |= JVS_R; + + return buttons; + } + + JvsButtons InputEmulator::GetButtonFromHold() + { + JvsButtons buttons = JVS_NONE; + + if (holdTbl[0]) + buttons |= JVS_TRIANGLE; + if (holdTbl[1]) + buttons |= JVS_CIRCLE; + if (holdTbl[2]) + buttons |= JVS_CROSS; + if (holdTbl[3]) + buttons |= JVS_SQUARE; + + return buttons; + } + + char InputEmulator::GetKeyState() + { + auto keyboard = Keyboard::GetInstance(); + + bool upper = keyboard->IsDown(VK_SHIFT); + constexpr char caseDifference = 'A' - 'a'; + + char inputKey = 0x00; + + for (char key = '0'; key < 'Z'; key++) + { + if (keyboard->IsIntervalTapped(key)) + inputKey = (upper || key < 'A') ? key : (key - caseDifference); + } + + if (keyboard->IsIntervalTapped(VK_BACK)) + inputKey = 0x08; + + if (keyboard->IsIntervalTapped(VK_TAB)) + inputKey = 0x09; + + if (keyboard->IsIntervalTapped(VK_SPACE)) + inputKey = 0x20; + + return inputKey; + } + + void InputEmulator::UpdateInputBit(uint32_t bit, uint8_t keycode) + { + auto keyboard = Keyboard::GetInstance(); + + inputState->SetBit(bit, keyboard->IsTapped(keycode), InputBufferType_Tapped); + inputState->SetBit(bit, keyboard->IsReleased(keycode), InputBufferType_Released); + inputState->SetBit(bit, keyboard->IsDown(keycode), InputBufferType_Down); + inputState->SetBit(bit, keyboard->IsDoubleTapped(keycode), InputBufferType_DoubleTapped); + inputState->SetBit(bit, keyboard->IsIntervalTapped(keycode), InputBufferType_IntervalTapped); + + } +} \ No newline at end of file diff --git a/source/plugins/TLAC/Components/Input/InputEmulator.h b/source/plugins/TLAC/Components/Input/InputEmulator.h new file mode 100644 index 0000000..dd99ed1 --- /dev/null +++ b/source/plugins/TLAC/Components/Input/InputEmulator.h @@ -0,0 +1,102 @@ +#pragma once +#include +#include +#include "InputState.h" +#include "../EmulatorComponent.h" +#include "../../Input/Bindings/Binding.h" +#include "../GameTargets/TargetTypes.h" +#include "../GameTargets/TargetHitStates.h" +#include "../GameTargets/HoldState.h" +#include "../GameTargets/TargetInspector.h" + +namespace TLAC::Components +{ + struct KeyBit + { + uint32_t Bit; + uint8_t KeyCode; + }; + + class InputEmulator : public EmulatorComponent + { + public: + Input::Binding* TestBinding; + Input::Binding* ServiceBinding; + + Input::Binding* StartBinding; + Input::Binding* SankakuBinding; + Input::Binding* ShikakuBinding; + Input::Binding* BatsuBinding; + Input::Binding* MaruBinding; + + Input::Binding* LeftBinding; + Input::Binding* RightBinding; + + InputEmulator(); + ~InputEmulator(); + + virtual const char* GetDisplayName() override; + + virtual void Initialize(ComponentsManager*) override; + virtual void Update() override; + virtual void UpdateInput() override; + + virtual void OnFocusLost() override; + + private: + ComponentsManager* componentsManager; + + bool mouseScrollPvSelection = false; + const uint32_t scrollUpBit = 99; + const uint32_t scrollDownBit = 100; + + KeyBit keyBits[20] = + { + { 5, VK_LEFT }, + { 6, VK_RIGHT }, + + { 29, VK_SPACE }, + { 39, 'A' }, + { 43, 'E' }, + { 42, 'D' }, + { 55, 'Q' }, + { 57, 'S' }, // unsure + { 61, 'W' }, + { 63, 'Y' }, + { 84, 'L' }, // unsure + + { 80, VK_RETURN }, + { 81, VK_SHIFT }, + { 82, VK_CONTROL }, + { 83, VK_MENU }, + + { 91, VK_UP }, + { 93, VK_DOWN }, + + { 96, MK_LBUTTON }, + { 97, VK_MBUTTON }, + { 98, MK_RBUTTON }, + }; + + InputState* inputState; + JvsButtons lastDownState; + JvsButtons heldButtons; + + int holdTbl[4]; + HoldState holdState; + + void UpdateJvsInput(); + void UpdateDwGuiInput(); + void UpdateMousePvScroll(); + void UpdateHoldState(); + InputState* GetInputStatePtr(void* address); + JvsButtons GetJvsButtonsState(bool(*buttonTestFunc)(void*)); + JvsButtons GetButtonFromHold(); + char GetKeyState(); + HoldState GetHoldState(); + int GetMaxHoldState(); + bool IsHold(); + + void UpdateInputBit(uint32_t bit, uint8_t keycode); + }; +} diff --git a/source/plugins/TLAC/Components/Input/InputState.cpp b/source/plugins/TLAC/Components/Input/InputState.cpp new file mode 100644 index 0000000..181c21e --- /dev/null +++ b/source/plugins/TLAC/Components/Input/InputState.cpp @@ -0,0 +1,58 @@ +#include "InputState.h" +#include +#include + +namespace TLAC::Components +{ + void InputState::ClearState() + { + memset(this, 0, sizeof(InputState)); + } + + void InputState::HideCursor() + { + MouseX = INT32_MIN; + MouseY = INT32_MIN; + MouseDeltaX = 0; + MouseDeltaY = 0; + } + + void InputState::SetBit(uint32_t bit, bool value, InputBufferType inputType) + { + uint8_t* data = GetInputBuffer(inputType); + + if (data == nullptr || bit < 0 || bit >= MAX_BUTTON_BIT) + return; + + int byteIndex = (bit / 8); + int bitIndex = (bit % 8); + + BYTE mask = (1 << bitIndex); + + data[byteIndex] = value ? (data[byteIndex] | mask) : (data[byteIndex] & ~mask); + } + + uint8_t* InputState::GetInputBuffer(InputBufferType inputType) + { + switch (inputType) + { + case InputBufferType_Tapped: + return (uint8_t*)&Tapped; + + case InputBufferType_Released: + return (uint8_t*)&Released; + + case InputBufferType_Down: + return (uint8_t*)&Down; + + case InputBufferType_DoubleTapped: + return (uint8_t*)&DoubleTapped; + + case InputBufferType_IntervalTapped: + return (uint8_t*)&IntervalTapped; + + default: + return nullptr; + } + } +} diff --git a/source/plugins/TLAC/Components/Input/InputState.h b/source/plugins/TLAC/Components/Input/InputState.h new file mode 100644 index 0000000..7a79920 --- /dev/null +++ b/source/plugins/TLAC/Components/Input/InputState.h @@ -0,0 +1,47 @@ +#pragma once +#include "JvsButtons.h" +#include "InputBufferType.h" + +namespace TLAC::Components +{ + const int MAX_BUTTON_BIT = 0x6F; + + // The button state is larger than the size of a register + // but only the first 32 bits are used during normal gameplay + // so this will provide the convenience of still being able to access them through a bit field + union ButtonState + { + JvsButtons Buttons; + uint32_t State[4]; + }; + + // total sizeof() == 0x20E0 + struct InputState + { + ButtonState Tapped; + ButtonState Released; + + ButtonState Down; + uint32_t Padding_20[4]; + + ButtonState DoubleTapped; + uint32_t Padding_30[4]; + + ButtonState IntervalTapped; + uint32_t Padding_38[12]; + + int32_t MouseX; + int32_t MouseY; + int32_t MouseDeltaX; + int32_t MouseDeltaY; + + uint32_t Padding_AC[8]; + uint8_t Padding_D0[3]; + char Key; + + void ClearState(); + void HideCursor(); + void SetBit(uint32_t bit, bool value, InputBufferType inputType); + uint8_t* GetInputBuffer(InputBufferType inputType); + }; +} diff --git a/source/plugins/TLAC/Components/Input/JvsButtons.h b/source/plugins/TLAC/Components/Input/JvsButtons.h new file mode 100644 index 0000000..7dcde08 --- /dev/null +++ b/source/plugins/TLAC/Components/Input/JvsButtons.h @@ -0,0 +1,24 @@ +#pragma once +#include + +namespace TLAC::Components +{ + enum JvsButtons : uint32_t + { + JVS_NONE = 0 << 0x00, // 0x0 + + JVS_TEST = 1 << 0x00, // 0x1 + JVS_SERVICE = 1 << 0x01, // 0x2 + + JVS_START = 1 << 0x02, // 0x4 + JVS_TRIANGLE = 1 << 0x07, // 0x80 + JVS_SQUARE = 1 << 0x08, // 0x100 + JVS_CROSS = 1 << 0x09, // 0x200 + JVS_CIRCLE = 1 << 0x0A, // 0x400 + JVS_L = 1 << 0x0B, // 0x800 + JVS_R = 1 << 0x0C, // 0x1000 + + JVS_SW1 = 1 << 0x12, // 0x40000 + JVS_SW2 = 1 << 0x13, // 0x80000 + }; +} \ No newline at end of file diff --git a/source/plugins/TLAC/Components/Input/TouchPanelEmulator.cpp b/source/plugins/TLAC/Components/Input/TouchPanelEmulator.cpp new file mode 100644 index 0000000..e13e403 --- /dev/null +++ b/source/plugins/TLAC/Components/Input/TouchPanelEmulator.cpp @@ -0,0 +1,59 @@ +#include "TouchPanelEmulator.h" +#include +#include "../ComponentsManager.h" +#include "../../Constants.h" +#include "../../Input/Mouse/Mouse.h" +#include "../../Input/Keyboard/Keyboard.h" + +using namespace TLAC::Input; + +namespace TLAC::Components +{ + TouchPanelEmulator::TouchPanelEmulator() + { + } + + TouchPanelEmulator::~TouchPanelEmulator() + { + } + + const char* TouchPanelEmulator::GetDisplayName() + { + return "touch_panel_emulator"; + } + + void TouchPanelEmulator::Initialize(ComponentsManager* manager) + { + componentsManager = manager; + state = GetTouchStatePtr((void*)TASK_TOUCH_ADDRESS); + } + + void TouchPanelEmulator::Update() + { + state->ConnectionState = 1; + } + + void TouchPanelEmulator::UpdateInput() + { + if (!componentsManager->GetUpdateGameInput() || componentsManager->IsDwGuiActive() || componentsManager->IsDwGuiHovered()) + return; + + // TODO: rescale TouchReaction aet position + auto keyboard = Keyboard::GetInstance(); + auto pos = Mouse::GetInstance()->GetRelativePosition(); + + state->XPosition = (float)pos.x; + state->YPosition = (float)pos.y; + + bool down = keyboard->IsDown(VK_LBUTTON); + bool released = keyboard->IsReleased(VK_LBUTTON); + + state->ContactType = (down ? 0x2 : released ? 0x1 : 0x0); + state->Pressure = (float)(state->ContactType != 0); + } + + TouchPanelState* TouchPanelEmulator::GetTouchStatePtr(void *address) + { + return (TouchPanelState*)address; + } +} diff --git a/source/plugins/TLAC/Components/Input/TouchPanelEmulator.h b/source/plugins/TLAC/Components/Input/TouchPanelEmulator.h new file mode 100644 index 0000000..67f854f --- /dev/null +++ b/source/plugins/TLAC/Components/Input/TouchPanelEmulator.h @@ -0,0 +1,26 @@ +#pragma once +#include "../EmulatorComponent.h" +#include "TouchPanelState.h" + +namespace TLAC::Components +{ + class TouchPanelEmulator : public EmulatorComponent + { + public: + TouchPanelEmulator(); + ~TouchPanelEmulator(); + + virtual const char* GetDisplayName() override; + + virtual void Initialize(ComponentsManager*) override; + virtual void Update() override; + virtual void UpdateInput() override; + + private: + ComponentsManager* componentsManager; + + TouchPanelState* state; + TouchPanelState* GetTouchStatePtr(void *address); + }; +} + diff --git a/source/plugins/TLAC/Components/Input/TouchPanelState.h b/source/plugins/TLAC/Components/Input/TouchPanelState.h new file mode 100644 index 0000000..df89c62 --- /dev/null +++ b/source/plugins/TLAC/Components/Input/TouchPanelState.h @@ -0,0 +1,15 @@ +#pragma once + +namespace TLAC::Components +{ + struct TouchPanelState + { + int Padding00[0x1E]; + int ConnectionState; + int Padding01[0x06]; + float XPosition; + float YPosition; + float Pressure; + int ContactType; + }; +} diff --git a/source/plugins/TLAC/Components/Input/TouchSliderEmulator.cpp b/source/plugins/TLAC/Components/Input/TouchSliderEmulator.cpp new file mode 100644 index 0000000..b69accc --- /dev/null +++ b/source/plugins/TLAC/Components/Input/TouchSliderEmulator.cpp @@ -0,0 +1,138 @@ +#include "TouchSliderEmulator.h" +#include "../ComponentsManager.h" +#include "../../Constants.h" +#include "../../framework.h" +#include "../../Input/Mouse/Mouse.h" +#include "../../Input/Keyboard/Keyboard.h" +#include "../../Input/Bindings/KeyboardBinding.h" +#include "../../Input/KeyConfig/Config.h" +#include "../../FileSystem/ConfigFile.h" +#include "../../Utilities/Math.h" +#include +#include + +using namespace TLAC::Input; +using namespace TLAC::Input::KeyConfig; +using namespace TLAC::Utilities; + +namespace TLAC::Components +{ + const std::string KEY_CONFIG_FILE_NAME = "keyconfig.ini"; + + TouchSliderEmulator::TouchSliderEmulator() + { + } + + TouchSliderEmulator::~TouchSliderEmulator() + { + delete LeftSideSlideLeft; + delete LeftSideSlideRight; + + delete RightSideSlideLeft; + delete RightSideSlideRight; + } + + const char* TouchSliderEmulator::GetDisplayName() + { + return "touch_slider_emulator"; + } + + void TouchSliderEmulator::Initialize(ComponentsManager* manager) + { + componentsManager = manager; + sliderState = (TouchSliderState*)SLIDER_CTRL_TASK_ADDRESS; + + LeftSideSlideLeft = new Binding(); + LeftSideSlideRight = new Binding(); + + RightSideSlideLeft = new Binding(); + RightSideSlideRight = new Binding(); + + FileSystem::ConfigFile configFile(framework::GetModuleDirectory(), KEY_CONFIG_FILE_NAME); + configFile.OpenRead(); + + Config::BindConfigKeys(configFile.ConfigMap, "LEFT_SIDE_SLIDE_LEFT", *LeftSideSlideLeft, { "Q" }); + Config::BindConfigKeys(configFile.ConfigMap, "LEFT_SIDE_SLIDE_RIGHT", *LeftSideSlideRight, { "E" }); + + Config::BindConfigKeys(configFile.ConfigMap, "RIGHT_SIDE_SLIDE_LEFT", *RightSideSlideLeft, { "U" }); + Config::BindConfigKeys(configFile.ConfigMap, "RIGHT_SIDE_SLIDE_RIGHT", *RightSideSlideRight, { "O" }); + + float touchSliderEmulationSpeed = configFile.GetFloatValue("touch_slider_emulation_speed"); + + if (touchSliderEmulationSpeed != 0.0f) + sliderSpeed = touchSliderEmulationSpeed; + } + + void TouchSliderEmulator::Update() + { + sliderState->State = SLIDER_OK; + } + + void TouchSliderEmulator::UpdateInput() + { + if (!componentsManager->GetUpdateGameInput() || componentsManager->IsDwGuiActive()) + return; + + sliderIncrement = GetElapsedTime() / sliderSpeed; + + constexpr float sensorStep = (1.0f / SLIDER_SENSORS); + + EmulateSliderInput(LeftSideSlideLeft, LeftSideSlideRight, ContactPoints[0], 0.0f, 0.5f); + EmulateSliderInput(RightSideSlideLeft, RightSideSlideRight, ContactPoints[1], 0.5f + sensorStep, 1.0f + sensorStep); + + sliderState->ResetSensors(); + + for (int i = 0; i < CONTACT_POINTS; i++) + ApplyContactPoint(ContactPoints[i], i); + } + + void TouchSliderEmulator::OnFocusLost() + { + sliderState->ResetSensors(); + } + + void TouchSliderEmulator::EmulateSliderInput(Binding *leftBinding, Binding *rightBinding, ContactPoint &contactPoint, float start, float end) + { + bool leftDown = leftBinding->AnyDown(); + bool rightDown = rightBinding->AnyDown(); + + if (leftDown) + contactPoint.Position -= sliderIncrement; + else if (rightDown) + contactPoint.Position += sliderIncrement; + + if (contactPoint.Position < start) + contactPoint.Position = end; + + if (contactPoint.Position > end) + contactPoint.Position = start; + + bool leftTapped = leftBinding->AnyTapped(); + bool rightTapped = rightBinding->AnyTapped(); + + if (leftTapped || rightTapped) + contactPoint.Position = (start + end) / 2.0f; + + contactPoint.InContact = leftDown || rightDown; + } + + void TouchSliderEmulator::ApplyContactPoint(ContactPoint &contactPoint, int section) + { + sliderState->SectionTouched[section] = contactPoint.InContact; + + int pressure = contactPoint.InContact ? FULL_PRESSURE : NO_PRESSURE; + float position = std::clamp(contactPoint.Position, 0.0f, 1.0f); + + if (contactPoint.InContact) + { + int sensor = (int)(position * (SLIDER_SENSORS - 1)); + + sliderState->SetSensor(sensor, pressure); + } + + constexpr float startRange = -1.0f; + constexpr float endRange = +1.0f; + + sliderState->SectionPositions[section] = contactPoint.InContact ? (ConvertRange(0.0f, 1.0f, startRange, endRange, position)) : 0.0f; + } +} \ No newline at end of file diff --git a/source/plugins/TLAC/Components/Input/TouchSliderEmulator.h b/source/plugins/TLAC/Components/Input/TouchSliderEmulator.h new file mode 100644 index 0000000..1cc9869 --- /dev/null +++ b/source/plugins/TLAC/Components/Input/TouchSliderEmulator.h @@ -0,0 +1,49 @@ +#pragma once +#include "../EmulatorComponent.h" +#include "TouchSliderState.h" +#include "../../Input/Bindings/Binding.h" + +namespace TLAC::Components +{ + constexpr int SLIDER_INPUTS = 4; + constexpr int CONTACT_POINTS = 2; + + struct ContactPoint + { + float Position; + bool InContact; + }; + + class TouchSliderEmulator : public EmulatorComponent + { + public: + Input::Binding* LeftSideSlideLeft; + Input::Binding* LeftSideSlideRight; + + Input::Binding* RightSideSlideLeft; + Input::Binding* RightSideSlideRight; + + TouchSliderEmulator(); + ~TouchSliderEmulator(); + + virtual const char* GetDisplayName() override; + + virtual void Initialize(ComponentsManager*) override; + virtual void Update() override; + virtual void UpdateInput() override; + + virtual void OnFocusLost() override; + + private: + ComponentsManager* componentsManager; + float sliderSpeed = 750.0f; + float sliderIncrement; + + TouchSliderState *sliderState; + ContactPoint ContactPoints[CONTACT_POINTS]; + + void EmulateSliderInput(Input::Binding *leftBinding, Input::Binding *rightBinding, ContactPoint &contactPoint, float start, float end); + void ApplyContactPoint(ContactPoint &contactPoint, int section); + }; +} + diff --git a/source/plugins/TLAC/Components/Input/TouchSliderState.cpp b/source/plugins/TLAC/Components/Input/TouchSliderState.cpp new file mode 100644 index 0000000..24039e2 --- /dev/null +++ b/source/plugins/TLAC/Components/Input/TouchSliderState.cpp @@ -0,0 +1,19 @@ +#include "TouchSliderState.h" + +namespace TLAC::Components +{ + void TouchSliderState::SetSensor(int index, int value) + { + if (index < 0 || index >= SLIDER_SENSORS) + return; + + SensorPressureLevels[index] = value; + SensorTouched[index].IsTouched = value > 0; + } + + void TouchSliderState::ResetSensors() + { + for (int i = 0; i < SLIDER_SENSORS; i++) + SetSensor(i, NO_PRESSURE); + } +} \ No newline at end of file diff --git a/source/plugins/TLAC/Components/Input/TouchSliderState.h b/source/plugins/TLAC/Components/Input/TouchSliderState.h new file mode 100644 index 0000000..3830f9c --- /dev/null +++ b/source/plugins/TLAC/Components/Input/TouchSliderState.h @@ -0,0 +1,43 @@ +#pragma once +#include + +#define SLIDER_OK 3 +#define SLIDER_SECTIONS 4 +#define SLIDER_SENSORS 32 + +#define NO_PRESSURE 0 +#define FULL_PRESSURE 180 + +namespace TLAC::Components +{ + struct TouchSliderState + { + uint8_t Padding0000[112]; + + int32_t State; + + uint8_t Padding0074[20 + 12]; + + int32_t SensorPressureLevels[SLIDER_SENSORS]; + + uint8_t Padding0108[52 - 12]; + + float SectionPositions[SLIDER_SECTIONS]; + int SectionConnections[SLIDER_SECTIONS]; + uint8_t Padding015C[4]; + bool SectionTouched[SLIDER_SECTIONS]; + + uint8_t Padding013C[3128 - 52 - 40]; + + struct + { + uint8_t Padding00[2]; + bool IsTouched; + uint8_t Padding[45]; + } SensorTouched[SLIDER_SENSORS]; + + void SetSensor(int index, int value); + void ResetSensors(); + }; +} + diff --git a/source/plugins/TLAC/Components/PlayerData.h b/source/plugins/TLAC/Components/PlayerData.h new file mode 100644 index 0000000..34d8051 --- /dev/null +++ b/source/plugins/TLAC/Components/PlayerData.h @@ -0,0 +1,973 @@ +#pragma once +#include + +struct PlayerData +{ + int8_t use_card; + int8_t freeplay; + int8_t field_2; + int8_t field_3; + int32_t card_type; + int32_t field_8; + int32_t field_C; + int32_t field_10; + int32_t field_14; + int32_t field_18; + int32_t field_1C; + int32_t field_20; + int32_t field_24; + int32_t field_28; + int32_t field_2C; + int32_t field_30; + int32_t field_34; + int32_t field_38; + int32_t field_3C; + int32_t field_40; + int32_t field_44; + int32_t field_48; + int32_t field_4C; + int32_t field_50; + int32_t field_54; + int32_t field_58; + int32_t field_5C; + int32_t field_60; + int32_t field_64; + int32_t field_68; + int32_t field_6C; + int32_t field_70; + int32_t field_74; + int32_t field_78; + int32_t field_7C; + int32_t field_80; + int32_t field_84; + int32_t field_88; + int32_t field_8C; + int32_t field_90; + int32_t field_94; + int32_t field_98; + int32_t field_9C; + int32_t field_A0; + int32_t field_A4; + int32_t field_A8; + int32_t field_AC; + int32_t field_B0; + int32_t field_B4; + int32_t field_B8; + int32_t field_BC; + int32_t field_C0; + int32_t field_C4; + int32_t field_C8; + int32_t field_CC; + int32_t play_data_id; + int32_t accept_index; + int32_t start_index; + int32_t field_DC; + char* player_name; + int32_t field_E8; + int32_t field_EC; + int32_t field_F0; + int32_t field_F4; + int32_t field_F8; + int32_t field_FC; + char* level_name; + int32_t field_108; + int32_t field_10C; + int32_t field_110; + int32_t field_114; + int32_t field_118; + int32_t field_11C; + int32_t field_120; + int32_t level_plate_id; + int32_t field_128; + int32_t vocaloid_point; + int32_t hp_vol; + int32_t act_toggle; + int32_t act_vol; + int32_t act_slide_vol; + int32_t field_140; + int32_t field_144; + int32_t field_148; + int32_t field_14C; + int32_t field_150; + int32_t field_154; + int32_t field_158; + int32_t field_15C; + int32_t field_160; + int32_t field_164; + int32_t field_168; + int32_t field_16C; + int32_t field_170; + int32_t field_174; + int32_t field_178; + int32_t field_17C; + int32_t field_180; + int32_t field_184; + int32_t field_188; + int32_t field_18C; + int32_t field_190; + int32_t field_194; + int32_t field_198; + int32_t field_19C; + int32_t field_1A0; + int32_t field_1A4; + int32_t field_1A8; + int32_t field_1AC; + int32_t field_1B0; + int32_t field_1B4; + int32_t field_1B8; + int32_t field_1BC; + int32_t field_1C0; + int32_t field_1C4; + int32_t field_1C8; + int32_t field_1CC; + int32_t field_1D0; + int32_t field_1D4; + int32_t field_1D8; + int32_t field_1DC; + int32_t field_1E0; + int32_t field_1E4; + int32_t field_1E8; + int32_t field_1EC; + int32_t field_1F0; + int32_t field_1F4; + int32_t field_1F8; + int32_t field_1FC; + int32_t field_200; + int32_t field_204; + int32_t field_208; + int32_t field_20C; + int32_t field_210; + int32_t field_214; + int32_t field_218; + int32_t field_21C; + int32_t field_220; + int32_t field_224; + int32_t field_228; + int32_t field_22C; + int32_t field_230; + int32_t field_234; + int32_t field_238; + int32_t field_23C; + int32_t field_240; + int32_t field_244; + int32_t field_248; + int32_t field_24C; + int32_t field_250; + int32_t field_254; + int32_t field_258; + int32_t field_25C; + int32_t field_260; + int32_t field_264; + int32_t field_268; + int32_t field_26C; + int32_t field_270; + int32_t field_274; + int32_t field_278; + int32_t field_27C; + int32_t field_280; + int32_t field_284; + int32_t field_288; + int32_t field_28C; + int32_t field_290; + int32_t field_294; + int32_t field_298; + int32_t field_29C; + int32_t field_2A0; + int32_t field_2A4; + int32_t field_2A8; + int32_t field_2AC; + int8_t use_pv_module_equip; + int8_t ch_pv_module_equip; + int8_t field_2B2; + int8_t field_2B3; + int32_t module_filter_kind; + int32_t field_2B8; + int32_t field_2BC; + int32_t field_2C0; + int32_t field_2C4; + int32_t field_2C8; + int32_t field_2CC; + int32_t field_2D0; + int32_t field_2D4; + int32_t field_2D8; + int32_t field_2DC; + int32_t field_2E0; + int32_t field_2E4; + int32_t field_2E8; + int32_t field_2EC; + int32_t field_2F0; + int32_t field_2F4; + int32_t field_2F8; + int32_t field_2FC; + int32_t field_300; + int32_t field_304; + int32_t field_308; + int32_t field_30C; + int32_t field_310; + int32_t field_314; + int32_t field_318; + int32_t field_31C; + int32_t field_320; + int32_t field_324; + int32_t field_328; + int32_t field_32C; + int32_t field_330; + int32_t field_334; + int32_t field_338; + int32_t field_33C; + int32_t field_340; + int32_t field_344; + int32_t field_348; + int32_t field_34C; + int32_t field_350; + int32_t field_354; + int32_t field_358; + int32_t field_35C; + int32_t field_360; + int32_t field_364; + int32_t field_368; + int32_t field_36C; + int32_t field_370; + int32_t field_374; + int32_t field_378; + int32_t field_37C; + int32_t field_380; + int32_t field_384; + int32_t field_388; + int32_t field_38C; + int32_t field_390; + int32_t field_394; + int32_t field_398; + int32_t field_39C; + int32_t field_3A0; + int32_t field_3A4; + int32_t field_3A8; + int32_t field_3AC; + int32_t field_3B0; + int32_t field_3B4; + int32_t field_3B8; + int32_t field_3BC; + int32_t field_3C0; + int32_t field_3C4; + int32_t field_3C8; + int32_t field_3CC; + int32_t field_3D0; + int32_t field_3D4; + int32_t field_3D8; + int32_t field_3DC; + int32_t field_3E0; + int32_t field_3E4; + int32_t field_3E8; + int32_t field_3EC; + int32_t field_3F0; + int32_t field_3F4; + int32_t field_3F8; + int32_t field_3FC; + int32_t field_400; + int32_t field_404; + int32_t field_408; + int32_t field_40C; + int32_t field_410; + int32_t field_414; + int32_t field_418; + int32_t field_41C; + int32_t field_420; + int32_t field_424; + int32_t field_428; + int32_t field_42C; + int32_t field_430; + int32_t field_434; + int32_t field_438; + int32_t field_43C; + int32_t field_440; + int32_t field_444; + int32_t field_448; + int32_t field_44C; + int32_t field_450; + int32_t field_454; + int32_t field_458; + int32_t field_45C; + int32_t field_460; + int32_t field_464; + int32_t field_468; + int32_t field_46C; + int32_t field_470; + int32_t field_474; + int32_t field_478; + int32_t field_47C; + int32_t field_480; + int32_t field_484; + int32_t field_488; + int32_t field_48C; + int32_t field_490; + int32_t field_494; + int32_t field_498; + int32_t field_49C; + int32_t field_4A0; + int32_t field_4A4; + int32_t field_4A8; + int32_t field_4AC; + int32_t field_4B0; + int32_t field_4B4; + int32_t field_4B8; + int32_t field_4BC; + int32_t field_4C0; + int32_t field_4C4; + int32_t field_4C8; + int32_t field_4CC; + int32_t field_4D0; + int32_t field_4D4; + int32_t field_4D8; + int32_t field_4DC; + int32_t field_4E0; + int32_t field_4E4; + int32_t field_4E8; + int32_t field_4EC; + int32_t field_4F0; + int32_t field_4F4; + int32_t field_4F8; + int32_t field_4FC; + int32_t field_500; + int32_t field_504; + int32_t field_508; + int32_t field_50C; + int32_t field_510; + int32_t field_514; + int32_t field_518; + int32_t field_51C; + int32_t field_520; + int32_t field_524; + int32_t field_528; + int32_t field_52C; + int32_t field_530; + int32_t field_534; + int32_t field_538; + int32_t field_53C; + int32_t field_540; + int32_t field_544; + int32_t skin_equip; + int32_t skin_equip_cmn; + int32_t use_pv_skin_equip; + int32_t btn_se_equip; + int32_t btn_se_equip_cmn; + int32_t use_pv_btn_se_equip; + int32_t slide_se_equip; + int32_t slide_se_equip_cmn; + int32_t use_pv_slide_se_equip; + int32_t chainslide_se_equip; + int32_t chainslide_se_equip_cmn; + int32_t use_pv_chainslide_se_equip; + int32_t slidertouch_se_equip; + int32_t slidertouch_se_equip_cmn; + int32_t use_pv_slidertouch_se_equip; + int32_t field_584; + int32_t field_588; + int32_t field_58C; + int32_t field_590; + int32_t field_594; + int32_t field_598; + int32_t field_59C; + int32_t field_5A0; + int32_t field_5A4; + int32_t field_5A8; + int32_t field_5AC; + int32_t field_5B0; + int32_t field_5B4; + int32_t field_5B8; + int32_t field_5BC; + int32_t field_5C0; + int32_t field_5C4; + int32_t field_5C8; + int32_t field_5CC; + int32_t field_5D0; + int32_t field_5D4; + int32_t field_5D8; + int32_t field_5DC; + int32_t field_5E0; + int32_t field_5E4; + int32_t field_5E8; + int32_t field_5EC; + int32_t field_5F0; + int32_t field_5F4; + int32_t field_5F8; + int32_t field_5FC; + int32_t field_600; + int32_t field_604; + int32_t field_608; + int32_t field_60C; + int32_t field_610; + int32_t field_614; + int32_t field_618; + int32_t field_61C; + int32_t field_620; + int32_t field_624; + int32_t field_628; + int32_t field_62C; + int32_t field_630; + int32_t field_634; + int32_t field_638; + int32_t field_63C; + int32_t field_640; + int32_t field_644; + int32_t field_648; + int32_t field_64C; + int32_t field_650; + int32_t field_654; + int32_t field_658; + int32_t field_65C; + int32_t field_660; + int32_t field_664; + int32_t field_668; + int32_t field_66C; + int32_t field_670; + int32_t field_674; + int32_t field_678; + int32_t field_67C; + int32_t field_680; + int32_t field_684; + int32_t field_688; + int32_t field_68C; + int32_t field_690; + int32_t field_694; + int32_t field_698; + int32_t field_69C; + int32_t field_6A0; + int32_t field_6A4; + int32_t field_6A8; + int32_t field_6AC; + int32_t field_6B0; + int32_t field_6B4; + int32_t field_6B8; + int32_t field_6BC; + int32_t field_6C0; + int32_t field_6C4; + int32_t field_6C8; + int32_t field_6CC; + int32_t field_6D0; + int32_t field_6D4; + int32_t field_6D8; + int32_t field_6DC; + int32_t field_6E0; + int32_t field_6E4; + int32_t field_6E8; + int32_t field_6EC; + int32_t field_6F0; + int32_t field_6F4; + int32_t field_6F8; + int32_t field_6FC; + int32_t field_700; + int32_t field_704; + int32_t field_708; + int32_t field_70C; + int32_t field_710; + int32_t field_714; + int32_t field_718; + int32_t field_71C; + int32_t field_720; + int32_t field_724; + int32_t field_728; + int32_t field_72C; + int32_t field_730; + int32_t field_734; + int32_t field_738; + int32_t field_73C; + int32_t field_740; + int32_t field_744; + int32_t field_748; + int32_t field_74C; + int32_t field_750; + int32_t field_754; + int32_t field_758; + int32_t field_75C; + int32_t field_760; + int32_t field_764; + int32_t field_768; + int32_t field_76C; + int32_t field_770; + int32_t field_774; + int32_t field_778; + int32_t field_77C; + int32_t field_780; + int32_t field_784; + int32_t field_788; + int32_t field_78C; + int32_t field_790; + int32_t field_794; + int32_t field_798; + int32_t field_79C; + int32_t field_7A0; + int32_t field_7A4; + int32_t field_7A8; + int32_t field_7AC; + int32_t field_7B0; + int32_t field_7B4; + int32_t field_7B8; + int32_t field_7BC; + int32_t field_7C0; + int32_t field_7C4; + int32_t field_7C8; + int32_t field_7CC; + int32_t field_7D0; + int32_t field_7D4; + int32_t field_7D8; + int32_t field_7DC; + int32_t field_7E0; + int32_t field_7E4; + int32_t field_7E8; + int32_t field_7EC; + int32_t field_7F0; + int32_t field_7F4; + int32_t field_7F8; + int32_t field_7FC; + int32_t field_800; + int32_t field_804; + int32_t field_808; + int32_t field_80C; + int32_t field_810; + int32_t field_814; + int32_t field_818; + int32_t field_81C; + int32_t field_820; + int32_t field_824; + int32_t field_828; + int32_t field_82C; + int32_t field_830; + int32_t field_834; + int32_t field_838; + int32_t field_83C; + int32_t field_840; + int32_t field_844; + int32_t field_848; + int32_t field_84C; + int32_t field_850; + int32_t field_854; + int32_t field_858; + int32_t field_85C; + int32_t field_860; + int32_t field_864; + int32_t field_868; + int32_t field_86C; + int32_t field_870; + int32_t field_874; + int32_t field_878; + int32_t field_87C; + int32_t field_880; + int32_t field_884; + int32_t field_888; + int32_t field_88C; + int32_t field_890; + int32_t field_894; + int32_t field_898; + int32_t field_89C; + int32_t field_8A0; + int32_t field_8A4; + int32_t field_8A8; + int32_t field_8AC; + int32_t field_8B0; + int32_t field_8B4; + int32_t field_8B8; + int32_t field_8BC; + int32_t field_8C0; + int32_t field_8C4; + int32_t field_8C8; + int32_t field_8CC; + int32_t field_8D0; + int32_t field_8D4; + int32_t field_8D8; + int32_t field_8DC; + int32_t field_8E0; + int32_t field_8E4; + int32_t field_8E8; + int32_t field_8EC; + int32_t field_8F0; + int32_t field_8F4; + int32_t field_8F8; + int32_t field_8FC; + int32_t field_900; + int32_t field_904; + int32_t field_908; + int32_t field_90C; + int32_t field_910; + int32_t field_914; + int32_t field_918; + int32_t field_91C; + int32_t field_920; + int32_t field_924; + int32_t field_928; + int32_t field_92C; + int32_t field_930; + int32_t field_934; + int32_t field_938; + int32_t field_93C; + int32_t field_940; + int32_t field_944; + int32_t field_948; + int32_t field_94C; + int32_t field_950; + int32_t field_954; + int32_t field_958; + int32_t field_95C; + int32_t field_960; + int32_t field_964; + int32_t field_968; + int32_t field_96C; + int32_t field_970; + int32_t field_974; + int32_t field_978; + int32_t field_97C; + int32_t field_980; + int32_t field_984; + int32_t field_988; + int32_t field_98C; + int32_t field_990; + int32_t field_994; + int32_t field_998; + int32_t field_99C; + int32_t field_9A0; + int32_t field_9A4; + int32_t field_9A8; + int32_t field_9AC; + int32_t field_9B0; + int32_t field_9B4; + int32_t field_9B8; + int32_t field_9BC; + int32_t field_9C0; + int32_t field_9C4; + int32_t field_9C8; + int32_t field_9CC; + int32_t field_9D0; + int32_t field_9D4; + int32_t field_9D8; + int32_t field_9DC; + int32_t field_9E0; + int32_t field_9E4; + int32_t field_9E8; + int32_t field_9EC; + int32_t field_9F0; + int32_t field_9F4; + int32_t field_9F8; + int32_t field_9FC; + int32_t field_A00; + int32_t field_A04; + int32_t field_A08; + int32_t field_A0C; + int32_t field_A10; + int32_t field_A14; + int32_t field_A18; + int32_t field_A1C; + int32_t field_A20; + int32_t field_A24; + int32_t field_A28; + int32_t field_A2C; + int32_t field_A30; + int32_t field_A34; + int32_t field_A38; + int32_t field_A3C; + int32_t field_A40; + int32_t field_A44; + int32_t field_A48; + int32_t field_A4C; + int32_t field_A50; + int32_t field_A54; + int32_t field_A58; + int32_t field_A5C; + int32_t field_A60; + int32_t field_A64; + int32_t field_A68; + int32_t field_A6C; + int32_t field_A70; + int32_t field_A74; + int32_t field_A78; + int32_t field_A7C; + int32_t field_A80; + int32_t field_A84; + int32_t field_A88; + int32_t field_A8C; + int32_t field_A90; + int32_t field_A94; + int32_t field_A98; + int32_t field_A9C; + int32_t field_AA0; + int32_t field_AA4; + int32_t field_AA8; + int32_t field_AAC; + int32_t field_AB0; + int32_t field_AB4; + int32_t field_AB8; + int32_t field_ABC; + int32_t field_AC0; + int32_t field_AC4; + int32_t field_AC8; + int32_t field_ACC; + int32_t field_AD0; + int32_t field_AD4; + int32_t field_AD8; + int32_t field_ADC; + int32_t field_AE0; + int32_t field_AE4; + int32_t field_AE8; + int32_t field_AEC; + int32_t field_AF0; + int32_t field_AF4; + int32_t field_AF8; + int32_t field_AFC; + int32_t field_B00; + int32_t field_B04; + int32_t field_B08; + int32_t field_B0C; + int32_t field_B10; + int32_t field_B14; + int32_t field_B18; + int32_t field_B1C; + int32_t field_B20; + int32_t field_B24; + int32_t field_B28; + int32_t field_B2C; + int32_t field_B30; + int32_t field_B34; + int32_t field_B38; + int32_t field_B3C; + int32_t field_B40; + int32_t field_B44; + int32_t field_B48; + int32_t field_B4C; + int32_t field_B50; + int32_t field_B54; + int32_t field_B58; + int32_t field_B5C; + int32_t field_B60; + int32_t field_B64; + int32_t field_B68; + int32_t field_B6C; + int32_t field_B70; + int32_t field_B74; + int32_t field_B78; + int32_t field_B7C; + int32_t field_B80; + int32_t field_B84; + int32_t field_B88; + int32_t field_B8C; + int32_t field_B90; + int32_t field_B94; + int32_t field_B98; + int32_t field_B9C; + int32_t field_BA0; + int32_t field_BA4; + int32_t field_BA8; + int32_t field_BAC; + int32_t field_BB0; + int32_t field_BB4; + int32_t field_BB8; + int32_t field_BBC; + int32_t field_BC0; + int32_t field_BC4; + int32_t field_BC8; + int32_t field_BCC; + int32_t field_BD0; + int32_t field_BD4; + int32_t field_BD8; + int32_t field_BDC; + int32_t field_BE0; + int32_t field_BE4; + int32_t field_BE8; + int32_t field_BEC; + int32_t field_BF0; + int32_t field_BF4; + int32_t field_BF8; + int32_t field_BFC; + int32_t field_C00; + int32_t field_C04; + int32_t field_C08; + int32_t field_C0C; + int32_t field_C10; + int32_t field_C14; + int32_t field_C18; + int32_t field_C1C; + int32_t field_C20; + int32_t field_C24; + int32_t field_C28; + int32_t field_C2C; + int32_t field_C30; + int32_t field_C34; + int32_t field_C38; + int32_t field_C3C; + int32_t field_C40; + int32_t field_C44; + int32_t field_C48; + int32_t field_C4C; + int32_t field_C50; + int32_t field_C54; + int32_t field_C58; + int32_t field_C5C; + int32_t field_C60; + int32_t field_C64; + int32_t field_C68; + int32_t field_C6C; + int32_t field_C70; + int32_t field_C74; + int32_t field_C78; + int32_t field_C7C; + int32_t field_C80; + int32_t field_C84; + int32_t field_C88; + int32_t field_C8C; + int32_t field_C90; + int32_t field_C94; + int32_t field_C98; + int32_t field_C9C; + int32_t field_CA0; + int32_t field_CA4; + int32_t field_CA8; + int32_t field_CAC; + int32_t field_CB0; + int32_t field_CB4; + int32_t field_CB8; + int32_t field_CBC; + int32_t field_CC0; + int32_t field_CC4; + int32_t field_CC8; + int32_t field_CCC; + int32_t field_CD0; + int32_t field_CD4; + int32_t field_CD8; + int32_t field_CDC; + int32_t field_CE0; + int32_t field_CE4; + int32_t field_CE8; + int32_t field_CEC; + int32_t field_CF0; + int32_t field_CF4; + int32_t field_CF8; + int32_t field_CFC; + int32_t field_D00; + int32_t field_D04; + int32_t field_D08; + int32_t field_D0C; + int32_t field_D10; + int32_t field_D14; + int32_t field_D18; + int32_t field_D1C; + int32_t field_D20; + int32_t field_D24; + int32_t field_D28; + int32_t field_D2C; + int32_t field_D30; + int32_t field_D34; + int32_t field_D38; + int32_t field_D3C; + int32_t field_D40; + int32_t field_D44; + int32_t field_D48; + int32_t field_D4C; + int32_t field_D50; + int32_t field_D54; + int32_t field_D58; + int32_t field_D5C; + int32_t field_D60; + int32_t field_D64; + int32_t field_D68; + int32_t field_D6C; + int32_t field_D70; + int32_t field_D74; + int32_t field_D78; + int32_t field_D7C; + int32_t field_D80; + int32_t field_D84; + int32_t field_D88; + int32_t field_D8C; + int32_t field_D90; + int32_t field_D94; + int32_t field_D98; + int32_t field_D9C; + int32_t field_DA0; + int32_t field_DA4; + int32_t field_DA8; + int32_t field_DAC; + int32_t field_DB0; + int32_t field_DB4; + int32_t field_DB8; + int32_t field_DBC; + int32_t field_DC0; + int32_t field_DC4; + int32_t field_DC8; + int32_t field_DCC; + int32_t field_DD0; + int32_t field_DD4; + int32_t field_DD8; + int32_t field_DDC; + int32_t field_DE0; + int32_t field_DE4; + int32_t field_DE8; + int32_t field_DEC; + int32_t field_DF0; + int32_t field_DF4; + int32_t field_DF8; + int32_t field_DFC; + int32_t field_E00; + int32_t field_E04; + int32_t field_E08; + int32_t field_E0C; + int32_t field_E10; + int32_t field_E14; + int32_t field_E18; + int32_t field_E1C; + int32_t field_E20; + int32_t field_E24; + int32_t field_E28; + int32_t field_E2C; + int32_t field_E30; + int8_t field_E34; + int8_t game_opts; + int8_t field_E36; + int8_t field_E37; + int32_t field_E38; + int32_t field_E3C; + int32_t field_E40; + int32_t field_E44; + int32_t field_E48; + int32_t field_E4C; + int32_t field_E50; + int32_t field_E54; + int32_t field_E58; + int32_t field_E5C; + int32_t field_E60; + int32_t field_E64; + int32_t field_E68; + int32_t field_E6C; + int32_t field_E70; + int32_t field_E74; + int32_t field_E78; + int32_t field_E7C; + int32_t field_E80; + int32_t field_E84; + int32_t field_E88; + int32_t field_E8C; + int32_t field_E90; + int32_t field_E94; + int32_t field_E98; + int32_t field_E9C; + int32_t field_EA0; + int32_t field_EA4; + int32_t field_EA8; + int32_t field_EAC; + int32_t field_EB0; + int32_t field_EB4; + int32_t field_EB8; + int32_t field_EBC; + int32_t field_EC0; + int32_t field_EC4; + int32_t field_EC8; + int32_t field_ECC; + int32_t field_ED0; + int32_t field_ED4; + int32_t field_ED8; + int32_t field_EDC; + int32_t field_EE0; + int32_t field_EE4; + int32_t field_EE8; + int32_t field_EEC; + int32_t field_EF0; + int32_t field_EF4; + int32_t field_EF8; + int32_t field_EFC; +}; \ No newline at end of file diff --git a/source/plugins/TLAC/Components/PlayerDataManager.cpp b/source/plugins/TLAC/Components/PlayerDataManager.cpp new file mode 100644 index 0000000..71f794d --- /dev/null +++ b/source/plugins/TLAC/Components/PlayerDataManager.cpp @@ -0,0 +1,150 @@ +#include "PlayerDataManager.h" +#include +#include "../framework.h" +#include "../Constants.h" +#include "../Input/Keyboard/Keyboard.h" +#include "../FileSystem/ConfigFile.h" +#include "../Constants.h" + +const std::string PLAYER_DATA_FILE_NAME = "playerdata.ini"; + +namespace TLAC::Components +{ + PlayerDataManager::PlayerDataManager() + { + } + + PlayerDataManager::~PlayerDataManager() + { + if (customPlayerData != nullptr) + delete customPlayerData; + } + + const char* PlayerDataManager::GetDisplayName() + { + return "player_data_manager"; + } + + void PlayerDataManager::Initialize(ComponentsManager*) + { + playerData = (PlayerData*)PLAYER_DATA_ADDRESS; + + ApplyPatch(); + LoadConfig(); + ApplyCustomData(); + } + + void PlayerDataManager::Update() + { + ApplyCustomData(); + + if (false && Input::Keyboard::GetInstance()->IsTapped(VK_F12)) + { + printf("[TLAC] PlayerDataManager::Update(): Loading config...\n"); + LoadConfig(); + } + } + + void PlayerDataManager::ApplyPatch() + { + DWORD oldProtect; + VirtualProtect((void*)SET_DEFAULT_PLAYER_DATA_ADDRESS, sizeof(uint8_t), PAGE_EXECUTE_READWRITE, &oldProtect); + { + // prevent the PlayerData from being reset + *(uint8_t*)(SET_DEFAULT_PLAYER_DATA_ADDRESS) = RET_OPCODE; + } + VirtualProtect((void*)SET_DEFAULT_PLAYER_DATA_ADDRESS, sizeof(uint8_t), oldProtect, &oldProtect); + + // allow player to select the module and extra item + VirtualProtect((void*)MODSELECTOR_CHECK_FUNCTION_ERRRET_ADDRESS, sizeof(byte) * 2, PAGE_EXECUTE_READWRITE, &oldProtect); + { + *(byte*)(MODSELECTOR_CHECK_FUNCTION_ERRRET_ADDRESS) = 0xB0; // xor al,al -> ld al,1 + *(byte*)(MODSELECTOR_CHECK_FUNCTION_ERRRET_ADDRESS + 0x1) = 0x01; + } + VirtualProtect((void*)MODSELECTOR_CHECK_FUNCTION_ERRRET_ADDRESS, sizeof(byte) * 2, oldProtect, &oldProtect); + + // fix annoying behavior of closing after changing module or item (don't yet know the reason, maybe NW/Card checks) + { + VirtualProtect((void*)MODSELECTOR_CLOSE_AFTER_MODULE, sizeof(uint8_t), PAGE_EXECUTE_READWRITE, &oldProtect); + { + *(uint8_t*)(MODSELECTOR_CLOSE_AFTER_MODULE) = JNE_OPCODE; + } + VirtualProtect((void*)MODSELECTOR_CLOSE_AFTER_MODULE, sizeof(uint8_t), oldProtect, &oldProtect); + VirtualProtect((void*)MODSELECTOR_CLOSE_AFTER_CUSTOMIZE, sizeof(uint8_t), PAGE_EXECUTE_READWRITE, &oldProtect); + { + *(uint8_t*)(MODSELECTOR_CLOSE_AFTER_CUSTOMIZE) = JNE_OPCODE; + } + VirtualProtect((void*)MODSELECTOR_CLOSE_AFTER_CUSTOMIZE, sizeof(uint8_t), oldProtect, &oldProtect); + } + } + + void PlayerDataManager::LoadConfig() + { + if (playerData == nullptr) + return; + + FileSystem::ConfigFile config(framework::GetModuleDirectory(), PLAYER_DATA_FILE_NAME); + + if (!config.OpenRead()) + return; + + if (customPlayerData != nullptr) + delete customPlayerData; + + customPlayerData = new CustomPlayerData(); + config.TryGetValue("player_name", &customPlayerData->PlayerName); + config.TryGetValue("level_name", &customPlayerData->LevelName); + + customPlayerData->LevelPlateId = config.GetIntegerValue("level_plate_id"); + customPlayerData->SkinEquip = config.GetIntegerValue("skin_equip"); + customPlayerData->BtnSeEquip = config.GetIntegerValue("btn_se_equip"); + customPlayerData->SlideSeEquip = config.GetIntegerValue("slide_se_equip"); + customPlayerData->ChainslideSeEquip = config.GetIntegerValue("chainslide_se_equip"); + customPlayerData->ShowExcellentClearBorder = config.GetBooleanValue("border_excellent"); + customPlayerData->ShowGreatClearBorder = config.GetBooleanValue("border_great"); + customPlayerData->UseCard = config.GetBooleanValue("use_card"); + customPlayerData->GameModifierOptions = config.GetBooleanValue("gamemode_options"); + } + + void PlayerDataManager::ApplyCustomData() + { + // don't want to overwrite the default values + auto setIfNotEqual = [](int *target, int value, int comparison) + { + if (value != comparison) + *target = value; + }; + + setIfNotEqual(&playerData->level_plate_id, customPlayerData->LevelPlateId, 0); + setIfNotEqual(&playerData->skin_equip, customPlayerData->SkinEquip, 0); + setIfNotEqual(&playerData->btn_se_equip, customPlayerData->BtnSeEquip, -1); + setIfNotEqual(&playerData->slide_se_equip, customPlayerData->SlideSeEquip, -1); + setIfNotEqual(&playerData->chainslide_se_equip, customPlayerData->ChainslideSeEquip, -1); + + // Display clear borders on the progress bar + *(byte*)(PLAYER_DATA_ADDRESS + 0xD94) = (customPlayerData->ShowExcellentClearBorder << 1) | (customPlayerData->ShowGreatClearBorder); + + if (customPlayerData->UseCard) + playerData->use_card = 1; // required to allow for module selection + + if (customPlayerData->GameModifierOptions) + playerData->game_opts = 1; // hi-speed, etc.. + + memset((void*)MODULE_TABLE_START, 0xFF, 128); + memset((void*)ITEM_TABLE_START, 0xFF, 128); + + if (customPlayerData->PlayerName != nullptr) + { + playerData->field_DC = 0x10; + playerData->player_name = (char*)customPlayerData->PlayerName->c_str(); + } + + + if (customPlayerData->LevelName != nullptr) + { + playerData->level_name = (char*)customPlayerData->LevelName->c_str(); + playerData->field_110 = 0xFF; + playerData->field_118 = 0x1F; + } + } +} diff --git a/source/plugins/TLAC/Components/PlayerDataManager.h b/source/plugins/TLAC/Components/PlayerDataManager.h new file mode 100644 index 0000000..c9122f8 --- /dev/null +++ b/source/plugins/TLAC/Components/PlayerDataManager.h @@ -0,0 +1,29 @@ +#pragma once +#include "EmulatorComponent.h" +#include "PlayerData.h" +#include "CustomPlayerData.h" +#include + +namespace TLAC::Components +{ + class PlayerDataManager : public EmulatorComponent + { + public: + PlayerDataManager(); + ~PlayerDataManager(); + + virtual const char* GetDisplayName() override; + + virtual void Initialize(ComponentsManager*) override; + virtual void Update() override; + + private: + PlayerData* playerData; + CustomPlayerData* customPlayerData; + + void ApplyPatch(); + void LoadConfig(); + void ApplyCustomData(); + }; +} + diff --git a/source/plugins/TLAC/Components/ScaleComponent.cpp b/source/plugins/TLAC/Components/ScaleComponent.cpp new file mode 100644 index 0000000..a7802e2 --- /dev/null +++ b/source/plugins/TLAC/Components/ScaleComponent.cpp @@ -0,0 +1,109 @@ +#include "ScaleComponent.h" +#include +#include +#include "../Constants.h" +#include +#include "../framework.h" +#include +#include +#include +#include + +namespace TLAC::Components +{ + + ScaleComponent::ScaleComponent() + { + } + + ScaleComponent::~ScaleComponent() + { + } + + const char* ScaleComponent::GetDisplayName() + { + return "scale_component"; + } + + void ScaleComponent::Initialize(ComponentsManager*) + { + { + DWORD oldProtect, bck; + VirtualProtect((BYTE*)0x00000001404ACD24, 7, PAGE_EXECUTE_READWRITE, &oldProtect); + *((byte*)0x00000001404ACD24 + 0) = 0x44; + *((byte*)0x00000001404ACD24 + 1) = 0x8B; + *((byte*)0x00000001404ACD24 + 2) = 0x0D; + *((byte*)0x00000001404ACD24 + 3) = 0xD1; + *((byte*)0x00000001404ACD24 + 4) = 0x08; + *((byte*)0x00000001404ACD24 + 5) = 0xD0; + *((byte*)0x00000001404ACD24 + 6) = 0x00; + VirtualProtect((BYTE*)0x00000001404ACD24, 7, oldProtect, &bck); + } + { + DWORD oldProtect, bck; + VirtualProtect((BYTE*)0x00000001404ACD2B, 7, PAGE_EXECUTE_READWRITE, &oldProtect); + *((byte*)0x00000001404ACD2B + 0) = 0x44; + *((byte*)0x00000001404ACD2B + 1) = 0x8B; + *((byte*)0x00000001404ACD2B + 2) = 0x05; + *((byte*)0x00000001404ACD2B + 3) = 0xC6; + *((byte*)0x00000001404ACD2B + 4) = 0x08; + *((byte*)0x00000001404ACD2B + 5) = 0xD0; + *((byte*)0x00000001404ACD2B + 6) = 0x00; + VirtualProtect((BYTE*)0x00000001404ACD2B, 7, oldProtect, &bck); + } + { + DWORD oldProtect, bck; + VirtualProtect((BYTE*)0x00000001405030A0, 6, PAGE_EXECUTE_READWRITE, &oldProtect); + *((byte*)0x00000001405030A0 + 0) = 0x90; + *((byte*)0x00000001405030A0 + 1) = 0x90; + *((byte*)0x00000001405030A0 + 2) = 0x90; + *((byte*)0x00000001405030A0 + 3) = 0x90; + *((byte*)0x00000001405030A0 + 4) = 0x90; + *((byte*)0x00000001405030A0 + 5) = 0x90; + VirtualProtect((BYTE*)0x00000001404ACD2B, 6, oldProtect, &bck); + } + } + + void ScaleComponent::Update() + { + uiAspectRatio = (float*)UI_ASPECT_RATIO; + uiWidth = (float*)UI_WIDTH_ADDRESS; + uiHeight = (float*)UI_HEIGHT_ADDRESS; + fb1Height = (int*)FB1_HEIGHT_ADDRESS; + fb1Width = (int*)FB1_WIDTH_ADDRESS; + //fb2Height = (int*)FB2_HEIGHT_ADDRESS; + //fb2Width = (int*)FB2_WIDTH_ADDRESS; + fbAspectRatio = (double*)FB_ASPECT_RATIO; + RECT hWindow; + GetClientRect(TLAC::framework::DivaWindowHandle, &hWindow); + *uiAspectRatio = (float)(hWindow.right - hWindow.left) / (float)(hWindow.bottom - hWindow.top); + *fbAspectRatio = (double)(hWindow.right - hWindow.left) / (double)(hWindow.bottom - hWindow.top); + *uiWidth = hWindow.right - hWindow.left; + *uiHeight = hWindow.bottom - hWindow.top; + *fb1Width = hWindow.right - hWindow.left; + *fb1Height = hWindow.bottom - hWindow.top; + //*fb2Width = hWindow.right - hWindow.left; + //*fb2Height = hWindow.bottom - hWindow.top; + + *((int*)0x00000001411AD608) = 0; + + *((int*)0x0000000140EDA8E4) = *(int*)0x0000000140EDA8BC; + *((int*)0x0000000140EDA8E8) = *(int*)0x0000000140EDA8C0; + + *(float*)0x00000001411A1900 = 0; + *(float*)0x00000001411A1904 = (float)*(int*)0x0000000140EDA8BC; + *(float*)0x00000001411A1908 = (float)*(int*)0x0000000140EDA8C0; + //*((int*)0x00000001411AD5F8) = hWindow.right - hWindow.left; + //*((int*)0x00000001411AD5FC) = hWindow.bottom - hWindow.top; + + //*((int*)0x00000001411ABB48) = hWindow.right - hWindow.left; + //*((int*)0x00000001411ABB4C) = hWindow.bottom - hWindow.top; + + //*((int*)0x00000001411ABB5C) = (int)(*((int*)0x00000001411ABB54) * ((float)8 / (float)9)); //wtf?? + } + + void ScaleComponent::UpdateInput() + { + return; + } +} \ No newline at end of file diff --git a/source/plugins/TLAC/Components/ScaleComponent.h b/source/plugins/TLAC/Components/ScaleComponent.h new file mode 100644 index 0000000..fb25f4f --- /dev/null +++ b/source/plugins/TLAC/Components/ScaleComponent.h @@ -0,0 +1,35 @@ +#pragma once +#include "EmulatorComponent.h" +#include "GameState.h" + +namespace TLAC::Components +{ + class ScaleComponent : public EmulatorComponent + { + public: + ScaleComponent(); + ~ScaleComponent(); + + virtual const char* GetDisplayName() override; + + virtual void Initialize(ComponentsManager*) override; + virtual void Update() override; + virtual void UpdateInput() override; + + const int updatesPerFrame = 39; + + float* uiAspectRatio; + float* uiWidth; + float* uiHeight; + + int* fb1Width; + int* fb1Height; + int* fb2Width; + int* fb2Height; + + double* fbAspectRatio; + + private: + + }; +} \ No newline at end of file diff --git a/source/plugins/TLAC/Components/StageManager.cpp b/source/plugins/TLAC/Components/StageManager.cpp new file mode 100644 index 0000000..91edff9 --- /dev/null +++ b/source/plugins/TLAC/Components/StageManager.cpp @@ -0,0 +1,41 @@ +#include "StageManager.h" +#include "../Constants.h" +#include + +namespace TLAC::Components +{ + StageManager::StageManager() + { + } + + StageManager::~StageManager() + { + } + + const char* StageManager::GetDisplayName() + { + return "stage_manager"; + } + + void StageManager::Initialize(ComponentsManager*) + { + // add the offset between moving 2 into ebx and the start of the function + int32_t* playCount = (int32_t*)(((uint8_t*)PLAYS_PER_SESSION_GETTER_ADDRESS) + 0x7); + + DWORD oldProtect; + VirtualProtect(playCount, sizeof(int32_t), PAGE_EXECUTE_READWRITE, &oldProtect); + + // set ’ʏ탂�[ƒh per session play count + *playCount = GetPlayCount(); + } + + void StageManager::Update() + { + return; + } + + int32_t StageManager::GetPlayCount() + { + return INT32_MAX; + } +} \ No newline at end of file diff --git a/source/plugins/TLAC/Components/StageManager.h b/source/plugins/TLAC/Components/StageManager.h new file mode 100644 index 0000000..c6ae946 --- /dev/null +++ b/source/plugins/TLAC/Components/StageManager.h @@ -0,0 +1,21 @@ +#pragma once +#include "EmulatorComponent.h" +#include + +namespace TLAC::Components +{ + class StageManager : public EmulatorComponent + { + public: + StageManager(); + ~StageManager(); + + virtual const char* GetDisplayName() override; + + virtual void Initialize(ComponentsManager*) override; + virtual void Update() override; + + private: + int32_t GetPlayCount(); + }; +} \ No newline at end of file diff --git a/source/plugins/TLAC/Components/SysTimer.cpp b/source/plugins/TLAC/Components/SysTimer.cpp new file mode 100644 index 0000000..def84db --- /dev/null +++ b/source/plugins/TLAC/Components/SysTimer.cpp @@ -0,0 +1,34 @@ +#include "SysTimer.h" +#include "../Constants.h" + +namespace TLAC::Components +{ + SysTimer::SysTimer() + { + } + + SysTimer::~SysTimer() + { + } + + const char* SysTimer::GetDisplayName() + { + return "sys_timer"; + } + + void SysTimer::Initialize(ComponentsManager*) + { + selPvTime = GetSysTimePtr((void*)SEL_PV_TIME_ADDRESS); + } + + void SysTimer::Update() + { + // account for the decrement that occures during this frame + *selPvTime = SEL_PV_FREEZE_TIME * SYS_TIME_FACTOR + 1; + } + + int* SysTimer::GetSysTimePtr(void *address) + { + return (int*)address; + } +} diff --git a/source/plugins/TLAC/Components/SysTimer.h b/source/plugins/TLAC/Components/SysTimer.h new file mode 100644 index 0000000..5f78dff --- /dev/null +++ b/source/plugins/TLAC/Components/SysTimer.h @@ -0,0 +1,25 @@ +#pragma once +#include "EmulatorComponent.h" + +namespace TLAC::Components +{ + class SysTimer : public EmulatorComponent + { + const int SYS_TIME_FACTOR = 60; + const int SEL_PV_FREEZE_TIME = 39; + + public: + SysTimer(); + ~SysTimer(); + + virtual const char* GetDisplayName() override; + + virtual void Initialize(ComponentsManager*) override; + virtual void Update() override; + + private: + int* selPvTime; + int* GetSysTimePtr(void *address); + }; +} + diff --git a/source/plugins/TLAC/Constants.h b/source/plugins/TLAC/Constants.h new file mode 100644 index 0000000..7796b7f --- /dev/null +++ b/source/plugins/TLAC/Constants.h @@ -0,0 +1,102 @@ +#pragma once +#include + +constexpr uint8_t NOP_OPCODE = 0x90; +constexpr uint8_t RET_OPCODE = 0xC3; +constexpr uint8_t JMP_OPCODE = 0xE9; +constexpr uint8_t JNE_OPCODE = 0x85; + +//constexpr uint64_t ENGINE_UPDATE_HOOK_TARGET_ADDRESS = 0x000000014018CC40; +constexpr uint64_t ENGINE_UPDATE_INPUT_ADDRESS = 0x000000014018CBB0; + +constexpr uint64_t CURRENT_GAME_STATE_ADDRESS = 0x0000000140EDA810; +constexpr uint64_t RESOLUTION_WIDTH_ADDRESS = 0x0000000140EDA8BC; +constexpr uint64_t RESOLUTION_HEIGHT_ADDRESS = 0x0000000140EDA8C0; + +constexpr uint64_t SYSTEM_WARNING_ELAPSED_ADDRESS = (0x00000001411A1430 + 0x68); +constexpr uint64_t DATA_INIT_STATE_ADDRESS = 0x0000000140EDA7A8; + +constexpr uint64_t AET_FRAME_DURATION_ADDRESS = 0x00000001409A0A58; +constexpr uint64_t PV_FRAME_RATE_ADDRESS = 0x0000000140EDA7CC; +constexpr uint64_t FRAME_SPEED_ADDRESS = 0x0000000140EDA798; +constexpr uint64_t FRAME_RATE_ADDRESS = 0x0000000140EDA6D0; + +constexpr uint64_t DW_GUI_DISPLAY_INSTANCE_PTR_ADDRESS = 0x0000000141190108; +constexpr uint64_t INPUT_STATE_PTR_ADDRESS = 0x0000000140EDA330; +constexpr uint64_t SLIDER_CTRL_TASK_ADDRESS = 0x000000014CC5DE40; +constexpr uint64_t TASK_TOUCH_ADDRESS = 0x000000014CC9EC30; +constexpr uint64_t SEL_PV_TIME_ADDRESS = 0x000000014CC12498; +constexpr uint64_t PLAYER_DATA_ADDRESS = 0x00000001411A8850; +constexpr uint64_t SET_DEFAULT_PLAYER_DATA_ADDRESS = 0x00000001404A7370; +constexpr uint64_t PLAYS_PER_SESSION_GETTER_ADDRESS = 0x000000014038AEE0; +constexpr uint64_t PV_SEL_SLOTS_TO_SCROLL = 0x000000014CC12470; +constexpr uint64_t PV_SEL_SLOTS_CONST = 0x000000014CC119C8; +constexpr uint64_t MODULE_SEL_SLOTS_TO_SCROLL = 0x00000001418047EC; +constexpr uint64_t MODULE_IS_RECOMMENDED = 0x00000001418047E0; + +constexpr uint64_t CAMERA_ADDRESS = 0x0000000140FBC2C0; +constexpr uint64_t CAMERA_POS_SETTER_ADDRESS = 0x00000001401F9460; +constexpr uint64_t CAMERA_INTR_SETTER_ADDRESS = 0x00000001401F93F0; +constexpr uint64_t CAMERA_ROT_SETTER_ADDRESS = 0x00000001401F9480; +constexpr uint64_t CAMERA_PERS_SETTER_ADDRESS = 0x00000001401F9430; + +constexpr uint64_t UPDATE_TASKS_ADDRESS = 0x000000014019B980; +constexpr uint64_t GLUT_SET_CURSOR_ADDRESS = 0x00000001408B68E6; +constexpr uint64_t CHANGE_MODE_ADDRESS = 0x00000001401953D0; +constexpr uint64_t CHANGE_SUB_MODE_ADDRESS = 0x0000000140195260; + +constexpr uint64_t TGT_TYPE_BASE_ADDRESS = 0x0000000140D0B69C; +constexpr uint64_t TGT_REMAINING_DURATION_BASE_ADDRESS = 0x0000000140D0B6A0; +constexpr uint64_t TGT_HIT_STATE_BASE_ADDRESS = 0x0000000140D0BAE4; +constexpr uint64_t TGT_ON_SCREEN_ADDRESS = 0x0000000140D0B678; + +constexpr uint64_t HOLD_STATE_ADDRESS = 0x0000000140D1E20C; +constexpr uint64_t MAX_HOLD_STATE_ADDRESS = 0x0000000140D1E234; + +constexpr uint64_t CHECK_SOMETHING_SET_MODULE_ADDRESS = 0x0000000140581C78; +constexpr uint64_t MODSELECTOR_CHECK_FUNCTION_ERRRET_ADDRESS = 0x00000001405869AD; +constexpr uint64_t MODSELECTOR_CLOSE_AFTER_MODULE = 0x0000000140583B45; +constexpr uint64_t MODSELECTOR_CLOSE_AFTER_CUSTOMIZE = 0x0000000140583C8C; +constexpr uint64_t MODULE_TABLE_START = PLAYER_DATA_ADDRESS + 0x140; +constexpr uint64_t MODULE_TABLE_END = MODULE_TABLE_START + 128; +constexpr uint64_t ITEM_TABLE_START = PLAYER_DATA_ADDRESS + 0x2B8; +constexpr uint64_t ITEM_TABLE_END = ITEM_TABLE_START + 128; + +constexpr uint64_t FB_WIDTH_ADDRESS = 0x00000001411ABCA8; +constexpr uint64_t FB_HEIGHT_ADDRESS = 0x00000001411ABCAC; +constexpr uint64_t FB1_WIDTH_ADDRESS = 0x00000001411AD5F8; +constexpr uint64_t FB1_HEIGHT_ADDRESS = 0x00000001411AD5FC; +constexpr uint64_t FB2_WIDTH_ADDRESS = 0x0000000140EDA8E4; +constexpr uint64_t FB2_HEIGHT_ADDRESS = 0x0000000140EDA8E8; + +constexpr uint64_t FB_RESOLUTION_WIDTH_ADDRESS = 0x00000001411ABB50; +constexpr uint64_t FB_RESOLUTION_HEIGHT_ADDRESS = 0x00000001411ABB54; + +constexpr uint64_t UI_WIDTH_ADDRESS = 0x000000014CC621E4; +constexpr uint64_t UI_HEIGHT_ADDRESS = 0x000000014CC621E8; + +constexpr uint64_t FB_ASPECT_RATIO = 0x0000000140FBC2E8; +constexpr uint64_t UI_ASPECT_RATIO = 0x000000014CC621D0; + +#define XINPUT_A 0x00 +#define XINPUT_B 0x01 +#define XINPUT_X 0x02 +#define XINPUT_Y 0x03 +#define XINPUT_UP 0x10 +#define XINPUT_DOWN 0x11 +#define XINPUT_LEFT 0x12 +#define XINPUT_RIGHT 0x13 +#define XINPUT_LS 0x20 +#define XINPUT_RS 0x21 +#define XINPUT_LT 0x22 +#define XINPUT_RT 0x23 +#define XINPUT_START 0x30 +#define XINPUT_BACK 0x31 +#define XINPUT_LUP 0x40 +#define XINPUT_LDOWN 0x41 +#define XINPUT_LLEFT 0x42 +#define XINPUT_LRIGHT 0x43 +#define XINPUT_RUP 0x50 +#define XINPUT_RDOWN 0x51 +#define XINPUT_RLEFT 0x52 +#define XINPUT_RRIGHT 0x53 \ No newline at end of file diff --git a/source/plugins/TLAC/FileSystem/ConfigFile.cpp b/source/plugins/TLAC/FileSystem/ConfigFile.cpp new file mode 100644 index 0000000..63bf0f3 --- /dev/null +++ b/source/plugins/TLAC/FileSystem/ConfigFile.cpp @@ -0,0 +1,72 @@ +#include +#include "ConfigFile.h" +#include "../Utilities/Operations.h" + +namespace TLAC::FileSystem +{ + ConfigFile::ConfigFile(const std::string &path) : TextFile(path) + { + return; + } + + ConfigFile::ConfigFile(const std::string &directory, const std::string &file) : TextFile(directory, file) + { + return; + } + + bool ConfigFile::TryGetValue(const std::string &key, std::string **value) + { + auto pair = ConfigMap.find(key); + bool found = pair != ConfigMap.end(); + + *value = found ? new std::string(pair->second) : nullptr; + return found; + } + + int ConfigFile::GetIntegerValue(const std::string& key) + { + auto pair = ConfigMap.find(key); + bool found = pair != ConfigMap.end(); + + return found ? atoi(pair->second.c_str()) : 0; + } + + bool ConfigFile::GetBooleanValue(const std::string& key) + { + auto pair = ConfigMap.find(key); + bool found = pair != ConfigMap.end(); + + return found ? pair->second == "true" : false; + } + + float ConfigFile::GetFloatValue(const std::string & key) + { + auto pair = ConfigMap.find(key); + bool found = pair != ConfigMap.end(); + + return found ? (float)atof(pair->second.c_str()) : 0.0f; + } + + void ConfigFile::Parse(std::ifstream &fileStream) + { + std::string line; + + while (std::getline(fileStream, line)) + { + if (IsComment(line)) + continue; + + auto splitline = Utilities::Split(line, "="); + + for (auto &line : splitline) + Utilities::Trim(line); + + ConfigMap.insert(std::make_pair(splitline[0], splitline[1])); + } + } + + bool ConfigFile::IsComment(const std::string &line) + { + return line.size() <= 0 || line[0] == '#' || line._Starts_with("//"); + } +} diff --git a/source/plugins/TLAC/FileSystem/ConfigFile.h b/source/plugins/TLAC/FileSystem/ConfigFile.h new file mode 100644 index 0000000..8f453ea --- /dev/null +++ b/source/plugins/TLAC/FileSystem/ConfigFile.h @@ -0,0 +1,28 @@ +#pragma once + +#include "TextFile.h" +#include + +namespace TLAC::FileSystem +{ + class ConfigFile : public TextFile + { + public: + ConfigFile(const std::string &path); + ConfigFile(const std::string &directory, const std::string &file); + + std::unordered_map ConfigMap; + + bool TryGetValue(const std::string &key, std::string **value); + int GetIntegerValue(const std::string& key); + bool GetBooleanValue(const std::string& key); + float GetFloatValue(const std::string& key); + + protected: + virtual void Parse(std::ifstream &fileStream) override; + + private: + bool IsComment(const std::string &line); + }; +} + diff --git a/source/plugins/TLAC/FileSystem/TextFile.cpp b/source/plugins/TLAC/FileSystem/TextFile.cpp new file mode 100644 index 0000000..b58104c --- /dev/null +++ b/source/plugins/TLAC/FileSystem/TextFile.cpp @@ -0,0 +1,41 @@ +#include "TextFile.h" +#include + +namespace fs = std::filesystem; + +namespace TLAC::FileSystem +{ + TextFile::TextFile(const std::string &path) + { + FileName = path; + } + + TextFile::TextFile(const std::string &directory, const std::string &file) + { + auto fullPath = directory + "/" + file; + FileName = fullPath; + } + + TextFile::~TextFile() + { + } + + bool TextFile::OpenRead() + { + fs::path configPath = fs::u8path(FileName); + + if (!fs::exists(configPath)) + return false; + + std::ifstream fileStream(configPath.wstring().c_str()); + + if (!fileStream.is_open()) + return false; + + Parse(fileStream); + + fileStream.close(); + + return true; + } +} diff --git a/source/plugins/TLAC/FileSystem/TextFile.h b/source/plugins/TLAC/FileSystem/TextFile.h new file mode 100644 index 0000000..a2a24fd --- /dev/null +++ b/source/plugins/TLAC/FileSystem/TextFile.h @@ -0,0 +1,22 @@ +#pragma once + +#include +#include + +namespace TLAC::FileSystem +{ + class TextFile + { + public: + std::string FileName; + + TextFile(const std::string &path); + TextFile(const std::string &directory, const std::string &file); + ~TextFile(); + + bool OpenRead(); + + protected: + virtual void Parse(std::ifstream &fileStream) = 0; + }; +} diff --git a/source/plugins/TLAC/Input/Bindings/Binding.cpp b/source/plugins/TLAC/Input/Bindings/Binding.cpp new file mode 100644 index 0000000..9d24e90 --- /dev/null +++ b/source/plugins/TLAC/Input/Bindings/Binding.cpp @@ -0,0 +1,91 @@ +#include "Binding.h" + +namespace TLAC::Input +{ + Binding::Binding() + { + } + + Binding::~Binding() + { + for (auto& binding : InputBindings) + delete binding; + } + + void Binding::AddBinding(IInputBinding* inputBinding) + { + InputBindings.push_back(inputBinding); + } + + bool Binding::AnyDown() + { + for (const auto& binding : InputBindings) + { + if (binding->IsDown()) + return true; + } + + return false; + } + + bool Binding::AnyTapped() + { + for (const auto& binding : InputBindings) + { + if (binding->IsTapped()) + return true; + } + + return false; + } + + bool Binding::AnyReleased() + { + for (const auto& binding : InputBindings) + { + if (binding->IsReleased()) + return true; + } + + return false; + } + + int Binding::GetDownCount() + { + int count = 0; + + for (const auto& binding : InputBindings) + { + if (binding->IsDown()) + count++; + } + + return count; + } + + int Binding::GetTappedCount() + { + int count = 0; + + for (const auto& binding : InputBindings) + { + if (binding->IsTapped()) + count++; + } + + return count; + } + + int Binding::GetReleasedCount() + { + int count = 0; + + for (const auto& binding : InputBindings) + { + if (binding->IsReleased()) + count++; + } + + return count; + } +} diff --git a/source/plugins/TLAC/Input/Bindings/Binding.h b/source/plugins/TLAC/Input/Bindings/Binding.h new file mode 100644 index 0000000..359c993 --- /dev/null +++ b/source/plugins/TLAC/Input/Bindings/Binding.h @@ -0,0 +1,26 @@ +#pragma once +#include +#include "IInputBinding.h" + +namespace TLAC::Input +{ + class Binding + { + public: + std::vector InputBindings; + + Binding(); + ~Binding(); + + void AddBinding(IInputBinding* inputBinding); + + bool AnyDown(); + bool AnyTapped(); + bool AnyReleased(); + + int GetDownCount(); + int GetTappedCount(); + int GetReleasedCount(); + }; +} + diff --git a/source/plugins/TLAC/Input/Bindings/Ds4Binding.cpp b/source/plugins/TLAC/Input/Bindings/Ds4Binding.cpp new file mode 100644 index 0000000..63f1300 --- /dev/null +++ b/source/plugins/TLAC/Input/Bindings/Ds4Binding.cpp @@ -0,0 +1,29 @@ +#include "Ds4Binding.h" + +namespace TLAC::Input +{ + #define Ds4InstanceCheckDefault(checkFunc) (DualShock4::InstanceInitialized() ? DualShock4::GetInstance()->checkFunc : false) + + Ds4Binding::Ds4Binding(Ds4Button button) : Button(button) + { + } + + Ds4Binding::~Ds4Binding() + { + } + + bool Ds4Binding::IsDown() + { + return Ds4InstanceCheckDefault(IsDown(Button)); + } + + bool Ds4Binding::IsTapped() + { + return Ds4InstanceCheckDefault(IsTapped(Button)); + } + + bool Ds4Binding::IsReleased() + { + return Ds4InstanceCheckDefault(IsReleased(Button)); + } +} \ No newline at end of file diff --git a/source/plugins/TLAC/Input/Bindings/Ds4Binding.h b/source/plugins/TLAC/Input/Bindings/Ds4Binding.h new file mode 100644 index 0000000..3cdd7cf --- /dev/null +++ b/source/plugins/TLAC/Input/Bindings/Ds4Binding.h @@ -0,0 +1,19 @@ +#pragma once +#include "IInputBinding.h" +#include "../DirectInput/Ds4/DualShock4.h" + +namespace TLAC::Input +{ + class Ds4Binding : public IInputBinding + { + public: + Ds4Button Button; + + Ds4Binding(Ds4Button button); + ~Ds4Binding(); + + bool IsDown() override; + bool IsTapped() override; + bool IsReleased() override; + }; +} \ No newline at end of file diff --git a/source/plugins/TLAC/Input/Bindings/IInputBinding.h b/source/plugins/TLAC/Input/Bindings/IInputBinding.h new file mode 100644 index 0000000..54a3bf7 --- /dev/null +++ b/source/plugins/TLAC/Input/Bindings/IInputBinding.h @@ -0,0 +1,12 @@ +#pragma once + +namespace TLAC::Input +{ + class IInputBinding + { + public: + virtual bool IsDown() = 0; + virtual bool IsTapped() = 0; + virtual bool IsReleased() = 0; + }; +} diff --git a/source/plugins/TLAC/Input/Bindings/KeyboardBinding.cpp b/source/plugins/TLAC/Input/Bindings/KeyboardBinding.cpp new file mode 100644 index 0000000..5ac7cc4 --- /dev/null +++ b/source/plugins/TLAC/Input/Bindings/KeyboardBinding.cpp @@ -0,0 +1,27 @@ +#include "KeyboardBinding.h" + +namespace TLAC::Input +{ + KeyboardBinding::KeyboardBinding(BYTE keycode) : Keycode(keycode) + { + } + + KeyboardBinding::~KeyboardBinding() + { + } + + bool KeyboardBinding::IsDown() + { + return Keyboard::GetInstance()->IsDown(Keycode); + } + + bool KeyboardBinding::IsTapped() + { + return Keyboard::GetInstance()->IsTapped(Keycode); + } + + bool KeyboardBinding::IsReleased() + { + return Keyboard::GetInstance()->IsReleased(Keycode); + } +} diff --git a/source/plugins/TLAC/Input/Bindings/KeyboardBinding.h b/source/plugins/TLAC/Input/Bindings/KeyboardBinding.h new file mode 100644 index 0000000..0083b9e --- /dev/null +++ b/source/plugins/TLAC/Input/Bindings/KeyboardBinding.h @@ -0,0 +1,19 @@ +#pragma once +#include "IInputBinding.h" +#include "../Keyboard/Keyboard.h" + +namespace TLAC::Input +{ + class KeyboardBinding : public IInputBinding + { + public: + BYTE Keycode; + + KeyboardBinding(BYTE keycode); + ~KeyboardBinding(); + + bool IsDown() override; + bool IsTapped() override; + bool IsReleased() override; + }; +} diff --git a/source/plugins/TLAC/Input/Bindings/MouseBinding.cpp b/source/plugins/TLAC/Input/Bindings/MouseBinding.cpp new file mode 100644 index 0000000..0331db2 --- /dev/null +++ b/source/plugins/TLAC/Input/Bindings/MouseBinding.cpp @@ -0,0 +1,70 @@ +#include "MouseBinding.h" +#include "../Keyboard/Keyboard.h" + +namespace TLAC::Input +{ + MouseBinding::MouseBinding(MouseAction action) : Action(action) + { + } + + MouseBinding::~MouseBinding() + { + } + + bool MouseBinding::IsDown() + { + switch (Action) + { + case MouseAction_LeftButton: + return Keyboard::GetInstance()->IsDown(MK_LBUTTON); + case MouseAction_RightButton: + return Keyboard::GetInstance()->IsDown(MK_RBUTTON); + case MouseAction_MiddleButton: + return Keyboard::GetInstance()->IsDown(MK_MBUTTON); + case MouseAction_ScrollUp: + return Mouse::GetInstance()->GetIsScrolledUp(); + case MouseAction_ScrollDown: + return Mouse::GetInstance()->GetIsScrolledDown(); + default: + return false; + } + } + + bool MouseBinding::IsTapped() + { + switch (Action) + { + case MouseAction_LeftButton: + return Keyboard::GetInstance()->IsTapped(MK_LBUTTON); + case MouseAction_RightButton: + return Keyboard::GetInstance()->IsTapped(MK_RBUTTON); + case MouseAction_MiddleButton: + return Keyboard::GetInstance()->IsTapped(MK_MBUTTON); + case MouseAction_ScrollUp: + return Mouse::GetInstance()->GetIsScrolledUp(); + case MouseAction_ScrollDown: + return Mouse::GetInstance()->GetIsScrolledDown(); + default: + return false; + } + } + + bool MouseBinding::IsReleased() + { + switch (Action) + { + case MouseAction_LeftButton: + return Keyboard::GetInstance()->IsReleased(MK_LBUTTON); + case MouseAction_RightButton: + return Keyboard::GetInstance()->IsReleased(MK_RBUTTON); + case MouseAction_MiddleButton: + return Keyboard::GetInstance()->IsReleased(MK_MBUTTON); + case MouseAction_ScrollUp: + return Mouse::GetInstance()->GetWasScrolledUp(); + case MouseAction_ScrollDown: + return Mouse::GetInstance()->GetWasScrolledDown(); + default: + return false; + } + } +} diff --git a/source/plugins/TLAC/Input/Bindings/MouseBinding.h b/source/plugins/TLAC/Input/Bindings/MouseBinding.h new file mode 100644 index 0000000..f236c9d --- /dev/null +++ b/source/plugins/TLAC/Input/Bindings/MouseBinding.h @@ -0,0 +1,28 @@ +#pragma once +#include "IInputBinding.h" +#include "../Mouse/Mouse.h" + +namespace TLAC::Input +{ + enum MouseAction + { + MouseAction_LeftButton, + MouseAction_RightButton, + MouseAction_MiddleButton, + MouseAction_ScrollUp, + MouseAction_ScrollDown, + }; + + class MouseBinding : public IInputBinding + { + public: + MouseAction Action; + + MouseBinding(MouseAction action); + ~MouseBinding(); + + bool IsDown() override; + bool IsTapped() override; + bool IsReleased() override; + }; +} \ No newline at end of file diff --git a/source/plugins/TLAC/Input/Bindings/XinputBinding.cpp b/source/plugins/TLAC/Input/Bindings/XinputBinding.cpp new file mode 100644 index 0000000..d00eaee --- /dev/null +++ b/source/plugins/TLAC/Input/Bindings/XinputBinding.cpp @@ -0,0 +1,33 @@ +#include "XinputBinding.h" +#include "..\Xinput\Xinput.h" + +namespace TLAC::Input +{ + XinputBinding::XinputBinding(BYTE keycode) : Keycode(keycode) + { + } + + XinputBinding::~XinputBinding() + { + } + + bool XinputBinding::IsDown() + { + return Xinput::GetInstance()->IsDown(Keycode); + } + + bool XinputBinding::IsTapped() + { + return Xinput::GetInstance()->IsTapped(Keycode); + } + + bool XinputBinding::IsReleased() + { + return Xinput::GetInstance()->IsReleased(Keycode); + } + + bool XinputBinding::IsDoubleTapped() + { + return Xinput::GetInstance()->IsDoubleTapped(Keycode); + } +} \ No newline at end of file diff --git a/source/plugins/TLAC/Input/Bindings/XinputBinding.h b/source/plugins/TLAC/Input/Bindings/XinputBinding.h new file mode 100644 index 0000000..efe5ee6 --- /dev/null +++ b/source/plugins/TLAC/Input/Bindings/XinputBinding.h @@ -0,0 +1,21 @@ +#pragma once +#include "IInputBinding.h" +#include +#include + +namespace TLAC::Input +{ + class XinputBinding : public IInputBinding + { + public: + BYTE Keycode; + + XinputBinding(BYTE keycode); + ~XinputBinding(); + + bool IsDown() override; + bool IsTapped() override; + bool IsReleased() override; + bool IsDoubleTapped(); + }; +} \ No newline at end of file diff --git a/source/plugins/TLAC/Input/DirectInput/Controller.cpp b/source/plugins/TLAC/Input/DirectInput/Controller.cpp new file mode 100644 index 0000000..b91bb6c --- /dev/null +++ b/source/plugins/TLAC/Input/DirectInput/Controller.cpp @@ -0,0 +1,13 @@ +#include "Controller.h" + +namespace TLAC::Input +{ + Controller::Controller() + { + } + + Controller::~Controller() + { + + } +} \ No newline at end of file diff --git a/source/plugins/TLAC/Input/DirectInput/Controller.h b/source/plugins/TLAC/Input/DirectInput/Controller.h new file mode 100644 index 0000000..7ea3d2f --- /dev/null +++ b/source/plugins/TLAC/Input/DirectInput/Controller.h @@ -0,0 +1,12 @@ +#pragma once +#include "DirectInputDevice.h" + +namespace TLAC::Input +{ + class Controller : public DirectInputDevice + { + protected: + Controller(); + ~Controller(); + }; +} \ No newline at end of file diff --git a/source/plugins/TLAC/Input/DirectInput/DirectInput.cpp b/source/plugins/TLAC/Input/DirectInput/DirectInput.cpp new file mode 100644 index 0000000..cda427c --- /dev/null +++ b/source/plugins/TLAC/Input/DirectInput/DirectInput.cpp @@ -0,0 +1,26 @@ +#include "DirectInput.h" + +namespace TLAC::Input +{ + IDirectInput8 *IDirectInputInstance = nullptr; + + HRESULT InitializeDirectInput(HMODULE module) + { + HRESULT result = DirectInput8Create(module, DIRECTINPUT_VERSION, IID_IDirectInput8, (VOID**)&IDirectInputInstance, nullptr); + return result; + } + + bool DirectInputInitialized() + { + return IDirectInputInstance != nullptr; + } + + void DisposeDirectInput() + { + if (IDirectInputInstance == nullptr) + return; + + IDirectInputInstance->Release(); + IDirectInputInstance = nullptr; + } +} \ No newline at end of file diff --git a/source/plugins/TLAC/Input/DirectInput/DirectInput.h b/source/plugins/TLAC/Input/DirectInput/DirectInput.h new file mode 100644 index 0000000..26942b2 --- /dev/null +++ b/source/plugins/TLAC/Input/DirectInput/DirectInput.h @@ -0,0 +1,12 @@ +#pragma once +#define DIRECTINPUT_VERSION 0x0800 +#include + +namespace TLAC::Input +{ + extern IDirectInput8 *IDirectInputInstance; + + HRESULT InitializeDirectInput(HMODULE module); + bool DirectInputInitialized(); + void DisposeDirectInput(); +} \ No newline at end of file diff --git a/source/plugins/TLAC/Input/DirectInput/DirectInputDevice.cpp b/source/plugins/TLAC/Input/DirectInput/DirectInputDevice.cpp new file mode 100644 index 0000000..85dbd12 --- /dev/null +++ b/source/plugins/TLAC/Input/DirectInput/DirectInputDevice.cpp @@ -0,0 +1,66 @@ +#include "DirectInputDevice.h" + +namespace TLAC::Input +{ + HRESULT DirectInputDevice::DI_CreateDevice(const GUID &guid) + { + if (!DirectInputInitialized()) + return DIERR_NOTINITIALIZED; + + HRESULT result = IDirectInputInstance->CreateDevice(guid, &directInputdevice, NULL); + return result; + } + + HRESULT DirectInputDevice::DI_SetDataFormat(LPCDIDATAFORMAT dataFormat) + { + HRESULT result = directInputdevice->SetDataFormat(dataFormat); + return result; + } + + HRESULT DirectInputDevice::DI_SetCooperativeLevel(HWND windowHandle, DWORD flags) + { + HRESULT result = directInputdevice->SetCooperativeLevel(windowHandle, flags); + return result; + } + + HRESULT DirectInputDevice::DI_Acquire() + { + HRESULT result = directInputdevice->Acquire(); + return result; + } + + HRESULT DirectInputDevice::DI_Unacquire() + { + HRESULT result = directInputdevice->Unacquire(); + return result; + } + + HRESULT DirectInputDevice::DI_Release() + { + HRESULT result = directInputdevice->Release(); + return result; + } + + HRESULT DirectInputDevice::DI_Poll() + { + HRESULT result = directInputdevice->Poll(); + return result; + } + + HRESULT DirectInputDevice::DI_GetDeviceState(DWORD size, LPVOID data) + { + HRESULT result = directInputdevice->GetDeviceState(size, data); + return result; + } + + void DirectInputDevice::DI_Dispose() + { + if (directInputdevice == nullptr) + return; + + HRESULT result = NULL; + + result = DI_Unacquire(); + result = DI_Release(); + } +} \ No newline at end of file diff --git a/source/plugins/TLAC/Input/DirectInput/DirectInputDevice.h b/source/plugins/TLAC/Input/DirectInput/DirectInputDevice.h new file mode 100644 index 0000000..1579413 --- /dev/null +++ b/source/plugins/TLAC/Input/DirectInput/DirectInputDevice.h @@ -0,0 +1,22 @@ +#pragma once +#include "DirectInput.h" + +namespace TLAC::Input +{ + class DirectInputDevice + { + protected: + IDirectInputDevice8 *directInputdevice; + + HRESULT DI_CreateDevice(const GUID& guid); + HRESULT DI_SetDataFormat(LPCDIDATAFORMAT dataFormat); + HRESULT DI_SetCooperativeLevel(HWND windowHandle, DWORD flags); + HRESULT DI_Acquire(); + HRESULT DI_Unacquire(); + HRESULT DI_Release(); + HRESULT DI_Poll(); + HRESULT DI_GetDeviceState(DWORD size, LPVOID data); + + void DI_Dispose(); + }; +} \ No newline at end of file diff --git a/source/plugins/TLAC/Input/DirectInput/DirectInputMouse.cpp b/source/plugins/TLAC/Input/DirectInput/DirectInputMouse.cpp new file mode 100644 index 0000000..b3dd6b3 --- /dev/null +++ b/source/plugins/TLAC/Input/DirectInput/DirectInputMouse.cpp @@ -0,0 +1,35 @@ +#include "DirectInputMouse.h" + +namespace TLAC::Input +{ + DirectInputMouse::DirectInputMouse() + { + HRESULT result = NULL; + + result = DI_CreateDevice(GUID_SysMouse); + + if (FAILED(result)) + return; + + result = DI_SetDataFormat(&c_dfDIMouse); + result = DI_Acquire(); + } + + DirectInputMouse::~DirectInputMouse() + { + DI_Dispose(); + } + + bool DirectInputMouse::Poll() + { + if (!DirectInputInitialized()) + return FALSE; + + HRESULT result = NULL; + + result = DI_Poll(); + result = DI_GetDeviceState(sizeof(mouseState), &mouseState); + + return !FAILED(result); + } +} \ No newline at end of file diff --git a/source/plugins/TLAC/Input/DirectInput/DirectInputMouse.h b/source/plugins/TLAC/Input/DirectInput/DirectInputMouse.h new file mode 100644 index 0000000..24a64b6 --- /dev/null +++ b/source/plugins/TLAC/Input/DirectInput/DirectInputMouse.h @@ -0,0 +1,21 @@ +#pragma once +#include "DirectInputDevice.h" + +namespace TLAC::Input +{ + class DirectInputMouse : public DirectInputDevice + { + public: + DirectInputMouse(); + ~DirectInputMouse(); + + bool Poll(); + + inline long GetXPosition() { return mouseState.lX; }; + inline long GetYPosition() { return mouseState.lY; }; + inline long GetMouseWheel() { return mouseState.lZ; }; + + private: + DIMOUSESTATE mouseState; + }; +} \ No newline at end of file diff --git a/source/plugins/TLAC/Input/DirectInput/Ds4/Ds4Button.h b/source/plugins/TLAC/Input/DirectInput/Ds4/Ds4Button.h new file mode 100644 index 0000000..b9e16a0 --- /dev/null +++ b/source/plugins/TLAC/Input/DirectInput/Ds4/Ds4Button.h @@ -0,0 +1,52 @@ +#pragma once + +namespace TLAC::Input +{ + enum Direction + { + DIR_UP, + DIR_RIGHT, + DIR_DOWN, + DIR_LEFT + }; + + enum Ds4Button : int + { + DS4_SQUARE = 0, + DS4_CROSS = 1, + DS4_CIRCLE = 2, + DS4_TRIANGLE = 3, + + DS4_L1 = 4, + DS4_R1 = 5, + + DS4_L_TRIGGER = 6, + DS4_R_TRIGGER = 7, + + DS4_SHARE = 8, + DS4_OPTIONS = 9, + + DS4_L3 = 10, + DS4_R3 = 11, + + DS4_PS = 12, + DS4_TOUCH = 13, + + DS4_DPAD_UP, + DS4_DPAD_RIGHT, + DS4_DPAD_DOWN, + DS4_DPAD_LEFT, + + DS4_L_STICK_UP, + DS4_L_STICK_RIGHT, + DS4_L_STICK_DOWN, + DS4_L_STICK_LEFT, + + DS4_R_STICK_UP, + DS4_R_STICK_RIGHT, + DS4_R_STICK_DOWN, + DS4_R_STICK_LEFT, + + DS4_BUTTON_MAX, + }; +} \ No newline at end of file diff --git a/source/plugins/TLAC/Input/DirectInput/Ds4/Ds4State.cpp b/source/plugins/TLAC/Input/DirectInput/Ds4/Ds4State.cpp new file mode 100644 index 0000000..aaa4ef0 --- /dev/null +++ b/source/plugins/TLAC/Input/DirectInput/Ds4/Ds4State.cpp @@ -0,0 +1,14 @@ +#include "Ds4State.h" + +namespace TLAC::Input +{ + Joystick::Joystick() : XAxis(0.0f), YAxis(0.0f) + { + return; + }; + + Joystick::Joystick(float xAxis, float yAxis) : XAxis(xAxis), YAxis(yAxis) + { + return; + }; +} \ No newline at end of file diff --git a/source/plugins/TLAC/Input/DirectInput/Ds4/Ds4State.h b/source/plugins/TLAC/Input/DirectInput/Ds4/Ds4State.h new file mode 100644 index 0000000..d671936 --- /dev/null +++ b/source/plugins/TLAC/Input/DirectInput/Ds4/Ds4State.h @@ -0,0 +1,39 @@ +#pragma once +#include "../DirectInput.h" +#include "Ds4Button.h" + +namespace TLAC::Input +{ + struct Joystick + { + FLOAT XAxis, YAxis; + + Joystick(); + Joystick(float xAxis, float yAxis); + }; + + struct Dpad + { + BOOL IsDown; + FLOAT Angle; + Joystick Stick; + }; + + struct Trigger + { + FLOAT Axis; + }; + + struct Ds4State + { + DIJOYSTATE2 DI_JoyState; + + BYTE Buttons[DS4_BUTTON_MAX]; + + Dpad Dpad; + Joystick LeftStick; + Joystick RightStick; + Trigger LeftTrigger; + Trigger RightTrigger; + }; +} \ No newline at end of file diff --git a/source/plugins/TLAC/Input/DirectInput/Ds4/DualShock4.cpp b/source/plugins/TLAC/Input/DirectInput/Ds4/DualShock4.cpp new file mode 100644 index 0000000..899f56c --- /dev/null +++ b/source/plugins/TLAC/Input/DirectInput/Ds4/DualShock4.cpp @@ -0,0 +1,169 @@ +#include "DualShock4.h" +#include "../../../Utilities/Math.h" +#include "../../../framework.h" +#include + +namespace TLAC::Input +{ + DualShock4* DualShock4::instance; + + DualShock4::DualShock4() + { + } + + DualShock4::~DualShock4() + { + DI_Dispose(); + } + + bool DualShock4::TryInitializeInstance() + { + if (InstanceInitialized()) + return true; + + if (!DirectInputInitialized()) + return false; + + DualShock4 *dualShock4 = new DualShock4(); + + bool success = dualShock4->Initialize(); + instance = success ? dualShock4 : nullptr; + + if (!success) + delete dualShock4; + + return success; + } + + bool DualShock4::Initialize() + { + HRESULT result = NULL; + + const size_t guidCount = sizeof(GUID_Ds4) / sizeof(GUID); + for (size_t i = 0; i < guidCount; i++) + { + result = DI_CreateDevice(GUID_Ds4[i]); + + if (!FAILED(result)) + break; + else if (i == guidCount - 1) + return false; + } + + if (FAILED(result = DI_SetDataFormat(&c_dfDIJoystick2))) + return false; + + result = DI_Acquire(); + + return true; + } + + bool DualShock4::PollInput() + { + lastState = currentState; + + HRESULT result = NULL; + result = DI_Poll(); + result = DI_GetDeviceState(sizeof(DIJOYSTATE2), ¤tState.DI_JoyState); + + if (result != DI_OK) + return false; + + UpdateInternalDs4State(currentState); + + for (int button = 0; button < DS4_BUTTON_MAX; button++) + currentState.Buttons[button] = GetButtonState(currentState, (Ds4Button)button); + + return true; + } + + void DualShock4::UpdateInternalDs4State(Ds4State &state) + { + if (state.Dpad.IsDown = state.DI_JoyState.rgdwPOV[0] != -1) + { + state.Dpad.Angle = (state.DI_JoyState.rgdwPOV[0] / 100.0f); + + auto direction = Utilities::GetDirection(state.Dpad.Angle); + state.Dpad.Stick = { direction.Y, -direction.X }; + } + else + { + state.Dpad.Angle = 0; + state.Dpad.Stick = Joystick(); + } + + state.LeftStick = NormalizeStick(state.DI_JoyState.lX, state.DI_JoyState.lY); + state.RightStick = NormalizeStick(state.DI_JoyState.lZ, state.DI_JoyState.lRz); + + state.LeftTrigger = { NormalizeTrigger(state.DI_JoyState.lRx) }; + state.RightTrigger = { NormalizeTrigger(state.DI_JoyState.lRy) }; + } + + bool DualShock4::IsDown(Ds4Button button) + { + return currentState.Buttons[button]; + } + + bool DualShock4::IsUp(Ds4Button button) + { + return !IsDown(button); + } + + bool DualShock4::IsTapped(Ds4Button button) + { + return IsDown(button) && WasUp(button); + } + + bool DualShock4::IsReleased(Ds4Button button) + { + return IsUp(button) && WasDown(button); + } + + bool DualShock4::WasDown(Ds4Button button) + { + return lastState.Buttons[button]; + } + + bool DualShock4::WasUp(Ds4Button button) + { + return !WasDown(button); + } + + bool DualShock4::MatchesDirection(Joystick joystick, Direction directionEnum, float threshold) + { + switch (directionEnum) + { + case TLAC::Input::DIR_UP: + return joystick.YAxis <= -threshold; + + case TLAC::Input::DIR_RIGHT: + return joystick.XAxis >= +threshold; + + case TLAC::Input::DIR_DOWN: + return joystick.YAxis >= +threshold; + + case TLAC::Input::DIR_LEFT: + return joystick.XAxis <= -threshold; + + default: + return false; + } + } + + bool DualShock4::GetButtonState(Ds4State &state, Ds4Button button) + { + if (button >= DS4_SQUARE && button <= DS4_TOUCH) + return state.DI_JoyState.rgbButtons[button]; + + if (button >= DS4_DPAD_UP && button <= DS4_DPAD_LEFT) + return state.Dpad.IsDown ? MatchesDirection(state.Dpad.Stick, (Direction)(button - DS4_DPAD_UP), dpadThreshold) : false; + + if (button >= DS4_L_STICK_UP && button <= DS4_L_STICK_LEFT) + return MatchesDirection(state.LeftStick, (Direction)(button - DS4_L_STICK_UP), joystickThreshold); + + if (button >= DS4_R_STICK_UP && button <= DS4_R_STICK_LEFT) + return MatchesDirection(state.RightStick, (Direction)(button - DS4_R_STICK_UP), joystickThreshold); + + return false; + } +} diff --git a/source/plugins/TLAC/Input/DirectInput/Ds4/DualShock4.h b/source/plugins/TLAC/Input/DirectInput/Ds4/DualShock4.h new file mode 100644 index 0000000..56fdf50 --- /dev/null +++ b/source/plugins/TLAC/Input/DirectInput/Ds4/DualShock4.h @@ -0,0 +1,62 @@ +#pragma once +#include "../Controller.h" +#include "../../IInputDevice.h" +#include "Ds4State.h" + +namespace TLAC::Input +{ + // DualShock 4 Wireless Controller Product GUIDs: + const GUID GUID_Ds4[2] = + { + // First Generation: {05C4054C-0000-0000-0000-504944564944} + { 0x05C4054C, 0x0000, 0x0000, { 0x00, 0x00, 0x50, 0x49, 0x44, 0x56, 0x49, 0x44 } }, + // Second Generation: {09CC054C-0000-0000-0000-504944564944} + { 0x09CC054C, 0x0000, 0x0000, { 0x00, 0x00, 0x50, 0x49, 0x44, 0x56, 0x49, 0x44 } }, + }; + + class DualShock4 : public Controller, public IInputDevice + { + public: + DualShock4(); + ~DualShock4(); + + static bool TryInitializeInstance(); + + bool Initialize(); + bool PollInput() override; + + bool IsDown(Ds4Button button); + bool IsUp(Ds4Button button); + bool IsTapped(Ds4Button button); + bool IsReleased(Ds4Button button); + bool WasDown(Ds4Button button); + bool WasUp(Ds4Button button); + + inline Joystick GetLeftStick() { return currentState.LeftStick; }; + inline Joystick GetRightStick() { return currentState.RightStick; }; + inline Joystick GetDpad() { return currentState.Dpad.Stick; }; + + static inline bool InstanceInitialized() { return instance != nullptr; }; + static inline DualShock4* GetInstance() { return instance; }; + static inline void DeleteInstance() { delete instance; instance = nullptr; }; + + private: + static DualShock4* instance; + + Ds4State lastState; + Ds4State currentState; + + const float triggerThreshold = 0.5f; + const float joystickThreshold = 0.5f; + const float dpadThreshold = 0.5f; + + inline float NormalizeTrigger(long value) { return (float)value / USHRT_MAX; }; + inline float NormalizeStick(long value) { return (float)value / USHRT_MAX * 2.0f - 1.0f; }; + inline Joystick NormalizeStick(long x, long y) { return Joystick(NormalizeStick(x), NormalizeStick(y)); }; + + void UpdateInternalDs4State(Ds4State &state); + + bool MatchesDirection(Joystick joystick, Direction directionEnum, float threshold); + bool GetButtonState(Ds4State &state, Ds4Button button); + }; +} \ No newline at end of file diff --git a/source/plugins/TLAC/Input/IInputDevice.h b/source/plugins/TLAC/Input/IInputDevice.h new file mode 100644 index 0000000..c29a053 --- /dev/null +++ b/source/plugins/TLAC/Input/IInputDevice.h @@ -0,0 +1,10 @@ +#pragma once + +namespace TLAC::Input +{ + class IInputDevice + { + public: + virtual bool PollInput() = 0; + }; +} diff --git a/source/plugins/TLAC/Input/KeyConfig/Config.cpp b/source/plugins/TLAC/Input/KeyConfig/Config.cpp new file mode 100644 index 0000000..ac80fe0 --- /dev/null +++ b/source/plugins/TLAC/Input/KeyConfig/Config.cpp @@ -0,0 +1,271 @@ +#include "Config.h" +#include "windows.h" +#include "../Bindings/KeyboardBinding.h" +#include "../Bindings/Ds4Binding.h" +#include "../../Utilities/Operations.h" +#include "../Bindings/XinputBinding.h" +#include "../../Constants.h" + +namespace TLAC::Input::KeyConfig +{ + KeycodeMap Config::Keymap = + { + // NumPad Keys + { "NumPad0", VK_NUMPAD0 }, + { "NumPad1", VK_NUMPAD1 }, + { "NumPad2", VK_NUMPAD2 }, + { "NumPad3", VK_NUMPAD3 }, + { "NumPad4", VK_NUMPAD4 }, + { "NumPad5", VK_NUMPAD5 }, + { "NumPad6", VK_NUMPAD6 }, + { "NumPad7", VK_NUMPAD7 }, + { "NumPad8", VK_NUMPAD8 }, + { "NumPad9", VK_NUMPAD9 }, + { "Plus", VK_ADD }, + { "Minus", VK_SUBTRACT }, + { "Divide", VK_DIVIDE }, + { "Multiply", VK_MULTIPLY }, + { "Comma", VK_OEM_COMMA }, + { "Period", VK_OEM_PERIOD }, + { "Slash", VK_OEM_2 }, + // F-Keys + { "F1", VK_F1 }, + { "F2", VK_F2 }, + { "F3", VK_F3 }, + { "F4", VK_F4 }, + { "F5", VK_F5 }, + { "F6", VK_F6 }, + { "F7", VK_F7 }, + { "F8", VK_F8 }, + { "F9", VK_F9 }, + { "F10", VK_F10 }, + { "F11", VK_F11 }, + { "F12", VK_F12 }, + { "F13", VK_F13 }, + { "F14", VK_F14 }, + { "F15", VK_F15 }, + { "F16", VK_F16 }, + { "F17", VK_F17 }, + { "F18", VK_F18 }, + { "F19", VK_F19 }, + { "F20", VK_F20 }, + { "F21", VK_F21 }, + { "F22", VK_F22 }, + { "F23", VK_F23 }, + { "F24", VK_F24 }, + // Shift Keys + { "LeftShift", VK_LSHIFT }, + { "LShift", VK_LSHIFT }, + { "RightShift", VK_RSHIFT }, + { "RShift", VK_RSHIFT }, + // Control Keys + { "LeftControl", VK_LCONTROL }, + { "LControl", VK_LCONTROL }, + { "LCtrl", VK_LCONTROL }, + { "RightControl", VK_RCONTROL }, + { "RControl", VK_RCONTROL }, + { "RCtrl", VK_RCONTROL }, + // Arrow Keys + { "Up", VK_UP }, + { "Down", VK_DOWN }, + { "Left", VK_LEFT }, + { "Right", VK_RIGHT }, + // Special Keys + { "Enter", VK_RETURN }, + { "Return", VK_RETURN }, + { "Tab", VK_TAB }, + { "Back", VK_BACK }, + { "Backspace", VK_BACK }, + { "Insert", VK_INSERT }, + { "Ins", VK_INSERT }, + { "Delete", VK_DELETE }, + { "Del", VK_DELETE }, + { "Home", VK_HOME }, + { "End", VK_END }, + { "PageUp", VK_PRIOR }, + { "PageDown", VK_NEXT }, + { "ESC", VK_ESCAPE }, + { "Escape", VK_ESCAPE }, + { "Comma", VK_OEM_COMMA }, + { "Period", VK_OEM_PERIOD }, + { "Slash", VK_OEM_2 }, + // Mouse buttons + { "MouseLeft", VK_LBUTTON }, + { "MouseMiddle", VK_MBUTTON }, + { "MouseRight", VK_RBUTTON }, + { "MouseX1", VK_XBUTTON1 }, + { "MouseX2", VK_XBUTTON2 }, + }; + + KeycodeMap Config::XinputMap = + { + //XINPUT + { "XINPUT_A", XINPUT_A}, + { "XINPUT_B", XINPUT_B}, + { "XINPUT_X", XINPUT_X}, + { "XINPUT_Y", XINPUT_Y}, + { "XINPUT_UP", XINPUT_UP}, + { "XINPUT_DOWN", XINPUT_DOWN}, + { "XINPUT_LEFT", XINPUT_LEFT}, + { "XINPUT_RIGHT", XINPUT_RIGHT}, + { "XINPUT_START", XINPUT_START}, + { "XINPUT_BACK", XINPUT_BACK}, + { "XINPUT_LS", XINPUT_LS}, + { "XINPUT_RS", XINPUT_RS}, + { "XINPUT_LT", XINPUT_LT}, + { "XINPUT_RT", XINPUT_RT}, + { "XINPUT_LLEFT", XINPUT_LLEFT}, + { "XINPUT_LRIGHT", XINPUT_LRIGHT}, + { "XINPUT_RLEFT", XINPUT_RLEFT}, + { "XINPUT_RRIGHT", XINPUT_RRIGHT}, + }; + + Ds4ButtonMap Config::Ds4Map = + { + // Face Buttons + { "DS4_SQUARE", DS4_SQUARE }, + { "Ds4_Square", DS4_SQUARE }, + + { "DS4_CROSS", DS4_CROSS }, + { "Ds4_Cross", DS4_CROSS }, + + { "DS4_CIRCLE", DS4_CIRCLE }, + { "Ds4_Circle", DS4_CIRCLE }, + + { "DS4_TRIANGLE", DS4_TRIANGLE }, + { "Ds4_Triangle", DS4_TRIANGLE }, + + // Standard Buttons + { "DS4_SHARE", DS4_SHARE }, + { "Ds4_Share", DS4_SHARE }, + + { "DS4_OPTIONS", DS4_OPTIONS }, + { "Ds4_Options", DS4_OPTIONS }, + + { "DS4_PS", DS4_PS }, + { "Ds4_PS", DS4_PS }, + + { "DS4_TOUCH", DS4_TOUCH }, + { "Ds4_Touch", DS4_TOUCH }, + + { "DS4_L1", DS4_L1 }, + { "Ds4_L1", DS4_L1 }, + + { "DS4_R1", DS4_R1 }, + { "Ds4_R1", DS4_R1 }, + + // D-Pad Directions + { "DS4_DPAD_UP", DS4_DPAD_UP }, + { "Ds4_DPad_Up", DS4_DPAD_UP }, + + { "DS4_DPAD_RIGHT", DS4_DPAD_RIGHT }, + { "Ds4_DPad_Right", DS4_DPAD_RIGHT }, + + { "DS4_DPAD_DOWN", DS4_DPAD_DOWN }, + { "Ds4_DPad_Down", DS4_DPAD_DOWN }, + + { "DS4_DPAD_LEFT", DS4_DPAD_LEFT }, + { "Ds4_DPad_Left", DS4_DPAD_LEFT }, + + // Trigger Buttons + { "DS4_L_TRIGGER", DS4_L_TRIGGER }, + { "Ds4_L_Trigger", DS4_L_TRIGGER }, + + { "DS4_R_TRIGGER", DS4_R_TRIGGER }, + { "Ds4_R_Trigger", DS4_R_TRIGGER }, + + // Joystick Buttons + { "DS4_L3", DS4_L3 }, + { "Ds4_L3", DS4_L3 }, + + { "DS4_R3", DS4_R3 }, + { "Ds4_R3", DS4_R3 }, + + // Left Joystick + { "DS4_L_STICK_UP", DS4_L_STICK_UP }, + { "Ds4_L_Stick_Up", DS4_L_STICK_UP }, + + { "DS4_L_STICK_RIGHT", DS4_L_STICK_RIGHT }, + { "Ds4_L_Stick_Right", DS4_L_STICK_RIGHT }, + + { "DS4_L_STICK_DOWN", DS4_L_STICK_DOWN }, + { "Ds4_L_Stick_Down", DS4_L_STICK_DOWN }, + + { "DS4_L_STICK_LEFT", DS4_L_STICK_LEFT }, + { "Ds4_L_Stick_Left", DS4_L_STICK_LEFT }, + + // Right Joystick + { "DS4_R_STICK_UP", DS4_R_STICK_UP }, + { "Ds4_R_Stick_Up", DS4_R_STICK_UP }, + + { "DS4_R_STICK_RIGHT", DS4_R_STICK_RIGHT }, + { "Ds4_R_Stick_Right", DS4_R_STICK_RIGHT }, + + { "DS4_R_STICK_DOWN", DS4_R_STICK_DOWN }, + { "Ds4_R_Stick_Down", DS4_R_STICK_DOWN }, + + { "DS4_R_STICK_LEFT", DS4_R_STICK_LEFT }, + { "Ds4_R_Stick_Left", DS4_R_STICK_LEFT }, + }; + + void Config::BindConfigKeys(std::unordered_map &configMap, const char *configKeyName, Binding &bindObj, std::vector defaultKeys) + { + std::vector keys; + + auto configPair = configMap.find(configKeyName); + + // config variable was found in the ini + if (configPair != configMap.end()) + { + keys = Utilities::Split(configPair->second, ","); + } + else + { + keys = defaultKeys; + } + + for (std::string key : keys) + { + Utilities::Trim(key); + + // Applies only for Single-Character keys + if (key.length() == 1) + { + bindObj.AddBinding(new KeyboardBinding(key[0])); + } + else // for special key names + { + auto keycode = Config::Keymap.find(key.c_str()); + + // name is known in the special keys map + if (keycode != Config::Keymap.end()) + { + bindObj.AddBinding(new KeyboardBinding(keycode->second)); + } + else + { + auto xinputBtn = Config::XinputMap.find(key.c_str()); + + if (xinputBtn != Config::XinputMap.end()) + { + bindObj.AddBinding(new XinputBinding(xinputBtn->second)); + } + else + { + // just gonna be lazy for now and put this inside an else statement + auto ds4Button = Config::Ds4Map.find(key.c_str()); + + if (ds4Button != Config::Ds4Map.end()) + { + bindObj.AddBinding(new Ds4Binding(ds4Button->second)); + } + else + { + printf("[TLAC] Config::BindConfigKeys(): Unable to parse key: '%s'\n", key.c_str()); + } + } + } + } + } + } +} \ No newline at end of file diff --git a/source/plugins/TLAC/Input/KeyConfig/Config.h b/source/plugins/TLAC/Input/KeyConfig/Config.h new file mode 100644 index 0000000..194cd13 --- /dev/null +++ b/source/plugins/TLAC/Input/KeyConfig/Config.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include "KeyString.h" +#include "KeyStringHash.h" +#include "../Bindings/Binding.h" +#include "../DirectInput/Ds4/Ds4Button.h" + +namespace TLAC::Input::KeyConfig +{ + typedef std::unordered_map KeycodeMap; + typedef std::unordered_map Ds4ButtonMap; + + class Config + { + public: + static KeycodeMap Keymap; + static Ds4ButtonMap Ds4Map; + static KeycodeMap XinputMap; + + static void BindConfigKeys(std::unordered_map &configMap, const char *configKeyName, Binding &bindObj, std::vector defaultKeys); + }; +} diff --git a/source/plugins/TLAC/Input/KeyConfig/KeyString.cpp b/source/plugins/TLAC/Input/KeyConfig/KeyString.cpp new file mode 100644 index 0000000..522f519 --- /dev/null +++ b/source/plugins/TLAC/Input/KeyConfig/KeyString.cpp @@ -0,0 +1,13 @@ +#include "KeyString.h" + +namespace TLAC::Input::KeyConfig +{ + KeyString::KeyString(const char* str) : value(str) + { + } + + bool KeyString::operator==(const KeyString& rsv) const + { + return !_strcmpi(value.c_str(), rsv.value.c_str()); + } +} diff --git a/source/plugins/TLAC/Input/KeyConfig/KeyString.h b/source/plugins/TLAC/Input/KeyConfig/KeyString.h new file mode 100644 index 0000000..71b7416 --- /dev/null +++ b/source/plugins/TLAC/Input/KeyConfig/KeyString.h @@ -0,0 +1,14 @@ +#pragma once +#include + +namespace TLAC::Input::KeyConfig +{ + struct KeyString + { + std::string value; + + KeyString(const char* str); + bool operator==(const KeyString& rsv) const; + }; +} + diff --git a/source/plugins/TLAC/Input/KeyConfig/KeyStringHash.cpp b/source/plugins/TLAC/Input/KeyConfig/KeyStringHash.cpp new file mode 100644 index 0000000..2ef4f4a --- /dev/null +++ b/source/plugins/TLAC/Input/KeyConfig/KeyStringHash.cpp @@ -0,0 +1,13 @@ +#include "KeyStringHash.h" +#include + +namespace TLAC::Input::KeyConfig +{ + size_t KeyStringHash::operator()(const KeyString& key) const + { + std::string ret = key.value; + std::transform(ret.begin(), ret.end(), ret.begin(), + [](unsigned char c) { return std::tolower(c, std::locale()); }); + return std::hash()(ret); + } +} \ No newline at end of file diff --git a/source/plugins/TLAC/Input/KeyConfig/KeyStringHash.h b/source/plugins/TLAC/Input/KeyConfig/KeyStringHash.h new file mode 100644 index 0000000..21ae364 --- /dev/null +++ b/source/plugins/TLAC/Input/KeyConfig/KeyStringHash.h @@ -0,0 +1,10 @@ +#pragma once +#include "KeyString.h" + +namespace TLAC::Input::KeyConfig +{ + struct KeyStringHash + { + size_t operator()(const KeyString& key) const; + }; +} diff --git a/source/plugins/TLAC/Input/Keyboard/Keyboard.cpp b/source/plugins/TLAC/Input/Keyboard/Keyboard.cpp new file mode 100644 index 0000000..7ab85ae --- /dev/null +++ b/source/plugins/TLAC/Input/Keyboard/Keyboard.cpp @@ -0,0 +1,100 @@ +#include "Keyboard.h" + +namespace TLAC::Input +{ + Keyboard* Keyboard::instance; + + Keyboard::Keyboard() + { + } + + Keyboard* Keyboard::GetInstance() + { + if (instance == nullptr) + instance = new Keyboard(); + + return instance; + } + + bool Keyboard::PollInput() + { + lastState = currentState; + + float elapsed = keyIntervalWatch.Restart(); + + for (BYTE i = 0; i < KEYBOARD_KEYS; i++) + { + // DOWN + bool isDown = GetAsyncKeyState(i) < 0; + currentState.KeyStates[i] = isDown; + + // DOUBLE TAPPED + bool isTapped = IsTapped(i); + keyDoubleTapStates[i] = isTapped ? keyDoubleTapWatches[i].Restart() <= DOUBLE_TAP_THRESHOLD : false; + + // INTERVAL TAPPED + keyIntervalTapStates[i] = isTapped; + + if (isTapped) + { + keyIntervalTapTimes[i] = 0; + keyIntervalInitials[i] = true; + } + else if (isDown) + { + float threshold = keyIntervalInitials[i] ? INTERVAL_TAP_DELAY_THRESHOLD : INTERVAL_TAP_THRESHOLD; + + bool intervalTapped = (keyIntervalTapTimes[i] += elapsed) > threshold; + keyIntervalTapStates[i] = intervalTapped; + + if (intervalTapped) + { + keyIntervalTapTimes[i] = 0; + keyIntervalInitials[i] = false; + } + } + } + + return true; + } + + bool Keyboard::IsDown(BYTE keycode) + { + return currentState.IsDown(keycode); + } + + bool Keyboard::IsUp(BYTE keycode) + { + return !IsDown(keycode); + } + + bool Keyboard::IsTapped(BYTE keycode) + { + return IsDown(keycode) && WasUp(keycode); + } + + bool Keyboard::IsDoubleTapped(BYTE keycode) + { + return keyDoubleTapStates[keycode]; + } + + bool Keyboard::IsReleased(BYTE keycode) + { + return IsUp(keycode) && WasDown(keycode); + } + + inline bool Keyboard::WasDown(BYTE keycode) + { + return lastState.IsDown(keycode); + } + + inline bool Keyboard::WasUp(BYTE keycode) + { + return !WasDown(keycode); + } + + bool Keyboard::IsIntervalTapped(BYTE keycode) + { + return keyIntervalTapStates[keycode]; + } +} diff --git a/source/plugins/TLAC/Input/Keyboard/Keyboard.h b/source/plugins/TLAC/Input/Keyboard/Keyboard.h new file mode 100644 index 0000000..9521ccb --- /dev/null +++ b/source/plugins/TLAC/Input/Keyboard/Keyboard.h @@ -0,0 +1,47 @@ +#pragma once +#include "../IInputDevice.h" +#include "KeyboardState.h" +#include "../../Utilities/Stopwatch.h" + +using Stopwatch = TLAC::Utilities::Stopwatch; + +namespace TLAC::Input +{ + constexpr float DOUBLE_TAP_THRESHOLD = 200.0f; + constexpr float INTERVAL_TAP_DELAY_THRESHOLD = 500.0f; + constexpr float INTERVAL_TAP_THRESHOLD = 75.0f; + + class Keyboard : public IInputDevice + { + public: + static Keyboard* GetInstance(); + + bool PollInput() override; + bool IsDown(BYTE keycode); + bool IsUp(BYTE keycode); + bool IsTapped(BYTE keycode); + bool IsDoubleTapped(BYTE keycode); + bool IsReleased(BYTE keycode); + bool IsIntervalTapped(BYTE keycode); + + bool WasDown(BYTE keycode); + bool WasUp(BYTE keycode); + + private: + Keyboard(); + KeyboardState lastState; + KeyboardState currentState; + + Stopwatch keyIntervalWatch; + + BYTE keyDoubleTapStates[KEYBOARD_KEYS]; + Stopwatch keyDoubleTapWatches[KEYBOARD_KEYS]; + + BOOL keyIntervalInitials[KEYBOARD_KEYS]; + BYTE keyIntervalTapStates[KEYBOARD_KEYS]; + FLOAT keyIntervalTapTimes[KEYBOARD_KEYS]; + + static Keyboard* instance; + }; +} + diff --git a/source/plugins/TLAC/Input/Keyboard/KeyboardState.cpp b/source/plugins/TLAC/Input/Keyboard/KeyboardState.cpp new file mode 100644 index 0000000..6bedc6f --- /dev/null +++ b/source/plugins/TLAC/Input/Keyboard/KeyboardState.cpp @@ -0,0 +1,9 @@ +#include "KeyboardState.h" + +namespace TLAC::Input +{ + bool KeyboardState::IsDown(BYTE keycode) + { + return KeyStates[keycode]; + } +} diff --git a/source/plugins/TLAC/Input/Keyboard/KeyboardState.h b/source/plugins/TLAC/Input/Keyboard/KeyboardState.h new file mode 100644 index 0000000..dbddc15 --- /dev/null +++ b/source/plugins/TLAC/Input/Keyboard/KeyboardState.h @@ -0,0 +1,15 @@ +#pragma once +#include + +namespace TLAC::Input +{ + const int KEYBOARD_KEYS = 0xFF; + + struct KeyboardState + { + BYTE KeyStates[KEYBOARD_KEYS]; + + bool IsDown(BYTE keycode); + }; +} + diff --git a/source/plugins/TLAC/Input/Mouse/Mouse.cpp b/source/plugins/TLAC/Input/Mouse/Mouse.cpp new file mode 100644 index 0000000..9612e96 --- /dev/null +++ b/source/plugins/TLAC/Input/Mouse/Mouse.cpp @@ -0,0 +1,135 @@ +#include "Mouse.h" +#include "../../framework.h" +#include "../../Constants.h" + +namespace TLAC::Input +{ + Mouse* Mouse::instance; + + Mouse::Mouse() + { + directInputMouse = new DirectInputMouse(); + } + + Mouse::~Mouse() + { + if (directInputMouse != nullptr) + delete directInputMouse; + } + + Mouse* Mouse::GetInstance() + { + if (instance == nullptr) + instance = new Mouse(); + + return instance; + } + + POINT Mouse::GetPosition() + { + return currentState.Position; + } + + POINT Mouse::GetRelativePosition() + { + return currentState.RelativePosition; + } + + POINT Mouse::GetDeltaPosition() + { + return + { + currentState.Position.x - lastState.Position.x, + currentState.Position.y - lastState.Position.y + }; + } + + long Mouse::GetMouseWheel() + { + return currentState.MouseWheel; + } + + long Mouse::GetDeltaMouseWheel() + { + return currentState.MouseWheel - lastState.MouseWheel; + } + + bool Mouse::HasMoved() + { + POINT delta = GetDeltaPosition(); + return delta.x != 0 || delta.y != 0; + } + + bool Mouse::GetIsScrolledUp() + { + return currentState.ScrolledUp; + } + + bool Mouse::GetIsScrolledDown() + { + return currentState.ScrolledDown; + } + + bool Mouse::GetWasScrolledUp() + { + return lastState.ScrolledUp; + } + + bool Mouse::GetWasScrolledDown() + { + return lastState.ScrolledDown; + } + + void Mouse::SetPosition(int x, int y) + { + lastState.Position.x = x; + lastState.Position.y = y; + SetCursorPos(x, y); + } + + bool Mouse::PollInput() + { + lastState = currentState; + + GetCursorPos(¤tState.Position); + currentState.RelativePosition = currentState.Position; + + if (framework::DivaWindowHandle != NULL) + ScreenToClient(framework::DivaWindowHandle, ¤tState.RelativePosition); + + RECT hWindow; + GetClientRect(TLAC::framework::DivaWindowHandle, &hWindow); + + gameHeight = (int*)RESOLUTION_HEIGHT_ADDRESS; + gameWidth = (int*)RESOLUTION_WIDTH_ADDRESS; + fbWidth = (int*)FB_WIDTH_ADDRESS; + fbHeight = (int*)FB_HEIGHT_ADDRESS; + + if (directInputMouse != nullptr) + { + if (directInputMouse->Poll()) + currentState.MouseWheel += directInputMouse->GetMouseWheel(); + + currentState.ScrolledUp = (GetDeltaMouseWheel() > 0); + currentState.ScrolledDown = (GetDeltaMouseWheel() < 0); + } + + if ((fbWidth != gameWidth) && (fbHeight != gameHeight)) { + xoffset = ((float)16 / (float)9) * (hWindow.bottom - hWindow.top); + if (xoffset != (hWindow.right - hWindow.left)) + { + scale = xoffset / (hWindow.right - hWindow.left); + xoffset = ((hWindow.right - hWindow.left) / 2) - (xoffset / 2); + } + else { + xoffset = 0; + scale = 1; + } + + currentState.RelativePosition.x = ((currentState.RelativePosition.x - round(xoffset)) * *gameWidth / (hWindow.right - hWindow.left)) / scale; + currentState.RelativePosition.y = currentState.RelativePosition.y * *gameHeight / (hWindow.bottom - hWindow.top); + } + + return true; + } +} diff --git a/source/plugins/TLAC/Input/Mouse/Mouse.h b/source/plugins/TLAC/Input/Mouse/Mouse.h new file mode 100644 index 0000000..6e22858 --- /dev/null +++ b/source/plugins/TLAC/Input/Mouse/Mouse.h @@ -0,0 +1,49 @@ +#pragma once +#include "../IInputDevice.h" +#include "../DirectInput/DirectInputMouse.h" +#include "MouseState.h" + +namespace TLAC::Input +{ + class Mouse : public IInputDevice + { + public: + ~Mouse(); + + static Mouse* GetInstance(); + + bool PollInput() override; + + POINT GetPosition(); + POINT GetRelativePosition(); + POINT GetDeltaPosition(); + + long GetMouseWheel(); + long GetDeltaMouseWheel(); + + bool HasMoved(); + bool GetIsScrolledUp(); + bool GetIsScrolledDown(); + bool GetWasScrolledUp(); + bool GetWasScrolledDown(); + + void SetPosition(int x, int y); + + private: + Mouse(); + MouseState lastState; + MouseState currentState; + DirectInputMouse* directInputMouse = nullptr; + + int* gameWidth; + int* gameHeight; + int* fbWidth; + int* fbHeight; + + float xoffset; + float scale; + + static Mouse* instance; + }; +} + diff --git a/source/plugins/TLAC/Input/Mouse/MouseState.h b/source/plugins/TLAC/Input/Mouse/MouseState.h new file mode 100644 index 0000000..8c88edb --- /dev/null +++ b/source/plugins/TLAC/Input/Mouse/MouseState.h @@ -0,0 +1,15 @@ +#pragma once +#include + +namespace TLAC::Input +{ + struct MouseState + { + POINT Position; + POINT RelativePosition; + long MouseWheel; + bool ScrolledUp; + bool ScrolledDown; + }; +} + diff --git a/source/plugins/TLAC/Input/Xinput/Xinput.cpp b/source/plugins/TLAC/Input/Xinput/Xinput.cpp new file mode 100644 index 0000000..c2bb9b4 --- /dev/null +++ b/source/plugins/TLAC/Input/Xinput/Xinput.cpp @@ -0,0 +1,677 @@ +#include +#include "Xinput.h" +#include "../../Constants.h" + +namespace TLAC::Input +{ + Xinput* Xinput::instance; + + Xinput::Xinput() + { + + } + + Xinput* Xinput::GetInstance() + { + if (instance == nullptr) + { + instance = new Xinput(); + } + return instance; + } + + bool Xinput::PollInput() + { + lastState = currentState; + ZeroMemory(&state, sizeof(XINPUT_STATE)); + float elapsed = keyIntervalWatch.Restart(); + + if (XInputGetState(0, &state) == ERROR_SUCCESS) + { + BYTE i = XINPUT_A; + { + currentState.KeyStates[i] = false; + if (state.Gamepad.wButtons & XINPUT_GAMEPAD_A) + currentState.KeyStates[i] = true; + KeyDoubleTapStates[i] = IsTapped(i) ? KeyDoubleTapWatches[i].Restart() <= DOUBLE_TAP_THRESHOLD : false; + { + bool isDown = currentState.KeyStates[i]; + bool isTapped = IsTapped(i); + + keyIntervalTapStates[i] = isTapped; + + if (isTapped) + { + keyIntervalTapTimes[i] = 0; + keyIntervalInitials[i] = true; + } + else if (isDown) + { + float threshold = keyIntervalInitials[i] ? INTERVAL_TAP_DELAY_THRESHOLD : INTERVAL_TAP_THRESHOLD; + + bool intervalTapped = (keyIntervalTapTimes[i] += elapsed) > threshold; + keyIntervalTapStates[i] = intervalTapped; + + if (intervalTapped) + { + keyIntervalTapTimes[i] = 0; + keyIntervalInitials[i] = false; + } + } + } + } + + i = XINPUT_DOWN; + { + currentState.KeyStates[i] = false; + if (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN) + currentState.KeyStates[i] = true; + KeyDoubleTapStates[i] = IsTapped(i) ? KeyDoubleTapWatches[i].Restart() <= DOUBLE_TAP_THRESHOLD : false; + { + bool isDown = currentState.KeyStates[i]; + bool isTapped = IsTapped(i); + + keyIntervalTapStates[i] = isTapped; + + if (isTapped) + { + keyIntervalTapTimes[i] = 0; + keyIntervalInitials[i] = true; + } + else if (isDown) + { + float threshold = keyIntervalInitials[i] ? INTERVAL_TAP_DELAY_THRESHOLD : INTERVAL_TAP_THRESHOLD; + + bool intervalTapped = (keyIntervalTapTimes[i] += elapsed) > threshold; + keyIntervalTapStates[i] = intervalTapped; + + if (intervalTapped) + { + keyIntervalTapTimes[i] = 0; + keyIntervalInitials[i] = false; + } + } + } + } + + i = XINPUT_B; + { + currentState.KeyStates[i] = false; + if (state.Gamepad.wButtons & XINPUT_GAMEPAD_B) + currentState.KeyStates[i] = true; + KeyDoubleTapStates[i] = IsTapped(i) ? KeyDoubleTapWatches[i].Restart() <= DOUBLE_TAP_THRESHOLD : false; + { + bool isDown = currentState.KeyStates[i]; + bool isTapped = IsTapped(i); + + keyIntervalTapStates[i] = isTapped; + + if (isTapped) + { + keyIntervalTapTimes[i] = 0; + keyIntervalInitials[i] = true; + } + else if (isDown) + { + float threshold = keyIntervalInitials[i] ? INTERVAL_TAP_DELAY_THRESHOLD : INTERVAL_TAP_THRESHOLD; + + bool intervalTapped = (keyIntervalTapTimes[i] += elapsed) > threshold; + keyIntervalTapStates[i] = intervalTapped; + + if (intervalTapped) + { + keyIntervalTapTimes[i] = 0; + keyIntervalInitials[i] = false; + } + } + } + } + + i = XINPUT_RIGHT; + { + currentState.KeyStates[i] = false; + if (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT) + currentState.KeyStates[i] = true; + KeyDoubleTapStates[i] = IsTapped(i) ? KeyDoubleTapWatches[i].Restart() <= DOUBLE_TAP_THRESHOLD : false; + { + bool isDown = currentState.KeyStates[i]; + bool isTapped = IsTapped(i); + + keyIntervalTapStates[i] = isTapped; + + if (isTapped) + { + keyIntervalTapTimes[i] = 0; + keyIntervalInitials[i] = true; + } + else if (isDown) + { + float threshold = keyIntervalInitials[i] ? INTERVAL_TAP_DELAY_THRESHOLD : INTERVAL_TAP_THRESHOLD; + + bool intervalTapped = (keyIntervalTapTimes[i] += elapsed) > threshold; + keyIntervalTapStates[i] = intervalTapped; + + if (intervalTapped) + { + keyIntervalTapTimes[i] = 0; + keyIntervalInitials[i] = false; + } + } + } + } + + i = XINPUT_X; + { + currentState.KeyStates[i] = false; + if (state.Gamepad.wButtons & XINPUT_GAMEPAD_X) + currentState.KeyStates[i] = true; + KeyDoubleTapStates[i] = IsTapped(i) ? KeyDoubleTapWatches[i].Restart() <= DOUBLE_TAP_THRESHOLD : false; + { + bool isDown = currentState.KeyStates[i]; + bool isTapped = IsTapped(i); + + keyIntervalTapStates[i] = isTapped; + + if (isTapped) + { + keyIntervalTapTimes[i] = 0; + keyIntervalInitials[i] = true; + } + else if (isDown) + { + float threshold = keyIntervalInitials[i] ? INTERVAL_TAP_DELAY_THRESHOLD : INTERVAL_TAP_THRESHOLD; + + bool intervalTapped = (keyIntervalTapTimes[i] += elapsed) > threshold; + keyIntervalTapStates[i] = intervalTapped; + + if (intervalTapped) + { + keyIntervalTapTimes[i] = 0; + keyIntervalInitials[i] = false; + } + } + } + } + + i = XINPUT_LEFT; + { + currentState.KeyStates[i] = false; + if (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT) + currentState.KeyStates[i] = true; + KeyDoubleTapStates[i] = IsTapped(i) ? KeyDoubleTapWatches[i].Restart() <= DOUBLE_TAP_THRESHOLD : false; + { + bool isDown = currentState.KeyStates[i]; + bool isTapped = IsTapped(i); + + keyIntervalTapStates[i] = isTapped; + + if (isTapped) + { + keyIntervalTapTimes[i] = 0; + keyIntervalInitials[i] = true; + } + else if (isDown) + { + float threshold = keyIntervalInitials[i] ? INTERVAL_TAP_DELAY_THRESHOLD : INTERVAL_TAP_THRESHOLD; + + bool intervalTapped = (keyIntervalTapTimes[i] += elapsed) > threshold; + keyIntervalTapStates[i] = intervalTapped; + + if (intervalTapped) + { + keyIntervalTapTimes[i] = 0; + keyIntervalInitials[i] = false; + } + } + } + } + + i = XINPUT_Y; + { + currentState.KeyStates[i] = false; + if (state.Gamepad.wButtons & XINPUT_GAMEPAD_Y) + currentState.KeyStates[i] = true; + KeyDoubleTapStates[i] = IsTapped(i) ? KeyDoubleTapWatches[i].Restart() <= DOUBLE_TAP_THRESHOLD : false; + { + bool isDown = currentState.KeyStates[i]; + bool isTapped = IsTapped(i); + + keyIntervalTapStates[i] = isTapped; + + if (isTapped) + { + keyIntervalTapTimes[i] = 0; + keyIntervalInitials[i] = true; + } + else if (isDown) + { + float threshold = keyIntervalInitials[i] ? INTERVAL_TAP_DELAY_THRESHOLD : INTERVAL_TAP_THRESHOLD; + + bool intervalTapped = (keyIntervalTapTimes[i] += elapsed) > threshold; + keyIntervalTapStates[i] = intervalTapped; + + if (intervalTapped) + { + keyIntervalTapTimes[i] = 0; + keyIntervalInitials[i] = false; + } + } + } + } + + i = XINPUT_UP; + { + currentState.KeyStates[i] = false; + if (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP) + currentState.KeyStates[i] = true; + KeyDoubleTapStates[i] = IsTapped(i) ? KeyDoubleTapWatches[i].Restart() <= DOUBLE_TAP_THRESHOLD : false; + { + bool isDown = currentState.KeyStates[i]; + bool isTapped = IsTapped(i); + + keyIntervalTapStates[i] = isTapped; + + if (isTapped) + { + keyIntervalTapTimes[i] = 0; + keyIntervalInitials[i] = true; + } + else if (isDown) + { + float threshold = keyIntervalInitials[i] ? INTERVAL_TAP_DELAY_THRESHOLD : INTERVAL_TAP_THRESHOLD; + + bool intervalTapped = (keyIntervalTapTimes[i] += elapsed) > threshold; + keyIntervalTapStates[i] = intervalTapped; + + if (intervalTapped) + { + keyIntervalTapTimes[i] = 0; + keyIntervalInitials[i] = false; + } + } + } + } + + i = XINPUT_LS; + { + currentState.KeyStates[i] = false; + if (state.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER) + currentState.KeyStates[i] = true; + KeyDoubleTapStates[i] = IsTapped(i) ? KeyDoubleTapWatches[i].Restart() <= DOUBLE_TAP_THRESHOLD : false; + { + bool isDown = currentState.KeyStates[i]; + bool isTapped = IsTapped(i); + + keyIntervalTapStates[i] = isTapped; + + if (isTapped) + { + keyIntervalTapTimes[i] = 0; + keyIntervalInitials[i] = true; + } + else if (isDown) + { + float threshold = keyIntervalInitials[i] ? INTERVAL_TAP_DELAY_THRESHOLD : INTERVAL_TAP_THRESHOLD; + + bool intervalTapped = (keyIntervalTapTimes[i] += elapsed) > threshold; + keyIntervalTapStates[i] = intervalTapped; + + if (intervalTapped) + { + keyIntervalTapTimes[i] = 0; + keyIntervalInitials[i] = false; + } + } + } + } + + i = XINPUT_RS; + { + currentState.KeyStates[i] = false; + if (state.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER) + currentState.KeyStates[i] = true; + KeyDoubleTapStates[i] = IsTapped(i) ? KeyDoubleTapWatches[i].Restart() <= DOUBLE_TAP_THRESHOLD : false; + { + bool isDown = currentState.KeyStates[i]; + bool isTapped = IsTapped(i); + + keyIntervalTapStates[i] = isTapped; + + if (isTapped) + { + keyIntervalTapTimes[i] = 0; + keyIntervalInitials[i] = true; + } + else if (isDown) + { + float threshold = keyIntervalInitials[i] ? INTERVAL_TAP_DELAY_THRESHOLD : INTERVAL_TAP_THRESHOLD; + + bool intervalTapped = (keyIntervalTapTimes[i] += elapsed) > threshold; + keyIntervalTapStates[i] = intervalTapped; + + if (intervalTapped) + { + keyIntervalTapTimes[i] = 0; + keyIntervalInitials[i] = false; + } + } + } + } + + i = XINPUT_START; + { + currentState.KeyStates[i] = false; + if (state.Gamepad.wButtons & XINPUT_GAMEPAD_START) + currentState.KeyStates[i] = true; + KeyDoubleTapStates[i] = IsTapped(i) ? KeyDoubleTapWatches[i].Restart() <= DOUBLE_TAP_THRESHOLD : false; + { + bool isDown = currentState.KeyStates[i]; + bool isTapped = IsTapped(i); + + keyIntervalTapStates[i] = isTapped; + + if (isTapped) + { + keyIntervalTapTimes[i] = 0; + keyIntervalInitials[i] = true; + } + else if (isDown) + { + float threshold = keyIntervalInitials[i] ? INTERVAL_TAP_DELAY_THRESHOLD : INTERVAL_TAP_THRESHOLD; + + bool intervalTapped = (keyIntervalTapTimes[i] += elapsed) > threshold; + keyIntervalTapStates[i] = intervalTapped; + + if (intervalTapped) + { + keyIntervalTapTimes[i] = 0; + keyIntervalInitials[i] = false; + } + } + } + } + + i = XINPUT_BACK; + { + currentState.KeyStates[i] = false; + if (state.Gamepad.wButtons & XINPUT_GAMEPAD_BACK) + currentState.KeyStates[i] = true; + KeyDoubleTapStates[i] = IsTapped(i) ? KeyDoubleTapWatches[i].Restart() <= DOUBLE_TAP_THRESHOLD : false; + + { + bool isDown = currentState.KeyStates[i]; + bool isTapped = IsTapped(i); + + keyIntervalTapStates[i] = isTapped; + + if (isTapped) + { + keyIntervalTapTimes[i] = 0; + keyIntervalInitials[i] = true; + } + else if (isDown) + { + float threshold = keyIntervalInitials[i] ? INTERVAL_TAP_DELAY_THRESHOLD : INTERVAL_TAP_THRESHOLD; + + bool intervalTapped = (keyIntervalTapTimes[i] += elapsed) > threshold; + keyIntervalTapStates[i] = intervalTapped; + + if (intervalTapped) + { + keyIntervalTapTimes[i] = 0; + keyIntervalInitials[i] = false; + } + } + } + } + + i = XINPUT_LT; + { + currentState.KeyStates[i] = false; + if (state.Gamepad.bLeftTrigger > 230) + currentState.KeyStates[i] = true; + KeyDoubleTapStates[i] = IsTapped(i) ? KeyDoubleTapWatches[i].Restart() <= DOUBLE_TAP_THRESHOLD : false; + + { + bool isDown = currentState.KeyStates[i]; + bool isTapped = IsTapped(i); + + keyIntervalTapStates[i] = isTapped; + + if (isTapped) + { + keyIntervalTapTimes[i] = 0; + keyIntervalInitials[i] = true; + } + else if (isDown) + { + float threshold = keyIntervalInitials[i] ? INTERVAL_TAP_DELAY_THRESHOLD : INTERVAL_TAP_THRESHOLD; + + bool intervalTapped = (keyIntervalTapTimes[i] += elapsed) > threshold; + keyIntervalTapStates[i] = intervalTapped; + + if (intervalTapped) + { + keyIntervalTapTimes[i] = 0; + keyIntervalInitials[i] = false; + } + } + } + } + + i = XINPUT_RT; + { + currentState.KeyStates[i] = false; + if (state.Gamepad.bRightTrigger > 230) + currentState.KeyStates[i] = true; + KeyDoubleTapStates[i] = IsTapped(i) ? KeyDoubleTapWatches[i].Restart() <= DOUBLE_TAP_THRESHOLD : false; + + { + bool isDown = currentState.KeyStates[i]; + bool isTapped = IsTapped(i); + + keyIntervalTapStates[i] = isTapped; + + if (isTapped) + { + keyIntervalTapTimes[i] = 0; + keyIntervalInitials[i] = true; + } + else if (isDown) + { + float threshold = keyIntervalInitials[i] ? INTERVAL_TAP_DELAY_THRESHOLD : INTERVAL_TAP_THRESHOLD; + + bool intervalTapped = (keyIntervalTapTimes[i] += elapsed) > threshold; + keyIntervalTapStates[i] = intervalTapped; + + if (intervalTapped) + { + keyIntervalTapTimes[i] = 0; + keyIntervalInitials[i] = false; + } + } + } + } + + + { + //float normLX = fmaxf(-1, (float)state.Gamepad.sThumbLX / 32767); + i = XINPUT_LRIGHT; + currentState.KeyStates[i] = false; + if (state.Gamepad.sThumbLX > 10000) + currentState.KeyStates[i] = true; + + + KeyDoubleTapStates[i] = IsTapped(i) ? KeyDoubleTapWatches[i].Restart() <= DOUBLE_TAP_THRESHOLD : false; + + { + bool isDown = currentState.KeyStates[i]; + bool isTapped = IsTapped(i); + + keyIntervalTapStates[i] = isTapped; + + if (isTapped) + { + keyIntervalTapTimes[i] = 0; + keyIntervalInitials[i] = true; + } + else if (isDown) + { + float threshold = keyIntervalInitials[i] ? INTERVAL_TAP_DELAY_THRESHOLD : INTERVAL_TAP_THRESHOLD; + + bool intervalTapped = (keyIntervalTapTimes[i] += elapsed) > threshold; + keyIntervalTapStates[i] = intervalTapped; + + if (intervalTapped) + { + keyIntervalTapTimes[i] = 0; + keyIntervalInitials[i] = false; + } + } + } + i = XINPUT_LLEFT; + currentState.KeyStates[i] = false; + if (state.Gamepad.sThumbLX < -10000) + currentState.KeyStates[i] = true; + KeyDoubleTapStates[i] = IsTapped(i) ? KeyDoubleTapWatches[i].Restart() <= DOUBLE_TAP_THRESHOLD : false; + + { + bool isDown = currentState.KeyStates[i]; + bool isTapped = IsTapped(i); + + keyIntervalTapStates[i] = isTapped; + + if (isTapped) + { + keyIntervalTapTimes[i] = 0; + keyIntervalInitials[i] = true; + } + else if (isDown) + { + float threshold = keyIntervalInitials[i] ? INTERVAL_TAP_DELAY_THRESHOLD : INTERVAL_TAP_THRESHOLD; + + bool intervalTapped = (keyIntervalTapTimes[i] += elapsed) > threshold; + keyIntervalTapStates[i] = intervalTapped; + + if (intervalTapped) + { + keyIntervalTapTimes[i] = 0; + keyIntervalInitials[i] = false; + } + } + } + } + + { + //float normLX = fmaxf(-1, (float)state.Gamepad.sThumbRX / 32767); + i = XINPUT_RRIGHT; + currentState.KeyStates[i] = false; + if (state.Gamepad.sThumbRX > 10000) + currentState.KeyStates[i] = true; + KeyDoubleTapStates[i] = IsTapped(i) ? KeyDoubleTapWatches[i].Restart() <= DOUBLE_TAP_THRESHOLD : false; + + { + bool isDown = currentState.KeyStates[i]; + bool isTapped = IsTapped(i); + + keyIntervalTapStates[i] = isTapped; + + if (isTapped) + { + keyIntervalTapTimes[i] = 0; + keyIntervalInitials[i] = true; + } + else if (isDown) + { + float threshold = keyIntervalInitials[i] ? INTERVAL_TAP_DELAY_THRESHOLD : INTERVAL_TAP_THRESHOLD; + + bool intervalTapped = (keyIntervalTapTimes[i] += elapsed) > threshold; + keyIntervalTapStates[i] = intervalTapped; + + if (intervalTapped) + { + keyIntervalTapTimes[i] = 0; + keyIntervalInitials[i] = false; + } + } + } + i = XINPUT_RLEFT; + currentState.KeyStates[i] = false; + if (state.Gamepad.sThumbRX < -10000) + currentState.KeyStates[i] = true; + KeyDoubleTapStates[i] = IsTapped(i) ? KeyDoubleTapWatches[i].Restart() <= DOUBLE_TAP_THRESHOLD : false; + { + bool isDown = currentState.KeyStates[i]; + bool isTapped = IsTapped(i); + + keyIntervalTapStates[i] = isTapped; + + if (isTapped) + { + keyIntervalTapTimes[i] = 0; + keyIntervalInitials[i] = true; + } + else if (isDown) + { + float threshold = keyIntervalInitials[i] ? INTERVAL_TAP_DELAY_THRESHOLD : INTERVAL_TAP_THRESHOLD; + + bool intervalTapped = (keyIntervalTapTimes[i] += elapsed) > threshold; + keyIntervalTapStates[i] = intervalTapped; + + if (intervalTapped) + { + keyIntervalTapTimes[i] = 0; + keyIntervalInitials[i] = false; + } + } + } + } + } + else { + ZeroMemory(&state, sizeof(XINPUT_STATE)); + ZeroMemory(¤tState, sizeof(currentState)); + } + return TRUE; + } + + + bool Xinput::IsDown(BYTE keycode) + { + return currentState.IsDown(keycode); + } + + bool Xinput::IsUp(BYTE keycode) + { + return !IsDown(keycode); + } + + bool Xinput::IsTapped(BYTE keycode) + { + return IsDown(keycode) && WasUp(keycode); + } + + bool Xinput::IsDoubleTapped(BYTE keycode) + { + return KeyDoubleTapStates[keycode]; + } + + bool Xinput::IsReleased(BYTE keycode) + { + return IsUp(keycode) && WasDown(keycode); + } + + inline bool Xinput::WasDown(BYTE keycode) + { + return lastState.IsDown(keycode); + } + + inline bool Xinput::WasUp(BYTE keycode) + { + return !WasDown(keycode); + } + + bool Xinput::IsIntervalTapped(BYTE keycode) + { + return keyIntervalTapStates[keycode]; + } +} \ No newline at end of file diff --git a/source/plugins/TLAC/Input/Xinput/Xinput.h b/source/plugins/TLAC/Input/Xinput/Xinput.h new file mode 100644 index 0000000..255fe95 --- /dev/null +++ b/source/plugins/TLAC/Input/Xinput/Xinput.h @@ -0,0 +1,49 @@ +#pragma once +#include "../IInputDevice.h" +#include +#include "../../Utilities/Stopwatch.h" +#include "XinputState.h" + +using Stopwatch = TLAC::Utilities::Stopwatch; + +namespace TLAC::Input +{ + class Xinput : public IInputDevice + { + const float DOUBLE_TAP_THRESHOLD = 200.0f; + const float INTERVAL_TAP_DELAY_THRESHOLD = 500.0f; + const float INTERVAL_TAP_THRESHOLD = 75.0f; + + public: + static Xinput* GetInstance(); + + bool PollInput() override; + bool IsDown(BYTE keycode); + bool IsUp(BYTE keycode); + bool IsTapped(BYTE keycode); + bool IsReleased(BYTE keycode); + bool IsDoubleTapped(BYTE keycode); + bool IsIntervalTapped(BYTE keycode); + + bool WasDown(BYTE keycode); + bool WasUp(BYTE keycode); + + private: + Xinput(); + XinputState lastState; + XinputState currentState; + + XINPUT_STATE state; + + BYTE KeyDoubleTapStates[0xFF]; + Utilities::Stopwatch KeyDoubleTapWatches[0xFF]; + + Stopwatch keyIntervalWatch; + + BOOL keyIntervalInitials[0xFF]; + BYTE keyIntervalTapStates[0xFF]; + FLOAT keyIntervalTapTimes[0xFF]; + + static Xinput* instance; + }; +} diff --git a/source/plugins/TLAC/Input/Xinput/XinputState.cpp b/source/plugins/TLAC/Input/Xinput/XinputState.cpp new file mode 100644 index 0000000..53d985d --- /dev/null +++ b/source/plugins/TLAC/Input/Xinput/XinputState.cpp @@ -0,0 +1,9 @@ +#include "XinputState.h" + +namespace TLAC::Input +{ + bool XinputState::IsDown(BYTE keycode) + { + return KeyStates[keycode]; + } +} \ No newline at end of file diff --git a/source/plugins/TLAC/Input/Xinput/XinputState.h b/source/plugins/TLAC/Input/Xinput/XinputState.h new file mode 100644 index 0000000..6bba790 --- /dev/null +++ b/source/plugins/TLAC/Input/Xinput/XinputState.h @@ -0,0 +1,12 @@ +#pragma once +#include + +namespace TLAC::Input +{ + struct XinputState + { + bool KeyStates[0x8F]; + + bool IsDown(BYTE keycode); + }; +} diff --git a/source/plugins/TLAC/TLAC.vcxproj b/source/plugins/TLAC/TLAC.vcxproj new file mode 100644 index 0000000..df1945c --- /dev/null +++ b/source/plugins/TLAC/TLAC.vcxproj @@ -0,0 +1,285 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + {2B5533BB-04A1-424F-9BCA-1CA963B46B7F} + Win32Proj + TLAC + 10.0 + + + + DynamicLibrary + true + v142 + Unicode + + + DynamicLibrary + false + v142 + true + Unicode + + + DynamicLibrary + true + v142 + Unicode + + + DynamicLibrary + false + v142 + true + Unicode + + + + + + + + + + + + + + + + + + + + + false + $(ProjectName) + .dva + + + true + + + true + $(ProjectName) + .dva + + + false + + + + NotUsing + Level3 + MaxSpeed + true + true + true + NDEBUG;TLAC_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + + + stdcpp17 + ..\..\..\dependencies\detours\include;..\..\..\dependencies\freeglut\include;%(AdditionalIncludeDirectories) + + + Windows + true + true + true + false + XINPUT9_1_0.LIB;dinput8.lib;dxguid.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + Xinput.h + ..\..\..\dependencies\detours\lib;..\..\..\dependencies\freeglut\lib;%(AdditionalLibraryDirectories) + + + + + Use + Level3 + Disabled + true + WIN32;_DEBUG;TLAC_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + pch.h + + + Windows + true + false + + + + + NotUsing + Level3 + Disabled + true + _DEBUG;TLAC_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + + + stdcpp17 + ..\..\..\dependencies\detours\include;..\..\..\dependencies\freeglut\include;%(AdditionalIncludeDirectories) + + + Windows + true + false + ..\..\..\dependencies\detours\lib;..\..\..\dependencies\freeglut\lib;%(AdditionalLibraryDirectories) + XINPUT9_1_0.LIB;dinput8.lib;dxguid.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + Xinput.h + + + + + Use + Level3 + MaxSpeed + true + true + true + WIN32;NDEBUG;TLAC_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + pch.h + + + Windows + true + true + true + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/source/plugins/TLAC/TLAC.vcxproj.filters b/source/plugins/TLAC/TLAC.vcxproj.filters new file mode 100644 index 0000000..7659af6 --- /dev/null +++ b/source/plugins/TLAC/TLAC.vcxproj.filters @@ -0,0 +1,336 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;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 + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/source/plugins/TLAC/Utilities/EnumBitwiseOperations.h b/source/plugins/TLAC/Utilities/EnumBitwiseOperations.h new file mode 100644 index 0000000..27f9272 --- /dev/null +++ b/source/plugins/TLAC/Utilities/EnumBitwiseOperations.h @@ -0,0 +1,12 @@ +#pragma once + +namespace TLAC::Utilities +{ + template inline T operator~ (T a) { return (T)~(int)a; } + template inline T operator| (T a, T b) { return (T)((int)a | (int)b); } + template inline T operator& (T a, T b) { return (T)((int)a & (int)b); } + template inline T operator^ (T a, T b) { return (T)((int)a ^ (int)b); } + template inline T& operator|= (T& a, T b) { return (T&)((int&)a |= (int)b); } + template inline T& operator&= (T& a, T b) { return (T&)((int&)a &= (int)b); } + template inline T& operator^= (T& a, T b) { return (T&)((int&)a ^= (int)b); } +} \ No newline at end of file diff --git a/source/plugins/TLAC/Utilities/Math.cpp b/source/plugins/TLAC/Utilities/Math.cpp new file mode 100644 index 0000000..a20b661 --- /dev/null +++ b/source/plugins/TLAC/Utilities/Math.cpp @@ -0,0 +1,37 @@ +#define _USE_MATH_DEFINES +#include "Math.h" + +namespace TLAC::Utilities +{ + float ToDegrees(float radians) + { + return radians * (180.0f / M_PI); + } + + float ToRadians(float degrees) + { + return (degrees * M_PI) / 180.0f; + } + + Vec2 GetDirection(float degrees) + { + float radians = ToRadians(degrees); + return Vec2(cos(radians), sin(radians)); + } + + Vec2 PointFromAngle(float degrees, float distance) + { + float radians = ToRadians(degrees + 90.0f); + return Vec2(-1 * std::cos(radians) * distance, -1 * std::sin(radians) * distance); + } + + float AngleFromPoints(Vec2 p0, Vec2 p1) + { + return (float)(std::atan2(p1.Y - p0.Y, p1.X - p0.X) * 180.0 / M_PI) + 90.0f; + } + + float ConvertRange(float originalStart, float originalEnd, float newStart, float newEnd, float value) + { + return newStart + ((value - originalStart) * (newEnd - newStart) / (originalEnd - originalStart)); + } +} \ No newline at end of file diff --git a/source/plugins/TLAC/Utilities/Math.h b/source/plugins/TLAC/Utilities/Math.h new file mode 100644 index 0000000..780526c --- /dev/null +++ b/source/plugins/TLAC/Utilities/Math.h @@ -0,0 +1,15 @@ +#pragma once +#include +#include "Vec2.h" +#include "Vec3.h" + +namespace TLAC::Utilities +{ + float ToDegrees(float radians); + float ToRadians(float degrees); + + Vec2 GetDirection(float degrees); + Vec2 PointFromAngle(float degrees, float distance); + float AngleFromPoints(Vec2 p0, Vec2 p1); + float ConvertRange(float originalStart, float originalEnd, float newStart, float newEnd, float value); +} \ No newline at end of file diff --git a/source/plugins/TLAC/Utilities/Operations.cpp b/source/plugins/TLAC/Utilities/Operations.cpp new file mode 100644 index 0000000..fdca5b3 --- /dev/null +++ b/source/plugins/TLAC/Utilities/Operations.cpp @@ -0,0 +1,60 @@ +#pragma once +#include "Operations.h" + +namespace TLAC::Utilities +{ + std::vector Split(const std::string& str, const std::string& delim) + { + std::vector tokens; + size_t prev = 0, pos = 0; + do + { + pos = str.find(delim, prev); + if (pos == std::string::npos) + pos = str.length(); + + std::string token = str.substr(prev, pos - prev); + + if (!token.empty()) + tokens.push_back(token); + + prev = pos + delim.length(); + } while (pos < str.length() && prev < str.length()); + + return tokens; + } + + void LeftTrim(std::string &s) + { + s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int ch) + { + return !std::isspace(ch); + })); + } + + void RightTrim(std::string &s) + { + s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) + { + return !std::isspace(ch); + }).base(), s.end()); + } + + void Trim(std::string &s) + { + s = trim(s); + } + + std::string trim(const std::string& str, const std::string& whitespace) + { + const size_t strBegin = str.find_first_not_of(whitespace); + + if (strBegin == std::string::npos) + return ""; + + const size_t strEnd = str.find_last_not_of(whitespace); + const size_t strRange = strEnd - strBegin + 1; + + return str.substr(strBegin, strRange); + } +} \ No newline at end of file diff --git a/source/plugins/TLAC/Utilities/Operations.h b/source/plugins/TLAC/Utilities/Operations.h new file mode 100644 index 0000000..b11c8a6 --- /dev/null +++ b/source/plugins/TLAC/Utilities/Operations.h @@ -0,0 +1,16 @@ +#pragma once +#include +#include +#include +#include + +namespace TLAC::Utilities +{ + std::vector Split(const std::string& str, const std::string& delim); + + void LeftTrim(std::string &s); + void RightTrim(std::string &s); + void Trim(std::string &s); + + std::string trim(const std::string& str, const std::string& whitespace = " \t"); +} diff --git a/source/plugins/TLAC/Utilities/Stopwatch.cpp b/source/plugins/TLAC/Utilities/Stopwatch.cpp new file mode 100644 index 0000000..f1c9304 --- /dev/null +++ b/source/plugins/TLAC/Utilities/Stopwatch.cpp @@ -0,0 +1,36 @@ +#include "Stopwatch.h" + +namespace TLAC::Utilities +{ + Stopwatch::Stopwatch() + { + } + + Stopwatch::~Stopwatch() + { + } + + void Stopwatch::Start() + { + start = high_resolution_clock::now(); + } + + float Stopwatch::Stop() + { + end = high_resolution_clock::now(); + return GetElapsed(); + } + + float Stopwatch::Restart() + { + float elapsed = Stop(); + Start(); + + return elapsed; + } + + float Stopwatch::GetElapsed() + { + return (float)(chrono::duration_cast(end - start).count() / TIME_FACTOR); + } +} \ No newline at end of file diff --git a/source/plugins/TLAC/Utilities/Stopwatch.h b/source/plugins/TLAC/Utilities/Stopwatch.h new file mode 100644 index 0000000..d21d268 --- /dev/null +++ b/source/plugins/TLAC/Utilities/Stopwatch.h @@ -0,0 +1,30 @@ +#pragma once +#include + +namespace chrono = std::chrono; + +typedef chrono::time_point steady_clock; +typedef chrono::high_resolution_clock high_resolution_clock; + +namespace TLAC::Utilities +{ + class Stopwatch + { + const float TIME_FACTOR = 1000.0f; + + public: + Stopwatch(); + ~Stopwatch(); + + void Start(); + float Stop(); + float Restart(); + + float GetElapsed(); + + private: + steady_clock start; + steady_clock end; + }; +} + diff --git a/source/plugins/TLAC/Utilities/Vec2.cpp b/source/plugins/TLAC/Utilities/Vec2.cpp new file mode 100644 index 0000000..0b27e03 --- /dev/null +++ b/source/plugins/TLAC/Utilities/Vec2.cpp @@ -0,0 +1,34 @@ +#include "Vec2.h" + +namespace TLAC::Utilities +{ + Vec2::Vec2() + { + }; + + Vec2::Vec2(float x, float y) : X(x), Y(y) + { + }; + + Vec2 Vec2::operator+(Vec2 value) + { + return Vec2(X + value.X, Y + value.Y); + } + + void Vec2::operator+=(const Vec2 &value) + { + X += value.X; + Y += value.Y; + } + + Vec2 Vec2::operator-(Vec2 value) + { + return Vec2(X - value.X, Y - value.Y); + } + + void Vec2::operator-=(const Vec2 &value) + { + X -= value.X; + Y -= value.Y; + } +} \ No newline at end of file diff --git a/source/plugins/TLAC/Utilities/Vec2.h b/source/plugins/TLAC/Utilities/Vec2.h new file mode 100644 index 0000000..0dd3aa4 --- /dev/null +++ b/source/plugins/TLAC/Utilities/Vec2.h @@ -0,0 +1,18 @@ +#pragma once + +namespace TLAC::Utilities +{ + struct Vec2 + { + float X, Y; + + Vec2(); + Vec2(float x, float y); + + Vec2 operator+(Vec2 value); + void operator+=(const Vec2 &value); + + Vec2 operator-(Vec2 value); + void operator-=(const Vec2 &value); + }; +} diff --git a/source/plugins/TLAC/Utilities/Vec3.cpp b/source/plugins/TLAC/Utilities/Vec3.cpp new file mode 100644 index 0000000..b5c8a34 --- /dev/null +++ b/source/plugins/TLAC/Utilities/Vec3.cpp @@ -0,0 +1,58 @@ +#include "Vec3.h" + +namespace TLAC::Utilities +{ + Vec3::Vec3() + { + }; + + Vec3::Vec3(float x, float y, float z) : X(x), Y(y), Z(z) + { + }; + + Vec3 Vec3::operator+(Vec2 value) + { + return Vec3(X + value.X, Y, Z + value.Y); + } + + void Vec3::operator+=(const Vec2 &value) + { + X += value.X; + Z += value.Y; + } + + Vec3 Vec3::operator-(Vec2 value) + { + return Vec3(X - value.X, Y, Z - value.Y); + } + + void Vec3::operator-=(const Vec2 &value) + { + X -= value.X; + Z -= value.Y; + } + + Vec3 Vec3::operator+(Vec3 value) + { + return Vec3(X + value.X, Y + value.Y, Z + value.Z); + } + + void Vec3::operator+=(const Vec3 &value) + { + X += value.X; + Y += value.Y; + Z += value.Z; + } + + Vec3 Vec3::operator-(Vec3 value) + { + return Vec3(X - value.X, Y - value.Y, Z - value.Z); + } + + void Vec3::operator-=(const Vec3 &value) + { + X -= value.X; + Y -= value.Y; + Z -= value.Z; + } +} \ No newline at end of file diff --git a/source/plugins/TLAC/Utilities/Vec3.h b/source/plugins/TLAC/Utilities/Vec3.h new file mode 100644 index 0000000..37c30fd --- /dev/null +++ b/source/plugins/TLAC/Utilities/Vec3.h @@ -0,0 +1,27 @@ +#pragma once +#include "Vec2.h" + +namespace TLAC::Utilities +{ + struct Vec3 + { + float X, Y, Z; + + Vec3(); + Vec3(float x, float y, float z); + + // Vec2 + Vec3 operator+(Vec2 value); + void operator+=(const Vec2 &value); + + Vec3 operator-(Vec2 value); + void operator-=(const Vec2 &value); + + // Vec3 + Vec3 operator+(Vec3 value); + void operator+=(const Vec3 &value); + + Vec3 operator-(Vec3 value); + void operator-=(const Vec3 &value); + }; +} \ No newline at end of file diff --git a/source/plugins/TLAC/dllmain.cpp b/source/plugins/TLAC/dllmain.cpp new file mode 100644 index 0000000..c3ed8ca --- /dev/null +++ b/source/plugins/TLAC/dllmain.cpp @@ -0,0 +1,337 @@ +#include +#include +#include +#include +#include +#include "Constants.h" +#include "framework.h" +#include "Input/Mouse/Mouse.h" +#include "Input/Xinput/Xinput.h" +#include "Input/Keyboard/Keyboard.h" +#include "Input/DirectInput/DirectInput.h" +#include "Input/DirectInput/Ds4/DualShock4.h" +#include "Components/ComponentsManager.h" +#include +#include + +#include +#pragma comment(lib, "detours.lib") + +void(__cdecl* divaEngineUpdate)() = (void(__cdecl*)())0x14018CC40; + +LRESULT CALLBACK MessageWindowProcessCallback(HWND, UINT, WPARAM, LPARAM); +DWORD WINAPI WindowMessageDispatcher(LPVOID); +VOID RegisterMessageWindowClass(); + +struct +{ + DWORD ID = NULL; + HANDLE Handle = NULL; +} MessageThread; + +const wchar_t* MessageWindowClassName = TEXT("MessageWindowClass"); +const wchar_t* MessageWindowName = TEXT("MessageWindowTitle"); + +namespace TLAC +{ + Components::ComponentsManager ComponentsManager; + bool DeviceConnected = true; + bool FirstUpdateTick = true; + bool HasWindowFocus, HadWindowFocus; + + void InitializeTick() + { + RegisterMessageWindowClass(); + if ((MessageThread.Handle = CreateThread(0, 0, WindowMessageDispatcher, 0, 0, 0)) == NULL) + printf("[TLAC] InitializeTick(): CreateThread() Error: %d\n", GetLastError()); + + framework::DivaWindowHandle = FindWindow(0, framework::DivaWindowName); + if (framework::DivaWindowHandle == NULL) + framework::DivaWindowHandle = FindWindow(0, framework::GlutDefaultName); + if (framework::DivaWindowHandle == NULL) + framework::DivaWindowHandle = FindWindow(0, framework::freeGlutDefaultName); + + HRESULT diInitResult = Input::InitializeDirectInput(framework::Module); + if (FAILED(diInitResult)) + printf("[TLAC] InitializeTick(): Failed to initialize DirectInput. Error: 0x%08X\n", diInitResult); + + ComponentsManager.Initialize(); + } + + void UpdateTick() + { + if (FirstUpdateTick) + { + FirstUpdateTick = false; + InitializeTick(); + } + + if (DeviceConnected) + { + DeviceConnected = false; + + if (!Input::DualShock4::InstanceInitialized()) + { + if (Input::DualShock4::TryInitializeInstance()) + printf("[TLAC] UpdateTick(): DualShock4 connected and initialized\n"); + } + } + + ComponentsManager.Update(); + + HadWindowFocus = HasWindowFocus; + HasWindowFocus = framework::DivaWindowHandle == NULL || GetForegroundWindow() == framework::DivaWindowHandle; + + if (HasWindowFocus && GetAsyncKeyState(VK_ESCAPE)) + { + exit(1); + } + + if ((HasWindowFocus) && (!framework::inputDisable)) + { + Input::Keyboard::GetInstance()->PollInput(); + Input::Mouse::GetInstance()->PollInput(); + Input::Xinput::GetInstance()->PollInput(); + + if (Input::DualShock4::GetInstance() != nullptr) + { + if (!Input::DualShock4::GetInstance()->PollInput()) + { + Input::DualShock4::DeleteInstance(); + printf("[TLAC] UpdateTick(): DualShock4 connection lost\n"); + } + } + + ComponentsManager.UpdateInput(); + } + + if ((framework::inputDisable)) + { + Input::Keyboard::GetInstance()->PollInput(); + Input::Mouse::GetInstance()->PollInput(); + Input::Xinput::GetInstance()->PollInput(); + + if (Input::DualShock4::GetInstance() != nullptr) + { + if (!Input::DualShock4::GetInstance()->PollInput()) + { + Input::DualShock4::DeleteInstance(); + printf("[TLAC] UpdateTick(): DualShock4 connection lost\n"); + } + } + } + + if (HasWindowFocus && !HadWindowFocus) + ComponentsManager.OnFocusGain(); + + if (!HasWindowFocus && HadWindowFocus) + ComponentsManager.OnFocusLost(); + } + + void InitializeExtraSettings() + { + const LPCTSTR RESOLUTION_CONFIG_FILE_NAME = _T(".\\config.ini"); + auto nCustomRes = GetPrivateProfileIntW(L"resolution", L"r.enable", FALSE, RESOLUTION_CONFIG_FILE_NAME); + auto nCustomWidth = GetPrivateProfileIntW(L"resolution", L"r.width", NULL, RESOLUTION_CONFIG_FILE_NAME); + auto nCustomHeight = GetPrivateProfileIntW(L"resolution", L"r.height", NULL, RESOLUTION_CONFIG_FILE_NAME); + auto nTAA = GetPrivateProfileIntW(L"graphics", L"taa", TRUE, RESOLUTION_CONFIG_FILE_NAME); + auto nMLAA = GetPrivateProfileIntW(L"graphics", L"mlaa", TRUE, RESOLUTION_CONFIG_FILE_NAME); + + if (nCustomRes) + { + printf("[TLAC] Custom internal resolution enabled\n"); + printf("[TLAC] X: %d Y: %d\n", nCustomWidth, nCustomHeight); + // Requires -wqhd launch parameter or Render.dva plugin + { + DWORD oldProtect, bck; + VirtualProtect((BYTE*)0x00000001409B8B68, 4, PAGE_EXECUTE_READWRITE, &oldProtect); + *((int*)0x00000001409B8B68) = nCustomWidth; + VirtualProtect((BYTE*)0x00000001409B8B68, 6, oldProtect, &bck); + } + { + DWORD oldProtect, bck; + VirtualProtect((BYTE*)0x00000001409B8B6C, 4, PAGE_EXECUTE_READWRITE, &oldProtect); + *((int*)0x00000001409B8B6C) = nCustomHeight; + VirtualProtect((BYTE*)0x00000001409B8B6C, 6, oldProtect, &bck); + } + + //*((int*)0x00000001409B8B6C) = maxHeight; + //*((int*)0x00000001409B8B14) = maxWidth; + //*((int*)0x00000001409B8B18) = maxHeight; + + //*((int*)0x00000001409B8B1C) = maxWidth; // No parameters width? + //*((int*)0x00000001409B8B20) = maxHeight; // No parameters height? + } + if (!nTAA) + { + DWORD oldProtect, bck; + VirtualProtect((BYTE*)0x00000001411AB67C, 1, PAGE_EXECUTE_READWRITE, &oldProtect); + *((byte*)0x00000001411AB67C + 0) = 0x00; + VirtualProtect((BYTE*)0x00000001411AB67C, 1, oldProtect, &bck); + + printf("[TLAC] TAA disabled\n"); + } + if (!nMLAA) + { + DWORD oldProtect, bck; + VirtualProtect((BYTE*)0x00000001411AB680, 1, PAGE_EXECUTE_READWRITE, &oldProtect); + *((byte*)0x00000001411AB680 + 0) = 0x00; + VirtualProtect((BYTE*)0x00000001411AB680, 1, oldProtect, &bck); + + printf("[TLAC] MLAA disabled\n"); + } + } + + void Dispose() + { + ComponentsManager.Dispose(); + + delete Input::Keyboard::GetInstance(); + delete Input::Mouse::GetInstance(); + delete Input::DualShock4::GetInstance(); + + Input::DisposeDirectInput(); + + PostThreadMessage(MessageThread.ID, WM_QUIT, 0, 0); + } +} + +DWORD WINAPI WindowMessageDispatcher(LPVOID lpParam) +{ + HWND windowHandle = CreateWindowW( + MessageWindowClassName, + MessageWindowName, + WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, CW_USEDEFAULT, + CW_USEDEFAULT, CW_USEDEFAULT, + NULL, NULL, + TLAC::framework::Module, + NULL); + + if (!windowHandle) + { + printf("[TLAC] WindowMessageDispatcher(): CreateWindowW() Error: %d\n", GetLastError()); + return 1; + } + + MessageThread.ID = GetCurrentThreadId(); + + MSG message; + DWORD returnValue; + + printf("[TLAC] WindowMessageDispatcher(): Entering message loop...\n"); + + while (1) + { + returnValue = GetMessage(&message, NULL, 0, 0); + if (returnValue != -1) + { + TranslateMessage(&message); + DispatchMessage(&message); + } + else + { + printf("[TLAC] WindowMessageDispatcher(): GetMessage() Error: %d\n", returnValue); + } + } + + DestroyWindow(windowHandle); + return 0; +} + +BOOL RegisterDeviceInterface(HWND hWnd, HDEVNOTIFY* hDeviceNotify) +{ + DEV_BROADCAST_DEVICEINTERFACE NotificationFilter = {}; + + NotificationFilter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE); + NotificationFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE; + + *hDeviceNotify = RegisterDeviceNotification(hWnd, &NotificationFilter, DEVICE_NOTIFY_ALL_INTERFACE_CLASSES); + + return *hDeviceNotify != NULL; +} + +LRESULT CALLBACK MessageWindowProcessCallback(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch (message) + { + case WM_CREATE: + { + HDEVNOTIFY hDevNotify = NULL; + + if (!RegisterDeviceInterface(hWnd, &hDevNotify)) + printf("[TLAC] MessageWindowProcessCallback(): RegisterDeviceInterface() Error: %d\n", GetLastError()); + + break; + } + + case WM_DEVICECHANGE: + { + switch (wParam) + { + case DBT_DEVICEARRIVAL: + TLAC::DeviceConnected = true; + break; + + default: + break; + } + } + + default: + return DefWindowProc(hWnd, message, wParam, lParam); + } + + return 0; +} + +VOID RegisterMessageWindowClass() +{ + WNDCLASS windowClass = { }; + + windowClass.lpfnWndProc = MessageWindowProcessCallback; + windowClass.hInstance = TLAC::framework::Module; + windowClass.lpszClassName = MessageWindowClassName; + + RegisterClass(&windowClass); +} + +void hookedEngineUpdate() +{ + TLAC::UpdateTick(); + //divaEngineUpdate(); +} + +BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) +{ + HWND consoleHandle = GetConsoleWindow(); + + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + + // Are you a dev and want to debug? Want to always be able to watch the console upon game initialization? Comment this. + ShowWindow(consoleHandle, SW_HIDE); + + printf("[TLAC] DllMain(): Installing hooks...\n"); + + + //InstallHook((void*)ENGINE_UPDATE_HOOK_TARGET_ADDRESS, (void*)UpdateTick, 0xE); + DisableThreadLibraryCalls(hModule); + DetourTransactionBegin(); + DetourUpdateThread(GetCurrentThread()); + DetourAttach(&(PVOID&)divaEngineUpdate, hookedEngineUpdate); + DetourTransactionCommit(); + + TLAC::InitializeExtraSettings(); + + TLAC::framework::Module = hModule; + break; + + case DLL_PROCESS_DETACH: + TLAC::Dispose(); + break; + } + + return TRUE; +} \ No newline at end of file diff --git a/source/plugins/TLAC/framework.cpp b/source/plugins/TLAC/framework.cpp new file mode 100644 index 0000000..7d629c2 --- /dev/null +++ b/source/plugins/TLAC/framework.cpp @@ -0,0 +1,40 @@ +#include "framework.h" +#include + +namespace TLAC +{ + typedef std::filesystem::path fspath; + + std::string *framework::moduleDirectory; + + const wchar_t* framework::DivaWindowName = L"Hatsune Miku Project DIVA Arcade Future Tone"; + const wchar_t* framework::GlutDefaultName = L"GLUT"; + const wchar_t* framework::freeGlutDefaultName = L"FREEGLUT"; + + HWND framework::DivaWindowHandle; + HMODULE framework::Module; + + bool framework::inputDisable = false; + + std::string framework::GetModuleDirectory() + { + if (moduleDirectory == nullptr) + { + WCHAR modulePathBuffer[MAX_PATH]; + GetModuleFileNameW(framework::Module, modulePathBuffer, MAX_PATH); + + fspath configPath = fspath(modulePathBuffer).parent_path(); + moduleDirectory = new std::string(configPath.u8string()); + } + + return *moduleDirectory; + } + + RECT framework::GetWindowBounds() + { + RECT windowRect; + GetWindowRect(DivaWindowHandle, &windowRect); + + return windowRect; + } +} diff --git a/source/plugins/TLAC/framework.h b/source/plugins/TLAC/framework.h new file mode 100644 index 0000000..85f893e --- /dev/null +++ b/source/plugins/TLAC/framework.h @@ -0,0 +1,25 @@ +#pragma once +#include +#include + +namespace TLAC +{ + class framework + { + private: + static std::string *moduleDirectory; + + public: + static const wchar_t* DivaWindowName; + static const wchar_t* GlutDefaultName; + static const wchar_t* freeGlutDefaultName; + + static bool inputDisable; + + static HWND DivaWindowHandle; + static HMODULE Module; + + static std::string GetModuleDirectory(); + static RECT GetWindowBounds(); + }; +}