Java Edition:Header Editing
Header editing is a technique where by downgrading a c0.0.13a_03+ world to rd-132211, metadata about the world (the header) will be loaded as blocks, see the classic level format. This includes the world name, creator name, created time, and world size.
These can then be edited like any other block, being set to stone or air, which are IDs 01, and 00 respectively. This also applies in reverse, you can get this data to remain as blocks after upgrading, by making the header shorter, allowing you to obtain almost any block ID, with varying levels of complexity.
This technique works because rd-132211 is so simplistic, the save format it uses is simply a list of block IDs, and it can handle invalid block IDs without crashing, treating them like air. This means when loading a save from a later version, which includes extra data at the start, this data will be loaded as blocks, and the fact that this data corresponds to invalid block IDs is fine, and it can be interacted with anyway.
This can be combined with other save file manipulation techniques (essentially controlled corruption), to get more varied values, not merely setting it to 01 or 00, though there are still heavy restrictions.
Explanation
Save File Formats
Save files in all classic versions are stored as a gzipped file called level.dat, making them all technically inter-compatible, with caveats.
In rd-132211 it's a list of block IDs, each one a byte, the world is fixed at 256x256x64 in size (xzy), blocks are saved and read starting at the front-left-bottom corner (based on spawn facing), going left-to-right, front-to-back, bottom-to-top.
In c0.0.13 the save file gained a sequence before the blocks, here called the header, which stores:
- A magic number 4 bytes in length, to identify the file as a minecraft save, treating any other sequence as being an rd-132211 save file, and updating it accordingly.
- A version number, to plan for future changes to the format, (starts at 1 in c0.0.13)
- The world name, usually fixed at "A Nice World" or "--", stored as 2 bytes for the length, followed by an MUTF-8 representation.
- The creator name, set to either the username used when creating the world, "noname", or "unknown", stored the same way as the world name
- The creation date, set to either the date the world was created, or 1970-01-01 stored as milliseconds since that date
- The world size, in Height, Width, Depth (xzy), each stored as 2 bytes, usually 256x256x64
- The blocks themselves, as a list of block IDs, the same length as height*width*depth
In c0.0.14 the start of the header is identical, with the magic number and version (with the version bumped to 2), but the remainder of the file uses the Java's Serializable interface to store the world file, this format is much more complicated, and not viable to edit too severely, however it is important for 2 reasons: the blocks are still stored as a list of IDs, and the part before this looks like a header, even though it's not, and there's data afterwards.
The "target format" for header editing, is almost always rd-132211, or c0.0.13, you are trying to create a file which is read as one of these two formats. The formats afterwards are not viable to edit due to their complexity, and due to the fact that rd-132211 worlds are restricted to 256x256x64 exactly, any files larger will get the end deleted, and anything smaller will get padded, corrupting the file in most cases.
Targetting rd-132211
Since rd-132211 save files are just a list of block IDs, that don't start with the magic number, it's trivial to convert any other save file into this format, by overwriting the magic number, invalidating it. This will leave behind invalid block IDs however, which then need to be erased, as future versions crash immediately if these are present (the world will load, and save correctly, but you won't be able to do anything else).
Targetting c0.0.13
c0.0.13 save files are also fairly simple, and it's possible for them to stay valid, after some fairly significant edits, since the world name and creator name don't have any restrictions other than valid MUTF-8. In general this means keeping bytes between 00 and 7F as these are all assigned to ASCII characters, but 80 and above correspond to a multi-byte sequence, where there are a lot of gaps in encoding, meaning you're likely to hit something invalid. Simply a sequence of repeated 01 bytes is valid, corresponding to ␁. A sequence of repeated 00 however is not valid, as MUTF-8 uses the 2-byte overlong encoding of null, but in practice this is usually decoded anyway, and if you plan to go through the JavaScript remake you must overwrite strings this way, as 01 isn't valid for its strings.
So when your target is c0.0.13, you want to get a save file into a state where the bytes you want are where you want them, then change the version to 1 if it's not already, so it's treated as a c0.0.13 save file. Then change the world name length and creator name length in tandem, so they then end just before where you want the creation time, world size, and blocks to be read from. Then remove invalid block IDs, and invalid MUTF-8 characters. Then do any further editing to the creation time, and world size as required, before finally loading the result in c0.0.13 itself, or later, as later versions can still read older formats for conversion purposes.
A Word of Warning
This technique is very, very prone to corruption and crashes, as it essentially is corruption, but in a controlled manner, so if you don't control it properly, it's just regular corruption. Here is an incomplete list of what can cause problems:
Invalid Block IDs
Invalid block IDs are handled fine by rd-132211, but not by most other versions, if the invalid block is exposed to skylight, is not at the bottommost layer of the world, and is an ID of 80 or higher, the world will fail to load and it will forcibly re-generate a new one. If the invalid block is not exposed to skylight, or is 00-7F, it will crash the game loop as soon as it tries to render (this seems to be unavoidable), if you're in c0.0.14, you can then close the game and it will save fine. In c0.0.19+ the game will close itself and not save unless you manually close it first. Starting in c0.0.12a_03 block IDs of 80 or higher no longer crash the game immediately, but instead as soon as it is random ticked.
Invalid MUTF-8
Having invalid characters in your strings will cause the game to fail to load the save, and crash. This usually means a byte above 80.
Invalid Magic Number/Version
This doesn't directly cause a game crash, however this causes the save to be interpreted as a rd-132211 save, causing the remaining parts of the header to turn into blocks, which will then likely cause an invalid block ID crash.
Oversized World
If the world dimensions are larger than the number of block IDs listed, the game will crash upon reaching the end, as it expects more blocks than the save contains.
Undersized World
The game will crash if one of the world's dimensions is negative. Starting in c0.0.13a_03 loading a world that is 0 blocks wide will crash with a division by zero error, and starting in c0.0.14a_08 loading a world that is 1 block wide will crash with an illegal argument exception.
Spawning Outside the World
If your spawn position is outside the bounds of the world, the save will never load.
Obtaining
It is recommended to use a hex editor/viewer to view the contents of the save file, so you can have a better idea of what you are editing, you should find the level.dat, unzip it, and then open this file, this can be done beforehand or on a copy, if you don't wish to view your main save in this way. In rd-132211 itself, you should go to the front-left-bottom corner of the world, where the header starts, and dig a tunnel, so you can access the entire header, this will be approximately 30-40 blocks in the case of an c0.0.13 header, approximately 2.5 rows of blocks in the case of c0.0.14, and more if Header Stacking is used.
Simple Overwrites
Since the crux of the technique is simply writing 00 and 01 bytes, if this is the only modification you want to make, it's trivial. This could be changing the time stamp to far in the future, or far in the past, or making the world 1 block in size.
While in theory this technique could be used on c0.0.14+ worlds, where there are more fields to manipulate, in practice, the file is either too large, and will cut off important data, or too small, and get padded, mostly likely corrupting it. You could in theory perfectly manipulate the world size, so when the extra data is added, it equals exactly 256x64x256, this is not very feasible.
Username Overrun
Since strings, including the username, are stored with a length, you can change the length of the username to be shorter than it really is, this means the end of the username will be treated as the start of the timestamp, or depending on how long it is, the world size too, and even blocks.
Your regular username is unlikely to produce anything interesting, but some launchers like betacraft, allow you to choose essentially any username. However it's impossible to know what username restrictions were in place when this version was released, so it's unclear what could have been done at the time, and what couldn't, even if you registered the exact username you want, so this method is of questionable legitimacy.
Manual Downgrade
By far the most powerful form of this technique, is taking a save file, and changing data until it looks like a different save file. This is possible because of how the game determines what type of save file you're using, it looks for a matching magic number, if it doesn't find one, it assumes rd-132211 format. If it does find one, it checks the version, a version of 1 means 0.0.13, a version of 2 means 0.0.14+. So you can take a 0.0.14 save, and replace the version number, and suddenly it's a 0.0.13 save.
However changing only the version leaves you with a very corrupt 0.0.13 save, what's now being treated as the world name length is incredibly long, and contains many invalid characters, and the data after is not likely to fit either. You normally want to lower the world name length, and then the username length, and fill in all invalid characters within them with either stone or air (Start of Heading or null).
By choosing what exact values you set the world and username length to, you can control where the timestamp starts being read from, and therefore the world size too, while there are a limited number of places this can point to, there are many ways to shuffle data around, so the data you wanted to point to, is now in a place that you're able to point to.
File Manipulation Techniques
Block Configurations
Later versions have a larger degree of block IDs, allowing you to use these to get more varied data, however because the magic value is required for the save to load, and contains IDs that are always invalid, you can't edit the header directly. However you can place these blocks after the header, and then edit the header to be longer, so it points to them.
A specific example of this is if you place blocks starting at the 48th block (counting from 1), in a c0.0.19+ world, and then do a manual downgrade (treating it as a 0.0.13 save). You set the version to 1, the world name length to 1, and the username length to 01 73 then the timestamp will be read from the first 8 blocks you placed, and then the width (x) from the next 2, height (z) from the next 2, and depth (y) from the final 2.
Header Stacking
If the bytes you want aren't in the right place, you can move them further down the file, by corrupting the header so it's treated as a rd save (destroying the magic value is easiest), and then whatever version you load it in, will append another header, your options for this are c0.0.13 which has a 20 (32) byte long header, c0.0.14-c0.0.18 which have a 013F (319) byte long header, and c0.0.19+ which has a 014E (334) byte long header. These versions do crash from invalid block IDs, but c0.0.14 saves fine, and c0.0.19 saves fine if you kill it before it crashes.
By determining where the bytes you want are, where you can point to, via extended username/world lengths (world length 1, username length 0573 is a useful one), and taking the difference between these two places, you can try and make that difference by adding combinations of the mentioned header sizes. Though pay attention to what order you do them, the last one will be the one you have to take your username and world length from, doing downgrades doesn't require overwriting the magic value, since the save is invalid for other reasons, so this can be used to save time. And since c0.0.19 needs manually killing, and can be somewhat inconsistent, if you want to ensure it did save, you can place something in rd, and check that it shifts over, as the addition of a header would do, or check that the edit you did to make it invalid is gone, meaning the blocks in that area are a new header, and not an old one.
Spawn Angle Manipulation
The spawn angle, is the angle that you were facing when you last saved your location with enter, or 0 if newly launched, only left/right facing (yaw) is saved, up/down (pitch) is reset to flat every respawn/reload. This angle is stored as a 32 bit floating point number representing degrees, with turning right being positive, and turning left being negative. For every pixel your cursor moves on your screen, this angle is incremented/decremented by 0.15, or the appropriate multiple if you move your cursor multiple pixels in 1 tick.
This in theory leaves these bytes very limited, and very difficult to manipulate, however float rounding errors can be heavily abused. As 0.15 cannot be represented perfectly as a float, it is actually changed by 0.1500000059604644775390625 this means that a much wider range of values are achievable, as by adding this over and over, the imprecision adds up. The way facing angle is updated with multiple pixel turns creates more possibilities, however this is very difficult to do manually, and so is not going to be considered here.
A simple brute force tool that adds this value over and over, comparing the resulting bytes to the desired bytes, can be used to determine what turn will get what you want to perform, if such a turn exists. Then by lowering your mouse sensitivity and dpi, until you can turn at a very slow pace (at maximum 3 degrees per second), you can turn to the desired angle.
It's recommended to edit your save to match this angle ahead of time, so you can find a visual setup for it, comparing your cursor position to pixels on logs and leaves works well, and then by replicating this setup, and turning slowly until it matches, you can get your desired angle without needing to count each pixel turned.
This technique is particularly useful, as it is 4 consecutive, semi-manipulable bytes, and since it comes immediately after the world's height value, this can also be included.
Height Packing
The world's height (Z size), can be manipulated in many ways, as mentioned throughout this page, and it comes immediately before the spawn facing angle, which is manipulable, as mentioned in the section above. This makes a series of 5/6 consecutive bytes that are semi-manipulable, making it the perfect candidate for truly custom world sizes. Height packing then is the process of manipulating the world size, and then using this to do further manipulation, "carrying over" a byte from a previous manipulation.
Care should still be taken in manipulating the width (X size), and depth (Y size), in the setup manipulation. Though these are not actually used in the header pack itself, having these values multiply to more than (256*256*64)-header_size would cause the world to not load correctly, and having them be significantly smaller will cause you to lose blocks.
Since this also represents one of the only situations where you have to edit a world that already has a custom size, it's worth noting that this will cause the footer to be in-bounds of the rd-132211 world. This footer contains the entity list, username, and world name, this is going to contain many invalid block IDs, including ones over 80 and since they're at the top of the world, they will be exposed to skylight. This means any header packing done on this world will fail, unless these values are covered/overwritten. If you set your username to all 01 bytes, this will be very easy to see, as it will be a large "platform" of stone in the sky, so it's recommended you do this.
Spawn Position Manipulation
While spawn position can be manipulated, and this may be useful, as it represents one of the easiest ways to get a very specific byte. However these values are not stored in ways and positions that are particularly useful for multi-byte manipulation.
Each axis is a 4 byte integer, but the top 2 bytes are near-impossible to set for X and Z, as you can't make the world this long with most edits, and impractical to set for Y, as while you can jump immediately after a respawn to gain infinite height, this is hard to count, and tedious. In addition these values are all stored back to back, at the very end, immediately before a definition section which is fixed, meaning they can't be combined with any other manipulated fields.
While a custom sized world can be used to get any 2 byte sequence in theory, this requires a world size that is likely not achievable, limiting your options, and would take multiple hours of walking if it is possible. This is on top of the initial walk to the corner to determine what your position actually is without checking the file, as it will be randomised, if not explicitly edited.
This all means that this is a very useful technique if you want to manipulate 1 specific byte, to a value that can't be achieved another way, but is not practical for most other edits.
Uses
World Name
As the world name is the first field, you have no control over where it starts, and you have next-to-no control over the bytes at the start of the header with any technique, however with longer world names, you can do something akin to blackout poetry by replacing all the characters you don't want with null (air), which isn't usually rendered.
Creator Name
The creator name is similar to world name, except you have slightly more control over where it starts (based on manipulating the length of the world name), but generally, you still won't be able to spell anything meaningful as your creator name this way. We have no way of knowing what username restrictions were in place at the time this version released, so while it's probably possible to obtain invalid creator names this way, it's technically possible that all of these usernames were possible at the time too. And several launchers allow you to specify any username you like when playing in singleplayer, allowing you to achieve the same thing.
Created Time
Almost any time can be set by this, dates between the release of the game, and the present are just valid dates that could exist in a save file, dates in the future are temporary, as eventually we'll reach them, but so are dates in the past once it overflows, however 7F FF FF FF FF FF FF FF, right before the overflow, is ~292 million years in the future, and it will take another ~292 million years to wrap back around, so for all intents and purposes, dates before the version's release are invalid dates, and dates other than 1970-01-01 00:00:00.000 are unique to this method.
World Size
World size is 6 consecutive bytes, 2 for each of width, height, and depth (xzy), however the world size needs to be smaller than (256*256*64) - header size as otherwise the world will try to read more blocks than exist, and crash. This means in practice you don't actually need to manipulate all 6 bytes, since even if you could, it likely wouldn't be useful, for the most part, only the low order byte of each dimension is useful, as 01 FF (the highest achievable without control over the upper byte), is already 511, which is quite large already, considering how much this requires the other 2 dimensions to get smaller, and even if a size larger than 511 was wanted, even on 2 axes, that's still only 5 out of the 6 bytes needed.
Unobtainable Blocks
You can obtain bedrock simply by finding a 07 byte, and modifying the header so that this is treated as a block, this is fairly trivial actually using a c0.0.13 header with "unknown" creator name, and overwriting the entire header except the length of this string. The same can be applied to water, lava, and grass, all of which are normally mutually exclusive with the "unknown" creator name, and "--" world name.