PokeHacking

Join our Discord community

Dynamic cameras in Gen IV: How to implement and how to use them

Mikelan98, Trifindo

Target games:


Introduction

Pokémon video games for Nintendo DS are made in 3D inasmuch as 3D models are used for representing maps where the player moves. However, Gen V games take the credit of being the one that takes the best advantage in terms of playability; this is due to the use of a deeper perspective, complex 3D models and, above all, the fact that it plays with the camera in a multitude of scenarios.


Panoramic view from Aspertia City’s lookout in Pokémon Black 2 and White 2.

Practically, it can be said that, with a few exceptions, 3D is rather unexploited in Gen IV games, although it is still there and it can still be used (unlike other platforms where the use of 3D would imply a complete change of the game engine). Models and elements on the maps completely depend on the personal style of each game developer, while the default perspectives of the camera are codified in Overlay 1 and can be relatively easily modified but, what can we do to move the camera in our maps? There are many forms of implementing something like that in the ROM but, anyways, it is needed to incorporate ASM code from scratch.


Research

The first step to research how can we move the game camera in the overworld is to find the values that define the parameters of the camera within the RAM memory. To find them, the cheat utility in DeSmuME can be used to do a comparative search of values in the RAM (for instance, moving horizontally the player and searching for higher or lower values).


Cheats window report in DeSmuME. The 3 last values belong to the camera box.

If done correctly, DeSmuME should report some addresses: three of them should belong to a memory region that hereinafter will be named “camera box”. It contains all the necessary values that define the camera’s properties.


Camera box in Pokémon HeartGold’s memory.

In the camera box, the camera’s position, the target’s position -where the camera is looking at- and the camera vector can be found. These parameters are enough to generate any camera movement, although other values can be modified having different effects. The properties of each parameter are the same in Pokémon Platinum and Pokémon HeartGold/SoulSilver.

ID Parameter Increasing effect Decreasing effect
00
04
08
0C
10
14 Camera X Counterclockwise rotation around the player vertical axis Clockwise rotation around the player vertical axis
18 Camera Y Zoom out and a more cenital camera Zoom in and a less cenital camera
1C Camera Z Zoom out and a less cenital camera Zoom in and a more cenital camera
20 Target X Counterclockwise rotation around the vertical axis camera Clockwise rotation around the vertical axis camera
24 Target Y Move up the camera Move down the camera
28 Target Z Rotate the camera down towards Rotate the camera up towards
2C Camera Up Vector X Counterclockwise rotation of the screen Clockwise rotation of the screen
30 Camera Up Vector Y
34 Camera Up Vector Z
38
3C
40
44
48 Target Previous X Retarded movement of the camera to the right Retarded movement of the camera to the left
4C Target Previous Y Retarded movement of the camera upwards Retarded movement of the camera downwards
50 Target Previous Z Retarded movement of the camera to the front Retarded movement of the camera to the back

Meanwhile, it is not trivial to create functions to modify these parameters. It should also be taken in consideration that these values have to be modified gradually, frame by frame and that it is not enough to modify them by replacing for any value (that would simply teleport the camera). This needs to be taken into account when designing all the subroutines, consequently this forces us to look for any subroutine where we can link our code and that executes at least once every frame.

The first option to integrate the camera movements in the map is to use triggers and scripts to call the subroutines that will move the cameras. Although this method has the advantage of not having to add anything that already exists (except for the subroutines for modifying the camera values) and keeping every file format, there are some disadvantages. On one side, each time a trigger is entered on, the game freezes for a few frames, which can be visually undesirable. On the other side, there would be too many elements involved in the ROM (maps, events and scripts).


Camera movement executed by a trigger in Pokémon Light Platinum DS. Some freezes can be noticed at the start of the animation.

Any other option would imply the designing of a new type of format to determine the camera movements. Logically, this file should be included within the map files along with the collision files, sound plates files and terrain files. Although this is even more difficult than working with triggers, once the necessary subroutines are programmed, it would be easier to design the camera movements for each map.

To proceed with this last strategy, it is necessary to have a deep knowledge about how the map data is loaded into the RAM memory. Every section is loaded into different locations of the memory -in other words, the map file is not kept as a whole within the RAM-. If raw data from the map file is searched, the first thing that appears within the RAM is the collision file, which always has a fixed size of 0x800 bytes (2048 bytes, corresponding to 32 tiles x 32 tiles x 2 permission layers). By modifying the value in the map header corresponding to the size of the collision files, the section size is not modified or it even may crash the game, so it can be deduced that this value is ignored by the game -or it should be ignored by the ROM hacker- and that the collisions must always have a fixed size of 0x800 bytes. At the end of the section there are many pointers to different locations in the RAM memory that contains other map sections.


Pointers within the RAM memory for each section of the map file. The collisions’ section is remarked in blue.

One of these pointers is the the one to the BDHC file, at the end of which the camera file can be added (it could also be added in the sound plates files, but in that case, a different strategy for Pokémon Platinum would be needed, as it does not have this section and there wouldn’t be compatibility between both). Our format, consequently, must be designed as a plate file. Inserting the camera file as an independent section of the map file would be extremely difficult and it would force us to modify the code that reads every other section (due to how the pointers are distributed in the memory, as mentioned above).

To check the possibility of adding data at the end of the BDHC files (so this data would be found in the RAM memory after the terrain file) a map file was modified and a random sequence of bytes was added at the end of it (in other words, at the end of the BDHC section). However, the sequence of bytes wasn’t found in the memory dump of the game. Consequently, this data wasn’t being loaded into the RAM. This is because the size of the BDHC file that is going to be loaded into the RAM is defined by the header of the map file and the header of BDHC itself. It is necessary to increase the size of the BDHC in the map header as much bytes as the camera file added at the end of the BDHC has. Furthermore, in the header of the BDHC it is necessary to increase the size of Part U in [bytes of the camera file / 2], so the camera file is incorporated in Part U of BDHC. Only in this case, the data in the memory dump can be found.


BDHC section in the RAM memory, along with a marker that states that it is possible to add data at the end of the section. BDHC file is remarked in blue.

It is necessary to tell the game where the dynamic camera file begins (hereinafter named DCAM) inside the BDHC file. Also, we must insert the size of the DCAM section in order to increase that value to the size of Part U, so they can be loaded together in the RAM (since manually incrementing the size of Part U in the header of every file is quite tedious and may end up causing complications). For that purpose, the 4 bytes of the magic number of BDHC file will be used. These bytes correspond with the first four bytes of the file, that contains the string “BDHC”. The first two bytes will be reserved for the size of DCAM sub-section, while the next two bytes will be used for the relative position of DCAM within the content of the BDHC file. This would restrict the size of BDHC because it cannot exceed 65535 bytes (64 KB), although this is a hypothetical limitation, for the most complex terrain files rarely exceed 3 KB. The DCAM section cannot exceed 64 KB either, although it rarely will exceed 1KB. Also, it needs to be noted that, within the RAM, the header of the BDHC file is removed, so the relative position of the DCAM file within the BDHC has to be counted from the first byte of Part P (this is, just after the header).


BDHC file header in Platinum, HeartGold and SoulSilver. Bytes that are going to be used are remarked in blue.

With the design of the files established, it only remains to find an original subroutine in the ROM that fulfils the next requirements:

As a previous requirement, the original subroutine was sought to work with the map data pointer so it could be used directly for the subroutine. However, a method to obtain the pointer from the memory in any subroutine was later discovered reading the FIELDSYS_WORK structure, so it was no longer necessary.

The ideal would be to use the own BDHC reading subroutine in order to use a branch link (BL) to the code of DCAM reading. However, if a debugger such as IDA Pro is used and breakpoints are set in this subroutine, we can see that some requirements are not fulfilled. The subroutine that reads the collisions file does fulfil the requirements in HeartGold and SoulSilver, so initially the code was linked with it. Specifically, in the offset 0x020547F4 (IPKS) a BL was modified so it pointed to another place where the custom code was. However, when it was tested in Pokémon Platinum the subroutine was not executed in each frame, making it useless.

Finally, the subroutine that updates the position of the camera every time depending on the player’s position was chosen: some parts of this function are only executed in the overwold, fulfilling every requirement imposed. However, being a subroutine rather different to the ones initially chosen -the BDHC reading one and the one collisions reading one- makes the divmap unpredictable at the time this subroutine is executed, and it must be recalculated again. The divmap is a number that identifies in which map we are within the 4 possible maps loaded in the game memory. Thus, it can only have 4 different values (from 0 to 3) as seen in the next scheme.


Divmap assignment scheme for the 4 maps loaded in the RAM.

Tweaking glitch in Gen IV consists precisely on combining chaotic movements faster enough so the divmap can’t be updated correctly, thus data from wrong maps are read.


Tweaking glitch in Gen IV.

As the divmap loaded by the collision subroutine cannot be used, a new subroutine must be designed so it can calculate the correct divmap where we should be, depending on the position on the player. By doing this, the camera file of the map where the player is can be correctly read.

In the visual scope, it is important to know that by modifying the camera, the vertex normals of the 3D models will interact in a different way with the camera. Due to this, it is common to see some polygons, or even sprites, getting darker or clearer while the camera moves. At the moment, there is no way of solving this, because it would be extremely complex to modify the normals of a 3D model in real time.


Screenshots of the same map with different camera angles. In picture A, the map has the camera angle by default. In picture B, the polygons’ shades are a little darker, while in picture C, the sprites are darker.


Tutorial

In order to incorporate this new functionality to the ROM, it is necessary to expand the ARM9, as explained in this tutorial.

The final format designed for DCAM files is detailed below. There are 2 kinds of plates: those who perform a camera animation defined in an interval of time, and those who modify the camera depending on the position of the player inside the plate. The last ones can have horizontal or vertical dependence.


General scheme of the final design of a DCAM file.

The first thing the subroutine must do, as mentioned before, is to obtain the correct divmap so the correct DCAM file -corresponding to the map where the player is- can be readed. With that purpose, knowing the position of the player each time the routine is executed is enough for this, although some arrangements of the code are needed in those cases where the player is in the centre of the maps (because the divmap of the actual map changes in those tiles).

The next thing to do is to read correctly the DCAM file from the headers that have been modified, and increase or lower the values of the camera box. In plates that have a camera movement that depends on the position, the graduality of the movement of the camera is defined by the graduality of the own movement of the player moving through the plate. In plates that have a time-dependent animation, by contrast, graduality has to be obtained by limiting the increase of the values of the camera box in each frame. The subroutine might be executed more than once per frame, so an auxiliary subroutine that acts as a boolean function is needed to guarantee the graduality and consistency, and it needs to tell the game if at least 1 frame has passed since the last time it was executed (using the frame counter in the RTC structure for this purpose).

In addition, the subroutine requires a small space in the RAM memory to store some parameters (increments applied to each value of the camera box, angles currently applied, original values of the camera box…). This space is located at the end of the main subroutine as a space filled with 00. This region of the subroutine mustn’t be altered or be occupied by any other values, as it may end up in an unpredictable behaviour of the camera.

Below, there’s a table that states some replacements that have to be made and where. Due to its size, the DCAM reading subroutine is found as a separate file and not as a hex string.

Game Code Branch Offset Branch Code Expanded ARM9 file Dynamic Camera Subroutine Offset (relative to the expanded ARM9 file) Dynamic Camera Subroutine Code
CPUE 0x0202040C B9 F3 E2 F8 data/weather_sys/weather_sys_9.rlcn 0x000115B0 cpue_cam.bin
IPKE 0x02023174 B6 F3 2E FA a/0/2/8/0.rlcn 0x000115B0 ipke_cam.bin
IPGE 0x02023174 B6 F3 2E FA a/0/2/8/0.rlcn 0x000115B0 ipge_cam.bin
CPUS 0x0202047C B9 F3 AA F8 data/weather_sys/weather_sys_9.rlcn 0x000115B0 cpus_cam.bin
IPKS 0x02023174 B6 F3 2E FA a/0/2/8/0.rlcn 0x000115B0 ipks_cam.bin
IPGS 0x02023174 B6 F3 2E FA a/0/2/8/0.rlcn 0x000115B0 ipgs_cam.bin
IPKJ 0x02022E14 B6 F3 DE FB a/0/2/8/0.rlcn 0x000115B0 ipkj_cam.bin
IPGJ 0x02022E14 B6 F3 DE FB a/0/2/8/0.rlcn 0x000115B0 ipgj_cam.bin

It is also necessary to slightly modify the file-loading subroutines of the BDHC files so they can correctly read the DCAM segment and append it to the Part U, saving it in the memory.

Game Code Overlay File First Offset (relative to overlay file) First Code Second Offset (relative to overlay file) Second Code
CPUE Overlay 5 0x0001E1B4 00 4B 18 47 41 9C 3D 02 0x0001E2CC 00 4B 18 47 01 9C 3D 02
IPKE Overlay 1 0x0001574C 00 4B 18 47 41 9C 3D 02 0x00015864 00 4B 18 47 01 9C 3D 02
IPGE Overlay 1 0x0001574C 00 4B 18 47 41 9C 3D 02 0x00015864 00 4B 18 47 01 9C 3D 02
CPUS Overlay 5 0x0001E1BC 00 4B 18 47 41 9C 3D 02 0x0001E2D4 00 4B 18 47 01 9C 3D 02
IPKS Overlay 1 0x0001574C 00 4B 18 47 41 9C 3D 02 0x00015864 00 4B 18 47 01 9C 3D 02
IPGS Overlay 1 0x0001574C 00 4B 18 47 41 9C 3D 02 0x00015864 00 4B 18 47 01 9C 3D 02
IPKJ Overlay 1 0x000155D4 00 4B 18 47 41 9C 3D 02 0x000156EC 00 4B 18 47 01 9C 3D 02
IPGJ Overlay 1 0x000155D4 00 4B 18 47 41 9C 3D 02 0x000156EC 00 4B 18 47 01 9C 3D 02

BDHC files that have incorporated the DCAM section (these files are named BDHCam) can be made manually via hex editing, but PDSMS can generate these files, so it can be made easily and with visual references. Also, importation of BDHCam in map files can be done with SDSME or DSPRE, just like if they were common terrain files. It is not necessary to do any other modification. Original BDHC files (without the DCAM section) still work in the game and are correctly interpreted. BDHCam files are intercompatible between HeartGold/SoulSilver (IPKX/IPGX) and Platinum (CPUX).

If trying to create a BDHCam file manually with hex edition, it is recommended that the DCAM section is aligned in 4 bytes, so sometimes a 2 bytes padding (FF FF) is needed between Part U and DCAM. These 2 bytes need to be counted for the size of the DCAM section in the BDHC header.


Demanded credits

If you are planning to use this research on your project, credits for Mikelan98 and Trifindo are needed. You can cite this research by copying the following reference in the credits section of your project's thread or webpage:

Mikelan98, Trifindo: Dynamic Cameras (pokehacking.com/r/20110901)

Total or partial reproduction of this research in other site is forbidden without prior authorization of the authors.






© 2020 PokeHacking

Pokémon characters and images belong to The Pokémon Company International and Nintendo.
This website is in no way affiliated with or endorsed by Nintendo, Creatures, GAMEFREAK, The Pokémon Company or The Pokémon Company International.