Created
October 25, 2018 02:20
-
-
Save Nisto/65d45f5aea3d73ce1fc9625d1a752ed2 to your computer and use it in GitHub Desktop.
FFX extractor
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
import os | |
import struct | |
FS_HEADER_SECT = None # SLPS-25050: 279 | |
DIRSIZE_TBL_TOC_ENT = None # SLPS-25050: 16 | |
ISOPATH = r'path to iso here' | |
OUTDIR = r'path to output directory here' | |
def get_u32_le(buf, off=0): | |
return struct.unpack("<I", buf[off:off+4])[0] | |
def get_s16_le(buf, off=0): | |
return struct.unpack("<h", buf[off:off+2])[0] | |
def main(): | |
if FS_HEADER_SECT is None \ | |
or DIRSIZE_TBL_TOC_ENT is None \ | |
or os.path.isfile(ISOPATH) is not True \ | |
or os.path.isdir(OUTDIR) is not True: | |
input("Please configure the script!") | |
return 1 | |
isopath = os.path.realpath(ISOPATH) | |
if not os.path.isfile(isopath): | |
print("Image path invalid!") | |
return 1 | |
outdir = os.path.realpath(OUTDIR) | |
if not os.path.isdir(outdir): | |
os.makedirs(outdir) | |
with open(isopath, "rb") as iso: | |
iso.seek(FS_HEADER_SECT * 2048) | |
header = iso.read(0x30) | |
mdg_off = get_u32_le(header, 0x10) * 2048 | |
mdg_size = get_u32_le(header, 0x14) | |
fid_off = get_u32_le(header, 0x18) * 2048 | |
fid_size = get_u32_le(header, 0x1C) | |
# read in the TOC | |
iso.seek(mdg_off) | |
mdg_buf = iso.read(mdg_size) | |
# read in the folder ID table | |
iso.seek(fid_off) | |
fid_buf = iso.read(fid_size) | |
# read in the folder size table | |
word = get_u32_le(mdg_buf, DIRSIZE_TBL_TOC_ENT*4) | |
next_word = get_u32_le(mdg_buf, DIRSIZE_TBL_TOC_ENT*4+4) | |
sector = word & 0x3FFFFF | |
next_sector = next_word & 0x3FFFFF | |
size = (next_sector - sector) * 2048 | |
excess = word >> 24 | |
size -= excess * 8 | |
iso.seek(sector * 2048) | |
bin_buf = iso.read(size) | |
for dir_idx in range(64): | |
print("*** extracting DIRECTORY %d" % dir_idx) | |
outsubdir = os.path.join(outdir, "ffx%d" % dir_idx) | |
if not os.path.isdir(outsubdir): | |
os.makedirs(outsubdir) | |
toc_dir_idx = get_s16_le(fid_buf, dir_idx*2) # the first TOC file entry of the folder | |
dir_sect_cnt = get_u32_le(bin_buf, dir_idx*4) # how many sectors the folder occupies | |
if toc_dir_idx > -1 and dir_sect_cnt > 0: | |
i = toc_dir_idx | |
while dir_sect_cnt > 0: | |
word = get_u32_le(mdg_buf, i*4) | |
sector = word & 0x3FFFFF | |
compressed = word & 0x400000 | |
dummy = word & 0x800000 | |
excess = word >> 24 | |
if not dummy: | |
next_sector = get_u32_le(mdg_buf, i*4+4) & 0x3FFFFF | |
num_sectors = next_sector - sector | |
offset = sector * 2048 | |
size = (num_sectors * 2048) - (excess * 8) | |
if compressed: | |
outpath = os.path.join(outsubdir, "TOC_ENT_%05d.cbin" % i) | |
else: | |
outpath = os.path.join(outsubdir, "TOC_ENT_%05d.bin" % i) | |
iso.seek(offset) | |
print("extracting file %d... " % i, end='') | |
with open(outpath, "wb") as bin: | |
bin.write( iso.read(size) ) | |
print("ok") | |
dir_sect_cnt -= num_sectors | |
i += 1 | |
return 0 | |
if __name__=="__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment