├── bridge ├── preview.png ├── BuildLinked.ps1 ├── Reloaded.Checks.targets ├── modconfig.json ├── template │ ├── modcontext.cs │ ├── configuration │ │ ├── utilities.cs │ │ ├── configuratormixinbase.cs │ │ ├── configurator.cs │ │ └── configurable.cs │ ├── modbase.cs │ └── startup.cs ├── bridge.csproj ├── mod.cs ├── .github │ └── workflows │ │ └── reloaded.yml ├── config.cs ├── Reloaded.Trimming.targets └── Publish.ps1 ├── sh-fixed-edition ├── framework.h ├── pch.cpp ├── dllmain.cpp ├── pch.h ├── sh-fixed-edition.vcxproj.filters ├── sh-fixed-edition.vcxproj ├── MemAccess.h ├── FunctionHook.h └── mod.cpp ├── README.md ├── .gitattributes ├── sh-fixed-edition.sln └── .gitignore /bridge/preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaphaelDrewBoltman/sh-fixed-edition/HEAD/bridge/preview.png -------------------------------------------------------------------------------- /sh-fixed-edition/framework.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers 4 | // Windows Header Files 5 | #include 6 | -------------------------------------------------------------------------------- /sh-fixed-edition/pch.cpp: -------------------------------------------------------------------------------- 1 | // pch.cpp: source file corresponding to the pre-compiled header 2 | 3 | #include "pch.h" 4 | 5 | // When you are using pre-compiled headers, this source file is necessary for compilation to succeed. 6 | -------------------------------------------------------------------------------- /bridge/BuildLinked.ps1: -------------------------------------------------------------------------------- 1 | # Set Working Directory 2 | Split-Path $MyInvocation.MyCommand.Path | Push-Location 3 | [Environment]::CurrentDirectory = $PWD 4 | 5 | Remove-Item "$env:RELOADEDIIMODS/bridge/*" -Force -Recurse 6 | dotnet publish "./bridge.csproj" -c Release -o "$env:RELOADEDIIMODS/bridge" /p:OutputPath="./bin/Release" /p:ReloadedILLink="true" 7 | 8 | # Restore Working Directory 9 | Pop-Location -------------------------------------------------------------------------------- /bridge/Reloaded.Checks.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /sh-fixed-edition/dllmain.cpp: -------------------------------------------------------------------------------- 1 | // dllmain.cpp : Defines the entry point for the DLL application. 2 | #include "pch.h" 3 | 4 | BOOL APIENTRY DllMain( HMODULE hModule, 5 | DWORD ul_reason_for_call, 6 | LPVOID lpReserved 7 | ) 8 | { 9 | switch (ul_reason_for_call) 10 | { 11 | case DLL_PROCESS_ATTACH: 12 | case DLL_THREAD_ATTACH: 13 | case DLL_THREAD_DETACH: 14 | case DLL_PROCESS_DETACH: 15 | break; 16 | } 17 | return TRUE; 18 | } 19 | 20 | -------------------------------------------------------------------------------- /sh-fixed-edition/pch.h: -------------------------------------------------------------------------------- 1 | // pch.h: This is a precompiled header file. 2 | // Files listed below are compiled only once, improving build performance for future builds. 3 | // This also affects IntelliSense performance, including code completion and many code browsing features. 4 | // However, files listed here are ALL re-compiled if any one of them is updated between builds. 5 | // Do not add files here that you will be updating frequently as this negates the performance advantage. 6 | 7 | #ifndef PCH_H 8 | #define PCH_H 9 | 10 | // add headers that you want to pre-compile here 11 | #include "framework.h" 12 | 13 | #endif //PCH_H 14 | -------------------------------------------------------------------------------- /bridge/modconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "ModId": "sonicheroes.utils.fixededition", 3 | "ModName": "SH:FE", 4 | "ModAuthor": "DonutStopGaming, Kell", 5 | "ModVersion": "1.03", 6 | "ModDescription": "Compilation of code-based fixes and improvements regarding graphical/gameplay bugs and overall broken code for Sonic Heroes, alongside model/texture fixes.", 7 | "ModDll": "bridge.dll", 8 | "ModIcon": "preview.png", 9 | "ModR2RManagedDll32": "", 10 | "ModR2RManagedDll64": "", 11 | "ModNativeDll32": "", 12 | "ModNativeDll64": "", 13 | "IsLibrary": false, 14 | "ReleaseMetadataFileName": "sonicheroes.utils.fixededition.ReleaseMetadata.json", 15 | "PluginData": {}, 16 | "IsUniversalMod": false, 17 | "ModDependencies": [ 18 | "reloaded.sharedlib.hooks", 19 | "reloaded.universal.redirector" 20 | ], 21 | "OptionalDependencies": [], 22 | "SupportedAppId": [ 23 | "tsonic_win.exe" 24 | ], 25 | "ProjectUrl": "" 26 | } -------------------------------------------------------------------------------- /bridge/template/modcontext.cs: -------------------------------------------------------------------------------- 1 | using bridge.Configuration; 2 | using Reloaded.Mod.Interfaces; 3 | using IReloadedHooks = Reloaded.Hooks.ReloadedII.Interfaces.IReloadedHooks; 4 | 5 | namespace bridge.Template 6 | { 7 | /// 8 | /// Represents information passed in from the mod loader template to the implementing mod. 9 | /// 10 | public class ModContext 11 | { 12 | /// 13 | /// Provides access to the mod loader API. 14 | /// 15 | public IModLoader ModLoader { get; set; } = null!; 16 | 17 | /// 18 | /// Provides access to the Reloaded.Hooks API. 19 | /// 20 | public IReloadedHooks? Hooks { get; set; } = null!; 21 | 22 | /// 23 | /// Provides access to the Reloaded logger. 24 | /// 25 | public ILogger Logger { get; set; } = null!; 26 | 27 | /// 28 | /// Provides access to this mod's configuration. 29 | /// 30 | public Config Configuration { get; set; } = null!; 31 | 32 | /// 33 | /// Configuration of this mod. 34 | /// 35 | public IModConfig ModConfig { get; set; } = null!; 36 | 37 | /// 38 | /// Instance of the IMod interface that created this mod instance. 39 | /// 40 | public IMod Owner { get; set; } = null!; 41 | } 42 | } -------------------------------------------------------------------------------- /bridge/template/configuration/utilities.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | 3 | namespace bridge.Template.Configuration 4 | { 5 | public class Utilities 6 | { 7 | /// Function that retrieves the value. 8 | /// The timeout in milliseconds. 9 | /// Amount of sleep per iteration/attempt. 10 | /// Token that allows for cancellation of the task. 11 | /// Timeout expired. 12 | public static T TryGetValue(Func getValue, int timeout, int sleepTime, CancellationToken token = default) where T : new() 13 | { 14 | Stopwatch watch = new Stopwatch(); 15 | watch.Start(); 16 | bool valueSet = false; 17 | T value = new T(); 18 | 19 | while (watch.ElapsedMilliseconds < timeout) 20 | { 21 | if (token.IsCancellationRequested) 22 | return value; 23 | 24 | try 25 | { 26 | value = getValue(); 27 | valueSet = true; 28 | break; 29 | } 30 | catch (Exception) { /* Ignored */ } 31 | 32 | Thread.Sleep(sleepTime); 33 | } 34 | 35 | if (valueSet == false) 36 | throw new Exception($"Timeout limit {timeout} exceeded."); 37 | 38 | return value; 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /sh-fixed-edition/sh-fixed-edition.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 | Header Files 20 | 21 | 22 | Header Files 23 | 24 | 25 | Header Files 26 | 27 | 28 | Header Files 29 | 30 | 31 | 32 | 33 | Source Files 34 | 35 | 36 | Source Files 37 | 38 | 39 | Source Files 40 | 41 | 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sonic Heroes: Fixed Edition 2 | Sonic Heroes' Windows Port is known for its bugs, glitches, issues... it was never the perfect system for everybody, which tend to make people either suffer with how the game works and looks or unfortunately migrate to the GameCube port. From elements from prototypes, to messed up code, to even wrong assets, SH felt like it waas unpolished the more you look at it upclose. 3 | 4 | Luckily, there is a solution - Sonic Heroes: Fixed Edition! This mod is a compilation of code-based or graphical fixes, for a better gameplay experience and polished material. It comes with restorations, improvements, some enhancements and additions. Every single detail mattered to make this half of your experience better. 5 | 6 | ## Installation 7 | Simply install the mod manually or via 1-Click Install. Once you have it on the mod loader, make total sure you move SH:FE to the very top of the list. Fixed Edition should always come first before any other mod. And now it's just saving and launching the game - simple! 8 | 9 | ## Wiki 10 | There is a [wiki](http://github.com/raphaeldrewboltman/sh-fixed-edition/wiki) (***WiP!***) that tells and shows each fix included in this mod, divided by code-based and filesystem fixes done for the game and featuring comparison images. As well as a subdivision for missing fixes - these are some that aren't containg in SH:FE and with time will be brought to it in future updates. 11 | 12 | ## Issues 13 | Found any issues on the mod, or found something on a port that can be for this mod? Feel free to write an issue on GitHub. Make sure it's not repetitive compared to other issues, and to check the wiki so you don't write any duplicates. 14 | 15 | ## Help 16 | Interested in helping out with missing fixes? That's rare- 17 | 18 | But join the Heroes Hacking Central Discord Server where you'll find a forum thread for this mod in specific. Any technical/code-based help is appreciated! -------------------------------------------------------------------------------- /bridge/bridge.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0-windows 5 | false 6 | true 7 | 12.0 8 | enable 9 | True 10 | $(RELOADEDIIMODS)/sh-fixed-edition 11 | enable 12 | 13 | 16 | false 17 | $(RELOADEDIIMODS)\sh-fixed-edition 18 | $(RELOADEDIIMODS)\sh-fixed-edition 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | Always 41 | 42 | 43 | PreserveNewest 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /bridge/template/configuration/configuratormixinbase.cs: -------------------------------------------------------------------------------- 1 | using bridge.Configuration; 2 | using Reloaded.Mod.Interfaces; 3 | 4 | namespace bridge.Template.Configuration 5 | { 6 | /// 7 | /// Creates the various different configurations used by the mod. 8 | /// These configurations are available in the dropdown in Reloaded launcher. 9 | /// 10 | public class ConfiguratorMixinBase 11 | { 12 | /// 13 | /// Defines the configuration items to create. 14 | /// 15 | /// Folder storing the configuration. 16 | public virtual IUpdatableConfigurable[] MakeConfigurations(string configFolder) 17 | { 18 | // You can add any Configurable here. 19 | return new IUpdatableConfigurable[] 20 | { 21 | Configurable.FromFile(Path.Combine(configFolder, "config.json"), "Configure Sonic Heroes: Fixed Edition") 22 | }; 23 | } 24 | 25 | /// 26 | /// Allows for custom launcher/configurator implementation. 27 | /// If you have your own configuration program/code, run that code here and return true, else return false. 28 | /// 29 | public virtual bool TryRunCustomConfiguration(Configurator configurator) 30 | { 31 | return false; 32 | } 33 | 34 | #region Config Migration (Must implement if coming from old mod template with config in mod folder) 35 | /// 36 | /// Migrates from the old config location (usually mod folder) to the newer config location (separate folder). 37 | /// 38 | /// Old directory containing the mod configs. 39 | /// New directory pointing to user config folder. 40 | public virtual void Migrate(string oldDirectory, string newDirectory) 41 | { 42 | // Uncomment to move files from older to newer config directory. 43 | // TryMoveFile("Config.json"); 44 | 45 | #pragma warning disable CS8321 46 | void TryMoveFile(string fileName) 47 | { 48 | try { File.Move(Path.Combine(oldDirectory, fileName), Path.Combine(newDirectory, fileName)); } 49 | catch (Exception) { /* Ignored */ } 50 | } 51 | #pragma warning restore CS8321 52 | } 53 | #endregion 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /bridge/template/modbase.cs: -------------------------------------------------------------------------------- 1 | using bridge.Configuration; 2 | 3 | namespace bridge.Template 4 | { 5 | /// 6 | /// Base class for implementing mod functionality. 7 | /// 8 | public class ModBase 9 | { 10 | /// 11 | /// Returns true if the suspend functionality is supported, else false. 12 | /// 13 | public virtual bool CanSuspend() => false; 14 | 15 | /// 16 | /// Returns true if the unload functionality is supported, else false. 17 | /// 18 | public virtual bool CanUnload() => false; 19 | 20 | /// 21 | /// Suspends your mod, i.e. mod stops performing its functionality but is not unloaded. 22 | /// 23 | public virtual void Suspend() 24 | { 25 | /* Some tips if you wish to support this (CanSuspend == true) 26 | 27 | A. Undo memory modifications. 28 | B. Deactivate hooks. (Reloaded.Hooks Supports This!) 29 | */ 30 | } 31 | 32 | /// 33 | /// Unloads your mod, i.e. mod stops performing its functionality but is not unloaded. 34 | /// 35 | /// In most cases, calling suspend here is sufficient. 36 | public virtual void Unload() 37 | { 38 | /* Some tips if you wish to support this (CanUnload == true). 39 | 40 | A. Execute Suspend(). [Suspend should be reusable in this method] 41 | B. Release any unmanaged resources, e.g. Native memory. 42 | */ 43 | } 44 | 45 | /// 46 | /// Automatically called by the mod loader when the mod is about to be unloaded. 47 | /// 48 | public virtual void Disposing() 49 | { 50 | 51 | } 52 | 53 | /// 54 | /// Automatically called by the mod loader when the mod is about to be unloaded. 55 | /// 56 | public virtual void Resume() 57 | { 58 | /* Some tips if you wish to support this (CanSuspend == true) 59 | 60 | A. Redo memory modifications. 61 | B. Re-activate hooks. (Reloaded.Hooks Supports This!) 62 | */ 63 | } 64 | 65 | public virtual void ConfigurationUpdated(Config configuration) 66 | { 67 | // Apply settings from configuration. 68 | // ... your code here. 69 | } 70 | } 71 | } -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /sh-fixed-edition.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.14.36705.20 d17.14 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sh-fixed-edition", "sh-fixed-edition\sh-fixed-edition.vcxproj", "{9F6DCBD1-F175-4290-BF84-1F4D57776ADA}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "bridge", "bridge\bridge.csproj", "{167CB672-F57B-4970-B17A-E2D1241170FB}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Debug|x64 = Debug|x64 14 | Debug|x86 = Debug|x86 15 | Release|Any CPU = Release|Any CPU 16 | Release|x64 = Release|x64 17 | Release|x86 = Release|x86 18 | EndGlobalSection 19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 20 | {9F6DCBD1-F175-4290-BF84-1F4D57776ADA}.Debug|Any CPU.ActiveCfg = Debug|x64 21 | {9F6DCBD1-F175-4290-BF84-1F4D57776ADA}.Debug|Any CPU.Build.0 = Debug|x64 22 | {9F6DCBD1-F175-4290-BF84-1F4D57776ADA}.Debug|x64.ActiveCfg = Debug|x64 23 | {9F6DCBD1-F175-4290-BF84-1F4D57776ADA}.Debug|x64.Build.0 = Debug|x64 24 | {9F6DCBD1-F175-4290-BF84-1F4D57776ADA}.Debug|x86.ActiveCfg = Debug|Win32 25 | {9F6DCBD1-F175-4290-BF84-1F4D57776ADA}.Debug|x86.Build.0 = Debug|Win32 26 | {9F6DCBD1-F175-4290-BF84-1F4D57776ADA}.Release|Any CPU.ActiveCfg = Release|x64 27 | {9F6DCBD1-F175-4290-BF84-1F4D57776ADA}.Release|Any CPU.Build.0 = Release|x64 28 | {9F6DCBD1-F175-4290-BF84-1F4D57776ADA}.Release|x64.ActiveCfg = Release|x64 29 | {9F6DCBD1-F175-4290-BF84-1F4D57776ADA}.Release|x64.Build.0 = Release|x64 30 | {9F6DCBD1-F175-4290-BF84-1F4D57776ADA}.Release|x86.ActiveCfg = Release|Win32 31 | {9F6DCBD1-F175-4290-BF84-1F4D57776ADA}.Release|x86.Build.0 = Release|Win32 32 | {167CB672-F57B-4970-B17A-E2D1241170FB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 33 | {167CB672-F57B-4970-B17A-E2D1241170FB}.Debug|Any CPU.Build.0 = Debug|Any CPU 34 | {167CB672-F57B-4970-B17A-E2D1241170FB}.Debug|x64.ActiveCfg = Debug|Any CPU 35 | {167CB672-F57B-4970-B17A-E2D1241170FB}.Debug|x64.Build.0 = Debug|Any CPU 36 | {167CB672-F57B-4970-B17A-E2D1241170FB}.Debug|x86.ActiveCfg = Debug|Any CPU 37 | {167CB672-F57B-4970-B17A-E2D1241170FB}.Debug|x86.Build.0 = Debug|Any CPU 38 | {167CB672-F57B-4970-B17A-E2D1241170FB}.Release|Any CPU.ActiveCfg = Release|Any CPU 39 | {167CB672-F57B-4970-B17A-E2D1241170FB}.Release|Any CPU.Build.0 = Release|Any CPU 40 | {167CB672-F57B-4970-B17A-E2D1241170FB}.Release|x64.ActiveCfg = Release|Any CPU 41 | {167CB672-F57B-4970-B17A-E2D1241170FB}.Release|x64.Build.0 = Release|Any CPU 42 | {167CB672-F57B-4970-B17A-E2D1241170FB}.Release|x86.ActiveCfg = Release|Any CPU 43 | {167CB672-F57B-4970-B17A-E2D1241170FB}.Release|x86.Build.0 = Release|Any CPU 44 | EndGlobalSection 45 | GlobalSection(SolutionProperties) = preSolution 46 | HideSolutionNode = FALSE 47 | EndGlobalSection 48 | GlobalSection(ExtensibilityGlobals) = postSolution 49 | SolutionGuid = {82FA81D5-D33C-4D13-B780-338700750480} 50 | EndGlobalSection 51 | EndGlobal 52 | -------------------------------------------------------------------------------- /bridge/mod.cs: -------------------------------------------------------------------------------- 1 | using bridge.Configuration; 2 | using bridge.Template; 3 | using Reloaded.Hooks.ReloadedII.Interfaces; 4 | using Reloaded.Mod.Interfaces; 5 | using System.Runtime.InteropServices; 6 | 7 | namespace bridge 8 | { 9 | /// 10 | /// Your mod logic goes here. 11 | /// 12 | public class Mod : ModBase // <= Do not Remove. 13 | { 14 | /// 15 | /// Provides access to the mod loader API. 16 | /// 17 | private readonly IModLoader _modLoader; 18 | 19 | /// 20 | /// Provides access to the Reloaded.Hooks API. 21 | /// 22 | /// This is null if you remove dependency on Reloaded.SharedLib.Hooks in your mod. 23 | private readonly IReloadedHooks? _hooks; 24 | 25 | /// 26 | /// Provides access to the Reloaded logger. 27 | /// 28 | private readonly ILogger _logger; 29 | 30 | /// 31 | /// Entry point into the mod, instance that created this class. 32 | /// 33 | private readonly IMod _owner; 34 | 35 | /// 36 | /// Provides access to this mod's configuration. 37 | /// 38 | private Config _configuration; 39 | 40 | /// 41 | /// The configuration of the currently executing mod. 42 | /// 43 | private readonly IModConfig _modConfig; 44 | 45 | [DllImport("sh-fixed-edition.dll", CallingConvention = CallingConvention.Cdecl)] 46 | public static extern void InitMod(); 47 | 48 | public Mod(ModContext context) 49 | { 50 | _modLoader = context.ModLoader; 51 | _hooks = context.Hooks; 52 | _logger = context.Logger; 53 | _owner = context.Owner; 54 | _configuration = context.Configuration; 55 | _modConfig = context.ModConfig; 56 | 57 | 58 | InitMod(); 59 | 60 | //! 61 | } 62 | 63 | #region Standard Overrides 64 | public override void ConfigurationUpdated(Config configuration) 65 | { 66 | /* 67 | if (_configuration.TitleScr != configuration.TitleScr) 68 | Config_TitleScr(configuration.TitleScr); 69 | 70 | if (_configuration.DemoMode != configuration.DemoMode) 71 | Config_DemoMode(configuration.DemoMode); 72 | 73 | Config_TDarkChaosEme(configuration.TDarkChaosEmeEnum); 74 | _configuration = configuration; 75 | 76 | if (_configuration.IndirectOFF != configuration.IndirectOFF) 77 | Config_IndirectOFF(configuration.IndirectOFF); 78 | 79 | if (_configuration.Exit != configuration.Exit) 80 | Config_DemoMode(configuration.Exit); 81 | */ 82 | _configuration = configuration; 83 | _logger.WriteLine($"SH Fixed Edition configuration updated: Please restart the game for the changes to take effect."); 84 | } 85 | #endregion 86 | 87 | #region For Exports, Serialization etc. 88 | #pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. 89 | public Mod() { } 90 | #pragma warning restore CS8618 91 | #endregion 92 | } 93 | } -------------------------------------------------------------------------------- /bridge/template/configuration/configurator.cs: -------------------------------------------------------------------------------- 1 | using bridge.Configuration; 2 | using Reloaded.Mod.Interfaces; 3 | 4 | namespace bridge.Template.Configuration 5 | { 6 | public class Configurator : IConfiguratorV3 7 | { 8 | private static ConfiguratorMixin _configuratorMixin = new ConfiguratorMixin(); 9 | 10 | /// 11 | /// The folder where the modification files are stored. 12 | /// 13 | public string? ModFolder { get; private set; } 14 | 15 | /// 16 | /// Full path to the config folder. 17 | /// 18 | public string? ConfigFolder { get; private set; } 19 | 20 | /// 21 | /// Specifies additional information for the configurator. 22 | /// 23 | public ConfiguratorContext Context { get; private set; } 24 | 25 | /// 26 | /// Returns a list of configurations. 27 | /// 28 | public IUpdatableConfigurable[] Configurations => _configurations ?? MakeConfigurations(); 29 | private IUpdatableConfigurable[]? _configurations; 30 | 31 | private IUpdatableConfigurable[] MakeConfigurations() 32 | { 33 | _configurations = _configuratorMixin.MakeConfigurations(ConfigFolder!); 34 | 35 | // Add self-updating to configurations. 36 | for (int x = 0; x < Configurations.Length; x++) 37 | { 38 | var xCopy = x; 39 | Configurations[x].ConfigurationUpdated += configurable => 40 | { 41 | Configurations[xCopy] = configurable; 42 | }; 43 | } 44 | 45 | return _configurations; 46 | } 47 | 48 | public Configurator() { } 49 | public Configurator(string configDirectory) : this() 50 | { 51 | ConfigFolder = configDirectory; 52 | } 53 | 54 | /* Configurator V2 */ 55 | 56 | /// 57 | /// Migrates from the old config location to the newer config location. 58 | /// 59 | /// Old directory containing the mod configs. 60 | /// New directory pointing to user config folder. 61 | public void Migrate(string oldDirectory, string newDirectory) => _configuratorMixin.Migrate(oldDirectory, newDirectory); 62 | 63 | /* Configurator */ 64 | 65 | /// 66 | /// Gets an individual user configuration. 67 | /// 68 | public TType GetConfiguration(int index) => (TType)Configurations[index]; 69 | 70 | /* IConfigurator. */ 71 | 72 | /// 73 | /// Sets the config directory for the Configurator. 74 | /// 75 | public void SetConfigDirectory(string configDirectory) => ConfigFolder = configDirectory; 76 | 77 | /// 78 | /// Specifies additional context for the configurator. 79 | /// 80 | public void SetContext(in ConfiguratorContext context) => Context = context; 81 | 82 | /// 83 | /// Returns a list of user configurations. 84 | /// 85 | public IConfigurable[] GetConfigurations() => Configurations; 86 | 87 | /// 88 | /// Allows for custom launcher/configurator implementation. 89 | /// If you have your own configuration program/code, run that code here and return true, else return false. 90 | /// 91 | public bool TryRunCustomConfiguration() => _configuratorMixin.TryRunCustomConfiguration(this); 92 | 93 | /// 94 | /// Sets the mod directory for the Configurator. 95 | /// 96 | public void SetModDirectory(string modDirectory) { ModFolder = modDirectory; } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /bridge/template/startup.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * This file and other files in the `Template` folder are intended to be left unedited (if possible), 3 | * to make it easier to upgrade to newer versions of the template. 4 | */ 5 | 6 | using bridge.Configuration; 7 | using bridge.Template.Configuration; 8 | using Reloaded.Hooks.ReloadedII.Interfaces; 9 | using Reloaded.Mod.Interfaces; 10 | using Reloaded.Mod.Interfaces.Internal; 11 | 12 | namespace bridge.Template 13 | { 14 | public class Startup : IMod 15 | { 16 | /// 17 | /// Used for writing text to the Reloaded log. 18 | /// 19 | private ILogger _logger = null!; 20 | 21 | /// 22 | /// Provides access to the mod loader API. 23 | /// 24 | private IModLoader _modLoader = null!; 25 | 26 | /// 27 | /// Stores the contents of your mod's configuration. Automatically updated by template. 28 | /// 29 | private Config _configuration = null!; 30 | 31 | /// 32 | /// An interface to Reloaded's the function hooks/detours library. 33 | /// See: https://github.com/Reloaded-Project/Reloaded.Hooks 34 | /// for documentation and samples. 35 | /// 36 | private IReloadedHooks? _hooks; 37 | 38 | /// 39 | /// Configuration of the current mod. 40 | /// 41 | private IModConfig _modConfig = null!; 42 | 43 | /// 44 | /// Encapsulates your mod logic. 45 | /// 46 | private ModBase _mod = new Mod(); 47 | 48 | /// 49 | /// Entry point for your mod. 50 | /// 51 | public void StartEx(IModLoaderV1 loaderApi, IModConfigV1 modConfig) 52 | { 53 | _modLoader = (IModLoader)loaderApi; 54 | _modConfig = (IModConfig)modConfig; 55 | _logger = (ILogger)_modLoader.GetLogger(); 56 | _modLoader.GetController()?.TryGetTarget(out _hooks!); 57 | 58 | // Your config file is in Config.json. 59 | // Need a different name, format or more configurations? Modify the `Configurator`. 60 | // If you do not want a config, remove Configuration folder and Config class. 61 | var configurator = new Configurator(_modLoader.GetModConfigDirectory(_modConfig.ModId)); 62 | _configuration = configurator.GetConfiguration(0); 63 | _configuration.ConfigurationUpdated += OnConfigurationUpdated; 64 | 65 | // Please put your mod code in the class below, 66 | // use this class for only interfacing with mod loader. 67 | _mod = new Mod(new ModContext() 68 | { 69 | Logger = _logger, 70 | Hooks = _hooks, 71 | ModLoader = _modLoader, 72 | ModConfig = _modConfig, 73 | Owner = this, 74 | Configuration = _configuration, 75 | }); 76 | } 77 | 78 | private void OnConfigurationUpdated(IConfigurable obj) 79 | { 80 | /* 81 | This is executed when the configuration file gets 82 | updated by the user at runtime. 83 | */ 84 | 85 | // Replace configuration with new. 86 | _configuration = (Config)obj; 87 | _mod.ConfigurationUpdated(_configuration); 88 | } 89 | 90 | /* Mod loader actions. */ 91 | public void Suspend() => _mod.Suspend(); 92 | public void Resume() => _mod.Resume(); 93 | public void Unload() => _mod.Unload(); 94 | 95 | /* If CanSuspend == false, suspend and resume button are disabled in Launcher and Suspend()/Resume() will never be called. 96 | If CanUnload == false, unload button is disabled in Launcher and Unload() will never be called. 97 | */ 98 | public bool CanUnload() => _mod.CanUnload(); 99 | public bool CanSuspend() => _mod.CanSuspend(); 100 | 101 | /* Automatically called by the mod loader when the mod is about to be unloaded. */ 102 | public Action Disposing => () => _mod.Disposing(); 103 | } 104 | } -------------------------------------------------------------------------------- /bridge/.github/workflows/reloaded.yml: -------------------------------------------------------------------------------- 1 | # Script to build and publish a Reloaded Mod. 2 | # by Sewer56 3 | 4 | # Produces: 5 | # - Build to Upload to GameBanana 6 | # - Build to Upload to GitHub 7 | # - Build to Upload to NuGet 8 | # - Changelog 9 | 10 | # When pushing a tag 11 | # - Upload to GitHub Releases 12 | # - Upload to Reloaded NuGet Repository (if GitHub Secret RELOADED_NUGET_KEY is specified) 13 | 14 | name: Build and Publish Reloaded Mod 15 | 16 | on: 17 | push: 18 | branches: [ main ] 19 | tags: 20 | - '*' 21 | pull_request: 22 | branches: [ main ] 23 | workflow_dispatch: 24 | 25 | env: 26 | PUBLISH_COMMON_PATH: ./Publish/ToUpload/ 27 | 28 | PUBLISH_GAMEBANANA_PATH: ./Publish/ToUpload/GameBanana 29 | PUBLISH_GITHUB_PATH: ./Publish/ToUpload/Generic 30 | PUBLISH_NUGET_PATH: ./Publish/ToUpload/NuGet 31 | 32 | PUBLISH_CHANGELOG_PATH: ./Publish/Changelog.md 33 | PUBLISH_PATH: ./Publish 34 | 35 | RELOADEDIIMODS: . 36 | 37 | # Default value is official Reloaded package server. 38 | NUGET_URL: http://packages.sewer56.moe:5000/v3/index.json 39 | 40 | IS_RELEASE: ${{ startsWith(github.ref, 'refs/tags/') }} 41 | RELEASE_TAG: ${{ github.ref_name }} 42 | 43 | jobs: 44 | build: 45 | runs-on: windows-latest 46 | defaults: 47 | run: 48 | shell: pwsh 49 | 50 | steps: 51 | - uses: actions/checkout@v4 52 | with: 53 | fetch-depth: 0 54 | submodules: 'recursive' 55 | 56 | - name: Setup .NET Core SDK (5.0) 57 | uses: actions/setup-dotnet@v4 58 | with: 59 | dotnet-version: 5.0.x 60 | 61 | - name: Setup .NET Core SDK (8.0) 62 | uses: actions/setup-dotnet@v4 63 | with: 64 | dotnet-version: 8.0.x 65 | 66 | - name: Setup Node.js 67 | uses: actions/setup-node@v4 68 | with: 69 | node-version: '14' 70 | 71 | - name: Setup AutoChangelog 72 | run: npm install -g auto-changelog 73 | 74 | - name: Create Changelog 75 | run: | 76 | [System.IO.Directory]::CreateDirectory("$env:PUBLISH_PATH") 77 | if ($env:IS_RELEASE -eq 'true') { 78 | auto-changelog --sort-commits date --hide-credit --template keepachangelog --commit-limit false --starting-version "$env:RELEASE_TAG" --output "$env:PUBLISH_CHANGELOG_PATH" 79 | } 80 | else { 81 | auto-changelog --sort-commits date --hide-credit --template keepachangelog --commit-limit false --unreleased --output "$env:PUBLISH_CHANGELOG_PATH" 82 | } 83 | 84 | - name: Build 85 | run: ./Publish.ps1 -ChangelogPath "$env:PUBLISH_CHANGELOG_PATH" 86 | 87 | - name: Upload GitHub Release Artifact 88 | uses: actions/upload-artifact@v4 89 | with: 90 | # Artifact name 91 | name: GitHub Release 92 | # A file, directory or wildcard pattern that describes what to upload 93 | path: | 94 | ${{ env.PUBLISH_GITHUB_PATH }}/* 95 | 96 | - name: Upload GameBanana Release Artifact 97 | uses: actions/upload-artifact@v4 98 | with: 99 | # Artifact name 100 | name: GameBanana Release 101 | # A file, directory or wildcard pattern that describes what to upload 102 | path: | 103 | ${{ env.PUBLISH_GAMEBANANA_PATH }}/* 104 | 105 | - name: Upload NuGet Release Artifact 106 | uses: actions/upload-artifact@v4 107 | with: 108 | # Artifact name 109 | name: NuGet Release 110 | # A file, directory or wildcard pattern that describes what to upload 111 | path: | 112 | ${{ env.PUBLISH_NUGET_PATH }}/* 113 | 114 | - name: Upload Changelog Artifact 115 | uses: actions/upload-artifact@v4 116 | with: 117 | # Artifact name 118 | name: Changelog 119 | # A file, directory or wildcard pattern that describes what to upload 120 | path: ${{ env.PUBLISH_CHANGELOG_PATH }} 121 | retention-days: 0 122 | 123 | - name: Upload to GitHub Releases (on Tag) 124 | uses: softprops/action-gh-release@v2 125 | if: env.IS_RELEASE == 'true' 126 | with: 127 | # Path to load note-worthy description of changes in release from 128 | body_path: ${{ env.PUBLISH_CHANGELOG_PATH }} 129 | # Newline-delimited list of path globs for asset files to upload 130 | files: | 131 | ${{ env.PUBLISH_GITHUB_PATH }}/* 132 | 133 | - name: Push to NuGet (on Tag) 134 | env: 135 | NUGET_KEY: ${{ secrets.RELOADED_NUGET_KEY }} 136 | if: env.IS_RELEASE == 'true' 137 | run: | 138 | if ([string]::IsNullOrEmpty("$env:NUGET_KEY")) 139 | { 140 | Write-Host "NuGet Repository Key (GitHub Secrets -> RELOADED_NUGET_KEY) Not Specified. Skipping." 141 | return 142 | } 143 | 144 | $items = Get-ChildItem -Path "$env:PUBLISH_NUGET_PATH/*.nupkg" 145 | Foreach ($item in $items) 146 | { 147 | Write-Host "Pushing $item" 148 | dotnet nuget push "$item" -k "$env:NUGET_KEY" -s "$env:NUGET_URL" --skip-duplicate 149 | } 150 | -------------------------------------------------------------------------------- /bridge/template/configuration/configurable.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | using System.Text.Json; 3 | using System.Text.Json.Serialization; 4 | using Reloaded.Mod.Interfaces; 5 | 6 | namespace bridge.Template.Configuration 7 | { 8 | public class Configurable : IUpdatableConfigurable where TParentType : Configurable, new() 9 | { 10 | // Default Serialization Options 11 | public static JsonSerializerOptions SerializerOptions { get; } = new JsonSerializerOptions() 12 | { 13 | Converters = { new JsonStringEnumConverter() }, 14 | WriteIndented = true 15 | }; 16 | 17 | /* Events */ 18 | 19 | /// 20 | /// Automatically executed when the external configuration file is updated. 21 | /// Passes a new instance of the configuration as parameter. 22 | /// Inside your event handler, replace the variable storing the configuration with the new one. 23 | /// 24 | [Browsable(false)] 25 | public event Action? ConfigurationUpdated; 26 | 27 | /* Class Properties */ 28 | 29 | /// 30 | /// Full path to the configuration file. 31 | /// 32 | [JsonIgnore] 33 | [Browsable(false)] 34 | public string? FilePath { get; private set; } 35 | 36 | /// 37 | /// The name of the configuration file. 38 | /// 39 | [JsonIgnore] 40 | [Browsable(false)] 41 | public string? ConfigName { get; private set; } 42 | 43 | /// 44 | /// Receives events on whenever the file is actively changed or updated. 45 | /// 46 | [JsonIgnore] 47 | [Browsable(false)] 48 | private FileSystemWatcher? ConfigWatcher { get; set; } 49 | 50 | /* Construction */ 51 | public Configurable() { } 52 | 53 | private void Initialize(string filePath, string configName) 54 | { 55 | // Initializes an instance after construction by e.g. a serializer. 56 | FilePath = filePath; 57 | ConfigName = configName; 58 | 59 | MakeConfigWatcher(); 60 | Save = OnSave; 61 | } 62 | 63 | /* Cleanup */ 64 | public void DisposeEvents() 65 | { 66 | // Halts the FilesystemWatcher and all events associated with this instance. 67 | ConfigWatcher?.Dispose(); 68 | ConfigurationUpdated = null; 69 | } 70 | 71 | /* Load/Save support. */ 72 | 73 | /// 74 | /// Saves the configuration to the hard disk. 75 | /// 76 | [JsonIgnore] 77 | [Browsable(false)] 78 | public Action? Save { get; private set; } 79 | 80 | /// 81 | /// Safety lock for when changed event gets raised twice on file save. 82 | /// 83 | [Browsable(false)] 84 | private static object _readLock = new object(); 85 | 86 | /// 87 | /// Loads a specified configuration from the hard disk, or creates a default if it does not exist. 88 | /// 89 | /// The full file path of the config. 90 | /// The name of the configuration. 91 | public static TParentType FromFile(string filePath, string configName) => ReadFrom(filePath, configName); 92 | 93 | /* Event */ 94 | 95 | /// 96 | /// Creates a that will automatically raise an 97 | /// event when the config file is changed. 98 | /// 99 | /// 100 | private void MakeConfigWatcher() 101 | { 102 | ConfigWatcher = new FileSystemWatcher(Path.GetDirectoryName(FilePath)!, Path.GetFileName(FilePath)!); 103 | ConfigWatcher.Changed += (sender, e) => OnConfigurationUpdated(); 104 | ConfigWatcher.EnableRaisingEvents = true; 105 | } 106 | 107 | /// 108 | /// Reloads the configuration from the hard disk and raises the updated event. 109 | /// 110 | private void OnConfigurationUpdated() 111 | { 112 | lock (_readLock) 113 | { 114 | // Load and copy events. 115 | // Note: External program might still be writing to file while this is being executed, so we need to keep retrying. 116 | var newConfig = Utilities.TryGetValue(() => ReadFrom(FilePath!, ConfigName!), 250, 2); 117 | newConfig.ConfigurationUpdated = ConfigurationUpdated; 118 | 119 | // Disable events for this instance. 120 | DisposeEvents(); 121 | 122 | // Call subscribers through the new config. 123 | newConfig.ConfigurationUpdated?.Invoke(newConfig); 124 | } 125 | } 126 | 127 | private void OnSave() 128 | { 129 | var parent = (TParentType)this; 130 | File.WriteAllText(FilePath!, JsonSerializer.Serialize(parent, SerializerOptions)); 131 | } 132 | 133 | /* Utility */ 134 | private static TParentType ReadFrom(string filePath, string configName) 135 | { 136 | var result = (File.Exists(filePath) 137 | ? JsonSerializer.Deserialize(File.ReadAllBytes(filePath), SerializerOptions) 138 | : new TParentType()) ?? new TParentType(); 139 | 140 | result.Initialize(filePath, configName); 141 | return result; 142 | } 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /bridge/config.cs: -------------------------------------------------------------------------------- 1 | using bridge.Template.Configuration; 2 | using Reloaded.Mod.Interfaces.Structs; 3 | using System.ComponentModel; 4 | using System.ComponentModel.DataAnnotations; 5 | 6 | namespace bridge.Configuration 7 | { 8 | public class Config : Configurable 9 | { 10 | /* (collaped configs in case i need or mess smt up lol) 11 | User Properties: 12 | - Please put all of your configurable properties here. 13 | 14 | By default, configuration saves as "Config.json" in mod user config folder. 15 | Need more config files/classes? See Configuration.cs 16 | 17 | Available Attributes: 18 | - Category 19 | - DisplayName 20 | - Description 21 | - DefaultValue 22 | 23 | // Technically Supported but not Useful 24 | - Browsable 25 | - Localizable 26 | 27 | The `DefaultValue` attribute is used as part of the `Reset` button in Reloaded-Launcher. 28 | 29 | 30 | [DisplayName("String")] 31 | [Description("This is a string.")] 32 | [DefaultValue("Default Name")] 33 | public string String { get; set; } = "Default Name"; 34 | 35 | [DisplayName("Int")] 36 | [Description("This is an int.")] 37 | [DefaultValue(42)] 38 | public int Integer { get; set; } = 42; 39 | 40 | [DisplayName("Bool")] 41 | [Description("This is a bool.")] 42 | [DefaultValue(true)] 43 | public bool Boolean { get; set; } = true; 44 | 45 | [DisplayName("Float")] 46 | [Description("This is a floating point number.")] 47 | [DefaultValue(6.987654F)] 48 | public float Float { get; set; } = 6.987654F; 49 | 50 | [DisplayName("Enum")] 51 | [Description("This is an enumerable.")] 52 | [DefaultValue(SampleEnum.ILoveIt)] 53 | public SampleEnum Reloaded { get; set; } = SampleEnum.ILoveIt; 54 | 55 | public enum SampleEnum 56 | { 57 | [Display(Name = "No Opinion 🤷")] 58 | NoOpinion, 59 | [Display(Name = "It's Sucks! 👎")] 60 | Sucks, 61 | [Display(Name = "It's mediocre 😐")] 62 | IsMediocre, 63 | [Display(Name = "It's okay! 👍")] 64 | IsOk, 65 | [Display(Name = "It's cool! 😎")] 66 | IsCool, 67 | [Display(Name = "I Love It!!! ❤️🔥")] 68 | ILoveIt, 69 | } 70 | 71 | [DisplayName("Int Slider")] 72 | [Description("This is a int that uses a slider control similar to a volume control slider.")] 73 | [DefaultValue(100)] 74 | [SliderControlParams( 75 | minimum: 0.0, 76 | maximum: 100.0, 77 | smallChange: 1.0, 78 | largeChange: 10.0, 79 | tickFrequency: 10, 80 | isSnapToTickEnabled: false, 81 | tickPlacement: SliderControlTickPlacement.BottomRight, 82 | showTextField: true, 83 | isTextFieldEditable: true, 84 | textValidationRegex: "\\d{1-3}")] 85 | public int IntSlider { get; set; } = 100; 86 | 87 | [DisplayName("Double Slider")] 88 | [Description("This is a double that uses a slider control without any frills.")] 89 | [DefaultValue(0.5)] 90 | [SliderControlParams(minimum: 0.0, maximum: 1.0)] 91 | public double DoubleSlider { get; set; } = 0.5; 92 | 93 | [DisplayName("File Picker")] 94 | [Description("This is a sample file picker.")] 95 | [DefaultValue("")] 96 | [FilePickerParams(title: "Choose a File to load from")] 97 | public string File { get; set; } = ""; 98 | 99 | [DisplayName("Folder Picker")] 100 | [Description("Opens a file picker but locked to only allow folder selections.")] 101 | [DefaultValue("")] 102 | [FolderPickerParams( 103 | initialFolderPath: Environment.SpecialFolder.Desktop, 104 | userCanEditPathText: false, 105 | title: "Custom Folder Select", 106 | okButtonLabel: "Choose Folder", 107 | fileNameLabel: "ModFolder", 108 | multiSelect: true, 109 | forceFileSystem: true)] 110 | public string Folder { get; set; } = ""; 111 | 112 | */ 113 | 114 | [Category("Common Settings")] 115 | [DisplayName("Unfrozen Title Screen")] 116 | [Description("Makes the title screen animation continuous instead of freezing after\n pressing START Button/ENTER Key.")] 117 | [DefaultValue(true)] 118 | public bool TitleScr { get; set; } = true; 119 | 120 | [Category("Common Settings")] 121 | [DisplayName("Demo Gameplay")] 122 | [Description("Enables Demo Mode that is leftover in the Windows port of the game.\nGameCube and PlayStation ports contain Demo Gameplay by default.")] 123 | [DefaultValue(true)] 124 | public bool DemoMode { get; set; } = true; 125 | 126 | [Category("Character Settings")] 127 | [DisplayName("Shadow's Chaos Emerald")] 128 | [Description("Chaos Emerald from Chaos Inferno has different blendings\n compared to other ports. Not to mention its model and\n blendings are completely different from SA2.\n\n'Console Style' brings the same blendings as GC/XB/PS.\n'SA2 Style' brings a similar approach to SA2's emeralds.\n\nNote: Now the emerald's model by default is changed to\n use SA2's model as well")] 129 | [DefaultValue(TDarkChaosEme.Untouched)] 130 | public TDarkChaosEme TDarkChaosEmeEnum { get; set; } = TDarkChaosEme.Untouched; 131 | 132 | public enum TDarkChaosEme 133 | { 134 | [Display(Name = "Original/Untouched")] 135 | Untouched, 136 | [Display(Name = "Console Style")] 137 | Console, 138 | [Display(Name = "Sonic Adventure 2 Style")] 139 | SA2 140 | } 141 | 142 | [Category("Common Settings")] 143 | [DisplayName("No Exit Prompt")] 144 | [Description("Kell is a God.")] 145 | [DefaultValue(true)] 146 | public bool Exit { get; set; } = true; 147 | 148 | [Category("Stage Settings")] 149 | [DisplayName("Texture Pattern Animation")] 150 | [Description("Makes use of texture patern animation (similar to PS2 port) rather\n than indirect shaders.")] 151 | [DefaultValue(false)] 152 | public bool IndirectOFF { get; set; } = false; 153 | 154 | } 155 | 156 | /// 157 | /// Allows you to override certain aspects of the configuration creation process (e.g. create multiple configurations). 158 | /// Override elements in for finer control. 159 | /// 160 | public class ConfiguratorMixin : ConfiguratorMixinBase 161 | { 162 | // 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /bridge/Reloaded.Trimming.targets: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 13 | 14 | 15 | 16 | false 17 | 18 | 19 | false 20 | 21 | 22 | 23 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | <__PDBToLink Include="@(ResolvedFileToPublish)" Exclude="@(ManagedAssemblyToLink->'%(RelativeDir)%(Filename).pdb')" /> 37 | <_PDBToLink Include="@(ResolvedFileToPublish)" Exclude="@(__PDBToLink)" /> 38 | 39 | 40 | 41 | <_LinkedResolvedFileToPublishCandidate Include="@(ManagedAssemblyToLink->'$(IntermediateLinkDir)%(Filename)%(Extension)')" /> 42 | <_LinkedResolvedFileToPublishCandidate Include="@(_PDBToLink->'$(IntermediateLinkDir)%(Filename)%(Extension)')" /> 43 | 44 | 45 | 46 | 47 | 48 | 49 | true 50 | 51 | 52 | 53 | 54 | link 55 | 56 | copy 57 | $(TreatWarningsAsErrors) 58 | <_ExtraTrimmerArgs>--skip-unresolved true $(_ExtraTrimmerArgs) 59 | true 60 | 61 | 62 | 65 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | $(TrimmerDefaultAction) 85 | 86 | 87 | 88 | $(TrimMode) 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | Input Assembly: %(filename) [Mode: %(ManagedAssemblyToLink.TrimMode)] 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | <_LinkedResolvedFileToPublish Include="@(_LinkedResolvedFileToPublishCandidate)" Condition="Exists('%(Identity)')" /> 143 | 144 | 145 | 146 | 147 | 148 | 149 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Ww][Ii][Nn]32/ 27 | [Aa][Rr][Mm]/ 28 | [Aa][Rr][Mm]64/ 29 | bld/ 30 | [Bb]in/ 31 | [Oo]bj/ 32 | [Oo]ut/ 33 | [Ll]og/ 34 | [Ll]ogs/ 35 | 36 | # Visual Studio 2015/2017 cache/options directory 37 | .vs/ 38 | # Uncomment if you have tasks that create the project's static files in wwwroot 39 | #wwwroot/ 40 | 41 | # Visual Studio 2017 auto generated files 42 | Generated\ Files/ 43 | 44 | # MSTest test Results 45 | [Tt]est[Rr]esult*/ 46 | [Bb]uild[Ll]og.* 47 | 48 | # NUnit 49 | *.VisualState.xml 50 | TestResult.xml 51 | nunit-*.xml 52 | 53 | # Build Results of an ATL Project 54 | [Dd]ebugPS/ 55 | [Rr]eleasePS/ 56 | dlldata.c 57 | 58 | # Benchmark Results 59 | BenchmarkDotNet.Artifacts/ 60 | 61 | # .NET Core 62 | project.lock.json 63 | project.fragment.lock.json 64 | artifacts/ 65 | 66 | # ASP.NET Scaffolding 67 | ScaffoldingReadMe.txt 68 | 69 | # StyleCop 70 | StyleCopReport.xml 71 | 72 | # Files built by Visual Studio 73 | *_i.c 74 | *_p.c 75 | *_h.h 76 | *.ilk 77 | *.meta 78 | *.obj 79 | *.iobj 80 | *.pch 81 | *.pdb 82 | *.ipdb 83 | *.pgc 84 | *.pgd 85 | *.rsp 86 | *.sbr 87 | *.tlb 88 | *.tli 89 | *.tlh 90 | *.tmp 91 | *.tmp_proj 92 | *_wpftmp.csproj 93 | *.log 94 | *.vspscc 95 | *.vssscc 96 | .builds 97 | *.pidb 98 | *.svclog 99 | *.scc 100 | 101 | # Chutzpah Test files 102 | _Chutzpah* 103 | 104 | # Visual C++ cache files 105 | ipch/ 106 | *.aps 107 | *.ncb 108 | *.opendb 109 | *.opensdf 110 | *.sdf 111 | *.cachefile 112 | *.VC.db 113 | *.VC.VC.opendb 114 | 115 | # Visual Studio profiler 116 | *.psess 117 | *.vsp 118 | *.vspx 119 | *.sap 120 | 121 | # Visual Studio Trace Files 122 | *.e2e 123 | 124 | # TFS 2012 Local Workspace 125 | $tf/ 126 | 127 | # Guidance Automation Toolkit 128 | *.gpState 129 | 130 | # ReSharper is a .NET coding add-in 131 | _ReSharper*/ 132 | *.[Rr]e[Ss]harper 133 | *.DotSettings.user 134 | 135 | # TeamCity is a build add-in 136 | _TeamCity* 137 | 138 | # DotCover is a Code Coverage Tool 139 | *.dotCover 140 | 141 | # AxoCover is a Code Coverage Tool 142 | .axoCover/* 143 | !.axoCover/settings.json 144 | 145 | # Coverlet is a free, cross platform Code Coverage Tool 146 | coverage*.json 147 | coverage*.xml 148 | coverage*.info 149 | 150 | # Visual Studio code coverage results 151 | *.coverage 152 | *.coveragexml 153 | 154 | # NCrunch 155 | _NCrunch_* 156 | .*crunch*.local.xml 157 | nCrunchTemp_* 158 | 159 | # MightyMoose 160 | *.mm.* 161 | AutoTest.Net/ 162 | 163 | # Web workbench (sass) 164 | .sass-cache/ 165 | 166 | # Installshield output folder 167 | [Ee]xpress/ 168 | 169 | # DocProject is a documentation generator add-in 170 | DocProject/buildhelp/ 171 | DocProject/Help/*.HxT 172 | DocProject/Help/*.HxC 173 | DocProject/Help/*.hhc 174 | DocProject/Help/*.hhk 175 | DocProject/Help/*.hhp 176 | DocProject/Help/Html2 177 | DocProject/Help/html 178 | 179 | # Click-Once directory 180 | publish/ 181 | 182 | # Publish Web Output 183 | *.[Pp]ublish.xml 184 | *.azurePubxml 185 | # Note: Comment the next line if you want to checkin your web deploy settings, 186 | # but database connection strings (with potential passwords) will be unencrypted 187 | *.pubxml 188 | *.publishproj 189 | 190 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 191 | # checkin your Azure Web App publish settings, but sensitive information contained 192 | # in these scripts will be unencrypted 193 | PublishScripts/ 194 | 195 | # NuGet Packages 196 | *.nupkg 197 | # NuGet Symbol Packages 198 | *.snupkg 199 | # The packages folder can be ignored because of Package Restore 200 | **/[Pp]ackages/* 201 | # except build/, which is used as an MSBuild target. 202 | !**/[Pp]ackages/build/ 203 | # Uncomment if necessary however generally it will be regenerated when needed 204 | #!**/[Pp]ackages/repositories.config 205 | # NuGet v3's project.json files produces more ignorable files 206 | *.nuget.props 207 | *.nuget.targets 208 | 209 | # Microsoft Azure Build Output 210 | csx/ 211 | *.build.csdef 212 | 213 | # Microsoft Azure Emulator 214 | ecf/ 215 | rcf/ 216 | 217 | # Windows Store app package directories and files 218 | AppPackages/ 219 | BundleArtifacts/ 220 | Package.StoreAssociation.xml 221 | _pkginfo.txt 222 | *.appx 223 | *.appxbundle 224 | *.appxupload 225 | 226 | # Visual Studio cache files 227 | # files ending in .cache can be ignored 228 | *.[Cc]ache 229 | # but keep track of directories ending in .cache 230 | !?*.[Cc]ache/ 231 | 232 | # Others 233 | ClientBin/ 234 | ~$* 235 | *~ 236 | *.dbmdl 237 | *.dbproj.schemaview 238 | *.jfm 239 | *.pfx 240 | *.publishsettings 241 | orleans.codegen.cs 242 | 243 | # Including strong name files can present a security risk 244 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 245 | #*.snk 246 | 247 | # Since there are multiple workflows, uncomment next line to ignore bower_components 248 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 249 | #bower_components/ 250 | 251 | # RIA/Silverlight projects 252 | Generated_Code/ 253 | 254 | # Backup & report files from converting an old project file 255 | # to a newer Visual Studio version. Backup files are not needed, 256 | # because we have git ;-) 257 | _UpgradeReport_Files/ 258 | Backup*/ 259 | UpgradeLog*.XML 260 | UpgradeLog*.htm 261 | ServiceFabricBackup/ 262 | *.rptproj.bak 263 | 264 | # SQL Server files 265 | *.mdf 266 | *.ldf 267 | *.ndf 268 | 269 | # Business Intelligence projects 270 | *.rdl.data 271 | *.bim.layout 272 | *.bim_*.settings 273 | *.rptproj.rsuser 274 | *- [Bb]ackup.rdl 275 | *- [Bb]ackup ([0-9]).rdl 276 | *- [Bb]ackup ([0-9][0-9]).rdl 277 | 278 | # Microsoft Fakes 279 | FakesAssemblies/ 280 | 281 | # GhostDoc plugin setting file 282 | *.GhostDoc.xml 283 | 284 | # Node.js Tools for Visual Studio 285 | .ntvs_analysis.dat 286 | node_modules/ 287 | 288 | # Visual Studio 6 build log 289 | *.plg 290 | 291 | # Visual Studio 6 workspace options file 292 | *.opt 293 | 294 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 295 | *.vbw 296 | 297 | # Visual Studio LightSwitch build output 298 | **/*.HTMLClient/GeneratedArtifacts 299 | **/*.DesktopClient/GeneratedArtifacts 300 | **/*.DesktopClient/ModelManifest.xml 301 | **/*.Server/GeneratedArtifacts 302 | **/*.Server/ModelManifest.xml 303 | _Pvt_Extensions 304 | 305 | # Paket dependency manager 306 | .paket/paket.exe 307 | paket-files/ 308 | 309 | # FAKE - F# Make 310 | .fake/ 311 | 312 | # CodeRush personal settings 313 | .cr/personal 314 | 315 | # Python Tools for Visual Studio (PTVS) 316 | __pycache__/ 317 | *.pyc 318 | 319 | # Cake - Uncomment if you are using it 320 | # tools/** 321 | # !tools/packages.config 322 | 323 | # Tabs Studio 324 | *.tss 325 | 326 | # Telerik's JustMock configuration file 327 | *.jmconfig 328 | 329 | # BizTalk build output 330 | *.btp.cs 331 | *.btm.cs 332 | *.odx.cs 333 | *.xsd.cs 334 | 335 | # OpenCover UI analysis results 336 | OpenCover/ 337 | 338 | # Azure Stream Analytics local run output 339 | ASALocalRun/ 340 | 341 | # MSBuild Binary and Structured Log 342 | *.binlog 343 | 344 | # NVidia Nsight GPU debugger configuration file 345 | *.nvuser 346 | 347 | # MFractors (Xamarin productivity tool) working folder 348 | .mfractor/ 349 | 350 | # Local History for Visual Studio 351 | .localhistory/ 352 | 353 | # BeatPulse healthcheck temp database 354 | healthchecksdb 355 | 356 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 357 | MigrationBackup/ 358 | 359 | # Ionide (cross platform F# VS Code tools) working folder 360 | .ionide/ 361 | 362 | # Fody - auto-generated XML schema 363 | FodyWeavers.xsd -------------------------------------------------------------------------------- /sh-fixed-edition/sh-fixed-edition.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 17.0 23 | Win32Proj 24 | {9f6dcbd1-f175-4290-bf84-1f4d57776ada} 25 | shfixededition 26 | 10.0 27 | 28 | 29 | 30 | DynamicLibrary 31 | true 32 | v143 33 | Unicode 34 | 35 | 36 | DynamicLibrary 37 | false 38 | v143 39 | true 40 | Unicode 41 | 42 | 43 | DynamicLibrary 44 | true 45 | v143 46 | Unicode 47 | 48 | 49 | DynamicLibrary 50 | false 51 | v143 52 | true 53 | Unicode 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | Level3 76 | true 77 | WIN32;_DEBUG;SHFIXEDEDITION_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) 78 | true 79 | Use 80 | pch.h 81 | 82 | 83 | Windows 84 | true 85 | false 86 | 87 | 88 | 89 | 90 | Level3 91 | true 92 | true 93 | true 94 | WIN32;NDEBUG;SHFIXEDEDITION_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) 95 | true 96 | Use 97 | pch.h 98 | 99 | 100 | Windows 101 | true 102 | false 103 | 104 | 105 | 106 | 107 | Level3 108 | true 109 | _DEBUG;SHFIXEDEDITION_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) 110 | true 111 | Use 112 | pch.h 113 | 114 | 115 | Windows 116 | true 117 | false 118 | 119 | 120 | 121 | 122 | Level3 123 | true 124 | true 125 | true 126 | NDEBUG;SHFIXEDEDITION_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) 127 | true 128 | Use 129 | pch.h 130 | 131 | 132 | Windows 133 | true 134 | false 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | Create 148 | Create 149 | Create 150 | Create 151 | 152 | 153 | 154 | 155 | 156 | -------------------------------------------------------------------------------- /sh-fixed-edition/MemAccess.h: -------------------------------------------------------------------------------- 1 | /** 2 | * SADX Mod Loader. 3 | * Memory access inline functions. 4 | */ 5 | 6 | #ifndef MODLOADER_MEMACCESS_H 7 | #define MODLOADER_MEMACCESS_H 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | // Utility Functions 14 | 15 | /** 16 | * Get the number of elements in an array. 17 | * @return Number of elements in the array. 18 | */ 19 | template 20 | static constexpr Tret LengthOfArray(const T(&)[N]) noexcept 21 | { 22 | return (Tret)N; 23 | } 24 | 25 | /** 26 | * Get the size of an array. 27 | * @return Size of the array, in bytes. 28 | */ 29 | template 30 | static constexpr Tret SizeOfArray(const T(&)[N]) noexcept 31 | { 32 | return (Tret)(N * sizeof(T)); 33 | } 34 | 35 | // Macros for functions that need both an array 36 | // and the array length or size. 37 | #define arrayptrandlengthT(data,T) data, LengthOfArray(data) 38 | #define arraylengthandptrT(data,T) LengthOfArray(data), data 39 | #define arrayptrandsizeT(data,T) data, SizeOfArray(data) 40 | #define arraysizeandptrT(data,T) SizeOfArray(data), data 41 | 42 | // Macros for functions that need both an array 43 | // and the array length or size. 44 | #define arrayptrandlength(data) data, LengthOfArray(data) 45 | #define arraylengthandptr(data) LengthOfArray(data), data 46 | #define arrayptrandsize(data) data, SizeOfArray(data) 47 | #define arraysizeandptr(data) SizeOfArray(data), data 48 | 49 | #ifndef WIN32_LEAN_AND_MEAN 50 | #define WIN32_LEAN_AND_MEAN 51 | #endif 52 | #include 53 | 54 | static inline BOOL WriteData(void *writeaddress, const void *data, SIZE_T datasize) 55 | { 56 | DWORD oldprot; 57 | VirtualProtect(writeaddress, datasize, PAGE_EXECUTE_WRITECOPY, &oldprot); 58 | memcpy(writeaddress, data, datasize); 59 | return true; 60 | } 61 | 62 | template 63 | static inline BOOL WriteData(T const *writeaddress, const T data) 64 | { 65 | return WriteData((void*)writeaddress, (void*)&data, (SIZE_T)sizeof(data)); 66 | } 67 | 68 | template 69 | static inline BOOL WriteData(T *writeaddress, const T &data) 70 | { 71 | return WriteData(writeaddress, &data, sizeof(data)); 72 | } 73 | 74 | template 75 | static inline BOOL WriteData(void *writeaddress, const T(&data)[N]) 76 | { 77 | return WriteData(writeaddress, data, SizeOfArray(data)); 78 | } 79 | 80 | /** 81 | * Write a repeated byte to an arbitrary address. 82 | * @param address [in] Address. 83 | * @param data [in] Byte to write. 84 | * @param byteswritten [out, opt] Number of bytes written. 85 | * @return Nonzero on success; 0 on error (check GetLastError()). 86 | */ 87 | template 88 | static inline BOOL WriteData(void *address, uint8_t data) 89 | { 90 | DWORD oldprot; 91 | VirtualProtect(address, count, PAGE_EXECUTE_WRITECOPY, &oldprot); 92 | memset(address, data, count); 93 | return true; 94 | } 95 | 96 | #if (defined(__i386__) || defined(_M_IX86)) && \ 97 | !(defined(__x86_64__) || defined(_M_X64)) 98 | 99 | // JMP/CALL DWORD relative opcode union. 100 | #pragma pack(1) 101 | union JmpCallDwordRel { 102 | struct { 103 | uint8_t opcode; 104 | int32_t address; 105 | }; 106 | uint8_t u8[5]; 107 | 108 | JmpCallDwordRel() {} 109 | 110 | JmpCallDwordRel(bool isCall, intptr_t src, intptr_t dst) 111 | { 112 | opcode = isCall ? 0xE8 : 0xE9; 113 | address = dst - (src + 5); 114 | } 115 | 116 | JmpCallDwordRel(bool isCall, void* src, void* dst) 117 | { 118 | opcode = isCall ? 0xE8 : 0xE9; 119 | address = (intptr_t)dst - ((intptr_t)src + 5); 120 | } 121 | }; 122 | #pragma pack() 123 | 124 | /** 125 | * Write a JMP instruction to an arbitrary address. 126 | * @param writeaddress Address to insert the JMP instruction. 127 | * @param funcaddress Address to JMP to. 128 | * @return Nonzero on success; 0 on error (check GetLastError()). 129 | */ 130 | static inline BOOL WriteJump(void *writeaddress, void *funcaddress) 131 | { 132 | JmpCallDwordRel data(false, writeaddress, funcaddress); 133 | return WriteData(writeaddress, data.u8); 134 | } 135 | 136 | /** 137 | * Write a CALL instruction to an arbitrary address. 138 | * @param writeaddress Address to insert the CALL instruction. 139 | * @param funcaddress Address to CALL. 140 | * @return Nonzero on success; 0 on error (check GetLastError()). 141 | */ 142 | static inline BOOL WriteCall(void *writeaddress, void *funcaddress) 143 | { 144 | JmpCallDwordRel data(true, writeaddress, funcaddress); 145 | return WriteData(writeaddress, data.u8); 146 | } 147 | 148 | #endif 149 | 150 | // Data pointer and array declarations. 151 | #define DataPointer(type, name, address) \ 152 | static type &name = *(type *)address 153 | #define DataArray(type, name, address, len) \ 154 | static DataArray_t name 155 | 156 | template 157 | struct DataArray_t final 158 | { 159 | typedef T value_type; 160 | typedef size_t size_type; 161 | typedef ptrdiff_t difference_type; 162 | typedef value_type& reference; 163 | typedef const value_type& const_reference; 164 | typedef value_type* pointer; 165 | typedef const value_type* const_pointer; 166 | typedef pointer iterator; 167 | typedef const_pointer const_iterator; 168 | typedef std::reverse_iterator reverse_iterator; 169 | typedef std::reverse_iterator const_reverse_iterator; 170 | 171 | DataArray_t() = default; // have to declare default constructor 172 | DataArray_t(const DataArray_t&) = delete; // object cannot be copied, prevents accidentally using DataArray in a function call 173 | DataArray_t(const DataArray_t&&) = delete; // object cannot be moved 174 | 175 | // Gets the underlying data for the array. 176 | constexpr pointer data() const noexcept { return reinterpret_cast(addr); } 177 | // Gets the underlying data for the array. 178 | constexpr const_pointer cdata() const noexcept { return reinterpret_cast(addr); } 179 | 180 | // Checks if the array is empty (no elements). 181 | constexpr bool empty() const noexcept { return len == 0; } 182 | 183 | // Gets the size of the array, in elements. 184 | constexpr size_type size() const noexcept { return len; } 185 | 186 | // Gets the maximum size of the array, in elements. 187 | constexpr size_type max_size() const noexcept { return len; } 188 | 189 | constexpr pointer operator&() const noexcept { return data(); } 190 | 191 | constexpr operator pointer() const noexcept { return data(); } 192 | 193 | // Gets an item from the array, with bounds checking. 194 | constexpr reference at(size_type i) 195 | { 196 | if (i < len) 197 | return data()[i]; 198 | throw std::out_of_range("Data access out of range."); 199 | } 200 | 201 | // Gets an item from the array, with bounds checking. 202 | constexpr const_reference at(size_type i) const 203 | { 204 | if (i < len) 205 | return cdata()[i]; 206 | throw std::out_of_range("Data access out of range."); 207 | } 208 | 209 | template 210 | // Gets an item from the array, with compile-time bounds checking. 211 | constexpr reference get() noexcept 212 | { 213 | static_assert(I < len, "index is within bounds"); 214 | return data()[I]; 215 | } 216 | 217 | template 218 | // Gets an item from the array, with compile-time bounds checking. 219 | constexpr const_reference get() const noexcept 220 | { 221 | static_assert(I < len, "index is within bounds"); 222 | return cdata()[I]; 223 | } 224 | 225 | // Gets the first item in the array. 226 | constexpr reference front() { return *data(); } 227 | // Gets the first item in the array. 228 | constexpr const_reference front() const { return *cdata(); } 229 | 230 | // Gets the last item in the array. 231 | constexpr reference back() { return data()[len - 1]; } 232 | // Gets the last item in the array. 233 | constexpr const_reference back() const { return cdata()[len - 1]; } 234 | 235 | // Gets an iterator to the beginning of the array. 236 | constexpr iterator begin() noexcept { return data(); } 237 | // Gets an iterator to the beginning of the array. 238 | constexpr const_iterator begin() const noexcept { return cdata(); } 239 | // Gets an iterator to the beginning of the array. 240 | constexpr const_iterator cbegin() const noexcept { return cdata(); } 241 | 242 | // Gets an iterator to the end of the array. 243 | constexpr iterator end() noexcept { return data() + len; } 244 | // Gets an iterator to the end of the array. 245 | constexpr const_iterator end() const noexcept { return cdata() + len; } 246 | // Gets an iterator to the end of the array. 247 | constexpr const_iterator cend() const noexcept { return cdata() + len; } 248 | 249 | // Gets a reverse iterator to the beginning of the array. 250 | constexpr reverse_iterator rbegin() noexcept { return data() + len; } 251 | // Gets a reverse iterator to the beginning of the array. 252 | constexpr const_reverse_iterator rbegin() const noexcept { return cdata() + len; } 253 | // Gets a reverse iterator to the beginning of the array. 254 | constexpr const_reverse_iterator crbegin() const noexcept { return cdata() + len; } 255 | 256 | // Gets a reverse iterator to the end of the array. 257 | constexpr reverse_iterator rend() noexcept { return data(); } 258 | // Gets a reverse iterator to the end of the array. 259 | constexpr const_reverse_iterator rend() const noexcept { return cdata(); } 260 | // Gets a reverse iterator to the end of the array. 261 | constexpr const_reverse_iterator crend() const noexcept { return cdata(); } 262 | }; 263 | 264 | // Function pointer declarations. 265 | #define FunctionPointer(RETURN_TYPE, NAME, ARGS, ADDRESS) \ 266 | static RETURN_TYPE (__cdecl *const NAME)ARGS = (RETURN_TYPE (__cdecl *)ARGS)ADDRESS 267 | #define StdcallFunctionPointer(RETURN_TYPE, NAME, ARGS, ADDRESS) \ 268 | static RETURN_TYPE (__stdcall *const NAME)ARGS = (RETURN_TYPE (__stdcall *)ARGS)ADDRESS 269 | #define FastcallFunctionPointer(RETURN_TYPE, NAME, ARGS, ADDRESS) \ 270 | static RETURN_TYPE (__fastcall *const NAME)ARGS = (RETURN_TYPE (__fastcall *)ARGS)ADDRESS 271 | #define ThiscallFunctionPointer(RETURN_TYPE, NAME, ARGS, ADDRESS) \ 272 | static RETURN_TYPE (__thiscall *const NAME)ARGS = (RETURN_TYPE (__thiscall *)ARGS)ADDRESS 273 | #define VoidFunc(NAME, ADDRESS) FunctionPointer(void,NAME,(void),ADDRESS) 274 | 275 | // Non-static FunctionPointer. 276 | // If declaring a FunctionPointer within a function, use this one instead. 277 | // Otherwise, the program will crash on Windows XP. 278 | #define NonStaticFunctionPointer(RETURN_TYPE, NAME, ARGS, ADDRESS) \ 279 | RETURN_TYPE (__cdecl *const NAME)ARGS = (RETURN_TYPE (__cdecl *)ARGS)ADDRESS 280 | 281 | #define patchdecl(address,data) { (void*)address, arrayptrandsize(data) } 282 | #define ptrdecl(address,data) { (void*)address, (void*)data } 283 | 284 | #endif /* MODLOADER_MEMACCESS_H */ 285 | -------------------------------------------------------------------------------- /sh-fixed-edition/FunctionHook.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "MemAccess.h" 3 | 4 | // Class defining a function that can be hooked and unhooked dynamically. 5 | template 6 | class FunctionHook 7 | { 8 | public: 9 | typedef TRet(*FuncType)(TArgs...); 10 | 11 | FunctionHook(FuncType address) : origaddr(address) { } 12 | 13 | FunctionHook(intptr_t address) : origaddr(reinterpret_cast(address)) { } 14 | 15 | // Initialize the object and immediately apply a hook. 16 | FunctionHook(FuncType address, FuncType hook) : FunctionHook(address) 17 | { 18 | Hook(hook); 19 | } 20 | 21 | // Initialize the object and immediately apply a hook. 22 | FunctionHook(intptr_t address, FuncType hook) : FunctionHook(address) 23 | { 24 | Hook(hook); 25 | } 26 | 27 | FunctionHook() = delete; 28 | FunctionHook(FunctionHook&) = delete; 29 | FunctionHook(FunctionHook&&) = delete; 30 | 31 | // Apply a hook to the unhooked function. 32 | void Hook(FuncType hook) 33 | { 34 | if (hookaddr) 35 | throw new std::exception("Cannot apply hook to already hooked function!"); 36 | memcpy(origbytes, origaddr, 5); 37 | hookaddr = hook; 38 | WriteJump(origaddr, hook); 39 | } 40 | 41 | // Call the original function, bypassing the hook. 42 | TRet Original(TArgs... args) 43 | { 44 | if (hookaddr) 45 | { 46 | uint8_t hookbytes[5]; 47 | memcpy(hookbytes, origaddr, 5); 48 | memcpy(origaddr, origbytes, 5); 49 | TRet retval = origaddr(args...); 50 | memcpy(origaddr, hookbytes, 5); 51 | return retval; 52 | } 53 | else 54 | return origaddr(args...); 55 | } 56 | 57 | // Get the address of the function this object is attached to. 58 | FuncType GetFunctionAddress() 59 | { 60 | return origaddr; 61 | } 62 | 63 | // Get the address of the currently applied hook. 64 | FuncType GetCurrentHook() 65 | { 66 | return hookaddr; 67 | } 68 | 69 | private: 70 | uint8_t origbytes[5]; 71 | const FuncType origaddr; 72 | FuncType hookaddr = nullptr; 73 | }; 74 | 75 | // Class defining a function that can be hooked and unhooked dynamically. 76 | template 77 | class FunctionHook 78 | { 79 | public: 80 | typedef void(*FuncType)(TArgs...); 81 | 82 | FunctionHook(FuncType address) : origaddr(address) { } 83 | 84 | FunctionHook(intptr_t address) : origaddr(reinterpret_cast(address)) { } 85 | 86 | // Initialize the object and immediately apply a hook. 87 | FunctionHook(FuncType address, FuncType hook) : FunctionHook(address) 88 | { 89 | Hook(hook); 90 | } 91 | 92 | // Initialize the object and immediately apply a hook. 93 | FunctionHook(intptr_t address, FuncType hook) : FunctionHook(address) 94 | { 95 | Hook(hook); 96 | } 97 | 98 | FunctionHook() = delete; 99 | FunctionHook(FunctionHook&) = delete; 100 | FunctionHook(FunctionHook&&) = delete; 101 | 102 | // Apply a hook to the unhooked function. 103 | void Hook(FuncType hook) 104 | { 105 | if (hookaddr) 106 | throw new std::exception("Cannot apply hook to already hooked function!"); 107 | memcpy(origbytes, origaddr, 5); 108 | hookaddr = hook; 109 | WriteJump(origaddr, hook); 110 | } 111 | 112 | // Call the original function, bypassing the hook. 113 | void Original(TArgs... args) 114 | { 115 | if (hookaddr) 116 | { 117 | uint8_t hookbytes[5]; 118 | memcpy(hookbytes, origaddr, 5); 119 | memcpy(origaddr, origbytes, 5); 120 | origaddr(args...); 121 | memcpy(origaddr, hookbytes, 5); 122 | } 123 | else 124 | origaddr(args...); 125 | } 126 | 127 | // Get the address of the function this object is attached to. 128 | constexpr FuncType GetFunctionAddress() 129 | { 130 | return origaddr; 131 | } 132 | 133 | // Get the address of the currently applied hook. 134 | FuncType GetCurrentHook() 135 | { 136 | return hookaddr; 137 | } 138 | 139 | private: 140 | uint8_t origbytes[5]; 141 | const FuncType origaddr; 142 | FuncType hookaddr = nullptr; 143 | }; 144 | 145 | // Class defining a function that can be hooked and unhooked dynamically. 146 | template 147 | class StdcallFunctionHook 148 | { 149 | public: 150 | typedef TRet(__stdcall* FuncType)(TArgs...); 151 | 152 | StdcallFunctionHook(FuncType address) : origaddr(address) { } 153 | 154 | StdcallFunctionHook(intptr_t address) : origaddr(reinterpret_cast(address)) { } 155 | 156 | // Initialize the object and immediately apply a hook. 157 | StdcallFunctionHook(FuncType address, FuncType hook) : StdcallFunctionHook(address) 158 | { 159 | Hook(hook); 160 | } 161 | 162 | // Initialize the object and immediately apply a hook. 163 | StdcallFunctionHook(intptr_t address, FuncType hook) : StdcallFunctionHook(address) 164 | { 165 | Hook(hook); 166 | } 167 | 168 | StdcallFunctionHook() = delete; 169 | StdcallFunctionHook(StdcallFunctionHook&) = delete; 170 | StdcallFunctionHook(StdcallFunctionHook&&) = delete; 171 | 172 | // Apply a hook to the unhooked function. 173 | void Hook(FuncType hook) 174 | { 175 | if (hookaddr) 176 | throw new std::exception("Cannot apply hook to already hooked function!"); 177 | memcpy(origbytes, origaddr, 5); 178 | hookaddr = hook; 179 | WriteJump(origaddr, hook); 180 | } 181 | 182 | // Call the original function, bypassing the hook. 183 | TRet Original(TArgs... args) 184 | { 185 | if (hookaddr) 186 | { 187 | uint8_t hookbytes[5]; 188 | memcpy(hookbytes, origaddr, 5); 189 | memcpy(origaddr, origbytes, 5); 190 | TRet retval = origaddr(args...); 191 | memcpy(origaddr, hookbytes, 5); 192 | return retval; 193 | } 194 | else 195 | return origaddr(args...); 196 | } 197 | 198 | // Get the address of the function this object is attached to. 199 | constexpr FuncType GetFunctionAddress() 200 | { 201 | return origaddr; 202 | } 203 | 204 | // Get the address of the currently applied hook. 205 | FuncType GetCurrentHook() 206 | { 207 | return hookaddr; 208 | } 209 | 210 | private: 211 | uint8_t origbytes[5]; 212 | const FuncType origaddr; 213 | FuncType hookaddr = nullptr; 214 | }; 215 | 216 | // Class defining a function that can be hooked and unhooked dynamically. 217 | template 218 | class StdcallFunctionHook 219 | { 220 | public: 221 | typedef void(__stdcall* FuncType)(TArgs...); 222 | 223 | StdcallFunctionHook(FuncType address) : origaddr(address) { } 224 | 225 | StdcallFunctionHook(intptr_t address) : origaddr(reinterpret_cast(address)) { } 226 | 227 | // Initialize the object and immediately apply a hook. 228 | StdcallFunctionHook(FuncType address, FuncType hook) : StdcallFunctionHook(address) 229 | { 230 | Hook(hook); 231 | } 232 | 233 | // Initialize the object and immediately apply a hook. 234 | StdcallFunctionHook(intptr_t address, FuncType hook) : StdcallFunctionHook(address) 235 | { 236 | Hook(hook); 237 | } 238 | 239 | StdcallFunctionHook() = delete; 240 | StdcallFunctionHook(StdcallFunctionHook&) = delete; 241 | StdcallFunctionHook(StdcallFunctionHook&&) = delete; 242 | 243 | // Apply a hook to the unhooked function. 244 | void Hook(FuncType hook) 245 | { 246 | if (hookaddr) 247 | throw new std::exception("Cannot apply hook to already hooked function!"); 248 | memcpy(origbytes, origaddr, 5); 249 | hookaddr = hook; 250 | WriteJump(origaddr, hook); 251 | } 252 | 253 | // Call the original function, bypassing the hook. 254 | void Original(TArgs... args) 255 | { 256 | if (hookaddr) 257 | { 258 | uint8_t hookbytes[5]; 259 | memcpy(hookbytes, origaddr, 5); 260 | memcpy(origaddr, origbytes, 5); 261 | origaddr(args...); 262 | memcpy(origaddr, hookbytes, 5); 263 | } 264 | else 265 | origaddr(args...); 266 | } 267 | 268 | // Get the address of the function this object is attached to. 269 | constexpr FuncType GetFunctionAddress() 270 | { 271 | return origaddr; 272 | } 273 | 274 | // Get the address of the currently applied hook. 275 | FuncType GetCurrentHook() 276 | { 277 | return hookaddr; 278 | } 279 | 280 | private: 281 | uint8_t origbytes[5]; 282 | const FuncType origaddr; 283 | FuncType hookaddr = nullptr; 284 | }; 285 | 286 | // Class defining a function that can be hooked and unhooked dynamically. 287 | template 288 | class FastcallFunctionHook 289 | { 290 | public: 291 | typedef TRet(__fastcall* FuncType)(TArgs...); 292 | 293 | FastcallFunctionHook(FuncType address) : origaddr(address) { } 294 | 295 | FastcallFunctionHook(intptr_t address) : origaddr(reinterpret_cast(address)) { } 296 | 297 | // Initialize the object and immediately apply a hook. 298 | FastcallFunctionHook(FuncType address, FuncType hook) : FastcallFunctionHook(address) 299 | { 300 | Hook(hook); 301 | } 302 | 303 | // Initialize the object and immediately apply a hook. 304 | FastcallFunctionHook(intptr_t address, FuncType hook) : FastcallFunctionHook(address) 305 | { 306 | Hook(hook); 307 | } 308 | 309 | FastcallFunctionHook() = delete; 310 | FastcallFunctionHook(FastcallFunctionHook&) = delete; 311 | FastcallFunctionHook(FastcallFunctionHook&&) = delete; 312 | 313 | // Apply a hook to the unhooked function. 314 | void Hook(FuncType hook) 315 | { 316 | if (hookaddr) 317 | throw new std::exception("Cannot apply hook to already hooked function!"); 318 | memcpy(origbytes, origaddr, 5); 319 | hookaddr = hook; 320 | WriteJump(origaddr, hook); 321 | } 322 | 323 | // Call the original function, bypassing the hook. 324 | TRet Original(TArgs... args) 325 | { 326 | if (hookaddr) 327 | { 328 | uint8_t hookbytes[5]; 329 | memcpy(hookbytes, origaddr, 5); 330 | memcpy(origaddr, origbytes, 5); 331 | TRet retval = origaddr(args...); 332 | memcpy(origaddr, hookbytes, 5); 333 | return retval; 334 | } 335 | else 336 | return origaddr(args...); 337 | } 338 | 339 | // Get the address of the function this object is attached to. 340 | constexpr FuncType GetFunctionAddress() 341 | { 342 | return origaddr; 343 | } 344 | 345 | // Get the address of the currently applied hook. 346 | FuncType GetCurrentHook() 347 | { 348 | return hookaddr; 349 | } 350 | 351 | private: 352 | uint8_t origbytes[5]; 353 | const FuncType origaddr; 354 | FuncType hookaddr = nullptr; 355 | }; 356 | 357 | // Class defining a function that can be hooked and unhooked dynamically. 358 | template 359 | class FastcallFunctionHook 360 | { 361 | public: 362 | typedef void(__fastcall* FuncType)(TArgs...); 363 | 364 | FastcallFunctionHook(FuncType address) : origaddr(address) { } 365 | 366 | FastcallFunctionHook(intptr_t address) : origaddr(reinterpret_cast(address)) { } 367 | 368 | // Initialize the object and immediately apply a hook. 369 | FastcallFunctionHook(FuncType address, FuncType hook) : FastcallFunctionHook(address) 370 | { 371 | Hook(hook); 372 | } 373 | 374 | // Initialize the object and immediately apply a hook. 375 | FastcallFunctionHook(intptr_t address, FuncType hook) : FastcallFunctionHook(address) 376 | { 377 | Hook(hook); 378 | } 379 | 380 | FastcallFunctionHook() = delete; 381 | FastcallFunctionHook(FastcallFunctionHook&) = delete; 382 | FastcallFunctionHook(FastcallFunctionHook&&) = delete; 383 | 384 | // Apply a hook to the unhooked function. 385 | void Hook(FuncType hook) 386 | { 387 | if (hookaddr) 388 | throw new std::exception("Cannot apply hook to already hooked function!"); 389 | memcpy(origbytes, origaddr, 5); 390 | hookaddr = hook; 391 | WriteJump(origaddr, hook); 392 | } 393 | 394 | // Call the original function, bypassing the hook. 395 | void Original(TArgs... args) 396 | { 397 | if (hookaddr) 398 | { 399 | uint8_t hookbytes[5]; 400 | memcpy(hookbytes, origaddr, 5); 401 | memcpy(origaddr, origbytes, 5); 402 | origaddr(args...); 403 | memcpy(origaddr, hookbytes, 5); 404 | } 405 | else 406 | origaddr(args...); 407 | } 408 | 409 | // Get the address of the function this object is attached to. 410 | constexpr FuncType GetFunctionAddress() 411 | { 412 | return origaddr; 413 | } 414 | 415 | // Get the address of the currently applied hook. 416 | FuncType GetCurrentHook() 417 | { 418 | return hookaddr; 419 | } 420 | 421 | private: 422 | uint8_t origbytes[5]; 423 | const FuncType origaddr; 424 | FuncType hookaddr = nullptr; 425 | }; 426 | -------------------------------------------------------------------------------- /bridge/Publish.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Builds and Publishes a Reloaded II Mod 4 | .DESCRIPTION 5 | Windows script to Build and Publish a Reloaded Mod. 6 | By default, published items will be output to a directory called `Publish/ToUpload`. 7 | 8 | If you acquired this script by creating a new Reloaded Mod in VS. Then most likely everything 9 | (aside from delta updates) should be preconfigured here. 10 | 11 | .PARAMETER ProjectPath 12 | Path to the project to be built. 13 | Useful if using this script from another script for the purpose of building multiple mods. 14 | 15 | .PARAMETER PackageName 16 | Name of the package to be built. 17 | Affects the name of the output files of the publish. 18 | 19 | .PARAMETER PublishOutputDir 20 | Default: "Publish/ToUpload" 21 | Declares the directory for placing the output files. 22 | 23 | .PARAMETER BuildR2R 24 | Default: $False 25 | 26 | Builds the mod using an optimisation called `Ready to Run`, which sacrifices file size for potentially 27 | faster startup time. This is only worth enabling on mods with a lot of code, usually it is best left disabled. 28 | 29 | For more details see: https://docs.microsoft.com/en-us/dotnet/core/deploying/ready-to-run 30 | 31 | .PARAMETER ChangelogPath 32 | Full or relative path to a file containing the changelog for the mod. 33 | The changelog should be written in Markdown format. 34 | 35 | .PARAMETER ReadmePath 36 | Full or relative path to a file containing the changelog for the mod. 37 | The changelog should be written in Markdown format. 38 | 39 | .PARAMETER IsPrerelease 40 | Default: $False 41 | 42 | If set to true, the version downloaded for delta package generation will be the latest pre-release 43 | as opposed to the latest stable version. 44 | 45 | .PARAMETER MakeDelta 46 | Default: $False 47 | 48 | Set to true to create Delta packages. 49 | Usually this is true in a CI/CD environment when creating a release, else false in development. 50 | 51 | If this is true, you should set UseGitHubDelta, UseGameBananaDelta, UseNuGetDelta or equivalent to true. 52 | 53 | .PARAMETER MetadataFileName 54 | Default: Sewer56.Update.ReleaseMetadata.json 55 | Name of the release metadata file used to download the delta package. 56 | 57 | .PARAMETER UseGitHubDelta 58 | Default: $False 59 | If true, sources the last version of the package to publish from GitHub. 60 | 61 | .PARAMETER UseGameBananaDelta 62 | Default: $False 63 | If true, sources the last version of the package to publish from GameBanana. 64 | 65 | .PARAMETER UseNuGetDelta 66 | Default: $False 67 | If true, sources the last version of the package to publish from NuGet. 68 | 69 | .PARAMETER GitHubUserName 70 | [Use if UseGitHubDelta is true] 71 | Sets the username used for obtaining Deltas from GitHub. 72 | 73 | .PARAMETER GitHubRepoName 74 | [Use if UseGitHubDelta is true] 75 | Sets the repository used for obtaining Deltas from GitHub. 76 | 77 | .PARAMETER GitHubFallbackPattern 78 | [Use if UseGitHubDelta is true] 79 | Allows you to specify a Wildcard pattern (e.g. *Update.zip) for the file to be downloaded. 80 | This is a fallback used in cases no Release Metadata file can be found. 81 | 82 | .PARAMETER GitHubInheritVersionFromTag 83 | [Use if UseGitHubDelta is true] 84 | Uses version determined from release tag (in GitHub Releases) as opposed to the 85 | Release Metadata file in latest release. 86 | 87 | .PARAMETER GameBananaItemId 88 | [Use if UseGameBananaDelta is true] 89 | Example: 150118 90 | 91 | Unique identifier for the individual mod. This is the last number of a GameBanana Mod Page URL 92 | e.g. https://gamebanana.com/mods/150118 -> 150118 93 | 94 | .PARAMETER NuGetPackageId 95 | [Use if UseNuGetDelta is true] 96 | Example: reloaded.sharedlib.hooks 97 | 98 | The ID of the package to use as delta. 99 | 100 | .PARAMETER NuGetFeedUrl 101 | [Use if UseNuGetDelta is true] 102 | Example: http://packages.sewer56.moe:5000/v3/index.json 103 | 104 | The URL of the NuGet feed to download the delta from. 105 | 106 | .PARAMETER NuGetAllowUnlisted 107 | [Use if UseNuGetDelta is true] 108 | Default: $False 109 | 110 | Allows for the downloading of unlisted packages. 111 | 112 | .PARAMETER PublishGeneric 113 | Default: $True 114 | 115 | Publishes a generic package that can be uploaded to any other website. 116 | 117 | .PARAMETER PublishNuGet 118 | Default: $True 119 | 120 | Publishes a package that can be uploaded to any NuGet Source. 121 | 122 | .PARAMETER PublishGameBanana 123 | Default: $True 124 | 125 | Publishes a package that can be uploaded to GameBanana. 126 | 127 | .PARAMETER Build 128 | Default: $True 129 | 130 | Whether the project should be built. 131 | Setting this to false lets you use the publish part of the script standalone in a non .NET environment. 132 | 133 | .PARAMETER RemoveExe 134 | Default: $True 135 | 136 | Removes executables from build output. Useful when performing R2R Optimisation. 137 | 138 | .PARAMETER UseScriptDirectory 139 | Default: $True 140 | 141 | Uses script directory for performing build. Otherwise uses current directory. 142 | 143 | .EXAMPLE 144 | .\Publish.ps1 -ProjectPath "Reloaded.Hooks.ReloadedII/Reloaded.Hooks.ReloadedII.csproj" -PackageName "Reloaded.Hooks.ReloadedII" -PublishOutputDir "Publish/ToUpload" 145 | 146 | .EXAMPLE 147 | .\Publish.ps1 -MakeDelta true -BuildR2R true -UseGitHubDelta True 148 | 149 | .EXAMPLE 150 | .\Publish.ps1 -BuildR2R true 151 | 152 | #> 153 | [cmdletbinding()] 154 | param ( 155 | $IsPrerelease=$False, 156 | $MakeDelta=$False, 157 | $ChangelogPath="", 158 | $ReadmePath="", 159 | $Build=$True, 160 | $BuildR2R=$False, 161 | $RemoveExe=$True, 162 | $UseScriptDirectory=$True, 163 | 164 | ## => User Config <= ## 165 | $ProjectPath = "bridge.csproj", 166 | $PackageName = "bridge", 167 | $PublishOutputDir = "Publish/ToUpload", 168 | 169 | ## => User: Delta Config 170 | # Pick one and configure settings below. 171 | $MetadataFileName = "Sewer56.Update.ReleaseMetadata.json", 172 | $UseGitHubDelta = $False, # GitHub Releases 173 | $UseGameBananaDelta = $False, 174 | $UseNuGetDelta = $False, 175 | 176 | $GitHubUserName = "", # Name of the GitHub user where the mod is contained 177 | $GitHubRepoName = "", # Name of the GitHub repo where the mod is contained 178 | $GitHubFallbackPattern = "", # For migrating from legacy build script. 179 | $GitHubInheritVersionFromTag = $True, # Uses version determined from release tag as opposed to metadata file in latest release. 180 | 181 | $GameBananaItemId = 333681, # From mod page URL. 182 | 183 | $NuGetPackageId = "bridge", 184 | $NuGetFeedUrl = "http://packages.sewer56.moe:5000/v3/index.json", 185 | $NuGetAllowUnlisted = $False, 186 | 187 | ## => User: Publish Config 188 | $PublishGeneric = $True, 189 | $PublishNuGet = $True, 190 | $PublishGameBanana = $True 191 | ) 192 | 193 | ## => User: Publish Output 194 | $publishBuildDirectory = "Publish/Builds/CurrentVersion" # Build directory for current version of the mod. 195 | $deltaDirectory = "Publish/Builds/LastVersion" # Path to last version of the mod. 196 | 197 | $PublishGenericDirectory = "$PublishOutputDir/Generic" # Publish files for any target not listed below. 198 | $PublishNuGetDirectory = "$PublishOutputDir/NuGet" # Publish files for NuGet 199 | $PublishGameBananaDirectory = "$PublishOutputDir/GameBanana" # Publish files for GameBanana 200 | 201 | ## => User Config <= ## 202 | # Tools 203 | $reloadedToolsPath = "./Publish/Tools/Reloaded-Tools" # Used to check if tools are installed. 204 | $updateToolsPath = "./Publish/Tools/Update-Tools" # Used to check if update tools are installed. 205 | $reloadedToolPath = "$reloadedToolsPath/Reloaded.Publisher.exe" # Path to Reloaded publishing tool. 206 | $updateToolPath = "$updateToolsPath/Sewer56.Update.Tool.dll" # Path to Update tool. 207 | $changelogFullPath = $null 208 | $readmeFullPath = $null 209 | if ($ChangelogPath) { $changelogFullPath = [System.IO.Path]::GetFullPath($ChangelogPath) } 210 | if ($ReadmePath) { $readmeFullPath = [System.IO.Path]::GetFullPath($ReadmePath) } 211 | 212 | ## => Script <= ## 213 | # Set Working Directory 214 | $UseScriptDirectory = [bool]::Parse($UseScriptDirectory) 215 | if ($UseScriptDirectory) { 216 | Split-Path $MyInvocation.MyCommand.Path | Push-Location 217 | [Environment]::CurrentDirectory = $PWD 218 | } 219 | 220 | # Convert Booleans 221 | $IsPrerelease = [bool]::Parse($IsPrerelease) 222 | $MakeDelta = [bool]::Parse($MakeDelta) 223 | $Build = [bool]::Parse($Build) 224 | $BuildR2R = [bool]::Parse($BuildR2R) 225 | $RemoveExe = [bool]::Parse($RemoveExe) 226 | $UseGitHubDelta = [bool]::Parse($UseGitHubDelta) 227 | $UseGameBananaDelta = [bool]::Parse($UseGameBananaDelta) 228 | $UseNuGetDelta = [bool]::Parse($UseNuGetDelta) 229 | $NuGetAllowUnlisted = [bool]::Parse($NuGetAllowUnlisted) 230 | $PublishGeneric = [bool]::Parse($PublishGeneric) 231 | $PublishNuGet = [bool]::Parse($PublishNuGet) 232 | $PublishGameBanana = [bool]::Parse($PublishGameBanana) 233 | $GitHubInheritVersionFromTag = [bool]::Parse($GitHubInheritVersionFromTag) 234 | $TempDirectory = [System.IO.Path]::GetTempPath() + [System.IO.Path]::GetRandomFileName() 235 | $TempDirectoryBuild = "$TempDirectory/build" 236 | 237 | function Get-Tools { 238 | # Download Tools (if needed) 239 | $ProgressPreference = 'SilentlyContinue' 240 | if (-not(Test-Path -Path $reloadedToolsPath -PathType Any)) { 241 | Write-Host "Downloading Reloaded Tools" 242 | Invoke-WebRequest -Uri "https://github.com/Reloaded-Project/Reloaded-II/releases/latest/download/Tools.zip" -OutFile "$TempDirectory/Tools.zip" 243 | Expand-Archive -LiteralPath "$TempDirectory/Tools.zip" -DestinationPath $reloadedToolsPath 244 | 245 | # Remove Items 246 | Remove-Item "$TempDirectory/Tools.zip" -ErrorAction SilentlyContinue 247 | } 248 | 249 | if ($MakeDelta -and -not(Test-Path -Path $updateToolsPath -PathType Any)) { 250 | Write-Host "Downloading Update Library Tools" 251 | Invoke-WebRequest -Uri "https://github.com/Sewer56/Update/releases/latest/download/Sewer56.Update.Tool.zip" -OutFile "$TempDirectory/Sewer56.Update.Tool.zip" 252 | Expand-Archive -LiteralPath "$TempDirectory/Sewer56.Update.Tool.zip" -DestinationPath $updateToolsPath 253 | 254 | # Remove Items 255 | Remove-Item "$TempDirectory/Sewer56.Update.Tool.zip" -ErrorAction SilentlyContinue 256 | } 257 | } 258 | 259 | # Publish for targets 260 | function Build { 261 | # Clean anything in existing Release directory. 262 | Remove-Item $publishBuildDirectory -Recurse -ErrorAction SilentlyContinue 263 | New-Item $publishBuildDirectory -ItemType Directory -ErrorAction SilentlyContinue 264 | 265 | # Build 266 | dotnet restore $ProjectPath 267 | dotnet clean $ProjectPath 268 | 269 | if ($BuildR2R) { 270 | dotnet publish $ProjectPath -c Release -r win-x86 --self-contained false -o "$publishBuildDirectory/x86" /p:PublishReadyToRun=true /p:OutputPath="$TempDirectoryBuild/x86" 271 | dotnet publish $ProjectPath -c Release -r win-x64 --self-contained false -o "$publishBuildDirectory/x64" /p:PublishReadyToRun=true /p:OutputPath="$TempDirectoryBuild/x64" 272 | 273 | # Remove Redundant Files 274 | Move-Item -Path "$publishBuildDirectory/x86/ModConfig.json" -Destination "$publishBuildDirectory/ModConfig.json" -ErrorAction SilentlyContinue 275 | Move-Item -Path "$publishBuildDirectory/x86/Preview.png" -Destination "$publishBuildDirectory/Preview.png" -ErrorAction SilentlyContinue 276 | Remove-Item "$publishBuildDirectory/x64/Preview.png" -ErrorAction SilentlyContinue 277 | Remove-Item "$publishBuildDirectory/x64/ModConfig.json" -ErrorAction SilentlyContinue 278 | } 279 | else { 280 | dotnet publish $ProjectPath -c Release --self-contained false -o "$publishBuildDirectory" /p:OutputPath="$TempDirectoryBuild" 281 | } 282 | 283 | # Cleanup Unnecessary Files 284 | Remove-Item $TempDirectoryBuild -Recurse -ErrorAction SilentlyContinue 285 | if ($RemoveExe) { 286 | Get-ChildItem $publishBuildDirectory -Include *.exe -Recurse | Remove-Item -Force -Recurse 287 | } 288 | 289 | Get-ChildItem $publishBuildDirectory -Include *.pdb -Recurse | Remove-Item -Force -Recurse 290 | Get-ChildItem $publishBuildDirectory -Include *.xml -Recurse | Remove-Item -Force -Recurse 291 | } 292 | 293 | function Get-Last-Version { 294 | 295 | Remove-Item $deltaDirectory -Recurse -ErrorAction SilentlyContinue 296 | New-Item $deltaDirectory -ItemType Directory -ErrorAction SilentlyContinue 297 | $arguments = "DownloadPackage --extract --outputpath `"$deltaDirectory`" --allowprereleases `"$IsPrerelease`" --metadatafilename `"$MetadataFileName`"" 298 | 299 | if ($UseGitHubDelta) { 300 | $arguments += " --source GitHub --githubusername `"$GitHubUserName`" --githubrepositoryname `"$GitHubRepoName`" --githublegacyfallbackpattern `"$GitHubFallbackPattern`" --githubinheritversionfromtag `"$GitHubInheritVersionFromTag`"" 301 | } 302 | elseif ($UseNuGetDelta) { 303 | $arguments += " --source NuGet --nugetpackageid `"$NuGetPackageId`" --nugetfeedurl `"$NuGetFeedUrl`" --nugetallowunlisted `"$NuGetAllowUnlisted`"" 304 | } 305 | elseif ($UseGameBananaDelta) { 306 | $arguments += " --source GameBanana --gamebananaitemid `"$GameBananaItemId`"" 307 | } 308 | 309 | Invoke-Expression "dotnet `"$updateToolPath`" $arguments" 310 | } 311 | 312 | function Get-Common-Publish-Args { 313 | 314 | param ( 315 | $AllowDeltas=$True 316 | ) 317 | 318 | $arguments = "--modfolder `"$publishBuildDirectory`" --packagename `"$PackageName`"" 319 | if ($ChangelogPath) { 320 | $arguments += " --changelogpath `"$changelogFullPath`"" 321 | } 322 | 323 | if ($ReadmePath) { 324 | $arguments += " --readmepath `"$readmeFullPath`"" 325 | } 326 | 327 | if ($AllowDeltas -and $MakeDelta) { 328 | $arguments += " --olderversionfolders `"$deltaDirectory`"" 329 | } 330 | 331 | return $arguments 332 | } 333 | 334 | function Publish-Common { 335 | 336 | param ( 337 | $Directory="", 338 | $AllowDeltas=$True, 339 | $PublishTarget="" 340 | ) 341 | 342 | Remove-Item $Directory -Recurse -ErrorAction SilentlyContinue 343 | New-Item $Directory -ItemType Directory -ErrorAction SilentlyContinue 344 | $arguments = "$(Get-Common-Publish-Args -AllowDeltas $AllowDeltas) --outputfolder `"$Directory`" --publishtarget $PublishTarget" 345 | $command = "$reloadedToolPath $arguments" 346 | Write-Host "$command`r`n`r`n" 347 | Invoke-Expression $command 348 | } 349 | 350 | function Publish-GameBanana { 351 | Publish-Common -Directory $PublishGameBananaDirectory -PublishTarget GameBanana 352 | } 353 | 354 | function Publish-NuGet { 355 | Publish-Common -Directory $PublishNuGetDirectory -PublishTarget NuGet -AllowDeltas $False 356 | } 357 | 358 | function Publish-Generic { 359 | Publish-Common -Directory $PublishGenericDirectory -PublishTarget Default 360 | } 361 | 362 | function Cleanup { 363 | Remove-Item $PublishOutputDir -Recurse -ErrorAction SilentlyContinue 364 | Remove-Item $PublishNuGetDirectory -Recurse -ErrorAction SilentlyContinue 365 | Remove-Item $PublishGenericDirectory -Recurse -ErrorAction SilentlyContinue 366 | Remove-Item $publishBuildDirectory -Recurse -ErrorAction SilentlyContinue 367 | Remove-Item $deltaDirectory -Recurse -ErrorAction SilentlyContinue 368 | } 369 | 370 | # Build & Publish 371 | New-Item $TempDirectory -ItemType Directory -ErrorAction SilentlyContinue 372 | Cleanup 373 | Get-Tools 374 | 375 | if ($MakeDelta) { 376 | Write-Host "Downloading Delta (Last Version)" 377 | Get-Last-Version 378 | } 379 | 380 | if ($Build) { 381 | Write-Host "Building Mod" 382 | Build 383 | } 384 | 385 | if ($PublishGeneric) { 386 | Write-Host "Publishing Mod for Default Target" 387 | Publish-Generic 388 | } 389 | 390 | if ($PublishNuGet) { 391 | Write-Host "Publishing Mod for NuGet Target" 392 | Publish-NuGet 393 | } 394 | 395 | if ($PublishGameBanana) { 396 | Write-Host "Publishing Mod for GameBanana Target" 397 | Publish-GameBanana 398 | } 399 | 400 | # Remove Temp Folder 401 | Remove-Item $TempDirectory -Recurse -ErrorAction SilentlyContinue 402 | 403 | # Restore Working Directory 404 | Write-Host "Done." 405 | Write-Host "Upload the files in folder `"$PublishOutputDir`" to respective location or website." 406 | if ($UseScriptDirectory) { 407 | Pop-Location 408 | } -------------------------------------------------------------------------------- /sh-fixed-edition/mod.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "MemAccess.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | 10 | // Unfrozen Title Screen 11 | /// Makes the title screen animation continuous instead of freezing after pressing START Button/ENTER Key. 12 | void TitleScr(bool enable) 13 | { 14 | if (enable) 15 | { 16 | char TitleScr1[] = { 17 | 0xA1, 0x30, 0x49, 0xAA, 0x00, 0x83, 0xF8, 0xFE, 0xB9, 0x03, 0x00, 0x00, 0x00, 0x89, 0x4F, 0x2C, 18 | 0x5E, 0x7C, 0x0B, 0x83, 0xF8, 0x63, 0x7D, 0x06, 0x83, 0xF8, 0xFE, 0x75, 0x01, 0x41, 0x8B, 0x87, 19 | 0x84, 0x00, 0x00, 0x00, 0x40, 0x89, 0x4F, 0x40, 0xC7, 0x47, 0x48, 0x01, 0x00, 0x00, 0x00, 0x89, 20 | 0x4F, 0x2C, 0x89, 0x87, 0x84, 0x00, 0x00, 0x00, 0x5B, 0x59, 0xC3, 0x83, 0xF8, 0x03, 0x75, 0x0A, 21 | 0xD9, 0x41, 0x34, 0xD9, 0xE8, 0xDE, 0xC1, 0xD9, 0x59, 0x34, 0x83, 0xE8, 0x05, 0x0F, 0x84, 0x20, 22 | 0xFE, 0xFF, 0xFF, 0xE9, 0xEF, 0xFD, 0xFF, 0xFF 23 | }; 24 | WriteData((void*)0x4569F1, TitleScr1); 25 | 26 | char TitleScr2[] = { 27 | 0xE9, 0xF4, 0x01, 0x00, 0x00 28 | }; 29 | WriteData((void*)0x456833, TitleScr2); 30 | } 31 | } 32 | 33 | // Demo Gameplay 34 | /// Enables Demo Mode that is leftover in the Windows port of the game. GameCube and PlayStation ports contain Demo Gameplay by default. 35 | void DemoMode(bool enable) 36 | { 37 | if (enable) 38 | { 39 | char DemoMode[] = { 40 | 0x80, 0xF9, 0x06, 0x0F, 0x4D, 0xCB 41 | }; 42 | WriteData((void*)0x45698A, DemoMode); 43 | } 44 | } 45 | 46 | // Menu Prompt/Credits Text Size 47 | /// The text on the menu prompts and credits don't resize properly, causing them to be smaller at bigger screen resolutions. 48 | ////// !!! WIP !!! 49 | 50 | // Knuckles Cut-Off Line 51 | /// Knuckles gets cut-off by the screen fade. 52 | void VoiceTimerRange_TSonic() 53 | { 54 | WriteData((char*)0x7443C8, (char)0xFF); 55 | } 56 | 57 | // Cheese on the Main Menu 58 | /// Cheese doesn't appear in the menu in every port. However, the PC port contains unused animations for his menu idling and lock-in pose. 59 | ////// !!! WIP !!! 60 | 61 | // Broken Env Map Lighting 62 | /// Environmental Maps don't get affected by stage lighting, making them still show and not blend in darker lightings. 63 | ////// !!! WIP !!! 64 | 65 | // Speed Formation Damage Stun Fix 66 | /// Fixes the Damage Stun on Speed Formation Characters (Sonic/Super, Shadow, Amy, Espio). 67 | /// Courtesy of AkoSiRyan for finding the fix originally! 68 | void SpdDamJmp() 69 | { 70 | WriteData((char*)0x5CF2DD, (char)0xEB); 71 | } 72 | 73 | // Shadow's Black Tornado 74 | ///Shadow's tornado effect is single-sided, caused by an incorrect culling materials applied to the model. 75 | void ShTornado() 76 | { 77 | char ShTornado[] = { 78 | 0x14, 0xE8, 0xE3, 0xCA 79 | }; 80 | WriteData((void*)0x63FEC7, ShTornado); 81 | } 82 | 83 | // Shadow's Chaos Emerald 84 | /// Chaos Emerald from Chaos Inferno has different blendings compared to other ports. Not to mention its model and blending is vastly different from SA2. 85 | enum class TDarkChaosEme { 86 | Untouched, 87 | Console, 88 | SA2 89 | }; 90 | void conifg_TDarkChaosEme(TDarkChaosEme selection) 91 | { 92 | switch (selection) 93 | { 94 | case TDarkChaosEme::Untouched: 95 | default: 96 | // Do nothing. 97 | break; 98 | case TDarkChaosEme::Console: 99 | WriteData((char*)0x5BDE87, (char)0x05); 100 | WriteData((char*)0x5BDE90, (char)0x02); 101 | WriteData((char*)0x5BDE99, (char)0x01); 102 | WriteData((char*)0x5BDEA5, (char)0x00); 103 | break; 104 | case TDarkChaosEme::SA2: 105 | WriteData((char*)0x5BDE87, (char)0x05); 106 | WriteData((char*)0x5BDE90, (char)0x06); 107 | WriteData((char*)0x5BDE99, (char)0x02); 108 | WriteData((char*)0x5BDEA5, (char)0x01); 109 | break; 110 | } 111 | } 112 | 113 | // Chaotix Recital's Wrong Spotlight Colors 114 | /// Spotlights for Team Chaotix's Team Blast are seemingly using one color (all purple) instead of three (purple, orange and green). 115 | ////// !!! WIP !!! 116 | 117 | // Fade-Out Effects 118 | /// Unlike on consoles, Some effects don't fade-out on PC. 119 | ////// !!! WIP !!! 120 | 121 | // Signal Monitor Flickering 122 | /// Flickering animation present in GC/XB ports that is missing on PC. 123 | void SignalFlick() 124 | { 125 | char SignalFlick1[] = { 126 | 0xA1, 0xE4, 0x77, 0xA7, 0x00, 0x83, 0xEC, 0x10, 0x57, 0x8B, 0xF9, 0x8A, 0x48, 0x20, 0x84, 0xC9, 127 | 0x0F, 0x85, 0xF4, 0x00, 0x00, 0x00, 0x55, 0x8B, 0x68, 0x30, 0x8D, 0x87, 0xBC, 0x00, 0x00, 0x00, 128 | 0x8B, 0x08, 0x8B, 0x50, 0x04, 0x8B, 0x40, 0x08, 0x89, 0x4C, 0x24, 0x08, 0x8B, 0x0D, 0x4C, 0x0A, 129 | 0x8E, 0x00, 0x89, 0x54, 0x24, 0x0C, 0x8D, 0x54, 0x24, 0x08, 0x89, 0x44, 0x24, 0x10, 0x8B, 0x01, 130 | 0x52, 0x50, 0xC7, 0x44, 0x24, 0x1C, 0x00, 0x00, 0x70, 0x41, 0xE8, 0xC1, 0x3E, 0x1C, 0x00, 0x83, 131 | 0xC4, 0x08, 0x85, 0xC0, 0x0F, 0x84, 0xAF, 0x00, 0x00, 0x00, 0x8B, 0x47, 0x2C, 0x8B, 0x40, 0x18, 132 | 0xC1, 0xE8, 0x12, 0x24, 0x07, 0x04, 0x04, 0x78, 0x09, 0x3C, 0x13, 0x7D, 0x05, 0xA2, 0x76, 0xDD, 133 | 0x9D, 0x00, 0x53, 0x8A, 0x1D, 0x76, 0xDD, 0x9D, 0x00, 0x56, 0xBE, 0xB8, 0xD8, 0x9D, 0x00, 0xE8, 134 | 0xEC, 0xFB, 0xF9, 0xFF, 0x0F, 0xBE, 0x87, 0xB8, 0x00, 0x00, 0x00, 0xB9, 0x20, 0x7D, 0xA7, 0x00, 135 | 0x5E, 0x5B, 0x8B, 0x44, 0x81, 0x0C, 0x8B, 0x09, 0x50, 0x51, 0xE8, 0x70, 0x00, 0x00, 0x00, 0xC1, 136 | 0xE5, 0x0B, 0x83, 0xC4, 0x08, 0x81, 0xE5, 0xFF, 0xFF, 0x00, 0x80, 0x79, 0x08, 0x4D, 0x81, 0xCD, 137 | 0x00, 0x00, 0xFF, 0xFF, 0x45, 0x81, 0xE5, 0xFF, 0xFF, 0x00, 0x00, 0xD9, 0x04, 0xAD, 0x08, 0xEE, 138 | 0x9D, 0x00, 0xD8, 0x05, 0x74, 0x9F, 0x78, 0x00, 0xD8, 0x0D, 0x54, 0x60, 0x74, 0x00, 0xE8, 0xCD, 139 | 0x0A, 0x29, 0x00, 0xD1, 0xE8, 0x0F, 0xB6, 0x80, 0x78, 0x6F, 0x48, 0x00, 0xBA, 0x24, 0x7D, 0xA7, 140 | 0x00, 0x8B, 0x44, 0x82, 0x20, 0x8B, 0x12, 0x50, 0x52, 0xE8, 0xF2, 0x68, 0x1E, 0x00, 0x8B, 0x87, 141 | 0xE0, 0x00, 0x00, 0x00, 0x6A, 0x00, 0x68, 0xF0, 0xC0, 0x42, 0x00, 0x50, 0xE8, 0x0F, 0x48, 0x1E, 142 | 0x00, 0xE8, 0xBA, 0x47, 0x1E, 0x00, 0x83, 0xC4, 0x14, 0x5D, 0x5F, 0x83, 0xC4, 0x10, 0xC3, 0xFF, 143 | 0x74, 0x24, 0x08, 0xFF, 0x74, 0x24, 0x08, 0xE8, 0xC4, 0x68, 0x1E, 0x00, 0x8B, 0x04, 0x24, 0x03, 144 | 0x05, 0x7C, 0x18, 0x8E, 0x00, 0x8B, 0x00, 0x85, 0xC0, 0x89, 0x04, 0x24, 0x74, 0x22, 0xA1, 0xE4, 145 | 0x77, 0xA7, 0x00, 0x8B, 0x40, 0x30, 0x83, 0xE0, 0x03, 0x0F, 0xB6, 0x80, 0x78, 0x6F, 0x48, 0x00, 146 | 0x8B, 0x04, 0x85, 0x90, 0x7C, 0xA7, 0x00, 0x89, 0x44, 0x24, 0x04, 0xE8, 0x90, 0x68, 0x1E, 0x00, 147 | 0x83, 0xC4, 0x08, 0xC3, 0xA1, 0xE4, 0x77, 0xA7, 0x00, 0x83, 0xEC, 0x1C, 0x57, 0x8B, 0xF9, 0x8A, 148 | 0x48, 0x20, 0x84, 0xC9, 0x0F, 0x85, 0xD9, 0x01, 0x00, 0x00, 0x81, 0xBF, 0xE8, 0x00, 0x00, 0x00, 149 | 0x00, 0x24, 0x74, 0xC9, 0x0F, 0x84, 0xC9, 0x01, 0x00, 0x00, 0x55, 0x8D, 0xAF, 0xE4, 0x00, 0x00, 150 | 0x00, 0x68, 0x00, 0x40, 0x1C, 0x47, 0x68, 0x00, 0x00, 0x70, 0x41, 0x8B, 0xCD, 0xE8, 0x0E, 0x11, 151 | 0x1A, 0x00, 0x83, 0xC4, 0x08, 0x85, 0xC0, 0x0F, 0x84, 0xA5, 0x01, 0x00, 0x00, 0x56, 0x8D, 0x4C, 152 | 0x24, 0x18, 0x51, 0x6A, 0x0E, 0xE8, 0x26, 0x5C, 0x1C, 0x00, 0x6A, 0x00, 0x6A, 0x0E, 0xE8, 0xCD, 153 | 0x5B, 0x1C, 0x00, 0xD9, 0x87, 0xC0, 0x00, 0x00, 0x00, 0xD8, 0xA7, 0xE8, 0x00, 0x00, 0x00, 0x83, 154 | 0xC4, 0x10, 0xC7, 0x44, 0x24, 0x1C, 0x00, 0x00, 0xC8, 0x41, 0xC7, 0x44, 0x24, 0x20, 0x00, 0x00, 155 | 0x80, 0x3F, 0xD8, 0x0D, 0x04, 0x5C, 0x74, 0x00, 0xC7, 0x44, 0x24, 0x24, 0x00, 0x00, 0x88, 0x41, 156 | 0xD8, 0x2D, 0x74, 0x9F, 0x78, 0x00, 0xD9, 0x54, 0x24, 0x0C, 0xD8, 0x1D, 0x00, 0x62, 0x74, 0x00, 157 | 0xDF, 0xE0, 0xF6, 0xC4, 0x41, 0x75, 0x26, 0x31, 0xF6, 0x46, 0xE8, 0x91, 0x48, 0x1B, 0x00, 0x8B, 158 | 0x54, 0x24, 0x0C, 0x52, 0x8D, 0x44, 0x24, 0x20, 0x50, 0x8D, 0x87, 0xF0, 0x00, 0x00, 0x00, 0x55, 159 | 0xE8, 0x6B, 0x49, 0x1B, 0x00, 0x83, 0xC4, 0x0C, 0xE8, 0x83, 0x4C, 0x1B, 0x00, 0x53, 0xB3, 0x10, 160 | 0xBE, 0xB8, 0xD8, 0x9D, 0x00, 0x88, 0x1D, 0x76, 0xDD, 0x9D, 0x00, 0xE8, 0x40, 0xFA, 0xF9, 0xFF, 161 | 0xA1, 0x1C, 0x7D, 0xA7, 0x00, 0x85, 0xC0, 0x5B, 0x0F, 0x84, 0xF4, 0x00, 0x00, 0x00, 0x8D, 0x4C, 162 | 0x24, 0x10, 0x51, 0x6A, 0x0A, 0xE8, 0x86, 0x5B, 0x1C, 0x00, 0x8D, 0x54, 0x24, 0x1C, 0x52, 0x6A, 163 | 0x0B, 0xE8, 0x7A, 0x5B, 0x1C, 0x00, 0x8D, 0x44, 0x24, 0x1C, 0x50, 0x6A, 0x14, 0xE8, 0x6E, 0x5B, 164 | 0x1C, 0x00, 0x6A, 0x05, 0x6A, 0x0A, 0xE8, 0x15, 0x5B, 0x1C, 0x00, 0x6A, 0x02, 0x6A, 0x0B, 0xE8, 165 | 0x0C, 0x5B, 0x1C, 0x00, 0x6A, 0x01, 0x6A, 0x14, 0xE8, 0x03, 0x5B, 0x1C, 0x00, 0x8B, 0x0D, 0x1C, 166 | 0x7D, 0xA7, 0x00, 0x8B, 0x71, 0x04, 0x6A, 0x00, 0x68, 0x28, 0x2C, 0x75, 0x00, 0x56, 0xE8, 0x5D, 167 | 0x57, 0x1C, 0x00, 0x8B, 0x97, 0xF4, 0x00, 0x00, 0x00, 0x6A, 0x02, 0x52, 0x68, 0xC8, 0x2E, 0x74, 168 | 0x00, 0x56, 0xE8, 0x29, 0x59, 0x1C, 0x00, 0x8B, 0x87, 0xF0, 0x00, 0x00, 0x00, 0x83, 0xC4, 0x4C, 169 | 0x6A, 0x02, 0x50, 0x68, 0xBC, 0x2E, 0x74, 0x00, 0x56, 0xE8, 0x12, 0x59, 0x1C, 0x00, 0x8B, 0x8F, 170 | 0xF8, 0x00, 0x00, 0x00, 0x6A, 0x02, 0x51, 0x68, 0xD4, 0x2E, 0x74, 0x00, 0x56, 0xE8, 0xFE, 0x58, 171 | 0x1C, 0x00, 0x6A, 0x02, 0x55, 0x56, 0xE8, 0x15, 0x57, 0x1C, 0x00, 0x8B, 0x0D, 0x28, 0x7D, 0xA7, 172 | 0x00, 0x83, 0xC4, 0x2C, 0x85, 0xC9, 0x74, 0x18, 0x0F, 0xBE, 0x87, 0xB8, 0x00, 0x00, 0x00, 0x83, 173 | 0xF8, 0x03, 0x77, 0x0C, 0x8D, 0x04, 0x85, 0xF4, 0xD1, 0x8D, 0x00, 0x8B, 0x10, 0x89, 0x51, 0x04, 174 | 0xA1, 0x1C, 0x7D, 0xA7, 0x00, 0x50, 0xE8, 0xB5, 0x45, 0x1E, 0x00, 0x8B, 0x4C, 0x24, 0x10, 0x51, 175 | 0x6A, 0x14, 0xE8, 0x69, 0x5A, 0x1C, 0x00, 0x8B, 0x54, 0x24, 0x1C, 0x52, 0x6A, 0x0A, 0xE8, 0x5D, 176 | 0x5A, 0x1C, 0x00, 0x8B, 0x44, 0x24, 0x28, 0x50, 0x6A, 0x0B, 0xE8, 0x51, 0x5A, 0x1C, 0x00, 0x83, 177 | 0xC4, 0x1C, 0x8B, 0x4C, 0x24, 0x18, 0x51, 0x6A, 0x0E, 0xE8, 0x42, 0x5A, 0x1C, 0x00, 0x83, 0xC4, 178 | 0x08, 0x5E, 0x5D, 0x5F, 0x83, 0xC4, 0x1C, 0xC3, 0x00, 0x02, 0x01, 0x02, 0xCC, 0xCC, 0xCC, 0xCC 179 | }; 180 | WriteData((void*)0x486C30, SignalFlick1); 181 | 182 | char SignalFlick2[] = { 183 | 0xE8, 0xAB, 0x1E, 0x02, 0x00 184 | }; 185 | WriteData((void*)0x464E8F, SignalFlick2); 186 | 187 | char SignalFlick3[] = { 188 | 0xE8, 0xAC, 0x38, 0x01, 0x00 189 | }; 190 | WriteData((void*)0x47348E, SignalFlick3); 191 | 192 | char SignalFlick4[] = { 193 | 0xE8, 0x61, 0xD9, 0x00, 0x00 194 | }; 195 | WriteData((void*)0x4793D9, SignalFlick4); 196 | 197 | char SignalFlick5[] = { 198 | 0x84, 0x6D, 0x48, 0x00 199 | }; 200 | WriteData((void*)0x752C0C, SignalFlick5); 201 | } 202 | 203 | // Leadership Crown 204 | /// Leadership Crown Texture is missing from the 2P Bobsled Race HUD. (?, this exists in vanilla.. i dont understand wheres the need for this lol) 205 | void BobCrown() 206 | { 207 | char BobCrown[] = { 208 | 0x31, 0xD2, 0x89, 0x15, 0x04, 0x4C, 0xAA, 0x00, 209 | 0x89, 0x15, 0x20, 0x4C, 0xAA, 0x00, 0xD9, 0x1D, 210 | 0x48, 0x4C, 0xAA, 0x00, 0xD9, 0x05, 0x74, 0x9F, 211 | 0x78, 0x00, 0xD9, 0x15, 0x3C, 0x4C, 0xAA, 0x00, 212 | 0xD9, 0x15, 0x58, 0x4C, 0xAA, 0x00, 0xD9, 0xC9, 213 | 0xD9, 0x1D, 0x4C, 0x4C, 0xAA, 0x00, 0x89, 0x15, 214 | 0x08, 0x4C, 0xAA, 0x00, 0xD9, 0x15, 0x24, 0x4C, 215 | 0xAA, 0x00, 0x89, 0x15, 0x40, 0x4C, 0xAA, 0x00, 216 | 0xD9, 0x15, 0x5C, 0x4C, 0xAA, 0x00, 0xA3, 0x00, 217 | 0x4C, 0xAA, 0x00, 0xD9, 0x15, 0xFC, 0x4B, 0xAA, 218 | 0x00, 0xA3, 0x1C, 0x4C, 0xAA, 0x00, 0xD9, 0x15, 219 | 0x18, 0x4C, 0xAA, 0x00, 0xA3, 0x38, 0x4C, 0xAA, 220 | 0x00, 0xD9, 0x15, 0x34, 0x4C, 0xAA, 0x00, 0xA3, 221 | 0x54, 0x4C, 0xAA, 0x00, 0xD9, 0x1D, 0x50, 0x4C, 222 | 0xAA, 0x00, 0xE8, 0xB9, 0x59, 0x24, 0x00, 0x83, 223 | 0xC4, 0x18, 0xC3, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC 224 | }; 225 | WriteData((void*)0x406FE0, BobCrown); 226 | } 227 | 228 | // Sun Lens Flare Size 229 | /// Similar to the text on menu prompts and credits, the Lens Flare doesn't resize properly, causing the object to be smaller at bigger screen resolutions. 230 | ////// !!! WIP !!! 231 | 232 | // Stage Speed Cap 233 | /// Missing feature on PC, causing characters to go faster on loops. 234 | ////// !!! WIP !!! 235 | 236 | // TXC (Texture Pattern Animation) 237 | /// Makes use of texture animation (similar to PS2 port) rather than indirect shaders. 238 | void IndirectOFF(bool enabled) 239 | { 240 | if (enabled) 241 | { 242 | WriteData((char*)0x749493, (char)0x78); 243 | WriteData((char*)0x76E6FA, (char)0x78); 244 | WriteData((char*)0x773227, (char)0x78); 245 | 246 | //Laser Beam Indirect Effects 247 | WriteData((char*)0x71A800, (char)0x40); 248 | WriteData((char*)0x71A950, (char)0x40); 249 | WriteData((char*)0x71AAA0, (char)0x40); 250 | WriteData((char*)0x71AB79, (char)0x40); 251 | } 252 | else 253 | { 254 | //Laser Beam Indirect Effects 255 | WriteData((char*)0x71A800, (char)0x50); 256 | WriteData((char*)0x71A950, (char)0x50); 257 | WriteData((char*)0x71AAA0, (char)0x50); 258 | WriteData((char*)0x71AB79, (char)0x50); 259 | } 260 | } 261 | 262 | // Grand Metropolis Energy Pipes 263 | /// The game does not scan for the high quality object archive for the lava indirect effects. (?, no idea what this changes actually lol) 264 | void stg03Pipe() 265 | { 266 | char stg03Pipe1[] = { 267 | 0xB0, 0x3B, 0x57, 0x00, 0xAD, 0x3C, 0x57, 0x00, 268 | 0x62, 0x3B, 0x57, 0x00 269 | }; 270 | WriteData((void*)0x8DB12C, stg03Pipe1); 271 | 272 | char stg03Pipe2[] = { 273 | 0x6A, 0xFF, 0x68, 0xEB, 0x7E, 0x71, 0x00, 0x64, 0xA1, 0x00, 0x00, 0x00, 0x00, 0x50, 0x64, 0x89, 274 | 0x25, 0x00, 0x00, 0x00, 0x00, 0x51, 0x8B, 0x15, 0x00, 0xEE, 0x9D, 0x00, 0xB9, 0xC0, 0x00, 0x00, 275 | 0x00, 0xE8, 0xF8, 0x46, 0xEB, 0xFF, 0x31, 0xC9, 0x89, 0x04, 0x24, 0x85, 0xC0, 0x89, 0x4C, 0x24, 276 | 0x0C, 0x74, 0x0C, 0x8B, 0x0D, 0xB4, 0x78, 0xA7, 0x00, 0x50, 0xE8, 0x34, 0x01, 0x00, 0x00, 0x8B, 277 | 0x4C, 0x24, 0x04, 0x64, 0x89, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x83, 0xC4, 0x10, 0xC3, 0x83, 0xEC, 278 | 0x20, 0xA1, 0x18, 0x67, 0x8D, 0x00, 0x85, 0xC0, 0x74, 0x02, 0x8B, 0x00, 0x50, 0x68, 0xF4, 0xE6, 279 | 0x76, 0x00, 0x8D, 0x44, 0x24, 0x08, 0x50, 0xE8, 0xA5, 0x67, 0x10, 0x00, 0x68, 0x28, 0x3D, 0xA2, 280 | 0x00, 0xE8, 0x0D, 0x00, 0x00, 0x00, 0x58, 0xE8, 0xBF, 0xFE, 0xFF, 0xFF, 0x83, 0xC4, 0x0C, 0x83, 281 | 0xC4, 0x20, 0xC3, 0xA1, 0xB0, 0xD8, 0x9D, 0x00, 0x8B, 0x80, 0x1C, 0x8C, 0x00, 0x00, 0x83, 0xEC, 282 | 0x20, 0x85, 0xC0, 0x0F, 0x84, 0xAE, 0x00, 0x00, 0x00, 0x53, 0x55, 0x56, 0x57, 0x50, 0xE8, 0x2B, 283 | 0xA8, 0x0D, 0x00, 0x5B, 0xE8, 0xF5, 0xD8, 0x0C, 0x00, 0x8B, 0x15, 0x4C, 0x78, 0xA7, 0x00, 0x8B, 284 | 0x82, 0x50, 0x0A, 0x00, 0x00, 0x8B, 0x6C, 0x24, 0x34, 0x55, 0xE8, 0xDF, 0xB4, 0xEB, 0xFF, 0xBF, 285 | 0xD4, 0x42, 0x8B, 0x00, 0x31, 0xED, 0x83, 0xC5, 0x06, 0x8B, 0x47, 0x04, 0x50, 0x8D, 0x4C, 0x24, 286 | 0x14, 0x68, 0x70, 0xE1, 0x76, 0x00, 0x51, 0xE8, 0x35, 0x67, 0x10, 0x00, 0x8B, 0x15, 0x4C, 0x78, 287 | 0xA7, 0x00, 0x8B, 0xB2, 0x50, 0x0A, 0x00, 0x00, 0x83, 0xC4, 0x0C, 0x8D, 0x4C, 0x24, 0x10, 0x8B, 288 | 0xC6, 0xE8, 0x28, 0xB6, 0xEB, 0xFF, 0x8B, 0xD8, 0x8B, 0x44, 0x24, 0x34, 0x50, 0x8B, 0xC6, 0xE8, 289 | 0x9A, 0xB4, 0xEB, 0xFF, 0x85, 0xC0, 0x74, 0x21, 0x8B, 0x4E, 0x40, 0x56, 0x8B, 0xC3, 0xE8, 0xCB, 290 | 0xB7, 0xEB, 0xFF, 0x85, 0xC0, 0x74, 0x12, 0x89, 0x07, 0x6A, 0x00, 0x68, 0xD0, 0x15, 0x64, 0x00, 291 | 0x50, 0xE8, 0xB8, 0x78, 0x0F, 0x00, 0x83, 0xC4, 0x0C, 0x83, 0xC7, 0x0C, 0x4D, 0x75, 0x9A, 0xA1, 292 | 0xB0, 0xD8, 0x9D, 0x00, 0x8B, 0x88, 0x1C, 0x8C, 0x00, 0x00, 0x51, 0xE8, 0x9E, 0xD8, 0x0C, 0x00, 293 | 0x83, 0xC4, 0x04, 0x5F, 0x5E, 0x5D, 0x5B, 0x83, 0xC4, 0x20, 0xC3, 0x56, 0x57, 0xBE, 0xD4, 0x42, 294 | 0x8B, 0x00, 0x31, 0xFF, 0x83, 0xC7, 0x06, 0x8B, 0x06, 0x85, 0xC0, 0x74, 0x0D, 0x50, 0xE8, 0x9B, 295 | 0x7F, 0x0F, 0x00, 0x31, 0xC0, 0x83, 0xC4, 0x04, 0x89, 0x06, 0x83, 0xC6, 0x0C, 0x4F, 0x75, 0xE7, 296 | 0x5F, 0x5E, 0xC3, 0x64, 0xA1, 0x00, 0x00, 0x00, 0x00, 0x6A, 0xFF, 0x68, 0x7E, 0x8C, 0x71, 0x00, 297 | 0x50, 0x64, 0x89, 0x25, 0x00, 0x00, 0x00, 0x00, 0x53, 0x55, 0x56, 0x57, 0x8B, 0x7C, 0x24, 0x20, 298 | 0x8B, 0xC7, 0xE8, 0x67, 0xF5, 0xEC, 0xFF, 0x31, 0xDB, 0x8D, 0x47, 0x28, 0x89, 0x5C, 0x24, 0x18, 299 | 0xE8, 0x19, 0xA3, 0xEC, 0xFF, 0x8D, 0x77, 0x30, 0x31, 0xC9, 0x41, 0x89, 0x5E, 0x10, 0x66, 0x89, 300 | 0x5E, 0x08, 0x66, 0x89, 0x5E, 0x02, 0x66, 0x89, 0x5E, 0x0A, 0x66, 0x89, 0x4E, 0x04, 0x66, 0x89, 301 | 0x4E, 0x06, 0x89, 0x5E, 0x78, 0xC6, 0x44, 0x24, 0x18, 0x02, 0x8B, 0x0D, 0x60, 0x43, 0x8B, 0x00, 302 | 0xBD, 0x60, 0xD8, 0x77, 0x00, 0x89, 0x28, 0x83, 0xC5, 0x04, 0x8B, 0x47, 0x2C, 0x89, 0x2F, 0x8B, 303 | 0x68, 0x2C, 0x89, 0x4F, 0x04, 0x66, 0xC7, 0x47, 0x1E, 0xC0, 0x00, 0x8A, 0x45, 0x00, 0x38, 0xD8, 304 | 0x7C, 0x04, 0x3C, 0x04, 0x7C, 0x03, 0x88, 0x5D, 0x00, 0x8B, 0xDF, 0xE8, 0x4E, 0x03, 0x00, 0x00, 305 | 0xE8, 0x29, 0x02, 0x00, 0x00, 0x8B, 0x15, 0x4C, 0x0A, 0x8E, 0x00, 0x6A, 0x30, 0xFF, 0x92, 0x34, 306 | 0x01, 0x00, 0x00, 0x8B, 0xD8, 0x83, 0xC4, 0x04, 0x85, 0xDB, 0x74, 0x30, 0x6A, 0x30, 0x68, 0x30, 307 | 0x43, 0x8B, 0x00, 0x53, 0xE8, 0x45, 0x66, 0x10, 0x00, 0x80, 0x4E, 0x09, 0x80, 0x83, 0xC4, 0x0C, 308 | 0x8B, 0xCE, 0x89, 0x5E, 0x10, 0x66, 0xC7, 0x06, 0x04, 0x00, 0x66, 0xC7, 0x46, 0x0A, 0x01, 0x00, 309 | 0xE8, 0x99, 0x37, 0xE9, 0xFF, 0x8B, 0xDE, 0xE8, 0x72, 0x09, 0xEA, 0xFF, 0x8B, 0x47, 0x40, 0x85, 310 | 0xC0, 0x74, 0x22, 0xD9, 0x45, 0x04, 0x8B, 0xDE, 0xD8, 0x48, 0x14, 0xD9, 0x58, 0x14, 0xD9, 0x45, 311 | 0x08, 0xD8, 0x48, 0x18, 0xD9, 0x58, 0x18, 0xD9, 0x45, 0x0C, 0xD8, 0x48, 0x1C, 0xD9, 0x58, 0x1C, 312 | 0xE8, 0x49, 0x09, 0xEA, 0xFF, 0x8B, 0x4C, 0x24, 0x10, 0x8B, 0xC7, 0x5F, 0x5E, 0x5D, 0x64, 0x89, 313 | 0x0D, 0x00, 0x00, 0x00, 0x00, 0x5B, 0x83, 0xC4, 0x0C, 0xC2, 0x04, 0x00, 0xCC, 0xCC 314 | }; 315 | WriteData((void*)0x573B62, stg03Pipe2); 316 | 317 | char stg03Pipe3[] = { 318 | 0x8B, 0x48, 0x40, 0x8B, 0x91, 0xE0, 0x00, 0x00, 0x00, 319 | 0x39, 0xC2, 0x75, 0x0B, 0x8B, 0x50, 0x3C, 0x89, 0x91, 320 | 0xE0, 0x00, 0x00, 0x00, 0xEB, 0x1B, 0x8D, 0x4A, 0x3C, 321 | 0x56, 0x39, 0x01, 0x74, 0x0C, 0x8B, 0x11, 0x8B, 0x72, 322 | 0x3C, 0x39, 0xC6, 0x8D, 0x4A, 0x3C, 0x75, 0xF4, 0x8B, 323 | 0x48, 0x3C, 0x89, 0x4A, 0x3C, 0x5E, 0x31, 0xD2, 0xC7, 324 | 0x40, 0x3C, 0x00, 0x00, 0x00, 0x00, 0xC3, 0x66, 0x81, 325 | 0x3D, 0x2C, 0x51, 0xAA, 0x00, 0x01, 0x01, 0x72, 0xF4, 326 | 0xE9, 0x38, 0x01, 0x00, 0x00 327 | }; 328 | WriteData((void*)0x573A60, stg03Pipe3); 329 | } 330 | 331 | // Grand Metropolis Pipe Glass 332 | /// Can be seen through walls, caused by an incorrect material flag. (?, no idea what this changes actually lol) 333 | void stg03PipeGlass() 334 | { 335 | WriteData((char*)0x574130, (char)0xA4); 336 | } 337 | 338 | // Power Plant Transparency issues 339 | /// No use of transparency on the stage (more specifically the green arrows and the Eggman's symbol on the caps) 340 | ////// !!! WIP !!! (WORK AROUND - i edited the textures so the black parts wouldnt show up) 341 | 342 | // Power Plant Steps 343 | /// Steps from the Storage Tank don't have their flicker effect. 344 | ////// !!! WIP !!! 345 | 346 | // Rail Grinding Effect 347 | /// Unused in the game completely as well, there are 3 models for a rail grinding effect (EF_GLIND0.DFF, EF_GLIND1.DFF & EF_GLIND2.DFF), similar to Sonic Adventure 2. 348 | ////// !!! WIP !!! 349 | 350 | // Frog Forest Root Ball 351 | /// The vertex paint disappears from the model. (?, no idea what this changes actually lol) 352 | void stg09RootBall() 353 | { 354 | WriteData((char*)0x4DDC4B, (char)0x60); 355 | } 356 | 357 | // Transparent Frog feet 358 | /// The frog's feet in Frog Forest and Lost Jungle are see-through when in deep touch with geometry. 359 | ///// !!! BROKEN, NEEDS BETTER CODE !!! 360 | /* 361 | void stg09FrogFeet() 362 | { 363 | char stg09FrogFeet1[] = { 364 | 0xC7, 0x05, 0xE4, 0xAB, 0x7B, 365 | 0x00, 0x70, 0x2E, 0x40, 0x00 366 | }; 367 | WriteData((void*)0x4EE3ED, stg09FrogFeet1); 368 | 369 | char stg09FrogFeet2[] = { 370 | 0x12 371 | }; 372 | WriteData((void*)0x868A20, stg09FrogFeet2); 373 | 374 | char stg09FrogFeet3[] = { 375 | 0x12 376 | }; 377 | WriteData((void*)0x868A44, stg09FrogFeet3); 378 | } 379 | */ 380 | 381 | // Final Fortress UFO Sign light 382 | /// Fading light arrow animations are missing. (yet does not affect Egg Fleet's UFO!) 383 | void stg14UFO() 384 | { 385 | char stg14UFO1[] = { 386 | 0x83, 0xEC, 0x08, 0xA1, 0x64, 0x34, 0xAA, 0x00, 387 | 0x56, 0x8B, 0xF1, 0x8B, 0x4E, 0x2C, 0x89, 0x44, 388 | 0x24, 0x04, 0x8B, 0x41, 0x18, 0xF6, 0xC4, 0x04, 389 | 0x75, 0x29, 0x0F, 0xB6, 0x51, 0x2B, 0x42, 0x89, 390 | 0x54, 0x24, 0x08, 0x51, 0xDB, 0x44, 0x24, 0x0C, 391 | 0xD8, 0x0D, 0x28, 0xA0, 0x78, 0x00, 0xD9, 0xC0, 392 | 0xD8, 0xC9, 0xD9, 0x1C, 0x24, 0xDD, 0xD8, 0xE8, 393 | 0x64, 0xA5, 0xF1, 0xFF, 0x83, 0xC4, 0x04, 0x85, 394 | 0xC0, 0x75, 0x76, 0x8B, 0x56, 0x2C, 0xF6, 0x42, 395 | 0x18, 0x04, 0x75, 0x6D, 0xA1, 0xE4, 0x77, 0xA7, 396 | 0x00, 0x8A, 0x48, 0x1F, 0x84, 0xC9, 0x74, 0x08, 397 | 0x8D, 0x46, 0x30, 0x8D, 0x4A, 0x0C, 0xEB, 0x50, 398 | 0x81, 0xC6, 0xB8, 0x00, 0x00, 0x00, 0xD9, 0x06, 399 | 0xD8, 0x05, 0xDC, 0xA0, 0x78, 0x00, 0xD8, 0x54, 400 | 0x24, 0x04, 0xDF, 0xE0, 0xF6, 0xC4, 0x01, 0x75, 401 | 0x04, 0xD8, 0x64, 0x24, 0x04, 0xD9, 0x1E, 0x8B, 402 | 0x0E, 0x57, 0x8B, 0x7E, 0x04, 0x51, 0xE8, 0x85, 403 | 0xE2, 0xFE, 0xFF, 0xA1, 0x48, 0x86, 0xA7, 0x00, 404 | 0x83, 0xC4, 0x04, 0x85, 0xC0, 0x5F, 0x74, 0x09, 405 | 0x50, 0x8B, 0x46, 0x08, 0xE8, 0x21, 0x00, 0x00, 406 | 0x00, 0x81, 0xEE, 0xB8, 0x00, 0x00, 0x00, 0x8B, 407 | 0x56, 0x2C, 0x8D, 0x46, 0x30, 0x8D, 0x4A, 0x0C, 408 | 0x5E, 0x83, 0xC4, 0x08, 0xE9, 0x47, 0x55, 0xF3, 409 | 0xFF, 0x80, 0x4E, 0x08, 0x01, 0x5E, 0x83, 0xC4, 410 | 0x08, 0xC3, 0x6A, 0x00, 0x68, 0x41, 0x3F, 0x52, 411 | 0x00, 0x50, 0xE8, 0x61, 0x78, 0x14, 0x00, 0xFF, 412 | 0x74, 0x24, 0x10, 0xE8, 0x48, 0x03, 0x00, 0x00, 413 | 0x83, 0xC4, 0x0C, 0xC2, 0x04, 0x00 414 | }; 415 | WriteData((void*)0x523C10, stg14UFO1); 416 | 417 | char stg14UFO2[] = { 418 | 0x8B, 0x40, 0x18, 0x6A, 0x08, 0x50, 0xE8, 0x04, 419 | 0xAD, 0x14, 0x00, 0x83, 0xC4, 0x08, 0xC3 420 | }; 421 | WriteData((void*)0x523F41, stg14UFO2); 422 | } 423 | 424 | // Final Fortress Laser Beam Disabled Backface Culling 425 | /// Backface Culling is rendered as 'front' instead of 'none', making the laser beam a bit incomplete. 426 | ////// !!! WIP !!! (WORK AROUND - i flipped normals so it would display the head, yet it would be better without any model edits personally, and having both sides showing) 427 | 428 | // Final Fortress Laser Beam Effect Display 429 | /// An effect that should appear before and during the laser beam activation, which is not rendered correctly. 430 | ////// !!! WIP !!! 431 | 432 | // Boss Defeat Explosion 433 | /// Explosion particles are missing when defeating a boss (Egg Hawk and Egg Albatoross), present on the GameCube port. 434 | ////// !!! WIP !!! 435 | 436 | // Missing Logos on Credits 437 | /// ADX and SofDec logos are missing in the game's code. Adding them to the credits text file (or porting the same file from GC/XB to PC) will cause a crash. 438 | ////// !!! WIP !!! 439 | 440 | // No Exit Prompt 441 | ///! Kell is a God. 442 | void Exit(bool enabled) 443 | { 444 | if (enabled) 445 | { 446 | char Exit[] = { 447 | 0xE9, 0xF1, 0x00, 0x00, 0x00 448 | }; 449 | WriteData((void*)0x446EFB, Exit); 450 | } 451 | else 452 | { 453 | // Do nothing. 454 | } 455 | } 456 | 457 | extern "C" 458 | { 459 | __declspec(dllexport) void InitMod() 460 | { 461 | TitleScr(true); 462 | DemoMode(true); 463 | VoiceTimerRange_TSonic(); 464 | SpdDamJmp(); 465 | ShTornado(); 466 | conifg_TDarkChaosEme(TDarkChaosEme::Untouched); 467 | SignalFlick(); 468 | BobCrown(); 469 | IndirectOFF(false); 470 | stg03Pipe(); 471 | stg03PipeGlass(); 472 | stg09RootBall(); 473 | stg14UFO(); 474 | Exit(true); 475 | } 476 | } --------------------------------------------------------------------------------