summaryrefslogtreecommitdiffstats
path: root/src/ace3.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/ace3.c')
-rw-r--r--src/ace3.c244
1 files changed, 244 insertions, 0 deletions
diff --git a/src/ace3.c b/src/ace3.c
new file mode 100644
index 0000000..b96e6b4
--- /dev/null
+++ b/src/ace3.c
@@ -0,0 +1,244 @@
+/*
+ * ace3.c
+ * Functions to handle Ace3/uarp firmware format
+ *
+ * Copyright (c) 2024 Nikias Bassen, All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <string.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <libimobiledevice-glue/nskeyedarchive.h>
+
+#include "common.h"
+#include "ace3.h"
+#include "endianness.h"
+
+static uint32_t crc_buffer(const unsigned char* buffer, unsigned int bufsize, unsigned int salt)
+{
+ uint32_t result;
+ unsigned int i;
+ unsigned int j;
+
+ if ( !buffer )
+ return 0xFFFFFFFF;
+ result = salt;
+ for (i = 0; i < bufsize; ++i) {
+ for (j = 0; j != 8; ++j) {
+ unsigned int tmp0 = 2 * result;
+ unsigned int tmp1 = *(unsigned char*)(buffer + i);
+ unsigned int tmp2 = ((unsigned int)result >> 31) ^ ((tmp1 >> j) & 1);
+ result = (tmp2 + 2 * result) ^ 0x4C11DB6;
+ if (!tmp2)
+ result = tmp0;
+ }
+ }
+ return result;
+}
+
+int ace3_create_binary(const unsigned char* uarp_fw, size_t uarp_size, uint64_t bdid, unsigned int prev, plist_t tss, unsigned char** bin_out, size_t* bin_size)
+{
+ struct ace3bin_header {
+ uint32_t magic; // 0xACE00003
+ uint32_t unk4; // 0x00203400
+ uint32_t unk8; // 0x00002800
+ uint32_t header_size; // 0x00000040
+ uint32_t data1_size;
+ uint32_t data2_size;
+ uint32_t im4m_offset;
+ uint32_t im4m_dl_size;
+ uint32_t content_size;
+ uint32_t crc;
+ uint64_t fill1; // 0xFFFFFFFFFFFFFFFF
+ uint64_t fill2; // 0xFFFFFFFFFFFFFFFF
+ uint64_t fill3; // 0xFFFFFFFFFFFFFFFF
+ };
+
+ struct uarp_header {
+ uint32_t unk_00; // BE 0x00000002
+ uint32_t header_size; // BE usually 0x0000002C
+ uint32_t plist_offset; // BE
+ uint32_t unk_0c; // 0
+ uint32_t unk_10; // 0
+ uint32_t unk_14; // 0
+ uint32_t unk_18; // 0
+ uint32_t c_offset; // BE
+ uint32_t unk_20; // 0
+ uint32_t toc_offset; // BE usually 0x0000002c
+ uint32_t toc_size; // BE
+ };
+ struct uarp_toc_entry {
+ uint32_t this_size; // BE usually 0x28
+ uint32_t fourcc; // 'PT01' or similar
+ uint32_t index; // BE starting with 0, increment+1 for each entry
+ uint32_t unk_0c; // BE usually not zero
+ uint32_t unk_10; // BE usually 0
+ uint32_t unk_14; // BE usually 0
+ uint32_t unk_18; // BE other offset, not sure
+ uint32_t unk_1c; // BE usually 0
+ uint32_t offset; // BE
+ uint32_t size; //
+ };
+
+ plist_t p_im4m = plist_dict_get_item(tss, "USBPortController1,Ticket");
+ uint64_t im4m_size = 0;
+ const char* im4m = plist_get_data_ptr(p_im4m, &im4m_size);
+
+ struct uarp_header* uarp_hdr = (struct uarp_header*)uarp_fw;
+ uint32_t uarp_hdr_size = be32toh(uarp_hdr->header_size);
+ uint32_t plist_offset = be32toh(uarp_hdr->plist_offset);
+ uint32_t plist_size = uarp_size - plist_offset;
+ nskeyedarchive_t ka = nskeyedarchive_new_from_data(uarp_fw + plist_offset, plist_size);
+ if (!ka) {
+ return -1;
+ }
+ plist_t uarp_dict = nskeyedarchive_to_plist(ka);
+ nskeyedarchive_free(ka);
+
+ // find the corresponding entries for given BoardID+PREV
+
+ char* payload_4cc = NULL;
+ char* data_payload_4ccs = NULL;
+
+ plist_t sb_payloads = plist_dict_get_item(uarp_dict, "SuperBinary Payloads");
+ if (PLIST_IS_ARRAY(sb_payloads)) {
+ plist_array_iter iter = NULL;
+ plist_array_new_iter(sb_payloads, &iter);
+ plist_t payload = NULL;
+ do {
+ plist_array_next_item(sb_payloads, iter, &payload);
+ if (!payload) {
+ break;
+ }
+ plist_t meta = plist_dict_get_item(payload, "Payload MetaData");
+ if (!PLIST_IS_DICT(meta)) {
+ continue;
+ }
+ plist_t prefix = plist_dict_get_item(meta, "Personalization Manifest Prefix");
+ if (!PLIST_IS_STRING(prefix)) {
+ continue;
+ }
+ if (strcmp(plist_get_string_ptr(prefix, NULL), "USBPortController") != 0) {
+ continue;
+ }
+ plist_t p_boardid = plist_dict_get_item(meta, "Personalization Board ID (64 bits)");
+ if (!PLIST_IS_INT(p_boardid)) {
+ continue;
+ }
+ uint64_t boardid = 0;
+ plist_get_uint_val(p_boardid, &boardid);
+ if (boardid == bdid) {
+ plist_t p4cc = plist_dict_get_item(payload, "Payload 4CC");
+ plist_get_string_val(p4cc, &payload_4cc);
+ plist_t matching = plist_dict_get_item(meta, "Personalization Matching Data");
+ if (PLIST_IS_ARRAY(matching)) {
+ plist_array_iter iter2 = NULL;
+ plist_array_new_iter(matching, &iter2);
+ plist_t match = NULL;
+ do {
+ plist_array_next_item(matching, iter2, &match);
+ if (!PLIST_IS_DICT(match)) {
+ break;
+ }
+ uint64_t minrev = 0;
+ plist_t p_min = plist_dict_get_item(match, "Personalization Matching Data Product Revision Minimum");
+ plist_get_uint_val(p_min, &minrev);
+ uint64_t maxrev = 0;
+ plist_t p_max = plist_dict_get_item(match, "Personalization Matching Data Product Revision Maximum");
+ plist_get_uint_val(p_max, &maxrev);
+ if (prev >= minrev && prev <= maxrev) {
+ plist_t tags = plist_dict_get_item(match, "Personalization Matching Data Payload Tags");
+ plist_get_string_val(tags, &data_payload_4ccs);
+ break;
+ }
+ } while (match);
+ plist_mem_free(iter2);
+ }
+ break;
+ }
+ } while (payload);
+ plist_mem_free(iter);
+ }
+ if (!payload_4cc) {
+ printf("Failed to get payload 4cc\n");
+ return -1;
+ }
+ if (!data_payload_4ccs) {
+ printf("Failed to get data payload 4ccs\n");
+ return -1;
+ }
+
+ // now find the blobs in UARP data
+ uint32_t dl_offset = 0;
+ uint32_t dl_size = 0;
+ uint32_t data1_offset = 0;
+ uint32_t data1_size = 0;
+ uint32_t data2_offset = 0;
+ uint32_t data2_size = 0;
+ uint32_t toc_offset = be32toh(uarp_hdr->toc_offset);
+ uint32_t toc_size = be32toh(uarp_hdr->toc_size);
+ const unsigned char* p = uarp_fw + uarp_hdr_size;
+ while (p < uarp_fw + toc_size) {
+ struct uarp_toc_entry* entry = (struct uarp_toc_entry*)p;
+ uint32_t te_size = be32toh(entry->this_size);
+ if (strncmp((char*)&(entry->fourcc), payload_4cc, 4) == 0) {
+ dl_offset = be32toh(entry->offset);
+ dl_size = be32toh(entry->size);
+ } else if (strncmp((char*)&(entry->fourcc), data_payload_4ccs, 4) == 0) {
+ data1_offset = be32toh(entry->offset);
+ data1_size = be32toh(entry->size);
+ } else if (strncmp((char*)&(entry->fourcc), data_payload_4ccs+5, 4) == 0) {
+ data2_offset = be32toh(entry->offset);
+ data2_size = be32toh(entry->size);
+ }
+ p += te_size;
+ }
+
+ uint32_t content_size = data1_size + data2_size + im4m_size + dl_size;
+
+ *bin_out = (unsigned char*)malloc(0x40 + content_size);
+ struct ace3bin_header* hdr = (struct ace3bin_header*)(*bin_out);
+ hdr->magic = htole32(0xACE00003);
+ hdr->unk4 = htole32(0x00203400);
+ hdr->unk8 = htole32(0x00002800);
+ hdr->header_size = htole32(0x40);
+ hdr->data1_size = htole32(data1_size);
+ hdr->data2_size = htole32(data2_size);;
+ hdr->im4m_offset = htole32(0x40 + data1_size + data2_size);
+ hdr->im4m_dl_size = htole32(im4m_size + dl_size);
+ hdr->content_size = htole32(content_size);
+ hdr->crc = 0;
+ hdr->fill1 = 0xFFFFFFFFFFFFFFFFLL;
+ hdr->fill2 = 0xFFFFFFFFFFFFFFFFLL;
+ hdr->fill3 = 0xFFFFFFFFFFFFFFFFLL;
+
+ // write data1 payload
+ memcpy(*bin_out + 0x40, uarp_fw + data1_offset, data1_size);
+ // write data2 payload
+ memcpy(*bin_out + 0x40 + data1_size, uarp_fw + data2_offset, data2_size);
+ // write IM4M
+ memcpy(*bin_out + 0x40 + data1_size + data2_size, im4m, im4m_size);
+ // write dl payload
+ memcpy(*bin_out + 0x40 + data1_size + data2_size + im4m_size, uarp_fw + dl_offset, dl_size);
+
+ // calculate CRC and update header
+ hdr->crc = htole32(crc_buffer(*bin_out + 0x40, content_size, 0xFFFFFFFF));
+
+ *bin_size = 0x40 + content_size;
+
+ return 0;
+}