So far, we’ve only really touched on some of the fundamentals of reverse engineering with one game example, but this time we’re looking at something a bit different.

Shenmue (1999) is widely known for its pursuit of realism and has remained an influential series to this day, with its third game releasing in 2019. This pursuit of realism wasn’t unintentional, but one of the key design choices they made early on in development. With 2 cult classic hits on the SEGA Dreamcast, in this post, we’ll look at some code in the game which reveals a mystery and how reverse engineering can be used to understand exactly how the game attempts to capture the essence of realism, and maybe even the original developers intentions..

Logic

Generally speaking, the concepts behind reverse engineering actual logic remains roughly the same. We have to reason with the code we see and try to apply that reasoning to the code. Machines don’t execute C code, they read and execute machine code, of which was generated during the lossy process of compilation itself. This creates something of an interesting place for reverse engineers and interested developers alike, as the skill of being able to reverse engineer some code, or data, even a bug, ultimately builds your skillset as a developer and thus can lead you into different career paths. An obvious example is security: malware reverse engineering and other areas of the cybersecurity field have an inherent desire to be able to identify exactly what a piece of code is doing.

Functions are transformed from their C source code into “machine code”, which is the assembled form of assembly instructions. This process, of C source code being transformed into assembly and then assembled into machine code, is a lossy process inherently. Thus, while attempting to reverse engineer logic, we often find that the underlying code has been slightly altered from its original form, in an effort to make the code more performant.

With this in mind, depending on the original source code, the compiler and toolchain being used, and some other factors, it can be challenging to truly understand the developers original intentions, especially since all variable, function names and comments are usually always removed as part of compilation and we must recreate these as much as we can ourselves. This is the most complex form of reverse engineering and a lot of time, effort and patience are what pay off the most.

The Mystery

In real life, the Earth’s axial tilt plays a crucial role in determining the suns position at any given time of the year.

Credits to Wikipedia for this!

In Shenmue, simulating everything would be a tall order, but with this they can certainly get most of the way there. In the game, there is a heavy emphasis on keeping to a schedule and the minutae of daily life, and this is evidenced in the below logic, which we’ll dissect.

This is the result of many hours of reverse engineering sessions and discovered a while ago, we noticed something quite interesting. For the purposes of demonstration, I’ve omitted certain things, this will become clear later on in the post.

As stated before, the game is heavily based around daily life and as such, I discovered that this code was responsible for calculating the suns position and direction, for the purposes of lighting the scene. Reversing is inherently an iterative process — as you progress, your understandings may change about a previous function or some piece of data — and as such the screenshot above shows what my understanding was at the time.

This line is converting the current time into a rotation, we know this because DEG_TO_RAD, in this case, is ฯ€ over 180: a common method for converting degrees into radians. But what is 12 and -15?

Moving onto the next line, we essentially have more confirmation that this value is a rotation value, as it is passed into sin, after being subtracted by HALF_PI. We progress onto the next line, which is where the meat of the calculation takes place.

This line condenses quite a bit of logic and we get more confirmation that this is a rotation being built up, through the heavy use of ฯ€ and moving in and out of degrees. There’s something peculiar here, though.

The magic numbers so far have mostly made sense, especially given their use with sin. But now we have 365.0 to add to our mystery number list, and an array:

This array is being accessed directly by the current month and, upon closer inspection, we can also see 365 at the end of the array and it seems that this array is in fact an array that maps the accumulated days within a year for each month. There are 12 numbers, each one representing the total number of days that have passed for that month. We’ll rename this array to g_accum_days to reflect our current research.

Now we’ve gained a little bit of a clearer picture as to what is happening here, part of the expression from the last line of code is calculating how far along the game currently is into the year:

g_accum_days[month-1] - day / 365

in which it is then converted into a rotation with the help of a couple more magic numbers.

Just like in previous posts, at this point, a new theory was emerging: it seems as though the game is performing a calculation that derives the suns position based on the games current progression into the year, by approximating its axial tilt, roughly 23.4ยฐ.

The Second Iteration

It’s just a theory so far, and we’ve only looked at Shenmue, but AM2 also released Shenmue II shortly after and, given that both games share the same philosophies, perhaps if we delve into its implementation, we can spot some differences and hopefully clear up the remaining mysteries behind the rest of the magic numbers.

Before looking at its actual code implementation, we confirm and rename the day accumulation array. It’s exactly the same as the first game, but how it is being used shows something else:

This line shows an extra statement within the calculation from the first game. Now, instead of 202, the game is now directly referencing the 5th element of the day accumulation array. Checking over the value of the 5th element gives us 181. This could have resulted in a slightly off approximation of the summer solstice, which occurs around day ~172 within the year. However, it is more than likely that the code was refactored to directly reference this within the day accumulation array.

Initially we thought that this could be a bug, but despite the omission of 202, the 2 implementations remain somewhat equivalent…

Some of the changes we see here are likely part compiler optimisations and the code being changed.

First, the game is essentially converting time into a rotation, where noon (12pm) is directly above and each hour represents 15 degrees, known as the solar time angle. The game then approximates the earths axial tilt, with a given latitude tiltAngle, based on the Earth’s tilt, 23.5ยฐ.

This step essentially allows the game to simulate the sun’s position as realistically as possible based on the current date and time.. and a given latitude.

Findings

To wrap all of this up, we’ve discovered that the game is correctly approximating the suns position in a way which aligns with the design goals and philosophies of one of gamings most beloved franchises. But up until this point, I’ve refrained from showing one key variable: tiltAngle

In order to perform this calculation, you need the latitude, and since Shenmue is based in Yokosuka, Japan, it would make sense that the latitude used for this calculation would be 35ยฐ, and Shenmue II as 22ยฐ, just as it is in real-life.

However, in this case, it appears as though this could be quite literally reversed (pun intended):

Shenmue I

Shenmue II

Ultimately, it is impossible to tell without clarification from the developer in this instance, but the intent here is to show how reverse engineering code is an iterative process and your understanding of the original logic is always changing the more you look into the details.

I hope you found this post informative and was as interesting to read as it was for me to dedicate large amounts of time reverse engineering these games. If you’ve never checked them out, I strongly suggest doing so.

Full code will be made available in the not too distant future.