Skip to content

Commit 836c859

Browse files
author
Megan Leet
committed
Detect more GBA sound drivers
1 parent fd7f208 commit 836c859

File tree

2 files changed

+137
-18
lines changed

2 files changed

+137
-18
lines changed

LICENSE

+7-1
Original file line numberDiff line numberDiff line change
@@ -55,4 +55,10 @@ Redistribution and use in source and binary forms, with or without modification,
5555

5656
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
5757

58-
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
58+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
59+
60+
It also includes stuff from GBAMusRipper, which doesn't have a standard license as such, but it does say this:
61+
62+
"GBAMusRipper is free and open source software. Anyone is free to redistribute it (with and without sources) and to improve it but just give me credit, THANK YOU VERY MUCH."
63+
64+
Hence, credit is given to Bregalad and CaptainSwag101 as the original authors of GBAMusRipper.

ROMniscience/Handlers/GBA.cs

+130-17
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232

3333
namespace ROMniscience.Handlers {
3434
//Mostly adapted from http://problemkaputt.de/gbatek.htm#gbacartridgeheader
35-
class GBA: Handler {
35+
class GBA : Handler {
3636
public override IDictionary<string, string> filetypeMap => new Dictionary<string, string> {
3737
{"gba","Nintendo Game Boy Advance ROM" },
3838
{"bin","Nintendo Game Boy Advance ROM" },
@@ -54,7 +54,7 @@ class GBA: Handler {
5454
{'M', "GBA Video"}, //Also used by mb2gba and any multiboot roms converted by it
5555
{'Z', "DS expansion"}, //Daigassou! Band-Brothers - Request Selection (it's just a slot 2 device for a DS game, but it has a
5656
//GBA ROM header surprisingly), also Nintendo MP3 Player which was marketed as being for the DS so maybe "DS expansion" isn't quite
57-
//the right name but it'll have to do
57+
//the right name but I dunno
5858
//Have also seen J for the Pokemon Aurora Ticket distribution cart, and G for GameCube multiboot images (they just use the product code of the GameCube disc they were from usually)
5959
};
6060

@@ -70,7 +70,7 @@ int calculateChecksum(WrappedInputStream f) {
7070
try {
7171
int x = 0;
7272
f.Position = 0xa0;
73-
while(f.Position <= 0xbc) {
73+
while (f.Position <= 0xbc) {
7474
x = (x - f.read()) & 0xff;
7575
}
7676
return (x - 0x19) & 0xff;
@@ -79,12 +79,6 @@ int calculateChecksum(WrappedInputStream f) {
7979
}
8080
}
8181

82-
readonly static byte[] SAPPY_SELECTSONG = {
83-
0x00, 0xB5, 0x00, 0x04, 0x07, 0x4A, 0x08, 0x49,
84-
0x40, 0x0B, 0x40, 0x18, 0x83, 0x88, 0x59, 0x00,
85-
0xC9, 0x18, 0x89, 0x00, 0x89, 0x18, 0x0A, 0x68,
86-
0x01, 0x68, 0x10, 0x1C, 0x00, 0xF0};
87-
8882
const int GBA_LOGO_CRC32 = -0x2F414AA2;
8983

9084
static bool isNintendoLogoEqual(byte[] nintendoLogo) {
@@ -102,22 +96,142 @@ static bool isNintendoLogoEqual(byte[] nintendoLogo) {
10296
//It also puts this in for games that use the real time clock
10397
readonly static byte[] RTC = Encoding.ASCII.GetBytes("SIIRTC_V");
10498

105-
static void detectSaveType(ROMInfo info, byte[] bytes) {
106-
if(ByteSearch.contains(bytes, EEPROM)) {
99+
static void detectSaveType(ROMInfo info, byte[] bytes) {
100+
if (ByteSearch.contains(bytes, EEPROM)) {
107101
info.addInfo("Save type", "EEPROM");
108102
//Can't tell the save size from this, it's either 512 or 8192 though
109-
} else if(ByteSearch.contains(bytes, SRAM) || ByteSearch.contains(bytes, SRAM_F)) {
103+
} else if (ByteSearch.contains(bytes, SRAM) || ByteSearch.contains(bytes, SRAM_F)) {
110104
info.addInfo("Save type", "SRAM");
111105
info.addInfo("Save size", 32 * 1024, ROMInfo.FormatMode.SIZE);
112-
} else if(ByteSearch.contains(bytes, FLASH) || ByteSearch.contains(bytes, FLASH_512)) {
106+
} else if (ByteSearch.contains(bytes, FLASH) || ByteSearch.contains(bytes, FLASH_512)) {
113107
info.addInfo("Save type", "Flash");
114108
info.addInfo("Save size", 64 * 1024, ROMInfo.FormatMode.SIZE);
115-
} else if(ByteSearch.contains(bytes, FLASH_1024)) {
109+
} else if (ByteSearch.contains(bytes, FLASH_1024)) {
116110
info.addInfo("Save type", "Flash");
117111
info.addInfo("Save size", 128 * 1024, ROMInfo.FormatMode.SIZE);
118112
}
119113
}
120114

115+
//Thanks to GBAMusRipper and saptapper for documenting these and whatnot
116+
readonly static byte[] MP2K_SELECTSONG = {
117+
0x00, 0xB5, 0x00, 0x04, 0x07, 0x4A, 0x08, 0x49,
118+
0x40, 0x0B, 0x40, 0x18, 0x83, 0x88, 0x59, 0x00,
119+
0xC9, 0x18, 0x89, 0x00, 0x89, 0x18, 0x0A, 0x68,
120+
0x01, 0x68, 0x10, 0x1C, 0x00, 0xF0
121+
};
122+
readonly static byte[] MP2K_NEW_SELECTSONG = {
123+
0x00, 0xB5, 0x00, 0x04, 0x07, 0x4B, 0x08, 0x49,
124+
0x40, 0x0B, 0x40, 0x18, 0x82, 0x88, 0x51, 0x00,
125+
0x89, 0x18, 0x89, 0x00, 0xC9, 0x18, 0x0A, 0x68,
126+
0x01, 0x68, 0x10, 0x1C, 0x00, 0xF0
127+
};
128+
readonly static byte[] NATSUME_MAIN = {
129+
0x70, 0xb5, 0x20, 0x49, 0x20, 0x4a, 0x10, 0x1c,
130+
0x08, 0x80, 0x00, 0xf0, 0x8d, 0xf8, 0x01, 0xf0,
131+
0x97, 0xfc, 0x00, 0xf0, 0x4b, 0xf8, 0x80, 0x21,
132+
0xc9, 0x04, 0x60, 0x20, 0x08, 0x80, 0x1b, 0x49,
133+
0x01, 0x20, 0x08, 0x60, 0x1a, 0x48, 0x00, 0x21,
134+
0x01, 0x60, 0x1a, 0x48, 0x01, 0x60, 0x37, 0xf0,
135+
0x81, 0xfa, 0x19, 0x48, 0x00, 0xf0, 0xce, 0xf8
136+
};
137+
138+
readonly static byte[] GAX2_INIT = { 0x47, 0x41, 0x58, 0x32, 0x5f, 0x49, 0x4e, 0x49, 0x54}; //Literally "GAX2_INIT" in ASCII
139+
//Taken from lib/mixer_func.s from the source of Krawall on Github, converted from assembly to hex. Seems to be good enough for identification
140+
readonly static byte[] KRAWALL_MIXCENTER = {
141+
0xf0, 0x0f, 0x2d, 0xe9, //stmdb sp! {r4-r11}
142+
0x08, 0x50, 0x90, 0xe5, //ldr r5, [r0, #8]
143+
0x14, 0x60, 0x90, 0xe5, //ldr r6, [r0, #20]
144+
0xbc, 0x71, 0xd0, 0xe1, //ldrh r7, [r0, #28]
145+
0x1e, 0x30, 0xd0, 0xe5, //ldrb r3, [r0, #30]
146+
0x22, 0x21, 0xa0, 0xe1 //mov r2, r2, lsr #2
147+
};
148+
//....Yeah
149+
readonly static byte[] RARE_AUDIO_ERROR = Encoding.ASCII.GetBytes("AUDIO ERROR, too many notes on channel 0.increase polyphony RAM");
150+
//Discovered this accidentally myself, sometimes it's credited as GBAModPlay and sometimes as LS_Play; sometimes 2002 and sometimes 2003; and Google has no results at all for either of these other than the latter appearing in the TCRF page for Garfield and His Nine Lives but having no explanation other than it being a hidden credit and the URL after the year is now simply about a mobile game which the company made and seems to be their only online presence
151+
readonly static byte[] LOGIK_STATE_COPYRIGHT = Encoding.ASCII.GetBytes(" (C) Logik State ");
152+
153+
static string detectSoundDriver(ROMInfo info, byte[] bytes) {
154+
if (ByteSearch.contains(bytes, MP2K_SELECTSONG)) {
155+
//The standard driver among most GBA games, seemingly included in the official GBA SDK (apparently). Otherwise
156+
//known as Sappy or M4A
157+
return "MP2000";
158+
} else if (ByteSearch.contains(bytes, MP2K_NEW_SELECTSONG)) {
159+
//Apparently it was also recompiled at some point and some games use it (Mother 3, Minish Cap, some others) but there doesn't seem to be any consistency in terms of new games using this and older games using the allegedly older driver
160+
return "MP2000 (new)";
161+
} else if (ByteSearch.contains(bytes, NATSUME_MAIN)) {
162+
//Not sure what uses this. Games developed by Natsume, I guess (which amounts to basically Medabots, Keitai Denju Telefang 2, Buffy the Vampire Slayer, Shaun Palmer's Pro Snowboarder, some Power Rangers and wrestling games)
163+
return "Natsume";
164+
} else if (ByteSearch.contains(bytes, KRAWALL_MIXCENTER)) {
165+
//A third party thing that plays converted s3m/xm files, used by a few games such as
166+
//Lord of the Rings and The Sims according to the author's website, and also
167+
//Dora the Explorer: Dora's World Adventure and Harry Potter and the Prisoner of Azkaban (but
168+
//not the other Dora or Harry Potter games), unless I'm actually detecting this all wrong
169+
//and they use something else. It's possible I am because it appears in a few homebrew
170+
//demos (excluding the obvious Krawall Demo), but then maybe they actually do use it since it's now LGPL'd
171+
//so I guess I should check the credits of those?
172+
return "Krawall";
173+
} else if (ByteSearch.contains(bytes, RARE_AUDIO_ERROR)) {
174+
//Games developed by Rare have this huge block of error text. This is probably the most wrong way to
175+
//possibly do this but it works and whatnot so maybe it isn't. But I feel dirty for doing this
176+
return "Rare";
177+
} else if (ByteSearch.contains(bytes, GAX2_INIT)) {
178+
//Used by various third-party games. All of them have a block of copyright text
179+
//specifying that the game uses the GAX engine, and also the version which is nice, that
180+
//the engine is developed by Shin'en Multimedia, and also some function names like
181+
//GAX2_INIT a bit after that block. Although I feel like this might result in
182+
//false positives.... should be fine, hopefully
183+
return "GAX";
184+
} else if(ByteSearch.contains(bytes, LOGIK_STATE_COPYRIGHT)) {
185+
//I don't know what to call this one; used in a few third party games (Asterisk & Obelisk XXL, Driv3r among others)
186+
//Gotta admit I don't really like this and should detect it better, but it is apparent that those two games use things by this
187+
//company at least
188+
return "GBAModPlay/LS_Play";
189+
} else {
190+
return "Unknown";
191+
}
192+
//Games with unknown sound drivers:
193+
//007: Nightfire (JV Games)
194+
//Barbie as the Island Princess (Human Soft)
195+
//Barbie Groovy Games (DICE)
196+
//Barbie Horse Adventures: Blue Ribbon Race (Mobius, Blitz)
197+
//Classic NES Series / Famicom Mini
198+
//Crazy Frog Racer (Independent Arts)
199+
//Crazy Taxi: Catch a Ride (Graphic State, music by Paragon 5)
200+
//Doom (David A. Palmer Productions)
201+
//Doom II (Torus, same Southpaw engine as used in Duke Nukem Advance)
202+
//Dora the Explorer: Super Spies (Cinegroupe)
203+
//Dora the Explorer: The Search for the Pirate Pig's Treasure (Cinegroupe)
204+
//Dragon Ball GT: Transformation (Webfoot)
205+
//Dragon Ball Z: Collectible Card Game (ImaginEngine)
206+
//Dragon Ball Z: Taiketsu (Webfoot)
207+
//Dragon Ball Z: The Legacy of Goku trilogy (Webfoot)
208+
//Duke Nukem Advance (Torus, same Southpaw engine as used in Doom II)
209+
//FIFA Soccer 07 (Exient)
210+
//Hamtaro: Ham-Ham Games (AlphaDream)
211+
//Hamtaro: Rainbow Rescue (AlphaDream)
212+
//Harry Potter and the Philosopher's Stone, Chamber of Secrets (Griptonite, says something about MusyX in the intro)
213+
//Hello Kitty: Happy Party Pals (Webfoot)
214+
//Lego Island 2 (Silicon Dreams)
215+
//Mario vs. Donkey Kong (Nintendo Software Technology)
216+
//Mary Kate & Ashley: Girls Night Out, Sweet 16: Licensed to Drive (Powerhead)
217+
//Max Payne (Mobius)
218+
//Meine Tiearztpraxis / Meine Tierpension (Independent Arts)
219+
//Metroid Fusion (Uses the MP2000 sequence format but not the MP2000 playback. Metroid: Zero Mission and Wario Land 4 are probably the same?)
220+
//My Little Pony: Crystal Princess: The Runaway Rainbow (Webfoot)
221+
//Need For Speed: Underground (Pocketeers)
222+
//Pinball Challenge Deluxe (Binary9, uses Logik State's music playback according to the credits but doesn't have the usual copyright string, so I may be doing something wrong)
223+
//SimCity 2000 (Full Fat)
224+
//Super Mario Advance 2/3/4 (Nintendo R&D2, Super Mario Advance 1 uses MP2000)
225+
//V-Rally 3 (Velez & Dubail)
226+
//WarioWare Inc, WarioWare: Twisted (Nintendo SPD 1)
227+
//Who Wants to be a Millionaire? (Houthouse)
228+
//GBA Video (4Kidz apparently?)
229+
//Rhythm Tengoku (Nintendo SPD 1)
230+
231+
//Pokemon Liquid Crystal and Pokemon Shiny Gold are Pokemon ROM hacks so they should use MP2000, but apparently they don't somehow or they broke something to make them not detect as using it
232+
//Also, apparently Mario & Luigi: Superstar Saga only uses MP2000 for the Mario Bros part and not for the main game, so that's weird
233+
}
234+
121235
public override void addROMInfo(ROMInfo info, ROMFile file) {
122236
info.addInfo("Platform", name);
123237
WrappedInputStream f = file.stream;
@@ -177,10 +291,9 @@ public override void addROMInfo(ROMInfo info, ROMFile file) {
177291
//0xe0 contains a joybus entry point if joybus stuff is set, meh
178292

179293
byte[] restOfCart = f.read((int)f.Length);
180-
detectSaveType(info, restOfCart);
181294
info.addInfo("Has RTC", ByteSearch.contains(restOfCart, RTC));
182-
info.addInfo("Sound driver", ByteSearch.contains(restOfCart, SAPPY_SELECTSONG) ? "Sappy" : "Unknown");
183-
//TODO Krawall is open source, see if we can detect that
295+
detectSaveType(info, restOfCart);
296+
info.addInfo("Sound driver", detectSoundDriver(info, restOfCart));
184297
}
185298
}
186299
}

0 commit comments

Comments
 (0)