A Deep Dive into Spyro 3's Antipiracy
The implementation of antipiracy into video games was commonplace in the 1990s and the methods by which the antipiracy was performed and what effects it had on gameplay (if you were even able to play the game) varied wildly between game and system.
Spyro 3's antipiracy features have become especially well-known over the last 25 years, and are infamous for how unpredictable and brutal they can be. This antipiracy system has been discussed in detail on countless websites, videos, and forum posts online, and was even explained quite thoroughly in an article by former Insomniac programmer Gavin Dodd. What a lot of this discussion lacks, though, is the kind of precision we can get from actually reverse engineering the antipiracy system. In this post I want to explain in a little more detail how it all works, what measures the game employs to hinder your progress, and what we can do to circumvent it.
Note that this research is based on NTSC-U revision 0, the earliest release version, and thus it's possible that I've missed things from the other 4 versions of the game that also implement the antipiracy.
How It Works
Level 1: PS1 Antipiracy (wobble groove) The PlayStation had a unique form of antipiracy that related to the manufacture process used to make PS1 discs. Typical PS1 discs were manufactured with a slight “wobble” in the data grooves in the lead-in section of the disc that is imperceptible to and unwritable by standard disc drives, but can be read by the PlayStation console. This gives the console region information about the disc, so not only does it allow only legitimate discs to be run, it also enables a form of region locking. Debug consoles like the ones used by developers and journalists didn’t have this protection, and prerelease builds such as the ones given to journalists would usually be written to standard CDs that weren’t playable on a typical console.
To get around this, pirates would begin producing “modchips” which could be installed in the console and would provide the console with that region code in place of the disc doing so, to trick the console into thinking any game it was running was actually legitimate. This meant games written to normal CDs could be played.
Level 2: Safechk and Libcrypt To counter the introduction of modchips, new games would begin implementing means of detecting the presence of a modchip and halting gameplay (officially called “safechk”), meaning that burning a disc and using a modchip would not be enough to play games with modchip protection included. Early “non-stealth” modchips would continue providing a region code to the console beyond the point that it would usually be accessible, so a game with this protection could use this to infer that a modchip is installed into the console. This also means that this protection would trigger in a completely legitimate retail copy of a game if a modchip is installed.
In Spyro 3, safechk terminates gameplay on the title screen with a spooky message:
In European markets a different method, known as Libcrypt, was used instead. It is speculated(?) that using forms of protection (such as the latter modchip protection) that penalised players for modifying their own consoles would violate consumer rights legislation in Europe, hence why Libcrypt was used. To clarify, NTSC-U copies of Spyro 3 used safechk, whereas PAL copies used LibCrypt.
Libcrypt works by storing an encryption key as bad position data in a number of the CD sectors’ subchannel data. Typical disc writing tools would try to correct this data, resulting in an incorrect value. The key is used when loading levels to decrypt a section of each level overlay - if the wrong key is provided, this code remains encrypted, resulting in a crash the moment the console tries to run it.
You may have seen this before if you’ve tried playing a PAL version of Spyro 2 or 3 on an emulator without an associated “SBI” file - entering any level except for the first one (e.g. Idol Springs or Cloud Spires) just results in a crash. The earliest levels (the first homeworld and first normal level) don't have this same encrypted region.
Level 3: Overlay Encryption and Anti-Crack The third layer of antipiracy is the one that Spyro 3 is infamous for: the anti-crack protection. Typically, safechk and Libcrypt would be circumvented by patching a game to remove this protection measure. Therefore, the logical next step for the developers is to encode a means of detecting when the game’s code has been modified.
In Spyro 3’s case, this is done multiple times throughout the game and the measures are gradually implemented, to make it less obvious to a pirate that their crack hadn’t been successful - the alternative would be making the game fail early on like the modchip protection does, which would send a clear message to a pirate that they hadn’t figured it out yet.
The game employs a type of hash function called a CRC in order to perform regular checks over its own code. I’ll get into the specifics of this checksumming later on in this post, but the tl;dr is that if any of the code has been modified, the checksum will fail, and the game will know that it’s been tampered with.
On top of all of this, the overlays stored on the disc are actually encrypted (on top of the aforementioned Libcrypt encryption!) and get decrypted when they're loaded into the game. The encryption is quite simple - a value at the top of the overlay is just XOR'd over most of the overlay's contents - but it's enough that it makes identifying where code is located and where those anti-crack measures take place just a bit more difficult.
Some Terminology For a more technical explanation of how the checksumming works, I’m gonna need to explain a few terms first, for the less tech- or Spyro- savvy people:
The “executable” is the file that contains all of the code that the game needs in order to run. However, as each level and scenario needs to also have its own unique code, some of the code is stored on the disc and is only loaded in when it’s necessary. This extra block of code is called an “overlay”, and there’s one for each level, one for each cutscene, one for the credits, one for the title screen, one for the atlas, one for the options menu, and another for loading screens.
When I make a distinction between code and data, by “code” I mean the functions that tell the game how to run, and by “data” I mean the values and assets that the code uses. A lot of the game’s data is stored in “wad” files for each of the levels - something like Sunny Villa’s models and layout are an example of data. Some data (such as the list of eggs collected by the player so far or the CD locations of where each line of audio dialogue is held) is stored in the executable and overlay files. The executable also holds all of the data that is used across all levels, like Spyro’s current state.
The code part of the executable - all of the functions - is stored in one big block called “.text”. We will consider the rest of the executable to be “data”, but there are actually several different blocks of data with different usages which surround the .text component.
“Moby”, short for “moveable object”, is the term Insomniac used to refer to the objects you encounter throughout the game. Most of the things you see in the game that aren’t Spyro (gems, Rhynocs, Sparx, etc.) are Mobys. Mobys carry a significant amount of the game’s basic functionality, and without them the game would be very different indeed. The “Moby class” is the type of Moby a Moby is.
A “primary” egg is the main egg required to complete a level in Spyro 3. This is also called an “end-of-level egg” sometimes.
The Checksum Specifically, the game performs a modified version of a CRC-16/UMTS checksum (initial and final XOR 0, polynomial 0x8005). The game typically scans two regions - usually scanning part of the executable and part of the overlay - and the result of the scan over the first region is fed into the beginning of the scan over the second region. The first region that is scanned is done so in an unusual way - rather than just incrementing the scan pointer by 1 after each byte (i.e. scanning every single byte in the region), the game will also increment the scan pointer by whatever the current checksum modulo 4 is, meaning the scan pointer will jump by 1-4 bytes after each byte is checked. This means that one region gets a "quick" scan that is likely to detect modifications which will alter the final checksum, whereas the other region gets a more thorough scan. Every region of code in the game receives at least one thorough scan. The game scans 64 bytes per frame, and in practice this means that a typical scan in a normal level will typically take around 2 minutes to complete - this means that most measures can be avoided by simply going through the game really quickly, and finishing levels before the scans complete.
Every overlay, as well as the executable, includes a small four byte instruction that looks like code, but really it’s never directly used. In pre-release builds and overlays that don't run antipiracy (e.g. the cutscenes), this instruction is just a jump instruction to itself. For level overlays in builds that do have antipiracy, though, the middle two bytes of this instruction were modified during the game's build process to make it so that the scan that runs over that particular overlay will ultimately equal zero. This is how the game checks whether it's been modified, and it's also how they managed to implement the antipiracy without explicitly including a checksum anywhere - the result of this scan will always be 0, and if it's not, something's been modified. This results in checksums that are much harder for a pirate to crack, but it also makes the implementation more complicated and time-consuming.
The result of this type of checksum is usually a two byte value, though in Spyro 3’s case it stores the value as a four byte value instead. Due to the way that CRC16 works, if the bottom two bytes (where the usual two-byte value is held) equal zero, then the upper two bytes also equal zero, which means in practice the distinction doesn’t matter very much. It also means that, as Gavin Dodd noted, any two consecutive bytes inside the region being checksummed can be modified to make the checksum equal whatever you want it to, which is how they're able to always fix it to be 0.
The scans are run over the ".text" regions of the overlay and executable. This means that although code is checked with a fine-toothed comb, data is skipped over entirely and has absolutely no impact on the antipiracy. People have asked me why my Spyro 3.5 mod doesn't trigger the antipiracy - the simple truth is that it doesn't really touch any code (or at least none in any level which runs antipiracy)!
Scan Types There's actually quite a few different ways that the scans are performed:
The most common kind of scan is performed in all typical levels, including critter levels and boss levels - only homeworlds, speedways, and Sparx levels are excluded. The point of each of these scans are to confirm the integrity of the level overlays - a "quick" scan is run over the executable (which means that any major corruption in the exe will also result in this scan failing), followed by a thorough scan over the overlay. 64 bytes are scanned per frame, and the scan starts (2 + levelIndex) bytes after the start of the exe's .text region (this level index adds some extra variation so that the outcome of the first half of the scan is less predictable).
The loading screen overlay performs a very similar type of scan to confirm its integrity. In order to obscure what this one is doing, the checksum pointer is offset from where is actually being scanned, and the value of the checksum is left shifted by 7 bits, but really it's all functionally identical to the scans in levels. 5120 bytes are scanned per frame, and the scan seems to start 1 byte after the start of the exe's .text region, for some reason.
A few scans that take place during startup start (with a quick scan) in the title screen overlay and end (with a thorough scan) in the executable, confirming the integrity of the latter. Rather than modifying a jump instruction to get this one to equal zero, it looks like they modified an unused jr ra instruction in the middle of the executable. Each region is scanned in a single frame, and the part of the executable that is scanned skips the first 2 bytes like the level overlay scans do.
In order to confirm the integrity of the title screen overlay (and to make it so that the check values added into the exe and title overlay don't conflict with eachother), this overlay is thoroughly scanned by itself a few times during startup and inside one of the title functions, each scan taking place in a single frame.
Scanning Mobys Although a few special scans are performed by loading screen and initialisation functions, most of the measures are run inside the code to update the Mobys. Each class of Moby has its own update function, and a handful of these Mobys contain the code to perform checksums (usually limited to one specific instance of a given moby class, like one particular NPC). Every Moby starts with a pointer to some extra property data (referred to as “extra data”, the “moby tag”, or the “props” depending on who you ask, though its official term isn’t known), and both the current value of the checksum and the current scanning address are stored in there somewhere for each scanning Moby.
When this checksum completes, if the check value equals 0, nothing happens. If it’s nonzero, the Moby performs one of the measures, and then usually sets the checksum to 0 so the measure doesn’t repeat on subsequent frames.
In a few instances the scanning Moby isn’t the one that performs the measure, and instead, a separate Moby watches the checksum and performs the measure itself.
Secondary Scans A few levels - critter levels and boss levels - have what I call a “secondary” scan. This additional layer of antipiracy performs a scan not over an entire overlay and executable, but rather just over a single function, scanning a single byte per frame. The result of this isn't usually 0, so instead the value is actually checked against a value stored inside one of the variables in the overlay header - this header is usually full of pointers to overlay functions, but when a secondary scan is present, one of these pointers will instead equal the address of the start of the overlay with the final checksum value embedded inside of it. If there's a mismatch, a secondary measure (different to the primary one) will be performed.
These secondary scans are typically a lot quicker to complete than primary scans, usually only lasting around 30 seconds. Because they don't need to equal 0 and instead just need to match something in the overlay header, a failing secondary scan can easily be fixed by updating this value.
The majority of the Mobys performing antipiracy scans are the ones you least expect - helpful NPCs you encounter on your adventure. Although it might not be a shock that the bosses are responsible for the primary scans in their levels, the critters that claim to help you are secretly performing secondary scans in the background too. The save file reset in Sorceress's Lair? That's actually Agent 9's doing.
Antipiracy Versions The antipiracy is present in all of the release versions of the game - both NTSC-U revisions and both PAL revisions. As far as I know, they're largely the same between each version of the game. A notable difference in the PAL version is that one measure changes the language of the game randomly - this measure is present in the NTSC-U versions too, but always results in the game staying in English.
The later of the two review builds - the September 11th SCEA review build - also includes the antipiracy. This version of the antipiracy might have some differences - one known difference is that Zoe's warning in Sunrise Spring Home is never given. The audio track for this interaction is present, and the measure that sets the flag to activate her is present, but the Moby that sits in the doorway waiting for the flag to be set and the code for checking this flag is not present.
Earlier builds - e.g. the September 4th SCEE review build and the demo versions - do not implement the antipiracy, likely a deliberate exclusion. You can see they were working on the antipiracy at this point in time, though, as the unmodified jump instruction used as an embedded checksum is seen to be present in versions of the game as early as the game's third (of six) demo disc build.
List of Measures
Here's a list of all of the measures employed by the game, under normal circumstances where all of the standard antipiracy effects are active:
Exe Corruptions If a checksum fails in the loading screen or title screen overlays (exactly the places where the player might try to circumvent the modchip and Libcrypt protection), the game will corrupt a random part of a function. The functions that can be corrupted are all ones that are only run once during startup, so this won’t affect how these functions behave but it will cause all subsequent checksums to fail.
One function that can be corrupted by this measure is the initialise function. This is the function that the infamous Sorceress measure checks, so these corruption measures are what silently set the player up for failure.
Zoe's Warning Arguably one of the two most infamous effects of the antipiracy is also the simplest. If the measure is triggered in the first level of the game, Sunny Villa, then upon returning to Sunrise Spring Home, you'll find that an extra Zoe has appeared, warning the player through Spyro that they're playing a hacked copy of the game. This measure sets a flag to the save file, amongst all the other progress flags, to make Zoe appear. Despite this, the presence of this flag - or of Zoe - doesn't actually have any impact on gameplay. Loading an affected save on a standard copy won't result in any of the other antipiracy measures running, and just because you haven't run into doesn't mean you're playing an legitimate copy - she only appears if you've spent long enough in Sunny Villa on a modified copy for the measure to trigger.
Based on what we see in the review builds, it's possible that this was one of the last measures added to the game.
Skate Pads Deactivation Sunny Villa is unique in that is has an extra measure, and it’s one that I’ve not seen documented anywhere. If you stay in the skateboarding sublevel for long enough for the antipiracy to trigger, it sets the state of all of the skateboard pads to 6, which effectively disables them. Pretty minor, compared to most of the measures, because the effect is only temporary.
Gem Removal One of the most well-known measures deletes gems from random levels, making those levels impossible to complete. Every level has a bitmask representing all of the collectables in that level (gems, breakable walls, extra life jars, that sort of thing). Each time this measure runs, it picks a random level, a random collectable flag in that level, and sets it to 1 (collected). If that flag corresponds with a gem, that gem will no longer appear, as the game believes it’s already been collected (without its value being added to your total).
How the collectable is selected, how often the measure runs, and whether this collectable flag actually corresponds with any collectable in-game is more complicated. Each level that this measure is run in only impacts a specific selection of levels - for example, the measure in Cloud Spires will only set a flag if the randomly selected level is from Midday Garden Home or later. When the measure is running and when the right conditions are met, every 32 frames:
A random number from 0 to 39 is selected, corresponding with the 37 levels of the game, plus 3 unused level indices. If the selected level is to be ignored by this measure, nothing happens and the game will wait another 32 frames to try again.
Pick two random numbers from 0 to 7. The collectable bitmask for the selected level is 8 ints long (256 bits), so the first of these random numbers is used to pick which int to write to, and the second is used to determine which of the lowest 8 bits should be set to 1. This means only the lowest 8 bits of each 32 bit int are affected, which I'm not sure is intentional - at most, a quarter of a level's collectables can be deleted in this way.
Naturally, some of the things marked as collected by this measure aren't even collectable and so may be totally unaffected by it. This would also theoretically talk to NPCs on your behalf, be able to break walls, and make life jars disappear. In the worst case scenario, 64 individual gems (of varying value) can be removed from every affected level - for some of the later levels this can amount to hundreds of gems. It'd be interesting to know how many gems that means in total across the entire game.
Fall Plane Lifting Every level has a value representing the height at which you lose control of Spyro if he falls off the world. I call this the "fall plane", though I think other people call it the "flop plane". It's not a physical plane that exists in the world, though, it's just a height value, a Z-coordinate. This measure sets the height of Sunrise Spring Home's fall plane to 13800, and then repeats this effect another 39 times... eh?
So, 13800 is a bit higher than its usual height for this world, albeit not high enough for the player to notice in usual play. If you use an out of bounds trick like Whirlwind Bypass, you might find Spyro falls to his death a bit earlier than you'd otherwise expect. Why would a measure so ineffective be implemented, and why does it set this same value 40 times?
I think this was either due to a small typo, or due to an intentional scaling back of the effects. One of the concerns when implementing the anti-piracy was that it shouldn't be so obvious and game breaking that players are immediately able to notice there's something wrong, so it's possible the original effect of this measure was deliberately removed. That original effect was most likely setting this value to 13800 in all levels, of which there are 40* (so this would have been a for loop that iterated over every level).
If you manually change this to 13800 for each level, you'll quickly realise how damaging this effect is. For many levels, Spyro's starting height is lower than this 13800 value. This causes Spyro to fall into the floor, which in turn pushes him upwards. The impact of this is a sight to behold:
If this effect were to be activated to its full extent, any progress through the game would essentially be completely halted once you reached Midday Garden Home - at least until you restart the console.
*Okay, there's 37 levels. But all of the level-related arrays have 40 entries, and in fact there are three additional level names at the end of the level names list - all three are just called "Empty". These probably weren't planned levels, I suspect they just wanted all of the arrays to align to a multiple of 4.
Egg Removal A lot of the measures result in your hard-fought eggs being snatched from right under your nose. When one of these measures activate, a specific set of eggs - usually either only the end-of-level "primary" egg or every egg in a level, for one or more levels - are set as uncollected. This can impede progress, especially if you reload the game and find your egg counter has gone down, but funnily enough, this doesn't affect the egg counter in the HUD until you restart your game. This makes it possible to go back and re-collect eggs you've already found to temporarily boost your egg count and open up portals early, at least until you quit the game and reload.
In a couple instances, one of the measures actually flips the collection state of a bunch of eggs, rather than setting them as just uncollected. Under normal means these eggs would certainly have to be collected before you could encounter this measure, so this would act as removing these eggs from your collection, but in the context of all the other measures which could also remove these eggs, this actually acts as a way of getting some back. There is also an instance of this running every frame once the measure starts (rather than just once), meaning that it'll even revert an egg collected in the level you're already in if the measure is running when you collect it.
Critter Removal Some levels take the removal of eggs one extra step and also reverse progress the player has made in freeing each of the critters. When this measure is run, as well just removing the end-of-level egg from a critter level from the player's collection, the game also locks that critter back into its cage and Moneybags returns to get some more cash out of you. You don't get the gems you've lost from your previous transaction back until you restart the game, so repeatedly paying for critters enough times could result in you temporarily going bankrupt.
Even if this measure activates after you've already done all of the critter areas in that critter's world, it can still prevent you from entering that critter's sublevels in future worlds, despite these usually always being open. There are a couple of exceptions to this where a sublevel is accessible regardless of whether the critter is accessible or not - see my old "doors" video for more info.
Stop Local Fodder Spawns One of the boss levels has a measure that stops sheep from spawning until you leave the level. Usually fodder spawns when your health is below a threshold that is determined by the adaptive difficulty and how many times you've died to the boss, but when this measure is active the fodder only spawns when it’s below zero, and this is preserved even after dying.
Unset HUB2HUB Vehical Progress Flags There are flags which are set to 1 when the balloon and whirligig are usable, respectively. These flags seem to be used by the game to figure out which vehicles should be present and what levels you should be able to travel to with it. One of the measures will turn off one or both of these flags, which in practice can make this menu show less than it's supposed to or make the wrong vehicles appear.
Supercharge Speed Decrease The player’s supercharge speed is a function of the angle of the slope Spyro is charging on. Usually, there’s three values - a base speed, a downhill speed, and an uphill speed - which are used to determine how fast you should go.
One level has a measure which makes the base and downhill speeds decrease by 16 every frame, and each of these values only stop decreasing once they're equal to or less than the (usually much slower) uphill speed. It takes around 14 seconds for this measure to reach its maximum potency, at which point Spyro’s supercharging speed is comparable to just walking. These values are used across levels, so it persists until a console reset!
Disable Pause Menu The measure in Enchanted Towers overwrites the start of the menu preparation function to return immediately. In practice, this makes the pause button completely stop working, and jumping onto one of the hub-to-hub vehicles will make Spyro jump straight off again.
Because this one changes the exe, the antipiracy failing in just this one level is enough to make checksums start failing in other levels and in loading screens too. But, since this measure also hinders progression quite significantly, it’s not likely that a player would stick around long enough to see the effect this causes.
Language Randomisation If you’re playing one of the PAL versions of the game, there’s an extra measure which appears in a couple of levels which changes the language setting to a randomly chosen language (which also means a 1 in 5 chance that the language switches to whatever it already was). In the NTSC-U builds, this measure still runs, but the only language that it can change to is English.
Unused Moneybags Gem Flag One measure sets a special progress flag in your save file. I believe this is the progress flag used in Spyro 2 to tell the game to restore all of the gems you’ve paid to Moneybags (e.g. once finishing the game) - it’s even in the earliest builds of Spyro 3, suggesting it’s certainly a leftover.
In Spyro 3, though, it behaves a bit funny. In practice, you’ll only notice the effects of this one if you quit to title after the measure has activated and then reload any game. Restarting the console entirely seems to make the flag load after the gems have already been applied to your save file, so it doesn’t have an effect.
What does the measure do? It just gives you back the cash you’ve spent on Moneybags until you quit the game again. I’m not really sure why this is used as an antipiracy measure, but I guess it would confuse any player that bumped into it.
Sparx Maximum Health Decrease A measure used in a few levels decreases Sparx’s maximum hitpoints. This is usually first noticed in Spike’s Arena, where Sparx stays green until the console is restarted. Later levels decrease his health even further, making Sparx disappear completely. Collecting a butterfly that would raise his health above the decreased maximum will also just make Sparx disappear.
Midnight Mountain Level Corruption One of the weirdest and most consequential measures overwrites the values of two important arrays:
An array that converts level indices to level IDs gets overwritten such that the values for all levels from Crystal Islands to Sorceress's Lair use the values for Sunny Villa to Buzz's Dungeon instead.
An array that converts level IDs to level indices gets overwritten such that the values for all levels from Crystal Islands to Sorceress's Lair use the values for Sunny Villa to Buzz's Dungeon instead.
This means that if you attempt to go to any of the levels in Midnight Mountain Home you'll end up in the corresponding Sunrise Spring level instead. The portals also tell you this, as you might've seen in this ancient video.
This also has some additional side effects that were most likely not intended, though. Certain egg related checks - like checks for whether you've got the Sorceress's egg - now end up looking at the eggs collected in the Sunrise Spring worlds instead. This means that every instance of Moneybags (who disappears if you've defeated the Sorceress) will instantly be paid off, which can cause critter unlocking animations to seemingly randomly play in homeworlds (as they play automatically if you've not seen the cutscene yet). He'll even turn up in Midnight Mountain Home with his secret egg, and Zoe will let you into Bugbot Factory (a level you can normally only enter after defeating the Sorceress) if you've collected the other Sparx eggs. Oops.
Sorceress's Lair can even be selected from the vehicle, meaning you might be able to enter her level early - except, of course, it dumps you in Buzz's Dungeon instead.
One of the strangest effects of this measure is due to the fact that this measure results in a discrepancy between the current level ID - which remains as it's supposed to be - and what level you appear to be in. One line in the vehicle code determines what level to take you to by just adding 3 to your current level ID. If you're in Buzz's Dungeon because you selected Sorceress's Lair on the vehicle warp screen, and you defeat Buzz, it can send you straight to Super Bonus Round instead!
All this has been used to allow for 117% Anti-Piracy speedruns, and if you've ever played any of the Russian / 3-in-1 Spyro hacks, you might have seen instructions for getting to Super Bonus Round included with the rom. A Google Doc made by ShingerZ0s detailing many features of the antipiracy, including how to run through the whole game and get into Super Bonus Round, can also be found here.
Save File Reset Of course, the most infamous measure takes place in Sorceress’s Lair. Half a minute into gameplay, the player is suddenly sent back to the balloon room in Sunrise Spring Home with all of their eggs and gems completely rescinded. All of the player’s progress is reverted akin to if they’d started a new game, with the exception of a few things that stay unchanged under the hood:
Any pause menu options that have been changed will stay as they were.
The player’s performance in collecting each egg is stored to allow the adaptive difficulty to calculate how challenging the game should be. This recorded performance (and the current difficulty) is not reset by the measure.
The total number of lives the player has is unchanged.
The total deaths the player has had is unchanged (a value stored in the save file that isn’t used in-game).
Speedway race times are not reset. Amusingly, this means that if you’ve played any of the speedway races and you’re on game revision 0, saving and quitting the game will instantly give you the infamous speedway bug, as those eggs will no longer be obtainable.
Where Are The Measures Used?
I've included a table of all of the measures the game employs and how they correspond with each level below. I should probably preface the list of antipiracy measures by saying that there are a few additional intricacies to this system:
Typically, if there are multiple instances of a moby whose class runs antipiracy checks in a level, there will be some extra conditions on when it runs so that only one of them will run the checks. For example, if an NPC class is programmed to run a scan, this may only apply to one specific instance of that NPC type.
There’s usually extra conditions that need to be met for a scan or measure to run, e.g. the moby needs to be in the right state. Since the first and second parts of a scan and the final measure(s) are all in different parts of a moby's code, sometimes the condition needed for each stage to run can be different. I've simplified things slightly here, and so there may be important edge cases where a measure will never occur.
There are a small handful of instances where the moby that performs the scan isn’t the same one as the one that performs the measure, and the measure moby always observes the scanner moby to see if the checksum has failed.
Mobys too far from the player often won’t run their code, which might cause some of these mobys to stop their scans. Each moby has an update radius and it's possible to set this radius to infinite (-1). I've not personally checked how many of these mobys have an infinite range and how many have a finite range, but it definitely seems to be a mixture of both.
Apologies for posting this table as images, if there's a nice way of including tables in a tumblr post I couldn't figure out what it was:
Some footnotes for the table above:
[1] In this instance, runs every frame after the measure starts.
[2] Molten Crater was likely intended but is not included.
[3] i.e. the Midnight Mountain worlds (including Bugbot Factory) and Super Bonus Round are untouched.
[4] An instance of this moby is present in both the main area and the Sleepyhead arena, so this scan can run in both areas.
[5] If the primary egg is collected when the scan is taking place in the executable, the scan will end and no measure will run (as these buttons only run this part of the scan whilst unpressed). However, if the scan is in the overlay when this egg is collected, it will continue until it's finished. Therefore, the timing of this egg has an effect on if the measure is run and which measure is run.
[6] The underwater area also seems to include some invisible copies of Sheriff Wyatt, probably for minigame code reasons. Assuming these run code as usual, these could also cause the antipiracy measures. This means this scan occurs in not only the main area, but two sublevels as well!
[7] In fact, these measures are performed by all mobys of a given class within a certain positional range. For Icy Peak this is all cannons below Z=10240, for Lost Fleet it's all cannons above Z=30000, and for Haunted Tomb it's all buttons with Y<30000.
[8] This is specifically all chickens that are set to have an infinite update distance.
[9] In fact this applies to all bird NPCs that aren't linked to a cage, of which Cpl. Gabrielle is the only one.
[10] This measure runs on all rockets in the initial layout (which act as spawners) that currently have a rocket spawned and also have an infinite update radius. However, all of the rocket spawners in each area of the level have finite update radii.
[11] This measure only runs when this moby is state 0, which in practice it never is.
Patching the Measures
The obvious question - particularly to those that want to mod the game - is “how do we prevent the antipiracy?”. This had been done in some form in the past, with Paradox’s “perfect crack” seemingly working around it as early as 2000 (I’ve not looked into how they did this, though!), and patches to remove various measures (primarily the big one in Sorceress’s Lair) have been produced since then.
In my research, I considered each of the following approaches to patching out the antipiracy:
Checksum correction: just running over each of the check values and correcting them to make all the checksums zero is enough to trick the game into thinking it’s legit - this is exactly what Insomniac used to make the game in the first place. Pros: allows for complex modding. Cons: not compatible with on-the-fly memory modification like GameShark.
Measure removal: prevent the game from performing any measures by simply removing all of them. Pros: relatively straightforward to produce and easy to apply. Cons: highly version dependent, and a bit tedious to produce.
Scan skipping: set each scan variable in the game’s data to where the scan should end to prevent them from taking place at all. Pros: an elegant solution which would be easy to implement. Cons: may provide an imperceptible performance advantage which may be used by speedrunners to get an edge? I’m reaching with this one.
Decompile: a full decompile could simply ifdef the measures out. Pros: incredibly effective, and would mean you can mix rom patches and GameShark codes easily. Cons: an absolute nightmare to get to this point, because it's a whole lot of effort.
These aren't the only approaches, of course - one recent idea (first implemented in December last year) comes from unicorngoulash, who experimented with using breakpoints to revert the game’s code whenever it’s read by the CRC scans, but not when it’s being executed. I believe this is now (or will soon be) a feature of the DuckStation emulator, so anyone using this emulator should be able to produce new GameShark codes using this method without triggering the antipiracy.
I spent some time working on each of my ideas and was able to come up with a solution for each one:
I've used a primitive and incomplete form of checksum correction in the past as far back as my Spyro 3 randomiser, but it wasn't until late last year that I produced a tool capable of handling all of the different checksums that the game throws at you. This tool is called Dragonbreath and is mostly useful if you're using something like the Spyro 3 Decompile to mod the game. I did also start making a Python script which uses Dragonbreath which can be run on any modified version of the game to automatically fix all of the checksums, and that's in the root directory of the decompile, but I've not finished making it as of this post. Previously this used brute forcing to find the desired check values, but more recently I've implemented a suggestion that uses a backwards CRC to calculate the specific value that the check needs to take for the overall checksum to be 0. I'd tried and failed to implement this method myself in the past, and mathematically I'm not really sure why this new method works... but it does.
Dragonbreath can be complicated to use, so I've also developed Acorn, which sets every checksum address to its final value, resulting in the game never running any scan (aside from a few that are effectively harmless if the rest don't run).
I’ve also produced a big GameShark code to patch out the measures for NTSC-U revision 0 (but you might want to check this works properly!)
As for the decompile, although it is publicly available to use, we aren’t yet at the point of being able to use it to completely patch out all the scans with ease, but it's definitely theoretically possible in the long run.
This all pretty much covers the entirety of how the anti-crack protection works in this game. It's really surprisingly straightforward when you see it all laid out like this (...right?), but the interweaving effects of all the different scans and measures still manages to create a truly bewildering experience for the player and for anyone that wants to patch it all out. There are some other things to cover, like whether there's any changes in other versions of the game, and what update radius each moby has, but I may cover all this another time.
If you want even more technical details and data relating to the antipiracy, see my Spyro data sheets.
Sources and Useful Links [1] Gavin Dodd's "Keeping the Pirates at Bay" [2] ConsoleMods page on Modchip protection [3] Tonyhax list of protected games [4] SBI files for LibCrypt-protected games [5] Red-J's “Libcrypt PS1 Protection bible” [6] Spyro 3 "Iso Glitches" video [7] 117% Anti-Piracy speedrun example by Plumed Basilisk [8] ShingerZ0s antipiracy guide [9] Spyro 3 Decompile [10] Dragonbreath Antipiracy Tool [11] Acorn Antipiracy Tool [12] Measure Removal GameShark Code [13] Spyro data sheets, including antipiracy data












