Last active
April 2, 2022 15:50
-
-
Save marccane/9878aece8e9e3c9571104d3c5663e140 to your computer and use it in GitHub Desktop.
Script to extract 3DS PICA200 shaders from (decrypted) binaries which can then be disassembled with aemstro or nihstro. Should work on anything like .cia .3ds .3dsx... Writes each shader in a .shbin file in the current directory.
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
#!/bin/python3 | |
#https://problemkaputt.de/gbatek-3ds-files-video-shaders-dvlb-and-dvob-format.htm | |
import sys, mmap, struct as s | |
magic = b"DVLB" | |
magicDVLE = b'DVLE' | |
magicDVLEInt = s.unpack("<I", magicDVLE)[0] | |
def ph(v): | |
print(hex(v)) | |
def readInt(mm, base, idx): | |
start = base+idx*4 | |
return s.unpack("<I", mm[start:start+4])[0] | |
def isValidDVLB(mm, pos, numDVLEs): | |
print("Found a possible DVLB at 0x%x, validating..." % (pos)) | |
if numDVLEs <= 0: | |
return False | |
for j in range(numDVLEs): | |
DVLEStart = pos + readInt(mm, pos, 2+j) | |
if DVLEStart < pos or DVLEStart-pos > 0x4000: #TODO: research this value further, biggest seen: 0x2038 (Art of Balance TOUCH!) | |
print("DVLB validation failed: DVLE is at offset = %s" % hex(DVLEStart-pos), file=sys.stderr) | |
return False | |
#print("DEBUG: Valid DVLE offset %s" % hex(DVLEStart-pos)) | |
DVLEHeader = readInt(mm, DVLEStart, 0) | |
if DVLEHeader != magicDVLEInt: | |
print("DVLB validation failed: DVLE has invalid magic", file=sys.stderr) | |
return False | |
shaderType = mm[DVLEStart+6] | |
if shaderType > 1: | |
print("DVLB validation failed: DVLE has invalid shaderType %d" % (shaderType), file=sys.stderr) | |
return False | |
return True | |
if len(sys.argv) != 2: | |
print("You need to pass a binary file to search shaders from", file=sys.stderr) | |
sys.exit(1) | |
filename = '.'.join(sys.argv[1].split('/')[-1].split('.')[0:-1]) | |
with open(sys.argv[1], "rb+") as f: | |
mm = mmap.mmap(f.fileno(), 0) | |
i=1 | |
pos = mm.find(magic, 0) | |
while(pos != -1): | |
numDVLEs=readInt(mm, pos, 1) | |
if isValidDVLB(mm, pos, numDVLEs): | |
shaderFilename = "%s_shader%d.shbin" % (filename, i) | |
print("Valid DVLB %d at 0x%x. Saving to current folder as %s" % (i, pos, shaderFilename)) | |
lastDVLEStart = pos + readInt(mm, pos, 2+ numDVLEs-1) | |
SymTableOff = readInt(mm, lastDVLEStart, 14) | |
SymTableSize = readInt(mm, lastDVLEStart, 15) | |
endOffset = lastDVLEStart+SymTableOff+SymTableSize | |
with open(shaderFilename, "wb") as shaderFile: | |
shaderFile.write(mm[pos:endOffset]) | |
i+=1 | |
pos = mm.find(magic, endOffset) | |
else: | |
pos = mm.find(magic, pos+1) | |
if i == 1: | |
print("No shaders found in %s. The binary may be encrypted" % sys.argv[1], file=sys.stderr) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment