Skip to content

Instantly share code, notes, and snippets.

@marccane
Last active April 2, 2022 15:50
Show Gist options
  • Save marccane/9878aece8e9e3c9571104d3c5663e140 to your computer and use it in GitHub Desktop.
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.
#!/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