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]))