|
| 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 | + |
0 commit comments