Replacing textures in SADX

Texture edits are the easiest mods to make. They don’t require custom code, so unless you’re adding new textures or replacing entire levels you don’t need to compile a DLL in Visual Studio. There are a few caveats and common issues that can complicate the task, however. Let’s discuss SADX textures in general and then focus on making a simple texture swap mod.

Prerequisites

To add or change textures in SADX you’ll need the following tools:

  • SA Tools, specifically PVMEditSharp and ArchiveTool
  • An image editor of your choice
  • (Optional) oxipng tool to optimize PNG textures
  • (Optional) PVMX tool to put folder texture packs into PVMX containers
  • You need to know where the textures you want to replace are located. I’m currently working on a spreadsheet that lists all PVM and PVR files in SADX. In the meantime you can use this page on Sonic Retro Wiki to find which PVM/PVR files to replace.

If you want to work with textures in the PVR format, you’ll also need the following:

Understanding textures

Before working with textures in SADX it’s important to understand what defines a texture. There are several texture characteristics that are common to textures in all games, and some that are specific to SADX. There are also several format quirks specific to SADX.

Each texture in SADX is defined by the following:

  • Dimensions: this is the horizontal and vertical size of the texture, like 128×128 or 2048×2048.
  • Pixel format, which can be one of the following for PVR textures:
    RGB565 – an opaque texture with 5 bits for red and blue and 6 bits for green.
    ARGB1555 – a transparent texture with 5 bits for each color and 1 bit for transparency (so each pixel can be either fully transparent or fully opaque)
    ARGB4444 – a transparent texture that uses 4 bits for each color and transparency.
    All of the above pixel formats are lossy, which means the original texture loses some color information when saved as PVR. RGB565 is the best format for opaque PVR textures, ARGB1555 works best for textures with basic transparency, and ARGB4444 should be used for textures that need partial transparency.
    The above limitations are irrelevant when using PNG textures. PNG textures are ARGB8888, storing 8 bits for each color and transparency, which is effectively lossless.
  • Data format: This applies only to PVR textures. A rectangular PVR texture uses a different format than a square PVR texture, there are also various VQ (compressed) formats and mipmap variations of the above. Unless you’re modding the Dreamcast version, you probably shouldn’t worry about this.
  • Mipmaps: a PVR texture may include smaller copies of itself to display from a distance. This helps achieve a smoother look even without texture filtering. Dreamcast and Gamecube versions of the game had mipmapped textures, however in the PC version and all subsequent ports the mipmaps were removed, even though the game still supports them. The Mod Loader has an option to generate mipmaps while the game is running, so this is no longer a problem.

In addition, each texture in SADX has a Global Index value, which is important so we’ll discuss it in a separate section.

Texture caching and Global Indices

Global Index (GBIX) is a value used in the game’s texture caching system. Each texture in SADX has an index that is a number using anywhere between 1 and 10 digits. For example, part of the “press start” texture in AVA_GTITLE0_E.PVM has a global index of 3489661283, while chair2.pvr in ADVSS00.PVM uses a global index of 13. In PVR textures the GBIX value is embedded in the texture file. When using PNG textures the GBIX value is loaded from the texture pack’s index.txt.

Before the game loads a new texture, it compares the new texture’s global index value with indices of other textures that are already loaded. If a match is found, the game reuses the already loaded texture instead of loading the new one, so the developers could put the same texture into multiple PVMs without making the game reload it between levels. This helped minimize memory usage in the Dreamcast days, but today the global index system is more of a nuisance than anything else. To make sure your textures load correctly you must double check all your textures for GBIX conflicts.

The game has a lot of textures that share the same GBIX value. Sometimes that can cause your custom textures to load incorrectly, or override textures in unwanted areas. You can check this list by MainMemory to find textures that use the same GBIX values. If you’re replacing a texture that has the same GBIX as textures in other PVMs, it might be a good idea to give it a unique GBIX value that isn’t used by anything else in the game. If you want to take advantage of this system in your mod, give the same GBIX value to each series of identical textures, and the game will load the identical textures only once.

Understanding texture lists and texture order

When working with PVM archives and texture packs in SADX you have to be extremely careful about the order of your textures in the PVM/texture pack. This is because of the way the game’s texturing system works. When the game loads a PVM or a texture pack, it associates the loaded textures with a structure known as texture list (texlist). Before displaying a texture or rendering a model, the game’s code specifies a texlist, which also determines which of the loaded PVMs/texture packs the game should pull the textures from. The game then uses texture IDs to determine which texture to display. For example, a texture ID of 0 will load the first texture in the PVM. Texture IDs are usually specified in a model’s material, but sometimes the game can override material texture IDs (for example, when using animated textures). GUI textures are rendered using function calls that have the texture ID as one of the arguments. So if you replace some textures in a PVM, the replacement PVM/texture pack must have exactly the same order of textures. Texture filenames are not important, the only thing that matters is the order.
Note: If you do need to change the order of textures in your texture pack, you can do so by rearranging lines in index.txt.

The most important part of a texture list is the number of textures. If the game attempts to set a texture ID that is higher than the number of textures in the texlist, it will crash. This is why it’s important to make sure your replacement PVM/texture pack has the same number of textures as the original PVM. If you’re only replacing existing textures without adding new ones, just matching the texture order and number of textures is enough.

Sometimes (for example when you want to replace a level) you may need to use more textures than the original PVM provides. Adding new textures to the texture pack is easy (just append a list of new texture filenames and global indices at the bottom of index.txt), but the number of textures in the texlist is pre-determined in code, so unless the texlist is resized too the game will crash. Recent versions of the Mod Loader attempt to resize texlists automatically when a higher texture number in the PVM/texture pack is detected, but you can also resize the texture list manually in your C++ mod using this function call:

ResizeTextureList(&texlist, number);

Where &texlist is a pointer to an NJS_TEXLIST structure and number is the new number of textures.

Texture file formats

Original SADX textures come in PVM archives, which contain textures in the PVR format. There are also a few single textures in the system folder, which are also PVR. Thanks to the Mod Loader SADX can read a variety of texture formats in addition to PVR, such as PNG, JPG, or DDS. Let’s have a look at common texture file formats:

PVR is the Dreamcast’s native texture format. PVR textures can be put into a container file with the PVM extension (which can also be compressed into PRS archives). While not bad for its time, PVR has limitations that make its usage problematic with custom and high resolution textures:

  • Pixel formats: either RGB565, ARGB1555 or ARGB4444 – all lossy.
  • Dimensions: must be a power of 2 (8×8, 16×16, 128×128 etc.), maximum size 1024×1024.

GVR is a version of the Dreamcast’s PVR format adapted for the Gamecube and Wii. It has fewer limitations than original Dreamcast PVR. GVR textures can be put into GVM archives. This texture format is used by SADX on the Gamecube, but you can’t use GVR textures with SADX Mod Loader, so I won’t be covering them. If you need to extract textures from the Gamecube version of SADX and convert them to PNG, you can use Puyo Tools to extract GVM archives.

PNG is a common image format meant for high quality graphics. This is the best format to use with custom textures.

JPG is a common image format meant for storing photos. It takes less space than PNG, but it uses compression and is almost always lossy. Normally you’d want to use PNG instead of JPG, but in some cases JPG offers advantages – for example, when you have a lot of textures where slight quality loss may be acceptable, or when your textures are made from real photos. In such cases it might be a good idea to use this format.

DDS is a file format meant for storing textures. DDS textures are the fastest to load because the GPU can load them as-is without decoding. DDS textures have many pixel formats and allow for various kinds of compression. The problem with using DDS textures is that lossless DDS textures take up a lot of space (more than PNG), and using compression can reduce the texture’s quality to PVR levels, in which case you might as well use the game’s native PVR format. You can use DDS textures with the Mod Loader when your source textures are already in this format – for example, some textures in SADX Steam are DDS.

Managing textures in SADX mods

There are two main ways to approach textures in SADX mods, which have their own pros and cons:

1) PVM archives in the mod’s system folder.

Pros: Fastest load times possible, support for built-in mipmaps, “native” texture format.
Cons: Quality degradation due to compression and format conversion, strict dimension limitations.
Use cases: When your textures are already in the PVR format – for example if you work with textures from the Dreamcast version of SA1, or from other games that use this texture format.

2) Folder/PVMX texture packs in the mod’s textures folder – not the system folder.

Pros: Allows to use lossless PNG textures, easy to use with custom high resolution textures, no dimension restrictions, possible to override the original texture’s dimensions with HD textures.
Cons: Slower load times, no built-in mipmap support (although the Mod Loader’s mipmap generation feature compensates for it).
Use cases: When you want to add custom textures or modify existing textures without worrying about quality loss.

In the majority of cases you’d want to use the second method because it works best for custom textures. The workflow will be different depending on the method, so let’s look at how both of them work.

Method 1: Using PVR textures and PVM archives

As mentioned above, this method is not recommended for custom textures (unless you’re making minor edits to the original PVR textures, in which case you might be able to keep them in the PVR format without significant quality loss*). This section will assume that you’ve got your textures in the PVR format.
*Note: The PVR textures in SADX PC have suffered from massive quality degradation. If you’re editing SADX textures, it’s better to use textures from SADX Gamecube or SA1 Dreamcast as a base. The Dreamcast version generally has the highest quality textures, although you may need to use both SADX Gamecube and SA1 Dreamcast because some textures were redesigned in SADX. When using textures from console versions of the game, be careful about texture mirroring – the PC version has several textures duplicated and/or stretched. Unless you’re replacing models as well, you’ll have to make the same adjustments to your Dreamcast/Gamecube textures. For more on textures in SA1, SADX Gamecube and SADX PC read this article.

Extracting SADX PVMs

To extract PVR textures from PVMs without format conversion, use ArchiveTool from SA Tools. Put the PVM file you want to extract into the same folder with ArchiveTool (or add the path to ArchiveTool to your PATH environment variable), open the command prompt window in the folder and run the tool like this (here we’ll be using ADVSS00.PVM as an example):

ArchiveTool ADVSS00.PVM

If your archive is PRS compressed (for example if you got it from the Dreamcast version), you can unpack the PRS right away:

ArchiveTool ADVSS00.PRS

The tool will produce a folder named ADVSS00 with PVR textures and a texture list named ADVSS00.txt. If you only want to replace existing textures, you don’t need to edit the texture list. If you want to add new textures, open ADVSS00.txt and add a list of your new textures at the bottom. Make sure the new textures are in the PVR format!

Viewing PVR textures

You can view PVR textures using the old PVRViewer tool, which can decode even the most obscure PVR formats. The tool will display the texture’s dimensions, format and global index. If you right click the texture filename you can convert it to BMP, JPEG or PNG.

Editing PVR textures

The easiest way to edit PVR textures without format conversion is to open them in Photoshop with the PVR plugin. To install the plugin, place pvrtex.8bi in Photoshop\Plug-ins\File Formats and restart the program. You can now open, edit and save PVR files in Photoshop.

Note: You could use PVMEditSharp to open a PVM file and import PNG textures directly into the PVM, but this is not recommended because PVMEditSharp will convert your texture to PVR, which may result in quality loss. To avoid quality loss, work with PVR textures directly in Photoshop.

Rebuilding the PVM archive

Go to the folder with your PVR textures (if you followed the examples above it should be the ADVSS00 folder), open the command prompt window and run ArchiveTool again:

ArchiveTool ADVSS00.txt

The tool will produce a PVM file, which you can put into the mod’s system folder. You can confirm the contents of the PVM file by opening it in PVMEditSharp.

If you need to PRS compress the PVM file (for example if you want to replace textures in the Dreamcast version) you can do it with the -prs command line switch:

ArchiveTool ADVSS00.txt -prs

Note: The PC version can’t read PRS-compressed PVMs!

Method 2: Using texture packs

This is the method to use when you want to load custom/HD textures. It’s also a much easier way to work with textures in comparison to the first method.

Exporting the original PVM

Open the PVM that you want to replace in PVMEditSharp and select File->Export Texture Pack:

Alternatively you could use PVM2TexPack, which is the command line version of PVMEditSharp’s “Export Texture Pack” option. Run it like this:

PVM2TexPack ADVSS00.PVM

A new folder containing PNG textures and index.txt will be created. You can now edit the PNG textures in an image editor of your choice, or replace them entirely.

Note 1: PVMEditSharp may crash when trying to open some PVMs, such as CON_REGULAR_E.PVM or E_SAI.PVM from the Dreamcast version. This happens because PVMEditSharp uses a library from PuyoTools, which has difficulties dealing with textures in some exotic PVR formats. To make texture packs from such PVMs, you need to do two things:
1) Use PVM2TexPack, which won’t crash, but some textures will be broken.
2) Extract the original PVM archive using ArchiveTool and convert the PVR textures to PNG using either PVR Viewer or Photoshop (see Method 1). Replace the textures in the texture pack with the ones you converted with the tool.

Note 2: The PVR textures in SADX PC have suffered from massive quality degradation. If you’re editing SADX textures, it’s better to use textures from SADX Gamecube or SA1 Dreamcast as a base. The Dreamcast version generally has the highest quality textures, although you may need to use both SADX Gamecube and SA1 Dreamcast because some textures were redesigned in SADX. When using textures from console versions of the game, be careful about texture mirroring – the PC version has several textures duplicated and/or stretched. Unless you’re replacing models as well, you’ll have to make the same adjustments to your Dreamcast/Gamecube textures. For more on textures in SA1, SADX Gamecube and SADX PC read this article.

Adding texture dimensions to index.txt

Sometimes the game expects a texture to be a certain size, and if you replace it with a higher resolution texture it will display incorrectly ingame. This becomes a problem with GUI textures in particular, which use hardcoded layouts and are scaled in different ways. To avoid this issue, you can specify the original texture’s dimensions in index.txt. For example, ast_hasi09 in ADVSS00 is 32×32 pixels, so we just add 32×32 after the texture filename in index.txt:

147,ast_hasi09.png,32x32

This step isn’t always required and generally you don’t need it for level textures, but to be absolutely safe it’s better to fill in the dimensions for all textures in the PVM you’re replacing.

Testing your texture pack

After you’re done editing the PNG textures, simply make a textures folder in your mod and put your folder texture packs there. If you’ve made a texture pack for ADVSS00.PVM, you need to have an ADVSS00 folder in sadx-test-mod\textures. To check whether the texture pack is being loaded, you can enable the debug console in SADX Mod Manager settings and go to the level that loads the PVM – in the case of ADVSS00 this would be the clock tower area in Station Square.

If your textures work as intended, you can now package them for easier redistribution.

Optimizing and packaging your textures: oxipng and PVMX

Depending on how you edited the textures it may be possible to reduce their size without quality loss. You can do that by optimizing the PNG textures with a tool called oxipng. Run it from the textures folder like this:

oxipng -r ADVSS00

This will optimize textures in the ADVSS00 folder without any quality loss.

Now you can pack your textures into a PVMX container. Like the above step with oxipng it isn’t absolutely required, but having the entire texture pack in one file can help reduce load times, and it’s also faster to download one bigger file than several smaller files when updating self-hosted mods. To pack your textures into a PVMX container, use the PVMX tool like this:

pvmx -c ADVSS00

Once you have the PVMX archives in the textures folder, you can remove the folder texture packs. You can always extract the PVMX container like this to get the original folder texture pack:

pvmx -e ADVSS00.PVMX

Replacing single PVR textures in SADX system folder

There are several textures in SADX that aren’t inside PVMs – for example, all level objectives (“Rescue Tails” etc.) and the snowboard texture are in single PVR files in the system folder. Replacing these textures is a bit more involved than replacing textures in PVMs. The two methods described above need some adaptations:

1) If you’re replacing the single PVR textures with other PVR textures, you need to put the replacement PVRs in the mod’s system folder. However, you must make sure the GBIX values match between the original and the replacement texture. If your texture has a different GBIX, you need to change it by resaving the texture in Photoshop with the PVR plugin.

2) If you’re replacing the single PVR textures with PNG/DDS/JPG textures, you need to make a special texture pack. Create index.txt in your mod’s textures folder (no subfolders!) and add a list of global indices and replacement texture filenames. The global indices must match between original PVR textures and index.txt. Here are some examples:

361009,ABC_TXT.PNG
13400000,HYOJI_EMBLEM0.PNG
13400001,HYOJI_EMBLEM1.PNG
305005,ST_064S_SCORE.PNG
90000,HYOJI_BALLS_E.PNG
400999,B32ASCII.PNG
400999,B32ASCII_J.PNG
365771,STAFFROLL_TXT.PNG
10000369,T_MISTICRUIN_E.PNG,512x256
10000370,T_EGGCARRIER_E.PNG,512x256
10000371,T_STATIONSQUARE_E.PNG,512x256

You can also use a near-complete index.txt from my HD GUI 2 mod.

Overriding PVM names in code

If you’re replacing entire levels, it may be a good idea to override the name of the PVM/texture pack loaded by your level in order to avoid mod conflicts. To do that, you can use the following code snippet. Add this to one of your headers or at the top of your main .cpp file:

#define ReplaceTextures(a,b) helperFunctions.ReplaceFile("system\" a ".PVM", "system\" b ".PVM");

Then add this line to the mod’s Init function:

char pathbuf[MAX_PATH];

Now you can override PVM names like this:

ReplaceTextures("ADVSS00", "ADVSS00_mymod");

Your mod MUST have PVMs or texture packs with the same name as the replacement, otherwise the game will crash. If you followed the example above, your ADVSS00 texture pack must be named ADVSS00_mymod instead. Try to come up with unique names for your texture packs to make them easier to distinguish from textures in other mods.
Note 1: Although the ReplaceTexture snippet specifies .PVM as the file extension, the Mod Loader will automatically pick up a folder/PVMX texture pack with the replacement name.
Note 2: You can edit the snippet to make it replace SET or CAM files, or any other files in SADX system folder.

Overriding names for individual PVR textures in the system folder is more complicated. You need to redirect each of the PVR textures to an index.txt in one of your mod’s subfolders. For example, the HD GUI 2 mod does it like this:

#define ReplacePNG(a) _snprintf_s(pathbuf, MAX_PATH, "%s\\textures\\pvr\\index.txt", path); helperFunctions.ReplaceFile("system\" a ".PVR", pathbuf);

And then in the Init function the textures are replaced like this:

ReplacePNG("ABC_TXT");

The above snippet overrides the individual PVR textures with an index.txt in the mod’s textures\pvr folder.
Note 1: You need to do this for each of the single PVR textures you’re replacing, and index.txt must list all of the textures to replace.
Note 2: You can name the pvr folder anything you want, just make sure the name is consistent everywhere.