perfect_dark/docs/piracychecks.md

6.1 KiB

Piracy Checks

Perfect Dark contains several checks to determine if the game is running in unusual conditions, such as with a modified boot loader or with modified functions. The functions which check for these are in various places throughout the code and some even occur during gameplay. Many of these functions are themselves checked by other functions. In other words, the game detects if you've defeated the first measure then applies a second measure.

If piracy is detected, the game will either corrupt certain functions in memory which causes instability and crashes, or it will alter things within the game to annoy you.

The decomp project wraps all decompiled piracy checks in #if PIRACYCHECKS statements. The project is built by default with piracy checks enabled. These are generally fine to keep enabled even when making changes to the source because the project recalculates and updates the expected checksums during the build process.

List of Piracy Checks

boot

When Called: On power-on before the legal screen appears.

What It Checks: Checks an address in the bootloader (ROM address 0x2e8) for a known value.

Payload: Goes into an infinite loop so the system never boots.

__scHandleTasks

When Called: On every frame.

What It Checks: Checksums boot to make sure it hasn't been modified.

Payload: Writes 40 bytes of 0xff to 0x80095210. This appears to be related to sound effects but has no obvious effect.

cheat_menu_handle_dialog

When Called: When the Cheats menu dialog is opened.

What It Checks: Checksums __scHandleTasks to make sure it hasn't been modified.

Payload: Corrupts __scHandleTasks by nopping the call to __scAppendList and some of the function arguments to osRecvMesg. Most likely results in a crash.


door_finish_close

When Called: When a door finishes closing.

What It Checks: Checks that the value of osCicId is 6105.

Payload: Rewrites the start of door_test_interact_angle so it immediately returns false. This makes it impossible to open doors.

bot_pickup_prop

When Called: When a simulant in multiplayer picks up an item.

What It Checks: Checksums door_finish_close to make sure it hasn't been modified.

Payload: Rewrites the start of chr_check_can_see_target so it immediately returns true. This makes all guards able to see Jo through walls.

chr_uncloak

When Called: When a guard uncloaks.

What It Checks: Checksums bot_pickup_prop to make sure it hasn't been modified.

Payload: Disables the ability for the player and other characters to go up or down slopes. This is done by nopping the jr ra instruction in cd_find_ground_at_cyl_ctfril, which causes it to flow into the following function, which unconditionally returns false and only exists for this purpose.


chrs_check_for_noise

When Called: When a guard hears you.

What It Checks: Checksums __scHandleRetrace to make sure it hasn't been modified. That function doesn't appear to have any piracy checks, however.

Payload: Sets the Skedar King's body file number to zero, which causes the game to crash when loading Skedar Ruins or WAR.


lv_get_slow_motion_type

When Called: On each tick when unpaused.

What It Checks: Checks an address in the bootloader (ROM address 0xa5c) for a known value.

Payload: Corrupts the rspboot microcode, causing a crash.

lv_reset

When Called: When loading any stage (including title screen).

What It Checks: Checksums lv_get_slow_motion_type to make sure it hasn't been modified.

Payload: Writes a filesystem terminator file to the start of EEPROM. This disables the cartridge's save data, removing the ability to read existing files and create new ones.

body_instantiate_eyespy

When Called: When loading any stage that uses the eyespy.

What It Checks: Checksums lv_reset to make sure it hasn't been modified.

Payload: Nops _mema_free entirely, so any time that function is called it'll flow into the following function, which just returns. The effect this has is that the system is unable to free individual mema allocations which makes it unable to load rooms as you move throughout the level.


bg_reset

When Called: When loading a normal stage (eg. CI Training).

What It Checks: Checks an address in the bootloader (ROM address 0x454) for a known value.

Payload: Copies 64 bytes from a random location in ROM to a random location in RAM.

chr_consider_grenade_throw

When Called: When a chr decides to throw a grenade.

What It Checks: Checksums bg_reset to make sure it hasn't been modified.

Payload: Surrounds the player in infinite explosions.


tags_reset

When Called: When loading a normal stage (eg. CI Training).

What It Checks: Calls mtx_get_obfuscated_rom_base to get the value of osRomBase then compares it with a known value.

Payload: Copies 4KB from a random location in ROM to a random location in RAM.

bgun_tick_gun_load

When Called: When equipping any weapon.

What It Checks: Checksums tags_reset to make sure it hasn't been modified.

Payload: Corrupts tags_reset by writing 4 bytes of 0xff.


menu_tick_timers

When Called: On every frame except credits.

What It Checks: Checksums mtx_get_obfuscated_rom_base to make sure it hasn't been modified.

Payload: Corrupts bg_reset by writing 16 bytes of 0x12 to a random address within that function.

bg_tick_counter

When Called: On every frame.

What It Checks: Checksums menu_tick_timers to make sure it hasn't been modified.

Payload: Corrupts bg_build_tables by adding a fixed amount to 16 bytes within that function.


glass_destroy

When Called: When the player breaks glass.

What It Checks: Checks an address in the bootloader (ROM address 0xdc0) for a known value.

Payload: Sets the audio frequency to a high value which makes everyone sound like chipmunks.

explosion_alert_chrs

When Called: When there's an explosion.

What It Checks: Checksums glass_destroy to make sure it hasn't been modified.

Payload: Makes all explosions huge.