Go Back   The PokéCommunity Forums > ROM Hacking > Research & Development

Notices
For all updates, view the main page.

Research & Development Got a well-founded knack with ROM hacking? Love reverse-engineering the Pokémon games? Or perhaps you love your assembly language. This is the spot for polling and gathering your ideas, and then implementing them! Share your hypothesis, get ideas from others, and collaborate to create!
Research & Development programs in this forum are subject to moderator approval before they are displayed.



Reply
 
Thread Tools
  #1    
Old September 6th, 2014 (08:22 PM). Edited September 11th, 2014 by Team Fail.
Team Fail
Find your way
Community Supporter
 
Join Date: May 2009
Age: 19
Gender: Male
Nature: Brave
Please note: I've used one particular game for research on this. There are multiple games that make use of this sound engine. For a complete list of games that use this engine (There are multiple systems, I'm doing Game Boy Advance), click here.

So in my research thread for Crash Bandicoot 2: N-Tranced, I've been working on the audio engine. There was a hack that replaced the "sappy" (M4A) engine with their own (If someone could remind me what hack that was, that'd be totally cool), and I thought it'd be neat to reverse-engineer this engine format (It's an in-house format, songs were composed and precompiled before being sent to game developers, so there's no leaked SDK) so that advanced hackers can have some additional playtoys to work with. If you want a reason for this, here's a few:
  • The format is based upon a tracker format, so you can (possibly, will have to work on this in the future) import your own tracked formats, perhaps even sequenced once it's fixed up
  • The format supports a maximum of 32 tracks. The M4A engine only offers a dismal 16.
  • Low RAM usage
There's also a drawback:
  • No stereo support (Yet, it may be implemented in other games I have yet to test)

So now, at this point, I'm going to basically do a copy-paste of my post of work from the other thread and put it here. The below information is for a Crash Bandicoot 2: N-Tranced rom (MD5: BB3139CAECF3888C22F5EA4B1D6FAF2F).

Audio Sequence Starting offsets (USA Version) (In internal order, note that names are stored internally and are referenced as so.):
theme: 0x518584
warproom: 0x519204
persian: 0x519B88
volcano: 0x51A700
fake: 0x51AFB0
ntropy: 0x51B90C
egypt: 0x51BCBC
jaws: 0x51C72C
drums: 0x51C9D0
mayan: 0x51CD94
cutscenes: 0x51D594 //This song is unused. I've been able to force it to load, and it's a leftover from the previous game. This manual GSF rip has the song in question. It's the final song in the archive.
bonus round: 0x51D7E4
evil coco: 0x51DB64
ntrance: 0x51E108
evil crunch: 0x51E5C0
coco: 0x51ECDC
cutscenes spooky: 0x51F478
cutscenes normal: 0x51F794
wake: 0x51FBE8

Sample Table? 0x4E45EC

I'm currently analysing the footer format, as it's based upon the AHX audio format that the C64 uses, although it's modified so that it can run on samples and whatnot stored internally. The footer contains a pointer back to the beginning of the audio file (That's where I nabbed the offsets), as well as a pointer to what I believe is the sample table. I'm not 100% sure yet, though, but I think loop data is also in the footer, but I haven't started playing with audio yet to be able to test that. I may, though, see if I can copy and paste data around and see if I can force alternate songs to load so I can see if there's any unused music in the game.

Actually, allow me to post the information for one song. I'll color-code in what I know.

0x51D594:
Code:
01 00 3E 27 0C C8 FA 0C C0 3E 27 0C A0 FA 0C C0
3E 27 0C C0 FA 0C C0 3E 27 0C A0 FA 0C C0 3E 27
0C C0 FA 0C C0 3E 27 0C A0 FA 0C C0 3E 27 0C C0
FA 0C C0 3E 27 0C A0 FA 0C C0 00 3E 4F 0F 08 80
BE 5C FF 03 3E 5C 0C C0 BE 5C FF 02 BE 5C 80 BE
4F 80 BE 5C 80 00 9C 1E 81 00 A8 1E 81 00 A3 1E
81 00 A4 1E 81 00 A5 1E 81 00 80 A3 1E 81 00 80
A3 1E 81 00 00 1C 4E 0C C0 FA 0C C0 28 4E 0C C0
FA 0C C0 23 4E 0C C0 FA 0C C0 24 4E 0C C0 FA 0C
C0 25 4E 0C C0 FA 0C C0 01 00 0C C0 23 4E 0C C0
01 00 0C C0 FA 0C C0 27 4E 0C C0 01 00 0C C0 00
9C 1E 81 00 A8 1E 81 00 A3 1E 81 00 A4 1E 81 00
A5 1E 81 00 80 A3 1E 81 00 80 A7 1E 81 00 00 A8
5D 80 B4 5D 80 AF 5D 80 B0 5D 80 B1 5D 80 AC 5D
AF 5D FF 02 AA 5D 80 00 FF 0E B3 5D 80 00 A8 5D
80 B4 5D 80 AF 5D 80 B0 5D 80 B1 5D 80 AC 5D AF
5D 80 AF 5D B3 5D AF 5D 22 63 75 74 73 63 65 6E
65 73 22 20 A9 20 4D 61 6E 66 72 65 64 20 4C 69
6E 7A 6E 65 72 00 00 00 01 00 00 00 01 00 00 00
01 00 00 00 01 00 00 00 3A 00 00 00 3A 00 00 00
3A 00 00 00 3A 00 00 00 55 00 00 00 AF 00 00 00
55 00 00 00 AF 00 00 00 74 00 00 00 74 00 00 00
74 00 00 00 74 00 00 00 55 00 0C 00 AF 00 0C 00
55 00 0C 00 AF 00 0C 00 00 00 00 00 E7 00 00 00
CE 00 00 00 ED 00 00 00 06 00 10 00 04 00 02 00
91 01 00 00 94 D5 51 08 EC 45 4E 08 04 84 51 08
21 52 00 00 01 00 00 00 BC D6 51 08 CC D6 51 08
DC D6 51 08 FC D6 51 08 0C D7 51 08 EC D6 51 08
-Cutscenes, unused audio right here :D

94 D5 51 08 - Pointer to the beginning of the sequence data, or, the beginning of this data block.
EC 45 4E 08 - Pointer to (what I think is) the sample table.
04 84 51 08 - Another table pointer. Not sure what these tables are yet.
The blue, underlined, and bold text are the credits for the song stored in plaintext. In this song, it says ""cutscenes" © Manfred Linzner".
Highlighted region: Data chunk that's referenced by the sound table. By changing pointers in the sound table, you can force different songs to load. The pointers after the second unknown table pointer reference the various channels (These are underlined) (I think).

0x1CFBE8 contains the sound table that references all these songs. This table references the footer block (Highlighted), which references the track data above it. This sound table also is pointed from 0x1910 (E8 FB 1C 08). Directly next door to this is a sound effect table pointer at 0x1914. I believe it's around the 0x1900 area that the audio engine is actually initiated.

So, if anyone would like to offer their two cents into this, that'd be terrific. I don't have a lot of time anymore because I'm starting College, so any input would be lovely to have. I don't want to be the only one working on this.

Edit: Some more info here: http://hcs64.com/mboard/forumlong.php?showthread=39&showpage=16
__________________
| | | | |
PM | VM



Do the best with whatever you can today.
Then tomorrow, you will have surely progressed.

Reply With Quote
  #2    
Old September 11th, 2014 (05:57 PM).
Team Fail
Find your way
Community Supporter
 
Join Date: May 2009
Age: 19
Gender: Male
Nature: Brave
Updated with a few additional notes, as well as some typo corrections.
__________________
| | | | |
PM | VM



Do the best with whatever you can today.
Then tomorrow, you will have surely progressed.

Reply With Quote
  #3    
Old December 8th, 2014 (08:48 AM). Edited December 8th, 2014 by Team Fail.
Team Fail
Find your way
Community Supporter
 
Join Date: May 2009
Age: 19
Gender: Male
Nature: Brave
So, in digging deeper into the hcs64 thread, I found some interesting things about how the engine works.

Basically, to initialise a sound, gax_init is used every time a new sound is loaded. However, the offset to the song (From the sound table) is passed through it to be loaded, which is rather interesting, but at the same time, allows for almost infinite sound table expansion. It does this instead of passing a position through which contains the offset. Theoretically, one can write a new song, and as long as you can pass through the offset to the new song, you can get it to play.

This can be seen when you breakpoint gax2_init in no$gba debugger and play the game. Whenever a new song is needed, it's called. It seems sound effects are loaded differently, though, since it's not called when a sound effect is run.

As a result of this, I believe the code is somewhat like this (Really bad example of pseudocode):

Code:
int[] songs = {offset0, offset1, ... , offset 15, offset 16}; //use positions in array to get song pointer. Defined on game boot so the entirety of the game can access this. Populated during make.

public static void loadLevel (Song song, *other crap*)
{
     gax2_init(song, *other params*); //song is the position in the array of the song to load. For example, if song == 0x1, it'd go to offset1 and load that sound data.
     //other level loading params here...
}
__________________
| | | | |
PM | VM



Do the best with whatever you can today.
Then tomorrow, you will have surely progressed.

Reply With Quote
  #4    
Old December 10th, 2014 (01:53 PM).
AtecainCorp.'s Avatar
AtecainCorp.
Rejishan awake...
 
Join Date: Jun 2008
Location: Takoabe Town (Region Thonsu)
Age: 22
Gender: Male
Nature: Hardy
Ok. Nice. But One Issue... When you inster GAX sound Engine how you think to change Music in Hacks? Since Sappy do not Support GAX.
__________________
This signature has been disabled.
Exceeds the 600px width limit.
Please review and fix the issues by reading the signature rules.

You must edit it to meet the limits set by the rules before you may remove the [sig-reason] code from your signature. Removing this tag will re-enable it.

Do not remove the tag until you fix the issues in your signature. You may be infracted for removing this tag if you do not fix the specified issues. Do not use this tag for decoration purposes.
Reply With Quote
  #5    
Old December 10th, 2014 (10:04 PM).
Team Fail
Find your way
Community Supporter
 
Join Date: May 2009
Age: 19
Gender: Male
Nature: Brave
Quote originally posted by Ksiazek Bartlomiej:
Ok. Nice. But One Issue... When you inster GAX sound Engine how you think to change Music in Hacks? Since Sappy do not Support GAX.
That's the whole point of this thread. The music format is unknown, and I'm aiming to find out exactly how it works, then create an application for such.
__________________
| | | | |
PM | VM



Do the best with whatever you can today.
Then tomorrow, you will have surely progressed.

Reply With Quote
  #6    
Old December 11th, 2014 (05:53 PM).
Team Fail
Find your way
Community Supporter
 
Join Date: May 2009
Age: 19
Gender: Male
Nature: Brave
So I got my hands on a plugin to convert from (To me) almost unreadable assembly and put it into something a bit more understandable to me C (I know C#, so it's kinda similar, although not really). I've been able to find out that gax2_init requires only two variables, and according to the IDA C disassembler, gax2_init takes two parameters when being called. One must be the pointer, but the other, I'm not sure. I'm going to have to debug the routine and check the registers to see what it uses.
__________________
| | | | |
PM | VM



Do the best with whatever you can today.
Then tomorrow, you will have surely progressed.

Reply With Quote
  #7    
Old December 12th, 2014 (02:44 PM).
Hopeless Masquerade's Avatar
Hopeless Masquerade
すきま妖怪
Community Supporter
 
Join Date: Mar 2010
Location: 幻想郷
Age: 17
Gender: Male
Nature: Relaxed
Quote originally posted by Team Fail:
So I got my hands on a plugin to convert from (To me) almost unreadable assembly and put it into something a bit more understandable to me C (I know C#, so it's kinda similar, although not really). I've been able to find out that gax2_init requires only two variables, and according to the IDA C disassembler, gax2_init takes two parameters when being called. One must be the pointer, but the other, I'm not sure. I'm going to have to debug the routine and check the registers to see what it uses.
If you'd be willing to send me a copy of the assembly, I'd like to take a look at it and see what I can figure out.
__________________
Reply With Quote
  #8    
Old December 12th, 2014 (06:00 PM).
Team Fail
Find your way
Community Supporter
 
Join Date: May 2009
Age: 19
Gender: Male
Nature: Brave
Quote originally posted by itari:
If you'd be willing to send me a copy of the assembly, I'd like to take a look at it and see what I can figure out.
The IDB I have going is in the Crash Bandicoot 2 thread. You can get it here:

http://www.pokecommunity.com/showthread.php?p=8463069#8463069

Just look for the "gax2_init" function.
__________________
| | | | |
PM | VM



Do the best with whatever you can today.
Then tomorrow, you will have surely progressed.

Reply With Quote
  #9    
Old December 17th, 2014 (06:15 PM).
Team Fail
Find your way
Community Supporter
 
Join Date: May 2009
Age: 19
Gender: Male
Nature: Brave
So, I'm working on perhaps programmatically detecting the gax_init subroutine, and I've found something in particular with the init routine that 4 games that use the engine.

Assume that there is an array of 0xF bytes with some bytes from the rom in int[] bytesRead.
Code:
if(bytesRead[0] == 0xF0 && bytesRead[1] == 0xB5 && bytesRead[3] == bytesRead[5])
	return position;
I might actually try coding this into an application, and see if I can actually get it to return not only the position of the gax_init function, but perhaps programmatically return the offset to the sound table and make it read the audio.
__________________
| | | | |
PM | VM



Do the best with whatever you can today.
Then tomorrow, you will have surely progressed.

Reply With Quote
  #10    
Old December 17th, 2014 (06:19 PM).
Hopeless Masquerade's Avatar
Hopeless Masquerade
すきま妖怪
Community Supporter
 
Join Date: Mar 2010
Location: 幻想郷
Age: 17
Gender: Male
Nature: Relaxed
Quote originally posted by Team Fail:
So, I'm working on perhaps programmatically detecting the gax_init subroutine, and I've found something in particular with the init routine that 4 games that use the engine.

Assume that there is an array of 0xF bytes with some bytes from the rom in int[] bytesRead.
Code:
if(bytesRead[0] == 0xF0 && bytesRead[1] == 0xB5 && bytesRead[3] == bytesRead[5])
	return position;
I might actually try coding this into an application, and see if I can actually get it to return not only the position of the gax_init function, but perhaps programmatically return the offset to the sound table and make it read the audio.
So my suggestion would be to find the code that loads from a game's song table, and try to find similar functions in other games. Assuming it's all standard code, the only differences would be pointers.

Also, I've got a function written up that finds the M4A song table already, so if you can find the needed code to search for this I can give it a try.
__________________
Reply With Quote
  #11    
Old December 17th, 2014 (07:33 PM). Edited December 18th, 2014 by Team Fail.
Team Fail
Find your way
Community Supporter
 
Join Date: May 2009
Age: 19
Gender: Male
Nature: Brave
Quote originally posted by itari:
So my suggestion would be to find the code that loads from a game's song table, and try to find similar functions in other games. Assuming it's all standard code, the only differences would be pointers.

Also, I've got a function written up that finds the M4A song table already, so if you can find the needed code to search for this I can give it a try.
Problem (And benefit) is, whenever a song is loaded, gax_init is called. This function contains the pointer to the song table. However, it doesn't look it up in the sound table, for some reason, it takes a pointer instead. I haven't debugged it in-depth yet, but that's what I've gotten from others' research.

I'm thinking that I'm going to make it search the init routine for the pointer. There shouldn't be many pointers to check. I'll see how it turns out though, it might not be as reliable as I think it is.

Edit:



So I just ran into a massive wall. It seems Sigma Star Saga does NOT have a freaking pointer table. How it works I have no idea.

I think, at this point, I'm going to completely scrap the whole scanning for sound table thing, and just make it refer to a database of pointers and whatnot, and use that to load from the GBA rom, as well as various loading methods depending on the database's configuration, whether it be reading the pointers directly and loading them into the program, or reading a table programmatically, or whatever else there will have to be. It'll involve manual intervention to find the song data within the rom, but once added to the database, the program will read it as such. It seems the song binary format isn't changing at all, but the way it's loaded is.

This is gonna be messy.
__________________
| | | | |
PM | VM



Do the best with whatever you can today.
Then tomorrow, you will have surely progressed.

Reply With Quote
  #12    
Old December 20th, 2014 (06:42 PM). Edited December 20th, 2014 by Hopeless Masquerade.
Hopeless Masquerade's Avatar
Hopeless Masquerade
すきま妖怪
Community Supporter
 
Join Date: Mar 2010
Location: 幻想郷
Age: 17
Gender: Male
Nature: Relaxed
Okay, so I was looking at the "sample table" that you reference, and here's what I've got.

It is (obviously) a list of pointers, 109 entries long (0x1B4 bytes).
Each entry (that I've bothered looking at) points to a 16 byte structure, all of which end in another pointer.

The data these pointers go to seem to be a second data structure, of variable length.
EDIT: If this second structure starts with 01 FF FF FF (-255?) it seems to be 20 bytes in length.

Based on what I know of I know of the samples that are used normally within M4A games, these do not appear to be samples at all.

If they are samples of some kind, maybe they are instrument behavior definitions?
__________________
Reply With Quote
Reply
Quick Reply

Sponsored Links
Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Minimum Characters Per Post: 25



All times are UTC -8. The time now is 09:21 PM.