感谢整理工具,这玩意手动整理起来太折磨了
牛逼,感谢大佬
大佬给了纯爱航线的spine 整理工具,我就来将原帖游戏的spine整理一下吧。
由于技术问题,我提取的skel和atlas的效率不是很理想(虽然可以用)。所以就借用一下纯爱航线的spine 整理工具。(不知道为什么把大佬的工具在hex修改salt也不能提取png)。
先用纯爱航线的spine 整理工具解密解压astc,提取skel和atlas;
然后用下面这个提取json格式的atlas文件,记得输出到新的文件夹中(偷懒,提取png的脚本不会识别json是否有atlas结构);
json版atlas寻找.py
import os
import shutil
import json
import time
def remove_quotes(path):
"""去除路径字符串中的引号"""
return path.strip('"\'')
def filter_json_files(input_folder, output_folder, target_strings):
"""
遍历输入文件夹,筛选包含特定字符串的JSON文件并复制到输出文件夹
参数:
input_folder (str): 输入文件夹路径
output_folder (str): 输出文件夹路径
target_strings (list): 需要查找的目标字符串列表
"""
# 去除引号
input_folder = remove_quotes(input_folder)
output_folder = remove_quotes(output_folder)
# 确保输出文件夹存在
if not os.path.exists(output_folder):
os.makedirs(output_folder)
print(f"创建输出文件夹: {output_folder}")
# 计数器
total_json_files = 0
matched_files = 0
processed_files = 0
# 开始计时
start_time = time.time()
# 遍历输入文件夹统计总数
print(f"\n开始扫描文件夹: {input_folder}")
for root, _, files in os.walk(input_folder):
for file in files:
if file.lower().endswith('.json'):
total_json_files += 1
print(f"找到 {total_json_files} 个JSON文件,开始筛选包含以下字符串的文件: {target_strings}")
# 再次遍历处理文件
for root, _, files in os.walk(input_folder):
for file in files:
if file.lower().endswith('.json'):
processed_files += 1
file_path = os.path.join(root, file)
# 显示进度
progress = (processed_files / total_json_files) * 100
print(f"\r处理进度: {processed_files}/{total_json_files} ({progress:.1f}%) - 检查 {file}", end="")
# 读取并解析JSON文件
try:
with open(file_path, 'r', encoding='utf-8') as f:
data = json.load(f)
# 检查是否包含所有目标字符串
if contains_all_strings(data, target_strings):
# 复制文件到输出文件夹
relative_path = os.path.relpath(file_path, input_folder)
dest_path = os.path.join(output_folder, relative_path)
# 确保目标文件夹存在
dest_dir = os.path.dirname(dest_path)
if not os.path.exists(dest_dir):
os.makedirs(dest_dir)
print(f"\n创建子文件夹: {dest_dir}")
shutil.copy2(file_path, dest_path)
matched_files += 1
print(f"\n匹配成功: {relative_path}")
except json.JSONDecodeError:
print(f"\n警告: {file_path} 不是有效的JSON文件")
except Exception as e:
print(f"\n处理文件 {file_path} 时出错: {e}")
# 结束计时
elapsed_time = time.time() - start_time
# 打印统计信息
print(f"\n\n处理完成!")
print(f"共扫描 {total_json_files} 个JSON文件")
print(f"匹配并复制了 {matched_files} 个文件到 {output_folder}")
print(f"总耗时: {elapsed_time:.2f} 秒")
def contains_all_strings(obj, strings):
"""递归检查对象的所有字符串值是否包含所有目标字符串"""
if isinstance(obj, str):
# 检查当前字符串是否包含所有目标字符串
return all(s in obj for s in strings)
elif isinstance(obj, dict):
# 递归检查字典的所有值
for value in obj.values():
if contains_all_strings(value, strings):
return True
elif isinstance(obj, list):
# 递归检查列表的所有元素
for item in obj:
if contains_all_strings(item, strings):
return True
return False
def main():
"""交互式主函数"""
target_strings = ['size:', 'format:', 'filter:'] # 目标字符串
while True:
print("\n" + "="*40)
print("=== JSON文件筛选复制工具 ===")
print("="*40)
print(f"输入文件夹路径,程序将筛选包含以下字符串的JSON文件: {target_strings}")
# 获取输入文件夹路径
input_folder = input("\n请输入源文件夹路径 (输入'q'退出): ").strip()
# 检查退出条件
if input_folder.lower() == 'q':
print("程序已退出。")
break
input_folder = remove_quotes(input_folder)
if not os.path.exists(input_folder):
print(f"错误: 文件夹 {input_folder} 不存在")
continue
# 获取输出文件夹路径
output_folder = input("请输入输出文件夹路径: ").strip()
output_folder = remove_quotes(output_folder)
# 确认输出文件夹
if os.path.exists(output_folder):
confirm = input(f"警告: 输出文件夹 {output_folder} 已存在,可能会覆盖文件。继续? (y/n): ").strip().lower()
if confirm != 'y':
print("操作已取消")
continue
# 执行筛选和复制
filter_json_files(input_folder, output_folder, target_strings)
# 询问是否继续
continue_choice = input("\n是否继续执行? (y/n): ").strip().lower()
if continue_choice != 'y':
print("程序已退出。")
break
if __name__ == "__main__":
main()
再用下面这个提取png;
提取png.py
import os
import re
import hashlib
import shutil
from pathlib import Path
import concurrent.futures
import multiprocessing
# Base64字符集和值映射(采用你提供的定义方式)
BASE64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
BASE64_VALUES = [0] * 128
for idx, char in enumerate(BASE64_CHARS):
BASE64_VALUES[ord(char)] = idx
HEX_CHARS = list('0123456789abcdef')
_t = ['', '', '', '']
UUID_TEMPLATE = _t + _t + ['-'] + _t + ['-'] + _t + ['-'] + _t + ['-'] + _t + _t + _t
INDICES = [i for i, x in enumerate(UUID_TEMPLATE) if x != '-']
def decode_uuid(base64_str):
"""将Base64编码的字符串还原为UUID格式(采用你提供的实现逻辑)"""
if len(base64_str) != 22:
return base64_str
result = UUID_TEMPLATE.copy()
result[0] = base64_str[0]
result[1] = base64_str[1]
j = 2
for i in range(2, 22, 2):
lhs = BASE64_VALUES[ord(base64_str[i])]
rhs = BASE64_VALUES[ord(base64_str[i + 1])]
result[INDICES[j]] = HEX_CHARS[lhs >> 2]
j += 1
result[INDICES[j]] = HEX_CHARS[((lhs & 3) << 2) | (rhs >> 4)]
j += 1
result[INDICES[j]] = HEX_CHARS[rhs & 0xF]
j += 1
print(f"解码UUID过程: base64_str={base64_str} -> uuid={''.join(result)}")
return ''.join(result)
def calculate_formatted_md5(uuid_str, suffix="120e8962b50b34d26e49730d884a32e4"):
"""计算MD5并格式化为指定形式"""
combined_str = f"{uuid_str}.astc{suffix}"
print(f"计算MD5输入: {combined_str}")
md5_hash = hashlib.md5(combined_str.encode()).hexdigest()
print(f"原始MD5哈希: {md5_hash} (长度: {len(md5_hash)})")
# 格式化为8-5-5-5-9的形式
if len(md5_hash) >= 32:
formatted = f"{md5_hash[:8]}-{md5_hash[8:13]}-{md5_hash[13:18]}-{md5_hash[18:23]}-{md5_hash[23:]}.png"
print(f"格式化后的文件名: {formatted}")
return formatted
else:
print(f"警告: MD5哈希长度不足,使用原始哈希: {md5_hash}.png")
return f"{md5_hash}.png"
def find_and_copy_file(filename, source_dir, dest_dir, json_filename):
"""在源目录中查找文件并复制到目标目录的子文件夹中"""
if not os.path.exists(source_dir):
print(f"错误: 源目录 {source_dir} 不存在")
return False
# 提取JSON文件名(不包含扩展名)
json_base_name = os.path.splitext(json_filename)[0]
# 寻找包含JSON文件名的子文件夹
found_subfolder = None
for subfolder in os.listdir(dest_dir):
subfolder_path = os.path.join(dest_dir, subfolder)
if os.path.isdir(subfolder_path) and json_base_name in subfolder:
found_subfolder = subfolder_path
print(f"提示: 找到包含JSON文件名的子文件夹 {found_subfolder},将使用此文件夹")
break
# 如果没有找到,使用output子文件夹
if found_subfolder is None:
output_subfolder = os.path.join(dest_dir, "output")
if not os.path.exists(output_subfolder):
os.makedirs(output_subfolder)
print(f"创建子文件夹: {output_subfolder}")
found_subfolder = output_subfolder
print(f"搜索文件: {filename} 在目录: {source_dir}")
found = False
for root, _, files in os.walk(source_dir):
if filename in files:
source_path = os.path.join(root, filename)
dest_path = os.path.join(found_subfolder, filename)
print(f"找到文件: {source_path},准备复制到: {dest_path}")
try:
# 覆盖已存在的文件
if os.path.exists(dest_path):
os.remove(dest_path)
shutil.copy2(source_path, dest_path)
print(f"成功复制: {source_path} -> {dest_path}")
found = True
break
except Exception as e:
print(f"复制文件时出错: {e}")
return False
if not found:
print(f"警告: 未找到文件 {filename} 在目录 {source_dir} 中")
return found
def rename_png_using_atlas(png_dir, atlas_filename, png_filename):
"""使用atlas文件中的信息重命名PNG文件(支持覆盖已存在文件)"""
atlas_path = os.path.join(png_dir, atlas_filename)
print(f"尝试读取atlas文件: {atlas_path}")
if not os.path.exists(atlas_path):
print(f"警告: 未找到atlas文件 {atlas_path}")
return False
try:
with open(atlas_path, 'r', encoding='utf-8') as f:
lines = f.readlines()
print(f"atlas文件包含 {len(lines)} 行")
for i, line in enumerate(lines):
line = line.strip()
print(f"处理第 {i+1} 行: '{line}'")
if '.png' in line:
parts = line.split()
print(f"分割后的行内容: {parts}")
if parts:
new_name = parts[0]
print(f"找到新文件名: {new_name}")
old_png_path = os.path.join(png_dir, png_filename)
new_png_path = os.path.join(png_dir, new_name)
print(f"原PNG路径: {old_png_path}")
print(f"新PNG路径: {new_png_path}")
if os.path.exists(old_png_path):
# 覆盖已存在的新文件名
if os.path.exists(new_png_path):
os.remove(new_png_path)
os.rename(old_png_path, new_png_path)
print(f"成功重命名: {png_filename} -> {new_name}")
return True
else:
print(f"警告: 未找到PNG文件 {old_png_path}")
return False
print(f"警告: 在atlas文件中未找到包含'.png'的行")
return False
except Exception as e:
print(f"读取atlas文件时出错: {e}")
return False
def process_json_file(json_path, png_dir, output_dir):
"""处理单个JSON文件"""
try:
json_filename = os.path.basename(json_path)
print(f"\n===== 开始处理JSON文件: {json_filename} =====")
# 读取前256字节
with open(json_path, 'r', encoding='utf-8', errors='ignore') as f:
first_256 = f.read(256)
print(f"读取JSON文件前256字节: {first_256[:100]}... (完整长度: {len(first_256)})")
# 提取符合模式的字符串 (22字符+@+5字符)
pattern = r'([A-Za-z0-9+/]{22})@[A-Za-z0-9]{5}'
matches = re.findall(pattern, first_256)
print(f"正则表达式匹配结果: {matches} (共 {len(matches)} 个匹配)")
if not matches:
print(f"警告: 在{json_filename}中未找到符合模式的字符串")
return
for i, match in enumerate(matches):
print(f"\n--- 处理第 {i+1} 个匹配项 ---")
base64_str = match
print(f"找到Base64字符串: {base64_str}")
# 还原UUID(使用新的解码逻辑)
uuid = decode_uuid(base64_str)
print(f"还原的UUID: {uuid}")
# 计算格式化的MD5
formatted_md5 = calculate_formatted_md5(uuid)
print(f"生成的文件名: {formatted_md5}")
# 查找并复制文件
found_subfolder = None # 初始化变量
if find_and_copy_file(formatted_md5, png_dir, output_dir, json_filename):
# 获取实际使用的子文件夹路径(从find_and_copy_file函数中获取)
json_base_name = os.path.splitext(json_filename)[0]
for subfolder in os.listdir(output_dir):
subfolder_path = os.path.join(output_dir, subfolder)
if os.path.isdir(subfolder_path) and json_base_name in subfolder:
found_subfolder = subfolder_path
break
# 如果没有找到,使用output子文件夹
if found_subfolder is None:
found_subfolder = os.path.join(output_dir, "output")
# 查找atlas文件并重命名PNG
print(f"atlas文件搜索目录: {found_subfolder}")
# 查找atlas文件并增加存在性检查
if os.path.exists(found_subfolder):
atlas_files = [f for f in os.listdir(found_subfolder) if f.endswith('.atlas')]
print(f"找到 {len(atlas_files)} 个atlas文件: {atlas_files}")
if atlas_files:
atlas_file = atlas_files[0]
print(f"使用atlas文件: {atlas_file}")
try:
rename_png_using_atlas(found_subfolder, atlas_file, formatted_md5)
except Exception as e:
print(f"重命名PNG时出错: {e}")
else:
print(f"警告: 在{found_subfolder}中未找到atlas文件")
else:
print(f"警告: 目录{found_subfolder}不存在")
except IndexError as e:
print(f"\n===== 处理文件{json_path}时发生索引越界错误 =====")
print(f"错误详情: {e}")
print(f"当前JSON文件名: {json_filename}")
# 打印可能导致错误的上下文信息
try:
with open(json_path, 'r', encoding='utf-8', errors='ignore') as f:
print(f"JSON文件前500字节内容: {f.read(500)}...")
except:
print("无法读取JSON文件内容进行调试")
except Exception as e:
print(f"处理文件{json_path}时出错: {e}")
def main():
"""交互式主函数"""
print("=== UUID还原与资源处理工具 (调试模式) ===")
print("本程序将详细显示字符串处理的每一步过程")
print("输入'q'或'exit'退出程序")
while True:
# 获取用户输入
json_input = input("\n请输入JSON文件或文件夹路径: ").strip()
if json_input.lower() in ['q', 'exit']:
print("程序已退出。")
break
png_dir = input("请输入PNG文件夹路径: ").strip()
if png_dir.lower() in ['q', 'exit']:
print("程序已退出。")
break
output_dir = input("请输入输出文件夹路径: ").strip()
if output_dir.lower() in ['q', 'exit']:
print("程序已退出。")
break
# 确保输出目录存在
os.makedirs(output_dir, exist_ok=True)
try:
if os.path.isfile(json_input) and json_input.endswith('.json'):
# 处理单个JSON文件
process_json_file(json_input, png_dir, output_dir)
elif os.path.isdir(json_input):
# 处理文件夹中的所有JSON文件
json_files = [os.path.join(json_input, f) for f in os.listdir(json_input)
if f.endswith('.json')]
if not json_files:
print(f"警告: 在{json_input}中未找到JSON文件")
else:
print(f"找到{len(json_files)}个JSON文件,开始处理...")
# 获取CPU核心数
num_threads = multiprocessing.cpu_count()
with concurrent.futures.ThreadPoolExecutor(max_workers=num_threads) as executor:
futures = [executor.submit(process_json_file, json_file, png_dir, output_dir) for json_file in json_files]
for future in concurrent.futures.as_completed(futures):
try:
future.result()
except Exception as e:
print(f"处理过程中发生错误: {e}")
else:
print(f"错误: 路径{json_input}不是有效的JSON文件或文件夹")
except Exception as e:
print(f"处理过程中发生错误: {e}")
# 询问是否继续
continue_choice = input("\n是否继续处理其他文件? (y/n): ").strip().lower()
if continue_choice != 'y':
print("程序已退出。")
break
if __name__ == "__main__":
main()
前两天整理工具天更新了v2版本,整理输出目录有所调整,其实早就写好了,只是忘记到这里更新了
1 个赞