-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
6bb0987
commit 59ac655
Showing
3 changed files
with
250 additions
and
0 deletions.
There are no files selected for viewing
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,8 @@ | ||
# UWDTool_Tuanjie | ||
团结引擎的WebglData解包,封包工具 | ||
|
||
此版本基于 [](https://github.com/yuria0309/uwdtool)修改过来,封包的时候无法区分是Unity还是团结,只能说给参数或者编译其他版本,如果是 | ||
|
||
Unity请用 [uwdTool](https://github.com/yuria0309/uwdtool) | ||
|
||
[README](https://github.com/yuria0309/uwdtool/blob/master/README.md) |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,244 @@ | ||
import argparse | ||
import struct | ||
import os | ||
import hashlib | ||
from glob import glob | ||
from pathlib import Path | ||
|
||
|
||
UWDT_VERSION = "1.1.0" | ||
HELP_STR = f"""UWDTool v{UWDT_VERSION}""" | ||
|
||
|
||
class UWDTException(Exception): | ||
def __init__(self, msg): | ||
self.msg = msg | ||
|
||
def __str__(self): | ||
return self.msg | ||
|
||
|
||
class BinaryReader: | ||
file = None | ||
|
||
def __init__(self, path): | ||
self.file = open(path, "rb") | ||
|
||
def read_string(self, size=None): | ||
if size is None: | ||
pass | ||
else: | ||
return self.file.read(size).decode('utf-8') | ||
|
||
def read_int(self): | ||
return struct.unpack("<I", self.file.read(4))[0] | ||
|
||
def tell(self): | ||
return self.file.tell() | ||
|
||
def seek(self, pos): | ||
self.file.seek(pos) | ||
|
||
def read_bin(self, size=1): | ||
return self.file.read(size) | ||
|
||
def close(self): | ||
self.file.close() | ||
|
||
|
||
class UnityWebData: | ||
SIGNATURE = None | ||
BEGINNING_OFFSET = None | ||
FILE_INFO = [] | ||
|
||
def load(self, path): | ||
file = BinaryReader(path) | ||
|
||
self.SIGNATURE = file.read_string(18) | ||
if self.SIGNATURE != "TuanjieWebData1.0\0": | ||
raise UWDTException("File is not a UnityWebData file") | ||
|
||
self.BEGINNING_OFFSET = file.read_int() | ||
|
||
while file.tell() < self.BEGINNING_OFFSET: | ||
offset = file.read_int() | ||
length = file.read_int() | ||
name_length = file.read_int() | ||
name = file.read_string(name_length) | ||
|
||
self.FILE_INFO.append({ | ||
"offset": offset, | ||
"length": length, | ||
"name_length": name_length, | ||
"name": name | ||
}) | ||
return file | ||
|
||
|
||
class Packer: | ||
input_path = None | ||
output_path = None | ||
|
||
def pack(self, input_path, output_path): | ||
print("Start packing...") | ||
self.input_path = input_path | ||
self.output_path = output_path | ||
|
||
if self.input_path is None: | ||
raise UWDTException(f"input path is None") | ||
if not os.path.isdir(self.input_path): | ||
raise UWDTException(f"input path {self.input_path} is not a directory") | ||
if self.output_path is None: | ||
raise UWDTException(f"output path is None") | ||
|
||
os.makedirs(Path(self.output_path).parent.absolute(), exist_ok=True) | ||
|
||
print(f"Pack files in {self.input_path} to {self.output_path}") | ||
|
||
files = [y for x in os.walk(self.input_path) for y in glob(os.path.join(x[0], '*'))] | ||
targets_ = [x for x in files if os.path.isfile(x)] | ||
targets = [] | ||
for target in targets_: | ||
if self.input_path.endswith("/"): | ||
targets.append(target[len(self.input_path):].replace("\\", "/")) | ||
else: | ||
targets.append(target[len(self.input_path)+1:].replace("\\", "/")) | ||
|
||
OUTPUT = open(self.output_path, "wb") | ||
|
||
OUTPUT.write(bytes("TuanjieWebData1.0\0", "utf-8")) | ||
|
||
header_length = 0 | ||
for file_name in targets: | ||
header_length += (4 + 4 + 4 + len(bytes(file_name, "utf-8"))) | ||
|
||
OUTPUT.write(struct.pack("<i", 20+header_length)) | ||
|
||
file_offset = 20+header_length | ||
for file_name in targets: | ||
OUTPUT.write(struct.pack("<i", file_offset)) | ||
file_size = os.path.getsize(os.path.join(self.input_path, file_name)) | ||
file_offset += file_size | ||
OUTPUT.write(struct.pack("<i", file_size)) | ||
OUTPUT.write(struct.pack("<i", len(bytes(file_name, "utf-8")))) | ||
OUTPUT.write(bytes(file_name, "utf-8")) | ||
|
||
for file_name in targets: | ||
print(f"Add file {file_name}...", end="") | ||
with open(os.path.join(self.input_path, file_name), "rb") as f: | ||
OUTPUT.write(f.read()) | ||
print("ok") | ||
|
||
OUTPUT.close() | ||
|
||
total_size = os.path.getsize(self.output_path) | ||
print("Packing ended successfully!") | ||
print(f"Total Size: {total_size}bytes ({Inspector().sizeof_fmt(total_size)})") | ||
md5 = hashlib.md5(open(self.output_path, "rb").read()).hexdigest() | ||
print(f"MD5 checksum: {md5}") | ||
|
||
|
||
class UnPacker: | ||
input_path = None | ||
output_path = None | ||
|
||
def unpack(self, input_path, output_path): | ||
print("Start unpacking...") | ||
self.input_path = input_path | ||
self.output_path = output_path | ||
|
||
if self.input_path is None: | ||
raise UWDTException(f"input path is None") | ||
if not os.path.isfile(self.input_path): | ||
raise UWDTException(f"input path {self.input_path} is not a file") | ||
|
||
uwd = UnityWebData() | ||
file = uwd.load(self.input_path) | ||
|
||
if self.output_path is None: | ||
self.output_path = os.path.join(os.getcwd(), "output") | ||
os.makedirs(self.output_path, exist_ok=True) | ||
print(f"Extract {self.input_path} to {self.output_path}") | ||
|
||
for info in uwd.FILE_INFO: | ||
offset = info["offset"] | ||
length = info["length"] | ||
name = info["name"] | ||
|
||
file.seek(offset) | ||
data = file.read_bin(length) | ||
|
||
file_output_path = os.path.join(self.output_path, name) | ||
os.makedirs(os.path.dirname(file_output_path), exist_ok=True) | ||
|
||
with open(file_output_path, "wb") as f: | ||
print(f"Extract {name}...", end="") | ||
f.write(data) | ||
print("ok") | ||
|
||
file.close() | ||
print("Extract end") | ||
|
||
|
||
class Inspector: | ||
path = None | ||
|
||
def sizeof_fmt(self, num, suffix="B"): | ||
for unit in ["", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi"]: | ||
if abs(num) < 1024.0: | ||
return f"{num:3.1f}{unit}{suffix}" | ||
num /= 1024.0 | ||
return f"{num:.1f}Yi{suffix}" | ||
|
||
def inspect(self, path): | ||
self.path = path | ||
file = UnityWebData() | ||
data = file.load(self.path) | ||
|
||
print(f"** Dump of {self.path}") | ||
print("") | ||
print(f"File Signature: {file.SIGNATURE}") | ||
print(f"Beginning Offset: {file.BEGINNING_OFFSET}") | ||
print("") | ||
|
||
for idx, info in enumerate(file.FILE_INFO): | ||
print(f"File #{idx}") | ||
print(f"Name: {info['name']}") | ||
print(f"Offset: {info['offset']}") | ||
size_h = self.sizeof_fmt(info['length']) | ||
print(f"Length: {info['length']} ({size_h})") | ||
print("") | ||
|
||
data.close() | ||
|
||
|
||
def main(): | ||
parser = argparse.ArgumentParser( | ||
prog="uwdtool", | ||
description=HELP_STR, | ||
formatter_class=argparse.RawTextHelpFormatter) | ||
g = parser.add_mutually_exclusive_group() | ||
|
||
g.add_argument("-p", "--pack", action="store_true", help="packing files in input-path directory") | ||
g.add_argument("-u", "--unpack", action="store_true", help="unpacking input-path file to output-path directory") | ||
g.add_argument("-isp", "--inspect", action="store_true", help="show file information list of input-path file") | ||
|
||
parser.add_argument("-i", dest="ARG_INPUT", help="input path") | ||
parser.add_argument("-o", dest="ARG_OUTPUT", help="output path") | ||
args = parser.parse_args() | ||
|
||
if args.pack: | ||
Packer().pack(args.ARG_INPUT, args.ARG_OUTPUT) | ||
elif args.unpack: | ||
UnPacker().unpack(args.ARG_INPUT, args.ARG_OUTPUT) | ||
elif args.inspect: | ||
if args.ARG_INPUT is None: | ||
raise UWDTException("input file option is missing") | ||
else: | ||
Inspector().inspect(args.ARG_INPUT) | ||
else: | ||
raise UWDTException("Unknown Control Option") | ||
|
||
|
||
if __name__ == "__main__": | ||
main() |
Empty file.