# Sonic 3 Blue Spheres: Relationship between Stages and Levels numerical system?

Discussion in 'General Sonic Discussion' started by BlueSpheres, Feb 28, 2024.

1. ### BlueSpheres

Member
9
0
1
[Hoping to posting in the right section]

Hi everybody.

I know it's a geeky question, but I can't understand or find information about the relationship between the level and stage numerical system (as shown in the pictures, in particular in the first two rows fo the table).

Can anyone help me with this?

Thank you.

2. ### MainMemory

Kate the Wolf Tech Member
4,750
340
63
SonLVL
Uhhh... It's complicated. Essentially, each stage in Blue Sphere is made up of four individual 16x16 "chunks", of which there are 128 total. The level number simply increases as you progress through each stage, while the stage number is the numerical IDs of all the chunks that make up the stage, stored as a series of four bytes and read back as a single 32-bit big endian integer. The stage number 000016643 thus represents a stage using the chunks 00 00 41 03. made up of the values of each chunk, shifted together such that the 7 bits of each chunk ID are directly connected, resulting in a 28-bit number. The stage number 000016643 thus represents a stage using the chunks 00 01 02 03 (left to right, top to bottom). It's also worth noting that the top right chunk is flipped horizontally, the bottom left chunk is flipped vertically, and the bottom right chunk is flipped both horizontally and vertically.
As for how the two numbers relate to each other, there is a formula the game uses to determine which chunks (and thus the stage ID) each level should use, something like this (adapted from the stage select menu in S3SSEdit [which I have only just now realized is more complicated than it needs to be]):
Code (Text):
1. BSChunks[0] = (byte)(levelnum % 128);
2. BSChunks[1] = (byte)((levelnum * 3 + 1) % 127);
3. BSChunks[2] = (byte)((levelnum * 5 + 2) % 126);
4. BSChunks[3] = (byte)((levelnum * 7 + 3) % 125);
The code system is even more complicated, involving sanity checks and whether the stage is from the full game or a ROM header.
I meant to make an online viewer for this stuff but then I remembered that I hate web programming.
If you're curious, the wiki has images of all 128 chunks.

Edit: forgot a crucial detail about the stage number.

Last edited: Feb 28, 2024
• Informative x 6
• List
3. ### BlueSpheres

Member
9
0
1
Amazing.

What does the zero in the most significant position stand for?

And another question (which might be trivial). How is the color of the pattern and the diamond decided? (Looking at the wiki page you sent me, each chunk seems to have an already assigned color pattern.)

Thanks again

4. ### Kilo

That inbetween sprite from S&K's title screen Tech Member
495
522
93
S1 - Metal Sonic's Challenge, Sonic 1 Rev01 ASMX Disasm
I believe the wiki's use of color is just to differentiate the chunks, they seem to follow the same color order as the main game's special stages.
Edit: Nevermind, see MainMemory's explanation.

Last edited: Feb 29, 2024
5. ### MainMemory

Kate the Wolf Tech Member
4,750
340
63
SonLVL
I don't know which zero you're referring to, but it's a zero, it doesn't stand for anything.

The colors of the grid, sky, and emerald are determined by the lower left chunk's ID, modulo 16 (modulo 8 for the emerald). Level 1 uses chunk ID 2 in the lower left, so it uses palette 2 (the third special stage's palette, because it starts at 0).

Last edited: Mar 1, 2024
6. ### BlueSpheres

Member
9
0
1
I meant the leftmost digit of the Stage number. Since the other eight digits indicate the chunks I was wondering what the leftmost digit indicates.

A (hopefully :D) last question: why they stopped to 7F representation for the chunks? Is there a technical reason to not reach the FF (in order to have a total of 256)?

Thanks again.

7. ### muteKi

Fuck it Member
7,857
138
43
The largest possible 28-bit value (thus representing the stage with all of the last chunk ID) converts to 268,435,455 in decimal. All nine digits of the stage number (which is decimal, not hexadecimal) are used to specify the chunk IDs in the stage.

8. ### DigitalDuck

Arriving four years late. Member
5,375
466
63
Lincs, UK
TurBoa, S1RL
Incidentally that stage number can't have a corresponding level numbers, due to the way the levels are assigned. Chunk 4 can only have a maximum ID of 124, chunk 3 a maximum ID of 125, and chunk 2 a maximum ID of 126. While stages do exist that exceed these chunk limits, they don't appear in the level sequence.

I'd imagine it's simply that they decided 268 million levels was enough, and didn't feel like doing double the amount of work to expand it to 4 billion.

A couple of years ago I did a long stream where I played through every Blue Sphere level that has all four chunks the same (and then cut it down into a YouTube video). Playing it basically chunk-by-chunk helps appreciate how much thought went into some of these chunks (and how little went into others). I think if they had to make another 128 of them they'd really be cutting down on quality on the second half.

9. ### Kilo

That inbetween sprite from S&K's title screen Tech Member
495
522
93
S1 - Metal Sonic's Challenge, Sonic 1 Rev01 ASMX Disasm
If they went above \$7F then they'd have to use 8 bit numbers, rather than 7 bits. And the special stages become very easy to decode. 000016643, which uses chunks 00, 01, 02, 03, would then become 000001002003. It's also a matter of ROM space, we can make ROMs as big as we want nowadays but back then you did not need to make 256 chunks for a random bonus game generator, even 128 could be considered excessive.

10. ### MainMemory

Kate the Wolf Tech Member
4,750
340
63
SonLVL
This thread inspired me to try out web dev again, this time with Avalonia, which basically lets me just turn a desktop program into a webpage.
With that, I made an online Blue Sphere Map Generator!

And yes, I'm aware this sort of thing has been done before. Highlights of this viewer include the ability to open a ROM file, search for a game by name (using the No-Intro set), or input various ROM header fields manually, as well as the use of actual graphics for the stage preview. Eventually I'd like to add the ability to display a level number when given a stage ID (not sure if this is possible without brute force or making a table of every level's corresponding stage ID), and displaying possible ROM header info for levels that don't match any known ROM.
I could also release this as a desktop program, but I'm not sure if there's any real demand for that.

By the way, in the process of adding the "Known ROM" list, I now have the ability to list off the stage/level/code for every MD ROM officially produced (and several protos/unlicensed ROMs). Is that something I should put on the wiki, like as a subpage of Blue Sphere?

• Like x 9
• Informative x 1
• List
11. ### Black Squirrel

no reverse gear Wiki Sysop
8,748
2,628
93
Northumberland, UK
steamboat wiki
Yes

There are a lot of ROMs though, and No-intro isn't always correct. Our list might help (though there might be some holes - someone feel free to fill them in).

12. ### MainMemory

Kate the Wolf Tech Member
4,750
340
63
SonLVL
Well unless someone has a Sega Retro Romset, that list isn't going to help me much. The list I was planning would just be the ROM name, the level number, stage number, code, and a column for notes (ROMs that aren't available as carts, are too big to work on HW, or have special handling).

13. ### DigitalDuck

Arriving four years late. Member
5,375
466
63
Lincs, UK
TurBoa, S1RL
If you don't mind the tiniest amount of bruteforcing, I made a Python script a while ago to handle stage ID to level number:

Code (Text):
1.
2. def stage2level(stage):
3.     chunks = [0, 0, 0, 0]
4.     temp = stage
5.     for i in range(4):
6.         chunks[3 - i] = temp % 128
7.         temp = int(math.floor(temp / 128))
8.     return chunks2level(chunks)
9.
10. def chunks2level(chunks):
11.     if chunks[1] > 126 or chunks[2] > 125 or chunks[3] > 124:
12.         return -1
13.     if chunks[0] % 2 != chunks[2] % 2:
14.         return -1
15.     l128 = chunks[0]
16.     l127 = (chunks[1] + 126) % 127
17.     while l127 % 3 > 0:
18.         l127 += 127
19.     l127 /= 3
20.     l126 = (chunks[2] + 124) % 126
21.     while l126 % 5 > 0:
22.         l126 += 126
23.     l126 /= 5
24.     l125 = (chunks[3] + 122) % 125
25.     while l125 % 7 > 0:
26.         l125 += 125
27.     l125 /= 7
28.     level = l128
29.     while level % 127 != l127:
30.         level += 128
31.     while level % 126 != l126:
32.         level += 16256
33.     while level % 125 != l125:
34.         level += 1024128
35.     return level + 1
36.
Not the best code I've ever written, but it does the job.

Last edited: Mar 2, 2024
14. ### MainMemory

Kate the Wolf Tech Member
4,750
340
63
SonLVL
@DigitalDuck Your code seems to result in infinite loops on some stages, such as changing the lower left chunk on level 1 from 2 to 3.

I put the ROM list on the wiki, in a sortable table. Apparently the earliest accessible level is 2325, from Rings of Power. I'm not quite sure where you'd fit the link into the main Blue Sphere article...

Last edited: Mar 3, 2024
15. ### DigitalDuck

Arriving four years late. Member
5,375
466
63
Lincs, UK
TurBoa, S1RL
You're correct. The reason for this is that there is no level for [0, 1, 3, 3] - chunks 0 and 2 have to be matching parity. For the same reason, I was also adding double what I should've been in the last one so half of the levels were returning the wrong value (exactly 128016000 more than they should've been).

I've modified the code so that both of these issues have been fixed.

16. ### MainMemory

Kate the Wolf Tech Member
4,750
340
63
SonLVL
Implemented the new stage-to-level algorithm, and buttons to generate a random level/stage.

17. ### BlueSpheres

Member
9
0
1

I used the topic of this thread to frame a Computer Architecture lesson in one of my classes!

I'll be asking some more questions soon, hoping they won't be too dumb :D

18. ### BlueSpheres

Member
9
0
1
As I said, maybe I'm too stupid and I'm pretty sure I'm doing the math wrong, so I'm asking for your kindly help once again:
1. I'm still confused about the stage numerical system. I get it that it's a 9-digit decimal number, but this make me a bit fuzzy. Following MainMemory's alogrithm, with a levelnum = 1
• BSChunks[0] = 1 (in hex 0x01);
• BSChunks[1] = 4 (in hex 0x04);
• BSChunks[2] = 7 (in hex 0x07);
• BSChunks[3] = 7 (in hex 0x07);
Now, how this group of single 4 digits are distribuited on a total of 9 digit? I can't see how to relete them to 000016643 stage code. Furthermore, the chunks should be 01, 04, 07 and 07, not 00, 01, 02, 03. Same situation when I try an higher level number like 128'016'000.

2. Why are 9-digits needed to represent the 128'016'000 layouts possible?

3. Since the layouts are 128'016'000, why the games goes until 134'217'728? Why specifically 2^27 (so 27 bits)?
Thank you

Last edited: Mar 30, 2024
19. ### saxman

Oldbie Tech Member
I'm having difficulty following your questions, but let me focus on question #1. Can you explain why it should be 1, 4, 7, 7? Walk us through that, but I don't come up with that result myself.

20. ### BlueSpheres

Member
9
0
1
I've ported the algorithm descripted by MainMemory in Python 3 and these are the results (actually the last execution gave me a 10 as result of BSChunks[3], that make more sense than 7)

#### Attached Files:

• ###### Screenshot_20240330_231328_Chrome.jpg
File size:
259.4 KB
Views:
4
Last edited: Mar 30, 2024