Skip to content

Commit ec74e4b

Browse files
AXiX-officialK0lb3
authored andcommitted
cpp implementation for ArchiveStorageDecryptor.decrypt_block
1 parent fe7db50 commit ec74e4b

File tree

5 files changed

+117
-1
lines changed

5 files changed

+117
-1
lines changed

UnityPy/UnityPyBoost.pyi

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,10 @@ class TypeTreeNode:
3737
m_MetaFlag: Optional[int] = None
3838
m_RefTypeHash: Optional[int] = None
3939
_clean_name: str
40+
41+
def decrypt_block(
42+
index_bytes: bytes,
43+
substitute_bytes: bytes,
44+
data: Union[bytes, bytearray],
45+
index: int,
46+
) -> None: ...

UnityPy/helpers/ArchiveStorageManager.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@
44

55
from ..streams import EndianBinaryReader
66

7+
try:
8+
from UnityPy import UnityPyBoost
9+
except ImportError:
10+
UnityPyBoost = None
11+
712
UNITY3D_SIGNATURE = b"#$unity3dchina!@"
813
DECRYPT_KEY: bytes = None
914

@@ -101,6 +106,11 @@ def __init__(self, reader: EndianBinaryReader) -> None:
101106
)
102107

103108
def decrypt_block(self, data: bytes, index: int):
109+
110+
if UnityPyBoost:
111+
UnityPyBoost.decrypt_block(bytes(self.index), bytes(self.substitute), data, index)
112+
return data
113+
104114
offset = 0
105115
size = len(data)
106116
data = bytearray(data)
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
// based on https://github.com/RazTools/Studio/blob/main/AssetStudio/Crypto/UnityCN.cs
2+
3+
#include "ArchiveStorageDecryptor.hpp"
4+
#include <Python.h>
5+
6+
inline unsigned char decrypt_byte(unsigned char* bytes, uint64_t& offset, uint64_t& index, unsigned char* index_data, unsigned char* substitute_data)
7+
{
8+
unsigned char count_byte = substitute_data[((index >> 2) & 3) + 4]
9+
+ substitute_data[index & 3]
10+
+ substitute_data[((index >> 4) & 3) + 8]
11+
+ substitute_data[((unsigned char)index >> 6) + 12];
12+
bytes[offset] = (unsigned char)((index_data[bytes[offset] & 0xF] - count_byte) & 0xF | 0x10 * (index_data[bytes[offset] >> 4] - count_byte));
13+
count_byte = bytes[offset++];
14+
index++;
15+
return count_byte;
16+
}
17+
18+
inline uint64_t decrypt(unsigned char* bytes, uint64_t index, uint64_t remaining, unsigned char* index_data, unsigned char* substitute_data)
19+
{
20+
uint64_t offset = 0;
21+
22+
unsigned char current_byte = decrypt_byte(bytes, offset, index, index_data, substitute_data);
23+
uint64_t current_byte_high = current_byte >> 4;
24+
uint64_t current_byte_low = current_byte & 0xF;
25+
26+
if (current_byte_high == 0xF)
27+
{
28+
unsigned char count_byte;
29+
do
30+
{
31+
count_byte = decrypt_byte(bytes, offset, index, index_data, substitute_data);
32+
current_byte_high += count_byte;
33+
} while (count_byte == 0xFF);
34+
}
35+
36+
offset += current_byte_high;
37+
38+
if (offset < remaining)
39+
{
40+
decrypt_byte(bytes, offset, index, index_data, substitute_data);
41+
decrypt_byte(bytes, offset, index, index_data, substitute_data);
42+
if (current_byte_low == 0xF)
43+
{
44+
unsigned char count_byte;
45+
do
46+
{
47+
count_byte = decrypt_byte(bytes, offset, index, index_data, substitute_data);
48+
} while (count_byte == 0xFF);
49+
}
50+
}
51+
52+
return offset;
53+
}
54+
55+
PyObject* decrypt_block(PyObject* self, PyObject* args) {
56+
PyObject* py_index_bytes;
57+
PyObject* py_substitute_bytes;
58+
PyObject* py_data;
59+
uint64_t index;
60+
61+
if (!PyArg_ParseTuple(args, "OOOi", &py_index_bytes, &py_substitute_bytes, &py_data, &index)) {
62+
return NULL;
63+
}
64+
65+
Py_buffer view;
66+
if (PyObject_GetBuffer(py_data, &view, PyBUF_SIMPLE) != 0) {
67+
return NULL;
68+
}
69+
70+
if (!PyBytes_Check(py_index_bytes) || !PyBytes_Check(py_substitute_bytes)) {
71+
PyBuffer_Release(&view);
72+
PyErr_SetString(PyExc_TypeError, "Attributes 'index' and 'substitute' must be bytes");
73+
return NULL;
74+
}
75+
76+
unsigned char* data = (unsigned char*)view.buf;
77+
uint64_t size = (uint64_t)view.len;
78+
unsigned char* index_data = (unsigned char*)PyBytes_AS_STRING(py_index_bytes);
79+
unsigned char* substitute_data = (unsigned char*)PyBytes_AS_STRING(py_substitute_bytes);
80+
81+
uint64_t offset = 0;
82+
while (offset < size) {
83+
offset += decrypt(data + offset, index++, size - offset, index_data, substitute_data);
84+
}
85+
86+
PyBuffer_Release(&view);
87+
Py_RETURN_NONE;
88+
}
89+
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#define PY_SSIZE_T_CLEAN
2+
#pragma once
3+
#include <Python.h>
4+
5+
PyObject* decrypt_block(PyObject* self, PyObject* args);

UnityPyBoost/UnityPyBoost.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#include <Python.h>
33
#include "Mesh.hpp"
44
#include "TypeTreeHelper.hpp"
5+
#include "ArchiveStorageDecryptor.hpp"
56

67
/* Mesh.py */
78

@@ -14,6 +15,10 @@ static struct PyMethodDef method_table[] = {
1415
(PyCFunction)read_typetree,
1516
METH_VARARGS | METH_KEYWORDS,
1617
"replacement for TypeTreeHelper.read_typetree"},
18+
{"decrypt_block",
19+
(PyCFunction)decrypt_block,
20+
METH_VARARGS,
21+
"replacement for ArchiveStorageDecryptor.decrypt_block"},
1722
{NULL,
1823
NULL,
1924
0,
@@ -39,4 +44,4 @@ PyMODINIT_FUNC PyInit_UnityPyBoost(void)
3944
PyObject *module = PyModule_Create(&UnityPyBoost_module);
4045
add_typetreenode_to_module(module);
4146
return module;
42-
}
47+
}

0 commit comments

Comments
 (0)