棕色尘埃2pc解包小白求助

我是直接在本地资源文件中通过文件大小判断角色模型文件大概在Addressable文件夹中,但里面项目繁多,如下所示

我找了几个_data文件,加上.bundle后缀后尝试用as解包,能导出一些文件,但这样工程量未免太大了吧

不知道这个方法对不对,求大佬解答一下

游戏安装包:下载链接:https://wwhu.lanzoub.com/igPFS33th1sh 密码:g3xp

我之前也看到了这个帖子,不过好奇想知道是不是一定要用filddler去抓资源包,从本地解包的话一般是做不到的吗,求大佬解答

可以啊,你都能直接下到本地了就没必要再抓包,而且这游戏也没加密

但是这么多文件该怎么解包找到角色模型文件啊,是要用脚本还是其他的方法呢,大佬求教

說明詳情

前言 :
遊戲資料都下載到本地後
原AB包文件名會變更
\hash\hash\__data 型式

本質一樣是AB包
可以使用RAZ版AssetStudio開啟

Step01
使用Windows內建搜索功能
搜索data
全選複製

Step02
新建目錄data_mix
將眾多已複製data貼到新的目錄

Step03
保留相同文件名data
Windows 會自動將眾多data重新命名

Step04
Raz 版AssetStudio設定 Options —> Export options —> Group exported assets by container path
效果 : 解包出來的資料會依AB包自帶路徑生成目錄 , 會將遊戲資料分類好

Step05
Raz 版AssetStudio設定 Options —> Specify Unity version —> 填入2022.3.22f1
效果 : 繞過File Header 文件頭簡單的偽裝 , 可直接讀取AB包內資料

Step06
Raz 版AssetStudio開啟一堆data文件名AB包 , 因為樓主要角色SPINE , 可以先將眾多data依文件大小排序
先開啟最大的AB包 , 我這邊一開就中 , 第一個AB包就是SPINE文件

Step07
Raz 版AssetStudio開啟AB包後 , 輸出類別勾選 Filter Type → TextAsset , Texture2D , 輸出目錄自行生成為out
註 : Spine文件勾選這兩項就行

Step08
輸出完成後會看到SPINE文件多了後綴asset
使用bat批次檔去除多於後綴

修改子目錄.asset副檔名(去掉.asset後綴).rar (435 字节)

.atlas.asset ----> .atlas
.skel.asset ----> .skel

Step09
skeletonViewer-4.1.24.jar 開啟skel文件 完成

char001197

过滤container啊
你都能用as打开了
排序和搜索这两是计算机基础的
而且你不需要要改后缀名as能打开所有符合unity的文件

ok了,发现是as的问题,换了一个版本的as解决了,谢谢大佬!辛苦

解决了,谢谢大佬,辛苦

Addressable应该是用的unity的Addressable可寻址封包方式,他通过远程加载的文件会以/{hash}/_data的形式缓存,基本上只能全部加载一遍然后导出texture和TextAsset,然后脚本整理spine。

整理提取spine
import os
import shutil
import re
import concurrent.futures
import multiprocessing


def get_unique_folder_name(destination_folder, base_name, parent_folder_name=None):
    # 先尝试用父文件夹名命名
    if parent_folder_name:
        target_folder = os.path.join(destination_folder, parent_folder_name)
        if not os.path.exists(target_folder):
            return target_folder
    # 若父文件夹名已存在,再尝试用atlas文件名命名
    target_folder = os.path.join(destination_folder, base_name)
    if not os.path.exists(target_folder):
        return target_folder
    # 若atlas文件名也已存在,使用atlas文件名+_number命名
    number = 1
    while True:
        target_folder = os.path.join(destination_folder, f"{base_name}_{number}")
        if not os.path.exists(target_folder):
            return target_folder
        number += 1


def process_atlas_file(root, file, destination_folder, operation):
    # 获取 .atlas 文件的基本名称
    base_name = os.path.splitext(file)[0]
    # 获取 .atlas 文件原地址所在的上一级文件夹名字
    parent_folder_name = os.path.basename(os.path.dirname(os.path.join(root, file)))
    # 获取唯一的目标文件夹名称
    target_folder = get_unique_folder_name(destination_folder, base_name, parent_folder_name)
    try:
        os.makedirs(target_folder)
        print(f"已创建子文件夹: {target_folder}")
    except FileExistsError:
        # 这种情况理论上不应该发生,但为了以防万一
        print(f"文件夹 {target_folder} 已存在,尝试生成新的唯一名称...")
        target_folder = get_unique_folder_name(destination_folder, base_name + "_new", parent_folder_name)
        os.makedirs(target_folder)
        print(f"已创建新的子文件夹: {target_folder}")

    # 处理 .atlas 文件
    atlas_file_path = os.path.join(root, file)
    if operation == 'copy':
        shutil.copy2(atlas_file_path, target_folder)
        print(f"已复制 .atlas 文件: {atlas_file_path} 到 {target_folder}")
    else:
        shutil.move(atlas_file_path, target_folder)
        print(f"已移动 .atlas 文件: {atlas_file_path} 到 {target_folder}")

    # 寻找并处理 .json 或 .skel 文件
    for ext in ['.json', '.skel']:
        related_file = base_name + ext
        related_file_path = os.path.join(root, related_file)
        if os.path.exists(related_file_path):
            if operation == 'copy':
                shutil.copy2(related_file_path, target_folder)
                print(f"已复制 {ext} 文件: {related_file_path} 到 {target_folder}")
            else:
                shutil.move(related_file_path, target_folder)
                print(f"已移动 {ext} 文件: {related_file_path} 到 {target_folder}")

    # 【修改部分】从atlas文件中提取PNG文件名
    png_filenames = []
    try:
        with open(atlas_file_path, 'r', encoding='utf-8') as f:
            atlas_content = f.read()
            # 匹配所有包含.png的字符串(完整文件名)
            # 正则规则:匹配以任意字符开头,包含.png的完整文件名(直到换行符)
            png_pattern = re.compile(r'[^\n]+\.png', re.IGNORECASE)
            png_matches = png_pattern.findall(atlas_content)
            # 去重处理
            png_filenames = list(set(png_matches))
            print(f"从.atlas文件中提取到PNG文件名: {png_filenames}")
    except Exception as e:
        print(f"读取.atlas文件失败: {e}")
        return

    # 【修改部分】用提取到的文件名匹配PNG文件
    search_dirs = [root] + [os.path.join(root, d) for d in os.listdir(root) if os.path.isdir(os.path.join(root, d))]
    parent_dir = os.path.dirname(root)
    if parent_dir:
        search_dirs.append(parent_dir)

    for png_filename in png_filenames:
        found = False
        for search_dir in search_dirs:
            png_file_path = os.path.join(search_dir, png_filename)
            if os.path.exists(png_file_path) and os.path.isfile(png_file_path):
                if operation == 'copy':
                    shutil.copy2(png_file_path, target_folder)
                    print(f"已复制 .png 文件: {png_file_path} 到 {target_folder}")
                else:
                    shutil.move(png_file_path, target_folder)
                    print(f"已移动 .png 文件: {png_file_path} 到 {target_folder}")
                found = True
                break
        if not found:
            print(f"未找到PNG文件: {png_filename}")


def find_and_copy_files(source_folder, destination_folder, operation):
    # 确保目标文件夹存在
    if not os.path.exists(destination_folder):
        os.makedirs(destination_folder)
        print(f"已创建目标文件夹: {destination_folder}")

    atlas_files = []
    # 遍历源文件夹及其子文件夹,找出所有 .atlas 文件
    for root, _, files in os.walk(source_folder):
        for file in files:
            if file.endswith('.atlas'):
                atlas_files.append((root, file))

    # 获取 CPU 核心数
    cpu_count = multiprocessing.cpu_count()
    # 使用线程池并行处理 .atlas 文件
    with concurrent.futures.ThreadPoolExecutor(max_workers=cpu_count) as executor:
        futures = []
        for root, file in atlas_files:
            future = executor.submit(process_atlas_file, root, file, destination_folder, operation)
            futures.append(future)

        # 等待所有任务完成
        for future in concurrent.futures.as_completed(futures):
            try:
                future.result()
            except Exception as e:
                print(f"处理文件时出现错误: {e}")


if __name__ == "__main__":
    source_folder = input("请输入源文件夹路径: ").strip()
    destination_folder = input("请输入目标文件夹路径: ").strip()
    # 去除引号
    source_folder = source_folder.strip('"\'')
    destination_folder = destination_folder.strip('"\'')

    operation = input("请选择操作(输入 'copy' 进行复制,输入 'move' 进行移动): ").strip().lower()

    if operation not in ['copy', 'move']:
        print("输入的操作无效,请输入 'copy' 或 'move'。")
    elif os.path.exists(source_folder):
        print("开始查找并处理文件...")
        find_and_copy_files(source_folder, destination_folder, operation)
        print("文件处理完成。")
    else:
        print("源文件夹路径不存在,请检查后重新输入。")

    input("按回车键退出程序...")