├── .github
└── ISSUE_TEMPLATE
│ └── bug_report.md
├── .gitignore
├── HintMachine.sln
├── HintMachine
├── App.config
├── App.xaml
├── App.xaml.cs
├── Assets
│ ├── Notification.wav
│ ├── covers
│ │ ├── 911_operator.png
│ │ ├── advance_wars_dual_strike.png
│ │ ├── bpm.png
│ │ ├── bust_a_move_4.png
│ │ ├── columns.png
│ │ ├── dorfromantik.png
│ │ ├── dragons_crown.png
│ │ ├── fzero_gx.png
│ │ ├── geometry_wars_galaxies.png
│ │ ├── geometry_wars_re.png
│ │ ├── islanders.png
│ │ ├── katamari_damacy_reroll.png
│ │ ├── luck_be_a_landord.png
│ │ ├── meteos.png
│ │ ├── metroid_prime_pinball.png
│ │ ├── minesweeper_classy.png
│ │ ├── mini_metro.png
│ │ ├── nexmachina.png
│ │ ├── nubbysnumberfactory.png
│ │ ├── one_finger_death_punch.png
│ │ ├── pacman_cedx.png
│ │ ├── paint_the_town_red.png
│ │ ├── papers_please.png
│ │ ├── peggle_deluxe.png
│ │ ├── peggle_nights.png
│ │ ├── pokemon_pinball_rs.png
│ │ ├── pokemon_puzzle_challenge.png
│ │ ├── puyo_puyo_2.png
│ │ ├── puyo_puyo_tetris.png
│ │ ├── rollcage_2.png
│ │ ├── sonic_blue_spheres.png
│ │ ├── stargunner.png
│ │ ├── super_hexagon.png
│ │ ├── super_mega_baseball_2.png
│ │ ├── super_monkey_ball.png
│ │ ├── super_monkey_ball_2.png
│ │ ├── tetris_effect_connected.png
│ │ ├── tetris_nes.png
│ │ ├── tmnt_shredders_revenge.png
│ │ ├── tony_hawks_pro_skater_1_2.png
│ │ ├── unknown.png
│ │ ├── xenotilt.png
│ │ └── zachtronics_solitaire_collection.png
│ ├── icon.ico
│ ├── icon.png
│ ├── logo.svg
│ └── logo_small.png
├── Helpers
│ ├── BizhawkHelper.cs
│ └── MegadriveRamAdapter.cs
├── HintMachine.csproj
├── HintMachine.csproj.user
├── HintMachine.user
├── Models
│ ├── ArchipelagoHintSession.cs
│ ├── Games
│ │ ├── AdvanceWarsDualStrikeConnector.cs
│ │ ├── BPMConnector.cs
│ │ ├── BustAMove4Connector.cs
│ │ ├── ColumnsConnector.cs
│ │ ├── DorfromantikConnector.cs
│ │ ├── DragonCrownConnector.cs
│ │ ├── FZeroGXConnector.cs
│ │ ├── GeometryWarsConnector.cs
│ │ ├── GeometryWarsGalaxiesConnector.cs
│ │ ├── IslandersConnector.cs
│ │ ├── KatamariDamacyRerollConnector.cs
│ │ ├── LuckBeALandlordConnector.cs
│ │ ├── MeteosConnector.cs
│ │ ├── MetroidPrimePinballConnector.cs
│ │ ├── MinesweeperClassyConnector.cs
│ │ ├── MiniMetroConnector.cs
│ │ ├── NexMachinaConnector.cs
│ │ ├── NubbysNumberFactoryConnector.cs
│ │ ├── NuclearThroneConnector.cs
│ │ ├── OneFingerDeathPunchConnector.cs
│ │ ├── Operator911Connector.cs
│ │ ├── PacManChampionshipEditionDXConnector.cs
│ │ ├── PaintTheTownRedConnector.cs
│ │ ├── PapersPleaseConnector.cs
│ │ ├── PeggleDeluxeConnector.cs
│ │ ├── PeggleNightsConnector.cs
│ │ ├── PokemonPinballRSConnector.cs
│ │ ├── PokemonPuzzleChallengeConnector.cs
│ │ ├── PuyoPuyo2MDConnector.cs
│ │ ├── PuyoTetrisConnector.cs
│ │ ├── Rollcage2Connector.cs
│ │ ├── SonicBlueSpheresConnector.cs
│ │ ├── StargunnerConnector.cs
│ │ ├── SuperHexagonConnector.cs
│ │ ├── SuperMegaBaseball2Connector.cs
│ │ ├── SuperMonkeyBall2Connector.cs
│ │ ├── SuperMonkeyBallConnector.cs
│ │ ├── TMNTShreddersRevengeConnector.cs
│ │ ├── TetrisEffectConnector.cs
│ │ ├── TetrisNESConnector.cs
│ │ ├── TonyHawksProSkater12Connector.cs
│ │ ├── XenotiltConnector.cs
│ │ └── ZachtronicsSolitaireConnector.cs
│ ├── GenericConnectors
│ │ ├── IDolphinConnector.cs
│ │ ├── IEmulatorConnector.cs
│ │ ├── IGameConnector.cs
│ │ ├── IGameboyAdvanceConnector.cs
│ │ ├── IGameboyColorConnector.cs
│ │ ├── IMegadriveConnector.cs
│ │ ├── INESConnector.cs
│ │ ├── INintendoDSConnector.cs
│ │ └── IPlayStationConnector.cs
│ ├── Globals.cs
│ ├── HintDetails.cs
│ ├── HintQuest.cs
│ ├── HintQuestCounter.cs
│ ├── HintQuestCumulative.cs
│ ├── Logger.cs
│ ├── ProcessRamWatcher.cs
│ └── Settings.cs
├── Properties
│ ├── AssemblyInfo.cs
│ ├── Resources.Designer.cs
│ ├── Resources.resx
│ ├── Settings.Designer.cs
│ └── Settings.settings
├── Views
│ ├── GameSelectionWindow.xaml
│ ├── GameSelectionWindow.xaml.cs
│ ├── HintsView.xaml
│ ├── HintsView.xaml.cs
│ ├── LoginWindow.xaml
│ ├── LoginWindow.xaml.cs
│ ├── MainWindow.xaml
│ ├── MainWindow.xaml.cs
│ ├── ManualHintWindow.xaml
│ ├── ManualHintWindow.xaml.cs
│ ├── MessageLog.xaml
│ ├── MessageLog.xaml.cs
│ ├── UpdateAvailablePopUp.xaml
│ └── UpdateAvailablePopUp.xaml.cs
└── packages.config
├── LICENSE
├── README.md
├── ThreadstackFinder
├── ThreadstackFinder.vcxproj
├── ThreadstackFinder.vcxproj.filters
├── ThreadstackFinder.vcxproj.user
└── main.cpp
└── adding_games.md
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Report a bug on the software or a game connector
4 | title: 'GAME_NAME: Bug description'
5 | labels: bug
6 | assignees: ''
7 |
8 | ---
9 |
10 | **HintMachine version**: ???
11 | **Game**: ??? (if relevant)
12 | **Game / Emulator version**: ??? (if relevant)
13 |
14 | ## Bug description
15 |
16 | A clear and concise description of what the bug is, and which game is impacted if relevant.
17 |
18 | ## Screenshots
19 |
20 | If applicable, add screenshots to help explain your problem.
21 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | HintMachine/obj
2 | HintMachine/bin
3 | ThreadStackFinder/bin/
4 | .vs
5 | packages/
--------------------------------------------------------------------------------
/HintMachine.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.7.34003.232
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HintMachine", "HintMachine\HintMachine.csproj", "{F07DABDF-B962-4696-B9DA-5E18E6E83D56}"
7 | ProjectSection(ProjectDependencies) = postProject
8 | {E204819B-3674-4252-91B7-C45252CEDA56} = {E204819B-3674-4252-91B7-C45252CEDA56}
9 | EndProjectSection
10 | EndProject
11 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ThreadstackFinder", "ThreadstackFinder\ThreadstackFinder.vcxproj", "{E204819B-3674-4252-91B7-C45252CEDA56}"
12 | EndProject
13 | Global
14 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
15 | Debug|x64 = Debug|x64
16 | Debug|x86 = Debug|x86
17 | Release|x64 = Release|x64
18 | Release|x86 = Release|x86
19 | EndGlobalSection
20 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
21 | {F07DABDF-B962-4696-B9DA-5E18E6E83D56}.Debug|x64.ActiveCfg = Debug|x64
22 | {F07DABDF-B962-4696-B9DA-5E18E6E83D56}.Debug|x64.Build.0 = Debug|x64
23 | {F07DABDF-B962-4696-B9DA-5E18E6E83D56}.Debug|x86.ActiveCfg = Debug|x64
24 | {F07DABDF-B962-4696-B9DA-5E18E6E83D56}.Debug|x86.Build.0 = Debug|x64
25 | {F07DABDF-B962-4696-B9DA-5E18E6E83D56}.Release|x64.ActiveCfg = Release|x64
26 | {F07DABDF-B962-4696-B9DA-5E18E6E83D56}.Release|x64.Build.0 = Release|x64
27 | {F07DABDF-B962-4696-B9DA-5E18E6E83D56}.Release|x86.ActiveCfg = Release|x64
28 | {F07DABDF-B962-4696-B9DA-5E18E6E83D56}.Release|x86.Build.0 = Release|x64
29 | {E204819B-3674-4252-91B7-C45252CEDA56}.Debug|x64.ActiveCfg = Debug|x64
30 | {E204819B-3674-4252-91B7-C45252CEDA56}.Debug|x64.Build.0 = Debug|x64
31 | {E204819B-3674-4252-91B7-C45252CEDA56}.Debug|x86.ActiveCfg = Debug|Win32
32 | {E204819B-3674-4252-91B7-C45252CEDA56}.Debug|x86.Build.0 = Debug|Win32
33 | {E204819B-3674-4252-91B7-C45252CEDA56}.Release|x64.ActiveCfg = Release|x64
34 | {E204819B-3674-4252-91B7-C45252CEDA56}.Release|x64.Build.0 = Release|x64
35 | {E204819B-3674-4252-91B7-C45252CEDA56}.Release|x86.ActiveCfg = Release|Win32
36 | {E204819B-3674-4252-91B7-C45252CEDA56}.Release|x86.Build.0 = Release|Win32
37 | EndGlobalSection
38 | GlobalSection(SolutionProperties) = preSolution
39 | HideSolutionNode = FALSE
40 | EndGlobalSection
41 | GlobalSection(ExtensibilityGlobals) = postSolution
42 | SolutionGuid = {17E0F06A-B0F0-4B62-BFE3-AEF219377F48}
43 | EndGlobalSection
44 | EndGlobal
45 |
--------------------------------------------------------------------------------
/HintMachine/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/HintMachine/App.xaml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/HintMachine/App.xaml.cs:
--------------------------------------------------------------------------------
1 | using System.Windows;
2 |
3 | namespace HintMachine
4 | {
5 | ///
6 | /// Interaction logic for App.xaml
7 | ///
8 | public partial class App : Application
9 | {}
10 | }
11 |
--------------------------------------------------------------------------------
/HintMachine/Assets/Notification.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HintMachine/hintMachine/24c97be899feeacf270cc81aa1027687e38c446a/HintMachine/Assets/Notification.wav
--------------------------------------------------------------------------------
/HintMachine/Assets/covers/911_operator.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HintMachine/hintMachine/24c97be899feeacf270cc81aa1027687e38c446a/HintMachine/Assets/covers/911_operator.png
--------------------------------------------------------------------------------
/HintMachine/Assets/covers/advance_wars_dual_strike.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HintMachine/hintMachine/24c97be899feeacf270cc81aa1027687e38c446a/HintMachine/Assets/covers/advance_wars_dual_strike.png
--------------------------------------------------------------------------------
/HintMachine/Assets/covers/bpm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HintMachine/hintMachine/24c97be899feeacf270cc81aa1027687e38c446a/HintMachine/Assets/covers/bpm.png
--------------------------------------------------------------------------------
/HintMachine/Assets/covers/bust_a_move_4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HintMachine/hintMachine/24c97be899feeacf270cc81aa1027687e38c446a/HintMachine/Assets/covers/bust_a_move_4.png
--------------------------------------------------------------------------------
/HintMachine/Assets/covers/columns.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HintMachine/hintMachine/24c97be899feeacf270cc81aa1027687e38c446a/HintMachine/Assets/covers/columns.png
--------------------------------------------------------------------------------
/HintMachine/Assets/covers/dorfromantik.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HintMachine/hintMachine/24c97be899feeacf270cc81aa1027687e38c446a/HintMachine/Assets/covers/dorfromantik.png
--------------------------------------------------------------------------------
/HintMachine/Assets/covers/dragons_crown.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HintMachine/hintMachine/24c97be899feeacf270cc81aa1027687e38c446a/HintMachine/Assets/covers/dragons_crown.png
--------------------------------------------------------------------------------
/HintMachine/Assets/covers/fzero_gx.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HintMachine/hintMachine/24c97be899feeacf270cc81aa1027687e38c446a/HintMachine/Assets/covers/fzero_gx.png
--------------------------------------------------------------------------------
/HintMachine/Assets/covers/geometry_wars_galaxies.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HintMachine/hintMachine/24c97be899feeacf270cc81aa1027687e38c446a/HintMachine/Assets/covers/geometry_wars_galaxies.png
--------------------------------------------------------------------------------
/HintMachine/Assets/covers/geometry_wars_re.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HintMachine/hintMachine/24c97be899feeacf270cc81aa1027687e38c446a/HintMachine/Assets/covers/geometry_wars_re.png
--------------------------------------------------------------------------------
/HintMachine/Assets/covers/islanders.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HintMachine/hintMachine/24c97be899feeacf270cc81aa1027687e38c446a/HintMachine/Assets/covers/islanders.png
--------------------------------------------------------------------------------
/HintMachine/Assets/covers/katamari_damacy_reroll.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HintMachine/hintMachine/24c97be899feeacf270cc81aa1027687e38c446a/HintMachine/Assets/covers/katamari_damacy_reroll.png
--------------------------------------------------------------------------------
/HintMachine/Assets/covers/luck_be_a_landord.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HintMachine/hintMachine/24c97be899feeacf270cc81aa1027687e38c446a/HintMachine/Assets/covers/luck_be_a_landord.png
--------------------------------------------------------------------------------
/HintMachine/Assets/covers/meteos.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HintMachine/hintMachine/24c97be899feeacf270cc81aa1027687e38c446a/HintMachine/Assets/covers/meteos.png
--------------------------------------------------------------------------------
/HintMachine/Assets/covers/metroid_prime_pinball.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HintMachine/hintMachine/24c97be899feeacf270cc81aa1027687e38c446a/HintMachine/Assets/covers/metroid_prime_pinball.png
--------------------------------------------------------------------------------
/HintMachine/Assets/covers/minesweeper_classy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HintMachine/hintMachine/24c97be899feeacf270cc81aa1027687e38c446a/HintMachine/Assets/covers/minesweeper_classy.png
--------------------------------------------------------------------------------
/HintMachine/Assets/covers/mini_metro.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HintMachine/hintMachine/24c97be899feeacf270cc81aa1027687e38c446a/HintMachine/Assets/covers/mini_metro.png
--------------------------------------------------------------------------------
/HintMachine/Assets/covers/nexmachina.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HintMachine/hintMachine/24c97be899feeacf270cc81aa1027687e38c446a/HintMachine/Assets/covers/nexmachina.png
--------------------------------------------------------------------------------
/HintMachine/Assets/covers/nubbysnumberfactory.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HintMachine/hintMachine/24c97be899feeacf270cc81aa1027687e38c446a/HintMachine/Assets/covers/nubbysnumberfactory.png
--------------------------------------------------------------------------------
/HintMachine/Assets/covers/one_finger_death_punch.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HintMachine/hintMachine/24c97be899feeacf270cc81aa1027687e38c446a/HintMachine/Assets/covers/one_finger_death_punch.png
--------------------------------------------------------------------------------
/HintMachine/Assets/covers/pacman_cedx.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HintMachine/hintMachine/24c97be899feeacf270cc81aa1027687e38c446a/HintMachine/Assets/covers/pacman_cedx.png
--------------------------------------------------------------------------------
/HintMachine/Assets/covers/paint_the_town_red.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HintMachine/hintMachine/24c97be899feeacf270cc81aa1027687e38c446a/HintMachine/Assets/covers/paint_the_town_red.png
--------------------------------------------------------------------------------
/HintMachine/Assets/covers/papers_please.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HintMachine/hintMachine/24c97be899feeacf270cc81aa1027687e38c446a/HintMachine/Assets/covers/papers_please.png
--------------------------------------------------------------------------------
/HintMachine/Assets/covers/peggle_deluxe.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HintMachine/hintMachine/24c97be899feeacf270cc81aa1027687e38c446a/HintMachine/Assets/covers/peggle_deluxe.png
--------------------------------------------------------------------------------
/HintMachine/Assets/covers/peggle_nights.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HintMachine/hintMachine/24c97be899feeacf270cc81aa1027687e38c446a/HintMachine/Assets/covers/peggle_nights.png
--------------------------------------------------------------------------------
/HintMachine/Assets/covers/pokemon_pinball_rs.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HintMachine/hintMachine/24c97be899feeacf270cc81aa1027687e38c446a/HintMachine/Assets/covers/pokemon_pinball_rs.png
--------------------------------------------------------------------------------
/HintMachine/Assets/covers/pokemon_puzzle_challenge.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HintMachine/hintMachine/24c97be899feeacf270cc81aa1027687e38c446a/HintMachine/Assets/covers/pokemon_puzzle_challenge.png
--------------------------------------------------------------------------------
/HintMachine/Assets/covers/puyo_puyo_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HintMachine/hintMachine/24c97be899feeacf270cc81aa1027687e38c446a/HintMachine/Assets/covers/puyo_puyo_2.png
--------------------------------------------------------------------------------
/HintMachine/Assets/covers/puyo_puyo_tetris.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HintMachine/hintMachine/24c97be899feeacf270cc81aa1027687e38c446a/HintMachine/Assets/covers/puyo_puyo_tetris.png
--------------------------------------------------------------------------------
/HintMachine/Assets/covers/rollcage_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HintMachine/hintMachine/24c97be899feeacf270cc81aa1027687e38c446a/HintMachine/Assets/covers/rollcage_2.png
--------------------------------------------------------------------------------
/HintMachine/Assets/covers/sonic_blue_spheres.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HintMachine/hintMachine/24c97be899feeacf270cc81aa1027687e38c446a/HintMachine/Assets/covers/sonic_blue_spheres.png
--------------------------------------------------------------------------------
/HintMachine/Assets/covers/stargunner.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HintMachine/hintMachine/24c97be899feeacf270cc81aa1027687e38c446a/HintMachine/Assets/covers/stargunner.png
--------------------------------------------------------------------------------
/HintMachine/Assets/covers/super_hexagon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HintMachine/hintMachine/24c97be899feeacf270cc81aa1027687e38c446a/HintMachine/Assets/covers/super_hexagon.png
--------------------------------------------------------------------------------
/HintMachine/Assets/covers/super_mega_baseball_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HintMachine/hintMachine/24c97be899feeacf270cc81aa1027687e38c446a/HintMachine/Assets/covers/super_mega_baseball_2.png
--------------------------------------------------------------------------------
/HintMachine/Assets/covers/super_monkey_ball.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HintMachine/hintMachine/24c97be899feeacf270cc81aa1027687e38c446a/HintMachine/Assets/covers/super_monkey_ball.png
--------------------------------------------------------------------------------
/HintMachine/Assets/covers/super_monkey_ball_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HintMachine/hintMachine/24c97be899feeacf270cc81aa1027687e38c446a/HintMachine/Assets/covers/super_monkey_ball_2.png
--------------------------------------------------------------------------------
/HintMachine/Assets/covers/tetris_effect_connected.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HintMachine/hintMachine/24c97be899feeacf270cc81aa1027687e38c446a/HintMachine/Assets/covers/tetris_effect_connected.png
--------------------------------------------------------------------------------
/HintMachine/Assets/covers/tetris_nes.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HintMachine/hintMachine/24c97be899feeacf270cc81aa1027687e38c446a/HintMachine/Assets/covers/tetris_nes.png
--------------------------------------------------------------------------------
/HintMachine/Assets/covers/tmnt_shredders_revenge.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HintMachine/hintMachine/24c97be899feeacf270cc81aa1027687e38c446a/HintMachine/Assets/covers/tmnt_shredders_revenge.png
--------------------------------------------------------------------------------
/HintMachine/Assets/covers/tony_hawks_pro_skater_1_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HintMachine/hintMachine/24c97be899feeacf270cc81aa1027687e38c446a/HintMachine/Assets/covers/tony_hawks_pro_skater_1_2.png
--------------------------------------------------------------------------------
/HintMachine/Assets/covers/unknown.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HintMachine/hintMachine/24c97be899feeacf270cc81aa1027687e38c446a/HintMachine/Assets/covers/unknown.png
--------------------------------------------------------------------------------
/HintMachine/Assets/covers/xenotilt.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HintMachine/hintMachine/24c97be899feeacf270cc81aa1027687e38c446a/HintMachine/Assets/covers/xenotilt.png
--------------------------------------------------------------------------------
/HintMachine/Assets/covers/zachtronics_solitaire_collection.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HintMachine/hintMachine/24c97be899feeacf270cc81aa1027687e38c446a/HintMachine/Assets/covers/zachtronics_solitaire_collection.png
--------------------------------------------------------------------------------
/HintMachine/Assets/icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HintMachine/hintMachine/24c97be899feeacf270cc81aa1027687e38c446a/HintMachine/Assets/icon.ico
--------------------------------------------------------------------------------
/HintMachine/Assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HintMachine/hintMachine/24c97be899feeacf270cc81aa1027687e38c446a/HintMachine/Assets/icon.png
--------------------------------------------------------------------------------
/HintMachine/Assets/logo_small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HintMachine/hintMachine/24c97be899feeacf270cc81aa1027687e38c446a/HintMachine/Assets/logo_small.png
--------------------------------------------------------------------------------
/HintMachine/Helpers/BizhawkHelper.cs:
--------------------------------------------------------------------------------
1 | using HintMachine.Models;
2 |
3 | namespace HintMachine.Helpers
4 | {
5 | static class BizhawkHelper
6 | {
7 | public static long GetCurrentFrameCount(ProcessRamWatcher ram)
8 | {
9 | // long framecountAddr = ram.ResolvePointerPath64(ram.Threadstack0 - 0xF48, new int[] { 0x8, 0x200, 0x10, 0x38 });
10 | // return ram.ReadUint32(framecountAddr);
11 | return 0;
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/HintMachine/Helpers/MegadriveRamAdapter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace HintMachine.Models.GenericConnectors
4 | {
5 | public class InvalidMegadriveAddressException : Exception
6 | {
7 | public InvalidMegadriveAddressException(string message) : base(message)
8 | { }
9 | }
10 |
11 | public class MegadriveRamAdapter : ProcessRamWatcher
12 | {
13 | public MegadriveRamAdapter(BinaryTarget target) : base(target)
14 | {}
15 |
16 | public new ushort ReadUint16(long address)
17 | {
18 | if (address % 2 != 0)
19 | throw new InvalidMegadriveAddressException("Cannot read a word or long starting at an odd address");
20 |
21 | return BitConverter.ToUInt16(ReadBytes(address, sizeof(ushort)), 0);
22 | }
23 |
24 | public new uint ReadUint32(long address)
25 | {
26 | ushort msWord = ReadUint16(address);
27 | ushort lsWord = ReadUint16(address+2);
28 | return (uint)(msWord << 16 + lsWord);
29 | }
30 |
31 | public new byte ReadUint8(long address)
32 | {
33 | ushort word = ReadUint16(address - (address % 2));
34 | if (address % 2 == 0)
35 | return (byte)(word >> 8);
36 | else
37 | return (byte)(word & 0x00FF);
38 | }
39 |
40 | public new ulong ReadUint64(long address)
41 | => throw new NotSupportedException();
42 |
43 | public new sbyte ReadInt8(long address)
44 | => (sbyte)ReadUint8(address);
45 |
46 | public new short ReadInt16(long address)
47 | => (short)ReadUint16(address);
48 |
49 | public new int ReadInt32(long address)
50 | => (int)ReadUint32(address);
51 |
52 | public new long ReadInt64(long address)
53 | => throw new NotSupportedException();
54 |
55 | public new double ReadDouble(long address)
56 | => throw new NotSupportedException();
57 |
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/HintMachine/HintMachine.csproj.user:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | publish\
5 |
6 |
7 |
8 |
9 |
10 | en-US
11 | false
12 |
13 |
--------------------------------------------------------------------------------
/HintMachine/HintMachine.user:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | true
5 |
6 |
7 | true
8 |
9 |
10 | false
11 |
12 |
--------------------------------------------------------------------------------
/HintMachine/Models/Games/AdvanceWarsDualStrikeConnector.cs:
--------------------------------------------------------------------------------
1 | using HintMachine.Models.GenericConnectors;
2 |
3 | namespace HintMachine.Models.Games
4 | {
5 | class AdvanceWarsDualStrikeConnector : INintendoDSConnector
6 | {
7 | private readonly HintQuestCumulative _basesCapturedQuest = new HintQuestCumulative
8 | {
9 | Name = "Bases Captured",
10 | GoalValue = 5,
11 | MaxIncrease = 1
12 | };
13 |
14 | private readonly HintQuestCumulative _unitsDestroyedQuest = new HintQuestCumulative
15 | {
16 | Name = "Units Destroyed",
17 | GoalValue = 15,
18 | MaxIncrease = 1,
19 | };
20 |
21 | private const string GAME_VERSION_PAL = "E4189A354066300BA95AA4F86458FB4CD02E69B722E1CF3F1D65EC3CE39EBE4E";
22 | private const string GAME_VERSION_NTSC_U = "66DA2A57878F367DE4A91A00CC14A2B37713D511E3F24814942C6F0C35A55EB1";
23 | private const string GAME_VERSION_NTSC_J = "8E1D5E689EA6C9805F2E2414A033EFD552F89ED00E6880AD5427CFC4BB70BE5E";
24 |
25 | // ---------------------------------------------------------
26 |
27 | public AdvanceWarsDualStrikeConnector()
28 | {
29 | Name = "Advance Wars: Dual Strike";
30 | Description = "Advance Wars: Dual Strike is the third installment in the Advance Wars series (first on DS media). Advance Wars is the international title of the Wars video game series, which dates back to the Family Computer game Famicom Wars in 1988.\r\nThe storyline is a continuation of the previous series and is set in the new location of Omega Land. Black Hole has returned under the leadership of a new commander who seeks to give himself eternal life by draining the energy of Omega Land. The Allied Nations struggle to overcome this threat and are eventually joined by several former Black Hole commanding officers in an effort to save the land.";
31 | CoverFilename = "advance_wars_dual_strike.png";
32 | Author = "Dinopony";
33 |
34 | SupportedVersions.Add("PAL (🇪🇺)");
35 | SupportedVersions.Add("NTSC-U (🇺🇸)");
36 | SupportedVersions.Add("NTSC-J (🇯🇵)");
37 |
38 | Quests.Add(_basesCapturedQuest);
39 | Quests.Add(_unitsDestroyedQuest);
40 |
41 | ValidROMs.Add(GAME_VERSION_PAL);
42 | ValidROMs.Add(GAME_VERSION_NTSC_U);
43 | ValidROMs.Add(GAME_VERSION_NTSC_J);
44 | }
45 |
46 | protected override bool Poll()
47 | {
48 | long medalsDataOffset;
49 | if (CurrentROM == GAME_VERSION_PAL)
50 | medalsDataOffset = 0x2BEF28;
51 | else if (CurrentROM == GAME_VERSION_NTSC_U)
52 | medalsDataOffset = 0x290A94;
53 | else /* if (CurrentROM == GAME_VERSION_NTSC_J) */
54 | medalsDataOffset = 0x2A8270;
55 |
56 | uint basesCaptured = _ram.ReadUint32(RamBaseAddress + medalsDataOffset + 0x3C);
57 | _basesCapturedQuest.UpdateValue(basesCaptured);
58 |
59 | uint unitsDestroyed = 0;
60 | for(int i=0; i<26; ++i)
61 | unitsDestroyed += _ram.ReadUint32(RamBaseAddress + medalsDataOffset + 0x138 + (i * 4));
62 | _unitsDestroyedQuest.UpdateValue(unitsDestroyed);
63 |
64 | return true;
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/HintMachine/Models/Games/BPMConnector.cs:
--------------------------------------------------------------------------------
1 | using HintMachine.Models.GenericConnectors;
2 |
3 | namespace HintMachine.Models.Games
4 | {
5 | public class BPMConnector : IGameConnector
6 | {
7 | private readonly HintQuestCumulative _enemiesKilledQuest = new HintQuestCumulative
8 | {
9 | Name = "Enemies killed",
10 | GoalValue = 80,
11 | MaxIncrease = 5
12 | };
13 |
14 | private readonly HintQuestCumulative _goldSpentQuest = new HintQuestCumulative
15 | {
16 | Name = "Gold spent in shops",
17 | GoalValue = 40,
18 | MaxIncrease = 30
19 | };
20 |
21 | private ProcessRamWatcher _ram = null;
22 |
23 | // -----------------------------------------------------------------------
24 |
25 | public BPMConnector()
26 | {
27 | Name = "BPM: Bullets Per Minute";
28 | Description = "BPM: Bullets Per Minute is a rhythm-based FPS roguelite.\n\nIn BPM, all of your actions and the actions of your enemies are tied to the beat of the music. Your enemies perform a dance-like sequence of attacks to an epic rock opera. BPM is inspired by retro shooters of the 90’s. It is fast, frenetic and rhythmical. You can double jump, dash, rocket jump and bunny hop to evade your opponents.";
29 | Platform = "PC";
30 | SupportedVersions.Add("Steam");
31 | CoverFilename = "bpm.png";
32 | Author = "Dinopony";
33 |
34 | Quests.Add(_enemiesKilledQuest);
35 | Quests.Add(_goldSpentQuest);
36 | }
37 |
38 | protected override bool Connect()
39 | {
40 | _ram = new ProcessRamWatcher("BPMGame-Win64-Shipping");
41 | return _ram.TryConnect();
42 | }
43 |
44 | public override void Disconnect()
45 | {
46 | _ram = null;
47 | }
48 |
49 | protected override bool Poll()
50 | {
51 | long lifetimeStatsAddr = _ram.ResolvePointerPath64(_ram.BaseAddress + 0x4952D20, new int[] { 0x288, 0x158 });
52 | if (lifetimeStatsAddr != 0)
53 | {
54 | uint enemiesKilled = _ram.ReadUint32(lifetimeStatsAddr + 0x14);
55 | _enemiesKilledQuest.UpdateValue(enemiesKilled);
56 |
57 | uint goldSpentHuggins = _ram.ReadUint32(lifetimeStatsAddr);
58 | uint goldSpentMunnin = _ram.ReadUint32(lifetimeStatsAddr + 0x4);
59 | _goldSpentQuest.UpdateValue(goldSpentHuggins + goldSpentMunnin);
60 | }
61 |
62 | return true;
63 | }
64 | }
65 | }
--------------------------------------------------------------------------------
/HintMachine/Models/Games/BustAMove4Connector.cs:
--------------------------------------------------------------------------------
1 | using HintMachine.Models.GenericConnectors;
2 |
3 | namespace HintMachine.Models.Games
4 | {
5 | class BustAMove4Connector : IPlayStationConnector
6 | {
7 | private readonly HintQuestCumulative _winQuest = new HintQuestCumulative
8 | {
9 | Name = "Wins",
10 | GoalValue = 3,
11 | MaxIncrease = 1,
12 | };
13 |
14 | public BustAMove4Connector()
15 | {
16 | Name = "Bust-a-Move 4";
17 | Description = "Puzzle Bobble 4 (also known as Bust-a-Move 4 in North America and Europe) is the third sequel to the video game Puzzle Bobble and is the final appearance of the series on the Arcade, PlayStation and Dreamcast. The game is also the final title to be recognizably similar in presentation to the original.\r\n\r\nBuilding upon the success of Puzzle Bobble 3, the game adds a pulley system that requires two sets of bubbles, attached to either side of a rope hanging across two pulleys. The game contains a story mode for single player play.\r\n\r\nIn total, the game features 640 levels. The console version features a level editor to either create and save a level, set a succession of levels, or to create an unlimited amount of extra levels and stages. It also has an alternative \"story mode\".";
18 | SupportedVersions.Add("US ROM");
19 | CoverFilename = "bust_a_move_4.png";
20 | Author = "CalDrac";
21 |
22 | Quests.Add(_winQuest);
23 |
24 | ValidROMs.Add("SLUS_007.54");
25 | }
26 |
27 | protected override bool Poll()
28 | {
29 | _winQuest.UpdateValue(_ram.ReadUint8(RamBaseAddress + 0x167A8A));
30 | return true;
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/HintMachine/Models/Games/ColumnsConnector.cs:
--------------------------------------------------------------------------------
1 | using HintMachine.Models.GenericConnectors;
2 |
3 | namespace HintMachine.Models.Games
4 | {
5 | public class ColumnsConnector : IMegadriveConnector
6 | {
7 | private readonly HintQuestCumulative _jewelsQuest = new HintQuestCumulative
8 | {
9 | Name = "Jewels (Arcade)",
10 | GoalValue = 100,
11 | MaxIncrease = 10,
12 | };
13 |
14 | // ----------------------------------------------------------
15 |
16 | public ColumnsConnector() : base()
17 | {
18 | Name = "Columns";
19 | Description = "Go back in time to a bygone civilization: the ancient world of Phoenicia. There you will play a simple and captivating game where sparkling, rainbow-coloured jewels drop one after another. According to the ancient merchants, by arranging three or more of the same jewels horizontally, vertically or diagonally, you shall perform miracles.";
20 | SupportedVersions.Add("Columns (World)");
21 | CoverFilename = "columns.png";
22 | Author = "Dinopony";
23 |
24 | Quests.Add(_jewelsQuest);
25 |
26 | // REV0
27 | ValidROMs.Add("D84F3E1BBEE9CE8E7FE1401DE9964DB505E3EE41C81A48664983D7A9D39084C8");
28 | // REV1
29 | ValidROMs.Add("A04711E2F511C03D41928091877FE7813EC1049662740962BED76B96CEDD9E9C");
30 | }
31 |
32 | protected override bool Poll()
33 | {
34 | bool inArcadeMode = (_ram.ReadUint8(RamBaseAddress + 0x8464) == 0);
35 | if (inArcadeMode)
36 | _jewelsQuest.UpdateValue(_ram.ReadUint16(RamBaseAddress + 0xC826));
37 |
38 | return true;
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/HintMachine/Models/Games/DorfromantikConnector.cs:
--------------------------------------------------------------------------------
1 | using HintMachine.Models.GenericConnectors;
2 |
3 | namespace HintMachine.Models.Games
4 | {
5 | public class DorfromantikConnector : IGameConnector
6 | {
7 | private readonly BinaryTarget GAME_VERSION_STEAM = new BinaryTarget
8 | {
9 | DisplayName = "Steam",
10 | ProcessName = "Dorfromantik",
11 | ModuleName = "mono-2.0-bdwgc.dll",
12 | Hash = "4C7312A39FA8401AF0D51D210931DAE8C8D85BBFE8E1C7F22E8A553E6D00B0EE"
13 | };
14 |
15 | private readonly HintQuestCumulative _scoreQuest = new HintQuestCumulative
16 | {
17 | Name = "Score",
18 | GoalValue = 1000,
19 | MaxIncrease = 500,
20 | };
21 |
22 | private readonly HintQuestCumulative _questQuest = new HintQuestCumulative
23 | {
24 | Name = "Quests Fulfilled",
25 | GoalValue = 10,
26 | MaxIncrease = 5,
27 | CooldownBetweenIncrements = 20,
28 | };
29 |
30 | private readonly HintQuestCumulative _perfectQuest = new HintQuestCumulative
31 | {
32 | Name = "Perfect Tile Placements",
33 | GoalValue = 10,
34 | MaxIncrease = 3,
35 | CooldownBetweenIncrements = 20,
36 | };
37 |
38 | private ProcessRamWatcher _ram = null;
39 |
40 | private long? _bufferedTileValue = null;
41 |
42 | public DorfromantikConnector()
43 | {
44 | Name = "Dorfromantik";
45 | Description = "Dorfromantik is a peaceful building strategy and puzzle game where you create a beautiful and ever-growing village landscape by placing tiles. Explore a variety of colorful biomes, discover and unlock new tiles and complete quests to fill your world with life!";
46 | Platform = "PC";
47 | SupportedVersions.Add("Steam");
48 | CoverFilename = "dorfromantik.png";
49 | Author = "Serpent.AI";
50 |
51 | Quests.Add(_scoreQuest);
52 | Quests.Add(_questQuest);
53 | Quests.Add(_perfectQuest);
54 | }
55 |
56 | protected override bool Connect()
57 | {
58 | _ram = new ProcessRamWatcher(GAME_VERSION_STEAM);
59 | return _ram.TryConnect();
60 | }
61 |
62 | public override void Disconnect()
63 | {
64 | _ram = null;
65 | }
66 |
67 | protected override bool Poll()
68 | {
69 | long rewardSystemStructAddress = _ram.ResolvePointerPath64(_ram.BaseAddress + 0x716018, new int[] { 0x8, 0x10, 0x48, 0x18, 0xB0, 0x30, 0x0 });
70 |
71 | if (rewardSystemStructAddress != 0)
72 | {
73 | try
74 | {
75 | long tileValue = _ram.ReadUint32(rewardSystemStructAddress + 0x104);
76 | long scoreValue = _ram.ReadUint32(rewardSystemStructAddress + 0x100);
77 | long questValue = _ram.ReadUint32(rewardSystemStructAddress + 0x114);
78 | long perfectValue = _ram.ReadUint32(rewardSystemStructAddress + 0x110);
79 |
80 | if (_bufferedTileValue == null) { _bufferedTileValue = tileValue; }
81 |
82 | if (tileValue < (long)_bufferedTileValue)
83 | {
84 | _scoreQuest.IgnoreNextValue();
85 | _questQuest.IgnoreNextValue();
86 | _perfectQuest.IgnoreNextValue();
87 | }
88 |
89 | _scoreQuest.UpdateValue(scoreValue);
90 | _questQuest.UpdateValue(questValue);
91 | _perfectQuest.UpdateValue(perfectValue);
92 |
93 | _bufferedTileValue = tileValue;
94 | }
95 | catch { }
96 | }
97 |
98 | return true;
99 | }
100 | }
101 | }
--------------------------------------------------------------------------------
/HintMachine/Models/Games/DragonCrownConnector.cs:
--------------------------------------------------------------------------------
1 | using HintMachine.Models.GenericConnectors;
2 |
3 | namespace HintMachine.Models.Games
4 | {
5 | class DragonCrownConnector : IGameConnector
6 | {
7 |
8 | private readonly HintQuestCumulative _scoreQuest = new HintQuestCumulative
9 | {
10 | Name = "Get score",
11 | GoalValue = 100000,
12 | MaxIncrease = 50000
13 | };
14 | private readonly HintQuestCumulative _goldQuest = new HintQuestCumulative
15 | {
16 | Name = "Get Gold",
17 | Description = "Only counts gold obtained during the quests",
18 | GoalValue = 5000,
19 | MaxIncrease = 3000
20 | };
21 |
22 | private ProcessRamWatcher _ram = null;
23 | private long _scoreAddr = 0;
24 | private long _goldAddr = 0;
25 |
26 | // ---------------------------------------------------------
27 |
28 | public DragonCrownConnector()
29 | {
30 | Name = "Dragon's Crown";
31 | Description = "Dragon's Crown is a multiplayer hack-and-slash beat'em up game with breathtaking visual style, a design built around cooperative play, epic boss fights, and the ability to discover a new adventure in every play session.";
32 | SupportedVersions.Add("1.09 US ROM");
33 | SupportedEmulators.Add("Vita3K v0.1.9 3443");
34 |
35 | CoverFilename = "dragons_crown.png";
36 | Platform = "PS Vita";
37 | Author = "CalDrac";
38 |
39 | Quests.Add(_scoreQuest);
40 | Quests.Add(_goldQuest);
41 | }
42 |
43 | protected override bool Connect()
44 | {
45 | _ram = new ProcessRamWatcher(GAME_VERSION);
46 | if (!_ram.TryConnect())
47 | return false;
48 |
49 | _scoreAddr = 0x404B3A2A8;
50 | _goldAddr = 0x404B3A26C;
51 | return true;
52 | }
53 |
54 | public override void Disconnect()
55 | {
56 | _ram = null;
57 | }
58 |
59 | protected override bool Poll()
60 | {
61 | uint score = _ram.ReadUint32(_scoreAddr);
62 | uint gold = _ram.ReadUint32(_goldAddr);
63 |
64 | _scoreQuest.UpdateValue(score);
65 | _goldQuest.UpdateValue(gold);
66 | return true;
67 | }
68 |
69 | private readonly BinaryTarget GAME_VERSION = new BinaryTarget
70 | {
71 | DisplayName = "Vita3K (v0.1.9 3443)",
72 | ProcessName = "Vita3K",
73 | Hash = "361D6493E7CA9CEDE67EB1CC59177F55C34B72C58FC8ECBC76A5D704594E7670"
74 | };
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/HintMachine/Models/Games/FZeroGXConnector.cs:
--------------------------------------------------------------------------------
1 | using HintMachine.Models.GenericConnectors;
2 |
3 | namespace HintMachine.Models.Games
4 | {
5 | public class FZeroGXConnector : IDolphinConnector
6 | {
7 | private readonly HintQuestCumulative _cupPointsQuest = new HintQuestCumulative
8 | {
9 | Name = "Cup points",
10 | GoalValue = 200,
11 | MaxIncrease = 100,
12 | CooldownBetweenIncrements = 60
13 | };
14 |
15 | private readonly HintQuestCumulative _knockoutsQuest = new HintQuestCumulative
16 | {
17 | Name = "Knock-outs",
18 | GoalValue = 20,
19 | MaxIncrease = 3,
20 | };
21 |
22 | private bool _wasRacingLastTick = false;
23 |
24 | // ----------------------------------------------------------
25 |
26 | public FZeroGXConnector() : base(false)
27 | {
28 | Name = "F-Zero GX";
29 | Description = "F-Zero GX is the fourth installment in the F-Zero series and the successor to F-Zero X. The game continues the series' difficult, high-speed racing style, retaining the basic gameplay and control system from the Nintendo 64 title. A heavy emphasis is placed on track memorization and reflexes, which aids in completing the title. GX introduces a \"Story Mode\" element, where the player walks in the footsteps of Captain Falcon through nine chapters, completing various missions.";
30 | SupportedVersions.Add("NTSC US");
31 | SupportedVersions.Add("PAL");
32 | CoverFilename = "fzero_gx.png";
33 | Author = "Dinopony";
34 |
35 | Quests.Add(_cupPointsQuest);
36 | Quests.Add(_knockoutsQuest);
37 |
38 | ValidROMs.Add("GFZE01"); // NTSC-U
39 | ValidROMs.Add("GFZP01"); // PAL
40 | }
41 |
42 | protected override bool Poll()
43 | {
44 | bool isPALVersion = (CurrentROM == "GFZP01");
45 |
46 | long isRacingAddr = isPALVersion ? 0x17BF7D : 0x17D7A9;
47 | long knockoutsAddr = isPALVersion ? 0xC10102 : 0xC46A22;
48 | long cupPointsAddr = isPALVersion ? 0x386D48 : 0x378668;
49 |
50 | bool isRacing = (_ram.ReadUint8(MEM1 + isRacingAddr) != 0);
51 | if (_wasRacingLastTick)
52 | {
53 | byte knockoutsCount = _ram.ReadUint8(MEM1 + knockoutsAddr);
54 | _knockoutsQuest.UpdateValue(knockoutsCount);
55 |
56 | ushort cupPoints = _ram.ReadUint16(MEM1 + cupPointsAddr);
57 | _cupPointsQuest.UpdateValue(cupPoints);
58 | }
59 |
60 | _wasRacingLastTick = isRacing;
61 |
62 | return true;
63 | }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/HintMachine/Models/Games/GeometryWarsConnector.cs:
--------------------------------------------------------------------------------
1 | using HintMachine.Models.GenericConnectors;
2 |
3 | namespace HintMachine.Models.Games
4 | {
5 | public class GeometryWarsConnector : IGameConnector
6 | {
7 | private ProcessRamWatcher _ram = null;
8 |
9 | private readonly HintQuestCumulative _scoreQuest = new HintQuestCumulative
10 | {
11 | Name = "Score",
12 | GoalValue = 50000
13 | };
14 |
15 | public GeometryWarsConnector()
16 | {
17 | Name = "Geometry Wars: Retro Evolved";
18 | Description = "Geometry Wars: Retro Evolved is a old school style shooter, but remixed for the 21st century with next generation graphics and deep, modern gameplay. Playing is simple: you are a geometric \"ship\" trapped in a grid world, facing off against waves of deadly wanderers, snakes, and repulsars. Your aim is to survive long enough to set a high score!";
19 | Platform = "PC";
20 | SupportedVersions.Add("Steam");
21 | CoverFilename = "geometry_wars_re.png";
22 | Author = "CalDrac";
23 |
24 | Quests.Add(_scoreQuest);
25 | }
26 |
27 | protected override bool Connect()
28 | {
29 | _ram = new ProcessRamWatcher("GeometryWars");
30 | return _ram.TryConnect();
31 | }
32 |
33 | public override void Disconnect()
34 | {
35 | _ram = null;
36 | }
37 |
38 | protected override bool Poll()
39 | {
40 | long scoreAddress = 0x63C890;
41 | _scoreQuest.UpdateValue(_ram.ReadUint32(scoreAddress));
42 |
43 | return true;
44 | }
45 | }
46 | }
--------------------------------------------------------------------------------
/HintMachine/Models/Games/GeometryWarsGalaxiesConnector.cs:
--------------------------------------------------------------------------------
1 | using HintMachine.Models.GenericConnectors;
2 |
3 | namespace HintMachine.Models.Games
4 | {
5 | public class GeometryWarsGalaxiesConnector : IDolphinConnector
6 | {
7 | private readonly HintQuestCumulative _geomsQuest = new HintQuestCumulative
8 | {
9 | Name = "Geoms collected",
10 | GoalValue = 10000,
11 | MaxIncrease = 200,
12 | };
13 |
14 | // ------------------------------------------------------
15 |
16 | public GeometryWarsGalaxiesConnector() : base(true)
17 | {
18 | Name = "Geometry Wars: Galaxies";
19 | Description = "Geometry Wars: Galaxies is set in a space-like environment where the player must shoot geometrical shapes in order to score points, gain lives, acquire bombs and survive as long as possible. The game is played to an upbeat electro soundtrack.";
20 | SupportedVersions.Add("Europe");
21 | CoverFilename = "geometry_wars_galaxies.png";
22 | Author = "Dinopony";
23 |
24 | Quests.Add(_geomsQuest);
25 |
26 | ValidROMs.Add("RGLP7D"); // PAL
27 | }
28 |
29 | protected override bool Poll()
30 | {
31 | uint geoms = _ram.ReadUint32(MEM2 + 0x23A2AF4);
32 | _geomsQuest.UpdateValue(geoms);
33 |
34 | return true;
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/HintMachine/Models/Games/IslandersConnector.cs:
--------------------------------------------------------------------------------
1 | using HintMachine.Models.GenericConnectors;
2 |
3 | namespace HintMachine.Models.Games
4 | {
5 | public class IslandersConnector : IGameConnector
6 | {
7 | private readonly BinaryTarget GAME_VERSION_STEAM = new BinaryTarget
8 | {
9 | DisplayName = "Steam",
10 | ProcessName = "ISLANDERS",
11 | ModuleName = "mono-2.0-bdwgc.dll",
12 | Hash = "1D29EAED8E610CE4370DB1B396D4DD7C5FED0DB267D18BF97CA91E865100B71E"
13 | };
14 |
15 | private readonly HintQuestCumulative _scoreQuest = new HintQuestCumulative
16 | {
17 | Name = "Score",
18 | GoalValue = 1000,
19 | MaxIncrease = 500,
20 | };
21 |
22 | private readonly HintQuestCumulative _boosterQuest = new HintQuestCumulative
23 | {
24 | Name = "Booster Packs Earned",
25 | GoalValue = 5,
26 | MaxIncrease = 2,
27 | CooldownBetweenIncrements = 15,
28 | };
29 |
30 | private readonly HintQuestCumulative _islandQuest = new HintQuestCumulative
31 | {
32 | Name = "Islands Visited",
33 | GoalValue = 3,
34 | MaxIncrease = 1,
35 | CooldownBetweenIncrements = 60,
36 | };
37 |
38 | private ProcessRamWatcher _ram = null;
39 |
40 | public IslandersConnector()
41 | {
42 | Name = "ISLANDERS";
43 | Description = "Islanders is a minimalist strategy game about building cities on colorful islands. Explore an infinite number of ever-changing new lands, expand your settlements from sprawling villages to vast cities and enjoy the relaxing atmosphere.";
44 | Platform = "PC";
45 | SupportedVersions.Add("Steam");
46 | CoverFilename = "islanders.png";
47 | Author = "Serpent.AI";
48 |
49 | Quests.Add(_scoreQuest);
50 | Quests.Add(_boosterQuest);
51 | Quests.Add(_islandQuest);
52 | }
53 |
54 | protected override bool Connect()
55 | {
56 | _ram = new ProcessRamWatcher(GAME_VERSION_STEAM);
57 | return _ram.TryConnect();
58 | }
59 |
60 | public override void Disconnect()
61 | {
62 | _ram = null;
63 | }
64 |
65 | protected override bool Poll()
66 | {
67 | long localGameManagerStructAddress = _ram.ResolvePointerPath64(_ram.BaseAddress + 0x7521F0, new int[] { 0x210, 0x700, 0x20, 0x5A0 });
68 |
69 | if (localGameManagerStructAddress != 0)
70 | {
71 | try
72 | {
73 | long scoreValue = _ram.ReadUint32(localGameManagerStructAddress + 0xD8);
74 | long boosterValue = _ram.ReadUint32(localGameManagerStructAddress + 0xB8);
75 | long islandValue = _ram.ReadUint32(localGameManagerStructAddress + 0xE8);
76 |
77 | _scoreQuest.UpdateValue(scoreValue);
78 | _boosterQuest.UpdateValue(boosterValue);
79 | _islandQuest.UpdateValue(islandValue);
80 | }
81 | catch { }
82 | }
83 |
84 | return true;
85 | }
86 | }
87 | }
--------------------------------------------------------------------------------
/HintMachine/Models/Games/KatamariDamacyRerollConnector.cs:
--------------------------------------------------------------------------------
1 | using HintMachine.Models.GenericConnectors;
2 |
3 | namespace HintMachine.Models.Games
4 | {
5 | public class KatamariDamacyRerollConnector : IGameConnector
6 | {
7 | private readonly BinaryTarget GAME_VERSION_STEAM = new BinaryTarget
8 | {
9 | DisplayName = "Steam",
10 | ProcessName = "katamari",
11 | ModuleName = "PS2KatamariSimulation.dll",
12 | Hash = "4E1E402B8E66F04E12688A8DD9BE80A061E8DCD7B1A368FAAECA151906474767"
13 | };
14 |
15 | private readonly HintQuestCumulative _objectQuest = new HintQuestCumulative
16 | {
17 | Name = "Objects Collected",
18 | GoalValue = 250,
19 | };
20 |
21 | private readonly HintQuestCumulative _constellationQuest = new HintQuestCumulative
22 | {
23 | Name = "Constellation Objects",
24 | GoalValue = 100,
25 | };
26 |
27 | private ProcessRamWatcher _ram = null;
28 |
29 | public KatamariDamacyRerollConnector()
30 | {
31 | Name = "Katamari Damacy REROLL";
32 | Description = "The stop-at-nothing pushing prince is back and ready to reroll! When the King of All Cosmos accidentally destroys all the stars in the sky, he orders you, his pint-sized princely son, to put the twinkle back in the heavens above. Join the King and Prince of Cosmos on their wacky adventure to restore the stars – now in full HD!\r\n\r\nThe beloved roll-em-up game returns with fully updated graphics, completely recreated cutscenes and in full HD!";
33 | Platform = "PC";
34 | SupportedVersions.Add("Steam");
35 | CoverFilename = "katamari_damacy_reroll.png";
36 | Author = "Serpent.AI";
37 |
38 | Quests.Add(_objectQuest);
39 | Quests.Add(_constellationQuest);
40 | }
41 |
42 | protected override bool Connect()
43 | {
44 | _ram = new ProcessRamWatcher(GAME_VERSION_STEAM);
45 | return _ram.TryConnect();
46 | }
47 |
48 | public override void Disconnect()
49 | {
50 | _ram = null;
51 | }
52 |
53 | protected override bool Poll()
54 | {
55 | try
56 | {
57 | long objectValue = _ram.ReadUint32(_ram.BaseAddress + 0x150EE8);
58 | long constellationValue = _ram.ReadUint32(_ram.BaseAddress + 0x150EEC);
59 |
60 | if (objectValue <= 5000) { _objectQuest.UpdateValue(objectValue); }
61 | if (constellationValue <= 5000) { _constellationQuest.UpdateValue(constellationValue); }
62 | }
63 | catch { }
64 |
65 | return true;
66 | }
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/HintMachine/Models/Games/LuckBeALandlordConnector.cs:
--------------------------------------------------------------------------------
1 | using HintMachine.Models.GenericConnectors;
2 |
3 | namespace HintMachine.Models.Games
4 | {
5 | public class LuckBeALandlordConnector : IGameConnector
6 | {
7 | private readonly BinaryTarget GAME_VERSION_STEAM = new BinaryTarget
8 | {
9 | DisplayName = "Steam",
10 | ProcessName = "Luck be a Landlord",
11 | Hash = "8A6EB15E774ED5F85B2CC8F70CCA4FC3EEFE69898A6AE418982065CAB5665740"
12 | };
13 |
14 | private readonly HintQuestCumulative _coinQuest = new HintQuestCumulative
15 | {
16 | Name = "Coins Spent",
17 | GoalValue = 375,
18 | Direction = HintQuestCumulative.CumulativeDirection.DESCENDING
19 | };
20 |
21 | private ProcessRamWatcher _ram = null;
22 |
23 | private long? _bufferedSpinCount = null;
24 |
25 | public LuckBeALandlordConnector()
26 | {
27 | Name = "Luck be a Landlord";
28 | Description = "Roguelike deckbuilder about using a slot machine to earn rent money and defeat capitalism. Your landlord is knocking on your door. You have one coin left to your name. You insert the coin into your slot machine...and...JACKPOT! Luck be a Landlord, tonight!";
29 | Platform = "PC";
30 | SupportedVersions.Add("Steam");
31 | CoverFilename = "luck_be_a_landord.png";
32 | Author = "Serpent.AI";
33 |
34 | Quests.Add(_coinQuest);
35 | }
36 |
37 | protected override bool Connect()
38 | {
39 | _ram = new ProcessRamWatcher(GAME_VERSION_STEAM);
40 | return _ram.TryConnect();
41 | }
42 |
43 | public override void Disconnect()
44 | {
45 | _ram = null;
46 | }
47 |
48 | protected override bool Poll()
49 | {
50 | long coinStructAddress = _ram.ResolvePointerPath64(_ram.BaseAddress + 0x21EFC90, new int[] { 0x218, 0x108, 0x10, 0x58, 0x20, 0x660 });
51 | long spinStructAddress = _ram.ResolvePointerPath64(_ram.BaseAddress + 0x21EFC90, new int[] { 0x218, 0x108, 0x38, 0x108, 0x0, 0x58, 0x20, 0x2D0 });
52 |
53 | if (coinStructAddress != 0 && spinStructAddress != 0)
54 | {
55 | try
56 | {
57 | long coinValue = (long)_ram.ReadDouble(coinStructAddress + 0x8);
58 | long spinValue = _ram.ReadUint32(spinStructAddress + 0x8);
59 |
60 | if (_bufferedSpinCount == null) { _bufferedSpinCount = spinValue; }
61 |
62 | if (spinValue < (long)_bufferedSpinCount) { _coinQuest.IgnoreNextValue(); }
63 |
64 | _coinQuest.UpdateValue(coinValue);
65 |
66 | _bufferedSpinCount = spinValue;
67 | }
68 | catch { }
69 | }
70 |
71 | return true;
72 | }
73 | }
74 | }
--------------------------------------------------------------------------------
/HintMachine/Models/Games/MeteosConnector.cs:
--------------------------------------------------------------------------------
1 | using HintMachine.Models.GenericConnectors;
2 |
3 | namespace HintMachine.Models.Games
4 | {
5 | class MeteosConnector : INintendoDSConnector
6 | {
7 | private readonly HintQuestCumulative _sendBlocksQuest = new HintQuestCumulative
8 | {
9 | Name = "Send meteos into space",
10 | Description = "Use simple and deluge mode for this quest",
11 | GoalValue = 500,
12 | MaxIncrease = 100
13 | };
14 |
15 | private readonly HintQuestCumulative _starTripQuest = new HintQuestCumulative
16 | {
17 | Name = "Finish Star Trip",
18 | GoalValue = 1,
19 | MaxIncrease = 1,
20 | CooldownBetweenIncrements = 180,
21 | };
22 |
23 | /* Might be added back later
24 | private readonly HintQuestCumulative _levelClearQuest = new HintQuestCumulative
25 | {
26 | Name = "Clear non-boss Star Trip levels",
27 | GoalValue = 6,
28 | MaxIncrease = 6
29 | };
30 | */
31 |
32 | private bool _starTripStarted = false;
33 |
34 | // ---------------------------------------------------------
35 |
36 | public MeteosConnector()
37 | {
38 | Name = "Meteos";
39 | Description = "An evil planet named Meteo is sending storms of world-ending meteors across the galaxy, and only your puzzle skills can stop them. As blocks drop down on the lower screen, you must use the DS's stylus to match up blocks of the same color. Once you have enough blocks connected, they'll shoot back into the sky to form planets on the upper screen.\r\n\r\nMatch 3+ blocks of the same color horizontally or vertically to ignite a propulsion and send them to your opponents.";
40 | SupportedVersions.Add("US ROM");
41 | CoverFilename = "meteos.png";
42 | Author = "CalDrac";
43 |
44 | Quests.Add(_sendBlocksQuest);
45 | Quests.Add(_starTripQuest);
46 | // Quests.Add(_levelClearQuest); -- Might be added back later
47 |
48 | ValidROMs.Add("85E4BF420FCC466F3CF9A5DA40471DF2789D0FE57461D0B0689C688F6A242D6C"); // NTSC-U
49 | }
50 |
51 | protected override bool Poll()
52 | {
53 | long level = _ram.ReadInt32(RamBaseAddress + 0x3BFEFC);
54 | if (level == 1)
55 | {
56 | _starTripStarted = true;
57 | _starTripQuest.UpdateValue(0);
58 | }
59 | if (level == 16)
60 | {
61 | // level = 16 is the ending
62 | if (_starTripStarted)
63 | {
64 | _starTripQuest.UpdateValue(1);
65 | _starTripStarted = false;
66 | }
67 | }
68 | if(level > 16)
69 | {
70 | _starTripStarted = false;
71 |
72 | uint statsPointer = _ram.ReadUint32(RamBaseAddress + 0x1091DC) - 0x2000000;
73 | if (statsPointer < 0x1000000)
74 | {
75 | uint sentBlocksAddr = statsPointer - 0x20;
76 | _sendBlocksQuest.UpdateValue(_ram.ReadUint32(RamBaseAddress + sentBlocksAddr));
77 | }
78 | }
79 |
80 | return true;
81 |
82 | /* levelQuest might be added back later
83 | if (level < 16 && level > 0)
84 | {
85 | //Prevent quest increase if retrying a level
86 | if (level > maxLevel)
87 | {
88 | maxLevel = level;
89 | }
90 | //Reset Star Trip condition
91 | if (level == 1)
92 | {
93 | maxLevel = 1;
94 | }
95 | //update nbNiveaux in Star trip
96 | _levelClearQuest.UpdateValue(maxLevel - 1);
97 | return true;
98 | }*/
99 | }
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/HintMachine/Models/Games/MetroidPrimePinballConnector.cs:
--------------------------------------------------------------------------------
1 | using HintMachine.Models.GenericConnectors;
2 |
3 | namespace HintMachine.Models.Games
4 | {
5 | class MetroidPrimePinballConnector : INintendoDSConnector
6 | {
7 | private readonly HintQuestCumulative _pointsQuest = new HintQuestCumulative
8 | {
9 | Name = "Points",
10 | GoalValue = 500000
11 | };
12 | private readonly HintQuestCumulative _artifactsQuest = new HintQuestCumulative
13 | {
14 | Name = "Chozo Artifacts",
15 | Description = "Only applicable in Multi-Mission mode.",
16 | GoalValue = 3
17 | };
18 |
19 | private const string REGION_PAL = "93B08A557E3B630FB25F9F331FF479974B21C017D16DA026DDA270504C62106C";
20 | private const string REGION_NTSC_U = "C724C1CE61F8E34393337C626EF462CB5E5BD84F3EB5A70985946127FDB8EFA1";
21 | private const string REGION_NTSC_J = "133184DE3C5AD82713347F41B83E8C354DACFE0C6F3FB4C20C57765D482CBF50";
22 |
23 | // ---------------------------------------------------------
24 |
25 | public MetroidPrimePinballConnector()
26 | {
27 | Name = "Metroid Prime Pinball";
28 | Description = "Metroid Prime, but abridged as a Pinball game.\n\nSamus Aran's entry into the bumper-and-flipper world is a sleek, sci-fi classic gaming adventure that has her careening into gigantic boss monsters and bouncing through a variety of exciting pinball tables. Play tables across two screens at the same time using the touch screen to nudge the pinball table. Battle deadly enemies and experience a number of special modes such as Clone Machine Multiball and the Wall-Jump Challenge while you blast and bomb your high score into a state of pure pinball pandemonium.";
29 | SupportedVersions.Add("PAL (🇪🇺)");
30 | SupportedVersions.Add("NTSC-U (🇺🇸)");
31 | SupportedVersions.Add("NTSC-J (🇯🇵)");
32 | CoverFilename = "metroid_prime_pinball.png";
33 | Author = "Knuxfan24";
34 |
35 | Quests.Add(_pointsQuest);
36 | Quests.Add(_artifactsQuest);
37 |
38 | ValidROMs.Add(REGION_PAL);
39 | ValidROMs.Add(REGION_NTSC_U);
40 | ValidROMs.Add(REGION_NTSC_J);
41 | }
42 |
43 | protected override bool Poll()
44 | {
45 | if (CurrentROM == REGION_PAL)
46 | {
47 | _pointsQuest.UpdateValue(_ram.ReadInt32(RamBaseAddress + 0x3BB9B4));
48 | _artifactsQuest.UpdateValue(_ram.ReadInt32(RamBaseAddress + 0x3D428C));
49 | }
50 | else if (CurrentROM == REGION_NTSC_U)
51 | {
52 | _pointsQuest.UpdateValue(_ram.ReadInt32(RamBaseAddress + 0x3AFC50));
53 | _artifactsQuest.UpdateValue(_ram.ReadInt32(RamBaseAddress + 0x3C7658));
54 | }
55 | else if (CurrentROM == REGION_NTSC_J)
56 | {
57 | _pointsQuest.UpdateValue(_ram.ReadInt32(RamBaseAddress + 0x3B5C50));
58 | _artifactsQuest.UpdateValue(_ram.ReadInt32(RamBaseAddress + 0x3CDCD4));
59 | }
60 |
61 | return true;
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/HintMachine/Models/Games/MinesweeperClassyConnector.cs:
--------------------------------------------------------------------------------
1 | using HintMachine.Models.GenericConnectors;
2 |
3 | namespace HintMachine.Models.Games
4 | {
5 | public class MinesweeperClassyConnector : IGameConnector
6 | {
7 | private readonly HintQuestCumulative _beginnerWinsQuest = new HintQuestCumulative
8 | {
9 | Name = "Beginner Wins",
10 | GoalValue = 8,
11 | MaxIncrease = 1,
12 | };
13 |
14 | private readonly HintQuestCumulative _intermediateWinsQuest = new HintQuestCumulative
15 | {
16 | Name = "Intermediate Wins",
17 | GoalValue = 2,
18 | MaxIncrease = 1,
19 | };
20 |
21 | private readonly HintQuestCumulative _expertWinsQuest = new HintQuestCumulative
22 | {
23 | Name = "Expert Wins",
24 | GoalValue = 1,
25 | MaxIncrease = 1,
26 | };
27 |
28 | private readonly HintQuestCumulative _expertLargeWinsQuest = new HintQuestCumulative
29 | {
30 | Name = "Expert (Large) Wins",
31 | GoalValue = 1,
32 | MaxIncrease = 1,
33 | AwardedHints = 2,
34 | };
35 |
36 | private ProcessRamWatcher _ram = null;
37 |
38 | // ----------------------------------------------------
39 |
40 | public MinesweeperClassyConnector()
41 | {
42 | Name = "Minesweeper Classy";
43 | Description = "A modern and stylish take on a classic logic puzzle game. Play the classic minesweeper levels, beat unique level layouts and challenges, or create and share your own level designs.";
44 | Platform = "PC";
45 | SupportedVersions.Add("Steam");
46 | CoverFilename = "minesweeper_classy.png";
47 | Author = "Dinopony";
48 |
49 | Quests.Add(_beginnerWinsQuest);
50 | Quests.Add(_intermediateWinsQuest);
51 | Quests.Add(_expertWinsQuest);
52 | Quests.Add(_expertLargeWinsQuest);
53 | }
54 |
55 | protected override bool Connect()
56 | {
57 | _ram = new ProcessRamWatcher(new BinaryTarget
58 | {
59 | DisplayName = "Steam",
60 | ProcessName = "Minesweeper Classy",
61 | ModuleName = "mono-2.0-bdwgc.dll",
62 | Hash = "D6CD45D35A0A01F977C64C6A9843F9CA51567102EA5E097F1E2E86BE1D000AF4"
63 | });
64 |
65 | return _ram.TryConnect();
66 | }
67 |
68 | public override void Disconnect()
69 | {
70 | _ram = null;
71 | }
72 |
73 | protected override bool Poll()
74 | {
75 | long statsStructureAddr = _ram.ResolvePointerPath64(_ram.BaseAddress + 0x499430, new int[] { 0x158, 0x2A8, 0x5F0, 0x198 });
76 | if (statsStructureAddr != 0)
77 | {
78 | uint beginnerWins = _ram.ReadUint32(statsStructureAddr + 4);
79 | _beginnerWinsQuest.UpdateValue(beginnerWins);
80 |
81 | uint intermediateWins = _ram.ReadUint32(statsStructureAddr + 12);
82 | _intermediateWinsQuest.UpdateValue(intermediateWins);
83 |
84 | uint expertWins = _ram.ReadUint32(statsStructureAddr + 20);
85 | _expertWinsQuest.UpdateValue(expertWins);
86 |
87 | uint expertLargeWins = _ram.ReadUint32(statsStructureAddr + 28);
88 | _expertLargeWinsQuest.UpdateValue(expertLargeWins);
89 | }
90 |
91 | return true;
92 | }
93 | }
94 | }
--------------------------------------------------------------------------------
/HintMachine/Models/Games/MiniMetroConnector.cs:
--------------------------------------------------------------------------------
1 | using HintMachine.Models.GenericConnectors;
2 |
3 | namespace HintMachine.Models.Games
4 | {
5 | public class MiniMetroConnector : IGameConnector
6 | {
7 | private readonly HintQuestCumulative _passengersQuest = new HintQuestCumulative
8 | {
9 | Name = "Passengers Delivered",
10 | GoalValue = 150,
11 | MaxIncrease = 10,
12 | };
13 |
14 | private ProcessRamWatcher _ram = null;
15 |
16 | public MiniMetroConnector()
17 | {
18 | Name = "Mini Metro";
19 | Description = "Mini Metro is a strategy simulation game about designing a subway map for a growing city.\n\nDraw lines between stations and start your trains running. As new stations open, redraw your lines to keep them efficient. Decide where to use your limited resources. How long can you keep the city moving?";
20 | Platform = "PC";
21 | SupportedVersions.Add("Steam");
22 | CoverFilename = "mini_metro.png";
23 | Author = "Chandler";
24 |
25 | Quests.Add(_passengersQuest);
26 | }
27 |
28 | protected override bool Connect()
29 | {
30 | _ram = new ProcessRamWatcher("MiniMetro", "mono-2.0-bdwgc.dll");
31 | return _ram.TryConnect();
32 | }
33 |
34 | public override void Disconnect()
35 | {
36 | _ram = null;
37 | }
38 |
39 | protected override bool Poll()
40 | {
41 | try {
42 | long address = _ram.ResolvePointerPath32(_ram.BaseAddress + 0x3A1574, new int[] { 0x690, 0x20, 0x8, 0x4C, 0x8, 0xC, 0x88 });
43 | _passengersQuest.UpdateValue(_ram.ReadUint32(address));
44 | }
45 | catch
46 | { }
47 |
48 | return true;
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/HintMachine/Models/Games/NexMachinaConnector.cs:
--------------------------------------------------------------------------------
1 | using HintMachine.Models.GenericConnectors;
2 |
3 | namespace HintMachine.Models.Games
4 | {
5 | public class NexMachinaConnector : IGameConnector
6 | {
7 | private ProcessRamWatcher _ram = null;
8 |
9 | private readonly HintQuestCumulative _humansQuest = new HintQuestCumulative
10 | {
11 | Name = "Humans saved",
12 | GoalValue = 50,
13 | MaxIncrease = 5,
14 | Description = "Secret humans also count, go find them !"
15 | };
16 |
17 | public NexMachinaConnector()
18 | {
19 | Name = "Nex Machina";
20 | Description = "Nex Machina is an explosive arcade experience created with competition in mind.";
21 | Platform = "PC";
22 | SupportedVersions.Add("Steam");
23 | CoverFilename = "nexmachina.png";
24 | Author = "CalDrac";
25 |
26 | Quests.Add(_humansQuest);
27 | }
28 |
29 | protected override bool Connect()
30 | {
31 | _ram = new ProcessRamWatcher("nex_machina");
32 | return _ram.TryConnect();
33 | }
34 |
35 | public override void Disconnect()
36 | {
37 | _ram = null;
38 | }
39 |
40 | protected override bool Poll()
41 | {
42 | long savesAddress = _ram.ResolvePointerPath64(_ram.BaseAddress + 0x00F5AEC8, new int[] { 0x20, 0xC8, 0x30, 0x20 });
43 | if (savesAddress != 0)
44 | {
45 | _humansQuest.UpdateValue(_ram.ReadUint32(savesAddress));
46 | }
47 | return true;
48 | }
49 | }
50 | }
--------------------------------------------------------------------------------
/HintMachine/Models/Games/NuclearThroneConnector.cs:
--------------------------------------------------------------------------------
1 | using HintMachine.Models.GenericConnectors;
2 |
3 | namespace HintMachine.Models.Games
4 | {
5 | public class NuclearThroneConnector : IGameConnector
6 | {
7 | private readonly HintQuestCounter _killThroneQuest = new HintQuestCounter
8 | {
9 | Name = "Wins",
10 | Description = "Sit on the Nuclear Throne and wait for the credits to end",
11 | AwardedHints = 2
12 | };
13 |
14 | private ProcessRamWatcher _ram = null;
15 | private uint _previousWinValue = 0;
16 |
17 | public NuclearThroneConnector()
18 | {
19 | Name = "Nuclear Throne";
20 | Description = "Kill your way to the Nuclear Throne";
21 | Platform = "PC";
22 | SupportedVersions.Add("NuclearThroneTogether mod");
23 | Author = "CalDrac";
24 |
25 | Quests.Add(_killThroneQuest);
26 | }
27 | protected override bool Connect()
28 | {
29 | _ram = new ProcessRamWatcher("nuclearthronetogether");
30 | _ram.Is64Bit = false;
31 |
32 | return _ram.TryConnect();
33 | }
34 |
35 | public override void Disconnect()
36 | {
37 | _ram = null;
38 | }
39 |
40 | protected override bool Poll()
41 | {
42 | int[] OFFSETS = new int[] { 0xA0, 0x60C, 0x104, 0x714, 0x0 };
43 | try
44 | {
45 | long throneAliveAddress = _ram.ResolvePointerPath32(_ram.Threadstack0 - 0x638, OFFSETS);
46 | if (throneAliveAddress == 0)
47 | return true;
48 |
49 | uint winValue = _ram.ReadUint32(throneAliveAddress);
50 | if (winValue == 542461785 && _previousWinValue != winValue)
51 | _killThroneQuest.CurrentValue += 1;
52 | _previousWinValue = winValue;
53 | }
54 | catch {}
55 | return true;
56 |
57 | }
58 | }
59 | }
--------------------------------------------------------------------------------
/HintMachine/Models/Games/OneFingerDeathPunchConnector.cs:
--------------------------------------------------------------------------------
1 | using HintMachine.Models.GenericConnectors;
2 |
3 | namespace HintMachine.Models.Games
4 | {
5 | public class OneFingerDeathPunchConnector : IGameConnector
6 | {
7 | private readonly HintQuestCumulative _killsQuest = new HintQuestCumulative
8 | {
9 | Name = "Kills",
10 | GoalValue = 450,
11 | Description = "Kill enemies in story mode or survival to progress",
12 | MaxIncrease = 100,
13 | Direction = HintQuestCumulative.CumulativeDirection.ASCENDING
14 |
15 | };
16 |
17 | private ProcessRamWatcher _ram = null;
18 |
19 | public OneFingerDeathPunchConnector()
20 | {
21 | Name = "One Finger Death Punch";
22 | Description = "Experience cinematic kung-fu battles in the fastest, most intense brawler the indie world has ever seen! With the unique 1:1 response system of One Finger Death Punch, players will feel the immediate feedback of every bone-crunching hit.\r\n\r\nPay tribute to the masters using five classic kung-fu styles mixed with additional weapons. Combine face-to-face combat with throwing weapons to recreate complex fight choreographies or just send bad guys flying through glass windows. Explore a world map with over 250 stages, 13 modes, and 3 difficulty levels. Unlock 21 different skills that can be combined in thousands of ways to assist you in your journey. Put your kung-fu to the ultimate test in the survival mode.";
23 | Platform = "PC";
24 | SupportedVersions.Add("Steam");
25 | CoverFilename = "one_finger_death_punch.png";
26 | Author = "CalDrac";
27 |
28 | Quests.Add(_killsQuest);
29 | }
30 |
31 | protected override bool Connect()
32 | {
33 | _ram = new ProcessRamWatcher("One Finger Death Punch");
34 | _ram.Is64Bit = false;
35 |
36 | if (!_ram.TryConnect())
37 | return false;
38 |
39 | return true;
40 | }
41 |
42 | public override void Disconnect()
43 | {
44 | _ram = null;
45 | }
46 |
47 | protected override bool Poll()
48 | {
49 | //long killsAddress = _ram.ResolvePointerPath32(_threadstack0Address.ToInt32() - 0x8cc, new int[] { 0x644, 0x90 });
50 | long killsAddress = _ram.ResolvePointerPath32(_ram.Threadstack0 - 0x8A0, new int[] { 0x710, 0x3C });
51 | _killsQuest.UpdateValue(_ram.ReadUint32(killsAddress));
52 |
53 | return true;
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/HintMachine/Models/Games/Operator911Connector.cs:
--------------------------------------------------------------------------------
1 | using HintMachine.Models.GenericConnectors;
2 |
3 | namespace HintMachine.Models.Games
4 | {
5 | public class Operator911Connector : IGameConnector
6 | {
7 | private readonly HintQuestCumulative _incidentsQuest = new HintQuestCumulative
8 | {
9 | Name = "Incidents Resolved",
10 | GoalValue = 15,
11 | MaxIncrease = 3,
12 | };
13 |
14 | private readonly HintQuestCumulative _cashQuest = new HintQuestCumulative
15 | {
16 | Name = "Cash Earned",
17 | GoalValue = 40000,
18 | MaxIncrease = 15000,
19 | };
20 |
21 | private ProcessRamWatcher _ram = null;
22 |
23 | public Operator911Connector()
24 | {
25 | Name = "911 Operator";
26 | Description = "In 911 Operator, you take on the role of an emergency dispatcher, who has to rapidly deal with the incoming reports. Your task is not just to pick up the calls, but also to react appropriately to the situation – sometimes giving first aid instructions is enough, at other times a police, fire department or paramedics’ intervention is a necessity. Keep in mind, that the person on the other side of the line might turn out to be a dying daughter’s father, an unpredictable terrorist, or just a prankster. Can you handle all of this?";
27 | Platform = "PC";
28 | SupportedVersions.Add("Steam");
29 | CoverFilename = "911_operator.png";
30 | Author = "Chandler";
31 |
32 | Quests.Add(_incidentsQuest);
33 | Quests.Add(_cashQuest);
34 | }
35 |
36 | protected override bool Connect()
37 | {
38 | _ram = new ProcessRamWatcher("911");
39 | return _ram.TryConnect();
40 | }
41 |
42 | public override void Disconnect()
43 | {
44 | _ram = null;
45 | }
46 |
47 | protected override bool Poll()
48 | {
49 | long incidentsAddress = _ram.ResolvePointerPath32(_ram.BaseAddress + 0x1037A40, new int[] { 0x4, 0x8, 0x50, 0x68, 0x38, 0x164, 0x14 });
50 | if(incidentsAddress != 0)
51 | _incidentsQuest.UpdateValue(_ram.ReadUint32(incidentsAddress));
52 |
53 | long cashAddress = _ram.ResolvePointerPath32(_ram.BaseAddress + 0xFD3730, new int[] { 0x4, 0x4, 0x8, 0x18, 0x3C, 0x14, 0x18C });
54 | if(cashAddress != 0)
55 | _cashQuest.UpdateValue(_ram.ReadUint32(cashAddress));
56 |
57 | return true;
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/HintMachine/Models/Games/PacManChampionshipEditionDXConnector.cs:
--------------------------------------------------------------------------------
1 | using HintMachine.Models.GenericConnectors;
2 |
3 | namespace HintMachine.Models.Games
4 | {
5 | public class PacManChampionshipEditionDXConnector : IGameConnector
6 | {
7 | private readonly BinaryTarget GAME_VERSION_STEAM = new BinaryTarget
8 | {
9 | DisplayName = "Steam",
10 | ProcessName = "PAC-MAN",
11 | Hash = "B96AAB05C2A3E767FDE271A14A0052915D89418F000F5BDE75B74777608721F1"
12 | };
13 |
14 | private readonly HintQuestCumulative _scoreQuest = new HintQuestCumulative
15 | {
16 | Name = "Score",
17 | GoalValue = 400000,
18 | };
19 |
20 | private ProcessRamWatcher _ram = null;
21 |
22 | public PacManChampionshipEditionDXConnector()
23 | {
24 | Name = "PAC-MAN Championship Edition DX+";
25 | Description = "The sequel to the original PAC-MAN and award-winning power pellet chomping game, PAC-MAN Championship Edition DX returns with even more content! Chomp through bright neon mazes at blistering speeds to unlock brand new achievements and medals for an increased challenge! With a refined user-interface, it's easier than ever to compare high scores with your friends! Get ready for more ghost chain gobbling and frantic action in PAC-MAN CE-DX+";
26 | Platform = "PC";
27 | SupportedVersions.Add("Steam");
28 | CoverFilename = "pacman_cedx.png";
29 | Author = "Serpent.AI";
30 |
31 | Quests.Add(_scoreQuest);
32 | }
33 |
34 | protected override bool Connect()
35 | {
36 | _ram = new ProcessRamWatcher(GAME_VERSION_STEAM);
37 | return _ram.TryConnect();
38 | }
39 |
40 | public override void Disconnect()
41 | {
42 | _ram = null;
43 | }
44 |
45 | protected override bool Poll()
46 | {
47 | _scoreQuest.UpdateValue(_ram.ReadUint32(_ram.BaseAddress + 0x33CA14));
48 | return true;
49 | }
50 | }
51 | }
--------------------------------------------------------------------------------
/HintMachine/Models/Games/PaintTheTownRedConnector.cs:
--------------------------------------------------------------------------------
1 | using HintMachine.Models.GenericConnectors;
2 |
3 | namespace HintMachine.Models.Games
4 | {
5 | public class PaintTheTownRedConnector : IGameConnector
6 | {
7 | private readonly BinaryTarget GAME_VERSION_STEAM = new BinaryTarget
8 | {
9 | DisplayName = "Steam",
10 | ProcessName = "PaintTheTownRed",
11 | ModuleName = "GameAssembly.dll",
12 | Hash = "D2233BEEA6BC9EA0CE0B2D356BE6851FF4599B8565323A584C01E6940C857DBB"
13 | };
14 |
15 | private readonly HintQuestCumulative _beneathGoldQuest = new HintQuestCumulative
16 | {
17 | Name = "Beneath: Gold Collected",
18 | GoalValue = 1000,
19 | };
20 |
21 | private readonly HintQuestCumulative _beneathEnergyQuest = new HintQuestCumulative
22 | {
23 | Name = "Beneath: Energy Spent in Shops",
24 | GoalValue = 25,
25 | };
26 |
27 | private ProcessRamWatcher _ram = null;
28 |
29 | public PaintTheTownRedConnector()
30 | {
31 | Name = "Paint the Town Red";
32 | Description = "Paint the Town Red is a chaotic first person melee combat game set in different locations and time periods and featuring a massive Rogue-Lite adventure. The voxel-based enemies can be punched, bashed, kicked, stabbed and sliced completely dynamically using almost anything that isn't nailed down.";
33 | Platform = "PC";
34 | SupportedVersions.Add("Steam");
35 | CoverFilename = "paint_the_town_red.png";
36 | Author = "Serpent.AI";
37 |
38 | Quests.Add(_beneathGoldQuest);
39 | Quests.Add(_beneathEnergyQuest);
40 | }
41 |
42 | protected override bool Connect()
43 | {
44 | _ram = new ProcessRamWatcher(GAME_VERSION_STEAM);
45 | return _ram.TryConnect();
46 | }
47 |
48 | public override void Disconnect()
49 | {
50 | _ram = null;
51 | }
52 |
53 | protected override bool Poll()
54 | {
55 | long beneathStructAddress = _ram.ResolvePointerPath64(_ram.BaseAddress + 0x2B36000, new int[] { 0xB8, 0x900 });
56 |
57 | if (beneathStructAddress != 0)
58 | {
59 | try
60 | {
61 | long goldValue = _ram.ReadUint32(beneathStructAddress + 0xB4);
62 | long energyValue = _ram.ReadUint32(beneathStructAddress + 0xBC);
63 |
64 | _beneathGoldQuest.UpdateValue(goldValue);
65 | _beneathEnergyQuest.UpdateValue(energyValue);
66 | }
67 | catch { }
68 | }
69 |
70 | return true;
71 | }
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/HintMachine/Models/Games/PapersPleaseConnector.cs:
--------------------------------------------------------------------------------
1 | using HintMachine.Models.GenericConnectors;
2 |
3 | namespace HintMachine.Models.Games
4 | {
5 | public class PapersPleaseConnector : IGameConnector
6 | {
7 | private readonly BinaryTarget GAME_VERSION_STEAM = new BinaryTarget
8 | {
9 | DisplayName = "Steam",
10 | ProcessName = "PapersPlease",
11 | ModuleName = "GameAssembly.dll",
12 | Hash = "9451692A33540D96352C290E1FB5FDADE29B2B112206CCD40D711E271D2886B7"
13 | };
14 |
15 | private long _previousPoints = 0;
16 |
17 | private readonly HintQuestCounter _entrantsQuest = new HintQuestCumulative
18 | {
19 | Name = "Entrants Correctly Processed",
20 | GoalValue = 5,
21 | MaxIncrease = 1,
22 | Description = "Endless Mode only"
23 | };
24 |
25 | private ProcessRamWatcher _ram = null;
26 |
27 | public PapersPleaseConnector()
28 | {
29 | Name = "Papers, Please";
30 | Description = "Congratulations. The October labor lottery is complete. Your name was pulled. For immediate placement, report to the Ministry of Admission at Grestin Border Checkpoint. An apartment will be provided for you and your family in East Grestin. Expect a Class-8 dwelling.\n\nYour job as immigration inspector is to control the flow of people entering the Arstotzkan side of Grestin from Kolechia. Among the throngs of immigrants and visitors looking for work are hidden smugglers, spies, and terrorists.\n\nUsing only the documents provided by travelers and the Ministry of Admission's primitive inspect, search, and fingerprint systems you must decide who can enter Arstotzka and who will be turned away or arrested.";
31 | Platform = "PC";
32 | SupportedVersions.Add("Steam");
33 | CoverFilename = "papers_please.png";
34 | Author = "Chandler";
35 |
36 | Quests.Add(_entrantsQuest);
37 | }
38 |
39 | protected override bool Connect()
40 | {
41 | _ram = new ProcessRamWatcher(GAME_VERSION_STEAM);
42 | return _ram.TryConnect();
43 | }
44 |
45 | public override void Disconnect()
46 | {
47 | _ram = null;
48 | }
49 |
50 | protected override bool Poll()
51 | {
52 | if (!_ram.TestProcess())
53 | return false;
54 |
55 | try {
56 | long address = _ram.ResolvePointerPath64(_ram.BaseAddress + 0xC6B6A0, new int[] { 0xB8, 0x10, 0x50, 0x18, 0x28, 0xB8, 0x38 });
57 | long points = _ram.ReadUint32(address);
58 |
59 | if (points > _previousPoints && points - _previousPoints <= 10) {
60 | _entrantsQuest.CurrentValue += 1;
61 | }
62 |
63 | _previousPoints = points;
64 | }
65 | catch
66 | { }
67 |
68 | return true;
69 | }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/HintMachine/Models/Games/PeggleDeluxeConnector.cs:
--------------------------------------------------------------------------------
1 | using HintMachine.Models.GenericConnectors;
2 |
3 | namespace HintMachine.Models.Games
4 | {
5 | public class PeggleDeluxeConnector : IGameConnector
6 | {
7 | private readonly BinaryTarget GAME_VERSION_STEAM = new BinaryTarget
8 | {
9 | DisplayName = "Steam",
10 | ProcessName = "popcapgame1",
11 | // We can't use hash checking for this connector. The popcapgame1 executable is created on the fly by Peggle.exe every time the game launches and we don't have the proper permissions to interact with it
12 | };
13 |
14 | private readonly HintQuestCumulative _orangeQuest = new HintQuestCumulative
15 | {
16 | Name = "Orange Pegs Cleared",
17 | GoalValue = 50,
18 | MaxIncrease = 10,
19 | };
20 |
21 | private readonly HintQuestCumulative _greenQuest = new HintQuestCumulative
22 | {
23 | Name = "Green Pegs Cleared",
24 | GoalValue = 12,
25 | };
26 |
27 | private readonly HintQuestCumulative _totalQuest = new HintQuestCumulative
28 | {
29 | Name = "Total Pegs Cleared",
30 | GoalValue = 300,
31 | MaxIncrease = 30,
32 | };
33 |
34 | private ProcessRamWatcher _ram = null;
35 |
36 | public PeggleDeluxeConnector()
37 | {
38 | Name = "Peggle Deluxe";
39 | Description = "Take your best shot with energizing arcade fun! Aim, shoot, clear the orange pegs, then sit back and cheer as 10 whimsical teachers guide you to Peggle greatness. Conquer 55 fanciful levels with 10 mystical Magic Powers, racking up bonus points and shots you'll smile about for weeks.";
40 | Platform = "PC";
41 | SupportedVersions.Add("Steam");
42 | CoverFilename = "peggle_deluxe.png";
43 | Author = "Serpent.AI";
44 |
45 | Quests.Add(_orangeQuest);
46 | Quests.Add(_greenQuest);
47 | Quests.Add(_totalQuest);
48 | }
49 |
50 | protected override bool Connect()
51 | {
52 | _ram = new ProcessRamWatcher(GAME_VERSION_STEAM);
53 | _ram.Is64Bit = false;
54 |
55 | return _ram.TryConnect();
56 | }
57 |
58 | public override void Disconnect()
59 | {
60 | _ram = null;
61 | }
62 |
63 | protected override bool Poll()
64 | {
65 | // Make sure popcapgame1.exe is actually Peggle and not a different PopCap game
66 | byte[] signature = _ram.ReadBytes(_ram.BaseAddress + 0x288BCC, 6);
67 | string signatureString = System.Text.Encoding.UTF8.GetString(signature, 0, signature.Length);
68 |
69 | if (signatureString != "Peggle") { return false; }
70 |
71 | long logicManagerStructAddress = _ram.ResolvePointerPath32(_ram.Threadstack0 - 0x268, new int[] { 0x1C, 0x150, 0x0 });
72 |
73 | if (logicManagerStructAddress != 0)
74 | {
75 | try
76 | {
77 | long orangeValue = _ram.ReadUint32(logicManagerStructAddress + 0x4C);
78 | long greenValue = _ram.ReadUint32(logicManagerStructAddress + 0x50);
79 | long totalValue = _ram.ReadUint32(logicManagerStructAddress + 0x12C);
80 |
81 | _orangeQuest.UpdateValue(orangeValue);
82 | _greenQuest.UpdateValue(greenValue);
83 | _totalQuest.UpdateValue(totalValue);
84 | }
85 | catch { }
86 | }
87 |
88 | return true;
89 | }
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/HintMachine/Models/Games/PeggleNightsConnector.cs:
--------------------------------------------------------------------------------
1 | using HintMachine.Models.GenericConnectors;
2 |
3 | namespace HintMachine.Models.Games
4 | {
5 | public class PeggleNightsConnector : IGameConnector
6 | {
7 | private readonly BinaryTarget GAME_VERSION_STEAM = new BinaryTarget
8 | {
9 | DisplayName = "Steam",
10 | ProcessName = "popcapgame1",
11 | // We can't use hash checking for this connector. The popcapgame1 executable is created on the fly by PeggleNights.exe every time the game launches and we don't have the proper permissions to interact with it
12 | };
13 |
14 | private readonly HintQuestCumulative _orangeQuest = new HintQuestCumulative
15 | {
16 | Name = "Orange Pegs Cleared",
17 | GoalValue = 50,
18 | MaxIncrease = 10,
19 | };
20 |
21 | private readonly HintQuestCumulative _greenQuest = new HintQuestCumulative
22 | {
23 | Name = "Green Pegs Cleared",
24 | GoalValue = 12,
25 | };
26 |
27 | private readonly HintQuestCumulative _totalQuest = new HintQuestCumulative
28 | {
29 | Name = "Total Pegs Cleared",
30 | GoalValue = 300,
31 | MaxIncrease = 30,
32 | };
33 |
34 | private ProcessRamWatcher _ram = null;
35 |
36 | public PeggleNightsConnector()
37 | {
38 | Name = "Peggle Nights";
39 | Description = "The sun has set at the Peggle Institute, but the bouncy delight has just begun! Join the Peggle Masters on a dreamtime adventure of alter egos and peg-tastic action. Stay up late to aim, shoot and clear orange pegs, and bask in Extreme Fever glory under the silver moon.";
40 | Platform = "PC";
41 | SupportedVersions.Add("Steam");
42 | CoverFilename = "peggle_nights.png";
43 | Author = "Serpent.AI";
44 |
45 | Quests.Add(_orangeQuest);
46 | Quests.Add(_greenQuest);
47 | Quests.Add(_totalQuest);
48 | }
49 |
50 | protected override bool Connect()
51 | {
52 | _ram = new ProcessRamWatcher(GAME_VERSION_STEAM);
53 | _ram.Is64Bit = false;
54 |
55 | return _ram.TryConnect();
56 | }
57 |
58 | public override void Disconnect()
59 | {
60 | _ram = null;
61 | }
62 |
63 | protected override bool Poll()
64 | {
65 | // Make sure popcapgame1.exe is actually Peggle Nights and not a different PopCap game
66 | byte[] signature = _ram.ReadBytes(_ram.BaseAddress + 0x2CA09A, 6);
67 | string signatureString = System.Text.Encoding.UTF8.GetString(signature, 0, signature.Length);
68 |
69 | if (signatureString != "Nights") { return false; }
70 |
71 | long logicManagerStructAddress = _ram.ResolvePointerPath32(_ram.Threadstack0 - 0x2A4, new int[] { 0x20, 0x0, 0x8, 0x104, 0x698, 0x0 });
72 |
73 | if (logicManagerStructAddress != 0)
74 | {
75 | try
76 | {
77 | long orangeValue = _ram.ReadUint32(logicManagerStructAddress + 0x4C);
78 | long greenValue = _ram.ReadUint32(logicManagerStructAddress + 0x50);
79 | long totalValue = _ram.ReadUint32(logicManagerStructAddress + 0x1B0);
80 |
81 | _orangeQuest.UpdateValue(orangeValue);
82 | _greenQuest.UpdateValue(greenValue);
83 | _totalQuest.UpdateValue(totalValue);
84 | }
85 | catch { }
86 | }
87 |
88 | return true;
89 | }
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/HintMachine/Models/Games/PokemonPinballRSConnector.cs:
--------------------------------------------------------------------------------
1 | using HintMachine.Models.GenericConnectors;
2 |
3 | namespace HintMachine.Models.Games
4 | {
5 | class PokemonPinballRSConnector : IGameboyAdvanceConnector
6 | {
7 | private readonly HintQuestCumulative _pokemonQuest = new HintQuestCumulative
8 | {
9 | Name = "Pokémon count",
10 | Description = "Catch, hatch and evolve Pokémon",
11 | GoalValue = 3,
12 | MaxIncrease = 1,
13 | };
14 |
15 | public PokemonPinballRSConnector()
16 | {
17 | Name = "Pokémon Pinball: Ruby & Sapphire";
18 | Description = "Pokémon Pinball has all the features you'd demand of a pinball game, including bonus tables, lots of bumpers and ways to score massive points." +
19 | "Instead of a ball, you make use of a Pokéball. Instead of standard bumpers, you're hitting the Pokéball against other Pokémon, and the ultimate goal is of course to \"catch 'em all\". The game features 200 Pokémon and two main tables.";
20 | SupportedVersions.Add("EU ROM");
21 | Author = "CalDrac";
22 | CoverFilename = "pokemon_pinball_rs.png";
23 |
24 | Quests.Add(_pokemonQuest);
25 |
26 | ValidROMs.Add("BPPP01");
27 | }
28 |
29 | protected override bool Poll()
30 | {
31 | _pokemonQuest.UpdateValue(_ram.ReadUint8(ExternalRamBaseAddress + 0x5F0));
32 | return true;
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/HintMachine/Models/Games/PokemonPuzzleChallengeConnector.cs:
--------------------------------------------------------------------------------
1 | using HintMachine.Models.GenericConnectors;
2 |
3 | namespace HintMachine.Models.Games
4 | {
5 | public class PokemonPuzzleChallengeConnector : IGameboyColorConnector
6 | {
7 | private readonly HintQuestCumulative _scoreQuest = new HintQuestCumulative
8 | {
9 | Name = "Score",
10 | GoalValue = 2500,
11 | MaxIncrease = 500,
12 | };
13 |
14 | public PokemonPuzzleChallengeConnector()
15 | {
16 | Name = "Pokémon Puzzle Challenge";
17 | Description = "In a rising pit of colored blocks, it's your task to continuously eliminate these tiles by switching two tiles' positions on the horizontal row. When three or more tiles link up in a up/down or left/right fashion, they disappear. Gravity kicks in and settles the rest of the tiles into the bin ? this can cause a chain reaction to occur since other blocks of the same color can fall into place next to each other. It's up to you to continuously work the bin to wipe out tiles by making combination connections and chain reactions. That's the game in a nutshell, but there are several modes of play that'll keep your fingers busy. In the game's Challenge mode, for example, you're up against a computer opponent and you'll have to make combos and chain reactions to send garbage blocks to his unseen screen -- but keep in mind your opponent's doing the same to you.";
18 | SupportedVersions.Add("PAL (🇪🇺)");
19 | SupportedVersions.Add("NTSC-U (🇺🇸)");
20 | SupportedVersions.Add("NTSC-J (🇯🇵)");
21 | Author = "Dinopony";
22 | CoverFilename = "pokemon_puzzle_challenge.png";
23 |
24 | Quests.Add(_scoreQuest);
25 |
26 | ValidROMs.Add("BPNP01");
27 | ValidROMs.Add("BPNE01");
28 | ValidROMs.Add("BPNJ01");
29 | }
30 |
31 | protected override bool Poll()
32 | {
33 | // Score is placed slightly differently in the Japanese version
34 | long scoreOffset = (CurrentROM == "BPNJ01") ? 0x822 : 0x842;
35 | _scoreQuest.UpdateValue(_ram.ReadUint16(RamBaseAddress + scoreOffset));
36 |
37 | return true;
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/HintMachine/Models/Games/PuyoPuyo2MDConnector.cs:
--------------------------------------------------------------------------------
1 | using HintMachine.Models.GenericConnectors;
2 |
3 | namespace HintMachine.Models.Games
4 | {
5 | public class PuyoPuyo2MDConnector : IMegadriveConnector
6 | {
7 | private readonly HintQuestCumulative _poppedPuyosQuest = new HintQuestCumulative
8 | {
9 | Name = "Popped Puyos",
10 | GoalValue = 200,
11 | MaxIncrease = 30,
12 | };
13 | private readonly HintQuestCumulative _chainsQuest = new HintQuestCumulative
14 | {
15 | Name = "Chains",
16 | GoalValue = 30,
17 | MaxIncrease = 4
18 | };
19 | private readonly HintQuestCounter _allClearsQuest = new HintQuestCounter
20 | {
21 | Name = "All Clears",
22 | GoalValue = 2,
23 | };
24 | private byte _previousPuyosOnBoard = 0;
25 |
26 | // ----------------------------------------------------------
27 |
28 | public PuyoPuyo2MDConnector() : base()
29 | {
30 | Name = "Puyo Puyo 2";
31 | Description = "The goal of this head-to-head puzzle game is to clear your grid of falling patterns called puyos by forming chains of four or more same-colored puyos in a straight line or one of several geometric patterns.";
32 | SupportedVersions.Add("NTSC-J (🇯🇵)");
33 | CoverFilename = "puyo_puyo_2.png";
34 | Author = "Dinopony";
35 |
36 | Quests.Add(_poppedPuyosQuest);
37 | Quests.Add(_chainsQuest);
38 | Quests.Add(_allClearsQuest);
39 |
40 | ValidROMs.Add("33C3F80F36DA94E25F11F1A2FCEBD5DF22E495567754DB47F330855F6DF91430");
41 | }
42 |
43 | protected override bool Poll()
44 | {
45 | _poppedPuyosQuest.UpdateValue(_ram.ReadUint16(RamBaseAddress + 0xD098));
46 |
47 | byte currentChain = _ram.ReadUint8(RamBaseAddress + 0xD089);
48 | if (currentChain > 0)
49 | currentChain -= 1;
50 | _chainsQuest.UpdateValue(currentChain);
51 |
52 | byte puyosOnBoard = _ram.ReadUint8(RamBaseAddress + 0x8279);
53 | ushort currentPairCount = _ram.ReadUint16(RamBaseAddress + 0xA064);
54 |
55 | // If there are no more puyos on board and we are not at the first pair, this means
56 | // it's an All-Clear!
57 | if (puyosOnBoard == 0 && _previousPuyosOnBoard > 0 && currentPairCount > 0)
58 | _allClearsQuest.CurrentValue += 1;
59 |
60 | _previousPuyosOnBoard = puyosOnBoard;
61 |
62 | return true;
63 | }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/HintMachine/Models/Games/PuyoTetrisConnector.cs:
--------------------------------------------------------------------------------
1 | using HintMachine.Models.GenericConnectors;
2 | using System.Collections.Generic;
3 |
4 | namespace HintMachine.Models.Games
5 | {
6 | public class PuyoTetrisConnector : IGameConnector
7 | {
8 | private readonly HintQuestCumulative _linesQuest = new HintQuestCumulative
9 | {
10 | Name = "Cleared Lines",
11 | GoalValue = 200,
12 | };
13 | private readonly HintQuestCumulative _tetrisesQuest = new HintQuestCumulative
14 | {
15 | Name = "Tetrises",
16 | GoalValue = 25,
17 | };
18 | private readonly HintQuestCumulative _tspinsQuest = new HintQuestCumulative
19 | {
20 | Name = "T-Spins",
21 | GoalValue = 40,
22 | };
23 | private readonly HintQuestCumulative _combosQuest = new HintQuestCumulative
24 | {
25 | Name = "Combos",
26 | GoalValue = 60,
27 | };
28 | // private readonly HintQuestCumulative _perfectClearsQuest = new HintQuestCumulative
29 | // {
30 | // Name = "Perfect Clears",
31 | // GoalValue = 5,
32 | // };
33 | private readonly HintQuestCumulative _backToBackQuest = new HintQuestCumulative
34 | {
35 | Name = "Back-to-Back",
36 | GoalValue = 30,
37 | };
38 | private readonly HintQuestCumulative _poppedPuyosQuest = new HintQuestCumulative
39 | {
40 | Name = "Popped Puyos",
41 | GoalValue = 300,
42 | };
43 | private readonly HintQuestCumulative _chainsQuest = new HintQuestCumulative
44 | {
45 | Name = "Chains",
46 | GoalValue = 100,
47 | };
48 | // private readonly HintQuestCumulative _allClearsQuest = new HintQuestCumulative
49 | // {
50 | // Name = "All Clears",
51 | // GoalValue = 3,
52 | // };
53 |
54 | private ProcessRamWatcher _ram = null;
55 |
56 | public PuyoTetrisConnector()
57 | {
58 | Name = "Puyo Puyo Tetris";
59 | Description = "Two puzzle game juggernauts collide as Tetris, one of the largest-selling and recognized brands in gaming history, and Puyo Puyo from SEGA have combined to create a fun-to-play, fast-paced, competitive party game like no other! The game has tons of different styles of gameplay – from the single-player Adventure and Challenge modes to the ferocious competition of the up-to-four player Arcade modes. There's plenty of Tetris/Puyo variety, even an in-game Shop where players can unlock new characters, new skins for Puyos and Tetriminos, new backgrounds and more.";
60 | Platform = "PC";
61 | SupportedVersions.Add("Steam");
62 | CoverFilename = "puyo_puyo_tetris.png";
63 | Author = "Dinopony";
64 |
65 | Quests = new List() {
66 | _linesQuest, _tetrisesQuest, _tspinsQuest, _combosQuest, /*_perfectClearsQuest,*/
67 | _poppedPuyosQuest, _chainsQuest, /*_allClearsQuest*/
68 | };
69 | }
70 |
71 | protected override bool Connect()
72 | {
73 | _ram = new ProcessRamWatcher("PuyoPuyoTetris");
74 | return _ram.TryConnect();
75 | }
76 |
77 | public override void Disconnect()
78 | {
79 | _ram = null;
80 | }
81 |
82 | protected override bool Poll()
83 | {
84 | ReadTetrisData();
85 | ReadPuyoData();
86 | return true;
87 | }
88 |
89 | private void ReadTetrisData()
90 | {
91 | int[] OFFSETS = new int[] { 0x378, 0x1D8, 0xE0, 0x08, 0x20, 0xC0, 0x140, 0x60, 0x3E8 };
92 | long tetrisDataBaseAddr = _ram.ResolvePointerPath64(_ram.BaseAddress + 0x461B20, OFFSETS);
93 | if (tetrisDataBaseAddr == 0)
94 | return;
95 |
96 | _linesQuest.UpdateValue(_ram.ReadUint16(tetrisDataBaseAddr));
97 | _tetrisesQuest.UpdateValue(_ram.ReadUint16(tetrisDataBaseAddr - 0x60));
98 | _tspinsQuest.UpdateValue(_ram.ReadUint16(tetrisDataBaseAddr - 0x50));
99 |
100 | byte comboCount = _ram.ReadUint8(tetrisDataBaseAddr - 0xC);
101 | if (comboCount > 0)
102 | comboCount -= 1;
103 | _combosQuest.UpdateValue(comboCount);
104 |
105 | // _perfectClearsQuest.UpdateValue(_ram.ReadUint16(tetrisDataBaseAddr - 0x54));
106 | _backToBackQuest.UpdateValue(_ram.ReadUint8(tetrisDataBaseAddr + 0xB));
107 | }
108 |
109 | private void ReadPuyoData()
110 | {
111 | int[] OFFSETS = new int[] { 0x38, 0x78, 0xE8, 0x28, 0x28, 0xA8, 0x134 };
112 | long puyoDataBaseAddr = _ram.ResolvePointerPath64(_ram.BaseAddress + 0x598A20, OFFSETS);
113 | if (puyoDataBaseAddr == 0)
114 | return;
115 |
116 | _poppedPuyosQuest.UpdateValue(_ram.ReadUint16(puyoDataBaseAddr + 0x154));
117 | _chainsQuest.UpdateValue(_ram.ReadUint8(puyoDataBaseAddr - 0x4));
118 | // _allClearsQuest.UpdateValue(_ram.ReadUint8(puyoDataBaseAddr + 0x34));
119 | }
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/HintMachine/Models/Games/Rollcage2Connector.cs:
--------------------------------------------------------------------------------
1 | using HintMachine.Models.GenericConnectors;
2 |
3 | namespace HintMachine.Models.Games
4 | {
5 | class Rollcage2Connector : IPlayStationConnector
6 | {
7 | private readonly HintQuestCounter _firstPlacesQuest = new HintQuestCounter
8 | {
9 | Name = "Finish first",
10 | GoalValue = 4,
11 | MaxIncrease = 1,
12 | };
13 |
14 | private bool _isFirst = false;
15 | private bool _raceStarted = false;
16 |
17 | public Rollcage2Connector()
18 | {
19 | Name = "Rollcage Stage II";
20 | Description = "Rollcage Stage II is an arcade-style racing game for PlayStation and PC, developed by Attention To Detail, and published by Psygnosis. It is the sequel to Rollcage and was released in 2000. On top of the basic racing concept, the cars can be equipped with weapons, that are picked up on the track as bonuses, which can be used against competing cars. The automobiles themselves, once again, have wheels that are larger than the body of the car thus creating a car that has no 'right way up' and can be flipped and continue to drive.";
21 | SupportedVersions.Add("Any ROM");
22 | CoverFilename = "rollcage_2.png";
23 | Author = "CalDrac";
24 |
25 | Quests.Add(_firstPlacesQuest);
26 |
27 | ValidROMs.Add("SLUS_008.67");
28 | }
29 |
30 | protected override bool Poll()
31 | {
32 | uint laps = _ram.ReadUint8(RamBaseAddress + 0xD23F8);
33 |
34 | if (laps == 0)
35 | {
36 | _raceStarted = true;
37 | }
38 |
39 | if (laps > 100 && _raceStarted)
40 | {
41 | if (_isFirst)
42 | {
43 | _firstPlacesQuest.CurrentValue += 1;
44 | }
45 | _raceStarted = false;
46 | }
47 |
48 | if (_raceStarted)
49 | {
50 | uint place = _ram.ReadUint8(RamBaseAddress + 0xD23F9);
51 | _isFirst = (place == 1);
52 | }
53 |
54 | return true;
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/HintMachine/Models/Games/SonicBlueSpheresConnector.cs:
--------------------------------------------------------------------------------
1 | using HintMachine.Models.GenericConnectors;
2 |
3 | namespace HintMachine.Models.Games
4 | {
5 | public class SonicBlueSpheresConnector : IMegadriveConnector
6 | {
7 | private readonly HintQuestCounter _levelsQuest = new HintQuestCounter
8 | {
9 | Name = "Completed Levels",
10 | GoalValue = 2,
11 | CooldownBetweenIncrements = 60,
12 | };
13 | private readonly HintQuestCumulative _ringsQuest = new HintQuestCumulative
14 | {
15 | Name = "Collected Rings",
16 | MaxIncrease = 10,
17 | GoalValue = 200,
18 | };
19 |
20 | private ushort _previousBlueSpheresWinAnimProgress = 0;
21 |
22 | public SonicBlueSpheresConnector() : base()
23 | {
24 | Name = "Sonic 3 Blue Spheres";
25 | Description = "When you plugged the Sonic The Hedgehog cartridge into the Sonic & Knuckles cartridge " +
26 | "using the wondrous Lock-On Technology™, it brought you to a secret standalone version " +
27 | "of the \"Get Blue Spheres\" minigame from Sonic The Hedgehog 3.\n\n" +
28 | "Play an almost infinite stream of procedurally generated levels, collect all the Blue Spheres and get hints!";
29 | SupportedVersions.Add("Sonic & Knuckles + Sonic the Hedgehog (World)");
30 | SupportedVersions.Add("Blue Spheres Plus");
31 | CoverFilename = "sonic_blue_spheres.png";
32 | Author = "Dinopony";
33 |
34 | Quests.Add(_levelsQuest);
35 | Quests.Add(_ringsQuest);
36 |
37 | // Sonic & Knuckles + Sonic 1
38 | ValidROMs.Add("C9DEA194C3169AD1287C84C157E1257326B393A315B763C5CD04B48DAD4A9DEB");
39 | // Blue Spheres Plus
40 | ValidROMs.Add("06DC460F28F77B31335FC55F0B74A21925983817BD9BBD97F8CCBFBDCAA69101");
41 | }
42 |
43 | protected override bool Poll()
44 | {
45 | ushort blueSpheresWinAnimProgress = _ram.ReadUint16(RamBaseAddress + 0xE44A);
46 | if (blueSpheresWinAnimProgress > 0 && _previousBlueSpheresWinAnimProgress == 0)
47 | _levelsQuest.CurrentValue += 1;
48 | _previousBlueSpheresWinAnimProgress = blueSpheresWinAnimProgress;
49 |
50 | ushort collectedRings = _ram.ReadUint16(RamBaseAddress + 0xE43A);
51 | _ringsQuest.UpdateValue(collectedRings);
52 |
53 | return true;
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/HintMachine/Models/Games/StargunnerConnector.cs:
--------------------------------------------------------------------------------
1 | using HintMachine.Models.GenericConnectors;
2 |
3 | namespace HintMachine.Models.Games
4 | {
5 | class StargunnerConnector : IGameConnector
6 | {
7 | private readonly HintQuestCumulative _scoreQuest = new HintQuestCumulative
8 | {
9 | Name = "Score",
10 | GoalValue = 500000
11 | };
12 | private readonly HintQuestCumulative _creditsQuest = new HintQuestCumulative
13 | {
14 | Name = "Credits",
15 | GoalValue = 5000,
16 | Description = "Collect green gems to obtain credits"
17 | };
18 |
19 | private ProcessRamWatcher _ram = null;
20 | private long _baseAddr = 0;
21 | private long _livesAddr = 0;
22 | private long _scoreAddr = 0;
23 | private long _creditsAddr = 0;
24 | private long _creditsShopAddr = 0;
25 | private bool _gameStarted = false;
26 |
27 | public StargunnerConnector()
28 | {
29 | Name = "Stargunner";
30 | Description = "In the far distant future, an epic war for survival takes place...\r\n\r\nDeep within the Andromeda galaxy, the people of Zile grow restless and greedy. The Zilions secretly prepare for a massive strike against the nearby planet Ytima. Fearing such an attack, the Ytimians train an elite squad of \"Stargunners.\" Their mission: To strike the planet Zile and cripple the Zilions’ three strongholds, where the Zilion war fleets await. If the Stargunners can surprise the Zilions on their own planet and wipe out their fleet, then good will triumph over evil once again.";
31 | Platform = "PC";
32 | SupportedVersions.Add("GOG");
33 | CoverFilename = "stargunner.png";
34 | Author = "CalDrac";
35 |
36 | Quests.Add(_scoreQuest);
37 | Quests.Add(_creditsQuest);
38 | }
39 |
40 | protected override bool Connect()
41 | {
42 | _ram = new ProcessRamWatcher("DOSBox");
43 | if (!_ram.TryConnect())
44 | return false;
45 |
46 | _baseAddr = _ram.ReadInt64(0x1D4A380);
47 | _livesAddr = _baseAddr + 0x5914C;
48 | _scoreAddr = _baseAddr + 0x59160;
49 | _creditsAddr = _baseAddr + 0x59168;
50 | _creditsShopAddr = _baseAddr + 0x58DC8;
51 | return true;
52 | }
53 |
54 | public override void Disconnect()
55 | {
56 | _ram = null;
57 | }
58 |
59 | protected override bool Poll()
60 | {
61 | uint livesNum = _ram.ReadUint32(_livesAddr);
62 | uint creditsShopNum = _ram.ReadUint32(_creditsShopAddr);
63 | if (livesNum <= 10 && creditsShopNum == 1500 && !_gameStarted)
64 | {
65 | _gameStarted = true;
66 | Logger.Debug("Start of game !");
67 | }
68 | if ((livesNum == 0 || livesNum > 10) && _gameStarted)
69 | {
70 | _gameStarted = false;
71 | Logger.Debug("End of game !");
72 | }
73 |
74 | if (_gameStarted)
75 | {
76 | _scoreQuest.UpdateValue(_ram.ReadUint32(_scoreAddr));
77 | _creditsQuest.UpdateValue(_ram.ReadUint32(_creditsAddr));
78 | }
79 | return true;
80 | }
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/HintMachine/Models/Games/SuperHexagonConnector.cs:
--------------------------------------------------------------------------------
1 | using HintMachine.Models.GenericConnectors;
2 |
3 | namespace HintMachine.Models.Games
4 | {
5 | public class SuperHexagonConnector : IGameConnector
6 | {
7 | private readonly BinaryTarget GAME_VERSION_STEAM = new BinaryTarget
8 | {
9 | DisplayName = "Steam",
10 | ProcessName = "superhexagon",
11 | Hash = "72B0C26053C37EDD3435DEF461E9027CD6FFAD12032DB2FD0B32C256FDBEE6B9"
12 | };
13 |
14 | private readonly BinaryTarget GAME_VERSION_ITCH = new BinaryTarget
15 | {
16 | DisplayName = "itch",
17 | ProcessName = "superhexagon",
18 | Hash = "A68BFFBDBC6C1C6C625D6E9F565C6DFDC1D517A98F781BDBD5D7C96636538625"
19 | };
20 |
21 | private readonly HintQuestCumulative _timeQuest = new HintQuestCumulative
22 | {
23 | Name = "Time Survived",
24 | Description = "Survive for 2 minutes. Time only counts if you survive for more than 10 seconds in a round.",
25 | GoalValue = 120,
26 | MaxIncrease = 15,
27 | };
28 |
29 | private ProcessRamWatcher _ram = null;
30 |
31 | private bool _past10seconds = false;
32 |
33 | // ------------------------------------------------------------
34 |
35 | public SuperHexagonConnector()
36 | {
37 | Name = "Super Hexagon";
38 | Description = "Super Hexagon is a minimal action game by Terry Cavanagh, with music by Chipzel.";
39 | Platform = "PC";
40 | SupportedVersions.Add("Steam");
41 | SupportedVersions.Add("itch.io");
42 | CoverFilename = "super_hexagon.png";
43 | Author = "Chandler (itch.io), Dinopony (Steam)";
44 |
45 | Quests.Add(_timeQuest);
46 | }
47 |
48 | protected override bool Connect()
49 | {
50 | _ram = new ProcessRamWatcher();
51 | _ram.SupportedTargets.Add(GAME_VERSION_STEAM);
52 | _ram.SupportedTargets.Add(GAME_VERSION_ITCH);
53 | return _ram.TryConnect();
54 | }
55 |
56 | public override void Disconnect()
57 | {
58 | _ram = null;
59 | }
60 |
61 | protected override bool Poll()
62 | {
63 | long timeReading = 0;
64 |
65 | if(_ram.CurrentTarget == GAME_VERSION_STEAM)
66 | {
67 | long timeAddress = _ram.ResolvePointerPath32(_ram.BaseAddress + 0x15E8EC, new int[] { 0x2928 });
68 | if(timeAddress != 0)
69 | timeReading = (long)(_ram.ReadDouble(timeAddress)) / 60;
70 | }
71 | else if(_ram.CurrentTarget == GAME_VERSION_ITCH)
72 | {
73 | long timeAddress = _ram.ResolvePointerPath32(_ram.BaseAddress + 0x29EB94, new int[] { 0xC, 0x10 });
74 | if(timeAddress != 0)
75 | timeReading = _ram.ReadUint32(timeAddress + 0x5518) / 60;
76 | }
77 |
78 | if (timeReading > 0)
79 | {
80 | if (_past10seconds)
81 | {
82 | if (timeReading < 10)
83 | {
84 | _past10seconds = false;
85 | }
86 | else
87 | {
88 | _timeQuest.UpdateValue(timeReading);
89 | }
90 | }
91 | else if (timeReading >= 10)
92 | {
93 | _past10seconds = true;
94 | _timeQuest.UpdateValue(0);
95 | }
96 | }
97 |
98 | return true;
99 | }
100 | }
101 | }
--------------------------------------------------------------------------------
/HintMachine/Models/Games/SuperMegaBaseball2Connector.cs:
--------------------------------------------------------------------------------
1 | using HintMachine.Models.GenericConnectors;
2 |
3 | namespace HintMachine.Models.Games
4 | {
5 | public class SuperMegaBaseball2Connector : IGameConnector
6 | {
7 | // AFAIK the only PC version out there is the Steam version. If there's some other (current!) obscure PC version out there, we'll need to get its hash and do some magic to support it.
8 | private readonly BinaryTarget GAME_VERSION_STEAM = new BinaryTarget
9 | {
10 | DisplayName = "Steam",
11 | ProcessName = "supermegabaseball",
12 | Hash = "3525F99F5811EC9ECB4041AC9752CBFC477F354EE63840B2A36D166AB8F584B9"
13 | };
14 |
15 |
16 | /// Starpoints: The rate at which starpoints are gained is chiefly affected by the intersection of EGO (difficulty) set and player skill.
17 | /// While higher EGO means higher multiplier (up to 100x at EGO 99), that can result in LOWER Starpoints in the end if player skill can't keep up.
18 | /// For sake of having a baseline for pace balancing, we're using a multiplier of x35, which corresponds to an EGO just above 60 (noted ingame as "Very Hard")
19 | /// The bottom end of the EGO setting for consideration is EGO 40 ("Serious", multiplier x14), who get score at 40% of this rate.
20 | /// The top end of the EGO setting for consideration is EGO 80 ("Good Luck", multiplier x63), who get score at 180% of this rate.
21 | /// Other reference points for EGO settings: EGO 50 ("Hard") -> x23, EGO 60 -> x34, and EGO 70 ("Extreme") -> x47.
22 |
23 | private readonly HintQuestCumulative _starpointsQuest = new HintQuestCumulative
24 | {
25 | Name = "Starpoints",
26 | GoalValue = 40000,
27 | };
28 |
29 | private ProcessRamWatcher _ram = null;
30 |
31 | // ------------------------------------------------------------------------
32 |
33 | public SuperMegaBaseball2Connector()
34 | {
35 | Name = "Super Mega Baseball 2";
36 | Description = "The second iteration of the Arcade-Style Baseball Game. Hit some dingers!";
37 | Platform = "PC";
38 | SupportedVersions.Add("Steam");
39 | CoverFilename = "super_mega_baseball_2.png";
40 | Author = "Kitsune Zeta";
41 |
42 | Quests.Add(_starpointsQuest);
43 | }
44 |
45 | protected override bool Connect()
46 | {
47 | _ram = new ProcessRamWatcher(GAME_VERSION_STEAM);
48 | return _ram.TryConnect();
49 | }
50 |
51 | public override void Disconnect()
52 | {
53 | _ram = null;
54 | }
55 |
56 | protected override bool Poll()
57 | {
58 | int[] OFFSETS = new int[] { 0x20, 0x98, 0x468, 0x10, 0x20, 0x278 };
59 | long scoreAddress = _ram.ResolvePointerPath64(_ram.BaseAddress + 0x02A88FB8, OFFSETS);
60 | if (scoreAddress != 0)
61 | {
62 | _starpointsQuest.UpdateValue(_ram.ReadUint32(scoreAddress));
63 | }
64 |
65 | return true;
66 | }
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/HintMachine/Models/Games/SuperMonkeyBall2Connector.cs:
--------------------------------------------------------------------------------
1 | using HintMachine.Models.GenericConnectors;
2 |
3 | namespace HintMachine.Models.Games
4 | {
5 | public class SuperMonkeyBall2Connector : IDolphinConnector
6 | {
7 | private readonly HintQuestCumulative _bananaQuest = new HintQuestCumulative
8 | {
9 | Name = "Bananas collected",
10 | GoalValue = 50,
11 | MaxIncrease = 10
12 | };
13 |
14 | private readonly HintQuestCumulative _stageQuest = new HintQuestCumulative
15 | {
16 | Name = "Stages cleared",
17 | GoalValue = 3,
18 | MaxIncrease = 1
19 | };
20 |
21 | public SuperMonkeyBall2Connector() : base(false)
22 | {
23 | Name = "Super Monkey Ball 2";
24 | Description = "Traverse through stages filled to the brim with obstacles and collect bananas " +
25 | "along the way as a monkey inside of a ball! Quests work on story mode and all difficulties of challenge mode.";
26 | SupportedVersions.Add("NTSC US");
27 | CoverFilename = "super_monkey_ball_2.png";
28 | Author = "Spicynun";
29 |
30 | Quests.Add(_bananaQuest);
31 | Quests.Add(_stageQuest);
32 |
33 | ValidROMs.Add("GM2E8P");
34 | }
35 |
36 | protected override bool Poll()
37 | {
38 | // Banana counts for story and challenge
39 | int player1banana = _ram.ReadUint8(MEM1 + 0x5bca1b);
40 | int bananaCount = player1banana;
41 | _bananaQuest.UpdateValue(bananaCount);
42 |
43 | // Story mode world clears
44 | int world1 = _ram.ReadUint8(MEM1 + 0x5d4b0b);
45 | int world2 = _ram.ReadUint8(MEM1 + 0x5d4b43);
46 | int world3 = _ram.ReadUint8(MEM1 + 0x5d4b7b);
47 | int world4 = _ram.ReadUint8(MEM1 + 0x5d4bb3);
48 | int world5 = _ram.ReadUint8(MEM1 + 0x5d4beb);
49 | int world6 = _ram.ReadUint8(MEM1 + 0x5d4c23);
50 | int world7 = _ram.ReadUint8(MEM1 + 0x5d4c5b);
51 | int world8 = _ram.ReadUint8(MEM1 + 0x5d4c93);
52 | int world9 = _ram.ReadUint8(MEM1 + 0x5d4ccb);
53 | int world10 = _ram.ReadUint8(MEM1 + 0x5d4d03);
54 |
55 | // Challenge mode clears
56 | int player1challenge = _ram.ReadUint8(MEM1 + 0x5d4973);
57 |
58 | // Checks if story menu is accessed which sets progress to 0
59 | int inStoryMenu = _ram.ReadUint8(MEM1 + 0x54dbef);
60 |
61 | // Prevent the stage clear from increasing every time a save in story mode is loaded
62 | if (inStoryMenu != 0)
63 | {
64 | int stageClears = world1 + world2 + world3 + world4 + world5 + world6 + world7 + world8 + world9 + world10 + player1challenge;
65 | _stageQuest.UpdateValue(stageClears);
66 | }
67 |
68 | return true;
69 | }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/HintMachine/Models/Games/SuperMonkeyBallConnector.cs:
--------------------------------------------------------------------------------
1 | using HintMachine.Models.GenericConnectors;
2 |
3 | namespace HintMachine.Models.Games
4 | {
5 | public class SuperMonkeyBallConnector : IDolphinConnector
6 | {
7 | private readonly HintQuestCumulative _bananaQuest = new HintQuestCumulative
8 | {
9 | Name = "Bananas collected",
10 | GoalValue = 50,
11 | MaxIncrease = 10
12 | };
13 |
14 | private readonly HintQuestCumulative _playPointsQuest = new HintQuestCumulative
15 | {
16 | Name = "Play Points",
17 | GoalValue = 250
18 | };
19 |
20 | public SuperMonkeyBallConnector() : base(false)
21 | {
22 | Name = "Super Monkey Ball";
23 | Description = "Conceived by Amusement Vision head Toshihiro Nagoshi, " +
24 | "Super Monkey Ball involves guiding a transparent ball containing one of four " +
25 | "monkeys—AiAi, MeeMee, Baby, and GonGon—across a series of maze-like platforms. " +
26 | "The player must reach the goal without falling off or letting the timer reach zero " +
27 | "to advance to the next stage. There are also several multiplayer modes: independent " +
28 | "minigames as well as extensions of the main single-player game. " +
29 | "Quests work on all difficulties of the main game.";
30 | SupportedVersions.Add("NTSC US");
31 | CoverFilename = "super_monkey_ball.png";
32 | Author = "Spicynun";
33 |
34 | Quests.Add(_bananaQuest);
35 | Quests.Add(_playPointsQuest);
36 |
37 | ValidROMs.Add("GMBE8P");
38 | }
39 |
40 | protected override bool Poll()
41 | {
42 | // Banana counts
43 | int bananaCount = _ram.ReadUint8(MEM1 + 0x205EDB);
44 | _bananaQuest.UpdateValue(bananaCount);
45 |
46 |
47 | // Play Points
48 | uint playPoints = _ram.ReadUint32(MEM1 + 0x2f1fc4);
49 | _playPointsQuest.UpdateValue(playPoints);
50 |
51 | return true;
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/HintMachine/Models/Games/TMNTShreddersRevengeConnector.cs:
--------------------------------------------------------------------------------
1 | using HintMachine.Models.GenericConnectors;
2 |
3 | namespace HintMachine.Models.Games
4 | {
5 | public class TMNTShreddersRevengeConnector : IGameConnector
6 | {
7 | private readonly BinaryTarget GAME_VERSION_STEAM = new BinaryTarget
8 | {
9 | DisplayName = "Steam",
10 | ProcessName = "TMNT",
11 | Hash = "8AAA6D910754E1B84D62702BF66BE4C22D46EA23D6DC0B80AD9761909EE22389"
12 | };
13 |
14 | private readonly HintQuestCumulative _enemyQuest = new HintQuestCumulative
15 | {
16 | Name = "Enemies Defeated",
17 | GoalValue = 100,
18 | Description = "Story Mode, Arcade Mode",
19 | MaxIncrease = 10,
20 | };
21 |
22 | private readonly HintQuestCumulative _comboQuest = new HintQuestCumulative
23 | {
24 | Name = "Combo Points",
25 | GoalValue = 500,
26 | Description = "Story Mode, Arcade Mode",
27 | MaxIncrease = 10,
28 | };
29 |
30 | private readonly HintQuestCumulative _objectQuest = new HintQuestCumulative
31 | {
32 | Name = "Objects Destroyed",
33 | GoalValue = 25,
34 | Description = "Story Mode, Arcade Mode",
35 | MaxIncrease = 3,
36 | };
37 |
38 | private ProcessRamWatcher _ram = null;
39 |
40 | public TMNTShreddersRevengeConnector()
41 | {
42 | Name = "TMNT: Shredder's Revenge";
43 | Description = "Teenage Mutant Ninja Turtles: Shredder’s Revenge features groundbreaking gameplay rooted in timeless classic brawling mechanics, brought to you by the beat ’em up experts at Dotemu (Streets of Rage 4) and Tribute Games. Bash your way through gorgeous pixel art environments and slay tons of hellacious enemies with your favorite Turtle, each with his own skills and moves - making each run unique! Choose a fighter, use radical combos to defeat your opponents and experience intense combats loaded with breathtaking action and outrageous ninja abilities. Stay sharp as you face off against Shredder and his faithful Foot Clan alone, or grab your best buds and play with up to 6 players simultaneously!";
44 | Platform = "PC";
45 | SupportedVersions.Add("Steam");
46 | CoverFilename = "tmnt_shredders_revenge.png";
47 | Author = "Serpent.AI";
48 |
49 | Quests.Add(_enemyQuest);
50 | Quests.Add(_comboQuest);
51 | Quests.Add(_objectQuest);
52 | }
53 |
54 | protected override bool Connect()
55 | {
56 | _ram = new ProcessRamWatcher();
57 | _ram.SupportedTargets.Add(GAME_VERSION_STEAM);
58 |
59 | return _ram.TryConnect();
60 | }
61 |
62 | public override void Disconnect()
63 | {
64 | _ram = null;
65 | }
66 |
67 | protected override bool Poll()
68 | {
69 | long gamePlayerInfoStructAddress = _ram.ResolvePointerPath64(_ram.Threadstack0 - 0xF70, new int[] { 0x30, 0x150, 0x80, 0x100, 0x28, 0x58 });
70 |
71 | if (gamePlayerInfoStructAddress != 0)
72 | {
73 | try
74 | {
75 | long enemyValue = _ram.ReadUint32(gamePlayerInfoStructAddress + 0x64);
76 | long comboValue = _ram.ReadUint32(gamePlayerInfoStructAddress + 0x34);
77 | long objectValue = _ram.ReadUint32(gamePlayerInfoStructAddress + 0x84);
78 |
79 | _enemyQuest.UpdateValue(enemyValue);
80 | _comboQuest.UpdateValue(comboValue);
81 | _objectQuest.UpdateValue(objectValue);
82 | }
83 | catch { }
84 | }
85 |
86 | return true;
87 | }
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/HintMachine/Models/Games/TetrisEffectConnector.cs:
--------------------------------------------------------------------------------
1 | using HintMachine.Models.GenericConnectors;
2 |
3 | namespace HintMachine.Models.Games
4 | {
5 | public class TetrisEffectConnector : IGameConnector
6 | {
7 | private BinaryTarget GAME_VERSION_STEAM = new BinaryTarget
8 | {
9 | DisplayName = "Steam",
10 | ProcessName = "TetrisEffect-Win64-Shipping",
11 | Hash = "1981E9829739E3C2E7549E9DEC3384A3E649632A514D9D5F0711A37CC945279D"
12 | };
13 |
14 | private BinaryTarget GAME_VERSION_EPIC = new BinaryTarget
15 | {
16 | DisplayName = "Epic",
17 | ProcessName = "TetrisEffect-Win64-Shipping",
18 | Hash = "45CF1A171161725BC6DFB3C3E3735580202BF5B5BE3A34769B378C503F94F83E"
19 | };
20 |
21 |
22 | private readonly HintQuestCumulative _linesQuest = new HintQuestCumulative
23 | {
24 | Name = "Cleared Lines",
25 | GoalValue = 200,
26 | };
27 | private readonly HintQuestCumulative _tetrisesQuest = new HintQuestCumulative
28 | {
29 | Name = "Tetris™",
30 | GoalValue = 25,
31 | };
32 | private readonly HintQuestCumulative _backToBackQuest = new HintQuestCumulative
33 | {
34 | Name = "Back-to-Back",
35 | GoalValue = 30,
36 | };
37 | private readonly HintQuestCumulative _tspinsQuest = new HintQuestCumulative
38 | {
39 | Name = "T-Spins",
40 | GoalValue = 40,
41 | };
42 | /*
43 | private readonly HintQuestCumulative _combosQuest = new HintQuestCumulative
44 | {
45 | Name = "Combos",
46 | GoalValue = 60,
47 | };
48 | private readonly HintQuestCumulative _perfectClearsQuest = new HintQuestCumulative
49 | {
50 | Name = "All Clears",
51 | GoalValue = 5,
52 | };
53 | */
54 |
55 | private ProcessRamWatcher _ram = null;
56 |
57 | // ------------------------------------------------------------------------
58 |
59 | public TetrisEffectConnector()
60 | {
61 | Name = "Tetris Effect: Connected";
62 | Description = "Named after a real-world phenomenon where players' brains are so engrossed that images of the iconic falling " +
63 | "Tetrimino blocks linger in their vision, thoughts, and even dreams, Tetris Effect amplifies this magical feeling " +
64 | "of total immersion by surrounding you with fantastic, fully three-dimensional worlds that react and evolve based " +
65 | "on how you play. Music, backgrounds, sounds, special effects - everything, down to the Tetris pieces themselves, " +
66 | "pulse, dance, shimmer, and explode in perfect sync with how you're playing.";
67 | Platform = "PC";
68 | SupportedVersions.Add("Steam");
69 | SupportedVersions.Add("Epic");
70 | CoverFilename = "tetris_effect_connected.png";
71 | Author = "Dinopony";
72 |
73 | Quests.Add(_linesQuest);
74 | Quests.Add(_tetrisesQuest);
75 | Quests.Add(_backToBackQuest);
76 | Quests.Add(_tspinsQuest);
77 | // Quests.Add(_perfectClearsQuest);
78 | // Quests.Add(_combosQuest);
79 | }
80 |
81 | protected override bool Connect()
82 | {
83 | _ram = new ProcessRamWatcher();
84 | _ram.SupportedTargets.Add(GAME_VERSION_STEAM);
85 | _ram.SupportedTargets.Add(GAME_VERSION_EPIC);
86 |
87 | return _ram.TryConnect();
88 | }
89 |
90 | public override void Disconnect()
91 | {
92 | _ram = null;
93 | }
94 |
95 | protected override bool Poll()
96 | {
97 | long baseOffset = (_ram.CurrentTarget == GAME_VERSION_STEAM) ? 0x4ED9990 : 0x4ECE880;
98 | int[] OFFSETS = new int[] { 0x8, 0x8, 0x220, 0x200, 0x64 };
99 |
100 | long linesAddress = _ram.ResolvePointerPath64(_ram.BaseAddress + baseOffset, OFFSETS);
101 | if (linesAddress != 0)
102 | {
103 | _linesQuest.UpdateValue(_ram.ReadUint32(linesAddress));
104 | _tetrisesQuest.UpdateValue(_ram.ReadUint32(linesAddress + 0x40));
105 | _backToBackQuest.UpdateValue(_ram.ReadUint32(linesAddress + 0x50));
106 | _tspinsQuest.UpdateValue(_ram.ReadUint32(linesAddress + 0x70));
107 |
108 | // _perfectClearsQuest.UpdateValue(_ram.ReadUint32(linesAddress + 0xC0));
109 | // _combosQuest.UpdateValue(_ram.ReadUint32(combosAddress));
110 | }
111 |
112 | return true;
113 | }
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/HintMachine/Models/Games/TetrisNESConnector.cs:
--------------------------------------------------------------------------------
1 | using HintMachine.Models.GenericConnectors;
2 |
3 | namespace HintMachine.Models.Games
4 | {
5 | public class TetrisNESConnector : INESConnector
6 | {
7 | private readonly HintQuestCumulative _linesQuest = new HintQuestCumulative
8 | {
9 | Name = "Cleared Lines",
10 | GoalValue = 40,
11 | };
12 |
13 | public TetrisNESConnector()
14 | {
15 | Name = "Tetris";
16 | Description = "Tetris is a tile-matching puzzle video game. The goal is to place pieces made up of four tiles in a ten-by-twenty well, organizing them into complete rows, which then disappear. The main objective of each round is to clear 25 lines, after which the player moves on to the next round. If the stack reaches the top of the field, the player loses a life, and if all three lives are lost, the game is over.\r\n\r\nThe game lets the player choose the starting stage and round, as well as one of three background tunes. Difficulty is increased throughout the stages by an increase in speed and the addition of garbage blocks in the well.";
17 | SupportedVersions.Add("PAL (🇪🇺)");
18 | SupportedVersions.Add("NTSC-U (🇺🇸)");
19 | CoverFilename = "tetris_nes.png";
20 | Author = "Dinopony";
21 |
22 | Quests.Add(_linesQuest);
23 |
24 | ValidROMs.Add("8DA063C3A4D9DB281B0F7E32DC9EFAC3CA505BB97845B6ECA2AF525960EB51A0");
25 | ValidROMs.Add("42CD2FF75AD808D7444FEEB64009DBAC817561BE807418B1E26355BB21254280");
26 | }
27 |
28 | protected override bool Poll()
29 | {
30 | int lowestTwoDigits = _ram.ReadUint8(RamBaseAddress + 0x50);
31 | int highestTwoDigits = _ram.ReadUint8(RamBaseAddress + 0x51);
32 |
33 | int[] digits = new int[4];
34 | digits[3] = (highestTwoDigits >> 4);
35 | digits[2] = (highestTwoDigits & 0x0F);
36 | digits[1] = (lowestTwoDigits >> 4);
37 | digits[0] = (lowestTwoDigits & 0x0F);
38 |
39 | int lines = 0;
40 | int currentPowerOf10 = 1;
41 | for (var i = 0; i < digits.Length; ++i)
42 | {
43 | lines += digits[i] * currentPowerOf10;
44 | currentPowerOf10 *= 10;
45 | }
46 | _linesQuest.UpdateValue(lines);
47 |
48 | return true;
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/HintMachine/Models/Games/TonyHawksProSkater12Connector.cs:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HintMachine/hintMachine/24c97be899feeacf270cc81aa1027687e38c446a/HintMachine/Models/Games/TonyHawksProSkater12Connector.cs
--------------------------------------------------------------------------------
/HintMachine/Models/Games/XenotiltConnector.cs:
--------------------------------------------------------------------------------
1 | using HintMachine.Models.GenericConnectors;
2 |
3 | namespace HintMachine.Models.Games
4 | {
5 | public class XenotiltConnector : IGameConnector
6 | {
7 | private readonly HintQuestCumulative _scoreQuest = new HintQuestCumulative
8 | {
9 | Name = "Score",
10 | GoalValue = 200000000,
11 | };
12 |
13 | private ProcessRamWatcher _ram = null;
14 |
15 | public XenotiltConnector()
16 | {
17 | Name = "Xenotilt";
18 | Description = "In this three parts pinball table, you will have to complete missions to earn a lot of points.";
19 | Platform = "PC";
20 | SupportedVersions.Add(".282");
21 | CoverFilename = "xenotilt.png";
22 | Author = "CalDrac";
23 |
24 | Quests.Add(_scoreQuest);
25 | }
26 |
27 | protected override bool Connect()
28 | {
29 | _ram = new ProcessRamWatcher("Xenotilt", "mono-2.0-bdwgc.dll");
30 | return _ram.TryConnect();
31 | }
32 |
33 | public override void Disconnect()
34 | {
35 | _ram = null;
36 | }
37 |
38 | protected override bool Poll()
39 | {
40 | long scoreAddress = _ram.ResolvePointerPath64(_ram.BaseAddress + 0x7270B8, new int[] { 0x30, 0x7e0, 0x7C0 });
41 | _scoreQuest.UpdateValue(_ram.ReadInt64(scoreAddress));
42 |
43 | return true;
44 | }
45 | }
46 | }
--------------------------------------------------------------------------------
/HintMachine/Models/Games/ZachtronicsSolitaireConnector.cs:
--------------------------------------------------------------------------------
1 | using HintMachine.Models.GenericConnectors;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.IO;
5 | using System.Text;
6 | using System.Text.RegularExpressions;
7 |
8 | namespace HintMachine.Models.Games
9 | {
10 | public class ZachtronicsSolitaireConnector : IGameConnector
11 | {
12 | private readonly HintQuestCounter _winsQuest = new HintQuestCounter {
13 | Name = "Wins",
14 | GoalValue = 1,
15 | MaxIncrease = 2,
16 | Description = "Fortune's Foundation wins are worth double"
17 | };
18 |
19 | private List _watchers = new List();
20 | private string _fileToReadOnNextTick = "";
21 | private Dictionary _totalWinCounts = new Dictionary();
22 | private ProcessRamWatcher _ram = null;
23 |
24 | // ----------------------------------------------------------------------------------
25 |
26 | public ZachtronicsSolitaireConnector()
27 | {
28 | Name = "Zachtronics Solitaire Collection";
29 | Description = "Play 8 different variants of Solitaire in this collection of games " +
30 | "initially created as mini-games for Zachtronics main titles.";
31 | Platform = "PC";
32 | SupportedVersions.Add("Steam");
33 | SupportedVersions.Add("GOG");
34 | SupportedVersions.Add("itch.io");
35 | CoverFilename = "zachtronics_solitaire_collection.png";
36 | Author = "Dinopony";
37 |
38 | Quests.Add(_winsQuest);
39 | }
40 |
41 | protected override bool Connect()
42 | {
43 | try
44 | {
45 | _ram = new ProcessRamWatcher("The Zachtronics Solitaire Collection");
46 | if (!_ram.TryConnect())
47 | return false;
48 |
49 | // Setup a watcher to be notified when the file is changed
50 | foreach(string pathToDir in FindPotentialSavefileDirectories())
51 | {
52 | FileSystemWatcher watcher = new FileSystemWatcher
53 | {
54 | Path = pathToDir,
55 | NotifyFilter = NotifyFilters.LastWrite,
56 | Filter = "save.dat",
57 | EnableRaisingEvents = true
58 | };
59 |
60 | string pathToFile = pathToDir + "/save.dat";
61 | _totalWinCounts[pathToFile] = ReadTotalWinCount(pathToFile);
62 |
63 | watcher.Changed += new FileSystemEventHandler((object source, FileSystemEventArgs e) => {
64 | _fileToReadOnNextTick = pathToFile;
65 | });
66 |
67 | _watchers.Add(watcher);
68 | }
69 |
70 | return true;
71 | }
72 | catch
73 | {
74 | return false;
75 | }
76 | }
77 |
78 | public override void Disconnect()
79 | {
80 | foreach (FileSystemWatcher watcher in _watchers)
81 | watcher.EnableRaisingEvents = false;
82 | _watchers.Clear();
83 |
84 | _ram = null;
85 | }
86 |
87 | protected override bool Poll()
88 | {
89 | if(!_ram.TestProcess())
90 | return false;
91 |
92 | if (_fileToReadOnNextTick != "")
93 | {
94 | int oldWins = _totalWinCounts[_fileToReadOnNextTick];
95 | int newWins = ReadTotalWinCount(_fileToReadOnNextTick);
96 | if (newWins > oldWins)
97 | _winsQuest.CurrentValue += (newWins - oldWins);
98 | _totalWinCounts[_fileToReadOnNextTick] = newWins;
99 |
100 | _fileToReadOnNextTick = "";
101 | }
102 |
103 | return true;
104 | }
105 |
106 | private int ReadTotalWinCount(string pathToFile)
107 | {
108 | int totalWinCount = 0;
109 |
110 | FileStream file = new FileStream(pathToFile, FileMode.Open, FileAccess.Read);
111 | using (var streamReader = new StreamReader(file, Encoding.UTF8))
112 | {
113 | string text = streamReader.ReadToEnd();
114 | string[] lines = text.Split('\n');
115 |
116 | foreach (string line in lines)
117 | {
118 | Regex regex = new Regex(" = ");
119 | string[] keyval = regex.Split(line);
120 | if (keyval.Length < 2)
121 | continue;
122 |
123 | if (keyval[0] == "Milan.WinCount")
124 | totalWinCount += int.Parse(keyval[1]) * 2; // Fortune's Foundation wins are worth double
125 | else if (keyval[0].Contains("WinCount"))
126 | totalWinCount += int.Parse(keyval[1]);
127 | }
128 | }
129 |
130 | file.Close();
131 |
132 | return totalWinCount;
133 | }
134 |
135 | private string[] FindPotentialSavefileDirectories()
136 | {
137 | string path = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
138 | path += @"\My Games\The Zachtronics Solitaire Collection\";
139 | return Directory.GetDirectories(path);
140 | }
141 | }
142 | }
143 |
--------------------------------------------------------------------------------
/HintMachine/Models/GenericConnectors/IDolphinConnector.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 | using System.Text;
3 |
4 | namespace HintMachine.Models.GenericConnectors
5 | {
6 | public abstract class IDolphinConnector : IEmulatorConnector
7 | {
8 | protected ProcessRamWatcher _ram = null;
9 | protected readonly bool _isWii;
10 |
11 | protected long MEM1 { get; private set; } = 0;
12 |
13 | protected long MEM2 { get; private set; } = 0;
14 |
15 | // ----------------------------------------------------------------
16 |
17 | public IDolphinConnector(bool isWii)
18 | {
19 | _isWii = isWii;
20 | Platform = (isWii) ? "Wii" : "GameCube";
21 | SupportedEmulators.Add("Dolphin 5");
22 | }
23 |
24 | protected override bool Connect()
25 | {
26 | _ram = new ProcessRamWatcher("Dolphin");
27 | _ram.IsBigEndian = true;
28 |
29 | if (!_ram.TryConnect())
30 | return false;
31 |
32 | if (!InitMEM1())
33 | return false;
34 |
35 | if (_isWii && !InitMEM2())
36 | return false;
37 |
38 | return true;
39 | }
40 |
41 | public override void Disconnect()
42 | {
43 | base.Disconnect();
44 | _ram = null;
45 | MEM1 = 0;
46 | MEM2 = 0;
47 | }
48 |
49 | private bool InitMEM1()
50 | {
51 | var regions = _ram.ListMemoryRegions().Where(r => r.Size == 0x2000000 && r.Type == MemoryRegionType.MEM_MAPPED);
52 | foreach (MemoryRegion region in regions)
53 | {
54 | byte[] headerBytes = _ram.ReadBytes(region.BaseAddress, 6);
55 | foreach (string gameCode in ValidROMs)
56 | {
57 | byte[] gameCodeBytes = Encoding.ASCII.GetBytes(gameCode);
58 |
59 | if (Enumerable.SequenceEqual(gameCodeBytes, headerBytes))
60 | {
61 | MEM1 = region.BaseAddress;
62 | Logger.Debug($"MEM1 = {MEM1:X}");
63 | return true;
64 | }
65 | }
66 | }
67 |
68 | return false;
69 | }
70 |
71 | private bool InitMEM2()
72 | {
73 | var regions = _ram.ListMemoryRegions().Where(r => r.Size == 0x4000000 && r.Type == MemoryRegionType.MEM_MAPPED);
74 | foreach (MemoryRegion region in regions)
75 | {
76 | MEM2 = region.BaseAddress;
77 | Logger.Debug($"MEM2 = {MEM2:X}");
78 | return true;
79 | }
80 |
81 | return false;
82 | }
83 |
84 | public override long GetCurrentFrameCount()
85 | {
86 | // Getting a reliable frameCount over many versions of Dolphin is pretty much impossible, so...
87 | // we don't do it.
88 | return 0;
89 | }
90 |
91 | public override string GetRomIdentity()
92 | {
93 | // Use the 6 characters long game code as identity
94 | byte[] headerBytes = _ram.ReadBytes(MEM1, 0x06);
95 | return Encoding.Default.GetString(headerBytes);
96 | }
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/HintMachine/Models/GenericConnectors/IEmulatorConnector.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace HintMachine.Models.GenericConnectors
4 | {
5 | public abstract class IEmulatorConnector : IGameConnector
6 | {
7 | public List ValidROMs { get; private set; } = new List();
8 |
9 | public string CurrentROM { get; private set; } = "";
10 |
11 | private long _previousFrameCount = 0;
12 |
13 | private long _tickId = 0;
14 |
15 | // -------------------------------------
16 | public IEmulatorConnector()
17 | {}
18 |
19 | public override void Disconnect()
20 | {
21 | _previousFrameCount = 0;
22 | _tickId = 0;
23 | }
24 |
25 | protected override bool AfterConnect()
26 | {
27 | if (!TestRomIdentity())
28 | {
29 | Logger.Debug($"Found a valid emu process with invalid ROM identity '{CurrentROM}'");
30 | return false;
31 | }
32 |
33 | return true;
34 | }
35 |
36 | protected override bool BeforePoll()
37 | {
38 | // Every 10 ticks (~1s), check if the ROM identity has changed to ensure there was no ROM swapping.
39 | // If ROM was indeed changed, cut the connection.
40 | if (++_tickId % 10 == 0 && !TestRomIdentity())
41 | {
42 | Logger.Debug("ROM identity has become wrong, disconnecting...");
43 | return false;
44 | }
45 |
46 | // Every tick, check if frame count went backwards to detect save state usage. If any save state
47 | // was used, cut the connection.
48 | long currentFrameCount = GetCurrentFrameCount();
49 | if (currentFrameCount != 0 && currentFrameCount < _previousFrameCount)
50 | {
51 | Logger.Debug($"Frame count rollback detected ({_previousFrameCount} -> {currentFrameCount}), disconnecting...");
52 | return false;
53 | }
54 | _previousFrameCount = currentFrameCount;
55 |
56 | return true;
57 | }
58 |
59 | public bool TestRomIdentity()
60 | {
61 | CurrentROM = GetRomIdentity();
62 | if (CurrentROM != "" && ValidROMs.Count > 0 && !ValidROMs.Contains(CurrentROM))
63 | return false;
64 | return true;
65 | }
66 |
67 | public abstract long GetCurrentFrameCount();
68 |
69 | public abstract string GetRomIdentity();
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/HintMachine/Models/GenericConnectors/IGameConnector.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace HintMachine.Models.GenericConnectors
4 | {
5 | public abstract class IGameConnector
6 | {
7 | ///
8 | /// Display name for this game used in the various UI components
9 | ///
10 | public string Name { get; protected set; } = "Unnamed Game";
11 |
12 | ///
13 | /// Brief description for the game used in the various UI components
14 | ///
15 | public string Description { get; protected set; } = "";
16 |
17 | ///
18 | /// The hardware platform the game was originally available on (e.g. PC for a PC game, "Nintendo 64" for the N64, etc...)
19 | ///
20 | public string Platform { get; protected set; } = "";
21 |
22 | ///
23 | /// String detailing which versions of the game have been tested to work with this connector
24 | ///
25 | public List SupportedVersions { get; protected set; } = new List{};
26 |
27 | ///
28 | /// String detailing which emulators have been tested to work with this connector (if relevant)
29 | ///
30 | public List SupportedEmulators { get; protected set; } = new List { };
31 |
32 | ///
33 | /// The author of the game connector
34 | ///
35 | public string Author { get; protected set; } = "";
36 |
37 | ///
38 | /// List of quests available for this game
39 | ///
40 | public List Quests { get; protected set; } = new List();
41 |
42 | ///
43 | /// The filename for the cover art of the game, which is looked for in the "Assets/covers/" directory
44 | ///
45 | public string CoverFilename { get; protected set; } = "unknown.png";
46 |
47 | ///
48 | /// Performs the full connection process by triggering BeforeConnect, Connect & AfterConnect methods.
49 | ///
50 | /// true if connection went well, false otherwise
51 | public bool DoConnect()
52 | {
53 | if (!BeforeConnect()) return false;
54 | if (!Connect()) return false;
55 | if (!AfterConnect()) return false;
56 |
57 | return true;
58 | }
59 |
60 | ///
61 | /// Function being called right before the connection process, which can be overriden.
62 | ///
63 | /// false if something went wrong, true if connection can keep going
64 | protected virtual bool BeforeConnect() => true;
65 |
66 | ///
67 | /// Abstract function meant to handle the connection to the related game process / files
68 | /// required for the data fetching to work afterwards.
69 | ///
70 | protected abstract bool Connect();
71 |
72 | ///
73 | /// Function being called right after the connection process, which can be overriden.
74 | ///
75 | /// false if something went wrong, true if connection can keep going
76 | protected virtual bool AfterConnect() => true;
77 |
78 | ///
79 | /// Abstract function handling disconnection from the game process / files, releasing all
80 | /// handles in a clean way.
81 | ///
82 | public abstract void Disconnect();
83 |
84 | ///
85 | /// Performs the full polling process by triggering BeforePoll, Poll & AfterPoll methods.
86 | ///
87 | /// true if polling went well, false otherwise
88 | public bool DoPoll()
89 | {
90 | if (!BeforePoll()) return false;
91 | if (!Poll()) return false;
92 | if (!AfterPoll()) return false;
93 |
94 | return true;
95 | }
96 |
97 | ///
98 | /// Function being called right before the polling process, which can be overriden.
99 | ///
100 | /// false if something went wrong, true if polling can keep going
101 | protected virtual bool BeforePoll() => true;
102 |
103 | ///
104 | /// Abstract function handling the actual data retrieval from the game process / files,
105 | /// storing them in connector attributes for further usage.
106 | ///
107 | protected abstract bool Poll();
108 |
109 | ///
110 | /// Function being called right after the polling process, which can be overriden.
111 | ///
112 | /// false if something went wrong, true if polling can keep going
113 | protected virtual bool AfterPoll() => true;
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/HintMachine/Models/GenericConnectors/IGameboyAdvanceConnector.cs:
--------------------------------------------------------------------------------
1 | using System.Text;
2 | using HintMachine.Helpers;
3 |
4 | namespace HintMachine.Models.GenericConnectors
5 | {
6 | public abstract class IGameboyAdvanceConnector : IEmulatorConnector
7 | {
8 | protected ProcessRamWatcher _ram = null;
9 |
10 | public long RomBaseAddress { get; private set; } = 0;
11 |
12 | public long ExternalRamBaseAddress { get; private set; } = 0;
13 |
14 | public long InternalRamBaseAddress { get; private set; } = 0;
15 |
16 | // ---------------------------------------------
17 |
18 | public IGameboyAdvanceConnector()
19 | {
20 | Platform = "GBA";
21 | SupportedEmulators.Add("BizHawk 2.9.1 (mGBA core)");
22 | }
23 |
24 | protected override bool Connect()
25 | {
26 | _ram = new MegadriveRamAdapter(new BinaryTarget
27 | {
28 | DisplayName = "2.9.1",
29 | ProcessName = "EmuHawk",
30 | ModuleName = "mgba.dll",
31 | Hash = "6CE622D4ED4E8460CE362CF35EF67DC70096FEC2C9A174CBEF6A3E5B04F18BCC"
32 | });
33 |
34 | if (!_ram.TryConnect())
35 | return false;
36 |
37 | ExternalRamBaseAddress = _ram.ResolvePointerPath64(_ram.BaseAddress + 0x00103448, new int[] { 0x10, 0x28, 0x0 });
38 | InternalRamBaseAddress = _ram.ResolvePointerPath64(_ram.BaseAddress + 0x00103448, new int[] { 0x10, 0x30, 0x0 });
39 | RomBaseAddress = _ram.ResolvePointerPath64(_ram.BaseAddress + 0x00103448, new int[] { 0x10, 0x38, 0x0 });
40 |
41 | return true;
42 | }
43 |
44 | public override void Disconnect()
45 | {
46 | base.Disconnect();
47 | _ram = null;
48 |
49 | ExternalRamBaseAddress = 0;
50 | InternalRamBaseAddress = 0;
51 | RomBaseAddress = 0;
52 | }
53 |
54 | public override long GetCurrentFrameCount()
55 | {
56 | return BizhawkHelper.GetCurrentFrameCount(_ram);
57 | }
58 |
59 | public override string GetRomIdentity()
60 | {
61 | // Use the 6 characters long game code as identity
62 | byte[] headerBytes = _ram.ReadBytes(RomBaseAddress + 0xAC, 0x06);
63 | return Encoding.Default.GetString(headerBytes);
64 | }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/HintMachine/Models/GenericConnectors/IGameboyColorConnector.cs:
--------------------------------------------------------------------------------
1 | using System.Text;
2 | using HintMachine.Helpers;
3 |
4 | namespace HintMachine.Models.GenericConnectors
5 | {
6 | public abstract class IGameboyColorConnector : IEmulatorConnector
7 | {
8 | protected ProcessRamWatcher _ram = null;
9 |
10 | public long RomBaseAddress { get; private set; } = 0;
11 |
12 | public long RamBaseAddress { get; private set; } = 0;
13 |
14 | // ---------------------------------------------
15 |
16 | public IGameboyColorConnector()
17 | {
18 | Platform = "GBC";
19 | SupportedEmulators.Add("BizHawk 2.9.1 (Gambatte core)");
20 | }
21 |
22 | protected override bool Connect()
23 | {
24 | _ram = new MegadriveRamAdapter(new BinaryTarget
25 | {
26 | DisplayName = "2.9.1",
27 | ProcessName = "EmuHawk",
28 | //ModuleName = "libgambatte.DLL",
29 | Hash = "6CE622D4ED4E8460CE362CF35EF67DC70096FEC2C9A174CBEF6A3E5B04F18BCC"
30 | });
31 |
32 | if (!_ram.TryConnect())
33 | return false;
34 |
35 | //RamBaseAddress = _ram.ResolvePointerPath64(_ram.BaseAddress + 0x7E050, new int[] { 0x278, 0 });
36 | RamBaseAddress = _ram.ResolvePointerPath64(_ram.Threadstack0 - 0xF48, new int[] { 0x8, 0x1F0, 0x18, 0x90, 0 });
37 |
38 | //RomBaseAddress = _ram.ResolvePointerPath64(_ram.BaseAddress + 0x7E050, new int[] { 0x10 });
39 | RomBaseAddress = _ram.ResolvePointerPath64(_ram.Threadstack0 - 0xF48, new int[] { 0x8, 0x1F0, 0x120, 0, 0 });
40 |
41 | return true;
42 | }
43 |
44 | public override void Disconnect()
45 | {
46 | base.Disconnect();
47 | _ram = null;
48 |
49 | RamBaseAddress = 0;
50 | RomBaseAddress = 0;
51 | }
52 |
53 | public override long GetCurrentFrameCount()
54 | {
55 | return BizhawkHelper.GetCurrentFrameCount(_ram);
56 | }
57 |
58 | public override string GetRomIdentity()
59 | {
60 | // Use the game code as identity
61 | byte[] codeBytes = _ram.ReadBytes(RomBaseAddress + 0x13F, 4);
62 | byte[] versionBytes = _ram.ReadBytes(RomBaseAddress + 0x144, 2);
63 | return Encoding.Default.GetString(codeBytes) + Encoding.Default.GetString(versionBytes);
64 | }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/HintMachine/Models/GenericConnectors/IMegadriveConnector.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using System.Security.Cryptography;
4 | using HintMachine.Helpers;
5 |
6 | namespace HintMachine.Models.GenericConnectors
7 | {
8 | public abstract class IMegadriveConnector : IEmulatorConnector
9 | {
10 | protected MegadriveRamAdapter _ram = null;
11 |
12 | public long RomBaseAddress { get; private set; } = 0;
13 |
14 | public long RamBaseAddress { get; private set; } = 0;
15 |
16 | // -------------------------------------------------
17 |
18 | public IMegadriveConnector()
19 | {
20 | Platform = "Megadrive";
21 | SupportedEmulators.Add("BizHawk 2.9.1 (Genesis Plus GX core)");
22 | }
23 |
24 | protected override bool Connect()
25 | {
26 | _ram = new MegadriveRamAdapter(new BinaryTarget
27 | {
28 | DisplayName = "2.9.1",
29 | ProcessName = "EmuHawk",
30 | Hash = "6CE622D4ED4E8460CE362CF35EF67DC70096FEC2C9A174CBEF6A3E5B04F18BCC"
31 | });
32 |
33 | if (!_ram.TryConnect())
34 | return false;
35 |
36 | // Find ROM base address
37 | var regions = _ram.ListMemoryRegions().Where(r => r.Size == 0x3158000 && r.Type == MemoryRegionType.MEM_MAPPED).ToList();
38 | if (regions.Count == 0)
39 | {
40 | Logger.Debug("IMegadriveConnector: Could not find ROM start address");
41 | return false;
42 | }
43 | RomBaseAddress = regions[0].BaseAddress + 0xE58000;
44 |
45 | // Find RAM base address
46 | regions = _ram.ListMemoryRegions().Where(r => r.Size == 0x2C000 && r.Type == MemoryRegionType.MEM_MAPPED).ToList();
47 | if (regions.Count == 0)
48 | {
49 | Logger.Debug("IMegadriveConnector: Could not find RAM start address");
50 | return false;
51 | }
52 | RamBaseAddress = regions[0].BaseAddress + 0x5D90;
53 | Logger.Debug($"RamBaseAddress = {RamBaseAddress:X}");
54 |
55 | return true;
56 | }
57 |
58 | public override void Disconnect()
59 | {
60 | base.Disconnect();
61 | _ram = null;
62 | RomBaseAddress = 0;
63 | RamBaseAddress = 0;
64 | }
65 |
66 | public override long GetCurrentFrameCount()
67 | {
68 | return BizhawkHelper.GetCurrentFrameCount(_ram);
69 | }
70 |
71 | public override string GetRomIdentity()
72 | {
73 | // Hash the relevant part of the ROM header
74 | byte[] bytes = _ram.ReadBytes(RomBaseAddress + 0x100, 0xA0);
75 | using (var sha = SHA256.Create("System.Security.Cryptography.SHA256Cng"))
76 | return BitConverter.ToString(sha.ComputeHash(bytes)).Replace("-", "");
77 | }
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/HintMachine/Models/GenericConnectors/INESConnector.cs:
--------------------------------------------------------------------------------
1 | using HintMachine.Helpers;
2 |
3 | namespace HintMachine.Models.GenericConnectors
4 | {
5 | public abstract class INESConnector : IEmulatorConnector
6 | {
7 | protected ProcessRamWatcher _ram = null;
8 |
9 | public long RomBaseAddress { get; private set; } = 0;
10 |
11 | public long RamBaseAddress { get; private set; } = 0;
12 |
13 | // ---------------------------------------------
14 |
15 | public INESConnector()
16 | {
17 | Platform = "NES";
18 | SupportedEmulators.Add("BizHawk 2.9.1 (NesHawk core)");
19 | }
20 |
21 | protected override bool Connect()
22 | {
23 | _ram = new ProcessRamWatcher(new BinaryTarget {
24 | DisplayName = "2.9.1",
25 | ProcessName = "EmuHawk",
26 | Hash = "6CE622D4ED4E8460CE362CF35EF67DC70096FEC2C9A174CBEF6A3E5B04F18BCC"
27 | });
28 |
29 | if (!_ram.TryConnect())
30 | return false;
31 |
32 | RamBaseAddress = _ram.ResolvePointerPath64(_ram.Threadstack0 - 0xF48, new int[] { 0x8, 0x1F0, 0x20, 0x10 });
33 | // RomBaseAddress = _ram.ResolvePointerPath64(_ram.Threadstack0 - 0xF48, new int[] { 0x8, 0x240, 0x68, 0x10, 0x250, 0x10 });
34 | return true;
35 | }
36 |
37 | public override void Disconnect()
38 | {
39 | base.Disconnect();
40 | _ram = null;
41 |
42 | RamBaseAddress = 0;
43 | RomBaseAddress = 0;
44 | }
45 |
46 | public override long GetCurrentFrameCount()
47 | {
48 | return BizhawkHelper.GetCurrentFrameCount(_ram);
49 | }
50 |
51 | public override string GetRomIdentity()
52 | {
53 | return "";
54 | // Hash the 0x800 first ROM bytes as identity
55 | // byte[] romStart = _ram.ReadBytes(RomBaseAddress, 0x800);
56 | // using (var sha = SHA256.Create("System.Security.Cryptography.SHA256Cng"))
57 | // return BitConverter.ToString(sha.ComputeHash(romStart)).Replace("-", "");
58 | }
59 | }
60 | }
--------------------------------------------------------------------------------
/HintMachine/Models/GenericConnectors/INintendoDSConnector.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Security.Cryptography;
3 | using HintMachine.Helpers;
4 |
5 | namespace HintMachine.Models.GenericConnectors
6 | {
7 | public abstract class INintendoDSConnector : IEmulatorConnector
8 | {
9 | protected ProcessRamWatcher _ram = null;
10 |
11 | public long RomBaseAddress { get; private set; } = 0;
12 |
13 | public long RamBaseAddress { get; private set; } = 0;
14 |
15 | // ---------------------------------------------
16 |
17 | public INintendoDSConnector()
18 | {
19 | Platform = "DS";
20 | SupportedEmulators.Add("BizHawk 2.9.1 (MelonDS core)");
21 | }
22 |
23 | protected override bool Connect()
24 | {
25 | _ram = new MegadriveRamAdapter(new BinaryTarget
26 | {
27 | DisplayName = "2.9.1",
28 | ProcessName = "EmuHawk",
29 | Hash = "6CE622D4ED4E8460CE362CF35EF67DC70096FEC2C9A174CBEF6A3E5B04F18BCC"
30 | });
31 |
32 | if (!_ram.TryConnect())
33 | return false;
34 |
35 | RomBaseAddress = 0x36F01D51E20; // or 0x36F00B36680
36 | RamBaseAddress = 0x36F01952020;
37 |
38 | return true;
39 | }
40 |
41 | public override void Disconnect()
42 | {
43 | base.Disconnect();
44 | _ram = null;
45 | RomBaseAddress = 0;
46 | RamBaseAddress = 0;
47 | }
48 |
49 | public override long GetCurrentFrameCount()
50 | {
51 | return BizhawkHelper.GetCurrentFrameCount(_ram);
52 | }
53 |
54 | public override string GetRomIdentity()
55 | {
56 | // Hash the relevant part of the ROM header
57 | byte[] bytes = _ram.ReadBytes(RomBaseAddress, 0xB0);
58 | using (var sha = SHA256.Create("System.Security.Cryptography.SHA256Cng"))
59 | return BitConverter.ToString(sha.ComputeHash(bytes)).Replace("-", "");
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/HintMachine/Models/GenericConnectors/IPlayStationConnector.cs:
--------------------------------------------------------------------------------
1 | using System.Text;
2 | using HintMachine.Helpers;
3 |
4 | namespace HintMachine.Models.GenericConnectors
5 | {
6 | public abstract class IPlayStationConnector : IEmulatorConnector
7 | {
8 | protected ProcessRamWatcher _ram = null;
9 |
10 | public long RamBaseAddress { get; private set; } = 0;
11 |
12 | // ---------------------------------------------
13 |
14 | public IPlayStationConnector()
15 | {
16 | Platform = "PS1";
17 | SupportedEmulators.Add("BizHawk 2.9.1 (Nymashock core)");
18 | }
19 |
20 | protected override bool Connect()
21 | {
22 | _ram = new ProcessRamWatcher(new BinaryTarget
23 | {
24 | DisplayName = "2.9.1",
25 | ProcessName = "EmuHawk",
26 | Hash = "6CE622D4ED4E8460CE362CF35EF67DC70096FEC2C9A174CBEF6A3E5B04F18BCC"
27 | });
28 |
29 | if (!_ram.TryConnect())
30 | return false;
31 |
32 | RamBaseAddress = 0x36F002D12A8;
33 |
34 | return true;
35 | }
36 |
37 | public override void Disconnect()
38 | {
39 | base.Disconnect();
40 | _ram = null;
41 | RamBaseAddress = 0;
42 | }
43 |
44 | public override long GetCurrentFrameCount()
45 | {
46 | return BizhawkHelper.GetCurrentFrameCount(_ram);
47 | }
48 |
49 | public override string GetRomIdentity()
50 | {
51 | // Get the game code from RAM
52 | return Encoding.Default.GetString(_ram.ReadBytes(RamBaseAddress + 0xB8B7, 11));
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/HintMachine/Models/Globals.cs:
--------------------------------------------------------------------------------
1 | using HintMachine.Models.Games;
2 | using HintMachine.Models.GenericConnectors;
3 | using System.Collections.Generic;
4 | using System.IO;
5 | using System.Reflection;
6 |
7 | namespace HintMachine.Models
8 | {
9 | internal static class Globals
10 | {
11 | public const string ProgramName = "HintMachine";
12 | public const string ProgramVersion = "1.2.0";
13 |
14 | ///
15 | /// The duration of a "tick" where the current game connector (if any) watches the game once, in milliseconds
16 | ///
17 | public const int TickInterval = 100;
18 |
19 | ///
20 | /// The minimal duration between two hint queries obtained from quests
21 | ///
22 | public const int HintQueueInterval = 1000;
23 |
24 | ///
25 | /// The maximal amount of pending hints obtained from quest that can be stored at any given time.
26 | /// If more hints are obtained, they will be discarded (but it will most likely be because of a bug / exploit anyway)
27 | ///
28 | public const int PendingHintsQueueMaxSize = 5;
29 |
30 | public static readonly string NotificationSoundPath =
31 | $@"{Path.GetDirectoryName(Assembly.GetEntryAssembly().Location)}\Assets\Notification.wav";
32 |
33 | public static readonly List Games = new List()
34 | {
35 | new XenotiltConnector(),
36 | new OneFingerDeathPunchConnector(),
37 | new PuyoTetrisConnector(),
38 | new TetrisEffectConnector(),
39 | new ZachtronicsSolitaireConnector(),
40 | new GeometryWarsConnector(),
41 | new GeometryWarsGalaxiesConnector(),
42 | //new NuclearThroneConnector(), //Instabilities, must be investigated
43 | new SonicBlueSpheresConnector(),
44 | new StargunnerConnector(),
45 | new BustAMove4Connector(),
46 | new Rollcage2Connector(),
47 | new FZeroGXConnector(),
48 | new IslandersConnector(),
49 | new DorfromantikConnector(),
50 | new MeteosConnector(),
51 | new PacManChampionshipEditionDXConnector(),
52 | new MetroidPrimePinballConnector(),
53 | new ColumnsConnector(),
54 | new BPMConnector(),
55 | new MiniMetroConnector(),
56 | new LuckBeALandlordConnector(),
57 | new SuperHexagonConnector(),
58 | new SuperMonkeyBall2Connector(),
59 | new Operator911Connector(),
60 | new DragonCrownConnector(),
61 | new SuperMegaBaseball2Connector(),
62 | new TonyHawksProSkater12Connector(),
63 | new PokemonPinballRSConnector(),
64 | new TMNTShreddersRevengeConnector(),
65 | new SuperMonkeyBallConnector(),
66 | new NexMachinaConnector(),
67 | new PokemonPuzzleChallengeConnector(),
68 | new PapersPleaseConnector(),
69 | new KatamariDamacyRerollConnector(),
70 | new MinesweeperClassyConnector(),
71 | new TetrisNESConnector(),
72 | new PuyoPuyo2MDConnector(),
73 | new PaintTheTownRedConnector(),
74 | new PeggleDeluxeConnector(),
75 | new PeggleNightsConnector(),
76 | new AdvanceWarsDualStrikeConnector(),
77 | };
78 |
79 | public static IGameConnector FindGameFromName(string name)
80 | {
81 | foreach (IGameConnector game in Games)
82 | if (game.Name == name)
83 | return game;
84 |
85 | return null;
86 | }
87 |
88 | // Easter egg commands
89 | public static readonly List HitMachineFacts = new List() {
90 | "Le saviez vous ? Avant d'être présenté par Charly et Lulu, le Hit Machine était animé par Ophelie Winter et Yves Noel.",
91 | "Le saviez vous ? Le Hit Machine a été diffusé sur M6 entre 1994 et 2009",
92 | "Le saviez vous ? Le developpement de la HintMachine a demarré en Septembre 2023.",
93 | "✨ Je m'appelle Charly - Et je m'appelle Lulu - On est sur M6 - pour le HitMachine ✨",
94 | "♪ Tous les oiseaux volent dans le ciel ♫"
95 | };
96 | public static readonly List CharlyMachineFacts = new List() {
97 | "Le saviez vous ? Charly se nomme Charly Nestor.",
98 | "Le saviez vous ? Charly est né le 10 avril 1964",
99 | "Le feu ça brule"
100 | };
101 | public static readonly List LuluMachineFacts = new List() {
102 | "Le saviez vous ? Lulu se nomme Jean-Marc Lubin",
103 | "Le saviez vous ? Lulu a commencé sa carriere télé en 1990",
104 | "L'eau ça mouille"
105 | };
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/HintMachine/Models/HintDetails.cs:
--------------------------------------------------------------------------------
1 | namespace HintMachine.Models
2 | {
3 | public class HintDetails : Archipelago.MultiClient.Net.Models.Hint
4 | {
5 | public string ReceivingPlayerName { get; set; }
6 |
7 | public string FindingPlayerName { get; set; }
8 |
9 | public string ItemName { get; set; }
10 |
11 | public string LocationName { get; set; }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/HintMachine/Models/HintQuest.cs:
--------------------------------------------------------------------------------
1 | using System.Windows.Controls;
2 |
3 | namespace HintMachine.Models
4 | {
5 | public abstract class HintQuest
6 | {
7 | ///
8 | /// The name of the quest, as shown to the user in the quests list
9 | ///
10 | public string Name { get; set; } = string.Empty;
11 |
12 | ///
13 | /// A detailed description of what must be accomplished in the quest in order to get hints.
14 | /// If not blank, a convenient "question mark" widget will be added with this string as a tooltip.
15 | ///
16 | public string Description { get; set; } = string.Empty;
17 |
18 | ///
19 | /// The number of hints awarded on quest completion
20 | ///
21 | public int AwardedHints { get; set; } = 1;
22 |
23 | // ----------------------------------------------------------------------------------
24 |
25 | public HintQuest()
26 | {}
27 |
28 | ///
29 | /// A method which checks if quest has been completed, decreases the CurrentValue until the quest reaches a
30 | /// "non-completed" state and returns the number of awarded hints.
31 | ///
32 | /// the number of obtained hints
33 | public abstract int CheckAndCommitCompletion();
34 |
35 | public abstract void InitComponents(Grid questsGrid);
36 |
37 | public abstract void UpdateComponents();
38 | }
39 | }
40 |
41 |
--------------------------------------------------------------------------------
/HintMachine/Models/HintQuestCumulative.cs:
--------------------------------------------------------------------------------
1 | namespace HintMachine.Models
2 | {
3 | public class HintQuestCumulative : HintQuestCounter
4 | {
5 | public enum CumulativeDirection
6 | {
7 | ASCENDING,
8 | DESCENDING
9 | }
10 |
11 | ///
12 | /// The direction of the tracking.
13 | /// If ASCENDING, value increments will be tracked and added to the quest progress.
14 | /// If DESCENDING, value decrements will be tracked instead.
15 | ///
16 | public CumulativeDirection Direction { get; set; } = CumulativeDirection.ASCENDING;
17 |
18 | private long? _lastMemoryReading = null;
19 |
20 | // ----------------------------------------------------------------------------------
21 |
22 | public HintQuestCumulative()
23 | {}
24 |
25 | public void UpdateValue(long memoryReading)
26 | {
27 | if (_lastMemoryReading != null)
28 | {
29 | if (Direction == CumulativeDirection.ASCENDING && memoryReading > _lastMemoryReading)
30 | {
31 | CurrentValue += (memoryReading - (long)_lastMemoryReading);
32 | }
33 | else if (Direction == CumulativeDirection.DESCENDING && memoryReading < _lastMemoryReading)
34 | {
35 | CurrentValue += ((long)_lastMemoryReading - memoryReading);
36 | }
37 | }
38 |
39 | _lastMemoryReading = memoryReading;
40 | }
41 |
42 | ///
43 | /// Ignores the next value that will be read, meaning it will update the internal variables of the quest
44 | /// without giving it the chance to increase the counter. This is especially useful when a break in the
45 | /// game flow is detected (loaded a save file, loaded a save state), putting back the quest in a stable
46 | /// state and removing potential abuses.
47 | ///
48 | public void IgnoreNextValue()
49 | {
50 | _lastMemoryReading = null;
51 | }
52 | }
53 | }
54 |
55 |
--------------------------------------------------------------------------------
/HintMachine/Models/Logger.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace HintMachine.Models
4 | {
5 | public enum LogMessageType
6 | {
7 | RAW = 0,
8 | INFO = 1,
9 | WARNING = 2,
10 | ERROR = 3,
11 | HINT = 4,
12 | CHAT = 5,
13 | ITEM_RECEIVED = 6,
14 | ITEM_SENT = 7,
15 | GOAL = 8,
16 | SERVER_RESPONSE = 9,
17 | JOIN_LEAVE = 10,
18 | }
19 |
20 | public static class Logger
21 | {
22 | public delegate void OnMessageLoggedCallback(string message, LogMessageType type);
23 |
24 | public static event OnMessageLoggedCallback OnMessageLogged;
25 |
26 | public static void Log(string message, LogMessageType logMessageType = LogMessageType.RAW)
27 | {
28 | Console.WriteLine(message);
29 | OnMessageLogged?.Invoke(message, logMessageType);
30 | }
31 |
32 | public static void Debug(string message)
33 | {
34 | // Only has an effect in debug builds
35 | #if DEBUG
36 | Log(message, LogMessageType.RAW);
37 | #endif
38 | }
39 |
40 | public static void Info(string message)
41 | {
42 | Log(message, LogMessageType.INFO);
43 | }
44 |
45 | public static void Warn(string message)
46 | {
47 | Log(message, LogMessageType.WARNING);
48 | }
49 |
50 | public static void Error(string message)
51 | {
52 | Log(message, LogMessageType.ERROR);
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/HintMachine/Models/Settings.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.IO;
3 | using System.Linq;
4 |
5 | namespace HintMachine.Models
6 | {
7 | public static class Settings
8 | {
9 | public static string Host = "archipelago.gg:12345";
10 | public static string Slot = "";
11 | public static string LastConnectedGame = "";
12 | public static bool DisplayChatMessages = true;
13 | public static bool DisplayFoundHintMessages = true;
14 | public static bool DisplayJoinLeaveMessages = false;
15 | public static bool DisplayItemReceivedMessages = true;
16 | public static bool DisplayItemSentMessages = false;
17 | public static bool PlaySoundOnHint = true;
18 | public static bool ShowUpdatePopUp = true;
19 |
20 | // ----------------------------------------------------------------------------------
21 |
22 | ///
23 | /// Save all user settings to a dedicated file alongside the application (named "settings.cfg")
24 | ///
25 | public static void SaveToFile()
26 | {
27 | Dictionary dict = new Dictionary() {
28 | { "host", Host },
29 | { "slot", Slot },
30 | { "game", LastConnectedGame },
31 | { "displayChatMessages", DisplayChatMessages.ToString() },
32 | { "displayFoundHintMessages", DisplayFoundHintMessages.ToString() },
33 | { "displayJoinLeaveMessages", DisplayJoinLeaveMessages.ToString() },
34 | { "displayItemReceivedMessages", DisplayItemReceivedMessages.ToString() },
35 | { "displayItemSentMessages", DisplayItemSentMessages.ToString() },
36 | { "playSoundOnHint", PlaySoundOnHint.ToString() },
37 | { "showUpdatePopUp", ShowUpdatePopUp.ToString() },
38 | };
39 | File.WriteAllLines("settings.cfg", dict.Select(x => x.Key + "=" + x.Value).ToArray());
40 | }
41 |
42 | ///
43 | /// Load all user settings from the dedicated file alongside the application (named "settings.cfg")
44 | ///
45 | public static void LoadFromFile()
46 | {
47 | try
48 | {
49 | string[] lines = File.ReadAllLines("settings.cfg");
50 | foreach (var line in lines)
51 | {
52 | int idx = line.IndexOf('=');
53 | if (idx == -1)
54 | continue;
55 |
56 | string value = line.Substring(idx + 1);
57 | if (line.StartsWith("host"))
58 | Host = value;
59 | else if (line.StartsWith("slot"))
60 | Slot = value;
61 | else if (line.StartsWith("game"))
62 | LastConnectedGame = value;
63 | else if (line.StartsWith("displayChatMessages"))
64 | DisplayChatMessages = bool.Parse(value);
65 | else if (line.StartsWith("displayFoundHintMessages"))
66 | DisplayFoundHintMessages = bool.Parse(value);
67 | else if (line.StartsWith("displayJoinLeaveMessages"))
68 | DisplayJoinLeaveMessages = bool.Parse(value);
69 | else if (line.StartsWith("displayItemReceivedMessages"))
70 | DisplayItemReceivedMessages = bool.Parse(value);
71 | else if (line.StartsWith("displayItemSentMessages"))
72 | DisplayItemSentMessages = bool.Parse(value);
73 | else if (line.StartsWith("playSoundOnHint"))
74 | PlaySoundOnHint = bool.Parse(value);
75 | else if (line.StartsWith("showUpdatePopUp"))
76 | ShowUpdatePopUp = bool.Parse(value);
77 | }
78 | }
79 | catch { }
80 | }
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/HintMachine/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Resources;
3 | using System.Runtime.CompilerServices;
4 | using System.Runtime.InteropServices;
5 | using System.Windows;
6 |
7 | // General Information about an assembly is controlled through the following
8 | // set of attributes. Change these attribute values to modify the information
9 | // associated with an assembly.
10 | [assembly: AssemblyTitle("HintMachine")]
11 | [assembly: AssemblyDescription("")]
12 | [assembly: AssemblyConfiguration("")]
13 | [assembly: AssemblyCompany("")]
14 | [assembly: AssemblyProduct("HintMachine")]
15 | [assembly: AssemblyCopyright("Copyright © 2023")]
16 | [assembly: AssemblyTrademark("")]
17 | [assembly: AssemblyCulture("")]
18 |
19 | // Setting ComVisible to false makes the types in this assembly not visible
20 | // to COM components. If you need to access a type in this assembly from
21 | // COM, set the ComVisible attribute to true on that type.
22 | [assembly: ComVisible(false)]
23 |
24 | //In order to begin building localizable applications, set
25 | //CultureYouAreCodingWith in your .csproj file
26 | //inside a . For example, if you are using US english
27 | //in your source files, set the to en-US. Then uncomment
28 | //the NeutralResourceLanguage attribute below. Update the "en-US" in
29 | //the line below to match the UICulture setting in the project file.
30 |
31 | //[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
32 |
33 |
34 | [assembly: ThemeInfo(
35 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
36 | //(used if a resource is not found in the page,
37 | // or application resource dictionaries)
38 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
39 | //(used if a resource is not found in the page,
40 | // app, or any theme specific resource dictionaries)
41 | )]
42 |
43 |
44 | // Version information for an assembly consists of the following four values:
45 | //
46 | // Major Version
47 | // Minor Version
48 | // Build Number
49 | // Revision
50 | //
51 | // You can specify all the values or you can default the Build and Revision Numbers
52 | // by using the '*' as shown below:
53 | // [assembly: AssemblyVersion("1.0.*")]
54 | [assembly: AssemblyVersion("1.0.0.0")]
55 | [assembly: AssemblyFileVersion("1.0.0.0")]
56 |
--------------------------------------------------------------------------------
/HintMachine/Properties/Resources.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace HintMachine.Properties {
12 | using System;
13 |
14 |
15 | ///
16 | /// A strongly-typed resource class, for looking up localized strings, etc.
17 | ///
18 | // This class was auto-generated by the StronglyTypedResourceBuilder
19 | // class via a tool like ResGen or Visual Studio.
20 | // To add or remove a member, edit your .ResX file then rerun ResGen
21 | // with the /str option, or rebuild your VS project.
22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
25 | internal class Resources {
26 |
27 | private static global::System.Resources.ResourceManager resourceMan;
28 |
29 | private static global::System.Globalization.CultureInfo resourceCulture;
30 |
31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
32 | internal Resources() {
33 | }
34 |
35 | ///
36 | /// Returns the cached ResourceManager instance used by this class.
37 | ///
38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
39 | internal static global::System.Resources.ResourceManager ResourceManager {
40 | get {
41 | if (object.ReferenceEquals(resourceMan, null)) {
42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("HintMachine.Properties.Resources", typeof(Resources).Assembly);
43 | resourceMan = temp;
44 | }
45 | return resourceMan;
46 | }
47 | }
48 |
49 | ///
50 | /// Overrides the current thread's CurrentUICulture property for all
51 | /// resource lookups using this strongly typed resource class.
52 | ///
53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
54 | internal static global::System.Globalization.CultureInfo Culture {
55 | get {
56 | return resourceCulture;
57 | }
58 | set {
59 | resourceCulture = value;
60 | }
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/HintMachine/Properties/Settings.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // Ce code a été généré par un outil.
4 | // Version du runtime :4.0.30319.42000
5 | //
6 | // Les modifications apportées à ce fichier peuvent provoquer un comportement incorrect et seront perdues si
7 | // le code est régénéré.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace HintMachine.Properties {
12 |
13 |
14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.7.0.0")]
16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
17 |
18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
19 |
20 | public static Settings Default {
21 | get {
22 | return defaultInstance;
23 | }
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/HintMachine/Properties/Settings.settings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/HintMachine/Views/GameSelectionWindow.xaml:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
11 |
12 |
13 |
16 |
17 |
18 |
19 |
20 |
23 |
24 |
25 |
26 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 | Game name
41 |
42 |
43 | Game description
44 |
45 |
46 | Game properties
47 |
48 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/HintMachine/Views/HintsView.xaml:
--------------------------------------------------------------------------------
1 |
8 |
9 |
11 |
12 |
13 |
14 |
15 |
16 | Receiving player
17 |
18 |
19 |
20 |
21 | Item
22 |
23 |
24 |
25 |
26 | Finding player
27 |
28 |
29 |
30 |
31 | Location
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/HintMachine/Views/HintsView.xaml.cs:
--------------------------------------------------------------------------------
1 | using Archipelago.MultiClient.Net.Enums;
2 | using HintMachine.Models;
3 | using System.Collections.Generic;
4 | using System.ComponentModel;
5 | using System.Windows;
6 | using System.Windows.Controls;
7 |
8 | namespace HintMachine.Views
9 | {
10 | public partial class HintsView : UserControl
11 | {
12 | private List _fullHintsList = null;
13 |
14 | // ----------------------------------------------------------------------------------
15 |
16 | public HintsView()
17 | {
18 | InitializeComponent();
19 | }
20 |
21 | public void UpdateItems(List knownHints)
22 | {
23 | _fullHintsList = knownHints;
24 |
25 | List filteredHints = new List();
26 |
27 | foreach (HintDetails hint in knownHints)
28 | {
29 | // Filter out already found items
30 | if (hint.Found)
31 | continue;
32 | // Filter out non-progression items if related checkbox is checked
33 | if ((CheckboxProgression.IsChecked ?? true) && (!hint.ItemFlags.HasFlag(ItemFlags.Advancement)))
34 | continue;
35 |
36 | filteredHints.Add(hint);
37 | }
38 |
39 | ListViewHints.ItemsSource = filteredHints;
40 |
41 | // Adjust all columns' size to fit their contents, as if the column header was double-clicked
42 | foreach (GridViewColumn c in grid.Columns)
43 | {
44 | // Code below was found in GridViewColumnHeader.OnGripperDoubleClicked() event handler (using Reflector)
45 | if (double.IsNaN(c.Width))
46 | {
47 | c.Width = c.ActualWidth;
48 | }
49 | c.Width = double.NaN;
50 | }
51 | }
52 |
53 | private void OnHintsListColumnClick(object sender, RoutedEventArgs e)
54 | {
55 | GridViewColumnHeader column = (sender as GridViewColumnHeader);
56 | string sortBy = column.Tag.ToString();
57 |
58 | ListSortDirection direction = ListSortDirection.Ascending;
59 | foreach (SortDescription desc in ListViewHints.Items.SortDescriptions)
60 | {
61 | if (desc.PropertyName == sortBy)
62 | {
63 | direction = (desc.Direction == ListSortDirection.Ascending)
64 | ? ListSortDirection.Descending
65 | : ListSortDirection.Ascending;
66 | break;
67 | }
68 | }
69 |
70 | ListViewHints.Items.SortDescriptions.Clear();
71 | ListViewHints.Items.SortDescriptions.Add(new SortDescription(sortBy, direction));
72 | }
73 |
74 | private void OnCheckboxProgressionChecked(object sender, RoutedEventArgs e)
75 | {
76 | if (ListViewHints != null && _fullHintsList != null)
77 | UpdateItems(_fullHintsList);
78 | }
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/HintMachine/Views/LoginWindow.xaml:
--------------------------------------------------------------------------------
1 |
8 |
9 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/HintMachine/Views/LoginWindow.xaml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Net.Http;
3 | using System.Net.Http.Headers;
4 | using System.Threading.Tasks;
5 | using System.Windows;
6 | using HintMachine.Models;
7 | using Newtonsoft.Json.Linq;
8 |
9 | namespace HintMachine.Views
10 | {
11 | ///
12 | /// Interaction logic for LoginWindow.xaml
13 | ///
14 | public partial class LoginWindow : Window
15 | {
16 | public LoginWindow()
17 | {
18 | InitializeComponent();
19 |
20 | Settings.LoadFromFile();
21 | InputHost.Text = Settings.Host;
22 | InputSlot.Text = Settings.Slot;
23 |
24 | if (Settings.ShowUpdatePopUp) {
25 | _ = CheckIfUpdateAvailableAsync();
26 | }
27 | }
28 |
29 | private void OnConnectButtonClick(object sender, RoutedEventArgs e)
30 | {
31 | // Try connecting to Archipelago
32 | string host = InputHost.Text;
33 | string slot = InputSlot.Text;
34 | string password = InputPassword.Text;
35 |
36 | ArchipelagoHintSession archipelagoSession = new ArchipelagoHintSession(host, slot, password);
37 | if (archipelagoSession.IsConnected)
38 | {
39 | // If connectionn succeeded, store the fields contents for next execution and move on to MainWindow
40 | Settings.Host = host;
41 | Settings.Slot = slot;
42 | Settings.SaveToFile();
43 |
44 | new MainWindow(archipelagoSession).Show();
45 | Close();
46 | }
47 | else
48 | {
49 | MessageBox.Show($"Could not connect to Archipelago: {archipelagoSession.ErrorMessage}",
50 | "Connection error", MessageBoxButton.OK, MessageBoxImage.Error);
51 | }
52 | }
53 |
54 | private async Task CheckIfUpdateAvailableAsync()
55 | {
56 | HttpClient client = new HttpClient();
57 | client.DefaultRequestHeaders.Accept.Clear();
58 | client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/vnd.github.v3+json"));
59 | client.DefaultRequestHeaders.Add("User-Agent", ".NET Foundation Repository Reporter");
60 | try
61 | {
62 | var response = await client.GetStringAsync("https://api.github.com/repos/CalDrac/hintMachine/releases");
63 | var responseJson = JArray.Parse(response);
64 |
65 | string lastVersion = "";
66 | foreach (var release in responseJson)
67 | {
68 | if (release["prerelease"].ToString() == "False")
69 | {
70 | // The first non-prerelease is the latest release
71 | lastVersion = release["tag_name"].ToString();
72 | break;
73 | }
74 | }
75 | Version currentVersion = new Version(Globals.ProgramVersion);
76 | Version latestVersion = new Version(lastVersion);
77 | Console.WriteLine($"Latest version is {latestVersion}");
78 |
79 | if (currentVersion.CompareTo(latestVersion) < 0)
80 | {
81 | new UpdateAvailablePopup().Show();
82 | }
83 | }
84 | catch
85 | {
86 | Console.WriteLine("Couldn't fetch latest version from GitHub API");
87 | }
88 | }
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/HintMachine/Views/ManualHintWindow.xaml:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
21 |
23 |
24 |
26 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/HintMachine/Views/ManualHintWindow.xaml.cs:
--------------------------------------------------------------------------------
1 | using HintMachine.Models;
2 | using System;
3 | using System.Windows;
4 | using System.Windows.Controls;
5 |
6 | namespace HintMachine.Views
7 | {
8 | ///
9 | /// Logique d'interaction pour ManualHintWindow.xaml
10 | ///
11 | public partial class ManualHintWindow : Window
12 | {
13 | public Action HintLocationCallback { get; set; }
14 | public Action HintItemCallback { get; set; }
15 |
16 | // ----------------------------------------------------------------------------------
17 |
18 | public ManualHintWindow(ArchipelagoHintSession archipelago)
19 | {
20 | InitializeComponent();
21 |
22 | foreach (string itemName in archipelago.GetItemNames())
23 | ComboboxHintedItem.Items.Add(itemName);
24 | foreach (string locName in archipelago.GetMissingLocationNames())
25 | ComboboxHintedLocation.Items.Add(locName);
26 |
27 | RadioHintItem.IsChecked = true;
28 |
29 | if (ComboboxHintedLocation.Items.Count > 0)
30 | ComboboxHintedLocation.SelectedItem = ComboboxHintedLocation.Items[0];
31 | else
32 | {
33 | RadioHintLocation.IsEnabled = false;
34 | ComboboxHintedLocation.IsEnabled = false;
35 | RadioHintItem.IsChecked = true;
36 | }
37 |
38 | if (ComboboxHintedItem.Items.Count > 0)
39 | ComboboxHintedItem.SelectedItem = ComboboxHintedItem.Items[0];
40 | else
41 | {
42 | RadioHintItem.IsEnabled = false;
43 | ComboboxHintedItem.IsEnabled = false;
44 | RadioHintLocation.IsChecked = true;
45 | }
46 |
47 | if (!RadioHintItem.IsEnabled && !RadioHintLocation.IsEnabled)
48 | Close();
49 | }
50 |
51 | private void OnValidateButtonClick(object sender, RoutedEventArgs e)
52 | {
53 | if (RadioHintItem.IsChecked == true)
54 | {
55 | HintItemCallback?.Invoke(ComboboxHintedItem.SelectedValue.ToString());
56 | }
57 | else if(RadioHintLocation.IsChecked == true)
58 | {
59 | HintLocationCallback?.Invoke(ComboboxHintedLocation.SelectedValue.ToString());
60 | }
61 | Close();
62 | }
63 |
64 | private void OnCancelButtonClick(object sender, RoutedEventArgs e)
65 | {
66 | Close();
67 | }
68 |
69 | private void OnComboboxChanged(object sender, SelectionChangedEventArgs e)
70 | {
71 | ComboBox comboBox = e.Source as ComboBox;
72 | if (comboBox == ComboboxHintedItem)
73 | RadioHintItem.IsChecked = true;
74 | else
75 | RadioHintLocation.IsChecked = true;
76 | }
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/HintMachine/Views/MessageLog.xaml:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/HintMachine/Views/UpdateAvailablePopUp.xaml:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
11 |
12 |
14 |
16 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/HintMachine/Views/UpdateAvailablePopUp.xaml.cs:
--------------------------------------------------------------------------------
1 | using HintMachine.Models;
2 | using System.Diagnostics;
3 | using System.Windows;
4 |
5 | namespace HintMachine.Views
6 | {
7 | ///
8 | /// Interaction logic for LoginWindow.xaml
9 | ///
10 | public partial class UpdateAvailablePopup : Window
11 | {
12 | public UpdateAvailablePopup()
13 | {
14 | InitializeComponent();
15 |
16 | Settings.LoadFromFile();
17 | }
18 |
19 | private void OnGoToReleasePageButtonClick(object sender, RoutedEventArgs e)
20 | {
21 | //open github page
22 | ProcessStartInfo sInfo = new ProcessStartInfo("https://github.com/CalDrac/hintMachine/releases");
23 | Process.Start(sInfo);
24 | this.Close();
25 | }
26 |
27 | private void OnCloseButtonClick(object sender, RoutedEventArgs e)
28 | {
29 | this.Close();
30 | }
31 |
32 | private void OnCloseAndDontShowAgainButtonClick(object sender, RoutedEventArgs e)
33 | {
34 | Settings.ShowUpdatePopUp = false;
35 | this.Close();
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/HintMachine/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Nicolas BRUNIQUEL
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | # HintMachine
11 |
12 | HintMachine is a "BK Game" client designed to work for the well-known multiworld ecosystem [Archipelago](https://github.com/ArchipelagoMW/Archipelago).
13 |
14 | It connects to a wide variety of games (through RAM peeking, save file watching, etc...) to track progress on specific "quests" which, when completed, award random location hints inside the Archipelago world you are connected to.
15 |
16 | Given its high dependency to system calls to do RAM peeking and stuff, only Windows is supported as of now.
17 |
18 | ## Usage
19 |
20 | ### Release versions
21 |
22 | - Download a release version's .zip file
23 | - Extract it, and launch HintMachine.exe
24 | - Connect to your Archipelago room by providing the hostname, the slot name and a password if there is one
25 | - You can now connect to any game from HintMachine's library and profit
26 |
27 | ### Running from source
28 |
29 | - Download the source
30 | - Open the solution with Visual Studio 2017+
31 | - It should generate & run right away
32 |
33 |
34 | ## Contributing
35 |
36 | You are free to contribute to HintMachine's development.
37 |
38 | You can check [this document](https://github.com/CalDrac/hintMachine/blob/dev/adding_games.md) for more details on how to add new games.
39 |
40 |
41 | ## Currently supported games
42 |
43 | | Game name | Platform |
44 | |----------------------------------|-----------|
45 | | 911 Operator | PC |
46 | | Advance Wars: Dual Strike | DS |
47 | | BPM: Bullets Per Minute | PC |
48 | | Bust-a-Move 4 | PS1 |
49 | | Columns | Megadrive |
50 | | Dorfromantik | PC |
51 | | Dragon's Crown | PS Vita |
52 | | F-Zero GX | GameCube |
53 | | Geometry Wars Galaxies | Wii |
54 | | Geometry Wars : Retro Evolved | PC |
55 | | ISLANDERS | PC |
56 | | Katamari Damacy REROLL | PC |
57 | | Luck be a Landlord | PC |
58 | | Meteos | DS |
59 | | Metroid Prime Pinball | DS |
60 | | Minesweeper Classy | PC |
61 | | Mini Metro | PC |
62 | | Nex Machina | PC |
63 | | One Finger Death Punch | PC |
64 | | PAC-MAN Championship Edition DX+ | PC |
65 | | Paint the Town Red | PC |
66 | | Papers, Please | PC |
67 | | Peggle Deluxe | PC |
68 | | Peggle Nights | PC |
69 | | Pokémon Pinball: Ruby & Sapphire | GBA |
70 | | Pokémon Puzzle Challenge | GBC |
71 | | Puyo Puyo 2 | Megadrive |
72 | | Puyo Puyo Tetris | PC |
73 | | Rollcage Stage 2 | PS1 |
74 | | Sonic 3 Blue Spheres | Megadrive |
75 | | Stargunner | PC |
76 | | Super Hexagon | PC |
77 | | Super Mega Baseball 2 | PC |
78 | | Super Monkey Ball | GameCube |
79 | | Super Monkey Ball 2 | GameCube |
80 | | TMNT: Shredder's Revenge | PC |
81 | | Tetris | NES |
82 | | Tetris Effect Connected | PC |
83 | | Tony Hawk's Pro Skater 1 + 2 | PC |
84 | | Xenotilt | PC |
85 | | Zachtronics Solitaire Collection | PC |
86 |
--------------------------------------------------------------------------------
/ThreadstackFinder/ThreadstackFinder.vcxproj.filters:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx
7 |
8 |
9 | {93995380-89BD-4b04-88EB-625FBE52EBFB}
10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd
11 |
12 |
13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
15 |
16 |
17 |
18 |
19 | Fichiers sources
20 |
21 |
22 |
--------------------------------------------------------------------------------
/ThreadstackFinder/ThreadstackFinder.vcxproj.user:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/ThreadstackFinder/main.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 |
7 | #if _WIN64
8 | typedef uint64_t StackPtrType;
9 | #else
10 | typedef uint32_t StackPtrType;
11 | #endif
12 |
13 | constexpr int PTR_SIZE = sizeof(StackPtrType);
14 | constexpr int STACK_SIZE = 1024 * PTR_SIZE;
15 |
16 | struct CLIENT_ID
17 | {
18 | PVOID UniqueProcess;
19 | PVOID UniqueThread;
20 | };
21 |
22 | struct THREAD_BASIC_INFORMATION
23 | {
24 | NTSTATUS ExitStatus;
25 | PVOID TebBaseAddress;
26 | CLIENT_ID ClientId;
27 | KAFFINITY AffinityMask;
28 | DWORD Priority;
29 | DWORD BasePriority;
30 | };
31 |
32 | enum THREADINFOCLASS
33 | {
34 | ThreadBasicInformation,
35 | };
36 |
37 | uint64_t find_thread_stack_top(HANDLE process_handle, HANDLE thread_handle)
38 | {
39 | bool loadedManually = false;
40 | HMODULE module = GetModuleHandle("ntdll.dll");
41 | if (!module)
42 | {
43 | module = LoadLibrary("ntdll.dll");
44 | if (!module)
45 | return 0;
46 |
47 | loadedManually = true;
48 | }
49 |
50 | uint64_t result = 0;
51 |
52 | NTSTATUS(__stdcall * NtQueryInformationThread)(HANDLE ThreadHandle, THREADINFOCLASS ThreadInformationClass, PVOID ThreadInformation, ULONG ThreadInformationLength, PULONG ReturnLength);
53 | NtQueryInformationThread = reinterpret_cast(GetProcAddress(module, "NtQueryInformationThread"));
54 | if (NtQueryInformationThread)
55 | {
56 | NT_TIB tib = { 0 };
57 | THREAD_BASIC_INFORMATION tbi = { 0 };
58 |
59 | NTSTATUS status = NtQueryInformationThread(thread_handle, ThreadBasicInformation, &tbi, sizeof(tbi), nullptr);
60 | if (status >= 0)
61 | {
62 | ReadProcessMemory(process_handle, tbi.TebBaseAddress, &tib, sizeof(tbi), nullptr);
63 | result = (uint64_t)tib.StackBase;
64 | }
65 | }
66 |
67 | if (loadedManually)
68 | FreeLibrary(module);
69 | return result;
70 | }
71 |
72 | uint64_t find_threadstack_for_thread(HANDLE process_handle, DWORD thread_id)
73 | {
74 | HANDLE thread_handle = OpenThread(THREAD_GET_CONTEXT | THREAD_QUERY_INFORMATION, FALSE, thread_id);
75 | if (!thread_handle)
76 | return 0;
77 |
78 | uint64_t stacktop = find_thread_stack_top(process_handle, thread_handle);
79 | CloseHandle(thread_handle);
80 |
81 | if (!stacktop)
82 | return 0;
83 |
84 | MODULEINFO mi;
85 | HMODULE kernel_module = GetModuleHandle("kernel32.dll");
86 | if (!kernel_module)
87 | return 0;
88 |
89 | GetModuleInformation(process_handle, kernel_module, &mi, sizeof(mi));
90 |
91 | StackPtrType buf[1024] {};
92 | if (ReadProcessMemory(process_handle, (LPCVOID)(stacktop - STACK_SIZE), buf, STACK_SIZE, NULL))
93 | {
94 | for (int i = STACK_SIZE / PTR_SIZE - 1; i >= 0; --i)
95 | {
96 | if (buf[i] >= (uint64_t)mi.lpBaseOfDll && buf[i] <= (uint64_t)mi.lpBaseOfDll + mi.SizeOfImage)
97 | {
98 | return stacktop - STACK_SIZE + (i * PTR_SIZE);
99 | break;
100 | }
101 | }
102 | }
103 |
104 | return 0;
105 | }
106 |
107 | DWORD get_first_thread_id(DWORD pid)
108 | {
109 | HANDLE h = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
110 | if (h == INVALID_HANDLE_VALUE)
111 | return -1;
112 |
113 | THREADENTRY32 te {};
114 | te.dwSize = sizeof(te);
115 | if (Thread32First(h, &te))
116 | {
117 | do {
118 | if (te.dwSize >= FIELD_OFFSET(THREADENTRY32, th32OwnerProcessID) + sizeof(te.th32OwnerProcessID))
119 | {
120 | if (te.th32OwnerProcessID == pid)
121 | return te.th32ThreadID;
122 | }
123 | te.dwSize = sizeof(te);
124 | } while (Thread32Next(h, &te));
125 | }
126 |
127 | return -1;
128 | }
129 |
130 | int main(int argc, char** argv)
131 | {
132 | try
133 | {
134 | DWORD pid = std::stol(argv[1]);
135 |
136 | HANDLE process_handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
137 | if (!process_handle || process_handle == INVALID_HANDLE_VALUE)
138 | return EXIT_FAILURE;
139 |
140 | DWORD thread_id = get_first_thread_id(pid);
141 |
142 | uint64_t threadstack_0 = find_threadstack_for_thread(process_handle, thread_id);
143 | std::cout << threadstack_0 << std::endl;
144 | return EXIT_SUCCESS;
145 | }
146 | catch (std::invalid_argument&)
147 | {
148 | return EXIT_FAILURE;
149 | }
150 |
151 | }
--------------------------------------------------------------------------------