tp/tools/package_game_assets.py

457 lines
12 KiB
Python

import os
import sys
import shutil
import extract_game_assets
from pathlib import Path
import hashlib
import struct
import ctypes
import oead
def copy(path,destPath):
for root,dirs,files in os.walk(str(path)):
for file in files:
outputDir = destPath/Path(str(root))
#print(str(outputDir.absolute())+file)
if not outputDir.absolute().exists():
os.makedirs(outputDir.absolute())
outputFile = Path(str(outputDir.absolute())+"/"+str(file))
inFile = Path(str(Path(root).absolute())+"/"+str(file))
if not outputFile.exists():
print(str(inFile)+" -> "+str(outputFile))
shutil.copyfile(inFile,outputFile)
else:
if os.path.getmtime(inFile)>os.path.getmtime(outputFile):
print(str(inFile)+" -> "+str(outputFile))
shutil.copyfile(inFile,outputFile)
aMemRels = """d_a_alldie.rel
d_a_andsw2.rel
d_a_bd.rel
d_a_canoe.rel
d_a_cstaf.rel
d_a_demo_item.rel
d_a_door_bossl1.rel
d_a_econt.rel
d_a_e_dn.rel
d_a_e_fm.rel
d_a_e_ga.rel
d_a_e_hb.rel
d_a_e_nest.rel
d_a_e_rd.rel
d_a_fr.rel
d_a_grass.rel
d_a_kytag05.rel
d_a_kytag10.rel
d_a_kytag11.rel
d_a_kytag14.rel
d_a_mg_fish.rel
d_a_npc_besu.rel
d_a_npc_fairy_seirei.rel
d_a_npc_fish.rel
d_a_npc_henna.rel
d_a_npc_kakashi.rel
d_a_npc_kkri.rel
d_a_npc_kolin.rel
d_a_npc_maro.rel
d_a_npc_taro.rel
d_a_npc_tkj.rel
d_a_obj_bhashi.rel
d_a_obj_bkdoor.rel
d_a_obj_bosswarp.rel
d_a_obj_cboard.rel
d_a_obj_digplace.rel
d_a_obj_eff.rel
d_a_obj_fmobj.rel
d_a_obj_gptaru.rel
d_a_obj_hhashi.rel
d_a_obj_kanban2.rel
d_a_obj_kbacket.rel
d_a_obj_kgate.rel
d_a_obj_klift00.rel
d_a_obj_ktonfire.rel
d_a_obj_ladder.rel
d_a_obj_lv2candle.rel
d_a_obj_magne_arm.rel
d_a_obj_metalbox.rel
d_a_obj_mgate.rel
d_a_obj_nameplate.rel
d_a_obj_ornament_cloth.rel
d_a_obj_rope_bridge.rel
d_a_obj_stick.rel
d_a_obj_stonemark.rel
d_a_obj_swallshutter.rel
d_a_obj_swpropeller.rel
d_a_obj_swpush5.rel
d_a_obj_yobikusa.rel
d_a_scene_exit2.rel
d_a_shop_item.rel
d_a_sq.rel
d_a_swc00.rel
d_a_tag_ajnot.rel
d_a_tag_attack_item.rel
d_a_tag_cstasw.rel
d_a_tag_gstart.rel
d_a_tag_hinit.rel
d_a_tag_hjump.rel
d_a_tag_hstop.rel
d_a_tag_lv2prchk.rel
d_a_tag_magne.rel
d_a_tag_mhint.rel
d_a_tag_mstop.rel
d_a_tag_spring.rel
d_a_tag_statue_evt.rel
d_a_ykgr.rel"""
mMemRels = """d_a_andsw.rel
d_a_arrow.rel
d_a_bg.rel
d_a_bg_obj.rel
d_a_boomerang.rel
d_a_crod.rel
d_a_demo00.rel
d_a_disappear.rel
d_a_dmidna.rel
d_a_door_dbdoor00.rel
d_a_door_knob00.rel
d_a_door_shutter.rel
d_a_door_spiral.rel
d_a_dshutter.rel
d_a_ep.rel
d_a_hitobj.rel
d_a_kytag00.rel
d_a_kytag04.rel
d_a_kytag17.rel
d_a_mg_rod.rel
d_a_midna.rel
d_a_nbomb.rel
d_a_obj_brakeeff.rel
d_a_obj_burnbox.rel
d_a_obj_carry.rel
d_a_obj_ito.rel
d_a_obj_life_container.rel
d_a_obj_movebox.rel
d_a_obj_swpush.rel
d_a_obj_timer.rel
d_a_obj_yousei.rel
d_a_path_line.rel
d_a_scene_exit.rel
d_a_set_bgobj.rel
d_a_spinner.rel
d_a_suspend.rel
d_a_swhit0.rel
d_a_tag_allmato.rel
d_a_tag_attention.rel
d_a_tag_camera.rel
d_a_tag_chkpoint.rel
d_a_tag_event.rel
d_a_tag_evt.rel
d_a_tag_evtarea.rel
d_a_tag_evtmsg.rel
d_a_tag_howl.rel
d_a_tag_kmsg.rel
d_a_tag_lantern.rel
d_a_tag_mist.rel
d_a_tag_msg.rel
d_a_tag_push.rel
d_a_tag_telop.rel
d_a_tbox.rel
d_a_tbox2.rel
d_a_vrbox.rel
d_a_vrbox2.rel
f_pc_profile_lst.rel"""
#Because libarc is only geared toward reading from arcs I'm writing my own arc writer in this file
class HEADER:
RARC : int
length : int
headerLength : int
fileDataOffset : int
fileDataLen : int
fileDataLen2 : int
unk1 : int
unk2 : int
class INFO:
numNodes : int
firstNodeOffset : int
totalDirNum : int
firstDirOffset : int
stringTableLen : int
stringTableOffset : int
numDirsThatAreFiles : int
unk1 : int
unk2 : int
class NODE:
NAME : int
stringTableOffset: int
hash : int
numDirs : int
firstDirIndex : int
class DIRECTORY:
dirIndex : int
stringHash : int
type : int
stringOffset : int
fileOffset : int
fileLength : int
unk1 : int
def computeHash(string):
hash = 0
for char in string:
hash = hash*3
hash = hash + ord(char)
hash = ctypes.c_ushort(hash)
hash = hash.value
return hash
def addFile(index,sizeIndex,dirs,name,stringTable,paths,data):
file = DIRECTORY()
file.dirIndex = index
file.stringHash = computeHash(name)
file.type = 0xA500
file.stringOffset = stringTable.find(name)
path = None
for relPath in paths:
if str(relPath).find(name)!=-1:
path = relPath
file.unk1 = 0
fileData = open(path,"rb")
compressedData = oead.yaz0.compress(fileData.read())
padding = (0x20-(len(compressedData)%0x20))
file.fileLength = len(compressedData)
file.fileOffset = sizeIndex
sizeIndex = sizeIndex + file.fileLength + padding
data += compressedData
data += bytearray(padding)
fileData.close()
dirs.append(file)
return dirs,data,sizeIndex
def copyRelFiles(buildPath,aMemList,mMemList):
relArcPaths = []
for root,dirs,files in os.walk(str(buildPath/"rel")):
for file in files:
if file.find(".rel")!=-1:
relArcFound = False
for rel in aMemList:
if rel==file:
relArcFound = True
for rel in mMemList:
if rel==file:
relArcFound = True
fullPath = Path(root+"/"+file)
if relArcFound==False:
print(str(fullPath)+" -> "+str(buildPath/"game/files/rel/Final/Release"/file))
relSource = open(fullPath,"rb")
data = relSource.read()
relSource.close()
data = oead.yaz0.compress(data)
relNew = open(buildPath/"game/files/rel/Final/Release"/file,"wb")
relNew.write(data)
relNew.truncate()
relNew.close()
else:
relArcPaths.append(fullPath)
arcHeader = HEADER()
arcHeader.RARC = 0x52415243
arcHeader.headerLength = 0x20
arcHeader.unk1 = 0
arcHeader.unk2 = 0
infoBlock = INFO()
infoBlock.numNodes = 3
infoBlock.numDirsThatAreFiles = 142
rootNode = NODE()
rootNode.NAME = 0x524F4F54
rootNode.numDirs = 4
rootNode.firstDirIndex = 0
rootNode.hash = computeHash("rels")
aMemNode = NODE()
aMemNode.NAME = 0x414d454d
aMemNode.hash = computeHash("amem")
aMemNode.numDirs = 79
aMemNode.firstDirIndex = 4
mMemNode = NODE()
mMemNode.hash = computeHash("mmem")
mMemNode.NAME = 0x4d4d454d
mMemNode.numDirs = 59
mMemNode.firstDirIndex = 83
stringTable = ".\0..\0rels\0amem\0"
for rel in aMemList:
stringTable = stringTable+rel+'\0'
stringTable = stringTable+"mmem\0"
for rel in mMemList:
stringTable = stringTable+rel+'\0'
stringTable = stringTable+"\0\0\0\0\0\0"
rootNode.stringTableOffset = stringTable.find("rels")
aMemNode.stringTableOffset = stringTable.find("amem")
mMemNode.stringTableOffset = stringTable.find("mmem")
aMemDir = DIRECTORY()
aMemDir.dirIndex = 0xFFFF
aMemDir.type = 0x200
aMemDir.stringOffset = stringTable.find("amem")
aMemDir.stringHash = computeHash("amem")
aMemDir.fileOffset = 1
aMemDir.fileLength = 0x10
aMemDir.unk1 = 0
mMemDir = DIRECTORY()
mMemDir.dirIndex = 0xFFFF
mMemDir.type = 0x200
mMemDir.stringOffset = stringTable.find("mmem")
mMemDir.stringHash = computeHash("mmem")
mMemDir.fileOffset = 2
mMemDir.fileLength = 0x10
mMemDir.unk1 = 0
unkDir = DIRECTORY()
unkDir.dirIndex = 0xFFFF
unkDir.stringHash = 0x2E
unkDir.type = 0x200
unkDir.stringOffset = 0
unkDir.fileOffset = 0
unkDir.fileLength = 0x10
unkDir.unk1 = 0
unkDir2 = DIRECTORY()
unkDir2.dirIndex = 0xFFFF
unkDir2.stringHash = 0xB8
unkDir2.type = 0x200
unkDir2.stringOffset = 2
unkDir2.fileOffset = 0xFFFFFFFF
unkDir2.fileLength = 0x10
unkDir2.unk1 = 0
dirs = [aMemDir,mMemDir,unkDir,unkDir2]
data = bytearray()
dirIndex = 4
sizeIndex = 0
for rel in aMemList:
retdirs,retdata,retSize = addFile(dirIndex,sizeIndex,dirs,rel,stringTable,relArcPaths,data)
dirIndex = dirIndex+1
sizeIndex = retSize
dirs = retdirs
data = retdata
dirs.append(unkDir)
dirs.append(unkDir2)
dirIndex = dirIndex+2
for rel in mMemList:
retdirs,retdata,retSize = addFile(dirIndex,sizeIndex,dirs,rel,stringTable,relArcPaths,data)
dirIndex = dirIndex+1
sizeIndex = retSize
#print(hex(dirIndex))
dirs = retdirs
data = retdata
unkDir3 = DIRECTORY()
unkDir3.dirIndex = 0xFFFF
unkDir3.stringHash = 0x2E
unkDir3.type = 0x200
unkDir3.stringOffset = 0
unkDir3.fileOffset = 2
unkDir3.fileLength = 0x10
unkDir3.unk1 = 0
unkDir4 = DIRECTORY()
unkDir4.dirIndex = 0xFFFF
unkDir4.stringHash = 0xB8
unkDir4.type = 0x200
unkDir4.stringOffset = 2
unkDir4.fileOffset = 0
unkDir4.fileLength = 0x10
unkDir4.unk1 = 0
dirs.append(unkDir3)
dirs.append(unkDir4)
dirIndex = dirIndex+2
arcHeader.length = len(stringTable)+0x20+0x20+0x30+(len(dirs)*0x14)+len(data)
arcHeader.fileDataOffset=0x14E0
arcHeader.fileDataLen=len(data)
arcHeader.fileDataLen2=arcHeader.fileDataLen
infoBlock.firstNodeOffset = 0x20
infoBlock.firstDirOffset = 0x60
infoBlock.stringTableLen = len(stringTable)
infoBlock.stringTableOffset = 0xB80
infoBlock.unk1 = 0x100
infoBlock.unk2 = 0
infoBlock.totalDirNum = 0x8E
outputArcFile = open(buildPath/"game/files/RELS.arc","wb")
outputArcFile.seek(0)
outputArcFile.write(struct.pack(">IIIIIIII",arcHeader.RARC,arcHeader.length,arcHeader.headerLength,arcHeader.fileDataOffset,arcHeader.fileDataLen,arcHeader.unk1,arcHeader.fileDataLen2,arcHeader.unk2))
outputArcFile.write(struct.pack(">IIIIIIHHI",infoBlock.numNodes,infoBlock.firstNodeOffset,infoBlock.totalDirNum,infoBlock.firstDirOffset,infoBlock.stringTableLen,infoBlock.stringTableOffset,infoBlock.numDirsThatAreFiles,infoBlock.unk1,infoBlock.unk2))
outputArcFile.write(struct.pack(">IIHHI",rootNode.NAME,rootNode.stringTableOffset,rootNode.hash,rootNode.numDirs,rootNode.firstDirIndex))
outputArcFile.write(struct.pack(">IIHHI",aMemNode.NAME,aMemNode.stringTableOffset,aMemNode.hash,aMemNode.numDirs,aMemNode.firstDirIndex))
outputArcFile.write(struct.pack(">IIHHI",mMemNode.NAME,mMemNode.stringTableOffset,mMemNode.hash,mMemNode.numDirs,mMemNode.firstDirIndex))
outputArcFile.write(bytearray(16))
for dir in dirs:
outputArcFile.write(struct.pack(">HHHHIII",dir.dirIndex,dir.stringHash,dir.type,dir.stringOffset,dir.fileOffset,dir.fileLength,dir.unk1))
outputArcFile.write(bytearray(8))
strBytearray = bytearray()
strBytearray.extend(map(ord,stringTable))
outputArcFile.write(strBytearray)
outputArcFile.write(data)
outputArcFile.truncate()
outputArcFile.close()
def main(gamePath,buildPath):
if not gamePath.exists():
gamePath.mkdir(parents=True, exist_ok=True)
iso = Path("gz2e01.iso")
if not iso.exists() or not iso.is_file():
print("gz2e01.iso doesn't exist in project directory!")
sys.exit(1)
if not (gamePath/"files").exists() or not (gamePath/"sys").exists():
print("ISO is not extracted; extracting...")
previousDir = os.getcwd()
os.chdir(str(gamePath.absolute()))
extract_game_assets.extract("../" + str(iso))
os.chdir(previousDir)
print("Copying game files...")
copy(gamePath,buildPath.absolute())
print(str(buildPath/"main_shift.dol")+" -> "+str(buildPath/"game/sys/main.dol"))
shutil.copyfile(buildPath/"main_shift.dol",buildPath/"game/sys/main.dol")
copyRelFiles(buildPath,aMemRels.splitlines(),mMemRels.splitlines())
if __name__ == "__main__":
main(Path(sys.argv[1]),Path(sys.argv[2]))