Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 98 additions & 0 deletions STROOP.Core/GameMemoryAccess/SavestateDecompression.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
using System.IO.Compression;

namespace STROOP.Core.GameMemoryAccess;

public static class SavestateDecompression
{
static readonly byte[] Lz4Magic = { 0x04, 0x22, 0x4D, 0x18 };

public static bool IsLz4(byte[] data) => data.Length >= 4 && data.AsSpan(0, 4).SequenceEqual(Lz4Magic);

public static byte[] Decompress(byte[] data)
{
if (IsLz4(data))
return DecompressLz4Frame(data);

using MemoryStream input = new MemoryStream(data);
using GZipStream gzipStream = new GZipStream(input, CompressionMode.Decompress);
using MemoryStream unzip = new MemoryStream();
gzipStream.CopyTo(unzip);
return unzip.ToArray();
}

static byte[] DecompressLz4Frame(byte[] data)
{
int p = 4;
byte flg = data[p];
p += 2;
bool blockChecksum = (flg & 0x10) != 0;
bool contentSize = (flg & 0x08) != 0;
bool dictId = (flg & 0x01) != 0;
if (contentSize) p += 8;
if (dictId) p += 4;
p += 1;

MemoryStream output = new MemoryStream(data.Length * 5);
while (p + 4 <= data.Length)
{
uint blockSize = BitConverter.ToUInt32(data, p);
p += 4;
if (blockSize == 0)
break;
bool uncompressed = (blockSize & 0x80000000) != 0;
int size = (int)(blockSize & 0x7FFFFFFF);
if (uncompressed)
output.Write(data, p, size);
else
DecodeBlock(data, p, size, output);
p += size;
if (blockChecksum) p += 4;
}

return output.ToArray();
}

static void DecodeBlock(byte[] src, int start, int length, MemoryStream output)
{
int p = start, end = start + length;
while (p < end)
{
int token = src[p++];
int literals = token >> 4;
if (literals == 15)
{
int b;
do literals += b = src[p++];
while (b == 255);
}

output.Write(src, p, literals);
p += literals;
if (p >= end)
break;

int offset = src[p] | src[p + 1] << 8;
p += 2;
int matchLength = token & 15;
if (matchLength == 15)
{
int b;
do matchLength += b = src[p++];
while (b == 255);
}

matchLength += 4;

byte[] buffer = output.GetBuffer();
int matchStart = (int)output.Length - offset;
if (offset >= matchLength)
output.Write(buffer, matchStart, matchLength);
else
for (int i = 0; i < matchLength; i++)
{
buffer = output.GetBuffer();
output.WriteByte(buffer[matchStart + i]);
}
}
}
}
6 changes: 1 addition & 5 deletions STROOP.Core/GameMemoryAccess/StFileIO.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,7 @@ public StFileIO(string path) : base()

private void LoadMemory()
{
using FileStream? fileStream = new FileStream(_path, FileMode.Open);
using GZipStream? gzipStream = new GZipStream(fileStream, CompressionMode.Decompress);
using MemoryStream unzip = new MemoryStream();
gzipStream.CopyTo(unzip);
_data = unzip.ToArray();
_data = SavestateDecompression.Decompress(File.ReadAllBytes(_path));
}

public void SaveMemory(string path)
Expand Down
7 changes: 7 additions & 0 deletions STROOP.Core/ProcessStream.cs
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,13 @@ public bool RefreshRam()
}
}

public void CopyCachedRam(byte[] destination)
{
var ram = _ram;
lock (ram)
Buffer.BlockCopy(ram, 0, destination, 0, Math.Min(ram.Length, destination.Length));
}

public bool GetAllRam(out byte[] allRam)
{
allRam = new byte[MAX_RAM_SIZE];
Expand Down
33 changes: 33 additions & 0 deletions STROOP/Resources/Shaders/LevelModel.frag.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#version 330 core

in vec2 fs_texCoord;
in vec4 fs_color;
in float fs_worldY;

uniform sampler2D tex;
uniform bool useTexture;
uniform bool decal;
uniform float alphaCutoff;
uniform bool opaque;
uniform float maxY;

layout(location=0) out vec4 color;

void main()
{
if (fs_worldY > maxY)
discard;

vec4 c = fs_color;
if (useTexture)
{
vec4 t = texture(tex, fs_texCoord);
c = decal ? vec4(mix(fs_color.rgb, t.rgb, t.a), fs_color.a) : t * fs_color;
}

if (c.a < alphaCutoff)
discard;
if (opaque)
c.a = 1;
color = c;
}
34 changes: 34 additions & 0 deletions STROOP/Resources/Shaders/LevelModel.vert.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#version 330 core

layout (location = 0) in vec3 vs_position;
layout (location = 1) in vec2 vs_texCoord;
layout (location = 2) in vec4 vs_color;
layout (location = 3) in vec4 vs_ambient;
layout (location = 4) in vec3 vs_normal;
layout (location = 5) in vec3 vs_lightDir;

uniform mat4 viewProjection;
uniform mat4 model;
uniform mat3 viewRotation;

out vec2 fs_texCoord;
out vec4 fs_color;
out float fs_worldY;

void main()
{
vec4 world = model * vec4(vs_position, 1);
gl_Position = viewProjection * world;
fs_texCoord = vs_texCoord;
fs_worldY = world.y;

vec4 color = vs_color;
if (vs_ambient.a > 0.5)
{
vec3 n = viewRotation * (mat3(model) * vs_normal);
float nLength = length(n), lLength = length(vs_lightDir);
float k = nLength > 0.0 && lLength > 0.0 ? max(0.0, dot(n / nLength, vs_lightDir / lLength)) : 0.0;
color.rgb = min(vec3(1.0), vs_ambient.rgb + vs_color.rgb * k);
}
fs_color = color;
}
59 changes: 40 additions & 19 deletions STROOP/Resources/Shaders/Lines.geom.glsl
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#version 330 core
#version 330 core

uniform vec2 viewportSize;

Expand All @@ -12,28 +12,49 @@ layout (triangle_strip, max_vertices = 4) out;

void main()
{
vec3 p1 = gl_in[1].gl_Position.xyz / gl_in[1].gl_Position.w;
vec3 p0 = gl_in[0].gl_Position.xyz / gl_in[0].gl_Position.w;
vec4 c0 = gl_in[0].gl_Position, c1 = gl_in[1].gl_Position;
vec4 color0 = gs_color[0], color1 = gs_color[1];
float width0 = gs_width[0], width1 = gs_width[1];

float d0 = c0.z + c0.w, d1 = c1.z + c1.w;
if (d0 < 0 && d1 < 0)
return;
if (d0 < 0)
{
float t = d0 / (d0 - d1);
c0 = mix(c0, c1, t);
color0 = mix(color0, color1, t);
width0 = mix(width0, width1, t);
}
else if (d1 < 0)
{
float t = d1 / (d1 - d0);
c1 = mix(c1, c0, t);
color1 = mix(color1, color0, t);
width1 = mix(width1, width0, t);
}

float w0 = max(c0.w, 1e-6);
float w1 = max(c1.w, 1e-6);
vec3 p0 = c0.xyz / w0;
vec3 p1 = c1.xyz / w1;
vec2 dist = p1.xy - p0.xy;
vec2 perpendicular = normalize(vec2(dist.y, -dist.x)) / viewportSize;

float w0 = gl_in[0].gl_Position.w;
float w1 = gl_in[1].gl_Position.w;

gl_Position = vec4(vec3(p0.xy + perpendicular * gs_width[0], p0.z) * w0, w0);
fs_color = gs_color[0];

gl_Position = vec4(vec3(p0.xy + perpendicular * width0, p0.z) * w0, w0);
fs_color = color0;
EmitVertex();
gl_Position = vec4(vec3(p0.xy - perpendicular * gs_width[0], p0.z) * w0, w0);
fs_color = gs_color[0];

gl_Position = vec4(vec3(p0.xy - perpendicular * width0, p0.z) * w0, w0);
fs_color = color0;
EmitVertex();
gl_Position = vec4(vec3(p1.xy + perpendicular * gs_width[1], p1.z) * w1, w1);
fs_color = gs_color[1];

gl_Position = vec4(vec3(p1.xy + perpendicular * width1, p1.z) * w1, w1);
fs_color = color1;
EmitVertex();
gl_Position = vec4(vec3(p1.xy - perpendicular * gs_width[1], p1.z) * w1, w1);
fs_color = gs_color[1];

gl_Position = vec4(vec3(p1.xy - perpendicular * width1, p1.z) * w1, w1);
fs_color = color1;
EmitVertex();
EndPrimitive();
}
}
23 changes: 23 additions & 0 deletions STROOP/Resources/Shaders/Skybox.frag.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#version 330 core

in vec2 fs_screen;

uniform sampler2D sky;
uniform bool useTexture;
uniform vec3 color;
uniform vec2 windowOrigin;

layout(location=0) out vec4 outColor;

void main()
{
if (!useTexture)
{
outColor = vec4(color, 1);
return;
}

float x = windowOrigin.x + fs_screen.x * 320.0;
float y = windowOrigin.y - fs_screen.y * 240.0;
outColor = vec4(texture(sky, vec2(x / 1600.0, (960.0 - y) / 960.0)).rgb, 1);
}
10 changes: 10 additions & 0 deletions STROOP/Resources/Shaders/Skybox.vert.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#version 330 core

out vec2 fs_screen;

void main()
{
vec2 ndc = vec2((gl_VertexID & 1) * 4.0 - 1.0, (gl_VertexID >> 1) * 4.0 - 1.0);
gl_Position = vec4(ndc, 1, 1);
fs_screen = vec2(ndc.x * 0.5 + 0.5, 0.5 - ndc.y * 0.5);
}
15 changes: 15 additions & 0 deletions STROOP/Tabs/GhostTab/ColoredHats.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,21 @@ partial class GhostTab

uint COLORED_HATS_LIGHTS_ADDR => ghostRegionBase + 0x8300u;

public uint ColoredHatsGeneratorAddress => ghostRegionBase + COLORED_HATS_CODE_OFFSET;

public uint ColoredHatsLightsAddress => COLORED_HATS_LIGHTS_ADDR;

public bool ColoredHatsInstalled =>
Config.Stream.GetUInt32(ColoredHatsGeneratorAddress) == 0x27BDFFC0
&& Config.Stream.GetUInt32(ColoredHatsGeneratorAddress + 4) == 0xAFBF0014
&& Config.Stream.GetUInt32(ColoredHatsGeneratorAddress + 8) == 0x3C090401;

public bool GhostHackInstalled =>
Config.Stream.GetUInt32(ghostRegionBase + GHOST_LOOP_CODE_OFFSET) == 0x27BDFFC0
&& Config.Stream.GetUInt32(ghostRegionBase + GHOST_LOOP_CODE_OFFSET + 4) == 0x3C088036;

public bool GhostsDrawnInGame => GhostHackInstalled && Config.Stream.GetByte(numGhostsAddr) > 0;

private void EnableColoredHats()
{
using (Config.Stream.Suspend())
Expand Down
Loading
Loading