Jump to content

Recommended Posts

Posted (edited)

I really need help with Asphalt Legends UNITE PC version unknown files because I really wanted to dump the game files using BigChillGhost's python script and manifest.map but this script only supports v3.6.3a Android version and my sample file is v24.5.130.0 PC version (the latest version) because there's even more unicorn cars i wanted to rip. Is there anyone who can help me with it?

 

The process on how to rip all assets from separate apks using both manifest.map and the python script (For Android version of Asphalt 9 Legends Unite) was:

 

I) put both manifest.map and the python file in the dump folder with encrypted files

 

II) run the python script. It'll rename all the filenames contained in the manifest.map

manifest.7z restoreFileHierarchy.py Asphalt 9 PC sample.7z

In the meantime, I'll also link my other topic relating to Asphalt 9/Legends Unite jmodel: 

Edited by UndercoverBoy833
Linking my other link related to it + the steps of extracting unknown files
Posted
On 2025/4/12 at PM4点55分, UndercoverBoy833 said:

我急需《狂野飙车:联合》PC 版未知文件的帮助,因为我真的很想用 BigChillGhost 的 Python 脚本和 manifest.map 转储游戏文件,但这个脚本只支持 v3.6.3a 安卓版本,而我的示例文件是 v24.5.130.0 PC 版(最新版本),因为里面还有更多独角兽车我想提取。有谁能帮我吗?

 

使用 manifest.map 和 python 脚本(针对 Android 版 Asphalt 9 Legends Unite)从单独的 apk 中提取所有资产的过程如下:

 

I) 将 manifest.map 和 python 文件放入包含加密文件的 dump 文件夹中

 

II) 运行 python 脚本。它将重命名 manifest.map 中包含的所有文件名

清单.7z 207.24 千字节 · 1 次下载 恢复文件层次结构.py 796 B · 1 次下载 狂野飙车9 PC 样本.7z 1.89 MB · 2 次下载

与此同时,我还将链接与 Asphalt 9/Legends Unite jmodel 相关的其他主题: 

Some of the files you provide contain zstd compression

File Type 1:89 6A 74 65 78 20 78 0D

Compression position is 0x5B

But there are 22 bytes of additional data before this

These additional data need to be added to the compressed file at the same time and skipped when decompressed

4 bytes - extra data + total compressed data size

4 bytes - unknown, very close to the size of the compressed data

4 bytes - Decompressed size (correct)

File Type 2:89 6A 6D 6F 78 0D 0A 01

The example of this file starts after 510 bytes

Because two unknowns need to be read, four compression sizes and four decompressed sizes

File Type 3:02 AF EA 61 6E 67 69 65

79 EC DC 47

The verification is too small, and I haven't seen whether there is compression for the time being, I'm not sure

For the first two data, I wrote a script to decompress the compressed data inside and directly extract binary data of unknown structures.

All research is based on guessing from existing files, if a more complete script is needed, you may need to add features based on more files, because I can't guarantee that the offsets displayed by each file are the same

import os
import struct
import pyzstd
import random
import string
from pathlib import Path

def random_filename(length=16, extension='.bin'):
    chars = string.ascii_letters + string.digits
    return ''.join(random.choice(chars) for _ in range(length)) + extension

def process_first_format(f):
    extracted_data = []
    f.seek(69)
    
    while True:
        position = f.tell()
        size_bytes = f.read(4)
        if len(size_bytes) < 4: break
        
        data_size = struct.unpack('<I', size_bytes)[0]
        remaining_size = data_size - 4
        data = f.read(remaining_size)
        if len(data) < remaining_size:
            print(f"Insufficient data @ {position}")
            break
        
        full_data = size_bytes + data
        processed_data = full_data[22:] if len(full_data) > 22 else full_data
        
        if len(processed_data) >= 4 and processed_data[:4] == bytes.fromhex('28 B5 2F FD'):
            try:
                extracted_data.append(pyzstd.decompress(processed_data))
            except Exception as e:
                print(f"Decompression failed: {e}")
                extracted_data.append(processed_data)
        else:
            extracted_data.append(processed_data)
    
    return extracted_data

def process_second_format(f):
    extracted_data = []
    f.seek(510)
    
    while True:
        position = f.tell()
        unknown = f.read(2)
        if len(unknown) < 2: break
        
        compression_info = f.read(8)
        if len(compression_info) < 8: break
        
        compressed_size = struct.unpack('<I', compression_info[:4])[0]
        compressed_data = f.read(compressed_size)
        if len(compressed_data) < compressed_size:
            print(f"Insufficient data @ {position}")
            break
        
        if len(compressed_data) >= 4 and compressed_data[:4] == bytes.fromhex('28 B5 2F FD'):
            try:
                extracted_data.append(pyzstd.decompress(compressed_data))
            except Exception as e:
                print(f"Decompression failed: {e}")
                extracted_data.append(compressed_data)
        else:
            extracted_data.append(compressed_data)
    
    return extracted_data

def process_single_file(input_file, output_dir):
    try:
        with open(input_file, 'rb') as f:
            header = f.read(8)
            
            if header == bytes.fromhex('89 6A 74 65 78 20 78 0D'):
                print(f"Processing {input_file} (Format 1)")
                results = process_first_format(f)
            elif header == bytes.fromhex('89 6A 6D 6F 78 0D 0A 01'):
                print(f"Processing {input_file} (Format 2)")
                results = process_second_format(f)
            else:
                print(f"Skipping {input_file} (Unknown format)")
                return
            
            for i, data in enumerate(results, 1):
                output_path = Path(output_dir) / random_filename()
                with open(output_path, 'wb') as out_file:
                    out_file.write(data)
                print(f"Saved block {i} from {input_file}")
    
    except Exception as e:
        print(f"Error processing {input_file}: {e}")

def process_directory(input_dir, output_dir):
    Path(output_dir).mkdir(parents=True, exist_ok=True)
    
    for entry in os.scandir(input_dir):
        if entry.is_file():
            process_single_file(entry.path, output_dir)

if __name__ == '__main__':
    import sys
    if len(sys.argv) != 3:
        print("Usage: python asphalt.py <input_directory> <output_directory>")
        sys.exit(1)
    
    process_directory(sys.argv[1], sys.argv[2])

 

  • Like 1
  • Engineers
Posted (edited)

Skipping this additional data may do the trick.

A9tool (which comes with libzstd.dll, btw, if user cared for BigChillghosts post more carefully) gives me this:

a9tool c.jtex
Error: missing configuration file! Default settings have been used!
Processing c.jtex...     1048576, 262144, 65536, 16384, 4096, 1024, 256,finished
 

(Where c.jtex is renamed 000C022A608FF242 for easier handling on command line.)

The resulting c.jtex.pvr doesn't show a valid tex, so I'll recompile libzstd asap, with taking the additional data into acount.

Thanks for the hint.

(I'd like to check your python code, but module pyzstd is not standard (haha) on Windows systems.)

 

 

Edited by shak-otay
Posted (edited)

This work is done on other systems, because I can't install pyzstd on Windows system, it may be a problem with my python version, maybe some alternatives can be used

Edited by wq223
  • Engineers
Posted (edited)

I got your script running under windows after

python -m pip install pyzstd==0.15.3

and 11 bins where created from the c.jtex mentioned here.

I compared the biggest bin file (1 MB) to the c.jtex.pvr (from offset 0x34) created by A9tool and the data is identical (apart from the fact that c.jtex.pvr is bigger).

So far so good. But why then doesn't the pvr contain a valid texture?

 

 

Edited by shak-otay
Posted
34 minutes ago, shak-otay said:

I got your script running under windows after

python -m pip install pyzstd==0.15.3

and 11 bins where created from the c.jtex mentioned here.

I compared the biggest bin file (1 MB) to the c.jtex.pvr (from offset 0x34) created by A9tool and the data is identical (apart from the fact that c.jtex.pvr is bigger).

So far so good. But why then doesn't the pvr contain a valid texture?

 

 

probably different format, also have you tried getting into game memory for any file format type in the PC version (Windows Store) of this game? If not, then it's probably that.

  • Engineers
Posted (edited)

How should "getting into game memory" help? I guess it's another problem because the biggest decompressed block has some structures:

texAssumed.png

edit: I have a working file, car_Lamborghini_Terzo_Millennio_details_mk.tga.jtex, which provides a valid pvr, so I think I need to understand the .jtex headers.

What I found is that the above shown pvr picture has the pixelformat ASTC_5x5x5, while the valid pvr has ETC2 RGB.

Edited by shak-otay

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...