Created
October 31, 2022 20:43
-
-
Save spezifisch/3e92aeca446fe8b17d07d8ad53df46fa to your computer and use it in GitHub Desktop.
RaveGenerator2 Sample Dumper
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"cells": [ | |
{ | |
"cell_type": "markdown", | |
"id": "7a529978", | |
"metadata": {}, | |
"source": [ | |
"# RaveGenerator2 Sample Dumper 💀\n", | |
"\n", | |
"This Notebook extracts embedded audio samples from the Linux version of the VST Plugin RaveGenerator2.\n", | |
"So you can do dirty rave sounds directly in your sampler of choice and without installing Qt4 (see https://discourse.ardour.org/t/rave-generator-2-vst-no-gui-amd-graphics/107099/3).\n", | |
"\n", | |
"Get the plugin here: https://blog.wavosaur.com/rave-generator-2-vst-audiounit-the-stab-machine-is-back-in-the-house/ (RaveGenerator2-Linux.tar.gz)\n", | |
"\n", | |
"Contained should be a file `RaveGenerator2VST-x64.so` that looks like:\n", | |
"\n", | |
"```console\n", | |
"$ file RaveGenerator2VST-x64.so\n", | |
"RaveGenerator2VST-x64.so: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, BuildID[sha1]=aeb7c5c39b06d96e0f5dc629457a66bc07522965, stripped\n", | |
"\n", | |
"$ sha512sum RaveGenerator2VST-x64.so \n", | |
"c32d1d7834075ddaab09305ef00020375f41ba4a420789e125778e26260da578c4f65d9e02d3ec08a6ee861643edda344e385c2d99351626ab61ba2b9c1909f0 RaveGenerator2VST-x64.so\n", | |
"```\n", | |
"\n", | |
"Tested with:\n", | |
"* Ghidra v10.1.15\n", | |
"* https://github.com/GhidraJupyter/ghidra-jupyter-kotlin v1.5.1\n", | |
"\n", | |
"## Usage\n", | |
"\n", | |
"1. Run Ghidra, throw `RaveGenerator2VST-x64.so` in there, and analyze it\n", | |
"2. Start GhidraJupyterKotlin kernel and open this notebook\n", | |
"3. Run the following two blocks\n", | |
"4. You will get all the contained samples as `.wav` files like:\n", | |
"```\n", | |
"ravegen_stabs3_Gabbers_C3.wav\n", | |
"ravegen_stabs3_Hardcore Hoover_D#2.wav\n", | |
"ravegen_stabs3_Hound Stab_C3.wav\n", | |
"ravegen_stabs3_House Nation_C3.wav\n", | |
"ravegen_stabs3_Magic Feet_C3.wav\n", | |
"ravegen_stabs3_Mayday_C3.wav\n", | |
"...\n", | |
"```" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"id": "ab24f59a", | |
"metadata": {}, | |
"source": [ | |
"## Run\n", | |
"\n", | |
"1. Select Output dir" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 16, | |
"id": "5f0cf278", | |
"metadata": { | |
"scrolled": true | |
}, | |
"outputs": [], | |
"source": [ | |
"var outDir = askDirectory(\"Select Audio Save Directory\", \"Select\")" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"id": "5da84729", | |
"metadata": {}, | |
"source": [ | |
"2. Find embedded samples and extract them to outDir" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 294, | |
"id": "ae504231", | |
"metadata": { | |
"scrolled": false | |
}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"found Sample::adjustRootNote at 001201a0 len 904\n", | |
"found Sample::pathToBuffer at 00120b20 len 2331\n", | |
"---\n", | |
"got root note: :/Resources/sounds/instrus/Get Up.wav -> G#2\n", | |
"got root note: :/Resources/sounds/stabs3/Hardcore Hoover.wav -> D#2\n", | |
"got root note: :/Resources/sounds/stabs2/HappyHardcoreStab.wav -> D#3\n", | |
"got root note: :/Resources/sounds/stabs2/BizarreInc1.wav -> E3\n", | |
"got root note: :/Resources/sounds/stabs2/FidelFattiPiano.wav -> D3\n", | |
"got root note: :/Resources/sounds/stabs2/Awesome3.wav -> G#3\n", | |
"got root note: :/Resources/sounds/instrus/Pizzicato Dance.wav -> F#3\n", | |
"got root note: :/Resources/sounds/stabs2/Dist Brazil.wav -> A#2\n", | |
"got root note: :/Resources/sounds/instrus/Mayday Dream.wav -> D3\n", | |
"got root note: :/Resources/sounds/instrus/Rave CutBass.wav -> D4\n", | |
"got root note: :/Resources/sounds/instrus/Juno Bass.wav -> F4\n", | |
"got root note: :/Resources/sounds/stabs2/Short Rave.wav -> D#3\n", | |
"got root note: :/Resources/sounds/instrus/Pump Bass.wav -> D3\n", | |
"got root note: :/Resources/sounds/stabs3/Break Boys.wav -> F#3\n", | |
"got root note: :/Resources/sounds/stabs3/CLS.wav -> D3\n", | |
"got root note: :/Resources/sounds/stabs3/CZorgan Alex Party.wav -> C2\n", | |
"got root note: :/Resources/sounds/stabs3/SL2.wav -> D#3\n", | |
"got root note: :/Resources/sounds/stabs3/Synth15 Stab.wav -> A#2\n", | |
"got root note: :/Resources/sounds/stabs3/Week End.wav -> D#3\n", | |
"got root note: :/Resources/sounds/stabs3/Wavez.wav -> C4\n", | |
"got root note: :/Resources/sounds/stabs3/Tremoradelterra.wav -> C#3\n", | |
"got root note: :/Resources/sounds/voices/Patti.wav -> C#3\n", | |
"---\n", | |
"exporting sample at addr=002cce28 len=30294 root=G#3 -> ravegen_stabs2_Awesome3_G#3.wav\n", | |
"exporting sample at addr=002d4482 len=40244 root=E3 -> ravegen_stabs2_BizarreInc1_E3.wav\n", | |
"exporting sample at addr=002de1ba len=32212 root=C3 -> ravegen_stabs2_Black Riot 2_C3.wav\n", | |
"exporting sample at addr=002e5f92 len=30812 root=C3 -> ravegen_stabs2_Cubic22_C3.wav\n", | |
"exporting sample at addr=002ed7f2 len=43796 root=C3 -> ravegen_stabs2_DeeLiteOrgan_C3.wav\n", | |
"exporting sample at addr=002f830a len=19828 root=A#2 -> ravegen_stabs2_Dist Brazil_A#2.wav\n", | |
"exporting sample at addr=002fd082 len=65482 root=C3 -> ravegen_stabs2_DJ Professor_C3.wav\n", | |
"exporting sample at addr=0030d050 len=28110 root=C3 -> ravegen_stabs2_Enjoy_C3.wav\n", | |
"exporting sample at addr=00313e22 len=51416 root=D3 -> ravegen_stabs2_FidelFattiPiano_D3.wav\n", | |
"exporting sample at addr=003206fe len=32918 root=D#3 -> ravegen_stabs2_HappyHardcoreStab_D#3.wav\n", | |
"exporting sample at addr=00328798 len=42760 root=C3 -> ravegen_stabs2_Hit House 2_C3.wav\n", | |
"exporting sample at addr=00332ea4 len=59320 root=C3 -> ravegen_stabs2_Landlord 2_C3.wav\n", | |
"exporting sample at addr=00341660 len=39944 root=C3 -> ravegen_stabs2_Poltergeist_C3.wav\n", | |
"exporting sample at addr=0034b26c len=53904 root=C3 -> ravegen_stabs2_Powerrr_C3.wav\n", | |
"exporting sample at addr=00358500 len=48926 root=C3 -> ravegen_stabs2_Rave Action_C3.wav\n", | |
"exporting sample at addr=00364422 len=28178 root=C3 -> ravegen_stabs2_RedTwo_C3.wav\n", | |
"exporting sample at addr=0036b238 len=16140 root=D#3 -> ravegen_stabs2_Short Rave_D#3.wav\n", | |
"exporting sample at addr=0036f148 len=41556 root=C3 -> ravegen_stabs2_SpeedSoul_C3.wav\n", | |
"exporting sample at addr=003793a0 len=28750 root=C3 -> ravegen_stabs2_Sweat_C3.wav\n", | |
"exporting sample at addr=003803f2 len=31488 root=C3 -> ravegen_stabs2_Take On Higher_C3.wav\n", | |
"exporting sample at addr=00387ef6 len=81388 root=C3 -> ravegen_stabs2_Toxic Two_C3.wav\n", | |
"exporting sample at addr=0039bce6 len=46420 root=C3 -> ravegen_stabs2_Wave Of Future_C3.wav\n", | |
"exporting sample at addr=003a723e len=25564 root=C3 -> ravegen_stabs2_Wild Child1_C3.wav\n", | |
"exporting sample at addr=003ad61e len=83632 root=C3 -> ravegen_stabs2_Wild Child2_C3.wav\n", | |
"exporting sample at addr=003c1cd2 len=42068 root=C3 -> ravegen_stabs2_Wild Child3_C3.wav\n", | |
"exporting sample at addr=003cc12a len=39688 root=C3 -> ravegen_stabs2_Zentral_C3.wav\n", | |
"exporting sample at addr=003d5c36 len=11142 root=C3 -> ravegen_stabs3_Belgian Rave_C3.wav\n", | |
"exporting sample at addr=003d87bc len=27700 root=F#3 -> ravegen_stabs3_Break Boys_F#3.wav\n", | |
"exporting sample at addr=003df3f0 len=21716 root=D3 -> ravegen_stabs3_CLS_D3.wav\n", | |
"exporting sample at addr=003e48c4 len=13910 root=C3 -> ravegen_stabs3_Cool Stab_C3.wav\n", | |
"exporting sample at addr=003e7f1a len=121920 root=C2 -> ravegen_stabs3_CZorgan Alex Party_C2.wav\n", | |
"exporting sample at addr=00405b5a len=37256 root=C3 -> ravegen_stabs3_Distorgan2_C3.wav\n", | |
"exporting sample at addr=0040ece2 len=35870 root=C3 -> ravegen_stabs3_Expansion_C3.wav\n", | |
"exporting sample at addr=00417900 len=50834 root=C3 -> ravegen_stabs3_Gabbers_C3.wav\n", | |
"exporting sample at addr=00423f92 len=81494 root=D#2 -> ravegen_stabs3_Hardcore Hoover_D#2.wav\n", | |
"exporting sample at addr=00437de8 len=64104 root=C3 -> ravegen_stabs3_Hound Stab_C3.wav\n", | |
"exporting sample at addr=00447850 len=22832 root=C3 -> ravegen_stabs3_House Nation_C3.wav\n", | |
"exporting sample at addr=0044d180 len=29140 root=C3 -> ravegen_stabs3_Magic Feet_C3.wav\n", | |
"exporting sample at addr=00454354 len=21716 root=C3 -> ravegen_stabs3_Mayday_C3.wav\n", | |
"exporting sample at addr=00459828 len=35300 root=C3 -> ravegen_stabs3_Organonox_C3.wav\n", | |
"exporting sample at addr=0046220c len=27668 root=C3 -> ravegen_stabs3_Party Children_C3.wav\n", | |
"exporting sample at addr=00468e20 len=31314 root=C3 -> ravegen_stabs3_Piano Chord_C3.wav\n", | |
"exporting sample at addr=00470872 len=30998 root=C3 -> ravegen_stabs3_Rotterdam Hoover_C3.wav\n", | |
"exporting sample at addr=00478188 len=28246 root=D#3 -> ravegen_stabs3_SL2_D#3.wav\n", | |
"exporting sample at addr=0047efde len=42450 root=A#2 -> ravegen_stabs3_Synth15 Stab_A#2.wav\n", | |
"exporting sample at addr=004895b0 len=2532 root=C3 -> ravegen_stabs3_Techno8_C3.wav\n", | |
"exporting sample at addr=00489f94 len=17684 root=C#3 -> ravegen_stabs3_Tremoradelterra_C#3.wav\n", | |
"exporting sample at addr=0048e4a8 len=27876 root=C3 -> ravegen_stabs3_Twilight Bep_C3.wav\n", | |
"exporting sample at addr=0049518c len=9169 root=C4 -> ravegen_stabs3_Wavez_C4.wav\n", | |
"exporting sample at addr=0049755d len=22292 root=D#3 -> ravegen_stabs3_Week End_D#3.wav\n", | |
"exporting sample at addr=0013ac0c len=64188 root=C3 -> ravegen_instrus_Get Up Bass_C3.wav\n", | |
"exporting sample at addr=0014a6c8 len=81494 root=C3 -> ravegen_instrus_Hardcore Hoover_C3.wav\n", | |
"exporting sample at addr=0015e51e len=313430 root=C3 -> ravegen_instrus_Hi Voltage_C3.wav\n", | |
"exporting sample at addr=001aad74 len=21458 root=F4 -> ravegen_instrus_Juno Bass_F4.wav\n", | |
"exporting sample at addr=001b0146 len=276600 root=C3 -> ravegen_instrus_Juno Hoover_C3.wav\n", | |
"exporting sample at addr=001f39be len=4106 root=C3 -> ravegen_instrus_Maniak Techno_C3.wav\n", | |
"exporting sample at addr=001f49c8 len=62032 root=D3 -> ravegen_instrus_Mayday Dream_D3.wav\n", | |
"exporting sample at addr=00203c18 len=4660 root=C3 -> ravegen_instrus_Omnibus_C3.wav\n", | |
"exporting sample at addr=00204e4c len=161340 root=F#3 -> ravegen_instrus_Pizzicato Dance_F#3.wav\n", | |
"exporting sample at addr=0022c488 len=30722 root=C3 -> ravegen_instrus_PsychotropiCZ_C3.wav\n", | |
"exporting sample at addr=00233c8a len=42900 root=D3 -> ravegen_instrus_Pump Bass_D3.wav\n", | |
"exporting sample at addr=0023e41e len=13236 root=D4 -> ravegen_instrus_Rave CutBass_D4.wav\n", | |
"exporting sample at addr=002417d2 len=11460 root=C3 -> ravegen_instrus_Rave Cycle_C3.wav\n", | |
"exporting sample at addr=00244496 len=95388 root=C3 -> ravegen_instrus_Square Bass_C3.wav\n", | |
"exporting sample at addr=0025b932 len=399228 root=C3 -> ravegen_jx1_JX1 C4+C5_C3.wav\n", | |
"exporting sample at addr=002bd0ae len=57734 root=C3 -> ravegen_instrus_m1organ_C3.wav\n", | |
"exporting sample at addr=005b45d0 len=341316 root=C3 -> ravegen_voices_Acieed_C3.wav\n", | |
"exporting sample at addr=00607b14 len=161370 root=C3 -> ravegen_voices_Ayeaaaa First Choice_C3.wav\n", | |
"exporting sample at addr=0062f16e len=36916 root=C3 -> ravegen_voices_Charly_C3.wav\n", | |
"exporting sample at addr=006381a2 len=139988 root=C3 -> ravegen_voices_Dish you_C3.wav\n", | |
"exporting sample at addr=0065a476 len=110420 root=C3 -> ravegen_voices_Energize_C3.wav\n", | |
"exporting sample at addr=006753ca len=30510 root=C3 -> ravegen_voices_Fast_C3.wav\n", | |
"exporting sample at addr=0067caf8 len=28238 root=C3 -> ravegen_voices_Go 2_C3.wav\n", | |
"exporting sample at addr=00683946 len=180912 root=C3 -> ravegen_voices_Godftaher The Joint_C3.wav\n", | |
"exporting sample at addr=006afbf6 len=317526 root=C3 -> ravegen_voices_jieeeeeeeaaaaaaaah_C3.wav\n", | |
"exporting sample at addr=006fd44c len=199412 root=C3 -> ravegen_voices_Kick Out The Jam_C3.wav\n" | |
] | |
}, | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"exporting sample at addr=0072df40 len=45268 root=C3 -> ravegen_voices_Loon_C3.wav\n", | |
"exporting sample at addr=00739014 len=171046 root=C3 -> ravegen_voices_Neneh Cherry_C3.wav\n", | |
"exporting sample at addr=00762c3a len=196436 root=C3 -> ravegen_voices_ooooooooh_C3.wav\n", | |
"exporting sample at addr=00792b8e len=123990 root=C3 -> ravegen_voices_ooooooooh2_C3.wav\n", | |
"exporting sample at addr=007b0fe4 len=318180 root=C3 -> ravegen_voices_Party_C3.wav\n", | |
"exporting sample at addr=007feac8 len=30164 root=C3 -> ravegen_voices_Paw_C3.wav\n", | |
"exporting sample at addr=0080609c len=280496 root=C#3 -> ravegen_voices_Patti_C#3.wav\n", | |
"done\n" | |
] | |
} | |
], | |
"source": [ | |
"import GhidraJupyterKotlin.extensions.address.*\n", | |
"\n", | |
"import ghidra.program.model.listing.Function\n", | |
"import ghidra.program.model.address.GenericAddress\n", | |
"import ghidra.program.model.address.AddressSet\n", | |
"import ghidra.program.model.data.WAVEDataType\n", | |
"import ghidra.program.model.data.Playable\n", | |
"import ghidra.app.util.exporter.BinaryExporter\n", | |
"import org.apache.commons.io.FilenameUtils\n", | |
"import java.io.File\n", | |
"\n", | |
"data class RGSample(var fileName: String? = null,\n", | |
" var resourceName: String? = null,\n", | |
" var waveAddr: GenericAddress? = null,\n", | |
" var rootNote: String? = null) {\n", | |
" fun valid(): Boolean {\n", | |
" return fileName != null && resourceName != null && waveAddr != null\n", | |
" }\n", | |
"}\n", | |
"\n", | |
"fun getRootNoteString(arg: Long): String {\n", | |
" // Sample::setRootNote(int) pitches the note up or down by the given number of semitones away from 60 (= C3)\n", | |
" // written as python:\n", | |
" // factor = 1.0\n", | |
" // if note > 60:\n", | |
" // for i in range(note - 60):\n", | |
" // factor /= 1.059463094359 # <- google that factor, it's the frequency ratio between to adjacent semitones\n", | |
" // elif note < 60:\n", | |
" // for i in range(60 - note):\n", | |
" // factor *= 1.059463094359\n", | |
"\n", | |
" // convert that number to a readable note, assuming C3=60, B2=59, C#3=61,...\n", | |
" val octave = (arg / 12).toInt() - 2\n", | |
" val semitone = (arg % 12).toInt()\n", | |
" val notes = arrayOf(\"C\", \"C#\", \"D\", \"D#\", \"E\", \"F\", \"F#\", \"G\", \"G#\", \"A\", \"A#\", \"B\")\n", | |
" \n", | |
" return \"${notes[semitone]}${octave}\"\n", | |
"}\n", | |
"\n", | |
"fun run() {\n", | |
" val debugPathToBuffer = false\n", | |
" val debugAdjustRootNote = false\n", | |
" val infoAdjustRootNote = true\n", | |
" val fileNamePrefix = \"ravegen_\"\n", | |
" \n", | |
" if (outDir == null || !outDir.isDirectory()) {\n", | |
" println(\"select output dir first\")\n", | |
" return\n", | |
" }\n", | |
"\n", | |
" // search functions\n", | |
" var pathToBuffer: Function? = null\n", | |
" var adjustRootNote: Function? = null\n", | |
" \n", | |
" val fm = currentProgram.getFunctionManager()\n", | |
" for (f in fm.getFunctions(true)) {\n", | |
" when (f.name) {\n", | |
" \"pathToBuffer\" -> {\n", | |
" val functionLen = f.body.numAddresses\n", | |
" if (functionLen <= 6) {\n", | |
" // ignore thunk\n", | |
" continue\n", | |
" }\n", | |
" println(\"found ${f} at ${f.entryPoint} len ${functionLen}\")\n", | |
" pathToBuffer = f\n", | |
" }\n", | |
" \"adjustRootNote\" -> {\n", | |
" val functionLen = f.body.numAddresses\n", | |
" if (functionLen <= 6) {\n", | |
" // ignore thunk\n", | |
" continue\n", | |
" }\n", | |
" println(\"found ${f} at ${f.entryPoint} len ${functionLen}\")\n", | |
" adjustRootNote = f\n", | |
" }\n", | |
" }\n", | |
" }\n", | |
" \n", | |
" if (pathToBuffer == null) {\n", | |
" println(\"pathToBuffer not found\")\n", | |
" return\n", | |
" }\n", | |
" \n", | |
" if (adjustRootNote == null) {\n", | |
" println(\"adjustRootNote not found\")\n", | |
" return\n", | |
" }\n", | |
" \n", | |
" println(\"---\")\n", | |
" \n", | |
" // get root notes\n", | |
" val defaultRootNote = getRootNoteString(60)\n", | |
" val rootNotes = hashMapOf<String, String>()\n", | |
" var currentResource = \"\"\n", | |
" \n", | |
" // parse adjustRootNote.\n", | |
" // that function contains some mappings from sample resource names to different root notes\n", | |
" // (default is probably C3 or something).\n", | |
" // we want to translate those to readable notes like C/D/E.. to add to the output file name.\n", | |
" for (codeUnit in currentProgram.listing.getCodeUnits(adjustRootNote.body, true)) {\n", | |
" if (monitor.isCancelled) {\n", | |
" return\n", | |
" }\n", | |
" \n", | |
" val mnem = codeUnit.mnemonicString\n", | |
" if (mnem == \"LEA\") {\n", | |
" // resource name loading is the same as in pathToBuffer\n", | |
" if (debugAdjustRootNote) {\n", | |
" println(\"${codeUnit.address}: $codeUnit\")\n", | |
" }\n", | |
" \n", | |
" if (currentResource != \"\") {\n", | |
" println(\"ERROR: we missed root note data\")\n", | |
" }\n", | |
" \n", | |
" // load resource name\n", | |
" val resourceNameAddr = codeUnit.getAddress(1)\n", | |
" val resourceName = getDataAt(resourceNameAddr).getValue() as String\n", | |
" currentResource = resourceName\n", | |
" } else if (mnem == \"JZ\") {\n", | |
" if (debugAdjustRootNote) {\n", | |
" println(\"${codeUnit.address}: $codeUnit\")\n", | |
" }\n", | |
" \n", | |
" // follow the jump target for the case of the resource name matching\n", | |
" val jmpTarget = codeUnit.getAddress(0)\n", | |
" for (nestedCU in currentProgram.listing.getCodeUnits(jmpTarget, true)) {\n", | |
" if (debugAdjustRootNote) {\n", | |
" println(\"NEST ${nestedCU.address}: $nestedCU\")\n", | |
" }\n", | |
"\n", | |
" if (nestedCU.mnemonicString.startsWith(\"J\")) {\n", | |
" // stop at the jump back to avoid cycles\n", | |
" break\n", | |
" } else if (nestedCU.toString().startsWith(\"MOV ESI,\")) {\n", | |
" if (currentResource == \"\") {\n", | |
" println(\"ERROR: we missed the root note name\")\n", | |
" }\n", | |
"\n", | |
" // ex: MOV ESI,0x38\n", | |
" // get argument to call of Sample::setRootNote()\n", | |
" val rootNoteRaw = nestedCU.getScalar(1).getValue()\n", | |
" val rootNote = getRootNoteString(rootNoteRaw)\n", | |
"\n", | |
" // write root note\n", | |
" if (debugAdjustRootNote || infoAdjustRootNote) {\n", | |
" println(\"got root note: ${currentResource} -> ${rootNote}\")\n", | |
" }\n", | |
"\n", | |
" rootNotes[currentResource] = rootNote\n", | |
" currentResource = \"\"\n", | |
" }\n", | |
" }\n", | |
" } else if (mnem == \"POP\" && codeUnit.toString() == \"POP RBP\") {\n", | |
" if (debugAdjustRootNote) {\n", | |
" println(\"end of function\")\n", | |
" }\n", | |
" break\n", | |
" }\n", | |
" }\n", | |
" \n", | |
" // get sample references\n", | |
" val samples = mutableListOf<RGSample>()\n", | |
" var currentSample = RGSample()\n", | |
" \n", | |
" // parse asm listing of pathToBuffer.\n", | |
" // pathToBuffer contains something like a QResource lookup table where resource names like\n", | |
" // \":/Resources/foo/bar.wav\" are mapped to memory references to content of the bundled file.\n", | |
" for (codeUnit in currentProgram.listing.getCodeUnits(pathToBuffer.body, true)) {\n", | |
" if (monitor.isCancelled) {\n", | |
" return\n", | |
" }\n", | |
" \n", | |
" val mnem = codeUnit.mnemonicString\n", | |
" if (mnem == \"LEA\") {\n", | |
" // load resource name\n", | |
" val resourceNameAddr = codeUnit.getAddress(1)\n", | |
" val resourceName = getDataAt(resourceNameAddr).getValue() as String\n", | |
" currentSample.resourceName = resourceName\n", | |
" currentSample.fileName = resourceName.removePrefix(\":/Resources/\")\n", | |
" \n", | |
" if (debugPathToBuffer) {\n", | |
" println(\"${codeUnit.address}: ${resourceNameAddr} ${resourceName}\")\n", | |
" }\n", | |
" } else if (mnem.contains(\"MOV\") && codeUnit.toString().contains(\" RAX,\")) {\n", | |
" // set function return value, probably\n", | |
" val wavePtr = if (mnem.startsWith(\"CMOV\")) {\n", | |
" getDataAt(codeUnit.getAddress(1)).getValue()\n", | |
" } else {\n", | |
" codeUnit.getAddress(0)\n", | |
" } as GenericAddress\n", | |
" \n", | |
" currentSample.waveAddr = wavePtr\n", | |
" currentSample.rootNote = if (currentSample.resourceName in rootNotes) {\n", | |
" rootNotes[currentSample.resourceName]\n", | |
" } else {\n", | |
" defaultRootNote\n", | |
" }\n", | |
" // this sample is done\n", | |
" samples.add(currentSample)\n", | |
" \n", | |
" if (debugPathToBuffer) {\n", | |
" println(\"${codeUnit.address}: wave address ${wavePtr}\")\n", | |
" }\n", | |
" \n", | |
" // next sample\n", | |
" currentSample = RGSample()\n", | |
" }\n", | |
" }\n", | |
" \n", | |
" println(\"---\")\n", | |
" // export sound files with nice names\n", | |
" val binaryExporter = BinaryExporter()\n", | |
" for (sample in samples) {\n", | |
" if (monitor.isCancelled) {\n", | |
" return\n", | |
" }\n", | |
" \n", | |
" // sanity checks\n", | |
" if (!sample.valid()) {\n", | |
" println(\"invalid RGSample\")\n", | |
" continue\n", | |
" }\n", | |
" \n", | |
" val wave = getDataAt(sample.waveAddr)\n", | |
" if (wave.getValue() !is Playable) {\n", | |
" println(\"got unplayable wave\")\n", | |
" continue\n", | |
" }\n", | |
" \n", | |
" // get wave byte range\n", | |
" val startAddr = sample.waveAddr!!\n", | |
" val addressSet = AddressSet(startAddr, startAddr + wave.getLength())\n", | |
" \n", | |
" // build output file name\n", | |
" // NOTE i'm not sure if the path splitting works on Windows because of the forward slashes\n", | |
" val origDirName = FilenameUtils.getName(FilenameUtils.getPathNoEndSeparator(sample.fileName))\n", | |
" val origFileName = FilenameUtils.removeExtension(FilenameUtils.getName(sample.fileName))\n", | |
" var fileExtension = FilenameUtils.getExtension(sample.fileName)\n", | |
" val fileName = \"${fileNamePrefix}${origDirName}_${origFileName}_${sample.rootNote}.${fileExtension}\"\n", | |
" // this is something like \"ravegen_stabs2_Awesome3_G#3.wav\"\n", | |
" val outputTarget = FilenameUtils.concat(outDir.absolutePath, fileName)\n", | |
" \n", | |
" println(\"exporting sample at addr=${startAddr} len=${wave.getLength()} root=${sample.rootNote} -> ${fileName}\")\n", | |
"\n", | |
" // write to output file\n", | |
" val outputFile = File(outputTarget)\n", | |
" if (outputFile.exists()) {\n", | |
" println(\"output file already exists, skipping\")\n", | |
" continue\n", | |
" }\n", | |
" \n", | |
" binaryExporter.export(outputFile, currentProgram, addressSet, monitor)\n", | |
" }\n", | |
" println(\"done\")\n", | |
"}\n", | |
"\n", | |
"run()" | |
] | |
} | |
], | |
"metadata": { | |
"kernelspec": { | |
"display_name": "Ghidra(Kotlin)", | |
"language": "kotlin", | |
"name": "ghidra-kotlin" | |
}, | |
"language_info": { | |
"codemirror_mode": "text/x-kotlin", | |
"file_extension": ".kt", | |
"mimetype": "text/x-kotlin", | |
"name": "kotlin", | |
"nbconvert_exporter": "", | |
"pygments_lexer": "kotlin", | |
"version": "1.6.0" | |
} | |
}, | |
"nbformat": 4, | |
"nbformat_minor": 5 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment