Jan 27, 2025 at 4:16 AM
Been here way too long...

"Big Joe Tire and Battery Restaurant! Opening Soon! Eat at Big Joes!"
Join Date: Oct 7, 2013
Location: India
Posts: 547
This may or may not warrant its own thread but... because I'd rather do literally anything other than my homework, I've been taking a look at how Rain works, and made some notes. There is still much to be known, so if any of you smart people want to take a crack at it, that'd be cool!
I guess the most immediate result here is that you can now change the music. Enjoy!
I guess the most immediate result here is that you can now change the music. Enjoy!
most numbers seem to be stored in 4-byte, little-endian format.
what is the fixed duration between each note?
each note consists of a single 'number' (i.e. 4-byte, little-endian).
0x1B07C is where drum track data begins.
note that there's an instruction at 4035C3 that seems to reference this data, but it uses the offset 1B078. perhaps there's a 4-byte before 1B07C that I overlooked. same with other tracks (melody etc). anyways,
the first two bytes consist of the drums to be played in that note. the first byte handles these drums:
0x01 = bass dram
0x02 = snare
(0x03 = both of the above)
0x10 = hi close
0x20 = hi open
the next byte handles these drums:
0x10 = tam h
0x20 = tam l
the third byte doesn't seem to do anything.
setting the fourth byte to 0x80 indicates that the track should loop after that note. this behaviour is the same for the harmonies/bass/melodies. so the pitch data is really only three bytes.
0x1BFC4 is where harmony data begins.
there are 20 notes (semitones, A1-E2), numbered 0th to 19th. each is associated with a bit. to play a note, flip that bit to 1. add these bits together to play notes simultaneously (similar to piyopiyo). since there is only up to a 19th note (0x00080000), the most significant 3 bits are not used in practice. to play all notes at once you would use 0x000fffff.
0x1CF0C is where bass data begins.
same principle as harmonies, but here there are only 17 notes (C1-E2), so the highest (16th note) would be 0x00010000.
0x1DE54 is where melody data begins.
same principle, 15 notes (G1-A3). highest is 0x00004000
Note: what's really frustrating is how the data is so similar to piyopiyo, but you can't write music for rain in piyopiyo because of the note ranges. Piyopiyo supports a range of 24 semitones for each instrument, and it's not like rain's instruments use any more than that - but piyopiyo's range specifically ranges from Cn to B(n+2). Rain's harmonies, on the other hand, go A1 to E2. Trying to emulate this in piyopiyo, we could try an instrument with a base octave of 1, which ranges C1 to B3, but this misses out the notes A1, As1 and B1. Trying a base octave of 0 misses out all notes from C2 to E2. this is so sad and I'm starting to wonder what sort of interface Pixel used to compose the music for rain (did he just compose it in some midi like editor and manually convert it to the rain format or what)
0x1ED98 starts various data about the instruments, using little-endian 4-byte numbers. each instrument has 10 numbers (0x28 bytes in total) associated with it:
1: describes, in pixels, the horizontal displacement of the centre of an instrument from the left of the screen.
2: vertical displacement from top (this affects when the raindrop hits it and thus when it produces sound.)
3: defines the .wav to be played when this instrument is hit
4: left framerect (BMP_BOTTLE.bmp)
5: top framerect
6: right framerect
7: bottom framerect
8: something to do with hitbox size? see 403BFF
9: handles the instrument animation when hit by a raindrop. it is by default 0, corresponding to frame number 0 in the 3-sprite animation. 32xFrameNumber is added to the top/bottom framerects of the instruments when hit, see e.g. 403B9E. note that the data is not modified directly, but only after being loaded into 'dynamic memory' (see notes below)
10: seems to be related to the above?
Order of instruments: bass dram, snare, tam h, tam l, hi close, hi open, harmony notes (starts 0x1EE88), bass notes (starts 0x1F1A8)
0x1F450 onwards there is more instrument data, 3 numbers associated with each instrument:
1: horizontal displacement of raindrop stream (from left)
2: vertical displacement of beginning of raindrop stream (from ceiling)
3: for the drums this is just the id as described in the track data. for the harmony/bass this is a number that starts at 1 for the lowest note and keeps doubling with each note. not sure what it's for. (solved? see e.g. function at 403430, this data helps the game code 'pick out' the instruments it needs)
Order of instruments same as before, with harmonies at 0x1F498 and bass at 0x1F588
Note: the loop at 403DCE loads these pieces of data into 'dynamic memory', with 0x1ED98 corresponding to [4213B8], and 0x1F450 corresponding to [420828].
0x1F654 could be padding?
0x1F658, there are 15 2-number records (say X, Y) corresponding to the melody notes. Y is again something that starts at 1 and keeps doubling. X starts at 0x32 and just increments with each note until 0x40. This seems to identify the pitch of the note to be played when the bit specified in Y appears in the track data. for example, setting all note's X value to 0x32 makes all the notes in the melody sound the same (G1). Setting all Y's to 01 makes it such that whenever a G1 was supposed to be played (a 01 bit appears in the track data), all notes are played simultaneously since all of them are associated with the 01 bit (this is also why Y needs to be a power of 2).
0x1F6D0: raindrop framerects (BMP_DROP.bmp). left, top, right, bottom respectively. four sprites in the animation.
0x1F710: musical note framerects (BMP_NOTE.bmp). seven of them.
0x1F780: cat framerects (BMP_MELODY.bmp). only top row sprites (bottom row seems to be unused?) Some framerects are repeated here, presumably because of how the animation is sequenced with the sprites, but manually recreating it that way didn't work out. huh.
0x1F800: background framerect (BMP_BACK.bmp)
402156: BMP_ESCAPE framerects
0x1F810: toggles tracks. 0x01 only drums, 0x02 only harmonies, 0x04 only bass, 0x08 only melody. Flip these bits for combinations thereof. Default is 0x0F (all tracks enabled). Note that this doesn't affect manually added raindrops.
0x1F814: ?
0x3339: how far from the ceiling the raindrops are allowed to fall before disappearing (unless they hit an instrument before that). also determines the range in which we can click to create raindrops.
this seems to be part of an asm function that handles raindrops, but i can't seem to figure anything else out
0x3E85 (just one byte): messing with this makes the melody show up slightly early
0x1B04C: framerects (left, top, right, bottom) for a region within raindrops, the loading screen logo, and the fps counter will be rendered (these objects still exist, just their rendering is restricted to within these bounds). by default of course this covers the entire game window, so 0,0,640,480.
why are these objects treated separately like this? no idea
0x1B06C: scale/zoom factor for game window
404B90: bitmap drawing function
404010: wav playing function
402720: function to draw the 'pixel' sign in loading screen. manually hardcoded pixel-by-pixel instead of just a bitmap for some reason. 402D0F holds the greyish white colour that makes up most of the text, for example.
401890: 'about' dialog box
402F30: mouse handling code? includes menu (with reset/volume/about/close)
403CC0: initialisation/reset function
403CE2: related to cat animation. uses framerects around 0x1F780, loading them into memories around [420E98].
403DCE: related to initialising the instruments (0x3DDA is number of instruments, not counting the melodies)
4034E0: track data parsing function (and who knows what else) (excludes melody)
403430: melody data parsing function. sheds some light on the 0x1F658 stuff. also, it calls 404050 at some point, but 404050 is a tiny function whose sole purpose is to further call 406680. i tried replacing the former call with a call directly to 406680 and it didn't seem to mess anything up.
403E30: start of a function that calls the data parsing functions. it is in turn called by 4022F0, which is called by 401F9F... no idea what these do
406830: raindrop clicking code.
4020B0: has something to do with timing. two single bytes at 0x020F9 and 0x02116 both seem to control the time between updates/frames, albeit in different units - also, it just changes the speed of the whole game. i'd like to find out how to change the bpm of the song though. probably somewhere else in this very function?
403B50: loops through instruments, checks whether they're hit or not by a raindrop, and modifies their framerects, calls 405220
402150: escape menu code
[4206F0]: boolean. if this is made nonzero, the music restarts
[4213A4]: position in song of melody (playhead, if this were a music player)
[420F20]: position in song of non-melody instruments
[420F18]: cat animation frame number
[420F24]: controls speed of song (non-melody instruments). appears to be regularly refreshed to the 'proper' value
[4213A8]: controls speed of song (melody). appears to be regularly refreshed
to change the size of the game, here are all the places where the dimensions appear:
4024F 9/E: dimensions of rendered area on the window
4025 8A/A5: dimensions of whole window itself
0x1F800: BMP_BACK framerects
0x170A4 etc related to os functions? see 40412C
401000, 401050, 401070, 4060c0, 403F40, 403F60, 404030, 404090, 4040C0, 406960: some uncalled debug functions?
401090, 4010A0: two functions that do literally nothing??
the code for the randomly generated rainbow-coloured musical notes remains elusive.
what is the fixed duration between each note?
each note consists of a single 'number' (i.e. 4-byte, little-endian).
0x1B07C is where drum track data begins.
note that there's an instruction at 4035C3 that seems to reference this data, but it uses the offset 1B078. perhaps there's a 4-byte before 1B07C that I overlooked. same with other tracks (melody etc). anyways,
the first two bytes consist of the drums to be played in that note. the first byte handles these drums:
0x01 = bass dram
0x02 = snare
(0x03 = both of the above)
0x10 = hi close
0x20 = hi open
the next byte handles these drums:
0x10 = tam h
0x20 = tam l
the third byte doesn't seem to do anything.
setting the fourth byte to 0x80 indicates that the track should loop after that note. this behaviour is the same for the harmonies/bass/melodies. so the pitch data is really only three bytes.
0x1BFC4 is where harmony data begins.
there are 20 notes (semitones, A1-E2), numbered 0th to 19th. each is associated with a bit. to play a note, flip that bit to 1. add these bits together to play notes simultaneously (similar to piyopiyo). since there is only up to a 19th note (0x00080000), the most significant 3 bits are not used in practice. to play all notes at once you would use 0x000fffff.
0x1CF0C is where bass data begins.
same principle as harmonies, but here there are only 17 notes (C1-E2), so the highest (16th note) would be 0x00010000.
0x1DE54 is where melody data begins.
same principle, 15 notes (G1-A3). highest is 0x00004000
Note: what's really frustrating is how the data is so similar to piyopiyo, but you can't write music for rain in piyopiyo because of the note ranges. Piyopiyo supports a range of 24 semitones for each instrument, and it's not like rain's instruments use any more than that - but piyopiyo's range specifically ranges from Cn to B(n+2). Rain's harmonies, on the other hand, go A1 to E2. Trying to emulate this in piyopiyo, we could try an instrument with a base octave of 1, which ranges C1 to B3, but this misses out the notes A1, As1 and B1. Trying a base octave of 0 misses out all notes from C2 to E2. this is so sad and I'm starting to wonder what sort of interface Pixel used to compose the music for rain (did he just compose it in some midi like editor and manually convert it to the rain format or what)
0x1ED98 starts various data about the instruments, using little-endian 4-byte numbers. each instrument has 10 numbers (0x28 bytes in total) associated with it:
1: describes, in pixels, the horizontal displacement of the centre of an instrument from the left of the screen.
2: vertical displacement from top (this affects when the raindrop hits it and thus when it produces sound.)
3: defines the .wav to be played when this instrument is hit
4: left framerect (BMP_BOTTLE.bmp)
5: top framerect
6: right framerect
7: bottom framerect
8: something to do with hitbox size? see 403BFF
9: handles the instrument animation when hit by a raindrop. it is by default 0, corresponding to frame number 0 in the 3-sprite animation. 32xFrameNumber is added to the top/bottom framerects of the instruments when hit, see e.g. 403B9E. note that the data is not modified directly, but only after being loaded into 'dynamic memory' (see notes below)
10: seems to be related to the above?
Order of instruments: bass dram, snare, tam h, tam l, hi close, hi open, harmony notes (starts 0x1EE88), bass notes (starts 0x1F1A8)
0x1F450 onwards there is more instrument data, 3 numbers associated with each instrument:
1: horizontal displacement of raindrop stream (from left)
2: vertical displacement of beginning of raindrop stream (from ceiling)
3: for the drums this is just the id as described in the track data. for the harmony/bass this is a number that starts at 1 for the lowest note and keeps doubling with each note. not sure what it's for. (solved? see e.g. function at 403430, this data helps the game code 'pick out' the instruments it needs)
Order of instruments same as before, with harmonies at 0x1F498 and bass at 0x1F588
Note: the loop at 403DCE loads these pieces of data into 'dynamic memory', with 0x1ED98 corresponding to [4213B8], and 0x1F450 corresponding to [420828].
0x1F654 could be padding?
0x1F658, there are 15 2-number records (say X, Y) corresponding to the melody notes. Y is again something that starts at 1 and keeps doubling. X starts at 0x32 and just increments with each note until 0x40. This seems to identify the pitch of the note to be played when the bit specified in Y appears in the track data. for example, setting all note's X value to 0x32 makes all the notes in the melody sound the same (G1). Setting all Y's to 01 makes it such that whenever a G1 was supposed to be played (a 01 bit appears in the track data), all notes are played simultaneously since all of them are associated with the 01 bit (this is also why Y needs to be a power of 2).
0x1F6D0: raindrop framerects (BMP_DROP.bmp). left, top, right, bottom respectively. four sprites in the animation.
0x1F710: musical note framerects (BMP_NOTE.bmp). seven of them.
0x1F780: cat framerects (BMP_MELODY.bmp). only top row sprites (bottom row seems to be unused?) Some framerects are repeated here, presumably because of how the animation is sequenced with the sprites, but manually recreating it that way didn't work out. huh.
0x1F800: background framerect (BMP_BACK.bmp)
402156: BMP_ESCAPE framerects
0x1F810: toggles tracks. 0x01 only drums, 0x02 only harmonies, 0x04 only bass, 0x08 only melody. Flip these bits for combinations thereof. Default is 0x0F (all tracks enabled). Note that this doesn't affect manually added raindrops.
0x1F814: ?
0x3339: how far from the ceiling the raindrops are allowed to fall before disappearing (unless they hit an instrument before that). also determines the range in which we can click to create raindrops.
this seems to be part of an asm function that handles raindrops, but i can't seem to figure anything else out
0x3E85 (just one byte): messing with this makes the melody show up slightly early
0x1B04C: framerects (left, top, right, bottom) for a region within raindrops, the loading screen logo, and the fps counter will be rendered (these objects still exist, just their rendering is restricted to within these bounds). by default of course this covers the entire game window, so 0,0,640,480.
why are these objects treated separately like this? no idea
0x1B06C: scale/zoom factor for game window
404B90: bitmap drawing function
404010: wav playing function
402720: function to draw the 'pixel' sign in loading screen. manually hardcoded pixel-by-pixel instead of just a bitmap for some reason. 402D0F holds the greyish white colour that makes up most of the text, for example.
401890: 'about' dialog box
402F30: mouse handling code? includes menu (with reset/volume/about/close)
403CC0: initialisation/reset function
403CE2: related to cat animation. uses framerects around 0x1F780, loading them into memories around [420E98].
403DCE: related to initialising the instruments (0x3DDA is number of instruments, not counting the melodies)
4034E0: track data parsing function (and who knows what else) (excludes melody)
403430: melody data parsing function. sheds some light on the 0x1F658 stuff. also, it calls 404050 at some point, but 404050 is a tiny function whose sole purpose is to further call 406680. i tried replacing the former call with a call directly to 406680 and it didn't seem to mess anything up.
403E30: start of a function that calls the data parsing functions. it is in turn called by 4022F0, which is called by 401F9F... no idea what these do
406830: raindrop clicking code.
4020B0: has something to do with timing. two single bytes at 0x020F9 and 0x02116 both seem to control the time between updates/frames, albeit in different units - also, it just changes the speed of the whole game. i'd like to find out how to change the bpm of the song though. probably somewhere else in this very function?
403B50: loops through instruments, checks whether they're hit or not by a raindrop, and modifies their framerects, calls 405220
402150: escape menu code
[4206F0]: boolean. if this is made nonzero, the music restarts
[4213A4]: position in song of melody (playhead, if this were a music player)
[420F20]: position in song of non-melody instruments
[420F18]: cat animation frame number
[420F24]: controls speed of song (non-melody instruments). appears to be regularly refreshed to the 'proper' value
[4213A8]: controls speed of song (melody). appears to be regularly refreshed
to change the size of the game, here are all the places where the dimensions appear:
4024F 9/E: dimensions of rendered area on the window
4025 8A/A5: dimensions of whole window itself
0x1F800: BMP_BACK framerects
0x170A4 etc related to os functions? see 40412C
401000, 401050, 401070, 4060c0, 403F40, 403F60, 404030, 404090, 4040C0, 406960: some uncalled debug functions?
401090, 4010A0: two functions that do literally nothing??
the code for the randomly generated rainbow-coloured musical notes remains elusive.