Internals¶
The overlay system¶
The game is divided into separate modules called overlays. Overlays (OVL in short) are chunk of compiled code that are loaded into a specific address based on different situations in the game. This allows to fit more content in the game.
Memory mapping (Alucard)¶
Address
End addr.
File name
Description
80010000
8009FFFF
main.exe
Hardware API, bootloader, shared memory
800A0000
8013BFFF
DRA.BIN
8013C000
8016FFFF
BIN/ARC_F.BIN
Alucard spritesheet
80170000
80179FFF
SERVANT/TT_*.BIN
Familiar program
8017A000
8017CFFF
BIN/WEAPON0.BIN
Right-hand equip program
8017D000
8017FFFF
BIN/WEAPON1.BIN
Left-hand equip program
80180000
801DFFFF
ST/*/*.BIN
Stages program
801E0000
801E8000
F_MAP.BIN
Castle map graphics
801F0000
801F7800
Heap
801F????
801FFFF0
Stack
80280000
80??????
Used to load debug assets
Memory mapping (Richter)¶
Address
End addr.
File name
Description
80010000
8009FFFF
main.exe
Hardware API, bootloader, shared memory
800A0000
8013BFFF
DRA.BIN
8013C000
8017FFFF
BIN/RIC.BIN
Richter program
80180000
801DFFFF
ST/*/*.BIN
Stages program
801E0000
801E8000
F_MAP.BIN
Castle map graphics
801F0000
801F7800
Heap
801F????
801FFFF0
Stack
main.exe¶
Loaded by the PlayStation 1 BIOS. On the game disk the name is SLUS_000.67 and it is described by the boot configuration file SYSTEM.CNF. In the absence of a configuration file, this executable can be named as PSX.EXE. The name main.exe is after its counterpart found in the PSP version.
It contains all the hardware API (eg. gamepad, CD, memory card, GPU renderer) of the PlayStation 1 console from the PSY-Q SDK. It does not contain any game logic. The entry point main is just responsible to load DRA.BIN and F_MAP.BIN from the disk and execute the entry point of DRA.BIN immediately.
This executable is also responsible of statically allocate a shared portion of memory that all the overlays can access to. One typical example is the GameApi structure. Overlays never communicate between one another with the exception of this portion of memory.
Game engine¶
Named as DRA.BIN. It contains all the business logic such as the game loop, the game API, logic to draw maps, load stages, render the graphics, handles the menu, defines monsters, equipment and much more. There is a state-machine that decides what the game engine is supposed to do (eg. load a new stage, spawn Alucard entity and move him across the stage, pause the game into the menu, play FMV). It is the most critical file of the game.
Stages¶
Contains all the unique logic to handle map specific events, cut-scenes, enemy AI, collisions and more. It also contains the rooms and entities layout. Each overlay can be considered as its own mini-game. The title screen SEL.BIN is an example of how a stage overlay can act very differently. It exposes a small portion of endpoints into the game API including the room location list or the sprite definitions.
The following is a list of the many stage overlays found in the binary of the game:
ID
PSX
PSP
Description
00
NO0
STP00
Marble Gallery
01
NO1
STP01
Outer Wall
02
LIB
STP02
Long Library
03
CAT
STP03
Catacombs
04
NO2
STP04
Olrox's Quarters
05
CHI
STP05
Abandoned Mine
06
DAI
STP06
Royal Chapel
07
NP3
STP07
Castle Entrance
08
CEN
STP08
Castle Center
09
NO4
STP09
Underground Caverns
0A
ARE
STP10
Colosseum
0B
TOP
STP11
Castle Keep (Hallway Entrance)
0C
NZ0
STP12
Alchemy Laboratory
0D
NZ1
STP13
Clock Tower
0E
WRP
STP14
Warp Room
0F
NO1
STP15
10
NO0
STP16
11
NO1
STP17
12
DRE
STP18
Nightmare
13
NZ0
STP19
14
NZ1
STP20
15
LIB
STP21
16
BO7
STP22
Cerberos (Boss)
17
MAR
STP23
Maria cutscene (clock room)
18
BO6
STP24
Richter (Boss)
19
BO5
STP25
Hippogryph (Boss)
1A
BO4
STP26
Doppleganger10 (Boss)
1B
BO3
STP27
Scylla (Boss)
1C
BO2
STP28
Minotaurus / Werewolf (Boss)
1D
BO1
STP29
Granfaloon (Boss)
1E
BO0
STP30
Olrox (Boss)
1F
ST0
STP31
Final Stage: Bloodlines
20
RNO0
STP32
Black Marble Gallery
21
RNO1
STP33
Reverse Outer Wall
22
RLIB
STP34
Forbidden Library
23
RCAT
STP35
Floating Catacombs
24
RNO2
STP36
Death Wing's Lair
25
RCHI
STP37
Cave (Reverse Abandoned Mine)
26
RDAI
STP38
Anti-Chapel
27
RNO3
STP39
Reverse Castle Entrance
28
RCEN
STP40
Reverse Castle Center (Shaft Boss Fight)
29
RNO4
STP41
Reverse Caverns
2A
RARE
STP42
Reverse Colosseum
2B
RTOP
STP43
Reverse Castle Keep (Hallway Entrance)
2C
RNZ0
STP44
Necromancy Laboratory
2D
RNZ1
STP45
Reverse Clock Tower
2E
RWRP
STP46
Reverse Warp Room
2F
NO1
STP47
30
NO1
STP48
31
NO1
STP49
32
NO1
STP50
33
NO1
STP51
34
NO1
STP52
35
RNZ1
STP53
36
RBO8
STP54
Galamoth (Boss)
37
RBO7
STP55
Akmodan II (Boss)
38
RBO6
STP56
Dracula (Boss)
39
RBO5
STP57
Doppleganger40 (Boss)
3A
RBO4
STP58
The Creature (Boss)
3B
RBO3
STP59
Medusa (Boss)
3C
RBO2
STP60
Death (Boss)
3D
RBO1
STP61
Beezelbub (Boss)
3E
RBO0
STP62
Fake Trevor / Fake Grant / Fake Sypha (Boss)
3F
NO1
STP63
40
MAD
STP64
Debug Room
41
NO3
STP65
Castle Entrance (Intro / Death Cutscene)
42
DAI
STP66
43
LIB
STP67
44
NO1
STP68
45
SEL
STP69
Title Screen / Main Menu
46
TE1
STP70
Test Room 1
47
TE2
STP71
Test Room 2
48
TE3
STP72
Test Room 3
49
TE4
STP73
Test Room 4
4A
TE5
STP74
Test Room 5
4B
TOP
STP75
4C
TE2
STP76
4D
TE2
STP77
4E
TE2
STP78
4F
TE2
STP79
Please note, at the time of writing, not all stage overlays are extracted and available for decompilation. This was a deliberate decision made in an effort to keep the scope of the project manageable during the beginning phase. As the project progresses, more overlays will likely be extracted and made available for decompilation.
Game API¶
Exposes endpoints from different overlays in a shared memory area, so they can communicate each other indirectly.
File handling¶
The game has three ways to load the game files.
LBA system¶
This is the main way the game loads files. To greatly improve loading times, files are accessed by directly specifying to the CD-ROM system the absolute position of the file to load in the disk. This saves time as the disk head would not need to fetch the file location and size from the main table searching by the name. The location is a 32-bit integer that represents the sector index of where the file is physically located on the disk. The size of a sector is physically 2352 bytes long, but only 2048 of them are used to store the actual game data (aside from the XA tracks and the STR videos).
There are mainly two tables in DRA.BIN that stores all the game data. One is at 800A3C40 which stores every stage offsets and size. The other table is at 800ACC74 and it is used to load specific files in different part of the memory based on their type. The stage ST/SEL/SEL.BIN contains a duplicate table of 800A3C40, probably to have some kind of stage select built in.
As the location of the files is hard-coded, their position has to be known before building the actual game. It is believed the PSY-Q SDK has tools to offer this specific functionality. In alternative the tool sotn-disk can be used to patch those binaries after the image is being built.
PSY-Q open¶
The classic way of opening files with the UNIX function open. This is only used by main.exe to bootstrap DRA.BIN and F_MAP.BIN. It is known to be slower than using an LBA table as it has to move the head at the beginning of the disk, look for the Primary Volume Descriptor offset and walk through the directories to resolve the chosen sector offset and file length.
Simulator¶
Used during the development. The file path prefix is sim: (eg. sim:c:\bin\f_title1.bin). The PlayStation 1 offers drivers for the file I/O. Most likely the developers registered the driver sim to allow the PlayStation to load overlays on the fly from a physical hard drive to quickly iterate between new builds. The main.exe initially attempts to load DRA.BIN and DRA.BIO (formal name of F_MAP.BIN) from the simulator. If it succeeded it sets the flag at 800978AC to a value different than 0, effectively indicating the game needs to use the simulator instead of the LBA for all the following files.
Graphics rendering¶
This is a write-up to break down how the game renders graphics on screen. In short the game has a primitive list that gets consolidated into a drawing scene every frame. When graphics contains sprites or textures the resources are loaded from the VRAM.
Primitive list¶
TODO
Drawing scene¶
TODO
Sprite banks¶
TODO: anim set, anim frame, clut index, tpage.
VRAM¶

This write-up focuses on how the Video RAM (VRAM) is managed by a stage. The VRAM can be divided into four quadrants, each measuring 512x256 pixels.
When working with graphics that have a 4 bits per pixel (4bpp) format, a Color Look-Up Table (CLUT) is utilized. In the following examples, a grey CLUT will be used as a reference.
To transfer graphics from the main memory to the VRAM, a DMA operation is performed using the PSY-Q SDK function LoadImage(RECT*, u_long*). The first parameter RECT specifies the location in the VRAM where the graphics will be placed. The second parameter u_long* is a pointer to the main memory location from which the graphics will be loaded. It is important to note that the coordinates and size mentioned in the RECT parameter are based on a 16-bit image. For example, to load a 256x256 4bpp texture, the RECT values will be set as w=64 and h=256.
Rendering targets¶
The upper-left quadrant of the VRAM serves as a double buffer and acts as the render target for creating the final picture. Utilizing a double buffer is crucial to prevent tearing artifacts in the displayed image. While one buffer is used to transfer its content to the video output, the other buffer can be populated to prepare for the next frame. The game employs two buffer descriptors, namely GpuBuffer g_GpuBuffers[2] and the variable g_CurrentBuffer to keep track of the currently active buffer for rendering the scene. When the frame is ready to be displayed on the screen, g_CurrentBuffer = g_CurrentBuffer->next is called to flip the buffers, ensuring that the rendering occurs on the appropriate buffer. During the rendering of stages, each buffer is represented as two 256x240 targets one next to the other. These buffers are stored in the RGBA5551 format, where each pixel is represented by a 16-bit value.
To achieve effects like the photo burning at the end of the prologue, the game creates a duplicate copy of one of the buffers and uses it as a texture. This technique is commonly used in PlayStation 1 games and subsequent game platforms.
Stage graphics¶

The second quadrant in the upper-right corner of the VRAM is dedicated to storing stage graphics. It specifically contains everything related to a given stage, including the tileset and decorative entities (e.g. the moving trees in parallax outside the castle or the clock tower in the prologue). Stage graphics are loaded from ST/{stage}/F_{stage}.BIN which is always a 256KB file consisting of 32 images of 128x128 pixels and a 4bpp format. Typically the graphics at the far left is used for the tile map and the one at the far right is for the entities.
The first few 256x256 tpages of the stage graphics are 256x240 of the actual tiles and the bottom 16x256 is utilised as a palette collection in the RGBA5551 format. All the stage graphics exclusively use the CLUT at that location to render the tile map. As a result the last row of the tileset is reserved for the palette, often resulting to either uncomprehensive graphics or left blank.
The tiles within the tileset are referenced by an index, where each tile occupies a space of 16x16 pixels. Tile 0 is always empty and it is never rendered. Tile 1 is located at (16, 0) while the tile 15 is is at (240, 0). Following this pattern, tile 16 can be located at (0, 16). It is worth noting that tile maps are crafted in a way where tiles from 240 to 255 are never used, as they contain the CLUT for the stage. Typically, after tile 239, the next tile would be tile 256, positioned at (256, 0).
DRA entities¶

The fourth quadrant at the bottom-right is dedicated to the game engine DRA.BIN. It stores the graphics that are shared across stages and can be reused without the need to load them every time. This is true even if the shared entities code (e.g. the candle or the item prize) is store inside each stage overlay and not in DRA. For Alucard, these graphics are loaded from BIN/F_GAME.BIN, while for Richter, they are loaded from BIN/F_GAME2.BIN. This section of VRAM contains a series of 32 images, each with a size of 128x128 pixels and a 4bpp format.
The file from which these graphics are loaded is 264KB in size. The last 8KB of the file is allocated for the CLUT, which is loaded at the very bottom of the first quadrant, just below the two rendering targets.
Dynamic entities¶

This last section is still work-in-progress. It is currently unknown how the game dynamically loads this graphics and where it comes from. It seems to be dynamically loaded from memory based on the room where the player is into.
Beta content¶
ST0¶
TODO
MAD¶
The debug room overlay ST/MAD.BIN was compiled earlier than the first retail release of the game. All the offsets that refers to DRA.BIN points to invalid portions of data or to the wrong API calls, effectively breaking the majority of its original functionalities. That is why the debug room does not contain any object. By compiling the debug room with make mad_fix you can restore it by redirecting the old pointers to the retail version of the game.
The factory system¶
Many entities in the game spawn other entities in a roundabout, indirect manner. For example, let's consider the function DoGravityJump, which is used when the player activates their gravity boots. It accelerates the player rapidly into the air, but it also creates a factory, using this syntax:
CreateEntFactoryFromEntity(g_CurrentEntity, FACTORY(0, 2), 0);
This will create a factory. The factory is a short-lived entity which exists solely for the purpose of spawning another entity. We pass it a basis entity (in this case g_CurrentEntity) which will act as the parent of our new entity. By default, our new entity gets spawned into the same location as the parent.
The FACTORY() macro is important. It controls the behavior of the factory to determine what child is made, and any parameters related to that.
Factory Blueprints¶
When a factory is created, it has the purpose of spawning a child. It builds that child (hence being called a factory), and does so based on a set of parameters. We call these parameters the "blueprint", the design for building the child. In this case, the gravity jump is creating a factory to use blueprint 2.
To see what blueprints are doing, we can look to assets/dra/factory_blueprint.json (note: Richter has his own set of blueprints, so any functions in RIC should look to assets/ric for those blueprints). We can then look at blueprint 2, as the blueprint with ID of 2. That is:
{
"id": 2,
"id_hex": "2",
"ram_addr": "800AD1E0",
"childId": 3,
"unk1": 1,
"unk2": {
"bit7": true,
"bit6": true,
"bottom6": 1
},
"unk3": 0,
"unk4": {
"topHalf": 2,
"bottomHalf": 0
},
"unk5": 0
},
Everything from childId down is controlling how the factory behaves. Some factories will spawn multiple child entities, for example. A lot of these fields are not understood, and should be researched. Look to EntityEntFactory's first block of lines to see how the data is loaded into the fields of the factory.
The most important element of the blueprint is the childId, which tells us what child is made. In this case, the child is entity #3.
Child entities¶
When looking at these child entities, they are numbered and are pulled from a few different tables. Factories in the main game and overlays will pull from g_DraEntityTbl (and in Richter's code, g_RicEntityTbl). In this case, we will look at the #3 entity (counting from 0 of course) in the DRA entity table, and we find EntityGravityBootBeam. Lines right up! This factory will create the blue and white beam from Alucard's feet when gravity jumping.
Weapons are different. You will usually see weapon calls like
g_api.CreateEntFactoryFromEntity(self, ((g_HandId + 1) << 0xE) + FACTORY(0x100, 88), 0);
The use of g_HandId is crucial for knowing this is a weapon call. You can look to func_8011A4D0 to understand how entity update functions are assigned to new entities. Once we identify this as a weapon call, we can look to the blueprints. We have blueprint 88, which says it has child 9 (again, in assets/dra/factory_blueprint.json). But this is not the 9th entity in g_DraEntityTbl! This is the 9th element in the Weapon struct in include/weapon.h. We can count these down and see func_ptr_80170024. This factory call will create an entity whose update function is func_ptr_80170024.
This process of identifying what a factory does can be a little confusing. Anywhere in the code which uses a CreateEntFactoryFromEntity call should ideally have a comment saying what entity is being made from the chosen blueprint.
It would also be nice if we had a macro for something like WPN_FACTORY() which would bake in the g_HandId logic, which is somewhat bulky.