Skip to content

Fix unity pointer paths on mono v2/v3 that have generic classes#132

Open
mitchell-merry wants to merge 11 commits into
LiveSplit:masterfrom
mitchell-merry:mono-v3-generic-class-field-count-fix
Open

Fix unity pointer paths on mono v2/v3 that have generic classes#132
mitchell-merry wants to merge 11 commits into
LiveSplit:masterfrom
mitchell-merry:mono-v3-generic-class-field-count-fix

Conversation

@mitchell-merry
Copy link
Copy Markdown
Contributor

@mitchell-merry mitchell-merry commented May 10, 2026

Why, what is the context?

Trying to fetch field offsets on generic classes in mono v2/v3, e.g. class MyClass : Singleton<MyClass> doesn't work since the field count logic is different on for generic classes in those versions.

What?

Add v2/v3 logic, which fetches the class kind and then if it's GINST fetches the generic container class.

This logic is stolen from a variety of places, but in particular asl-help does this: https://github.com/just-ero/asl-help/blob/9a17c5ec7108aba1fe1932358703f4e6adaa6b2c/src/Unity/Mono/Managers/MonoV2Manager.cs#L16 and I also had a look at the mono source to verify it: https://github.com/mono/mono/blob/0f53e9e151d92944cacab3e24ac359410c606df6/mono/metadata/class-accessors.c#L216

Testing

I tested this on:

  • Baldi's Basics Classic Remastered (64 bit windows + mac, mono v2)
  • Bread & Fred (64 bit windows + mac, mono v3)
  • A Short Hike (32 bit windows, mono v3)
  • I did not test this on 32 bit windows mono v2 since I couldn't find a game that matched that description. I used the offsets from https://github.com/hackf5/unityspy/blob/master/src/HackF5.UnitySpy/Offsets/MonoLibraryOffsets.cs#L12 (an old comment referenced these for this version) but can't verify the logic holds up. If you know of a game that fits this please let me know.
  • Mono V1 is unchanged

Additional thoughts
Not exactly sure what the best practice is in rust for this, but I think we should be using some sort of strategy pattern similar to what asl-help does. Having these match (mono version) cases everywhere is going to get convoluted, and in this case I've even had to include offsets that don't even exist in mono v1 (and thus I've defaulted them to 0x0).

Comment on lines +67 to +82
// See https://github.com/mono/mono/blob/337052f86112fc0dc8435c5c4a2de43b399a14bb/mono/metadata/class-internals.h#L327
Version::V2 => {
// TODO I feel like I'm doing this very poorly

let byte =
process.read::<u8>(self.class + module.offsets.class.class_kind)? & 0x7u8;

if !MonoTypeKind::is_valid_bit_pattern(&byte) {
return Err(Error {});
}

// SAFETY: We just checked if this was valid
let kind: MonoTypeKind = unsafe { (&raw const byte).cast::<MonoTypeKind>().read() };

Ok(kind)
}
Copy link
Copy Markdown
Contributor Author

@mitchell-merry mitchell-merry May 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Er, my rust noob-ness is showing here. I'm trying to read just these three bits into a MonoTypeKind, but I suspect this can be done much simpler than I've done it here... could use help on that

Maybe I should drop the enum and just use a u8?

Comment on lines +131 to +132
class_kind: 0x1E,
generic_class: 0x94,
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are the untested offsets

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant