mgba-emu / mgba

mGBA Game Boy Advance Emulator
https://mgba.io/
Mozilla Public License 2.0
5.63k stars 770 forks source link

[Emulator detection] Inside-cap visual novel ports #1815

Open dogtopus opened 4 years ago

dogtopus commented 4 years ago

Taken from VNDB, Inside-cap is

A now defunct japanese group who specialised in porting visual novels to the GBA.

Seems that their games have some sort of emulator detection mechanism that if it is running on an emulator, it will shows this screen and asks the user to enter a challenge-response sequence.

Higurashi no Nakukoroni (J)-0

The interesting part is I am pretty sure I booted one of their games (attached below) on an ancient build of mGBA without triggering the emulator detection, probably with LLE BIOS (either Normatt or stock).

Probably worth inspecting. @endrift what do you think?

Higurashi no Nakukoroni (J).zip

(It's properly ancient and the play value is almost 0 these days since we got phone ports, etc. that looks a lot better than the GBA version so I guess nobody would get hurt by dropping it here)

ghost commented 4 years ago

LLE:

[STUB] GBA I/O: Stub I/O register read: 088 # 515 times
[GAME ERROR] GBA Memory:    Bad BIOS Load32: 0x00000000
[GAME ERROR] GBA Memory:    Bad memory Load8: 0xe55ec003
[GAME ERROR] GBA Memory:    Bad memory Load8: 0xe55ec004
[GAME ERROR] GBA Memory:    Bad memory Load8: 0xe55ec002
[GAME ERROR] GBA Memory:    Bad memory Load8: 0xe55ec002

HLE:

[STUB] GBA BIOS:    Stub software interrupt: SoundBias (19)
[STUB] GBA BIOS:    Stub software interrupt: SoundBias (19)
[STUB] GBA I/O: Stub I/O register read: 088
[GAME ERROR] GBA Memory:    Bad BIOS Load32: 0x00000000
[GAME ERROR] GBA Memory:    Bad memory Load8: 0xe55ec003
[GAME ERROR] GBA Memory:    Bad memory Load8: 0xe55ec004
[GAME ERROR] GBA Memory:    Bad memory Load8: 0xe55ec002
[GAME ERROR] GBA Memory:    Bad memory Load8: 0xe55ec002
karmic64 commented 2 years ago

If it helps, the exact subroutine that detects emulation is at $8007150. Returns 0 in r0 if it detects real hardware.

endrift commented 2 years ago

Thank you, that helps a lot. This test then works by comparing the timings of three access loops over different memory regions. The results are stored in r4 (VRAM), r7 (IWRAM) and r0 (EWRAM), in that order. The actual comparisons happen starting at $08007206:

ASSERT(vram >= iwram);
ASSERT(iwram + 0x1C00 <= vram);
ASSERT(ewram >= iwram);
ASSERT(iwram + 0x9000 <= ewram);

Winds up being the actual tested results.

The loops do 0x1000 32-bit loads-then-stores to incur a double-penalty 0x1000 times each.

In essence, it checks that VRAM access stalls are at least 0.875 cycles and EWRAM are at least 4.5 cycles on average, for 32-bit accesses. These are both good assumptions, but mGBA's gamepak prefetch messes up calculating the stalls so it thinks that VRAM accesses are free (nope) and EWRAM accesses take 0.5 cycles extra on average (very nope).

Hilariously, this test passes in mGBA if gamepak prefetch is disabled. But the game doesn't disable it.

fleroviux commented 1 year ago

@endrift the loop in question hits a prefetch buffer edge-case that generates a one cycle penalty. If ROM is accessed (either code or data, in this case code) and the current ROM burst transfer is stopped and the prefetch unit is also exactly one cycle away from prefetching the next 16-bit value, then a one cycle penalty is incurred.

For data accesses any ROM access means the current burst transfer is stopped. For code accesses it's the case when the requested address is neither the one currently being prefetched nor the first entry of the prefetch buffer. This is typically the case when branching, for example the conditional branch at the end of the loop body.