import os from mippy_fast import MIPS2ASM from struct import unpack from sys import argv #Thanks for ZZT32 for these actor_types={ 0 : "type 0", 1 : "1 (Prop)", 2 : "type 2", 3 : "3 (Bomb)", 4 : "4 (NPC)", 5 : "5 (Enemy)", 6 : "6 (Prop)", 7 : "7 (Item/Action)", 8 : "8 (Miscellaneous)", 9 : "9 (Boss)", 10 : "type 10", 11 : "11 (Door?)", 12 : "type 12", 13 : "type 13", 14 : "type 14", 15 : "type 15" } #Thanks to MNGoldenEagle for the original version of the following list #TODO: map these out for MM and gigure out MM skyboxes skybox_types={ 0x00: "Black", 0x01: "Environment color (or blue skies)", 0x02: "Hylian Bazaar", 0x03: "Environment color (?)", 0x04: "Crashes/Market Ruins", 0x05: "Dark and cloudy but offset so it doesn't depend on having a ground plane, skybox rotates quickly", 0x06: "Environment color, possibly lighter? (or mildly pink)", 0x07: "Crashes/Link's House", 0x08: "Environment color (slightly lighter)", 0x09: "Crashes/Market", 0x0A: "Crashes/Market (Night)", 0x0B: "Happy Mask Shop; two sides of the skybox contain corrupt textures", 0x0C: "Crashes/Know-It-All Brothers' House", 0x0D: "Environment color (?)", 0x0E: "Kokiri Twins' House; all sides except back are visible, which is typical", 0x0F: "Crashes/Stable", 0x10: "Crashes/Stew Lady's House", 0x11: "Kokiri Shop", 0x12: "Environment color (?)", 0x13: "Goron Shop", 0x14: "Zora Shop", 0x15: "Environment color (?)", 0x16: "Kakariko Potions Shop", 0x17: "Hylian Potions Shop", 0x18: "Bomb Shop", 0x19: "Environment color (?)", 0x1A: "Crashes/Dog Lady's House", 0x1B: "Crashes/Impa's House", 0x1C: "Gerudo Tent", 0x1D: "Environment color, but the color is brighter (or a bluish gray)", 0x1E: "Environment color (?)", 0x1F: "Environment color (?)", 0x20: "Mido's House", 0x21: "Saria's House", 0x22: "Dog Guy's House" } def filterNonAscii(string): ret = "" for c in string: ordc = ord(c) if ordc > 0x7F or ordc < 32: ret+='.' elif c == ">": ret+=">" elif c == "<": ret+="<" elif c == "&": ret+="&" else: ret+=c return ret def COLLISION_TO_OBJ(infile,outfile,f_off,mdh_off): fopmo=f_off+mdh_off infile.seek(fopmo+12) ncverts = unpack(">H",infile.read(2))[0] infile.seek(fopmo+16) cvoff = (unpack(">L",infile.read(4))[0]&0xFFFFFF)+f_off nctris = unpack(">H",infile.read(2))[0] infile.seek(fopmo+24) ctoff = (unpack(">L",infile.read(4))[0]&0xFFFFFF)+f_off+2 fstr='#Exported from Zelda 64 formatted collision' infile.seek(cvoff) for i in range(ncverts): xyz=unpack(">hhh",infile.read(6)) fstr+="\nv %i %i %i"%xyz for i in range(nctris): infile.seek(ctoff) a,b,c=unpack(">HHH",infile.read(6)) a,b,c=(a&0xFFF)+1,(b&0xFFF)+1,(c&0xFFF)+1 fstr+="\nf %i %i %i"%(a,b,c) ctoff+=16 outfile.write(fstr) def hex2html(string,outfilename,title,PC): out=open(outfilename,"w") out.write('''
'''%title) for i in range(0,len(string),16): out.write("\n%08X | %08X %08X %08X %08X %s"%(PC,unpack(">L",string[i:i+4])[0],unpack(">L",string[i+4:i+8])[0],unpack(">L",string[i+8:i+12])[0],unpack(">L",string[i+12:i+16])[0],filterNonAscii(string[i:i+16]))) PC+=16 out.write('''up''') out.close def str2html(string,outfilename,title): out=open(outfilename,"w") out.write('''
%sup'''%(title,string)) out.close() def replace_zeros(string): ret='' for c in string: if c=='\x00': ret+=' ' else: ret+=c return ret def find_filetable(f,gbi=False): """Returns filetable offset if found, else None""" ret=None f.seek(0) for i in range(0,0x20000,16): DD=f.read(16) if len(DD.split("@srd"))==2: off=f.tell() if gbi==True: f.seek(f.tell()-16) gbi_=replace_zeros(f.read(0x30)) while gbi_[-1]==' ': gbi_=gbi_[:-1] break for i in range(0,0x80,16): off+=16 f.seek(off) D=f.read(8) if unpack(">Q",D)[0]==0x1060: ret = off break if gbi==False: return ret else: return ret, gbi_ def find_nametable(f): """Returns nametable offset if found, else None""" ret=None f.seek(0x1060) off=0x1060 for i in range(0,0x10000,16): off+=16 if unpack(">QQ",f.read(16))== (0x6D616B65726F6D00, 0x626F6F7400000000): ret=off-16 break return ret def find_code(f): """Returns code's offsets if found, else None""" ret=None ft_off=find_filetable(f) for i in range(0, 0x300, 16): f.seek(ft_off+i) vst,ve,pst,pe=unpack(">LLLL",f.read(16)) if pe==0: f.seek(ve-16) if unpack(">QQ",f.read(16))==(0x6A6E8276E707B8E3,0x7D8A471D6A6E18F9): ret=(vst,ve) break return ret def rip_files(f,directory,gbi): """Attempts to rip all files from ROM f to path directory""" if directory[-1]!="/": directory+='/' if os.path.exists(directory)==False: os.mkdir(directory) s_off,gbi_=find_filetable(f,gbi) n_off=find_nametable(f) foffs,fnams=[],[] fcount = 0 if s_off!=None: go=1 while go: f.seek(s_off) vst,ve,pst,pe=unpack(">LLLL",f.read(16)) if vst == ve: break if n_off!=None: name='' while len(name)<1: f.seek(n_off) name,char,oc='','',1 while oc != 0: name+=char char=f.read(1) oc=ord(char) n_off=f.tell() if name[0:4]=="ovl_": name+=".zactor" elif len(name.split("_room"))==2: name+=".zmap" elif name[0:7]=="object_": name+=".zobj" elif name[-6:]=="_scene": name+='.zscene' else: name+='.zdata' else: name="%08X-%08X.zdata"%(vst,ve) f.seek(pst) if pe!=0: #compressed name+=".yaz0" out=open(directory+name,"w") f.seek(pst) out.write(f.read(pe-pst)) else: #decompressed out=open(directory+name,"w") out.write(f.read(ve-vst)) out.close() s_off+=16 foffs.append((vst,ve)) fnams.append(name) fcount+=1 if gbi==False: return fnams, foffs, fcount else: return fnams, foffs, fcount, gbi_ def mk_frep(f,directory,fnams,foffs): if directory[-1]!="/": directory+='/' if os.path.exists(directory)==False: os.mkdir(directory) html=open(directory+'index.html','w') html.write('''
File No\t\tStart (hex)\tEnd (hex)\tSize(kb)\tName''') for i in range(len(fnams)): html.write('''\n%06i\t\t%08X\t%08X\t%s\t\t%s'''%(i,foffs[i][0],foffs[i][1],('%f'%((foffs[i][1]-foffs[i][0])/1024.))[:6],fnams[i],fnams[i].split('.')[0])) html.write('''up ''') html.close() #0xC74560 def mk_srep(f,directory): if directory[-1]!="/": directory+='/' if os.path.exists(directory)==False: os.mkdir(directory) html=open(directory+'index.html','w') html.write('''
Scene No\tStart (hex)\tEnd (hex)\tSize(kb)\tName''') home = 0xC5A250 snoff = 0xC74560 for i in range(102): f.seek(home) s,e,ts,te=unpack(">LLLL",f.read(16)) f.seek(snoff) c = f.read(1) fn = "" while (c != "\x00"): fn+=c c = f.read(1) snoff+=1 while (c == "\x00"): c = f.read(1) snoff+=1 if s == 0: html.write('''\n%03i (0x%02X)\t%08X\t%08X\t0.0000\t\t%s'''%(i,i,s,e,fn)) else: html.write('''\n%03i (0x%02X)\t%08X\t%08X\t%s\t\t%s'''%(i,i,s,e,('%f'%((e-s)/1024.))[:6],i,fn)) mk_s(f, directory+"%02X/"%i, "../../"+fn, fn.split(".")[0],(s,e),i,fn) home+=16 html.write('''up ''') html.close() def mk_s(f,directory,s_path,s_name,s_offs,s_number,name): if directory[-1]!="/": directory+='/' if os.path.exists(directory)==False: os.mkdir(directory) html=open(directory+'index.html','w') html.write('''
Name: %08X-%08X Number: 0x%04X Offsets: %08X - %08X'''%(s_number,s_name,s_number,s_name,s_offs[0],s_offs[1],s_offs[0],s_offs[1],s_number,s_offs[0],s_offs[1])) f.seek(s_offs[0]) header_offs=[0] f.seek(s_offs[0]) if unpack(">B",f.read(1))[0]==0x18: f.seek(s_offs[0]+4) o_=unpack(">L",f.read(4))[0] f.seek(s_offs[0]+(o_&0xFFFFFF)) while 1: w=unpack(">L",f.read(4))[0] if w>>24 == 2: header_offs.append(w&0xFFFFFF) else: break header_offs[0]=8 html.write("\n%i object set"%len(header_offs)) if len(header_offs)>1: html.write("s") j=0 collision_made=1 map_made=1 for offset in header_offs: html.write("\nObject set %i:"%j) bo=s_offs[0]+offset while 1: f.seek(bo) w=unpack(">BBBBL",f.read(8)) if w[0]==0x15: html.write("\n Music: 0x%02X"%(w[4]&0xFF)) elif w[0]==0: html.write("\n %03i Link setups:"%w[1]) f.seek(s_offs[0]+(w[4]&0xFFFFFF)) for i in range(w[1]): li=unpack(">HhhhhhhH",f.read(16)) html.write("""\n Link setup %03i:\n Actor: 0x%04X\n Position: %i, %i, %i\n Rotation: %f\xb0, %f\xb0, %f\xb0\n Variable: 0x%04X""" % (i,li[0],li[1],li[2],li[3],li[4]/182.044444444,li[5]/182.04444444,li[6]/182.044444444,li[7])) elif w[0]==3: if collision_made: sc_obj=open("%s%s.obj"%(directory,s_name),"w") COLLISION_TO_OBJ(f,sc_obj,s_offs[0],w[4]&0xFFFFFF) collision_made=0 html.write('\n Collision data offset: %08X\n Collision as object'%(w[4]&0xFFFFFF,s_name)) elif w[0]==0xE: html.write("\n %03i door setups:"%w[1]) f.seek(s_offs[0]+(w[4]&0xFFFFFF)) for i in range(w[1]): li=unpack(">BBBBHhhhhH",f.read(16)) html.write("""\n Door setup %03i:\n From front\n Room to: %i\n Camera Variable: 0x%02X\n From rear\n Room to: %i\n Camera Variable: 0x%02X\n Actor number: 0x%04X\n Position: %i, %i, %i\n Rotation: %f\xb0\n Variable: %04X""" % (i, li[0],li[1],li[2],li[3],li[4],li[4],li[5],li[6],li[7],li[8]/182.04444444444, li[9])) elif w[0]==0x4: html.write("\n %03i Maps:"%w[1]) mpoff=s_offs[0]+(w[4]&0xFFFFFF) for i in range(w[1]): f.seek(mpoff) m_offs=unpack(">LL",f.read(8)) m_name="%08X-%08X"%m_offs if map_made==1: m_path="../../%s"%(m_name) m_dir="%sm%03i"%(directory,i) mk_m(f,m_dir,m_name,m_path,m_offs) map_made=0 html.write('\n Map %03i\n Map offsets: %08X - %08X\n Map: %s' % (i,m_offs[0],m_offs[1],i,m_name)) mpoff+=8 # elif w[0]==0x11: # if w[4]&0x100: # html.write("\n Skybox: disabled") # else: # skybox_type=w[4]>>24 # html.write("\n Skybox: enabled, 0x%02X (%s)"%(skybox_type,skybox_types[skybox_type])) elif w[0]==0x14: break bo+=8 j+=1 html.write('''\n up\n \n''') html.close() def mk_m(f,directory,m_name,m_path,m_offs): if directory[-1]!="/": directory+='/' if os.path.exists(directory)==False: os.mkdir(directory) html=open(directory+'index.html','w') html.write('''
Name: %s Offsets: %08X - %08X'''%(m_name,m_name,m_path,m_name,m_offs[0],m_offs[1])) f.seek(m_offs[0]) header_offs=[0] f.seek(m_offs[0]) if (unpack(">B",f.read(1))[0] == 0x18): f.seek(m_offs[0]+4) o_=unpack(">L",f.read(4))[0] f.seek(m_offs[0]+(o_&0xFFFFFF)) while 1: w=unpack(">L",f.read(4))[0] if w>>24 == 3: header_offs.append(w&0xFFFFFF) else: break header_offs[0]=8 html.write("\n%i object set"%len(header_offs)) if len(header_offs)>1: html.write("s") j=0 for offset in header_offs: html.write("\nObject set %i:"%j) bo=m_offs[0]+offset while 1: f.seek(bo) w=unpack(">BBBBL",f.read(8)) if w[0]==0x16: html.write("\n Echo: 0x%02X" % (w[0]&0xFF)) elif w[0]==0x12: if w[4]>>24: html.write("\n Skybox: disabled (overrides scene)") else: html.write("\n Skybox: enabled (overrides scene)") elif w[0]==0x10: html.write("\n Starting time: 0x%04X\n Time speed: %i"% (w[4]>>16, (w[4]>>8)&0xFF)) elif w[0]==0xA: #Map dump process - Not today pass elif w[0]==0xB: html.write("\n %i objects loaded:"%w[1]) ol_off=(w[4]&0xFFFFFF)+m_offs[0] for x in range(w[1]): f.seek(ol_off) obj=unpack(">H",f.read(2))[0] f.seek(0xC58C80+8*obj) obj_offs=unpack(">LL",f.read(8)) obj_name="%08X-%08X" % obj_offs html.write('\n 0x%04X (%s)'%(obj,obj_name,obj_name.split(".")[0])) ol_off+=2 elif w[0]==1: html.write("\n %i actors:"%w[1]) f.seek((w[4]&0xFFFFFF)+m_offs[0]) for x in range(w[1]): an,ax,ay,az,au,av,aw,av=unpack(">HhhhhhhH",f.read(16)) html.write('\n Actor %i:\n Number: 0x%04X\n Pos: %i, %i, %i\n Rotation: %f\xb0, %f\xb0, %f\xb0\n Variable: 0x%04X'%(x,an&0xFFF,an&0xFFF,ax,ay,az,au/182.044444444,av/182.044444444,aw/182.044444444,av)) elif w[0]==0x14: break bo+=8 j+=1 html.write('''\n up\n \n''') html.close() def mk_arep(f,directory): if directory[-1]!="/": directory+='/' if os.path.exists(directory)==False: os.mkdir(directory) html=open(directory+'index.html','w') html.write('''
Number\t\tStart (hex)\tEnd (hex)\tvStart (hex)\tvEnd (hex)\tObject\tSize(kb)\tName''') offset=0xC45510 for i in range(0x2B2): f.seek(offset) obj_no=0 s,e,vs,ve,u,c,d=unpack(">LLLLLLL",f.read(28)) if s == 0 and c == 0: name = "NULL" elif d == 0: name='(No name)' if e!=0: f.seek(s+(c-vs)+2) act_type=actor_types[unpack(">B",f.read(1))[0]] f.seek(s+(c-vs)+8) obj_no=unpack(">H",f.read(2))[0] if obj_no>0x282: obj_no=0 mk_a(f,directory+"%04X"%i,i,name,(s,e),(vs,ve),obj_no,name,act_type) name='%s'%(i,name) html.write('''\n%04i (0x%04X)\t%08X\t%08X\t%08X\t%08X\t%04X\t%s\t\t%s'''%(i,i,s,e,vs,ve,obj_no,('%f'%((e-s)/1024.))[:6],name)); offset+=0x20 html.write('''up ''') html.close() def mk_a(f,directory,actor_number,act_name,act_offs,act_voffs,obj_no,fname,a_type): if directory[-1]!="/": directory+='/' if os.path.exists(directory)==False: os.mkdir(directory) f.seek(0xC58C80+8*obj_no) obj_offs=unpack(">LL",f.read(8)) obj_name="%08X-%08X.zdata"%obj_offs f.seek(act_offs[0]) hex2html((f.read(act_offs[1]-act_offs[0])),('%sactor_%04X.html'%(directory,actor_number)),('Viewing '+act_name),(act_voffs[0])) f.seek(act_offs[1]-4) hdr_off=unpack(">L",f.read(4))[0] f.seek(act_offs[1]-hdr_off) hdr_data=unpack(">LLLLL",f.read(20)) ops_len=hdr_data[0] f.seek(act_offs[0]) act_asm=f.read(ops_len) str2html(MIPS2ASM(act_asm, ops_len/4,act_voffs[0]),('%sactor_%04X_text.html'%(directory,actor_number)),'Viewing '+act_name+' text block') html=open(directory+'index.html','w') html.write('''
Name:\t\t\t%s Type:\t\t\t%s Physical Offsets:\t%08X - %08X Virtual Offsets:\t%08X - %08X Object Number:\t\t%04X Object Name:\t\t%s Object Offsets:\t\t%08X - %08X Hexadecimal view of actor Disassembled text block
Input file: %s
No. Files: %i
Build info: %s
Scene listing
File listing
Actor listing
Source
up