├── dxsdk
└── .copy_fxc_exe_here
├── .gitmodules
├── SaltAndSanctuary
├── shaders
│ ├── MainEffectLightsBlur.fx
│ ├── MainEffectLightsNoBlur.fx
│ ├── MainEffectNoLightsBlur.fx
│ ├── MainEffectNoLightsNoBlur.fx
│ ├── ClearAlpha.fx
│ ├── compile.bat
│ ├── SimplifiedMapBlood.fx
│ ├── SimplifiedParchment.fx
│ ├── SimplifiedGaussianBlur.fx
│ ├── SimplifiedTintBloom.fx
│ ├── SimplifiedLight.fx
│ ├── SimplifiedGameParchment.fx
│ └── MainEffectCommon.ifx
└── Makefile.inc
├── deps
├── FNA
│ └── .extract_fna_here
├── Harmony
│ └── .extract_harmony_here
└── MonoMod
│ └── .extract_monomod_here
├── Chasm
├── Makefile.inc
└── ChasmPatches.cs
├── Celeste
├── Makefile.inc
└── CelestePatches.cs
├── Anodyne
├── Makefile.inc
├── shaders
│ ├── compile.bat
│ └── bilinear-sharp.fx
└── AnodynePatches.cs
├── Shipwreck
├── Makefile.inc
└── ShipwreckPatches.cs
├── SteelAssault
├── Makefile.inc
└── SteelAssaultCsPatches.cs
├── .gitignore
├── PanzerPaladin
├── Makefile.inc
└── ParisEngine.PanzerPaladin.mm.cs
├── TMNTSR-Mods
├── Makefile.inc
├── TMNTSR-Mods.csproj
└── ParisEngine.TMNTSRMods.mm.cs
├── TMNTSR
├── Makefile.inc
├── TMNT.TMNTSRPatches.mm.cs
└── ParisEngine.TMNTSRPatches.mm.cs
├── Bleed
├── Makefile.inc
└── Bleed.BleedPatches.mm.cs
├── Makefile
├── libs
├── Makefile
└── fmodstudio_fix.c
├── LICENSE
├── Makefile.common
├── MMLoader.cs
└── README.md
/dxsdk/.copy_fxc_exe_here:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "VisFree"]
2 | path = VisFree
3 | url = https://github.com/JohnnyonFlame/VisFree
4 |
--------------------------------------------------------------------------------
/SaltAndSanctuary/shaders/MainEffectLightsBlur.fx:
--------------------------------------------------------------------------------
1 | #define HAS_LIGHTS 1
2 | #define HAS_BLUR 1
3 | #include "MainEffectCommon.ifx"
--------------------------------------------------------------------------------
/SaltAndSanctuary/shaders/MainEffectLightsNoBlur.fx:
--------------------------------------------------------------------------------
1 | #define HAS_LIGHTS 1
2 | #define HAS_BLUR 0
3 | #include "MainEffectCommon.ifx"
--------------------------------------------------------------------------------
/SaltAndSanctuary/shaders/MainEffectNoLightsBlur.fx:
--------------------------------------------------------------------------------
1 | #define HAS_LIGHTS 0
2 | #define HAS_BLUR 1
3 | #include "MainEffectCommon.ifx"
--------------------------------------------------------------------------------
/SaltAndSanctuary/shaders/MainEffectNoLightsNoBlur.fx:
--------------------------------------------------------------------------------
1 | #define HAS_LIGHTS 0
2 | #define HAS_BLUR 0
3 | #include "MainEffectCommon.ifx"
--------------------------------------------------------------------------------
/deps/FNA/.extract_fna_here:
--------------------------------------------------------------------------------
1 | You should extract the necessary FNA build here, you should have the follwoing files:
2 | FNA.dll OpenAL-CS.dll SDL2-CS.dll
--------------------------------------------------------------------------------
/deps/Harmony/.extract_harmony_here:
--------------------------------------------------------------------------------
1 | You should have the following structure here:
2 | Lib.Harmony.****.nupkg net35 net45 net472 net48 net5.0 netcoreapp3.0 netcoreapp3.1
--------------------------------------------------------------------------------
/Chasm/Makefile.inc:
--------------------------------------------------------------------------------
1 | SRC := $(MAKECMDGOALS)/ChasmPatches.cs
2 | ASM := $(SRC:%.cs=out/%.dll)
3 | DEPS_FOLDER ?= deps/$(MAKECMDGOALS)
4 | DEPS := $(DEPS_FOLDER)/Chasm.exe \
5 | deps/FNA/FNA.dll \
6 | deps/Harmony/net45/0Harmony.dll
7 |
--------------------------------------------------------------------------------
/Celeste/Makefile.inc:
--------------------------------------------------------------------------------
1 | SRC := $(MAKECMDGOALS)/CelestePatches.cs
2 | ASM := $(SRC:%.cs=out/%.dll)
3 | DEPS_FOLDER ?= deps/$(MAKECMDGOALS)
4 | DEPS := $(DEPS_FOLDER)/Celeste.exe \
5 | deps/FNA/FNA.dll \
6 | deps/Harmony/net45/0Harmony.dll
7 |
--------------------------------------------------------------------------------
/SaltAndSanctuary/Makefile.inc:
--------------------------------------------------------------------------------
1 | SRC := $(MAKECMDGOALS)/SaltPatches.cs
2 | ASM := $(SRC:%.cs=out/%.dll)
3 | DEPS_FOLDER ?= deps/$(MAKECMDGOALS)
4 | DEPS := $(DEPS_FOLDER)/Salt.exe \
5 | deps/FNA/FNA.dll \
6 | deps/Harmony/net45/0Harmony.dll
7 |
--------------------------------------------------------------------------------
/Anodyne/Makefile.inc:
--------------------------------------------------------------------------------
1 | SRC := $(MAKECMDGOALS)/AnodynePatches.cs
2 | ASM := $(SRC:%.cs=out/%.dll)
3 | DEPS_FOLDER ?= deps/$(MAKECMDGOALS)
4 | DEPS := $(DEPS_FOLDER)/AnodyneSharp.exe \
5 | deps/FNA/FNA.dll \
6 | deps/Harmony/net45/0Harmony.dll
7 |
--------------------------------------------------------------------------------
/Shipwreck/Makefile.inc:
--------------------------------------------------------------------------------
1 | SRC := $(MAKECMDGOALS)/ShipwreckPatches.cs
2 | ASM := $(SRC:%.cs=out/%.dll)
3 | DEPS_FOLDER ?= deps/$(MAKECMDGOALS)
4 | DEPS := $(DEPS_FOLDER)/Shipwreck.exe \
5 | deps/Shipwreck/FNA.dll \
6 | deps/Harmony/net45/0Harmony.dll
7 |
--------------------------------------------------------------------------------
/SteelAssault/Makefile.inc:
--------------------------------------------------------------------------------
1 | SRC := $(MAKECMDGOALS)/SteelAssaultCsPatches.cs
2 | ASM := $(SRC:%.cs=out/%.dll)
3 | DEPS_FOLDER ?= deps/$(MAKECMDGOALS)
4 | DEPS := $(DEPS_FOLDER)/SteelAssaultCs.exe \
5 | deps/FNA/FNA.dll \
6 | deps/Harmony/net45/0Harmony.dll
7 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | **/bin/**
2 | **/obj/**
3 | out/**
4 | deps/**
5 | **/monomod/**
6 | **/*.dll
7 | **/*.exe
8 | !deps/*/.*here
9 | !deps/FNA
10 | !deps/Harmony
11 | !deps/MonoMod
12 | **/shaders/out/**
13 | dxsdk/*
14 | !dxsdk/.copy_fxc_exe_here
15 | shaders/out/*
16 | .vscode/**
--------------------------------------------------------------------------------
/PanzerPaladin/Makefile.inc:
--------------------------------------------------------------------------------
1 | SRC := $(wildcard $(MAKECMDGOALS)/*.PanzerPaladin.mm.cs)
2 | ASM := $(SRC:%.cs=out/%.dll)
3 | DEPS_FOLDER ?= deps/$(MAKECMDGOALS)
4 | DEPS := $(DEPS_FOLDER)/PanzerPaladin.exe \
5 | $(DEPS_FOLDER)/ParisEngine.dll \
6 | deps/FNA/FNA.dll \
7 | deps/MonoMod/MonoMod.exe
8 |
--------------------------------------------------------------------------------
/SaltAndSanctuary/shaders/ClearAlpha.fx:
--------------------------------------------------------------------------------
1 | float4 ClearAlphaPS(float2 texCoord : TEXCOORD0) : COLOR0
2 | {
3 | return float4(1.0, 1.0, 1.0, 0.0);
4 | }
5 |
6 | technique ClearAlphaTechnique
7 | {
8 | pass Pass1
9 | {
10 | PixelShader = compile ps_3_0 ClearAlphaPS();
11 | }
12 | }
--------------------------------------------------------------------------------
/TMNTSR-Mods/Makefile.inc:
--------------------------------------------------------------------------------
1 | SRC := $(wildcard $(MAKECMDGOALS)/*.TMNTSRMods.mm.cs)
2 | ASM := $(SRC:%.cs=out/%.dll)
3 | DEPS_FOLDER ?= deps/$(MAKECMDGOALS)
4 | DEPS := $(DEPS_FOLDER)/TMNT.exe \
5 | $(DEPS_FOLDER)/ParisEngine.dll \
6 | deps/Cecil/*.dll \
7 | deps/FNA/FNA.dll \
8 | deps/MonoMod/MonoMod.exe
9 |
--------------------------------------------------------------------------------
/TMNTSR/Makefile.inc:
--------------------------------------------------------------------------------
1 | SRC := $(wildcard $(MAKECMDGOALS)/*.TMNTSRPatches.mm.cs)
2 | ASM := $(SRC:%.cs=out/%.dll)
3 | DEPS_FOLDER ?= deps/$(MAKECMDGOALS)
4 | DEPS := $(DEPS_FOLDER)/TMNT.exe \
5 | $(DEPS_FOLDER)/ParisEngine.dll \
6 | deps/Cecil/*.dll \
7 | deps/MonoMod/MonoMod.exe \
8 | deps/FNA/FNA.dll
9 |
--------------------------------------------------------------------------------
/Bleed/Makefile.inc:
--------------------------------------------------------------------------------
1 | SRC := $(MAKECMDGOALS)/Bleed.BleedPatches.mm.cs
2 | ASM := deps/$(MAKECMDGOALS)/MMHOOK_Bleed.dll $(SRC:%.cs=out/%.dll)
3 | DEPS_FOLDER ?= deps/$(MAKECMDGOALS)
4 | DEPS := $(DEPS_FOLDER)/Bleed.exe \
5 | deps/$(MAKECMDGOALS)/MMHOOK_Bleed.dll \
6 | deps/Cecil/*.dll \
7 | deps/FNA/FNA.dll \
8 | deps/MonoMod/MonoMod.exe \
9 | deps/MonoMod/MonoMod.Utils.dll
10 |
--------------------------------------------------------------------------------
/Anodyne/shaders/compile.bat:
--------------------------------------------------------------------------------
1 | mkdir out
2 | echo off
3 |
4 | rem -------------------------------
5 |
6 | setlocal enabledelayedexpansion
7 | for %%f in (*.fx) do (
8 | echo -- Compiling %%f --
9 | ..\..\dxsdk\fxc.exe /nologo /T fx_2_0 /O3 %%f /Fo out/%%f%b
10 | if !errorlevel! neq 0 exit /b !errorlevel!
11 | )
12 |
13 | echo -----------------------------
14 | echo - Compilation Successful :) -
15 | echo -----------------------------
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | PROJECTS:=$(wildcard */Makefile.inc)
2 | PROJECT_DIRS:=$(PROJECTS:%/Makefile.inc=%)
3 |
4 | all: $(PROJECTS) MMLoader libs
5 |
6 | # Suppress warnings when using the "clean" target.
7 | ifneq ($(MAKECMDGOALS), clean)
8 | $(MAKECMDGOALS):
9 | make -f Makefile.common $(MAKECMDGOALS)
10 | endif
11 |
12 | libs:
13 | make -C libs
14 |
15 | MMLoader:
16 | make -f Makefile.common MMLoader
17 |
18 | %/Makefile.inc: %
19 | make -f Makefile.common $<
20 |
21 | clean:
22 | @rm -rf out
23 |
24 | .PHONY: all $(PROJECT_DIRS) $(MAKECMDGOALS) libs
--------------------------------------------------------------------------------
/SaltAndSanctuary/shaders/compile.bat:
--------------------------------------------------------------------------------
1 | mkdir out
2 | echo off
3 |
4 | rem -- Setup shader detail level --
5 |
6 | rem - HAS_GRAIN: Number of maximum grain taps on parchmentEffect and gameParchEffect
7 | set HAS_GRAIN=0
8 |
9 | rem -------------------------------
10 |
11 | setlocal enabledelayedexpansion
12 | for %%f in (*.fx) do (
13 | echo -- Compiling %%f --
14 | ..\..\dxsdk\fxc.exe /nologo /DHAS_GRAIN=%HAS_GRAIN% /T fx_2_0 /O3 %%f /Fo out/%%f%b
15 | if !errorlevel! neq 0 exit /b !errorlevel!
16 | )
17 |
18 | echo -----------------------------
19 | echo - Compilation Successful :) -
20 | echo -----------------------------
--------------------------------------------------------------------------------
/TMNTSR-Mods/TMNTSR-Mods.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net5.0
6 | $(AssemblySearchPaths);{GAC};..\deps;..\deps\TMNTSR-Mods;..\deps\MonoMod;..\deps\Cecil
7 | .\deps;$(ReferencePath)
8 | true
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/deps/MonoMod/.extract_monomod_here:
--------------------------------------------------------------------------------
1 | You should have the following files on this directory:
2 | 0Harmony.dll Mono.Cecil.Pdb.dll MonoMod.Common.dll MonoMod.DebugIL.exe.config
3 | 0Harmony.xml Mono.Cecil.Rocks.dll MonoMod.Common.xml MonoMod.DebugIL.xml
4 | Mono.Cecil.Mdb.dll Mono.Cecil.dll MonoMod.DebugIL.exe MonoMod.RuntimeDetour.HookGen.exe
5 | MonoMod.RuntimeDetour.HookGen.exe.config MonoMod.RuntimeDetour.xml MonoMod.Utils.dll MonoMod.exe.config
6 | MonoMod.RuntimeDetour.HookGen.xml MonoMod.UnitTest.dll MonoMod.Utils.xml MonoMod.xml
7 | MonoMod.RuntimeDetour.dll MonoMod.UnitTest.xml MonoMod.exe
--------------------------------------------------------------------------------
/libs/Makefile:
--------------------------------------------------------------------------------
1 | CFLAGS?=-O3 -shared -rdynamic
2 |
3 | all: ../out/libs ../out/libs/fmodstudio_fix.so ../out/libs/EOSSDK-Win64-Shipping.so ../out/libs/libsteam_api.so
4 |
5 | clean:
6 | @rm -f ../out/libs/*
7 |
8 | ../out/libs:
9 | mkdir -p ../out/libs
10 |
11 | ../out/libs/fmodstudio_fix.so: ../out/libs fmodstudio_fix.c
12 | $(CC) -Wno-return-type $(CFLAGS) fmodstudio_fix.c -o ../out/libs/fmodstudio_fix.so
13 |
14 | ../out/libs/EOSSDK-Win64-Shipping.so: ../out/libs EOSSDK-Shipping-Stubs.c
15 | $(CC) -Wno-return-type $(CFLAGS) EOSSDK-Shipping-Stubs.c -o ../out/libs/EOSSDK-Win64-Shipping.so
16 |
17 | ../out/libs/libsteam_api.so: ../out/libs SteamStubs.c
18 | $(CC) -Wno-return-type $(CFLAGS) SteamStubs.c -o ../out/libs/libsteam_api.so
19 | @cp ../out/libs/libsteam_api.so ../out/libs/libsteam_api64.so
20 | @cp ../out/libs/libsteam_api.so ../out/libs/CSteamworks
21 |
--------------------------------------------------------------------------------
/SaltAndSanctuary/shaders/SimplifiedMapBlood.fx:
--------------------------------------------------------------------------------
1 | // mapblood.fx
2 | // XNA
3 |
4 | sampler bloodSampler;
5 | float bloodAlpha;
6 | float bloodSat;
7 |
8 | struct PS_INPUT
9 | {
10 | float4 Color : COLOR0;
11 | float2 TexCoord : TEXCOORD0;
12 | float4 VPos : VPOS;
13 | };
14 |
15 | float4 MapBlood(PS_INPUT Input) : COLOR0
16 | {
17 | float2 tex = Input.TexCoord;
18 |
19 | float4 blood = tex2D(bloodSampler, tex);
20 | float4 color;
21 | float c = blood.a * bloodAlpha * Input.Color.a * 0.75;
22 | color.rgb = blood.rgb * Input.Color.rgb * c;
23 | // Make the darker spots take more of the color away. Make it black.
24 | color.rgb += c * float3(0.35, 0.45, 0.45);
25 |
26 | return float4(color.rgb, 1.0);
27 | }
28 |
29 | technique PostBlood
30 | {
31 | pass P0
32 | {
33 | PixelShader = compile ps_3_0 MapBlood();
34 | }
35 | }
--------------------------------------------------------------------------------
/SaltAndSanctuary/shaders/SimplifiedParchment.fx:
--------------------------------------------------------------------------------
1 | // parchment.fx
2 | // XNA
3 |
4 | texture SampleTexture : register(t0);
5 | sampler2D t0_sampler = sampler_state {
6 | Texture = (SampleTexture);
7 | MagFilter = Linear;
8 | MinFilter = Linear;
9 | AddressU = Clamp;
10 | AddressV = Clamp;
11 | };
12 |
13 | float pX;
14 | float pY;
15 | float pA;
16 | float r;
17 | float g;
18 | float b;
19 |
20 | float4 Parchment(float2 texCoord : TEXCOORD0) : COLOR0
21 | {
22 | float2 tex = texCoord;
23 | float4 col = float4(r, g, b, 0.0f);
24 |
25 | float dif;
26 | float2 vDif = texCoord - 0.5f;
27 | vDif *= vDif;
28 | dif = vDif.x + vDif.y;
29 |
30 | float2 texFrac = frac(tex + float2(pX, pY));
31 | texFrac -= step(-texFrac, 0.0);
32 | #if HAS_GRAIN > 0
33 | col.a += tex2D(t0_sampler, texFrac).r * dif * pA;
34 | #else
35 | col.a += 0.55f * dif * pA;
36 | #endif
37 | return col;
38 | }
39 |
40 | technique ParchmentTechnique
41 | {
42 | pass P0
43 | {
44 | PixelShader = compile ps_3_0 Parchment();
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/SaltAndSanctuary/shaders/SimplifiedGaussianBlur.fx:
--------------------------------------------------------------------------------
1 | // Pixel shader applies a one dimensional gaussian blur filter.
2 | // This is used twice by the bloom postprocess, first to
3 | // blur horizontally, and then again to blur vertically.
4 |
5 | texture SampleTexture : register(t0);
6 | sampler2D s0_sampler = sampler_state {
7 | Texture = (SampleTexture);
8 | MagFilter = Linear;
9 | MinFilter = Linear;
10 | AddressU = Clamp;
11 | AddressV = Clamp;
12 | };
13 |
14 | #define SAMPLE_COUNT 7
15 |
16 | float2 SampleOffsets[SAMPLE_COUNT];
17 | float SampleWeights[SAMPLE_COUNT];
18 |
19 | float4 PixelShaderFunction(float2 texCoord : TEXCOORD0) : COLOR0
20 | {
21 | float4 c = 0;
22 |
23 | // Combine a number of weighted image filter taps.
24 | for (int i = 0; i < SAMPLE_COUNT; i++)
25 | {
26 | c += tex2D(s0_sampler, texCoord + SampleOffsets[i]) * SampleWeights[i];
27 | }
28 |
29 | return c;
30 | }
31 |
32 |
33 | technique GaussianBlur
34 | {
35 | pass Pass1
36 | {
37 | PixelShader = compile ps_3_0 PixelShaderFunction();
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Johnny on Flame.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Anodyne/shaders/bilinear-sharp.fx:
--------------------------------------------------------------------------------
1 | #define _vs(r) : register(vs, r)
2 | #define _ps(r) : register(ps, r)
3 | #define _cb(r)
4 |
5 | float4x4 MatrixTransform _vs(c0) _cb(c0);
6 |
7 | texture s0_texture : register(t0);
8 | sampler2D s0_sampler = sampler_state {
9 | Texture = (s0_texture);
10 | MagFilter = Linear;
11 | MinFilter = Linear;
12 | AddressU = Clamp;
13 | AddressV = Clamp;
14 | };
15 |
16 | float2 TexSize;
17 | float2 Scale;
18 |
19 | float4 SpritePixelShader(float4 color : COLOR0,
20 | float2 texCoord : TEXCOORD0) : SV_Target0
21 | {
22 | float2 vTexCoord = (texCoord * TexSize);
23 | float2 texel_floored = floor(vTexCoord);
24 | float2 s = frac(vTexCoord);
25 | float2 region_range = 0.5 - 0.5 / Scale;
26 | float2 center_dist = s - 0.5;
27 | float2 f = (center_dist - clamp(center_dist, -region_range, region_range)) * Scale + 0.5;
28 | float2 mod_texel = texel_floored + f;
29 | float4 col = tex2D(s0_sampler, mod_texel / TexSize);
30 | return col;
31 | }
32 |
33 | technique SpriteBatch
34 | {
35 | pass P0
36 | {
37 | PixelShader = compile ps_3_0 SpritePixelShader();
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/Makefile.common:
--------------------------------------------------------------------------------
1 | MONO_PREFIX ?= /usr
2 | MONO ?= $(MONO_PREFIX)/bin/mono
3 | MCS ?= $(MONO_PREFIX)/bin/mcs
4 | MONO_PATH ?= $(MONO_PREFIX)/lib/mono/4.5
5 | COMMON_DEPS := $(MONO_PATH)/Facades/netstandard.dll $(MONO_PATH)/mscorlib.dll $(MONO_PATH)/Facades/System.Runtime.dll $(wildcard $(MONO_PATH)/*.dll)
6 | COMPILER ?= VisFree/bin/Release/VisFree.exe
7 | HOOK_GEN ?= MonoMod.RuntimeDetour.HookGen.exe
8 |
9 | -include $(MAKECMDGOALS)/Makefile.inc
10 |
11 | .PHONY: out/$(MAKECMDGOALS) MMLoader clean
12 |
13 | all:
14 | clean:
15 | rm -rf out/
16 |
17 | $(MAKECMDGOALS): out/$(MAKECMDGOALS) $(ASM)
18 |
19 | out/$(MAKECMDGOALS):
20 | @mkdir -p out/$(MAKECMDGOALS)/
21 |
22 | deps/$(MAKECMDGOALS)/MMHOOK_%.dll: deps/$(MAKECMDGOALS)/%.exe
23 | @cd deps/MonoMod && MONO_PATH=$(MONO_PATH) $(MONO) $(HOOK_GEN) ../../$^
24 |
25 | deps/$(MAKECMDGOALS)/MMHOOK_%.dll: deps/$(MAKECMDGOALS)/%.dll
26 | @cd deps/MonoMod && MONO_PATH=$(MONO_PATH) $(MONO) $(HOOK_GEN) ../../$^
27 |
28 | out/$(MAKECMDGOALS)/%.dll: $(MAKECMDGOALS)/%.cs
29 | @MONO_PATH=$(MONO_PATH) $(MONO) $(COMPILER) $@ $< -- $(DEPS) $(COMMON_DEPS)
30 |
31 | MMLoader: out/MMLoader out/MMLoader/MMLoader.exe
32 | out/MMLoader/MMLoader.exe: MMLoader.cs
33 | @MONO_PATH=$(MONO_PATH) $(MCS) -optimize MMLoader.cs /out:out/MMLoader/MMLoader.exe
34 |
--------------------------------------------------------------------------------
/SaltAndSanctuary/shaders/SimplifiedTintBloom.fx:
--------------------------------------------------------------------------------
1 | struct InputStruct {
2 | float4 param0 : COLOR;
3 | float4 param1 : TEXCOORD;
4 | float4 param2 : POSITION;
5 | };
6 |
7 | struct OutputStruct {
8 | float4 param0 : COLOR;
9 | float4 param1 : TEXCOORD;
10 | float4 Position : SV_Position;
11 | };
12 |
13 | texture SampleTexture : register(t0);
14 | sampler2D s0_sampler = sampler_state {
15 | Texture = (SampleTexture);
16 | MagFilter = Linear;
17 | MinFilter = Linear;
18 | AddressU = Clamp;
19 | AddressV = Clamp;
20 | };
21 |
22 | float4x4 MatrixTransform;
23 | float3 tintColor;
24 | float a;
25 | float tint;
26 | float sat;
27 | float screenSizeX;
28 | float screenSizeY;
29 | float detailAlpha;
30 | float detailX1;
31 | float detailX2;
32 | float detailY1;
33 | float detailY2;
34 |
35 | float4 PixelShaderFunction(
36 | float4 color : COLOR0,
37 | float2 texCoord : TEXCOORD0,
38 | float4 vpos : VPOS) : SV_Target0
39 | {
40 | float4 col = tex2D(s0_sampler, texCoord) * color;
41 | float stint = tint * a;
42 | col.rgb = lerp(col.rgb, tintColor, stint);
43 |
44 | float b = dot(col.rgb, 1 / 3.0f);
45 | float isat = saturate(1.0f - sat);
46 |
47 | // Mix color channels
48 | col.rgb = lerp(col.rgb, float3(b, b, b), isat);
49 | // Pre-multiplied alpha
50 | col.rgb *= (col.a * a);
51 |
52 | return col;
53 | }
54 |
55 | technique Technique1
56 | {
57 | pass Pass1
58 | {
59 | PixelShader = compile ps_3_0 PixelShaderFunction();
60 | }
61 | }
--------------------------------------------------------------------------------
/SaltAndSanctuary/shaders/SimplifiedLight.fx:
--------------------------------------------------------------------------------
1 | texture LightTexture : register(t0);
2 | sampler2D LightTextureSampler = sampler_state {
3 | Texture = (LightTexture);
4 | MagFilter = Linear;
5 | MinFilter = Linear;
6 | AddressU = Clamp;
7 | AddressV = Clamp;
8 | };
9 |
10 | texture CloudsTexture : register(t1);
11 | sampler2D CloudsTextureSampler = sampler_state {
12 | Texture = (CloudsTexture);
13 | MagFilter = Linear;
14 | MinFilter = Linear;
15 | AddressU = Clamp;
16 | AddressV = Clamp;
17 | };
18 |
19 | float2 scroll = { 0.0f, 0.0f };
20 | float2 scroll2 = { 0.0f, 0.0f };
21 | float2 scroll3 = { 0.0f, 0.0f };
22 |
23 | float2 scrollDif = { 0.0f, 0.0f };
24 |
25 | float alpha = 1.0f;
26 | float lightFac = 1.0f;
27 |
28 | float4 LightMap(float2 texCoord : TEXCOORD0) : COLOR0
29 | {
30 | float4 col = tex2D(LightTextureSampler, texCoord);
31 |
32 | float aFac = col.r;
33 | // if (aFac > 0.5f)
34 | // aFac = 1.0f - aFac;
35 | // aFac *= 4.0f;
36 | // if (aFac > 1.0f)
37 | // aFac = 1.0f;
38 | // OR
39 | // The original code created a downwards V
40 | aFac = saturate(-(abs(aFac-0.5f)-0.5f)*4.0f);
41 | // OR
42 | //aFac = smoothstep(0.0, 0.25, aFac) + smoothstep(1.0, 0.75, aFac) - 1;
43 |
44 | float c = dot(col, 1.0f / 3.0f);
45 | c += aFac * lightFac;
46 | //c = smoothstep(-0.1f, 0.96f, c);
47 | c = saturate(c);
48 |
49 | return float4(c, c, c, alpha);
50 | }
51 |
52 | technique LightMapTechnique
53 | {
54 | pass P0
55 | {
56 | PixelShader = compile ps_3_0 LightMap();
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/libs/fmodstudio_fix.c:
--------------------------------------------------------------------------------
1 | // FMODStudio Fix
2 | // This is a wrapper that bridges FMOD 1.x and FMOD 2.x.
3 | // This is necessary, but not enough by itself, to run games such as Celeste on ARM64 systems.
4 | // You'll also need:
5 | // - The appropriate libfmodstudio.so.13 and libfmod.so.13 files (from https://www.fmod.com/download)
6 | // - Any necessary plugin to enable fmod to use your audio backend. (such as https://github.com/flibitijibibo/FMOD_SDL)
7 | // - The necessary mono .config file for your title, as an example, Celeste.exe.config:
8 |
9 | /*
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | */
21 |
22 | #include
23 | #define FMOD_VERSION 0x00020206
24 |
25 | # define DECLSPEC __attribute__ ((visibility("default")))
26 | DECLSPEC int _FMOD_Studio_EventInstance_SetParameterValue(void *system, const char *name, float value) {
27 | extern int FMOD_Studio_EventInstance_SetParameterByName(void *, const char *, float, int);
28 | return FMOD_Studio_EventInstance_SetParameterByName(system, name, value, 0);
29 | }
30 |
31 | DECLSPEC int _FMOD_Studio_System_Create(void *studiosystem, unsigned int headerversion)
32 | {
33 | // If this fails, check if the FMOD_VERSION number is correct.
34 | extern int FMOD_Studio_System_Create(void *, int);
35 | return FMOD_Studio_System_Create(studiosystem, FMOD_VERSION);
36 | }
37 |
--------------------------------------------------------------------------------
/SaltAndSanctuary/shaders/SimplifiedGameParchment.fx:
--------------------------------------------------------------------------------
1 | // gameparch.fx
2 | // XNA
3 |
4 | texture SampleTexture : register(t0);
5 | sampler2D t0_sampler = sampler_state {
6 | Texture = (SampleTexture);
7 | MagFilter = Linear;
8 | MinFilter = Linear;
9 | AddressU = Clamp;
10 | AddressV = Clamp;
11 | };
12 |
13 | float baseFac = 1.0f;
14 | float parchFac = 1.0f;
15 | float h;
16 | float frame = 0.0f;
17 |
18 | struct PS_INPUT
19 | {
20 | float2 TexCoord : TEXCOORD0;
21 | };
22 |
23 | #define USE_GRAIN 1
24 | float4 Parchment(PS_INPUT Input) : COLOR0
25 | {
26 | float2 tex = Input.TexCoord;
27 |
28 | #if USE_GRAIN > 0
29 | // Check `fxc.exe SimplifiedGameParchment.fx /O3 /Cc /T fx_2_0` for why the code
30 | // is like this. Most of these calculations will be performed on the preshader,
31 | // effectively lowering this effect from three texel fetches to two, while also
32 | // reducing the amount of arithmetic instructions required.
33 |
34 | // Is the number lower than [0, 1]?
35 | float f1 = (1.0f - step(1.0f, frame));
36 |
37 | // Is the number between [1, 2]?
38 | float f2 = (1.0f - step(2.0f, frame)) * (1.0 - f1);
39 |
40 | // Is the number between [2, 3]?
41 | float f3 = (1.0f - step(3.0f, frame)) * (1.0 - f2);
42 |
43 | // Select a pair of coordinates based on which group the number was contained within
44 | float4 f_tex = 0.0f;
45 | f_tex += f1 * float4(tex.x, tex.y, 1.0f - tex.x, tex.y);
46 | f_tex += f2 * float4(1.0f - tex.x, tex.y, tex.x, 1.0f - tex.y);
47 | f_tex += f3 * float4(tex.x, 1.0f - tex.y, tex.x, tex.y);
48 |
49 | // Calculate the coefficients
50 | float2 coef = float2(ceil(frame) - frame, frame - floor(frame));
51 | #if USE_GRAIN == 2
52 | // Fetch the textures
53 | float r = tex2D(t0_sampler, f_tex.xy).r * (coef.x) +
54 | tex2D(t0_sampler, f_tex.zw).r * (coef.y);
55 | #else // USE_GRAIN == 2
56 | float r = tex2D(t0_sampler, f_tex.xy).r * (coef.x + coef.y);
57 | #endif // USE_GRAIN == 2
58 | #else // USE_GRAIN > 0
59 | float2 coef = float2(ceil(frame) - frame, frame - floor(frame));
60 | // Grain texture average brightness is ~0.55f
61 | float r = 0.55f * (coef.x + coef.y);
62 | #endif // USE_GRAIN > 0
63 | float4 rVal = 0.0f;
64 |
65 | float aFac = 1.0f - (tex.y / h);
66 |
67 | rVal.a = aFac * baseFac;
68 | rVal.a += r * aFac * parchFac;
69 | rVal.rgb = 1.0f;
70 |
71 | return rVal;
72 | }
73 |
74 | technique ParchmentTechnique
75 | {
76 | pass P0
77 | {
78 | PixelShader = compile ps_3_0 Parchment();
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/MMLoader.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.IO;
3 | using System.Linq;
4 | using System.Reflection;
5 | using System.Runtime.CompilerServices;
6 | using System.Runtime.InteropServices;
7 | using System.Security.Policy;
8 | using System.Threading;
9 | using System;
10 |
11 | public class MMLoader {
12 | public static void Main(string[] args)
13 | {
14 | if (args.Length < 1)
15 | {
16 | Console.Error.WriteLine("Syntax: MMLoader.exe [appBasePath] [-- appArgs...]");
17 | Console.Error.WriteLine(" asssembly: Assembly to be loaded.");
18 | Console.Error.WriteLine(" appBasePath: Optionally Override AppDomain.CurrentDomain.BaseDirectory.");
19 | Console.Error.WriteLine(" '--': Anything afterwards is passed as arguments to the Assembly.");
20 | System.Environment.Exit(-1);
21 | }
22 |
23 | if (!File.Exists(args[0]))
24 | {
25 | Console.Error.WriteLine($"Unable to find assembly \"{args[0]}\"!");
26 | System.Environment.Exit(-1);
27 | }
28 |
29 | // Gather Arguments
30 | string assemblyFilePath = Path.GetDirectoryName(Path.GetFullPath(args[0]));
31 | Assembly assembly = Assembly.ReflectionOnlyLoadFrom(args[0]);
32 | AssemblyName assemblyName = assembly.GetName();
33 | string appBasePath = assemblyFilePath;
34 | string[] appArgs = new string[] { };
35 | try
36 | {
37 | int i = 1;
38 | if ((string)args.GetValue(i)?.ToString() != "--")
39 | appBasePath = (string)args?.GetValue(i++);
40 |
41 | if ((string)args?.GetValue(i) == "--")
42 | appArgs = args?.Skip(i+1).ToArray();
43 | }
44 | catch (Exception) { }
45 |
46 | Console.Out.WriteLine($"assemblyName: {assemblyName.Name}");
47 | Console.Out.WriteLine($"appBasePath: {appBasePath}");
48 | Console.Out.WriteLine($"appArgs: {{{string.Join(",", appArgs)}}}");
49 |
50 | // Create domain and load the base assembly
51 | Evidence evidence = new Evidence(AppDomain.CurrentDomain.Evidence);
52 | AppDomain domain = AppDomain.CreateDomain(assemblyName.Name, evidence, appBasePath, appBasePath, false);
53 | // This can cause issue with Assemblies that use the assembly path, such as Logging libraries
54 | //domain.Load(File.ReadAllBytes(Path.GetFullPath(args[0])));
55 |
56 | // See if we have MONOMODDED versions to load first...
57 | foreach (var asm in assembly.GetReferencedAssemblies())
58 | {
59 | string asmFileName = $"MONOMODDED_{asm.Name}.dll";
60 | if (File.Exists(asmFileName))
61 | {
62 | Console.Out.WriteLine($"Redirecting {asm.Name} to {asmFileName}");
63 | domain.Load(File.ReadAllBytes(Path.GetFullPath(asmFileName)));
64 | }
65 | }
66 |
67 | // Change current directory and jump into the assembly
68 | Directory.SetCurrentDirectory(appBasePath);
69 | domain.ExecuteAssembly(args[0], appArgs);
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/Bleed/Bleed.BleedPatches.mm.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Xna.Framework.Graphics;
2 | using Microsoft.Xna.Framework;
3 | using Mono.Cecil.Cil;
4 | using MonoMod.Cil;
5 |
6 | #pragma warning disable CS0626
7 |
8 | namespace TimeGuy
9 | {
10 | class patch_Program
11 | {
12 | // We're going to apply some Runtime Hooks before the init, so let's change the behavior of Program.Main:
13 | extern static void orig_Main(string[] args);
14 | private static void Main(string[] args)
15 | {
16 | // For this, you need to create the MMHOOK_Bleed.dll and reference it during build time.
17 | // Refer to the MonoMod.RuntimeDetour.HookGen.exe documentation.
18 | IL.TimeGuy.Game1.ctor += Hook_Game1_ctor;
19 |
20 | // Give execution back to the game
21 | orig_Main(args);
22 | }
23 |
24 | private static void Hook_Game1_ctor(ILContext il)
25 | {
26 | /*
27 | Consider the following line on Bleed's code:
28 |
29 | > ResolutionHelper.SetResolution(
30 | this.graphics.GraphicsDevice.DisplayMode.Width,
31 | this.graphics.GraphicsDevice.DisplayMode.Height,
32 | true);
33 |
34 | The `This.graphics.GraphicsDevice` member is NULL at this point on certain FNA+SDL2 configs,
35 | so to ensure it won't crash, we're going to use GraphicsAdapter.DefaultAdapter to query the
36 | default adapter's current display mode instead, so we need to patch the IL to do the following:
37 |
38 | > ResolutionHelper.SetResolution(
39 | GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Width,
40 | GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Width,
41 | true);
42 | */
43 | ILCursor ilCursor = new ILCursor(il);
44 | if (ilCursor.TryGotoNext(MoveType.Before,
45 | i => i.MatchLdarg(0),
46 | i => i.MatchLdfld(typeof(TimeGuy.Game1), "graphics"),
47 | i => i.MatchCallvirt(typeof(GraphicsDeviceManager), "get_GraphicsDevice"),
48 | i => i.MatchCallvirt(typeof(GraphicsDevice), "get_DisplayMode"),
49 | i => i.MatchCallvirt(typeof(DisplayMode), "get_Width"),
50 | i => i.MatchLdarg(0),
51 | i => i.MatchLdfld(typeof(TimeGuy.Game1), "graphics"),
52 | i => i.MatchCallvirt(typeof(GraphicsDeviceManager), "get_GraphicsDevice"),
53 | i => i.MatchCallvirt(typeof(GraphicsDevice), "get_DisplayMode"),
54 | i => i.MatchCallvirt(typeof(DisplayMode), "get_Height")
55 | ))
56 | {
57 | // Remove the above instructions
58 | ilCursor = ilCursor.RemoveRange(10);
59 |
60 | // Patch our new behavior in.
61 | ilCursor.Emit(OpCodes.Call, typeof(GraphicsAdapter).GetMethod("get_DefaultAdapter"));
62 | ilCursor.Emit(OpCodes.Callvirt, typeof(GraphicsAdapter).GetMethod("get_CurrentDisplayMode"));
63 | ilCursor.Emit(OpCodes.Callvirt, typeof(DisplayMode).GetMethod("get_Width"));
64 | ilCursor.Emit(OpCodes.Call, typeof(GraphicsAdapter).GetMethod("get_DefaultAdapter"));
65 | ilCursor.Emit(OpCodes.Callvirt, typeof(GraphicsAdapter).GetMethod("get_CurrentDisplayMode"));
66 | ilCursor.Emit(OpCodes.Callvirt, typeof(DisplayMode).GetMethod("get_Height"));
67 | }
68 | }
69 | }
70 | }
--------------------------------------------------------------------------------
/Anodyne/AnodynePatches.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Reflection;
3 | using System.Runtime.CompilerServices;
4 | using HarmonyLib;
5 | using Microsoft.Xna.Framework;
6 | using Microsoft.Xna.Framework.Graphics;
7 | using Microsoft.Xna.Framework.Content;
8 | using AnodyneSharp;
9 | using AnodyneSharp.Drawing;
10 | using AnodyneSharp.Registry;
11 |
12 | [assembly: IgnoresAccessChecksTo("AnodyneSharp")]
13 | [assembly: IgnoresAccessChecksTo("Microsoft.Xna.Framework")]
14 | [ModEntryPoint]
15 | public static class AnodynePatches
16 | {
17 | public static void Main()
18 | {
19 | Console.Out.WriteLine("Found AnodynePatches, running...");
20 | try
21 | {
22 | new Harmony("com.github.johnnyonflame.AnodynePatches").PatchAll(Assembly.GetExecutingAssembly());
23 | }
24 | catch (Exception e)
25 | {
26 | Console.Out.WriteLine($"Failed: {e.ToString()}");
27 | throw e;
28 | }
29 | }
30 |
31 | [HarmonyPatch(typeof(SpriteDrawer), nameof(SpriteDrawer.Load))]
32 | public static class SpriteDrawer_Load
33 | {
34 | public static Effect bilinear_sharp;
35 | public static bool Prefix(ContentManager c)
36 | {
37 | bilinear_sharp = c.Load("effects/bilinear-sharp");
38 | return true;
39 | }
40 | }
41 |
42 | [HarmonyPatch(typeof(SpriteDrawer), nameof(SpriteDrawer.Render))]
43 | public static class SpriteDrawer_Render
44 | {
45 | public static bool Prefix(Effect effect)
46 | {
47 | // We only override the behavior of Resolution.Stretch
48 | if (GlobalState.settings.resolution != Resolution.Stretch)
49 | return true;
50 |
51 | Rectangle r = new Rectangle();
52 | r.Width = SpriteDrawer._graphicsDevice.PresentationParameters.BackBufferWidth;
53 | r.Height = SpriteDrawer._graphicsDevice.PresentationParameters.BackBufferHeight;
54 | float aspectBackbuffer = (float)r.Width / (float)r.Height;
55 | float aspectGameBuffer = 160.0f / 180.0f;
56 |
57 | if (aspectBackbuffer > aspectGameBuffer) {
58 | r.Width = (int)((float)r.Height * aspectGameBuffer);
59 | } else {
60 | r.Height = (int)((float)r.Width / aspectGameBuffer);
61 | }
62 |
63 | r.X = SpriteDrawer._graphicsDevice.PresentationParameters.BackBufferWidth / 2 - r.Width / 2;
64 | r.Y = SpriteDrawer._graphicsDevice.PresentationParameters.BackBufferHeight / 2 - r.Height / 2;
65 |
66 | SpriteDrawer_Load.bilinear_sharp.Parameters["TexSize"].SetValue(new Vector2(160.0f, 180.0f));
67 | SpriteDrawer_Load.bilinear_sharp.Parameters["Scale"].SetValue(new Vector2(r.Width / 160.0f, r.Height / 180.0f));
68 |
69 | SpriteDrawer._graphicsDevice.SetRenderTarget(null);
70 | SpriteDrawer._graphicsDevice.Clear(Color.Black);
71 | SpriteDrawer._spriteBatch.Begin(SpriteSortMode.Texture, BlendState.AlphaBlend, SpriteDrawer.SamplerState, null, null, SpriteDrawer_Load.bilinear_sharp, null);
72 | SpriteDrawer._spriteBatch.Draw(SpriteDrawer._render, r, SpriteDrawer.FullScreenFade);
73 | SpriteDrawer._spriteBatch.End();
74 | return false;
75 | }
76 | }
77 | }
78 |
79 | namespace System.Runtime.CompilerServices
80 | {
81 | [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
82 | public class IgnoresAccessChecksToAttribute : Attribute
83 | {
84 | public IgnoresAccessChecksToAttribute(string assemblyName)
85 | {
86 | AssemblyName = assemblyName;
87 | }
88 |
89 | public string AssemblyName { get; }
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ### What is in this repository?
2 | -------------
3 |
4 | This repository contains game-specific hacks and patches for Mono/FNA games on low power SBCs and handhelds, and the
5 | required tooling to comfortably develop and deploy said patches are provided here.
6 |
7 | - **FMODStudio Fix**: A dead simple FMOD Studio 1.x to FMOD Studio 2.x wrapper.
8 | - **SteamStubs**: A series of 0-stubs to allow DRM-Free games not to crash when Steam's libraries aren't present.
9 | - **EOSSDK-Shipping-Stubs**: A series of 0-stubs to allow games not to crash when Epic Online Services aren't present.
10 | - **ChasmPatches**: Small patches for Chasm fullscreen logic on very small resolutions.
11 | - **CelestePatches**: Extensions to allow loading ASTC4x4 textures and Graphical Settings.
12 | - **SteelAssaultCsPatches**: A series of optimizations targeting low-power devices.
13 | - **PanzerPaladinPatches**: Fixes breakage when running non-Steam, newer FNA builds and does general optimizations.
14 | - **BleedPatches**: Fixes startup failures on certain FNA+SDL2 combinations.
15 | - **TMNTSR-Mods**: Load custom sound assets on TMNT: Shredder's Revenge.
16 | - **TMNTSRPatches**: Provides Performance settings, fixes Deadlocks on OpenGL, fixes file paths on Linux and more.
17 | - **MMLoader**: Force loading applications with MONOMODDED_*.dll redirection, change the AppDomain's BaseDirectory and more.
18 |
19 | ### Usage:
20 | -------------
21 |
22 | This isn't meant for end-users, but for development reference or contribution. Please refer to your favorite supported packaging system, such as [PortMaster](https://github.com/christianhaitian/PortMaster).
23 |
24 | ### Developing:
25 | -------------
26 |
27 | What follows is a non-exhaustive guide on how to first build the patch, it won't go into details on how to actually
28 | work with Harmony or how to approach the patch development. If you're looking for those, read [Harmony's documentation](https://harmony.pardeike.net/articles/intro.html), [MonoMod's documentation](https://github.com/MonoMod/MonoMod#readme) and check out [ILSpy](https://github.com/icsharpcode/ILSpy).
29 |
30 | ### Building:
31 | -------------
32 |
33 | - [Optional] Build mono with a local prefix.
34 | - Clone this repository recursively.
35 | - Build the VisFree compiler (check the project's README for instructions).
36 | - [Optional] Build the stubs and support libraries.
37 | - Copy any necessary dependencies (such as game assemblies) into the `deps` folder, refer to the dot files included.
38 | - Build one or all of the included projects, you have a few optional, configurable parameters:
39 | - `DEPS_FOLDER`: Where the referenced game assemblies are.
40 | - `MONO_PREFIX`: Where your mono install is.
41 | - `COMPILER`: Where your `VisFree.exe` assembly is.
42 |
43 | Example:
44 |
45 | ```bash
46 | export CONFIGURATION=Debug
47 | export COMPILER=VisFree/bin/Debug/VisFree.exe
48 | export MONO_PREFIX=~/mono-6.12.0.122/built
49 | export DEPS_FOLDER=~/.steam/steam/SteamApps/common/Celeste
50 | git clone --recursive https://github.com/JohnnyonFlame/FNAPatches
51 | (cd deps/Harmony && wget https://github.com/pardeike/Harmony/releases/download/v2.2.1.0/Harmony.2.2.1.0.zip && unzip Harmony.2.2.1.0.zip)
52 | (cd deps/MonoMod && wget https://github.com/MonoMod/MonoMod/releases/download/v22.05.01.01/MonoMod-22.05.01.01-net452.zip && unzip MonoMod-22.05.01.01-net452.zip)
53 | (cd deps/FNA && wget https://github.com/JohnnyonFlame/FNAHacks/releases/download/IntPtr-SoundEffect/FNA_3d954a5-aarch64.tar.gz && tar zxvf FNA_3d954a5-aarch64.tar.gz --strip-components=1 --wildcards dlls/\*.dll)
54 | make -C VisFree
55 | make libs
56 | make Celeste
57 | ```
58 |
59 | ### LICENSE:
60 | -------------
61 |
62 | The files in this repository are free software licensed under MIT, read the [License File](LICENSE) for reference.
63 |
--------------------------------------------------------------------------------
/SaltAndSanctuary/shaders/MainEffectCommon.ifx:
--------------------------------------------------------------------------------
1 | // main.fx
2 | // XNA
3 |
4 | sampler samplerState : register(s0);
5 | sampler lightTex : register(s1);
6 | sampler bayerTex: register(s2);
7 |
8 | // main pass
9 | cbuffer MyBuffer
10 | {
11 | float3 tColor = float3(0.0f, 0.0f, 0.0f);
12 | float3 bColor = float3(0.0f, 0.0f, 0.0f);
13 | float3 burnColor = float3(0.0f, 0.0f, 0.0f);
14 |
15 | //float sat = 1.0f;
16 | float brite = 0.0f;
17 |
18 | float gamma = 0.0f;
19 |
20 | float k = -0.05f;
21 | float kcube = 0.25f;
22 |
23 | float alpha = 1.0f;
24 | float floorVal = 0.2f;
25 | float lightFac = 1.0f;
26 | float lightFacBloom = 1.0f;
27 |
28 | float BloomVignette;
29 | float BaseIntensity;
30 | float BaseSaturation;
31 | float lightThresh = 0.5f;
32 | float2 lightRedBlue = float2(0.5f, 3.0f);
33 | float lightDesat = 1.0f;
34 | float darkBlur = 0.5f;
35 |
36 | float2 Dimensions;
37 | float ditherMinPixel;
38 | };
39 |
40 | // Helper for modifying the saturation of a color.
41 | float3 AdjustSaturation(float3 color, float saturation)
42 | {
43 | // The constants 0.3, 0.59, and 0.11 are chosen because the
44 | // human eye is more sensitive to green light, and less to blue.
45 | float grey = dot(color, float3(0.3, 0.59, 0.11));
46 |
47 | return lerp(grey, color, saturation);
48 | }
49 |
50 | void SpriteVertexShader(
51 | inout float4 color : COLOR0,
52 | inout float2 texCoord : TEXCOORD0,
53 | inout float4 position : SV_Position)
54 | {
55 | }
56 |
57 | float4 Main(
58 | float4 color : COLOR0,
59 | float2 texCoord : TEXCOORD0) : SV_Target0
60 | {
61 | float4 col = tex2D(samplerState, texCoord);
62 |
63 | float2 vDif = texCoord - 0.5f;
64 | vDif = vDif * vDif;
65 |
66 | float dif = saturate(vDif.x + vDif.y);
67 | // Stock shader would've applied some dithering based on the noise texture
68 | // we don't do that.
69 |
70 | // Do mainEffect pass
71 | float3 gBurn = col.rgb * burnColor;
72 | col.rgb *= lerp(tColor, bColor, texCoord.y);
73 | col.rgb = lerp(col.rgb, gBurn, dif);
74 | col.rgb = saturate(gamma + col.rgb * brite);
75 |
76 | #if HAS_LIGHTS
77 | // Do light pass
78 | float4 lightCol = tex2D(lightTex, texCoord);
79 | float aFac = lightCol.r;
80 |
81 | // if (aFac > 0.5f)
82 | // aFac = 1.0f - aFac;
83 | // aFac *= 4.0f;
84 | // if (aFac > 1.0f)
85 | // aFac = 1.0f;
86 | // -- OR --
87 | aFac = saturate(-(abs(aFac-0.5f)-0.5f)*4.0f);
88 |
89 |
90 | float preC = dot(lightCol.rgb, 1.0 / 3.0);
91 | float c = preC + aFac * lightFac;
92 | //if (c >= 0.9f)
93 | // c = 1.0f;
94 | //if (c <= 0.0f)
95 | // c = 0.0f;
96 | // -- OR --
97 | c = saturate(c);
98 |
99 | // Perform the subtractive blending manually
100 | col.rgb = saturate(col.rgb - (c * alpha));
101 | #else
102 | float preC = 1.0f;
103 | #endif
104 |
105 | #if HAS_BLUR == 0
106 | float lightBalance = 0.0f;
107 | float difVignette = length(texCoord - 0.5f);
108 | float tBase = BaseIntensity - difVignette * BloomVignette;
109 | float baseSat = BaseSaturation;
110 |
111 | float lightIntensity = 1.0f - preC;
112 | float satFac = 1.0f + (lightIntensity - 1.0f) * lightFacBloom * lightDesat;
113 |
114 | // Original code had some conditionals here, it's not necessary, just limit
115 | // lightFacBloom to not be negative and we're golden
116 | baseSat *= satFac;
117 | tBase -= (1.0f - lightIntensity) * lightFacBloom * darkBlur;
118 | lightBalance = (lightIntensity - lightThresh) * lightFacBloom;
119 |
120 | col.rgb = AdjustSaturation(col.rgb, baseSat) * tBase;
121 |
122 | //if (lightBalance < 0.0f)
123 | // col.b *= (1.0f + lightBalance * -lightBlue);
124 | //if (lightBalance > 0.0f)
125 | // col.r *= (1.0f + lightBalance * lightRed);
126 | // -- OR --
127 | float direction = step(0.0f, lightBalance);
128 | float2 directions = float2(direction, 1-direction);
129 | col.rb *= (1.0f + lightBalance * lightRedBlue * directions);
130 |
131 | col.rgb = saturate(col.rgb);
132 | #endif
133 | float2 texCoordDither = texCoord * Dimensions;
134 | float ditherValue = tex2D(bayerTex, texCoordDither).a * ditherMinPixel;
135 |
136 | // Do subtractive blend
137 | return float4(col.rgb + ditherValue, 1.0f);
138 | }
139 |
140 | technique MainTechnique
141 | {
142 | pass P0
143 | {
144 | //VertexShader = compile vs_3_0 SpriteVertexShader();
145 | PixelShader = compile ps_3_0 Main();
146 | }
147 | }
148 |
--------------------------------------------------------------------------------
/Shipwreck/ShipwreckPatches.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.CompilerServices;
3 | using System.Reflection;
4 | using HarmonyLib;
5 | using Microsoft.Xna.Framework;
6 | using Microsoft.Xna.Framework.Graphics;
7 | using Microsoft.Xna.Framework.Content;
8 | using Shipwreck;
9 | using Shipwreck.Layers;
10 |
11 | [assembly: IgnoresAccessChecksTo("Shipwreck")]
12 | [assembly: IgnoresAccessChecksTo("Microsoft.Xna.Framework")]
13 | [ModEntryPoint]
14 | public static class ShipwreckPatches
15 | {
16 | private static Effect effect_scaler = null;
17 |
18 | public static void Main()
19 | {
20 | Console.Out.WriteLine("Found ShipwreckPatches, running...");
21 | try
22 | {
23 | new Harmony("com.github.johnnyonflame.ShipwreckPatches").PatchAll(Assembly.GetExecutingAssembly());
24 | }
25 | catch (Exception e)
26 | {
27 | Console.Out.WriteLine($"Failed: {e.ToString()}");
28 | throw e;
29 | }
30 | }
31 |
32 | // Disable this, causes a feedback loop of going fullscreen and back due to SDL2 behavior
33 | [HarmonyPatch(typeof(ShipwreckGame), nameof(ShipwreckGame.Window_ClientSizeChanged))]
34 | internal static class internalShipwreckGame_Window_ClientSizeChanged
35 | {
36 | static bool Prefix(ref ShipwreckGame __instance, object sender, EventArgs e)
37 | {
38 | return false;
39 | }
40 | }
41 |
42 | // We're changing the fullscreen behavior, don't let resize happen.
43 | [HarmonyPatch(typeof(ShipwreckGame), nameof(ShipwreckGame.Resize))]
44 | internal static class ShipwreckGame_Resize
45 | {
46 | static bool Prefix(ref ShipwreckGame __instance)
47 | {
48 | return false;
49 | }
50 | }
51 |
52 | [HarmonyPatch(typeof(ShipwreckGame), nameof(ShipwreckGame.ChangeFullScreen))]
53 | internal static class ShipwreckGame_ChangeFullScreen
54 | {
55 | static bool Prefix(ref ShipwreckGame __instance)
56 | {
57 | // Always fullscreen, use the draw override for this.
58 | __instance._graphics.PreferredBackBufferWidth = GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Width;
59 | __instance._graphics.PreferredBackBufferHeight = GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Height;
60 | __instance._graphics.ApplyChanges();
61 | return false;
62 | }
63 | }
64 |
65 |
66 | [HarmonyPatch]
67 | public class GameBase
68 | {
69 | [HarmonyReversePatch]
70 | [HarmonyPatch(typeof(Game), nameof(Game.Draw))]
71 | public static void Draw(Game instance, GameTime gameTime) => throw new NotImplementedException("stub");
72 |
73 | }
74 |
75 | [HarmonyPatch(typeof(ShipwreckGame), nameof(ShipwreckGame.Draw))]
76 | internal class ShipwreckGame_Draw
77 | {
78 |
79 | static bool Prefix(ref ShipwreckGame __instance, GameTime gameTime)
80 | {
81 | ContentManager cm = (__instance as Game).Content;
82 | // Windowed mode means we're just using the default behavior
83 | if (!Settings.FullScreen)
84 | return true;
85 |
86 | LayerManager.PreDraw();
87 | __instance.GraphicsDevice.SetRenderTarget(__instance._gameTarget);
88 | __instance.GraphicsDevice.Clear(__instance.ClearColor);
89 |
90 | __instance.Drawer.Begin();
91 | LayerManager.Draw(__instance.Drawer);
92 | __instance.Drawer.End();
93 | GameBase.Draw(__instance, gameTime);
94 |
95 | __instance.GraphicsDevice.SetRenderTarget(null);
96 | __instance.GraphicsDevice.Clear(Color.Black);
97 |
98 | if (ShipwreckPatches.effect_scaler == null)
99 | {
100 | ShipwreckPatches.effect_scaler = cm.Load("shimmerless");
101 | }
102 |
103 | Rectangle r = new Rectangle();
104 | r.Width = __instance._graphics.PreferredBackBufferWidth;
105 | r.Height = __instance._graphics.PreferredBackBufferHeight;
106 | float aspectBackbuffer = (float)r.Width / (float)r.Height;
107 | float aspectGameBuffer = __instance._gameTarget.Width / __instance._gameTarget.Height;
108 |
109 | if (aspectBackbuffer > aspectGameBuffer) {
110 | r.Width = (int)((float)r.Height * aspectGameBuffer);
111 | } else {
112 | r.Height = (int)((float)r.Width / aspectGameBuffer);
113 | }
114 |
115 | r.X = __instance._graphics.PreferredBackBufferWidth / 2 - r.Width / 2;
116 | r.Y = __instance._graphics.PreferredBackBufferHeight / 2 - r.Height / 2;
117 |
118 | ShipwreckPatches.effect_scaler.Parameters["TexSize"].SetValue(new Vector2(__instance._gameTarget.Width, __instance._gameTarget.Height));
119 | ShipwreckPatches.effect_scaler.Parameters["InputSize"].SetValue(new Vector2(__instance._gameTarget.Width, __instance._gameTarget.Height));
120 | ShipwreckPatches.effect_scaler.Parameters["OutputSize"].SetValue(new Vector2(r.Width, r.Height));
121 |
122 | __instance.Drawer.SpriteBatch.Begin(SpriteSortMode.Immediate, BlendState.Opaque, SamplerState.PointClamp, null, null, effect_scaler);
123 | __instance.Drawer.SpriteBatch.Draw(__instance._gameTarget, r, Color.White);
124 | __instance.Drawer.SpriteBatch.End();
125 |
126 | return false;
127 | }
128 | }
129 | }
130 |
131 | namespace System.Runtime.CompilerServices
132 | {
133 | [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
134 | public class IgnoresAccessChecksToAttribute : Attribute
135 | {
136 | public IgnoresAccessChecksToAttribute(string assemblyName)
137 | {
138 | AssemblyName = assemblyName;
139 | }
140 |
141 | public string AssemblyName { get; }
142 | }
143 | }
144 |
--------------------------------------------------------------------------------
/PanzerPaladin/ParisEngine.PanzerPaladin.mm.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using System.Linq;
4 | using System.Threading;
5 | using MonoMod;
6 | using Paris.Engine.Context;
7 | using Paris.System.Helper;
8 | using Paris.Engine.Graphics;
9 |
10 | #pragma warning disable 1591
11 | #pragma warning disable CS0649
12 | #pragma warning disable CS0626
13 |
14 | namespace Paris.System.Helper {
15 | static class patch_PathManager
16 | {
17 | public extern static string orig_NormalizePath(string path);
18 | public static string NormalizePath(string path)
19 | {
20 | // Normalize paths by replacing backslashes.
21 | // This fixes failures to find assets on Linux when Steam isn't running.
22 | return path.Replace("\\", "/");
23 | }
24 | }
25 | }
26 |
27 | namespace Paris.Engine {
28 | class patch_GameAnalytics : GameAnalytics
29 | {
30 | public extern void orig_Init();
31 | new public void Init()
32 | {
33 | // Disable GameAnalytics
34 | }
35 | }
36 |
37 | class patch_ParisContentManager: ParisContentManager
38 | {
39 | public patch_ParisContentManager(IServiceProvider i_service, string i_sRoot) : base(i_service, i_sRoot)
40 | {
41 | }
42 |
43 | // Can't call double-inherited base functions... so we need this to call the original ContentManager Load method
44 | [MonoModLinkTo("Microsoft.Xna.Framework.Content.ContentManager", "Load")]
45 | [MonoModForceCall]
46 | [MonoModRemove]
47 | public extern T ContentManager_Load(string i_assetName);
48 |
49 | public extern T orig_Load(string i_assetName);
50 | public override T Load(string i_assetName)
51 | {
52 | bool isLockAcquired = false;
53 | T result;
54 | try
55 | {
56 | // We're changing how this lock works:
57 | // only the main thread can upload textures (see: ForceToMainThread in FNA3D_Driver_OpenGL.c),
58 | // if we let the entire method lock, then we basically get deadlocked by the main thread, or starve it
59 | // from this lock, since we'll be waiting for the main thread to consume the requests for texture uploads.
60 | // This change fixes deadlocks on preloading caused by using newer FNA builds.
61 | this.EnsureLock();
62 | isLockAcquired = true;
63 | string text = i_assetName.ToLower();
64 | if (typeof(T) == typeof(ReloadableTexture))
65 | {
66 | text += "Reloadable";
67 | }
68 | ParisContentManager.ObjectCacheItem objectCacheItem = null;
69 | if (!ContextManager.Singleton.Serializing && this.mloadedAssets.TryGetValue(text, out objectCacheItem))
70 | {
71 | T t = (T)((object)objectCacheItem.Obj);
72 | if (t != null)
73 | {
74 | return t;
75 | }
76 | this.mloadedAssets.TryRemove(text, out objectCacheItem);
77 | }
78 | Stopwatch stopwatch = Stopwatch.StartNew();
79 | i_assetName = PathManager.GetPlatformFilename(i_assetName);
80 | string text2 = i_assetName.Substring(i_assetName.LastIndexOf('\\') + 1);
81 | T t2 = default(T);
82 | if (!ContextManager.Singleton.Serializing)
83 | {
84 | objectCacheItem = new ParisContentManager.ObjectCacheItem();
85 | objectCacheItem.Name = text2;
86 | ParisContentManager.ObjectCacheItem objectCacheItem2 = this.mLoadingStack.LastOrDefault();
87 | if (objectCacheItem2 != null)
88 | {
89 | objectCacheItem2.Children.Add(new WeakReference(objectCacheItem));
90 | }
91 | this.mLoadingStack.Add(objectCacheItem);
92 | objectCacheItem.Parent = new WeakReference(objectCacheItem2);
93 | }
94 |
95 | Monitor.Exit(ParisContentManager.mLock);
96 | isLockAcquired = false;
97 |
98 | if (t2 == null)
99 | {
100 | t2 = this.LoadFromDLC(i_assetName);
101 | }
102 | if (t2 == null && typeof(T) == typeof(ReloadableTexture))
103 | {
104 | t2 = (T)((object)new ReloadableTexture(i_assetName));
105 | }
106 | if (t2 == null)
107 | {
108 | t2 = (T)((object)this.LoadParisBinary(i_assetName));
109 | }
110 | if (t2 == null)
111 | {
112 | t2 = this.LoadRawData(i_assetName);
113 | }
114 | if (t2 == null)
115 | {
116 | t2 = (T)((object)this.LoadCompressedJson(i_assetName));
117 | }
118 | if (t2 == null)
119 | {
120 | t2 = ContentManager_Load(i_assetName);
121 | }
122 |
123 | this.EnsureLock();
124 | isLockAcquired = true;
125 | if (objectCacheItem != null && t2 != null && typeof(T) != typeof(GameMenu))
126 | {
127 | objectCacheItem.Obj = t2;
128 | objectCacheItem.Children = (
129 | from x in objectCacheItem.Children
130 | where x.IsAlive
131 | select x).ToList();
132 | this.mloadedAssets.TryAdd(text, objectCacheItem);
133 | objectCacheItem.Time = (double)stopwatch.ElapsedMilliseconds;
134 | }
135 | result = t2;
136 | }
137 | finally
138 | {
139 | if (isLockAcquired)
140 | Monitor.Exit(ParisContentManager.mLock);
141 | }
142 | return result;
143 | }
144 | }
145 | }
146 |
147 | namespace Paris.Engine.Audio
148 | {
149 | class patch_Song: Song
150 | {
151 | public patch_Song(string filename, string introFilename): base(filename, introFilename)
152 | {
153 |
154 | }
155 |
156 | private extern void orig_LoadMainSong();
157 | private void LoadMainSong()
158 | {
159 | // First call the original function
160 | orig_LoadMainSong();
161 |
162 | // Now let's make the decoded buffer size per request a bit more forgiving...
163 | // 1000.0ms is too much and causes periodic stuttering
164 | this.bufferSize = soundEffect.GetSampleSizeInBytes(TimeSpan.FromMilliseconds(33.2));
165 | }
166 | }
167 | }
168 |
--------------------------------------------------------------------------------
/TMNTSR-Mods/ParisEngine.TMNTSRMods.mm.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Xna.Framework.Audio;
2 | using Microsoft.Xna.Framework;
3 | using Mono.Cecil;
4 | using MonoMod.InlineRT;
5 | using Paris.Engine.Context;
6 | using Paris.Engine.Data;
7 | using System.Collections.Generic;
8 | using System.IO;
9 | using System.Linq;
10 | using System.Runtime.CompilerServices;
11 | using System;
12 |
13 | #pragma warning disable 1591
14 | #pragma warning disable CS0649
15 | #pragma warning disable CS0626
16 |
17 | namespace MonoMod
18 | {
19 | public static class MonoModRules
20 | {
21 | static MonoModRules()
22 | {
23 | // Forcefully disable the access checks by creating the following:
24 | // [assembly: IgnoresAccessChecksTo("ParisEngine")]
25 | // [assembly: IgnoresAccessChecksTo("TMNT")]
26 | // [assembly: IgnoresAccessChecksTo("FNA")]
27 |
28 | CustomAttribute attribute;
29 | var mod = MonoModRule.Modder.Module;
30 | var ctor = typeof(IgnoresAccessChecksToAttribute).GetConstructor(new Type[] { typeof(string) });
31 | var attribConstructor = mod.ImportReference(ctor);
32 |
33 | attribute = new CustomAttribute(attribConstructor);
34 | attribute.ConstructorArguments.Add(new CustomAttributeArgument(mod.ImportReference(typeof(string)), (object)"ParisEngine"));
35 | mod.Assembly.CustomAttributes.Add(attribute);
36 |
37 | attribute = new CustomAttribute(attribConstructor);
38 | attribute.ConstructorArguments.Add(new CustomAttributeArgument(mod.ImportReference(typeof(string)), (object)"TMNT"));
39 | mod.Assembly.CustomAttributes.Add(attribute);
40 |
41 | attribute = new CustomAttribute(attribConstructor);
42 | attribute.ConstructorArguments.Add(new CustomAttributeArgument(mod.ImportReference(typeof(string)), (object)"FNA"));
43 | mod.Assembly.CustomAttributes.Add(attribute);
44 | }
45 | }
46 | }
47 |
48 | namespace TMNTMods
49 | {
50 | static class Mods
51 | {
52 | private static string[] _SearchPath = null;
53 | public static string[] SearchPath {
54 | get {
55 | if (_SearchPath == null)
56 | {
57 | string searchPathEnv = Environment.GetEnvironmentVariable("MONOMOD_MODS");
58 | if (searchPathEnv != null)
59 | _SearchPath = searchPathEnv.Split(new char [] { Path.PathSeparator });
60 | else
61 | {
62 | string searchPathGlob = Path.Combine(new string[] { TitleLocation.Path, "Mods" });
63 | _SearchPath = Directory.GetDirectories(searchPathGlob, "*");
64 | }
65 | }
66 |
67 | return _SearchPath;
68 | }
69 | set { _SearchPath = value; }
70 | }
71 | }
72 | }
73 |
74 | namespace Paris.Engine
75 | {
76 | class patch_ParisContentManager: ParisContentManager
77 | {
78 | patch_ParisContentManager(IServiceProvider i_service, string i_sRoot) : base(i_service, i_sRoot) { }
79 | public extern T orig_Load(string i_assetName);
80 | public override T Load(string i_assetName)
81 | {
82 | foreach (var path in TMNTMods.Mods.SearchPath)
83 | {
84 | try
85 | {
86 | return orig_Load(Path.Combine(new string[] { path, i_assetName }));
87 | }
88 | catch (Exception) { }
89 | }
90 |
91 | return orig_Load(i_assetName);
92 | }
93 | }
94 | }
95 |
96 | namespace Paris.Engine.Audio
97 | {
98 | class patch_AudioManager: AudioManager
99 | {
100 | new unsafe public virtual void Init()
101 | {
102 | // Just standard AudioManager.Init()...
103 | if (this._initialized || AudioManager.NoSound)
104 | return;
105 |
106 | this._initialized = true;
107 |
108 | // Load metadata...
109 | SoundSettings soundSettings = ContextManager.Singleton.LoadContent(EngineSettings.Singleton.SoundsSettings, true, false);
110 | float num = soundSettings.Settings.MasterVolume / 100f;
111 | this._masterMusicVolume = num * (soundSettings.Settings.MasterMusicVolume / 100f);
112 | this._masterSFXVolume = num * (soundSettings.Settings.MasterSoundVolume / 100f);
113 | this.CurrentMusicName = "";
114 | foreach (SoundSettings.SoundChannel soundChannel in soundSettings.Channels)
115 | {
116 | this._soundChannels.Add(new SFXChannel(soundChannel.Name, soundChannel.MaxSounds, soundChannel.Volume, soundChannel.PlayType));
117 | }
118 | string path = string.Empty;
119 | if (ParisContentManager.RawDataMode)
120 | {
121 | path = Path.Combine(ContextManager.Singleton.GlobalContentManager.RootDirectory, "Audio", "SFXPack.pbn");
122 | }
123 | else
124 | {
125 | path = ContextManager.Singleton.GlobalContentManager.GetFilePath(Path.Combine("Audio", "SFXPack")) + ".pbn";
126 | }
127 | if (File.Exists(path) && !this.EditorMode)
128 | {
129 | using (FileStream bankStream = File.OpenRead(path))
130 | using (BinaryReader bankReader = new BinaryReader(bankStream))
131 | {
132 | for (int i = 0; i < soundSettings.Sounds.Count; i++)
133 | {
134 | SoundSettings.SoundInfo soundInfo = soundSettings.Sounds[i];
135 | Console.Out.WriteLine($"Loading Tim Allen for {soundInfo.SoundID}");
136 | try
137 | {
138 | int sndSize = bankReader.ReadInt32();
139 | if (sndSize < 0)
140 | throw new Exception("sndSize < 0");
141 |
142 | var list =
143 | from channelName in soundInfo.PlaybackChannels
144 | from sfxChannel in this._soundChannels where sfxChannel.Name == channelName
145 | select sfxChannel;
146 |
147 | byte[] data = null;
148 | foreach (string searchPath in TMNTMods.Mods.SearchPath)
149 | {
150 | string wavPath = Path.Combine(new string[] { searchPath, "Audio", soundInfo.SoundID} ) + ".wav";
151 | if (File.Exists(wavPath))
152 | {
153 | data = File.ReadAllBytes(wavPath);
154 | bankStream.Seek(sndSize, SeekOrigin.Current);
155 | }
156 | }
157 |
158 | if (data == null)
159 | data = bankReader.ReadBytes(sndSize);
160 |
161 | this._sounds.Add(new SFX(
162 | soundInfo.SoundID,
163 | data,
164 | soundInfo.Volume / 100f,
165 | soundInfo.PoolSize,
166 | soundInfo.LoopType,
167 | Enumerable.ToArray(list),
168 | soundInfo.CutOffType,
169 | new Types.Range(soundInfo.PitchMin, soundInfo.PitchMax),
170 | soundInfo.IsVO,
171 | soundInfo.Cooldown));
172 | }
173 | catch (Exception ex)
174 | {
175 | this._sounds.Clear();
176 | throw new Exception(ex.ToString());
177 | }
178 | }
179 | }
180 | }
181 | if (this._voiceChat != null)
182 | {
183 | this._voiceChat.Play();
184 | }
185 | for (int k = 0; k < this._voiceBuffer.Length; k++)
186 | {
187 | this._voiceBuffer[k] = 0;
188 | }
189 | for (int l = 0; l < EngineSettings.Singleton.MaxPlayers; l++)
190 | {
191 | this._voicePlayerMuted[l] = false;
192 | }
193 |
194 | this._initialized = true;
195 | }
196 | }
197 | }
198 |
199 | namespace System.Runtime.CompilerServices
200 | {
201 | [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
202 | public class IgnoresAccessChecksToAttribute : Attribute
203 | {
204 | public IgnoresAccessChecksToAttribute(string assemblyName)
205 | {
206 | AssemblyName = assemblyName;
207 | }
208 |
209 | public string AssemblyName { get; }
210 | }
211 | }
--------------------------------------------------------------------------------
/Chasm/ChasmPatches.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using System.Collections.Generic;
4 | using System.Runtime.CompilerServices;
5 | using System.Reflection;
6 | using System.Reflection.Emit;
7 | using HarmonyLib;
8 | using Microsoft.Xna.Framework;
9 | using Microsoft.Xna.Framework.Graphics;
10 | using Chasm;
11 | using Chasm.Platforms;
12 | using Chasm.Graphics;
13 | using Chasm.Menus;
14 |
15 |
16 | [assembly: IgnoresAccessChecksTo("Chasm")]
17 | [assembly: IgnoresAccessChecksTo("Microsoft.Xna.Framework")]
18 | [ModEntryPoint]
19 | public static class ChasmPatches
20 | {
21 | public static void Main()
22 | {
23 | Console.Out.WriteLine("Found ChasmPatches, running...");
24 |
25 | try
26 | {
27 | new Harmony("com.github.johnnyonflame.ChasmPatches").PatchAll(Assembly.GetExecutingAssembly());
28 | }
29 | catch (Exception e)
30 | {
31 | Console.Out.WriteLine($"Failed: {e.ToString()}");
32 | throw e;
33 | }
34 | }
35 |
36 | [HarmonyPatch(typeof(ChasmGame), nameof(ChasmGame.LoadContent))]
37 | static class ChasmGame_LoadContent
38 | {
39 | public static bool Prefix(ref ChasmGame __instance)
40 | {
41 | // lambda can't capture ref...
42 | ChasmGame game = __instance;
43 |
44 | // We want to change the video mode whenever the resolution has changed!
45 | __instance.Window.ClientSizeChanged += (o, a) => game.SetFullScreen();
46 | return true;
47 | }
48 | }
49 |
50 | [HarmonyPatch(typeof(ChasmGame), nameof(ChasmGame.CalculateUpscaleResolution))]
51 | static class ChasmGame_CalculateUpscaleResolution
52 | {
53 | public static bool Prefix(ref ChasmGame __instance, ref Point __result)
54 | {
55 | int x, y;
56 | //Point result = new Point((int)((float)GlobalShare.NativeResolution.X * GlobalShare.Scaling), (int)((float)GlobalShare.NativeResolution.Y * GlobalShare.Scaling));
57 | Point result = new Point(GlobalShare.DeviceResolution.X, GlobalShare.DeviceResolution.Y);
58 | if (GlobalShare.FitToScreen)
59 | {
60 | __result = result;
61 | return false;
62 | }
63 |
64 | double scaleX = (double)GlobalShare.DeviceResolution.X / (double)GlobalShare.NativeResolution.X;
65 | double scaleY = (double)GlobalShare.DeviceResolution.Y / (double)GlobalShare.NativeResolution.Y;
66 | double scale = Math.Min(scaleX, scaleY);
67 | if (scaleX < scaleY)
68 | {
69 | x = GlobalShare.DeviceResolution.X;
70 | y = (int)Math.Round((double)GlobalShare.NativeResolution.Y * scale, MidpointRounding.AwayFromZero);
71 | }
72 | else
73 | {
74 | x = (int)Math.Round((double)GlobalShare.NativeResolution.X * scale, MidpointRounding.AwayFromZero);
75 | y = GlobalShare.DeviceResolution.Y;
76 | }
77 | __result = new Point(x, y);
78 | return false;
79 | }
80 | }
81 |
82 | // Fullscreen/Windowed logic is inverted
83 | // We remove the limitations that led low-resolution devices to not scale to the whole screen
84 | [HarmonyPatch(typeof(ChasmGame), nameof(ChasmGame.SetFullScreen))]
85 | static class ChasmGame_SetFullScreen
86 | {
87 | public static bool Prefix(ref ChasmGame __instance)
88 | {
89 | __instance._settingFullscreen = true;
90 | int num = ConfigReader.ReadInt("Scaling");
91 | DisplayMode displayMode = GlobalShare.GraphicsDevice.DisplayMode;
92 | if (GlobalShare.Fullscreen)
93 | {
94 | displayMode = GraphicsAdapter.Adapters[GlobalShare.FullscreenIndex - 1].CurrentDisplayMode;
95 | }
96 |
97 | GlobalShare.DeviceResolution = new Point(displayMode.Width, displayMode.Height);
98 |
99 | Point deviceResolution = GlobalShare.DeviceResolution;
100 | if (!GlobalShare.FitToScreen)
101 | {
102 | double val = (double)deviceResolution.X / (double)GlobalShare.NativeResolution.X;
103 | double val2 = (double)deviceResolution.Y / (double)GlobalShare.NativeResolution.Y;
104 | int num2 = (int)Math.Min(val, val2);
105 | GlobalShare.Scaling = (float)num2;
106 | }
107 | else
108 | {
109 | GlobalShare.Scaling = (float)num;
110 | }
111 | GlobalShare.UpscaleResolution = __instance.CalculateUpscaleResolution();
112 | GlobalShare.UpscaleRenderTarget = new RenderTarget2D(__instance.GraphicsDevice, GlobalShare.UpscaleResolution.X, GlobalShare.UpscaleResolution.Y);
113 |
114 | __instance._graphics.IsFullScreen = GlobalShare.Fullscreen;
115 | __instance._graphics.PreferredBackBufferWidth = deviceResolution.X;
116 | __instance._graphics.PreferredBackBufferHeight = deviceResolution.Y;
117 | __instance._graphics.ApplyChanges();
118 | __instance._settingFullscreen = false;
119 | __instance.SetShaderResolution();
120 |
121 | // Don't fall-through, we're overriding this method's behavior
122 | return false;
123 | }
124 | }
125 |
126 | [HarmonyPatch(typeof(ChasmGame), nameof(ChasmGame.Draw))]
127 | static class ChasmGame_Draw
128 | {
129 | static bool keepFs = false;
130 | static bool keepFit = false;
131 | //protected override void Draw(GameTime gameTime)
132 |
133 | // Fake the video mode so that we can always have the bilinear path, otherwise
134 | // text is unreadable
135 | private static bool Prefix(ChasmGame __instance)
136 | {
137 | keepFs = GlobalShare.Fullscreen;
138 | keepFit = GlobalShare.FitToScreen;
139 | GlobalShare.Fullscreen = true;
140 | GlobalShare.FitToScreen = true;
141 | return true;
142 | }
143 |
144 | private static void Postfix(ChasmGame __instance)
145 | {
146 | GlobalShare.Fullscreen = keepFs;
147 | GlobalShare.FitToScreen = keepFit;
148 | }
149 | }
150 |
151 | [HarmonyPatch(typeof(ChasmGame), nameof(ChasmGame.StartupInit))]
152 | static class ChasmGame_StartupInit
153 | {
154 | static IEnumerable Transpiler(IEnumerable instructions)
155 | {
156 | Console.WriteLine($"Patching ChasmGame at ChasmGame:StartupInit...");
157 | var codes = new List(instructions);
158 | for (int i = 0; i < codes.Count-2; i++)
159 | {
160 |
161 | if (
162 | codes[i].opcode == OpCodes.Newobj && codes[i].operand.ToString().Contains("Void .ctor") && // 2 000A newobj instance void Chasm.Platforms.Steam.SteamPlatform::.ctor()
163 | codes[i+1].opcode == OpCodes.Stsfld && codes[i+1].operand.ToString().Contains("Platform") // 3 000F stsfld class Chasm.Platforms.IPlatform Chasm.ChasmGame::Platform
164 | )
165 | {
166 | codes[i] = new CodeInstruction(OpCodes.Newobj, AccessTools.Constructor(typeof(PCPlatform), new Type[] {}));
167 | Console.WriteLine($"Patched ChasmGame at ChasmGame:StartupInit+{i}");
168 | break;
169 | }
170 | }
171 |
172 | return codes.AsEnumerable();
173 | }
174 | }
175 |
176 | [HarmonyPatch(typeof(AcheivementMessage), nameof(AcheivementMessage.Draw))]
177 | static class AcheivementMessage_Draw
178 | {
179 | private static bool Prefix(AcheivementMessage __instance)
180 | {
181 | // Don't draw achievements...
182 | return false;
183 | }
184 | }
185 |
186 | [HarmonyPatch(typeof(MainMenu), nameof(MainMenu.Show))]
187 | static class MainMenu_Show
188 | {
189 | static IEnumerable Transpiler(IEnumerable instructions)
190 | {
191 | Console.WriteLine($"Patching ChasmGame at MainMenu:Show...");
192 | var codes = new List(instructions);
193 | for (int i = 0; i < codes.Count-3; i++)
194 | {
195 | // Disable GOG_Galaxy query, we're logged in to nothing.
196 | if (
197 | codes[i].opcode == OpCodes.Ldsfld && codes[i].operand.ToString().Contains("m_instance") && // 282 0432 ldsfld class Chasm.Platforms.GOG.GogPlatform Chasm.Platforms.GOG.GogPlatform::m_instance
198 | codes[i+1].opcode == OpCodes.Ldc_I4_0 && // 283 0437 ldc.i4.0
199 | codes[i+2].opcode == OpCodes.Callvirt && // 284 0438 callvirt instance bool Chasm.Platforms.GOG.GogPlatform::IsLoggedOn(bool)
200 | codes[i+3].opcode == OpCodes.Brfalse_S // 285 043D brfalse.s 301 (0477) ldarg.0
201 | )
202 | {
203 | List newCodes = new List();
204 | var br = (System.Reflection.Emit.Label)codes[i+3].operand;
205 | newCodes.Add(new CodeInstruction(OpCodes.Br_S, br));
206 | codes.RemoveRange(i, 4);
207 | codes.InsertRange(i, newCodes);
208 | Console.WriteLine($"Patched ChasmGame at MainMenu:Show+{i}");
209 | break;
210 | }
211 | }
212 |
213 | return codes.AsEnumerable();
214 | }
215 | }
216 | }
217 |
218 | namespace System.Runtime.CompilerServices
219 | {
220 | [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
221 | public class IgnoresAccessChecksToAttribute : Attribute
222 | {
223 | public IgnoresAccessChecksToAttribute(string assemblyName)
224 | {
225 | AssemblyName = assemblyName;
226 | }
227 |
228 | public string AssemblyName { get; }
229 | }
230 | }
231 |
--------------------------------------------------------------------------------
/TMNTSR/TMNT.TMNTSRPatches.mm.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Xna.Framework.Audio;
2 | using Microsoft.Xna.Framework;
3 | using MonoMod;
4 | using Paris.Engine.Audio;
5 | using Paris.Engine.Context;
6 | using Paris.Engine.Cutscenes;
7 | using Paris.Engine.Graphics;
8 | using Paris.Engine.Menu.Control;
9 | using Paris.Engine.Save;
10 | using Paris.Engine.Scene;
11 | using Paris.Engine.System.Networking;
12 | using Paris.Engine;
13 | using Paris.Game.Data;
14 | using Paris.Game.System.Pathfinding;
15 | using Paris.Game;
16 | using Paris.System.Input;
17 | using System.IO;
18 | using System.Linq;
19 | using System.Reflection;
20 | using System;
21 |
22 | #pragma warning disable 1591
23 | #pragma warning disable CS0649
24 | #pragma warning disable CS0626
25 |
26 | namespace Paris.Game.Menu
27 | {
28 | public class patch_LogoScreen: LogoScreen
29 | {
30 | public override void Tick(float deltaTime)
31 | {
32 | GameContextManager singleton = GameContextManager.Singleton;
33 | if (singleton.Fadestate == FadeState.FadedIn)
34 | {
35 | if (!_startedSFX && _sfx != null)
36 | {
37 | _sfx.Play();
38 | _sfx.Volume = AudioManager.Singleton.FinalSoundVolume;
39 | _startedSFX = true;
40 | }
41 | _animatedLogo.Tick(deltaTime);
42 | if (_logoTimer > 0f)
43 | {
44 | _logoTimer -= deltaTime;
45 | }
46 | ulong handle = 0uL;
47 | bool flag = InputManagerBase.Singleton.SomeonePressedSomething(true, false, out handle) != ParisInputType.None;
48 | if (_logoTimer <= 0f || (!SaveSystemBase.Singleton.Loading && flag))
49 | {
50 | if (_sfx != null && _sfx.State == SoundState.Playing)
51 | {
52 | _sfx.Stop();
53 | }
54 | _skippedLast = flag;
55 | if (HasNextLogo)
56 | {
57 | singleton.RequestFadeOut(_skippedLast ? 0.1f : 0.5f, _clearColor);
58 | return;
59 | }
60 | if (CheatManager.Singleton.IsActive(16))
61 | {
62 | singleton.SwitchToContext(GameSettings.Singleton.GameContextMainMenu, _skippedLast ? 0.1f : 0.5f, _clearColor);
63 | return;
64 | }
65 | VideoContext.VideoPath = "Videos\\PATCH_TMNT_GameIntro";
66 | VideoContext.NextContext = GameSettings.Singleton.GameContextMainMenu;
67 | singleton.SwitchToContext(typeof(VideoContext).AssemblyQualifiedName, _skippedLast ? 0.1f : 0.5f, _clearColor);
68 | }
69 | }
70 | else if (singleton.Fadestate == FadeState.FadedOut)
71 | {
72 | LoadNextLogo();
73 | singleton.RequestFadeIn(_skippedLast ? 0.3f : 0.5f);
74 | _skippedLast = false;
75 | }
76 | }
77 | }
78 |
79 | public class patch_Options: Options
80 | {
81 | public static int shadowPreset = 1;
82 | public static int reducedRats = 1;
83 | private Control.OptionSelectionItem _shadowController;
84 | private Control.OptionSelectionItem _reducedRatsController;
85 | static private Type SaveSystem_t;
86 | static private MethodInfo SaveSystem_GetModConfigPath_m;
87 |
88 | private static string GetExtraConfigPath()
89 | {
90 | if (SaveSystem_t == null)
91 | {
92 | SaveSystem_t = Type.GetType("Paris.Engine.Save.SaveSystem, ParisEngine", true);
93 | SaveSystem_GetModConfigPath_m = SaveSystem_t.GetMethod("GetModConfigPath", BindingFlags.Static | BindingFlags.Public);
94 | }
95 |
96 | return (string)SaveSystem_GetModConfigPath_m.Invoke(null, new object[] {});
97 | }
98 |
99 | public static void LoadModConfig()
100 | {
101 | string cfg_path = GetExtraConfigPath();
102 | try
103 | {
104 | using (FileStream cfg = new FileStream(cfg_path, FileMode.Open))
105 | using (BinaryReader cfg_reader = new BinaryReader(cfg))
106 | {
107 | shadowPreset = cfg_reader.ReadInt32();
108 | reducedRats = cfg_reader.ReadInt32();
109 | }
110 | }
111 | catch(Exception e)
112 | {
113 | Console.Error.WriteLine(e.ToString());
114 | }
115 | }
116 |
117 | public static void SaveModConfig()
118 | {
119 | try
120 | {
121 | using (FileStream cfg = new FileStream(GetExtraConfigPath(), FileMode.Create))
122 | using (BinaryWriter cfg_writer = new BinaryWriter(cfg))
123 | {
124 | cfg_writer.Write(shadowPreset);
125 | cfg_writer.Write(reducedRats);
126 | }
127 | }
128 | catch(Exception e)
129 | {
130 | Console.Error.WriteLine(e.ToString());
131 | }
132 | }
133 |
134 | private Control.OptionSelectionItem InsertOptionsItemString(SelectionMenuControl selectionControl, string option)
135 | {
136 | int count = selectionControl.Items.Count;
137 | return (Control.OptionSelectionItem)selectionControl.Add(new Control.OptionSelectionItem(option, 340)
138 | {
139 | UnselectedFont = MainMenu.MENU_UNSELECTED_FONT,
140 | DisabledFont = MainMenu.MENU_DISABLED_FONT,
141 | ArrowLeftTexture = this._arrowLeftTexture,
142 | ArrowRightTexture = this._arrowRightTexture,
143 | ArrowLeftColor = GameColors.ArrowLeftColor,
144 | ArrowRightColor = GameColors.ArrowRightColor,
145 | OutlineSelectedColor = Color.Black,
146 | OutlineUnselectedColor = Color.Black,
147 | OutlineDisabledColor = Color.Black
148 | }, 0);
149 | }
150 |
151 | extern void orig_SetupOptions(Options.MenuSchemes menuScheme, SelectionMenuControl selectionControl);
152 | new public void SetupOptions(Options.MenuSchemes menuScheme, SelectionMenuControl selectionControl)
153 | {
154 | orig_SetupOptions(menuScheme, selectionControl);
155 | #if false
156 | // Disabled, does very little.
157 | this._shadowController = InsertOptionsItemString(selectionControl, "Shadows");
158 | this._shadowController.AddOption("Off"); // 0
159 | this._shadowController.AddOption("Low"); // 1
160 | this._shadowController.AddOption("Normal"); // 2
161 | this._shadowController.Value = patch_Options.shadowPreset;
162 | this._shadowController.OptionChangedCallback = new SelectionItem.SelectionCallback((sel) => {
163 | patch_Options.shadowPreset = ((Control.OptionSelectionItem)sel).Value;
164 | SaveModConfig();
165 | this.HaveChanged = true;
166 | });
167 | #endif
168 | this._reducedRatsController = InsertOptionsItemString(selectionControl, "Rat Count");
169 | this._reducedRatsController.AddOption("Normal");
170 | this._reducedRatsController.AddOption("Low");
171 | this._reducedRatsController.AddOption("Very Low");
172 | this._reducedRatsController.Value = patch_Options.reducedRats;
173 | this._reducedRatsController.OptionChangedCallback = new SelectionItem.SelectionCallback((sel) => {
174 | patch_Options.reducedRats = ((Control.OptionSelectionItem)sel).Value;
175 | SaveModConfig();
176 | this.HaveChanged = true;
177 | });
178 | }
179 | }
180 | }
181 |
182 | namespace Paris
183 | {
184 | class patch_Paris: Paris
185 | {
186 | protected override void Uninit()
187 | {
188 | //base.Uninit();
189 | GameInfo.Singleton.Uninit();
190 | NetworkMessageSystem.Singleton.Uninit();
191 | this.UninitLeaderboards();
192 | this.UninitPlatform();
193 | if (PathfindingManager.Singleton != null)
194 | PathfindingManager.Singleton.Uninit();
195 | }
196 |
197 | // Ensure the dispose method doesn't hang the application...
198 | protected override void Dispose(bool disposing)
199 | {
200 | if (!base._disposed)
201 | {
202 | base._disposed = true;
203 | if (AudioManager.Singleton != null)
204 | {
205 | AudioManager.Singleton.Dispose();
206 | }
207 | if (ContextManager.Singleton != null)
208 | {
209 | ContextManager.Singleton.Uninit();
210 | }
211 | if (NetworkManagerBase.Singleton != null)
212 | {
213 | NetworkManagerBase.Singleton.Dispose();
214 | }
215 | }
216 | }
217 |
218 | // Don't tick the Leaderboard, we no longer have it.
219 | private void UpdatePlatform(float deltaTime)
220 | {
221 | base.InitControllers();
222 | base.UpdateFullScreenToggle();
223 | }
224 | }
225 |
226 | static class patch_ParamManager
227 | {
228 | public extern static ParamFlag orig_ParseParams(String[] args);
229 | public static ParamFlag ParseParams(String[] args)
230 | {
231 | Game.Menu.patch_Options.LoadModConfig();
232 | Console.Out.WriteLine("Params Set:");
233 | ParamFlag flags = orig_ParseParams(args);
234 | if (flags == ParamFlag.None)
235 | {
236 | Console.Out.WriteLine("> None");
237 | }
238 | else
239 | {
240 | foreach (var param in Enum.GetValues(typeof(ParamFlag)).Cast())
241 | {
242 | if (param == 0)
243 | continue;
244 |
245 | if ((flags & param) == param)
246 | {
247 | Console.Out.WriteLine($"> {param}");
248 | }
249 | }
250 | }
251 |
252 | Console.Out.WriteLine("Removing Program.CrashHandler handler...");
253 | AppDomain.CurrentDomain.UnhandledException -= Program.CrashHandler;
254 |
255 | return flags;
256 | }
257 | }
258 | }
259 |
260 | // Lower the rat count a tiny bit on slower devices...
261 | namespace Paris.Game.Actor
262 | {
263 | public class patch_RatKing: RatKing
264 | {
265 | private void SpawnPiedPiperRatsAtPos(Vector3 position, int count, bool horizontalFlipped)
266 | {
267 | int reducedRats = 1 + Menu.patch_Options.reducedRats;
268 | for (int i = 0; i < count; i += reducedRats)
269 | {
270 | Rat rat = GameObjectPoolManager.Singleton.SpawnObject(this.RatPiedPiperTemplatePath, position, false, false, true) as Rat;
271 | rat.Owner = this;
272 | rat.HorizontalFlipped = horizontalFlipped;
273 | rat.SetupRatInStampede();
274 | rat.Position += new Vector3(0f, ((float)i - (float)count / 2f) * 10f, 0f);
275 | rat.Position += new Vector3(((float)ParisMath.GetRandom() * 10f - 5f) * reducedRats, ((float)ParisMath.GetRandom() * 10f - 5f) * reducedRats, 0f);
276 | rat.Animator.CurrentFrameID = ParisMath.GetRandomInt() % rat.Animator.CurrentAnimation.Frames.Count;
277 | rat.Animator.Scale = 1f + ((float)ParisMath.GetRandom() - 0.5f) * 0.1f;
278 | rat.LocallyControlled = true;
279 | this._piedPiperRats.Add(rat);
280 | }
281 | }
282 | }
283 | }
284 |
--------------------------------------------------------------------------------
/SteelAssault/SteelAssaultCsPatches.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Runtime.CompilerServices;
4 | using System.Linq;
5 | using System.Reflection;
6 | using System.Reflection.Emit;
7 | using System.Threading;
8 | using HarmonyLib;
9 | using Microsoft.Xna.Framework;
10 | using SteelAssaultCs.Engine;
11 | using SteelAssaultCs.Engine.Objects.Background;
12 | using SteelAssaultCs.Engine.Objects.Effects;
13 | using SteelAssaultCs.Graphics.DrawCalls;
14 |
15 | [assembly: IgnoresAccessChecksTo("SteelAssaultCs")]
16 | [assembly: IgnoresAccessChecksTo("Microsoft.Xna.Framework")]
17 | [ModEntryPoint]
18 | public static class SteelAssaultCsPatches
19 | {
20 | public static void Main()
21 | {
22 | Console.Out.WriteLine("Found SteelAssaultCsPatches, running...");
23 | try
24 | {
25 | new Harmony("com.github.johnnyonflame.SteelAssaultCsPatches").PatchAll(Assembly.GetExecutingAssembly());
26 | }
27 | catch (Exception e)
28 | {
29 | Console.Out.WriteLine($"Failed: {e.ToString()}");
30 | throw e;
31 | }
32 | }
33 |
34 | // Lower the water droplets from overhangs significantly
35 | [HarmonyPatch("DripSurface", "init_delayed")]
36 | private class DripSurface_init_delayed
37 | {
38 | private static void Prefix(ref DripSurface __instance, out int __state)
39 | {
40 | __state = __instance.width;
41 | __instance.width = Math.Max(4, __state / 7);
42 | }
43 |
44 | private static void Postfix(ref DripSurface __instance, int __state)
45 | {
46 | __instance.width = __state;
47 | }
48 | }
49 |
50 | // Cap the maximum water droplet splat effects to 1 every 20 frames
51 | [HarmonyPatch("RainSurface", "update")]
52 | private class RainSurface_update
53 | {
54 | static IEnumerable Transpiler(IEnumerable instructions)
55 | {
56 | var codes = new List(instructions);
57 | for (int i = 0; i < codes.Count; i++)
58 | {
59 | if (codes[i].opcode == OpCodes.Ldc_R8 && (double)codes[i].operand == 2.0)
60 | {
61 | if (codes[i-3].opcode != OpCodes.Ldc_R8 || (double)codes[i-3].operand != 50.0)
62 | continue;
63 |
64 | // Limit droplets to 1 every 20 frames.
65 | List newCodes = new List();
66 | newCodes.Add(new CodeInstruction(OpCodes.Ldloc_2));
67 | newCodes.Add(new CodeInstruction(OpCodes.Ldc_R8, 20.0));
68 | newCodes.Add(new CodeInstruction(OpCodes.Call, ((Func) Math.Max).Method));
69 | newCodes.Add(new CodeInstruction(OpCodes.Stloc_2));
70 | codes.InsertRange(i+4, newCodes);
71 | System.Console.WriteLine($"Patched RainSurface at RainSurface:update+{i}!");
72 | break;
73 | }
74 | }
75 | /*
76 | for (int i = 0; i < codes.Count; i++)
77 | {
78 | System.Console.WriteLine($"RainSurface:update+{i:000}: {codes[i].ToString()}");
79 | }
80 | */
81 | return codes.AsEnumerable();
82 | }
83 | }
84 |
85 | // Lower the rain intensity globally
86 | [HarmonyPatch("RainEffect", "regenerateCalls")]
87 | private class RainEffect_regenerateCalls
88 | {
89 | private static void Prefix(ref RainEffect __instance, out double __state)
90 | {
91 | __state = __instance.intensity;
92 | __instance.intensity = __state * 0.125;
93 | }
94 |
95 | private static void Postfix(ref RainEffect __instance, double __state)
96 | {
97 | __instance.intensity = __state;
98 | }
99 | }
100 |
101 | // Lower the Mecha Agni attack particles by more than half
102 | [HarmonyPatch("FireballAttack", "update")]
103 | private class FireballAttack_update
104 | {
105 | static IEnumerable Transpiler(IEnumerable instructions)
106 | {
107 | var codes = new List(instructions);
108 | for (int i = 0; i < codes.Count; i++)
109 | {
110 | if (codes[i].opcode == OpCodes.Call &&
111 | codes[i].operand.ToString().Contains(
112 | "Void Flamethrower(SteelAssaultCs.Geometry.Position, Double, Double, Int32, Double, Boolean, Boolean, Boolean, Int32)"))
113 | {
114 | if (codes[i-11].opcode != OpCodes.Ldc_I4_6)
115 | continue;
116 |
117 | codes[i-11].opcode = OpCodes.Ldc_I4_2;
118 | codes[i-5].opcode = OpCodes.Ldc_I4_8;
119 | System.Console.WriteLine($"Patched Flamethrower at FireballAttack:update+{i}!");
120 | break;
121 | }
122 | }
123 |
124 | return codes.AsEnumerable();
125 | }
126 | }
127 |
128 | // Lower the Mecha Agni attack particles by more than half
129 | [HarmonyPatch("SpitFlame", "update")]
130 | private class SpitFlame_update
131 | {
132 | static IEnumerable Transpiler(IEnumerable instructions)
133 | {
134 | var codes = new List(instructions);
135 | for (int i = 0; i < codes.Count; i++)
136 | {
137 | if (codes[i].opcode == OpCodes.Call &&
138 | codes[i].operand.ToString().Contains(
139 | "Void Flamethrower(SteelAssaultCs.Geometry.Position, Double, Double, Int32, Double, Boolean, Boolean, Boolean, Int32)"))
140 | {
141 | if (codes[i-12].opcode != OpCodes.Ldc_I4_8)
142 | continue;
143 |
144 | codes[i-12].opcode = OpCodes.Ldc_I4_3;
145 | codes[i-5].opcode = OpCodes.Ldc_I4_8;
146 | System.Console.WriteLine($"Patched Flamethrower at SpitFlame:update+{i}!");
147 | break;
148 | }
149 | }
150 |
151 | return codes.AsEnumerable();
152 | }
153 | }
154 |
155 | // Lower the flameturret attack particles by half
156 | [HarmonyPatch("WaitAndFire", "update")]
157 | private class BCFlameTurret_WaitAndFire_update
158 | {
159 | static IEnumerable Transpiler(IEnumerable instructions)
160 | {
161 | var codes = new List(instructions);
162 | for (int i = 0; i < codes.Count; i++)
163 | {
164 | if (codes[i].opcode == OpCodes.Call &&
165 | codes[i].operand.ToString().Contains(
166 | "Void Flamethrower(SteelAssaultCs.Geometry.Position, Double, Double, Int32, Double, Boolean, Boolean, Boolean, Int32)"))
167 | {
168 | if (codes[i-12].opcode != OpCodes.Ldc_I4_6)
169 | continue;
170 |
171 | codes[i-12].opcode = OpCodes.Ldc_I4_2;
172 | System.Console.WriteLine($"Patched BCFlameTurret at WaitAndFire:update+{i}!");
173 | break;
174 | }
175 | }
176 |
177 | return codes.AsEnumerable();
178 | }
179 | }
180 |
181 | // Lower the Flame Spider's front gun particles considerably
182 | [HarmonyPatch("FSGun", "update")]
183 | private class FlameSpider_FSGun_update
184 | {
185 | static IEnumerable Transpiler(IEnumerable instructions)
186 | {
187 | var codes = new List(instructions);
188 | for (int i = 0; i < codes.Count; i++)
189 | {
190 | if (codes[i].opcode == OpCodes.Call &&
191 | codes[i].operand.ToString().Contains(
192 | "Void Flamethrower(SteelAssaultCs.Geometry.Position, Double, Double, Int32, Double, Boolean, Boolean, Boolean, Int32)"))
193 | {
194 | if (codes[i-11].opcode != OpCodes.Ldc_I4_6)
195 | continue;
196 |
197 | codes[i-11].opcode = OpCodes.Ldc_I4_2;
198 | System.Console.WriteLine($"Patched FlameSpider at FSGun:update+{i}!");
199 | break;
200 | }
201 | }
202 |
203 | return codes.AsEnumerable();
204 | }
205 | }
206 |
207 | [HarmonyPatch("TSBillboard", "draw")]
208 | static class TSBillboard_draw
209 | {
210 | static bool Prefix(ref TSBillboard __instance, ICollection ret)
211 | {
212 | if (!GameManager.Camera.onscreen(__instance, 320))
213 | return false;
214 |
215 | __instance.calls.Clear();
216 | switch (__instance.type)
217 | {
218 | case TSBillboard.AdType.NUMELTA:
219 | __instance.numelta(__instance.calls);
220 | break;
221 | case TSBillboard.AdType.NERIO:
222 | __instance.nerio(__instance.calls);
223 | break;
224 | case TSBillboard.AdType.METROGRAPH:
225 | __instance.metrograph(__instance.calls);
226 | break;
227 | case TSBillboard.AdType.BROADWAY:
228 | __instance.broadway(__instance.calls);
229 | break;
230 | case TSBillboard.AdType.DIRAC:
231 | __instance.dirac(__instance.calls);
232 | break;
233 | case TSBillboard.AdType.RENT:
234 | __instance.rent(__instance.calls);
235 | break;
236 | case TSBillboard.AdType.AVALYN:
237 | __instance.avalyn(__instance.calls);
238 | break;
239 | case TSBillboard.AdType.SOPHIE:
240 | __instance.sophie(__instance.calls);
241 | break;
242 | case TSBillboard.AdType.STONKS:
243 | __instance.stocks(__instance.calls);
244 | break;
245 | case TSBillboard.AdType.NEWS:
246 | __instance.news(__instance.calls);
247 | break;
248 | case TSBillboard.AdType.SOROBAN:
249 | __instance.soroban(__instance.calls);
250 | break;
251 | case TSBillboard.AdType.BOND:
252 | __instance.bond(__instance.calls);
253 | break;
254 | case TSBillboard.AdType.AXON:
255 | __instance.axon(__instance.calls);
256 | break;
257 | case TSBillboard.AdType.PLAY:
258 | __instance.play(__instance.calls);
259 | break;
260 | case TSBillboard.AdType.CAMPAIGN:
261 | __instance.campaign(__instance.calls);
262 | break;
263 | case TSBillboard.AdType.ZENOVIA:
264 | __instance.zenovia(__instance.calls);
265 | break;
266 | case TSBillboard.AdType.TRIBUTE:
267 | __instance.tribute(__instance.calls);
268 | break;
269 | }
270 |
271 | __instance.calls.ForEach(x => ret.Add(x));
272 | return false;
273 | }
274 | }
275 |
276 | // Simplify the displaceSingleCall effect.
277 | [HarmonyPatch("TSBillboard", "displaceSingleCall")]
278 | static class TSBillboard_displaceSingleCall
279 | {
280 | static bool Prefix(ref TSBillboard __instance, List ret, TexturedDrawCall t)
281 | {
282 | ret.Add(t);
283 | return false;
284 | }
285 | }
286 |
287 | // Disabled on TIMES_SQUARE since that level has performance woes.
288 | [HarmonyPatch("MetroLightbeam", "draw")]
289 | static class MetroLightbeam_draw
290 | {
291 | static bool Prefix(ref MetroLightbeam __instance, ICollection ret)
292 | {
293 | Level level = GameManager.Level;
294 | if (level != null && level.currArea == Level.Area.TIMES_SQUARE)
295 | return false;
296 |
297 | return true;
298 | }
299 | }
300 | }
301 |
302 | namespace System.Runtime.CompilerServices
303 | {
304 | [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
305 | public class IgnoresAccessChecksToAttribute : Attribute
306 | {
307 | public IgnoresAccessChecksToAttribute(string assemblyName)
308 | {
309 | AssemblyName = assemblyName;
310 | }
311 |
312 | public string AssemblyName { get; }
313 | }
314 | }
315 |
--------------------------------------------------------------------------------
/TMNTSR/ParisEngine.TMNTSRPatches.mm.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Xna.Framework.Audio;
2 | using Microsoft.Xna.Framework;
3 | using MonoMod;
4 | using Paris.Engine.Context;
5 | using Paris.Engine.Graphics.Animations;
6 | using Paris.Engine.Graphics;
7 | using Paris.Engine.System.AssetPacks;
8 | using Paris.Engine.System.Helper;
9 | using Paris.Engine.System.Leaderboards;
10 | using Paris.Engine.System.Localisation;
11 | using Paris.Engine.System;
12 | using Paris.System.Helper;
13 | using Paris.Game.System.Progression;
14 | using Paris.Game.System;
15 | using System.Collections.Generic;
16 | using System.IO.MemoryMappedFiles;
17 | using System.IO;
18 | using System.Linq;
19 | using System.Reflection;
20 | using System.Threading;
21 | using System;
22 |
23 | #pragma warning disable 1591
24 | #pragma warning disable CS0649
25 | #pragma warning disable CS0626
26 |
27 | namespace Paris.Engine.Save
28 | {
29 | public class patch_SaveSystem: SaveSystem
30 | {
31 | public static string GetModConfigPath()
32 | {
33 | SaveSystemBase saveBase = SaveSystemBase.Singleton;
34 | return Path.Combine(SaveSystem.GetOSPath(), "Tribute Games", "TMNT", "ModSettings.dat");
35 | }
36 | }
37 | }
38 |
39 | namespace Paris.Engine.System.AssetPacks
40 | {
41 | // Fix issues with Alt VO fails to be applied on Linux when using the Windows builds
42 | public class patch_AssetPack : AssetPack
43 | {
44 | private void PopulateReplacementCache()
45 | {
46 | this._contentPathCache.Clear();
47 | string sfxFolder = Path.Combine(this.SourcePath, "Audio\\SFX\\");
48 | sfxFolder = sfxFolder.Replace("/", "\\");
49 | foreach (AssetPackItem assetPackItem in this.Items)
50 | {
51 | string originalPath = assetPackItem.OriginalPath;
52 | string replacementPath = assetPackItem.ReplacementPath;
53 | this._contentPathCache[originalPath] = replacementPath;
54 | if (originalPath.StartsWith("Audio\\SFX\\"))
55 | {
56 | string originalSfxPath = originalPath.Remove(0, "Audio\\SFX\\".Length);
57 | string replacementSfxPath = replacementPath;
58 | if (replacementPath.StartsWith("Audio\\SFX\\"))
59 | {
60 | replacementSfxPath = replacementPath.Remove(0, "Audio\\SFX\\".Length);
61 | }
62 | else if (replacementPath.StartsWith(sfxFolder))
63 | {
64 | replacementSfxPath = replacementPath.Remove(0, sfxFolder.Length);
65 | }
66 | this._sfxPathCache[originalSfxPath] = replacementSfxPath;
67 | }
68 | }
69 | }
70 | }
71 | }
72 |
73 | namespace Paris.Engine.Context
74 | {
75 | public class patch_ContextManager: ContextManager
76 | {
77 | patch_ContextManager(Microsoft.Xna.Framework.Game game): base(game) { }
78 |
79 | // Due to issues with loading assets async, we will be loading quite a few of these in the main thread,
80 | // otherwise, we _will_ cause deadlocks.
81 | new public void StartPreLoadingGlobalAssets()
82 | {
83 | if (EditorMode || _preloadingState != 0)
84 | {
85 | return;
86 | }
87 |
88 | // Those can be loaded async, since they have no texture data
89 | Action[] array = new Action[4] { PreloadAudio, CacheJsonFiles, CacheReflectionInfo, PreloadPoolObjects };
90 | foreach (Action action in array)
91 | {
92 | Thread thread = new Thread(action.Invoke);
93 | thread.Priority = ThreadPriority.BelowNormal;
94 | thread.IsBackground = true;
95 | thread.Name = string.Format("Preload : {0}", action.Method.Name);
96 | mGlobalLoadingThreads.Add(thread);
97 | thread.Start();
98 | }
99 |
100 | // These resources contain texture data, we need to load them synchronously
101 | AttackList.Load();
102 | PowerLevelList.Load();
103 | SurvivalDimensionList.Load();
104 | SurvivalWaveList.Load();
105 | SurvivalPowerLevelList.Load();
106 | CharacterManager.Singleton.Init();
107 |
108 | // This has to be loaded by the main thread.
109 | LoadGlobalContent();
110 |
111 | // These might have texture data, so they have to be loaded by the
112 | // main thread.
113 | foreach (Type loadableType in Assembly.GetCallingAssembly().GetLoadableTypes())
114 | {
115 | if (typeof(IPreloadable).IsAssignableFrom(loadableType))
116 | {
117 | IPreloadable parameter = (IPreloadable)Activator.CreateInstance(loadableType);
118 | PreloadCustom(parameter);
119 | }
120 | }
121 |
122 | _preloadingState = PreloadingState.Finished;
123 | }
124 | }
125 | }
126 |
127 | namespace Paris.Engine.System.Networking
128 | {
129 | // Fix story mode crashes
130 | class patch_NetworkManager: NetworkManager
131 | {
132 | public extern virtual void orig_Disconnect(bool transition = false);
133 | new public virtual void Disconnect(bool transition = false)
134 | {
135 | // try
136 | // {
137 | // orig_Disconnect(transition);
138 | // }
139 | // catch (Exception){ }
140 | }
141 |
142 | public extern virtual void orig_ChangeLobbyLocked(bool isLocked);
143 | new public virtual void ChangeLobbyLocked(bool isLocked)
144 | {
145 | // try
146 | // {
147 | // orig_ChangeLobbyLocked(isLocked);
148 | // }
149 | // catch (Exception){ }
150 | }
151 |
152 |
153 | }
154 | }
155 |
156 | namespace Paris.Engine {
157 | public class patch_ParisEngine: ParisEngine
158 | {
159 | public patch_ParisEngine() { }
160 | public static Thread frameworkDispatchWorker { get; private set; } = null;
161 | #if false
162 | static double elapsedAccum = 0.0f;
163 | static int elapsedCounter = 0;
164 | const int bufferFrames = 15;
165 | #endif
166 | public static void frameworkUpdateWork()
167 | {
168 | while (true)
169 | {
170 | FrameworkDispatcher.Update();
171 | Thread.Sleep(16);
172 | }
173 | }
174 |
175 | new void Update(GameTime gameTime)
176 | {
177 | lock (this.updateableComponents)
178 | {
179 | for (int i = 0; i < this.updateableComponents.Count; i++)
180 | {
181 | this.currentlyUpdatingComponents.Add(this.updateableComponents[i]);
182 | }
183 | }
184 |
185 | foreach (IUpdateable updateable in this.currentlyUpdatingComponents)
186 | {
187 | if (updateable.Enabled)
188 | {
189 | updateable.Update(gameTime);
190 | }
191 | }
192 |
193 | // Big ugly.
194 | if (frameworkDispatchWorker == null)
195 | {
196 | frameworkDispatchWorker = new Thread(frameworkUpdateWork);
197 | frameworkDispatchWorker.Start();
198 | }
199 |
200 | this.currentlyUpdatingComponents.Clear();
201 | base.IsFixedTimeStep = !Renderer.Singleton.VSync;
202 | }
203 | }
204 |
205 | public class patch_Stat: Stat
206 | {
207 | patch_Stat(string id, StatType type): base(id, type) { }
208 |
209 | // Fix story mode crash in earlier versions
210 | extern private bool orig_StoreSteam();
211 | private bool StoreSteam()
212 | {
213 | return false;
214 | }
215 | }
216 |
217 | class patch_GameAnalytics: GameAnalytics
218 | {
219 | public extern void orig_Init();
220 | new public void Init()
221 | {
222 | // Disable GameAnalytics
223 | }
224 | }
225 | }
226 |
227 | namespace Paris.Engine.Audio
228 | {
229 | public class MMappedSFXBank {
230 | public string assetPath;
231 | public MemoryMappedFile mmapFile;
232 | public MemoryMappedViewStream mvStream;
233 | public IntPtr basePtr;
234 |
235 | public MMappedSFXBank(string assetPath)
236 | {
237 | this.assetPath = assetPath;
238 | mmapFile = MemoryMappedFile.CreateFromFile(assetPath, FileMode.Open, assetPath, 0, MemoryMappedFileAccess.Read);
239 | mvStream = mmapFile.CreateViewStream(0, 0, MemoryMappedFileAccess.Read);
240 | basePtr = mvStream.SafeMemoryMappedViewHandle.DangerousGetHandle();
241 | }
242 |
243 | public void RewindStream()
244 | {
245 | mvStream.Seek(0, SeekOrigin.Begin);
246 | }
247 | };
248 |
249 | class patch_AudioManager: AudioManager
250 | {
251 | public static List loadedBanks = new List{};
252 |
253 | /*
254 | Replacement for SFX..ctor "public SFX(string assetName, byte[] data, float volume,
255 | int poolSize, SoundLoopType loopType, SFXChannel[] channels, SoundCutOffType cutOffType,
256 | Range randomPitch, bool isVO, float cooldown) : this(assetName, cooldown)"
257 | */
258 | public static SFX SFXFromIntPtr(string assetName, IntPtr data, int length, float volume, int poolSize, SoundLoopType loopType, SFXChannel[] channels, SoundCutOffType cutOffType, Types.Range randomPitch, bool isVO, float cooldown)
259 | {
260 | SFX sfx = new SFX(assetName, cooldown);
261 | sfx._soundEffectInstance = new SoundEffectInstance[poolSize];
262 | try
263 | {
264 | sfx._soundEffect = new SoundEffect(data, false, length, 48000, AudioChannels.Stereo);
265 | }
266 | catch (Exception)
267 | {
268 | sfx._soundEffect = null;
269 | }
270 | sfx._playVolume = volume;
271 | sfx.IsVO = isVO;
272 | int finalPoolSize = (loopType == SoundLoopType.NoLoop) ? poolSize : 1;
273 | for (int i = 0; i < finalPoolSize; i++)
274 | {
275 | if (sfx._soundEffect != null)
276 | {
277 | sfx._soundEffectInstance[i] = sfx._soundEffect.CreateInstance();
278 | sfx._soundEffectInstance[i].IsLooped = (loopType > SoundLoopType.NoLoop);
279 | }
280 | }
281 | sfx._channels = channels;
282 | sfx._cutOffType = cutOffType;
283 | sfx._loopType = loopType;
284 | if (randomPitch.Min != 0f || randomPitch.Max != 0f)
285 | {
286 | sfx._pitch = new Types.Range?(randomPitch);
287 | }
288 | sfx.Volume = 1f;
289 | return sfx;
290 | }
291 |
292 | /*
293 | This patch implements Memory Mapped SFX support, SFXPack.pbn is a 300mb file that gets loaded
294 | in it's entirety to ram, this patch allows said file to remain resident in virtual memory as a
295 | memory mapped file, while not taking as much physical memory.
296 | */
297 | new unsafe public void LoadSFXPack(List sounds, string packPath, AssetPackEnableFlags flags)
298 | {
299 | try
300 | {
301 | // Memory map the Voice Bank or reuse an already mapped one
302 | MMappedSFXBank bank = loadedBanks.Find(b => b.assetPath == packPath);
303 | if (bank == null)
304 | {
305 | bank = new MMappedSFXBank(packPath);
306 | loadedBanks.Add(bank);
307 | }
308 | else
309 | {
310 | bank.RewindStream();
311 | }
312 |
313 | BinaryReader bankReader = new BinaryReader(bank.mvStream);
314 |
315 | foreach (SoundSettings.SoundInfo soundInfo in this._settings.Sounds)
316 | {
317 | int sndSize = bankReader.ReadInt32();
318 | if (sndSize < 0)
319 | throw new Exception("sndSize < 0");
320 |
321 | if (sndSize > 0)
322 | {
323 | // We'd usually read the entire sound effect into memory, here we just take the offset
324 | // then we move the reading head forwards.
325 | long offset = bank.mvStream.Position;
326 | bank.mvStream.Seek(sndSize, SeekOrigin.Current);
327 |
328 | IEnumerable channels = (
329 | from channelName in soundInfo.PlaybackChannels
330 | from sfxChannel in this._soundChannels
331 | where sfxChannel.Name == channelName
332 | select sfxChannel
333 | );
334 |
335 | if ((((flags & AssetPackEnableFlags.Voice) != (AssetPackEnableFlags)0) || !soundInfo.IsVO) &&
336 | (((flags & AssetPackEnableFlags.Sound) != (AssetPackEnableFlags)0) || soundInfo.IsVO))
337 | {
338 | IntPtr sfxPtr = new IntPtr((Int64)bank.basePtr + offset);
339 | sounds.Add(SFXFromIntPtr(
340 | soundInfo.SoundID,
341 | sfxPtr,
342 | sndSize,
343 | soundInfo.Volume / 100f,
344 | soundInfo.PoolSize,
345 | soundInfo.LoopType,
346 | Enumerable.ToArray(channels),
347 | soundInfo.CutOffType,
348 | new Types.Range(soundInfo.PitchMin, soundInfo.PitchMax),
349 | soundInfo.IsVO,
350 | soundInfo.Cooldown));
351 | }
352 | }
353 | }
354 | }
355 | catch (Exception ex)
356 | {
357 | Console.Out.WriteLine("Invalid data being read in SFXPack using list of sounds from SoundsSettings; SoundsSettings & SFXPack are out of sync! Error message: " + ex.ToString());
358 | sounds.Clear();
359 | }
360 | }
361 | }
362 | }
363 |
364 | // Disable the Leaderboards, we're offline.
365 | [MonoModPatch("Paris.Engine.System.Leaderboards.Leaderboard")]
366 | public class patch_Leaderboard
367 | {
368 | [MonoModConstructor]
369 | public patch_Leaderboard(LocID name, string key, LeaderboardBase.SortMode sortMode, LeaderboardBase.DisplayType displayType)
370 | {
371 | }
372 | }
373 |
374 | // We don't have Steam present, and we want to be sure users aren't freeloading
375 | // on DLCs they did not purchase, we'll have to perform some silly heuristics
376 | // to check.
377 | namespace Paris.Engine.System.DLC
378 | {
379 | public class patch_DLCInfo: DLCInfo {
380 | public bool CheckOwnsShellshock()
381 | {
382 | // Having the "Aftermath" animation is a good smell to sniff out the
383 | // Shellshock DLC ownership status.
384 | try {
385 | ContextManager.Singleton.LoadContent("2d\\Animations\\BG\\Survival\\Aftermath\\Aftermath", true);
386 | State = LockState.Unlocked;
387 | return true;
388 | } catch (Exception) { }
389 |
390 | return false;
391 | }
392 |
393 | public patch_DLCInfo(int id) : base(id) {
394 |
395 | }
396 |
397 | public extern void orig_SetAppID(uint appid);
398 | new public void SetAppID(uint appid)
399 | {
400 | // AppID = new AppId_t(appid);
401 | orig_SetAppID(appid);
402 | switch (appid) {
403 | case 2348930: //Shellshock
404 | Console.Out.WriteLine($"Owns {appid}? {CheckOwnsShellshock()}");
405 | break;
406 | default:
407 | Console.Out.WriteLine($"Unknown DLC {appid}");
408 | break;
409 | }
410 | }
411 |
412 | new public bool IsInstalled() {
413 | CheckOwnsShellshock();
414 | return State == LockState.Unlocked;
415 | }
416 |
417 | new public void UpdateState()
418 | {
419 | CheckOwnsShellshock();
420 | }
421 | };
422 | }
--------------------------------------------------------------------------------
/Celeste/CelestePatches.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Runtime.CompilerServices;
4 | using System.IO;
5 | using System.Linq;
6 | using System.Reflection;
7 | using System.Reflection.Emit;
8 | using HarmonyLib;
9 | using Microsoft.Xna.Framework;
10 | using Microsoft.Xna.Framework.Graphics;
11 | using FMOD.Studio;
12 | using Celeste;
13 | using Monocle;
14 |
15 | [assembly: IgnoresAccessChecksTo("Celeste")]
16 | [assembly: IgnoresAccessChecksTo("Microsoft.Xna.Framework")]
17 | [ModEntryPoint]
18 | public static class CelestePatches
19 | {
20 | public static bool distortEnabled = false;
21 | public static bool bloomEnabled = false;
22 | public static byte lightingPreset = 0;
23 | public static bool particlesEnabled = false;
24 | public static bool dustEdgesEnabled = true;
25 | public static byte gaussianBlurPreset = 0;
26 |
27 | public static int gfxPreset = 1;
28 | public static void Main()
29 | {
30 | Console.Out.WriteLine("Found CelestePatches, running...");
31 | try
32 | {
33 | new Harmony("com.github.johnnyonflame.CelestePatches").PatchAll(Assembly.GetExecutingAssembly());
34 | }
35 | catch (Exception e)
36 | {
37 | Console.Out.WriteLine($"Failed: {e.ToString()}");
38 | throw e;
39 | }
40 |
41 | loadPreferences();
42 | Console.Out.WriteLine("Done.");
43 | }
44 |
45 | public static string GetCfgFilename()
46 | {
47 | return Path.Combine(UserIO.SavePath, "CelestePatches.dat");
48 | }
49 |
50 | public static void savePreferences()
51 | {
52 | using (FileStream fs = File.OpenWrite(GetCfgFilename()))
53 | {
54 | BinaryWriter wr = new BinaryWriter(fs);
55 | wr.Write(distortEnabled);
56 | wr.Write(bloomEnabled);
57 | wr.Write(lightingPreset);
58 | wr.Write(particlesEnabled);
59 | wr.Write(dustEdgesEnabled);
60 | wr.Write(gaussianBlurPreset);
61 | wr.Write(gfxPreset);
62 | }
63 | }
64 |
65 | public static void loadPreferences()
66 | {
67 | try
68 | {
69 | using (FileStream fs = File.OpenRead(GetCfgFilename()))
70 | {
71 | BinaryReader rd = new BinaryReader(fs);
72 | distortEnabled = rd.ReadBoolean();
73 | bloomEnabled = rd.ReadBoolean();
74 | lightingPreset = rd.ReadByte();
75 | particlesEnabled = rd.ReadBoolean();
76 | dustEdgesEnabled = rd.ReadBoolean();
77 | gaussianBlurPreset = rd.ReadByte();
78 | gfxPreset = rd.ReadInt32();
79 | }
80 | }
81 | catch (Exception e)
82 | {
83 | System.Console.WriteLine($"Unable to load mod config, {e.ToString()}");
84 | };
85 | }
86 |
87 | [HarmonyPatch(typeof(MenuOptions), nameof(MenuOptions.Create))]
88 | static class MenuOptions_Create
89 | {
90 | private static TextMenu.Option gfxPresetItem;
91 | private static TextMenu.Option distortItem;
92 | private static TextMenu.Option bloomItem;
93 | private static TextMenu.Option lightingItem;
94 | private static TextMenu.Option particlesItem;
95 | private static TextMenu.Option dustEdgesItem;
96 | private static TextMenu.Option gaussianBlurItem;
97 |
98 | private static void setGfxPreset(int preset)
99 | {
100 | gfxPreset = preset;
101 | switch (gfxPreset) {
102 | case 0: //return "Custom";
103 | break;
104 | case 1: //return "Very Low";
105 | distortEnabled = false;
106 | bloomEnabled = false;
107 | lightingPreset = 0;
108 | particlesEnabled = false;
109 | dustEdgesEnabled = true;
110 | gaussianBlurPreset = 0;
111 | break;
112 | case 2: //return "Low";
113 | distortEnabled = false;
114 | bloomEnabled = false;
115 | lightingPreset = 1;
116 | particlesEnabled = true;
117 | dustEdgesEnabled = true;
118 | gaussianBlurPreset = 1;
119 | break;
120 | case 3: //return "Normal";
121 | distortEnabled = true;
122 | bloomEnabled = true;
123 | lightingPreset = 3;
124 | particlesEnabled = true;
125 | dustEdgesEnabled = true;
126 | gaussianBlurPreset = 3;
127 | break;
128 | }
129 |
130 | distortItem.Index = distortEnabled ? 1 : 0;
131 | bloomItem.Index = bloomEnabled ? 1 : 0;
132 | lightingItem.Index = lightingPreset;
133 | particlesItem.Index = particlesEnabled ? 1 : 0;
134 | dustEdgesItem.Index = dustEdgesEnabled ? 1 : 0;
135 | gaussianBlurItem.Index = gaussianBlurPreset;
136 | savePreferences();
137 | }
138 |
139 | public static void SetAndForceCustom(ref T _out, T value)
140 | {
141 | _out = value;
142 |
143 | // Set preset to "Custom"
144 | gfxPreset = 0;
145 | gfxPresetItem.Index = 0;
146 | savePreferences();
147 | }
148 |
149 | public static void Postfix(bool inGame, EventInstance snapshot, ref TextMenu __result)
150 | {
151 | // Create a shallow copy of the original menu, then depopulate it
152 | List kept = MenuOptions.menu.items.ToList();
153 | MenuOptions.menu.Clear();
154 |
155 | // We're ridding ourselves of the header later, so recreate it
156 | MenuOptions.menu.Add(new TextMenu.Header(Dialog.Clean("options_title", null)));
157 |
158 | // Create our options first
159 | MenuOptions.menu.Add(new TextMenu.SubHeader("CelestePatches Options", true));
160 | MenuOptions.menu.Add(gfxPresetItem = new TextMenu.Slider("Graphics Preset", i => {
161 | switch (i) {
162 | case 0: return "Custom";
163 | case 1: return "Very Low";
164 | case 2: return "Low";
165 | case 3: return "Normal";
166 | default: return "???";
167 | }
168 | }, 0, 3, gfxPreset).Change(i => { setGfxPreset(i); }));
169 |
170 | MenuOptions.menu.Add(distortItem = new TextMenu.OnOff("Distort", distortEnabled).Change(i => { SetAndForceCustom(ref distortEnabled, i); }));
171 | MenuOptions.menu.Add(bloomItem = new TextMenu.OnOff("Bloom", bloomEnabled).Change(i => { SetAndForceCustom(ref bloomEnabled, i); }));
172 | MenuOptions.menu.Add(lightingItem = new TextMenu.Slider("Lighting", i => {
173 | switch (i) {
174 | case 0: return "Off";
175 | case 1: return "Very Low";
176 | case 2: return "Low";
177 | case 3: return "Normal";
178 | default: return "???";
179 | };
180 | }, 0, 3, (int)lightingPreset).Change(i => { SetAndForceCustom(ref lightingPreset, (byte)i); }));
181 | MenuOptions.menu.Add(particlesItem = new TextMenu.OnOff("Particles", particlesEnabled).Change(i => { SetAndForceCustom(ref particlesEnabled, i); }));
182 | MenuOptions.menu.Add(dustEdgesItem = new TextMenu.OnOff("Dust Edges", dustEdgesEnabled).Change(i => { SetAndForceCustom(ref dustEdgesEnabled, i); }));
183 | MenuOptions.menu.Add(gaussianBlurItem = new TextMenu.Slider("Max. Gaussian Blur", i => {
184 | switch (i) {
185 | case 0: return "Off";
186 | case 1: return "3 Samples";
187 | case 2: return "5 Samples";
188 | case 3: return "9 Samples";
189 | default: return "???";
190 | };
191 | }, 0, 3, (int)gaussianBlurPreset).Change(i => { SetAndForceCustom(ref gaussianBlurPreset, (byte)i); }));
192 |
193 | // Now copy the previous items back
194 | foreach (var item in kept.Skip(1))
195 | {
196 | MenuOptions.menu.Add(item);
197 | }
198 | }
199 | }
200 |
201 | // ----------------
202 | // Particle systems
203 | // ----------------
204 | [HarmonyPatch(typeof(ParticleSystem), nameof(ParticleSystem.Simulate))]
205 | static class ParticleSystem_Simulate
206 | {
207 | public static bool Prefix(ref ParticleSystem __instance, float duration, float interval, Action emitter)
208 | {
209 | return particlesEnabled;
210 | }
211 | }
212 |
213 | [HarmonyPatch(typeof(ParticleSystem), nameof(ParticleSystem.Render))]
214 | [HarmonyPatch(new Type[] { })]
215 | static class ParticleSystem_Render1
216 | {
217 | public static bool Prefix(ref ParticleSystem __instance)
218 | {
219 | return particlesEnabled;
220 | }
221 | }
222 |
223 | [HarmonyPatch(typeof(ParticleSystem), nameof(ParticleSystem.Render))]
224 | [HarmonyPatch(new Type[] { typeof(float) })]
225 | static class ParticleSystem_Render2
226 | {
227 | public static bool Prefix(ref ParticleSystem __instance, float alpha)
228 | {
229 | return particlesEnabled;
230 | }
231 | }
232 |
233 | [HarmonyPatch(typeof(ParticleSystem), nameof(ParticleSystem.Update))]
234 | static class ParticleSystem_Update
235 | {
236 | public static bool Prefix(ref ParticleSystem __instance)
237 | {
238 | return particlesEnabled;
239 | }
240 | }
241 |
242 | [HarmonyPatch(typeof(LightingRenderer), nameof(LightingRenderer.BeforeRender))]
243 | static class LightingRenderer_BeforeRender
244 | {
245 | public static bool Prefix(ref LightingRenderer __instance, Scene scene)
246 | {
247 | return lightingPreset > 0;
248 | }
249 |
250 | public static void ApplyBlurPreset()
251 | {
252 | // Off: This isn't called
253 | // Very Low Preset: No blur
254 | // Low Preset: 3 samples
255 | // Normal Preset: 9 samples
256 |
257 | switch (lightingPreset)
258 | {
259 | case 3:
260 | GaussianBlur.Blur(GameplayBuffers.Light, GameplayBuffers.TempA, GameplayBuffers.Light, 0f, true, GaussianBlur.Samples.Nine, 1f, GaussianBlur.Direction.Both, 1f);
261 | break;
262 | case 2:
263 | GaussianBlur.Blur(GameplayBuffers.Light, GameplayBuffers.TempA, GameplayBuffers.Light, 0f, true, GaussianBlur.Samples.Three, 1f, GaussianBlur.Direction.Both, 1f);
264 | break;
265 | }
266 | }
267 |
268 | static IEnumerable Transpiler(IEnumerable instructions)
269 | {
270 | var codes = new List(instructions);
271 | for (int i = 0; i < codes.Count; i++)
272 | {
273 | if (codes[i].opcode == OpCodes.Call && codes[i].operand.ToString().Contains("Texture2D Blur"))
274 | {
275 | for (int j = 0; j < 11; j++)
276 | codes[i-j].opcode = OpCodes.Nop;
277 |
278 | codes[i+1].opcode = OpCodes.Nop;
279 | codes[i-10].opcode = OpCodes.Call;
280 | codes[i-10].operand = AccessTools.Method(typeof(LightingRenderer_BeforeRender), "ApplyBlurPreset", new Type[] { });
281 | break;
282 | }
283 | }
284 |
285 | return codes.AsEnumerable();
286 | }
287 | }
288 |
289 | [HarmonyPatch(typeof(LightingRenderer), nameof(LightingRenderer.Render))]
290 | static class LightingRenderer_Render
291 | {
292 | public static bool Prefix(ref LightingRenderer __instance, Scene scene)
293 | {
294 | return lightingPreset > 0;
295 | }
296 | }
297 |
298 | [HarmonyPatch(typeof(LightingRenderer), nameof(LightingRenderer.Update))]
299 | static class LightingRenderer_Update
300 | {
301 | public static bool Prefix(ref LightingRenderer __instance, Scene scene)
302 | {
303 | __instance.Visible = lightingPreset > 0;
304 | return lightingPreset > 0;
305 | }
306 | }
307 |
308 | // --------------
309 | // Bloom Renderer
310 | // --------------
311 | [HarmonyPatch(typeof(BloomRenderer), nameof(BloomRenderer.Apply))]
312 | static class BloomRenderer_Apply
313 | {
314 | public static bool Prefix(ref BloomRenderer __instance, VirtualRenderTarget target, Scene scene)
315 | {
316 | return bloomEnabled;
317 | }
318 | }
319 |
320 | // ------------------
321 | // Distortion Effects
322 | // ------------------
323 | [HarmonyPatch(typeof(Distort), nameof(Distort.Render))]
324 | static class Distort_Render
325 | {
326 | public static bool Prefix(Texture2D source, Texture2D map, bool hasDistortion)
327 | {
328 | if (!distortEnabled) {
329 | Draw.SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, DepthStencilState.Default, RasterizerState.CullNone, null);
330 | Draw.SpriteBatch.Draw(source, Vector2.Zero, Color.White);
331 | Draw.SpriteBatch.End();
332 | }
333 |
334 | return distortEnabled;
335 | }
336 | }
337 |
338 | // -------------------
339 | // DustEdge Processing
340 | // -------------------
341 | [HarmonyPatch(typeof(DustEdges), nameof(DustEdges.BeforeRender))]
342 | static class DustEdges_BeforeRender
343 | {
344 | public static bool Prefix(ref DustEdges __instance)
345 | {
346 | return dustEdgesEnabled;
347 | }
348 | }
349 |
350 | [HarmonyPatch(typeof(DustEdges), nameof(DustEdges.Render))]
351 | static class DustEdges_Render
352 | {
353 | public static bool Prefix(ref DustEdges __instance)
354 | {
355 | return dustEdgesEnabled;
356 | }
357 | }
358 |
359 | [HarmonyPatch(typeof(DustEdges), nameof(DustEdges.Update))]
360 | static class DustEdges_Update
361 | {
362 | public static bool Prefix(ref DustEdges __instance)
363 | {
364 | __instance.Visible = dustEdgesEnabled;
365 | return dustEdgesEnabled;
366 | }
367 | }
368 |
369 | // ------------------
370 | // Gaussian Blur Pass
371 | // ------------------
372 | [HarmonyPatch(typeof(GaussianBlur), nameof(GaussianBlur.Blur))]
373 | static class GaussianBlur_Blur
374 | {
375 | public static bool Prefix(Texture2D texture, VirtualRenderTarget temp, VirtualRenderTarget output, float fade, bool clear,
376 | ref GaussianBlur.Samples samples, float sampleScale, GaussianBlur.Direction direction, float alpha, ref Texture2D __result)
377 | {
378 | samples = (GaussianBlur.Samples)Math.Min((int)gaussianBlurPreset - 1, (int)samples);
379 | if (samples < 0)
380 | {
381 | Engine.Instance.GraphicsDevice.SetRenderTarget(output);
382 | if (clear)
383 | Engine.Instance.GraphicsDevice.Clear(Color.Transparent);
384 |
385 | __result = (Texture2D)output;
386 | return false;
387 | }
388 |
389 | return true;
390 | }
391 | }
392 |
393 | // --------------------------
394 | // Texture Loading Extensions
395 | // --------------------------
396 | [HarmonyPatch(typeof(VirtualTexture), nameof(VirtualTexture.Reload))]
397 | static class VirtualTexture_Reload
398 | {
399 | static bool Prefix(ref VirtualTexture __instance)
400 | {
401 | // Is this a texture?
402 | string ext = System.IO.Path.GetExtension(__instance.Path);
403 | if (ext == ".data")
404 | {
405 | string astcEncodedFile = System.IO.Path.ChangeExtension(__instance.Path, ".astc");
406 | string astcEncodedFilePath = System.IO.Path.Combine(Engine.ContentDirectory, astcEncodedFile);
407 |
408 | // Is there an ASTC replacement file?
409 | if (System.IO.File.Exists(astcEncodedFilePath))
410 | {
411 | using (FileStream stream = File.OpenRead(astcEncodedFilePath))
412 | {
413 | // Usual flow won't be called, so we need to unload here since VirtualTexture.Reload would do so.
414 | __instance.Unload();
415 |
416 | // Read and parse ASTC header
417 | byte[] astc_header = new byte[16];
418 | stream.Read(astc_header, 0, 16);
419 |
420 | int block_x = astc_header[4];
421 | int block_y = astc_header[5];
422 | int width = astc_header[7] | astc_header[8] << 8 | astc_header[9] << 16;
423 | int height = astc_header[10] | astc_header[11] << 8 | astc_header[12] << 16;
424 |
425 | // Calculate payload size, then read it
426 | int bytes = (((width + block_x - 1) / block_x) * ((height + block_y - 1) / block_y)) * 16;
427 | if (stream.Read(VirtualTexture.buffer, 0, bytes) != bytes)
428 | {
429 | System.Console.WriteLine($"Unexpected end of ASTC stream.");
430 | }
431 |
432 | // Upload texture and set parameters
433 | __instance.Texture = new Texture2D(Engine.Graphics.GraphicsDevice, width, height, false, SurfaceFormat.Astc4x4EXT);
434 | __instance.Texture.SetData(VirtualTexture.buffer, 0, bytes);
435 |
436 | __instance.Texture.Width = width;
437 | __instance.Texture.Height = height;
438 | __instance.Width = width;
439 | __instance.Height = height;
440 |
441 | // Texture successfully loaded, don't allow the usual execution flow to proceed.
442 | return false;
443 | }
444 | }
445 | }
446 |
447 | // Fall-through to the usual execution flow to proceed.
448 | return true;
449 | }
450 | }
451 | }
452 |
453 | namespace System.Runtime.CompilerServices
454 | {
455 | [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
456 | public class IgnoresAccessChecksToAttribute : Attribute
457 | {
458 | public IgnoresAccessChecksToAttribute(string assemblyName)
459 | {
460 | AssemblyName = assemblyName;
461 | }
462 |
463 | public string AssemblyName { get; }
464 | }
465 | }
466 |
--------------------------------------------------------------------------------