diff options
-rw-r--r-- | utils/atj2137/atjboottool/atjboottool.c | 258 |
1 files changed, 203 insertions, 55 deletions
diff --git a/utils/atj2137/atjboottool/atjboottool.c b/utils/atj2137/atjboottool/atjboottool.c index 44bf875309..5f4d8afd40 100644 --- a/utils/atj2137/atjboottool/atjboottool.c +++ b/utils/atj2137/atjboottool/atjboottool.c | |||
@@ -763,8 +763,8 @@ static int process_block_B(uint8_t block[512]) | |||
763 | return 0; | 763 | return 0; |
764 | } | 764 | } |
765 | 765 | ||
766 | static int do_fwu_v3(int size, uint8_t *buf, uint8_t *blockA, uint8_t *blockB, | 766 | static int get_key_fwu_v3(int size, uint8_t *buf, uint8_t *blockA, uint8_t *blockB, |
767 | uint8_t *unk, uint8_t *unk2, uint8_t *blo) | 767 | uint8_t *keybuf, uint8_t *blo) |
768 | { | 768 | { |
769 | (void) size; | 769 | (void) size; |
770 | uint8_t smallblock[512]; | 770 | uint8_t smallblock[512]; |
@@ -777,7 +777,7 @@ static int do_fwu_v3(int size, uint8_t *buf, uint8_t *blockA, uint8_t *blockB, | |||
777 | uint8_t bb = buf[0x1fe] & 0xf; | 777 | uint8_t bb = buf[0x1fe] & 0xf; |
778 | 778 | ||
779 | cprintf_field(" Block A: ", "%d\n", ba + 2); | 779 | cprintf_field(" Block A: ", "%d\n", ba + 2); |
780 | cprintf(" Block B: ", "%d\n", ba + bb + 5); | 780 | cprintf_field(" Block B: ", "%d\n", ba + bb + 5); |
781 | 781 | ||
782 | *blockA = buf[494] & 0xf; | 782 | *blockA = buf[494] & 0xf; |
783 | *blockB = buf[510] & 0xf; | 783 | *blockB = buf[510] & 0xf; |
@@ -814,10 +814,10 @@ static int do_fwu_v3(int size, uint8_t *buf, uint8_t *blockA, uint8_t *blockB, | |||
814 | cprintf(GREEN, " Crypto 4: "); | 814 | cprintf(GREEN, " Crypto 4: "); |
815 | check_field(ret, 0, "Pass\n", "Fail\n"); | 815 | check_field(ret, 0, "Pass\n", "Fail\n"); |
816 | 816 | ||
817 | memcpy(unk2, &smallbuf[17], 32); | 817 | memcpy(keybuf, &smallbuf[17], 32); |
818 | int offset = g_decode_A_info.nr_words + 91; | 818 | int offset = g_decode_A_info.nr_words + 91; |
819 | 819 | ||
820 | decode_block_with_swap(unk2, 0, &buf[offset], 512 - offset, g_perm_B); | 820 | decode_block_with_swap(keybuf, 0, &buf[offset], 512 - offset, g_perm_B); |
821 | 821 | ||
822 | int pos = *(uint16_t *)&buf[offset]; | 822 | int pos = *(uint16_t *)&buf[offset]; |
823 | cprintf_field(" Word: ", "%d ", pos); | 823 | cprintf_field(" Word: ", "%d ", pos); |
@@ -854,12 +854,6 @@ static int do_fwu_v3(int size, uint8_t *buf, uint8_t *blockA, uint8_t *blockB, | |||
854 | cprintf(GREEN, " Compare: "); | 854 | cprintf(GREEN, " Compare: "); |
855 | check_field(ret, 0, "Pass\n", "Fail\n"); | 855 | check_field(ret, 0, "Pass\n", "Fail\n"); |
856 | 856 | ||
857 | uint8_t zero[16]; | ||
858 | memset(zero, 0, sizeof(zero)); | ||
859 | ret = memcmp(unk, zero, sizeof(zero)); | ||
860 | cprintf(GREEN, " Sanity: "); | ||
861 | check_field(ret, 0, "Pass\n", "Fail\n"); | ||
862 | |||
863 | /* | 857 | /* |
864 | ret = memcmp(midbuf + 25, zero, sizeof(zero)); | 858 | ret = memcmp(midbuf + 25, zero, sizeof(zero)); |
865 | cprintf(GREEN, " Sanity: "); | 859 | cprintf(GREEN, " Sanity: "); |
@@ -869,15 +863,81 @@ static int do_fwu_v3(int size, uint8_t *buf, uint8_t *blockA, uint8_t *blockB, | |||
869 | return 0; | 863 | return 0; |
870 | } | 864 | } |
871 | 865 | ||
872 | static int do_sthg_fwu_v3(uint8_t *buf, int *size, uint8_t *unk, uint8_t *block) | 866 | /* stolen from https://github.com/nfd/atj2127decrypt, I have no idea from where |
867 | * he got this sequence of code. This code is really weird, I copy verbatim | ||
868 | * his authors comment below. */ | ||
869 | uint32_t atj2127_key[] = | ||
870 | { | ||
871 | 0x42146ea2, 0x892c8e85, 0x9f9f6d27, 0x545fedc3, | ||
872 | 0x09e5c0ca, 0x2dfa7e61, 0x4e5322e6, 0xb19185b9 | ||
873 | }; | ||
874 | |||
875 | /* decrypt a 512-byte sector */ | ||
876 | static void atj2127_decrypt_sector(void *inbuf, size_t size, | ||
877 | uint32_t session_key[8], int rounds_to_perform) | ||
878 | { | ||
879 | uint32_t key[8]; | ||
880 | for(int i = 0; i < 8; i++) | ||
881 | key[i] = atj2127_key[i] ^ session_key[i]; | ||
882 | uint32_t *buf = inbuf; | ||
883 | if(size % 32) | ||
884 | cprintf(GREY, "Size is not a multiple of 32!!!\n"); | ||
885 | while(rounds_to_perform > 0) | ||
886 | { | ||
887 | uint32_t rollover = buf[7] ^ session_key[7]; | ||
888 | |||
889 | buf[0] ^= key[1]; | ||
890 | buf[1] ^= key[2]; | ||
891 | buf[2] ^= key[3]; | ||
892 | buf[3] ^= key[4]; | ||
893 | buf[4] ^= key[5]; | ||
894 | buf[5] ^= key[6]; | ||
895 | buf[6] ^= key[7]; | ||
896 | buf[7] ^= key[1] ^ key[4]; | ||
897 | |||
898 | key[1] = key[2]; | ||
899 | key[2] = key[3]; | ||
900 | key[3] = key[4]; | ||
901 | key[4] = key[5]; | ||
902 | key[5] = key[6]; | ||
903 | key[6] = key[7]; | ||
904 | key[7] = rollover; | ||
905 | |||
906 | buf += 8; | ||
907 | rounds_to_perform -= 1; | ||
908 | } | ||
909 | } | ||
910 | |||
911 | static void atj2127_decrypt(uint8_t *dst, const uint8_t *src, size_t size, | ||
912 | uint8_t keybuf[32], int rounds_to_perform) | ||
913 | { | ||
914 | cprintf(BLUE, "ATJ2127:\n"); | ||
915 | cprintf_field(" Rounds: ", "%d\n", rounds_to_perform); | ||
916 | while(size > 0) | ||
917 | { | ||
918 | int sec_sz = MIN(size, 512); | ||
919 | memcpy(dst, src, sec_sz); | ||
920 | atj2127_decrypt_sector(dst, sec_sz, (uint32_t *)keybuf, rounds_to_perform); | ||
921 | src += sec_sz; | ||
922 | dst += sec_sz; | ||
923 | size -= sec_sz; | ||
924 | } | ||
925 | } | ||
926 | |||
927 | static bool check_afi(uint8_t *buf, int size); | ||
928 | |||
929 | static int decrypt_fwu_v3(uint8_t *buf, int *size, uint8_t block[512], bool force_atj2127) | ||
873 | { | 930 | { |
874 | uint8_t blockA; | 931 | uint8_t blockA; |
875 | uint8_t blockB; | 932 | uint8_t blockB; |
876 | uint8_t unk2[32]; | 933 | uint8_t keybuf[32]; |
877 | memset(unk2, 0, sizeof(unk2)); | 934 | memset(keybuf, 0, sizeof(keybuf)); |
878 | int ret = do_fwu_v3(*size, buf, &blockA, &blockB, unk, unk2, block); | 935 | int ret = get_key_fwu_v3(*size, buf, &blockA, &blockB, keybuf, block); |
879 | continue_the_force(ret); | 936 | continue_the_force(ret); |
880 | 937 | ||
938 | int file_size = *size; | ||
939 | /* the input buffer is reorganized based on two offsets (blockA and blockB), | ||
940 | * skip 2048 bytes of data used for crypto init */ | ||
881 | *size -= 2048; | 941 | *size -= 2048; |
882 | uint8_t *tmpbuf = malloc(*size); | 942 | uint8_t *tmpbuf = malloc(*size); |
883 | memset(tmpbuf, 0, *size); | 943 | memset(tmpbuf, 0, *size); |
@@ -887,9 +947,54 @@ static int do_sthg_fwu_v3(uint8_t *buf, int *size, uint8_t *unk, uint8_t *block) | |||
887 | memcpy(tmpbuf + offsetA, buf + offsetA + 1536, offsetB); | 947 | memcpy(tmpbuf + offsetA, buf + offsetA + 1536, offsetB); |
888 | memcpy(tmpbuf + offsetA + offsetB, | 948 | memcpy(tmpbuf + offsetA + offsetB, |
889 | buf + offsetA + 1536 + offsetB + 512, *size - offsetA - offsetB); | 949 | buf + offsetA + 1536 + offsetB + 512, *size - offsetA - offsetB); |
890 | compute_perm(unk2, 32, g_perm_B); | 950 | /* stolen from https://github.com/nfd/atj2127decrypt, I have no idea from where |
891 | decode_perm(tmpbuf, *size, g_perm_B); | 951 | * he got this sequence of code. This code is really weird, I copy verbatim |
892 | memcpy(buf, tmpbuf, *size); | 952 | * his authors comment below. |
953 | * | ||
954 | * This is really weird. This is passed to the decrypt-sector function and | ||
955 | * determines how much of each 512-byte sector to decrypt, where for every | ||
956 | * 32MB of size above the first 32MB, one 32 byte chunk of each sector | ||
957 | * (starting from the end) will remain unencrypted, up to a maximum of 480 | ||
958 | * bytes of plaintext. Was this a speed-related thing? It just seems | ||
959 | * completely bizarre. */ | ||
960 | |||
961 | /* NOTE: the original code uses the file length to determine how much | ||
962 | * to encrypt and not the size reported in the header. Since | ||
963 | * the file size can be different from the size reported in the header | ||
964 | * (the infamous 512 bytes described above), this might be wrong. */ | ||
965 | int rounds_to_perform = 16 - (file_size >> 0x19); | ||
966 | if(rounds_to_perform <= 0) | ||
967 | rounds_to_perform = 1; | ||
968 | /* the ATJ213x and ATJ2127 do not use the same encryption at this point, and I | ||
969 | * don't see any obvious way to tell which encryption is used (since they | ||
970 | * use the same version above). The only difference is that ATJ2127 images | ||
971 | * I have seen have an extra 512 bytes at the end file (ie the actual file | ||
972 | * is 512 bytes larger than indicated by the header) but I don't know if this | ||
973 | * is the case for all files. Thus, unless the user force encryption mode, | ||
974 | * try both and see if one looks like an AFI file. To guess which one to use, | ||
975 | * decrypt the first sector and see if it looks like an AFI file */ | ||
976 | bool is_atj2127 = false; | ||
977 | if(force_atj2127) | ||
978 | is_atj2127 = true; | ||
979 | else | ||
980 | { | ||
981 | uint8_t hdr_buf[512]; | ||
982 | atj2127_decrypt(hdr_buf, tmpbuf, sizeof(hdr_buf), keybuf, rounds_to_perform); | ||
983 | is_atj2127 = check_afi(hdr_buf, sizeof(hdr_buf)); | ||
984 | if(is_atj2127) | ||
985 | cprintf(BLUE, "File looks like an ATJ2127 firmware\n"); | ||
986 | else | ||
987 | cprintf(BLUE, "File does not looks like an ATJ2127 firmware\n"); | ||
988 | } | ||
989 | |||
990 | if(is_atj2127) | ||
991 | atj2127_decrypt(buf, tmpbuf, *size, keybuf, rounds_to_perform); | ||
992 | else | ||
993 | { | ||
994 | compute_perm(keybuf, 32, g_perm_B); | ||
995 | decode_perm(tmpbuf, *size, g_perm_B); | ||
996 | memcpy(buf, tmpbuf, *size); | ||
997 | } | ||
893 | 998 | ||
894 | return 0; | 999 | return 0; |
895 | } | 1000 | } |
@@ -925,7 +1030,7 @@ static void build_out_prefix(char *add, char *replace, bool slash) | |||
925 | } | 1030 | } |
926 | } | 1031 | } |
927 | 1032 | ||
928 | static int do_fwu(uint8_t *buf, int size) | 1033 | static int do_fwu(uint8_t *buf, int size, bool force_atj2127) |
929 | { | 1034 | { |
930 | struct fwu_hdr_t *hdr = (void *)buf; | 1035 | struct fwu_hdr_t *hdr = (void *)buf; |
931 | 1036 | ||
@@ -988,11 +1093,9 @@ static int do_fwu(uint8_t *buf, int size) | |||
988 | 1093 | ||
989 | if(g_version[ver].version == 3) | 1094 | if(g_version[ver].version == 3) |
990 | { | 1095 | { |
991 | uint8_t unk[32]; | ||
992 | memset(unk, 0, sizeof(unk)); | ||
993 | uint8_t block[512]; | 1096 | uint8_t block[512]; |
994 | memset(block, 0, sizeof(block)); | 1097 | memset(block, 0, sizeof(block)); |
995 | int ret = do_sthg_fwu_v3(buf, &size, unk, block); | 1098 | int ret = decrypt_fwu_v3(buf, &size, block, force_atj2127); |
996 | continue_the_force(ret); | 1099 | continue_the_force(ret); |
997 | 1100 | ||
998 | cprintf(GREY, "Descrambling to %s... ", g_out_prefix); | 1101 | cprintf(GREY, "Descrambling to %s... ", g_out_prefix); |
@@ -1198,7 +1301,7 @@ static int do_afi(uint8_t *buf, int size) | |||
1198 | return 0; | 1301 | return 0; |
1199 | } | 1302 | } |
1200 | 1303 | ||
1201 | static bool check_afi(uint8_t *buf, int size) | 1304 | bool check_afi(uint8_t *buf, int size) |
1202 | { | 1305 | { |
1203 | struct afi_hdr_t *hdr = (void *)buf; | 1306 | struct afi_hdr_t *hdr = (void *)buf; |
1204 | 1307 | ||
@@ -1257,11 +1360,28 @@ struct fw_hdr_t | |||
1257 | struct fw_entry_t entry[FW_ENTRIES]; | 1360 | struct fw_entry_t entry[FW_ENTRIES]; |
1258 | } __attribute__((packed)); | 1361 | } __attribute__((packed)); |
1259 | 1362 | ||
1260 | const uint8_t g_fw_signature[FW_SIG_SIZE] = | 1363 | /* the s1fwx source code has a layout but it does not make any sense for firmwares |
1364 | * found in ATJ2127 for example. In doubt just don't do anything */ | ||
1365 | struct fw_hdr_f0_t | ||
1366 | { | ||
1367 | uint8_t sig[FW_SIG_SIZE]; | ||
1368 | uint8_t res[12]; | ||
1369 | uint32_t checksum; | ||
1370 | uint8_t res2[492]; | ||
1371 | |||
1372 | struct fw_entry_t entry[FW_ENTRIES]; | ||
1373 | } __attribute__((packed)); | ||
1374 | |||
1375 | const uint8_t g_fw_signature_f2[FW_SIG_SIZE] = | ||
1261 | { | 1376 | { |
1262 | 0x55, 0xaa, 0xf2, 0x0f | 1377 | 0x55, 0xaa, 0xf2, 0x0f |
1263 | }; | 1378 | }; |
1264 | 1379 | ||
1380 | const uint8_t g_fw_signature_f0[FW_SIG_SIZE] = | ||
1381 | { | ||
1382 | 0x55, 0xaa, 0xf0, 0x0f | ||
1383 | }; | ||
1384 | |||
1265 | static void build_filename_fw(char buf[16], struct fw_entry_t *ent) | 1385 | static void build_filename_fw(char buf[16], struct fw_entry_t *ent) |
1266 | { | 1386 | { |
1267 | int pos = 0; | 1387 | int pos = 0; |
@@ -1286,42 +1406,60 @@ static int do_fw(uint8_t *buf, int size) | |||
1286 | cprintf(GREEN, " Signature:"); | 1406 | cprintf(GREEN, " Signature:"); |
1287 | for(int i = 0; i < FW_SIG_SIZE; i++) | 1407 | for(int i = 0; i < FW_SIG_SIZE; i++) |
1288 | cprintf(YELLOW, " %02x", hdr->sig[i]); | 1408 | cprintf(YELLOW, " %02x", hdr->sig[i]); |
1289 | if(memcmp(hdr->sig, g_fw_signature, FW_SIG_SIZE) == 0) | 1409 | int variant = 0; |
1290 | cprintf(RED, " Ok\n"); | 1410 | if(memcmp(hdr->sig, g_fw_signature_f2, FW_SIG_SIZE) == 0) |
1411 | { | ||
1412 | variant = 0xf2; | ||
1413 | cprintf(RED, " Ok (f2 variant)\n"); | ||
1414 | } | ||
1415 | else if(memcmp(hdr->sig, g_fw_signature_f0, FW_SIG_SIZE) == 0) | ||
1416 | { | ||
1417 | variant = 0xf0; | ||
1418 | cprintf(RED, " Ok (f0 variant)\n"); | ||
1419 | } | ||
1291 | else | 1420 | else |
1292 | { | 1421 | { |
1293 | cprintf(RED, " Mismatch\n"); | 1422 | cprintf(RED, " Mismatch\n"); |
1294 | let_the_force_flow(__LINE__); | 1423 | let_the_force_flow(__LINE__); |
1295 | } | 1424 | } |
1296 | 1425 | ||
1297 | cprintf_field(" USB VID: ", "0x%x\n", hdr->usb_vid); | 1426 | /* both variants have the same header size, only the fields differ */ |
1298 | cprintf_field(" USB PID: ", "0x%x\n", hdr->usb_pid); | 1427 | if(variant == 0xf2) |
1299 | cprintf_field(" Date: ", "%x/%x/%x%x\n", hdr->day, hdr->month, hdr->year[0], hdr->year[1]); | 1428 | { |
1300 | cprintf_field(" Checksum: ", "%x\n", hdr->checksum); | 1429 | cprintf_field(" USB VID: ", "0x%x\n", hdr->usb_vid); |
1301 | cprintf_field(" Productor: ", "%.16s\n", hdr->productor); | 1430 | cprintf_field(" USB PID: ", "0x%x\n", hdr->usb_pid); |
1302 | cprintf_field(" String 2: ", "%.16s\n", hdr->str2); | 1431 | cprintf_field(" Date: ", "%x/%x/%02x%02x\n", hdr->day, hdr->month, hdr->year[0], hdr->year[1]); |
1303 | cprintf_field(" String 3: ", "%.32s\n", hdr->str3); | 1432 | cprintf_field(" Checksum: ", "%x\n", hdr->checksum); |
1304 | cprintf_field(" Device Name: ", "%.32s\n", hdr->dev_name); | 1433 | cprintf_field(" Productor: ", "%.16s\n", hdr->productor); |
1305 | cprintf(GREEN, " Unknown:\n"); | 1434 | cprintf_field(" String 2: ", "%.16s\n", hdr->str2); |
1306 | for(int i = 0; i < 8; i++) | 1435 | cprintf_field(" String 3: ", "%.32s\n", hdr->str3); |
1436 | cprintf_field(" Device Name: ", "%.32s\n", hdr->dev_name); | ||
1437 | cprintf(GREEN, " Unknown:\n"); | ||
1438 | for(int i = 0; i < 8; i++) | ||
1439 | { | ||
1440 | cprintf(YELLOW, " "); | ||
1441 | for(int j = 0; j < 16; j++) | ||
1442 | cprintf(YELLOW, "%02x ", hdr->res2[i * 16 + j]); | ||
1443 | cprintf(YELLOW, "\n"); | ||
1444 | } | ||
1445 | cprintf_field(" USB Name 1: ", "%.8s\n", hdr->usb_name1); | ||
1446 | cprintf_field(" USB Name 2: ", "%.8s\n", hdr->usb_name2); | ||
1447 | cprintf_field(" MTP Name 1: ", "%.32s\n", hdr->mtp_name1); | ||
1448 | cprintf_field(" MTP Name 2: ", "%.32s\n", hdr->mtp_name2); | ||
1449 | cprintf_field(" MTP Version: ", "%.32s\n", hdr->mtp_ver); | ||
1450 | |||
1451 | cprintf_field(" MTP VID: ", "0x%x\n", hdr->mtp_vid); | ||
1452 | cprintf_field(" MTP PID: ", "0x%x\n", hdr->mtp_pid); | ||
1453 | cprintf_field(" FW Version: ", "%.64s\n", hdr->fw_ver); | ||
1454 | } | ||
1455 | else | ||
1307 | { | 1456 | { |
1308 | cprintf(YELLOW, " "); | 1457 | struct fw_hdr_f0_t *hdr_f0 = (void *)hdr; |
1309 | for(int j = 0; j < 16; j++) | 1458 | cprintf(GREEN, " Header not dumped because format is unclear.\n"); |
1310 | cprintf(YELLOW, "%02x ", hdr->res2[i * 16 + j]); | ||
1311 | cprintf(YELLOW, "\n"); | ||
1312 | } | 1459 | } |
1313 | cprintf_field(" USB Name 1: ", "%.8s\n", hdr->usb_name1); | ||
1314 | cprintf_field(" USB Name 2: ", "%.8s\n", hdr->usb_name2); | ||
1315 | cprintf_field(" MTP Name 1: ", "%.32s\n", hdr->mtp_name1); | ||
1316 | cprintf_field(" MTP Name 2: ", "%.32s\n", hdr->mtp_name2); | ||
1317 | cprintf_field(" MTP Version: ", "%.32s\n", hdr->mtp_ver); | ||
1318 | |||
1319 | cprintf_field(" MTP VID: ", "0x%x\n", hdr->mtp_vid); | ||
1320 | cprintf_field(" MTP PID: ", "0x%x\n", hdr->mtp_pid); | ||
1321 | cprintf_field(" FW Version: ", "%.64s\n", hdr->fw_ver); | ||
1322 | 1460 | ||
1323 | build_out_prefix(".unpack", "", true); | 1461 | build_out_prefix(".unpack", "", true); |
1324 | 1462 | ||
1325 | cprintf(BLUE, "Entries\n"); | 1463 | cprintf(BLUE, "Entries\n"); |
1326 | for(int i = 0; i < AFI_ENTRIES; i++) | 1464 | for(int i = 0; i < AFI_ENTRIES; i++) |
1327 | { | 1465 | { |
@@ -1365,7 +1503,11 @@ static bool check_fw(uint8_t *buf, int size) | |||
1365 | 1503 | ||
1366 | if(size < (int)sizeof(struct fw_hdr_t)) | 1504 | if(size < (int)sizeof(struct fw_hdr_t)) |
1367 | return false; | 1505 | return false; |
1368 | return memcmp(hdr->sig, g_fw_signature, FW_SIG_SIZE) == 0; | 1506 | if(memcmp(hdr->sig, g_fw_signature_f2, FW_SIG_SIZE) == 0) |
1507 | return true; | ||
1508 | if(memcmp(hdr->sig, g_fw_signature_f0, FW_SIG_SIZE) == 0) | ||
1509 | return true; | ||
1510 | return false; | ||
1369 | } | 1511 | } |
1370 | 1512 | ||
1371 | static void usage(void) | 1513 | static void usage(void) |
@@ -1380,6 +1522,7 @@ static void usage(void) | |||
1380 | printf(" --fwu\tUnpack a FWU firmware file\n"); | 1522 | printf(" --fwu\tUnpack a FWU firmware file\n"); |
1381 | printf(" --afi\tUnpack a AFI archive file\n"); | 1523 | printf(" --afi\tUnpack a AFI archive file\n"); |
1382 | printf(" --fw\tUnpack a FW archive file\n"); | 1524 | printf(" --fw\tUnpack a FW archive file\n"); |
1525 | printf(" --atj2127\tForce ATJ2127 decryption mode\n"); | ||
1383 | printf("The default is to try to guess the format.\n"); | 1526 | printf("The default is to try to guess the format.\n"); |
1384 | printf("If several formats are specified, all are tried.\n"); | 1527 | printf("If several formats are specified, all are tried.\n"); |
1385 | printf("If no output prefix is specified, a default one is picked.\n"); | 1528 | printf("If no output prefix is specified, a default one is picked.\n"); |
@@ -1391,7 +1534,8 @@ int main(int argc, char **argv) | |||
1391 | bool try_fwu = false; | 1534 | bool try_fwu = false; |
1392 | bool try_afi = false; | 1535 | bool try_afi = false; |
1393 | bool try_fw = false; | 1536 | bool try_fw = false; |
1394 | 1537 | bool force_atj2127 = false; | |
1538 | |||
1395 | while(1) | 1539 | while(1) |
1396 | { | 1540 | { |
1397 | static struct option long_options[] = | 1541 | static struct option long_options[] = |
@@ -1403,6 +1547,7 @@ int main(int argc, char **argv) | |||
1403 | {"fwu", no_argument, 0, 'u'}, | 1547 | {"fwu", no_argument, 0, 'u'}, |
1404 | {"afi", no_argument, 0, 'a'}, | 1548 | {"afi", no_argument, 0, 'a'}, |
1405 | {"fw", no_argument, 0, 'w'}, | 1549 | {"fw", no_argument, 0, 'w'}, |
1550 | {"atj2127", no_argument, 0, '2'}, | ||
1406 | {0, 0, 0, 0} | 1551 | {0, 0, 0, 0} |
1407 | }; | 1552 | }; |
1408 | 1553 | ||
@@ -1437,6 +1582,9 @@ int main(int argc, char **argv) | |||
1437 | case 'w': | 1582 | case 'w': |
1438 | try_fw = true; | 1583 | try_fw = true; |
1439 | break; | 1584 | break; |
1585 | case '2': | ||
1586 | force_atj2127 = true; | ||
1587 | break; | ||
1440 | default: | 1588 | default: |
1441 | abort(); | 1589 | abort(); |
1442 | } | 1590 | } |
@@ -1471,12 +1619,12 @@ int main(int argc, char **argv) | |||
1471 | perror("Cannot read file"); | 1619 | perror("Cannot read file"); |
1472 | return 1; | 1620 | return 1; |
1473 | } | 1621 | } |
1474 | 1622 | ||
1475 | fclose(fin); | 1623 | fclose(fin); |
1476 | 1624 | ||
1477 | int ret = -99; | 1625 | int ret = -99; |
1478 | if(try_fwu || check_fwu(buf, size)) | 1626 | if(try_fwu || check_fwu(buf, size)) |
1479 | ret = do_fwu(buf, size); | 1627 | ret = do_fwu(buf, size, force_atj2127); |
1480 | else if(try_afi || check_afi(buf, size)) | 1628 | else if(try_afi || check_afi(buf, size)) |
1481 | ret = do_afi(buf, size); | 1629 | ret = do_afi(buf, size); |
1482 | else if(try_fw || check_fw(buf, size)) | 1630 | else if(try_fw || check_fw(buf, size)) |
@@ -1486,7 +1634,7 @@ int main(int argc, char **argv) | |||
1486 | cprintf(GREY, "No valid format found\n"); | 1634 | cprintf(GREY, "No valid format found\n"); |
1487 | ret = 1; | 1635 | ret = 1; |
1488 | } | 1636 | } |
1489 | 1637 | ||
1490 | if(ret != 0) | 1638 | if(ret != 0) |
1491 | { | 1639 | { |
1492 | cprintf(GREY, "Error: %d", ret); | 1640 | cprintf(GREY, "Error: %d", ret); |