Last active
October 10, 2023 06:58
-
-
Save uyjulian/bb0400c3768ea33be395a22df61a0c70 to your computer and use it in GitHub Desktop.
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
# SPDX-License-Identifier: MIT | |
# Script to extract and decompress data from Trails in the Sky / Sora no Kiseki dat/dir files. | |
# Better code have been released since then; see Trails Research Group: https://github.com/Trails-Research-Group | |
import sys | |
import os | |
import mmap | |
import struct | |
def open_and_map(filename): | |
# mmap filename | |
pass | |
def decompress(buffer, output, size): | |
offset = 0 # u16 | |
bits = 8 # 8 to start off with, then 16 | |
flags = int.from_bytes(buffer[offset:offset + 2], byteorder="little") | |
offset += 2 | |
flags >>= 8 | |
outputoffset = 0 # u16 | |
def getflag(): | |
nonlocal bits | |
nonlocal flags | |
nonlocal offset | |
if bits == 0: | |
slice_ = buffer[offset:offset + 2] | |
if len(slice_) < 2: | |
raise Exception("Out of data") | |
flags = int.from_bytes(slice_, byteorder="little") | |
offset += 2 | |
bits = 16 | |
flag = flags & 1 | |
flags >>= 1 | |
bits -= 1 | |
return flag | |
def setup_run(prev_u_buffer_pos): | |
nonlocal offset | |
nonlocal buffer | |
nonlocal output | |
nonlocal outputoffset | |
run = 2 # u16 | |
if getflag() == 0: | |
run += 1 | |
if getflag() == 0: | |
run += 1 | |
if getflag() == 0: | |
run += 1 | |
if getflag() == 0: | |
if getflag() == 0: | |
slice_ = buffer[offset:offset + 1] | |
if len(slice_) < 1: | |
raise Exception("Out of data") | |
run = int.from_bytes(slice_, byteorder="little") | |
offset += 1 | |
run += 0xE | |
else: | |
run = 0 | |
for i in range(3): | |
run = (run << 1) | getflag() | |
run += 0x6 | |
# Does the 'copy from buffer' thing | |
for i in range(run): | |
output[outputoffset] = output[outputoffset - prev_u_buffer_pos] | |
outputoffset += 1 | |
while True: | |
if getflag() != 0: # Call next method to process next flag | |
if getflag() != 0: # Long look-back distance or exit program or repeating sequence (flags = 11) | |
run = 0 # u16 | |
for i in range(5): # Load high-order distance from flags (max = 0x31) | |
run = (run << 1) | getflag() | |
prev_u_buffer_pos = int.from_bytes(buffer[offset:offset + 1], byteorder="little") # Load low-order distance (max = 0xFF) | |
# Also acts as flag byte | |
# run = 0 and byte = 0 -> exit program | |
# run = 0 and byte = 1 -> sequence of repeating bytes | |
offset += 1 | |
if run != 0: | |
prev_u_buffer_pos = prev_u_buffer_pos | (run << 8) # Add high and low order distance (max distance = 0x31FF) | |
setup_run(prev_u_buffer_pos) # Get run length and finish unpacking (write to output) | |
elif prev_u_buffer_pos > 2: # Is this used? Seems inefficient. | |
setup_run(prev_u_buffer_pos) | |
elif prev_u_buffer_pos == 0: # Decompression complete. End program. | |
break | |
else: # Repeating byte | |
branch = getflag() # True = long repeating sequence (> 30) | |
for i in range(4): | |
run = (run << 1) | getflag() | |
if branch != 0: | |
run = (run << 0x8) | int.from_bytes(buffer[offset:offset + 1], byteorder="little") # Load run length from byte and add high-order run length (max = 0xFFF + 0xE) | |
offset += 1 | |
run += 0xE | |
output[outputoffset:outputoffset + run] = bytes(buffer[offset:offset + 1]) * run | |
offset += 1 | |
outputoffset += run | |
else: # Short look-back distance (flags = 10) | |
prev_u_buffer_pos = int.from_bytes(buffer[offset:offset + 1], byteorder="little") # Get the look-back distance (max = 0xFF) | |
offset += 1 | |
setup_run(prev_u_buffer_pos) # Get run length and finish unpacking (write to output) | |
else: # Copy byte (flags = 0) | |
output[outputoffset:outputoffset + 1] = buffer[offset:offset + 1] | |
outputoffset += 1 | |
offset += 1 | |
return outputoffset, offset | |
def decompress2(buffer, output, size): | |
offset = 0 # u32 | |
outputoffset = 0 # u16 | |
while size > (offset + 2): | |
save1 = int.from_bytes(buffer[offset:offset + 1], byteorder="little") | |
offset += 1 | |
if (save1 & 0x80) == 0: | |
if (save1 & 64) == 0: | |
length = save1 & 31 | |
if (save1 & 32) == 0: | |
output[outputoffset:outputoffset + length] = buffer[offset:offset + length] | |
outputoffset += length | |
offset += length | |
else: | |
length = (length << 8) + int.from_bytes(buffer[offset:offset + 1], byteorder="little") | |
offset += 1 | |
output[outputoffset:outputoffset + length] = buffer[offset:offset + length] | |
outputoffset += length | |
offset += length | |
else: | |
if (save1 & 16) == 0: | |
length = (save1 & 15) + 4 | |
fillbyte = bytes(buffer[offset:offset + 1]) | |
output[outputoffset:outputoffset + length] = fillbyte * length | |
offset += 1 | |
outputoffset += length | |
else: | |
length = ((save1 & 15) << 8) + int.from_bytes(buffer[offset:offset + 1], byteorder="little") + 4 | |
offset += 1 | |
fillbyte = bytes(buffer[offset:offset + 1]) | |
offset += 1 | |
output[outputoffset:outputoffset + length] = fillbyte * length | |
outputoffset += length | |
else: | |
lookbacklength = ((save1 & 31) << 8) + int.from_bytes(buffer[offset:offset + 1], byteorder="little") | |
offset += 1 | |
save2 = offset | |
lookbackptr = (outputoffset - lookbacklength) | |
length = ((save1 >> 5) & 3) + 4 | |
if size > (offset + 2): | |
save3 = int.from_bytes(buffer[save2:save2 + 1], byteorder="little") | |
while ((save3 & 0xe0) == 96): | |
length += (save3 & 31) | |
offset += 1 | |
if size <= (offset + 2): | |
save3 = 0 | |
else: | |
save3 = int.from_bytes(buffer[offset:offset + 1], byteorder="little") | |
if lookbacklength < length: | |
for i in range(length): # this needs to be copied in this specific way: | |
output[outputoffset:outputoffset + 1] = output[lookbackptr:lookbackptr + 1] | |
outputoffset += 1 | |
lookbackptr += 1 | |
else: | |
output[outputoffset:outputoffset + length] = output[lookbackptr: lookbackptr + length] | |
outputoffset += length | |
if size != (offset + 2): | |
raise Exception("Malformed input") | |
return outputoffset, offset | |
output = bytearray(65535) | |
def decompress_FALCOM2_1(indata, pipi, length): | |
buffer = bytearray(length) # Should already be initialized with 0 | |
buffer[0:length] = indata[0:length] | |
offset = 0 # u32 | |
while len(buffer) > offset: | |
size = int.from_bytes(buffer[offset:offset + 2], byteorder="little") | |
offset += 2 | |
if size == 0: | |
break | |
num1 = 0 | |
output[0:len(output)] = b"\x00" * len(output) | |
if int.from_bytes(buffer[offset:offset + 1], byteorder="little") == 0: | |
num1, num2 = decompress(buffer[offset:offset + size], output, size) | |
offset += num2 | |
else: | |
num1, num2 = decompress2(buffer[offset:offset + size], output, size) | |
offset += num2 | |
pipi.write(output[0:num1]) | |
flag = int.from_bytes(buffer[offset:offset + 1], byteorder="little") | |
offset += 1 | |
if flag == 0: | |
break | |
return | |
inbuf_f = open(sys.argv[1], "rb") | |
inbuf = mmap.mmap(inbuf_f.fileno(), 0, prot=mmap.PROT_READ) | |
# print(sys.argv[1]) | |
if inbuf[:6] != b"LB DIR": | |
raise Exception("not lb dir") | |
inoffset = 0 | |
inoffset += 8 | |
numentries = int.from_bytes(inbuf[inoffset:inoffset + 4], byteorder="little") | |
inoffset += 4 | |
shouldbezero = int.from_bytes(inbuf[inoffset:inoffset + 4], byteorder="little") | |
inoffset += 4 | |
if shouldbezero != 0: | |
raise Exception("zero not detect") | |
filels = [] | |
for i in range(numentries): | |
filename = inbuf[inoffset:inoffset + 12] | |
inoffset += 12 | |
timestamp2 = int.from_bytes(inbuf[inoffset:inoffset + 4], byteorder="little") | |
inoffset += 4 | |
compress_size = int.from_bytes(inbuf[inoffset:inoffset + 4], byteorder="little") | |
inoffset += 4 | |
uncompress_size = int.from_bytes(inbuf[inoffset:inoffset + 4], byteorder="little") | |
inoffset += 4 | |
num5 = int.from_bytes(inbuf[inoffset:inoffset + 4], byteorder="little") | |
inoffset += 4 | |
timestamp = int.from_bytes(inbuf[inoffset:inoffset + 4], byteorder="little") | |
inoffset += 4 | |
offset = int.from_bytes(inbuf[inoffset:inoffset + 4], byteorder="little") | |
inoffset += 4 | |
# print(filename, timestamp2, compress_size, uncompress_size, num5, timestamp, offset) | |
if filename != b"/_______.___" and filename[-4:-3] == b".": | |
# print(filename, timestamp2, compress_size, uncompress_size, num5, timestamp, offset) | |
d = { | |
"filename": filename, | |
"compress_size" : compress_size, | |
"uncompress_size" : uncompress_size, | |
"timestamp" : timestamp, | |
"offset" : offset, | |
} | |
filels.append(d) | |
dirname = sys.argv[1][0:-4] | |
inbufdat_f = open(dirname + ".dat", "rb") | |
infotbl = [ | |
# Font files (most cases, pointless to convert, takes too much time) | |
{ "regex":r"FONT8\._DA", "bytes":1, "xres":8, "yres":3316, "imfmt":"gray", "imbits":8}, | |
{ "regex":r"FONT12\._DA", "bytes":1, "xres":12, "yres":43566, "imfmt":"gray", "imbits":8}, | |
{ "regex":r"FONT16\._DA", "bytes":1, "xres":16, "yres":58088, "imfmt":"gray", "imbits":8}, | |
{ "regex":r"FONT18\._DA", "bytes":1, "xres":18, "yres":65445, "imfmt":"gray", "imbits":8}, | |
{ "regex":r"FONT20\._DA", "bytes":1, "xres":20, "yres":72610, "imfmt":"gray", "imbits":8}, | |
{ "regex":r"FONT24\._DA", "bytes":1, "xres":24, "yres":87132, "imfmt":"gray", "imbits":8}, | |
{ "regex":r"FONT26\._DA", "bytes":1, "xres":26, "yres":94489, "imfmt":"gray", "imbits":8}, | |
{ "regex":r"FONT30\._DA", "bytes":1, "xres":30, "yres":109011, "imfmt":"gray", "imbits":8}, | |
{ "regex":r"FONT32\._DA", "bytes":1, "xres":32, "yres":116176, "imfmt":"gray", "imbits":8}, | |
{ "regex":r"FONT36\._DA", "bytes":1, "xres":36, "yres":130698, "imfmt":"gray", "imbits":8}, | |
{ "regex":r"FONT40\._DA", "bytes":1, "xres":40, "yres":145220, "imfmt":"gray", "imbits":8}, # TODO: calculated using proportions; verify | |
{ "regex":r"FONT44\._DA", "bytes":1, "xres":44, "yres":159742, "imfmt":"gray", "imbits":8}, | |
{ "regex":r"FONT48\._DA", "bytes":1, "xres":48, "yres":174264, "imfmt":"gray", "imbits":8}, | |
{ "regex":r"FONT50\._DA", "bytes":1, "xres":50, "yres":181621, "imfmt":"gray", "imbits":8}, | |
{ "regex":r"FONT54\._DA", "bytes":1, "xres":54, "yres":196143, "imfmt":"gray", "imbits":8}, | |
{ "regex":r"FONT60\._DA", "bytes":1, "xres":60, "yres":217830, "imfmt":"gray", "imbits":8}, | |
{ "regex":r"FONT64\._DA", "bytes":1, "xres":64, "yres":232352, "imfmt":"gray", "imbits":8}, | |
{ "regex":r"FONT72\._DA", "bytes":1, "xres":72, "yres":26496, "imfmt":"gray", "imbits":8}, | |
{ "regex":r"FONT80\._DA", "bytes":1, "xres":80, "yres":29440, "imfmt":"gray", "imbits":8}, | |
{ "regex":r"FONT96\._DA", "bytes":1, "xres":96, "yres":35328, "imfmt":"gray", "imbits":8}, | |
{ "regex":r"FONT128\._DA", "bytes":1, "xres":128, "yres":47104, "imfmt":"gray", "imbits":8}, | |
{ "regex":r"FONT144\._DA", "bytes":1, "xres":144, "yres":52992, "imfmt":"gray", "imbits":8}, | |
{ "regex":r"FONT160\._DA", "bytes":1, "xres":160, "yres":58880, "imfmt":"gray", "imbits":8}, | |
{ "regex":r"FONT192\._DA", "bytes":1, "xres":192, "yres":70656, "imfmt":"gray", "imbits":8}, | |
{ "regex":r"C_STCH\d\d\._CH", "bytes":4, "xres":512, "yres":512, "imfmt":"bgra", "imbits":8}, | |
{ "regex":r"H_STCH\d\d\._CH", "bytes":4, "xres":1024, "yres":1024, "imfmt":"bgra", "imbits":8}, | |
{ "regex":r"C_STCHR\d\._CH", "bytes":4, "xres":512, "yres":512, "imfmt":"bgra", "imbits":8}, | |
{ "regex":r"H_STCHR\d\._CH", "bytes":4, "xres":1024, "yres":1024, "imfmt":"bgra", "imbits":8}, | |
{ "regex":r"C_SUBTI\._CH", "bytes":4, "xres":256, "yres":256, "imfmt":"bgra", "imbits":8}, | |
{ "regex":r"H_SUBTI\._CH", "bytes":4, "xres":512, "yres":512, "imfmt":"bgra", "imbits":8}, | |
{ "regex":r"C_PASELE\._CH", "bytes":2, "xres":512, "yres":512, "fmt":"rgba4444"}, | |
{ "regex":r"H_PASELE\._CH", "bytes":2, "xres":1024, "yres":1024, "fmt":"rgba4444"}, | |
{ "regex":r"C_CAMP02\._CH", "bytes":2, "xres":256, "yres":256, "fmt":"rgba1555"}, | |
{ "regex":r"C_CAMP03\._CH", "bytes":2, "xres":256, "yres":256, "fmt":"rgba1555"}, | |
{"dir":"ED6_DT00", "regex":r"C_CAMP04\._CH", "bytes":2, "xres":256, "yres":256, "fmt":"rgba1555"}, | |
{"dir":"ED6_DT00", "regex":r"H_CAMP04\._CH", "bytes":2, "xres":512, "yres":512, "fmt":"rgba1555"}, | |
{ "regex":r"BFACE\d\d\d\._CH", "bytes":2, "xres":256, "yres":256, "fmt":"rgba1555"}, | |
{ "regex":r"HFACE\d\d\d\._CH", "bytes":2, "xres":512, "yres":512, "fmt":"rgba1555"}, | |
{ "regex":r"CTI\d\d\d\d\d\._CH", "bytes":2, "xres":256, "yres":256, "fmt":"rgba1555"}, | |
{ "regex":r"H_CAMP02\._CH", "bytes":2, "xres":512, "yres":512, "fmt":"rgba1555"}, | |
{ "regex":r"H_CAMP03\._CH", "bytes":2, "xres":512, "yres":512, "fmt":"rgba1555"}, | |
{ "regex":r"C_MNBG01\._CH", "bytes":2, "xres":128, "yres":128, "fmt":"rgba4444"}, | |
{ "regex":r"CA\d\d\d\d\d\._CH", "bytes":2, "xres":128, "yres":128, "fmt":"rgba1555"}, | |
{ "regex":r"CA\d\d\d\d\d\._CH", "bytes":2, "xres":256, "yres":256, "fmt":"rgba1555"}, | |
{"dir":"ED6_DT24", "regex":r"C_VIS419\._CH", "bytes":2, "xres":512, "yres":768, "fmt":"rgba4444"}, | |
{"dir":"ED6_DT24", "regex":r"C_VIS438\._CH", "bytes":2, "xres":512, "yres":768, "fmt":"rgba4444"}, | |
{"dir":"ED6_DT24", "regex":r"C_VIS439\._CH", "bytes":2, "xres":512, "yres":768, "fmt":"rgba4444"}, | |
{"dir":"ED6_DT24", "regex":r"C_VIS448\._CH", "bytes":2, "xres":512, "yres":768, "fmt":"rgba4444"}, | |
{"dir":"ED6_DT24", "regex":r"C_VIS478\._CH", "bytes":2, "xres":512, "yres":768, "fmt":"rgba4444"}, | |
{"dir":"ED6_DT24", "regex":r"C_VIS530\._CH", "bytes":2, "xres":512, "yres":768, "fmt":"rgba4444"}, | |
{"dir":"ED6_DT24", "regex":r"C_VIS531\._CH", "bytes":2, "xres":512, "yres":768, "fmt":"rgba4444"}, | |
{"dir":"ED6_DT24", "regex":r"C_VIS532\._CH", "bytes":2, "xres":512, "yres":768, "fmt":"rgba4444"}, | |
{"dir":"ED6_DT24", "regex":r"C_VIS533\._CH", "bytes":2, "xres":512, "yres":768, "fmt":"rgba4444"}, | |
{"dir":"ED6_DT24", "regex":r"C_VIS534\._CH", "bytes":2, "xres":512, "yres":768, "fmt":"rgba4444"}, | |
{"dir":"ED6_DT24", "regex":r"C_VIS535\._CH", "bytes":2, "xres":512, "yres":768, "fmt":"rgba4444"}, | |
{"dir":"ED6_DT24", "regex":r"C_VIS536\._CH", "bytes":2, "xres":512, "yres":768, "fmt":"rgba4444"}, | |
{"dir":"ED6_DT24", "regex":r"C_VIS537\._CH", "bytes":2, "xres":512, "yres":768, "fmt":"rgba4444"}, | |
{"dir":"ED6_DT24", "regex":r"C_VIS538\._CH", "bytes":2, "xres":512, "yres":768, "fmt":"rgba4444"}, | |
{"dir":"ED6_DT24", "regex":r"C_VIS539\._CH", "bytes":2, "xres":512, "yres":768, "fmt":"rgba4444"}, | |
{"dir":"ED6_DT24", "regex":r"C_VIS540\._CH", "bytes":2, "xres":512, "yres":768, "fmt":"rgba4444"}, | |
{"dir":"ED6_DT24", "regex":r"C_VIS541\._CH", "bytes":2, "xres":512, "yres":768, "fmt":"rgba4444"}, | |
{"dir":"ED6_DT24", "regex":r"C_VIS542\._CH", "bytes":2, "xres":512, "yres":768, "fmt":"rgba4444"}, | |
{"dir":"ED6_DT24", "regex":r"C_MAP010\._CH", "bytes":2, "xres":512, "yres":1024, "fmt":"rgba4444"}, | |
{"dir":"ED6_DT24", "regex":r"C_MAP011\._CH", "bytes":2, "xres":512, "yres":1024, "fmt":"rgba4444"}, | |
{"dir":"ED6_DT24", "regex":r"C_PLAC\d\d\._CH", "bytes":2, "xres":256, "yres":256, "fmt":"rgba4444"}, | |
{"dir":"ED6_DT24", "regex":r"C_PLAC\d\d\._CH", "bytes":2, "xres":512, "yres":512, "fmt":"rgba4444"}, | |
{"dir":"ED6_DT24", "regex":r"H_PLAC\d\d\._CH", "bytes":2, "xres":512, "yres":512, "fmt":"rgba4444"}, | |
{"dir":"ED6_DT24", "regex":r"C_TITLE1\._CH", "bytes":2, "xres":512, "yres":512, "fmt":"rgba4444"}, | |
{"dir":"ED6_DT24", "regex":r"C_TITLE2\._CH", "bytes":2, "xres":512, "yres":1024, "fmt":"rgba4444"}, | |
{"dir":"ED6_DT24", "regex":r"C_TITLE3\._CH", "bytes":2, "xres":512, "yres":512, "fmt":"rgba4444"}, | |
{"dir":"ED6_DT24", "regex":r"C_TITLE4\._CH", "bytes":2, "xres":512, "yres":1024, "fmt":"rgba4444"}, | |
{"dir":"ED6_DT24", "regex":r"H_MAP010\._CH", "bytes":2, "xres":1024, "yres":2048, "fmt":"rgba4444"}, | |
{"dir":"ED6_DT24", "regex":r"H_MAP011\._CH", "bytes":2, "xres":1024, "yres":2048, "fmt":"rgba4444"}, | |
{"dir":"ED6_DT24", "regex":r"H_MAP012\._CH", "bytes":2, "xres":1024, "yres":2048, "fmt":"rgba4444"}, | |
{"dir":"ED6_DT24", "regex":r"H_TITLE1\._CH", "bytes":2, "xres":1024, "yres":1024, "fmt":"rgba4444"}, | |
{"dir":"ED6_DT24", "regex":r"H_VIS419\._CH", "bytes":2, "xres":1024, "yres":1536, "fmt":"rgba4444"}, | |
{"dir":"ED6_DT24", "regex":r"H_VIS438\._CH", "bytes":2, "xres":1024, "yres":1536, "fmt":"rgba4444"}, | |
{"dir":"ED6_DT24", "regex":r"H_VIS439\._CH", "bytes":2, "xres":1024, "yres":1536, "fmt":"rgba4444"}, | |
{"dir":"ED6_DT04", "bytes":2, "xres":512, "yres":512, "fmt":"rgba1555"}, | |
{"dir":"ED6_DT24", "bytes":2, "xres":512, "yres":512, "fmt":"rgba1555"}, | |
{"dir":"ED6_DT04", "bytes":2, "xres":256, "yres":256, "fmt":"rgba1555"}, | |
{"dir":"ED6_DT24", "bytes":2, "xres":256, "yres":256, "fmt":"rgba1555"}, | |
{"dir":"ED6_DT05", "bytes":2, "xres":128, "yres":128, "fmt":"rgba1555"}, | |
{"dir":"ED6_DT25", "bytes":2, "xres":128, "yres":128, "fmt":"rgba1555"}, | |
{"dir":"ED6_DT05", "bytes":2, "xres":256, "yres":256, "fmt":"rgba1555"}, | |
{"dir":"ED6_DT25", "bytes":2, "xres":256, "yres":256, "fmt":"rgba1555"}, | |
{"dir":"ED6_DT04", "bytes":2, "xres":512, "yres":768, "fmt":"rgba1555"}, | |
{"dir":"ED6_DT04", "bytes":2, "xres":1024, "yres":1536, "fmt":"rgba1555"}, | |
{"dir":"ED6_DT24", "bytes":2, "xres":512, "yres":768, "fmt":"rgba1555"}, | |
{"dir":"ED6_DT24", "bytes":2, "xres":1024, "yres":1536, "fmt":"rgba1555"}, | |
{"dir":"ED6_DT04", "bytes":2, "xres":1024, "yres":2048, "fmt":"rgba1555"}, | |
{"dir":"ED6_DT24", "bytes":2, "xres":1024, "yres":2048, "fmt":"rgba1555"}, | |
{"dir":"ED6_DT04", "bytes":2, "xres":512, "yres":1024, "fmt":"rgba1555"}, | |
{"dir":"ED6_DT24", "bytes":2, "xres":512, "yres":1024, "fmt":"rgba1555"}, | |
{"dir":"ED6_DT04", "bytes":2, "xres":1024, "yres":1024, "fmt":"rgba1555"}, | |
{"dir":"ED6_DT24", "bytes":2, "xres":1024, "yres":1024, "fmt":"rgba1555"}, | |
{"dir":"ED6_DT24", "bytes":2, "xres":256, "yres":512, "fmt":"rgba1555"}, | |
{ "bytes":2, "xres":1024, "yres":1024, "fmt":"rgba4444"}, | |
{ "bytes":2, "xres":176, "yres":208, "fmt":"rgba4444"}, | |
{ "bytes":2, "xres":352, "yres":416, "fmt":"rgba4444"}, | |
{ "bytes":2, "xres":256, "yres":256, "fmt":"rgba4444"}, | |
{ "bytes":2, "xres":512, "yres":512, "fmt":"rgba4444"}, | |
] | |
def stdincmd(s, cmd): | |
pass | |
def c8888topngfile(outfile, sizew, sizeh, depth, fmt, s): | |
pass | |
def mkddsheader(width, height, rflag, gflag, bflag, aflag, bits): | |
return b"DDS\x20" + struct.pack("<IIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", 124, 0x1 | 0x2 | 0x4 | 0x1000 | 0x8, width, height, (height * bits + 7) // 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, (0x1 if (aflag != 0) else 0x0) | 0x40, 0, bits, rflag, gflag, bflag, aflag, 0x1000, 0, 0, 0, 0) | |
def dds_process(fname, blob): | |
if blob[0:3] == b"DDS": | |
with open(fname, "wb") as wf: | |
wf.write(blob) | |
return True | |
return False | |
def infoenum(dirname, fileinfo, blob, rawdata): | |
fname = dirname + b"/" + fileinfo["filename"].replace(b" ", b"") | |
if (fname[-3:] != "_CH") and (fname[-3:] != "_DS") and (fname[-3:] != "_DA"): | |
return False | |
if dds_process(fname, blob): | |
return True | |
for v in infotbl: | |
if v["regex"]: | |
# TODO: regex | |
continue | |
if v["bytes"]: | |
if (v["bytes"] * v["xres"] * x["yres"]) != len(blob): | |
continue | |
if v["dir"]: | |
if v["dir"] != dirname[-8:]: | |
continue | |
if v["fmt"]: | |
if v["fmt"] == "rgba4444": | |
dds_process(fname, mkddsheader(v["xres"], v["yres"], 0x0f00, 0x00f0, 0x000f, 0xf000, 16) + blob) | |
elif v["fmt"] == "rgba1555": | |
dds_process(fname, mkddsheader(v["xres"], v["yres"], 0x7c00, 0x03e0, 0x001f, 0x8000, 16) + blob) | |
else: | |
c8888topngfile(fname[0:-5], v["xres"], v["yres"], v["imbits"], v["imfmt"], blob) | |
return True | |
return False | |
os.makedirs(dirname, exist_ok=True) | |
# print(dirname) | |
# flush stdout | |
for v in filels: | |
if v["compress_size"] == 0: | |
continue | |
if v["uncompress_size"] == 0: | |
continue | |
fname = dirname + "/" + v["filename"].replace(b" ", b"").decode("ASCII") | |
if (fname[-3:] == "_CH" or fname[-3:] == "_DS" or fname[-3:] == "_DA") and (os.path.isfile(fname[0:-5] + ".png")): | |
continue | |
if os.path.isfile(fname): | |
continue | |
# print("FILE:", dirname, fname) | |
inbufdat_f.seek(v["offset"]) | |
rawdata = inbufdat_f.read(v["compress_size"]) | |
if dds_process(fname, rawdata): | |
continue | |
if (v["compress_size"] != v["uncompress_size"]) or ((dirname[-8:] != "ED6_DT16") and (fname[-3:] == "_X2")) or (fname[-3:] in ["_X3", "_OP", "_CL", "_CT", "_EF", "_EN", "_EP", "_HD", "_LM", "_MH"]): | |
with open(fname, "wb") as wf: | |
decompress_FALCOM2_1(rawdata, wf, v["compress_size"]) | |
# if dcdata == None or dcdata == b"": | |
# raise Exception("No data") | |
# else: | |
# # if not (os.path.isfile(fname + ".raw")) | |
# # with open(fname + ".raw", "wb") as wf: | |
# # wf.write(dcdata) | |
# # if not (os.path.isfile(fname + ".dec")) | |
# # with open(fname + ".dec", "wb") as wf: | |
# # wf.write(dcdata) | |
# # if not infoenum(dirname, v, dcdata, rawdata) | |
# # with open(fname, "wb") as wf: | |
# # wf.write(dcdata) | |
# with open(fname, "wb") as wf: | |
# wf.write(dcdata) | |
else: | |
if (fname[-3:] == "_DS"): | |
with open(fname, "wb") as wf: | |
decompress_FALCOM2_1(rawdata, wf, v["compress_size"]) | |
# with open(fname, "wb") as wf: | |
# wf.write(dcdata) | |
else: | |
with open(fname, "wb") as wf: | |
wf.write(rawdata[0:v["uncompress_size"]]) | |
os.utime(fname, (v["timestamp"], v["timestamp"])) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment