2019-03-21 22:54:06 +01:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
2019-06-28 19:44:03 +02:00
|
|
|
#include <endianness.h>
|
2019-03-21 22:54:06 +01:00
|
|
|
|
2022-03-20 20:45:51 +01:00
|
|
|
#include <fxgxa.h>
|
2019-03-21 22:54:06 +01:00
|
|
|
#include <g1a.h>
|
|
|
|
|
2022-03-20 20:45:51 +01:00
|
|
|
/* check_g1a(): Check validity of a g1a control or fixed field
|
2019-03-21 22:54:06 +01:00
|
|
|
|
|
|
|
This function checks a single field of a g1a header (depending on the value
|
|
|
|
of @test, from 0 up) and returns:
|
|
|
|
* 0 if the field is valid
|
|
|
|
* 1 if there is a minor error (wrong fixed-byte entry)
|
|
|
|
* 2 if there is a major error (like not a g1a, bad checksum, etc)
|
|
|
|
* -1 if the value of @test is out of bounds
|
|
|
|
|
|
|
|
It produces a description of the check in @status (even if the test is
|
|
|
|
passed); the string should have room for at least 81 bytes.
|
|
|
|
|
|
|
|
@test Test number
|
|
|
|
@g1a G1A file being manipulated
|
|
|
|
@size File size
|
|
|
|
@status Array row, at least 81 bytes free */
|
2022-03-20 20:45:51 +01:00
|
|
|
#define m(msg, ...) sprintf(status, msg, ##__VA_ARGS__)
|
|
|
|
static int check_g1a(int test, struct g1a const *g1a, size_t size,char *status)
|
2019-03-21 22:54:06 +01:00
|
|
|
{
|
|
|
|
|
2022-03-20 20:45:51 +01:00
|
|
|
struct g1a_header const *h = &g1a->header;
|
2019-03-21 22:54:06 +01:00
|
|
|
uint8_t const *raw = (void *)h;
|
|
|
|
|
|
|
|
uint16_t sum;
|
|
|
|
uint8_t ctrl;
|
|
|
|
|
|
|
|
switch(test)
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
m("Signature \"USBPower\" \"########\"");
|
|
|
|
strncpy(status + 28, h->magic, 8);
|
|
|
|
return strncmp(h->magic, "USBPower", 8) ? 2:0;
|
|
|
|
case 1:
|
|
|
|
m("MCS Type 0xf3 0x%02x", h->mcs_type);
|
|
|
|
return (h->mcs_type != 0xf3) ? 2:0;
|
|
|
|
case 2:
|
|
|
|
m("Sequence 1 0x0010001000 0x%02x%02x%02x%02x%02x",
|
|
|
|
h->seq1[0], h->seq1[1], h->seq1[2], h->seq1[3], h->seq1[4]);
|
2022-03-20 20:45:51 +01:00
|
|
|
return memcmp((const char *)h->seq1, "\x00\x10\x00\x10\x00",
|
2019-03-21 22:54:06 +01:00
|
|
|
5) ? 1:0;
|
|
|
|
case 3:
|
|
|
|
ctrl = raw[0x13] + 0x41;
|
|
|
|
m("Control 1 0x%02x 0x%02x", ctrl, h->control1);
|
|
|
|
return (h->control1 != ctrl) ? 2:0;
|
|
|
|
case 4:
|
|
|
|
m("Sequence 2 0x01 0x%02x", h->seq2);
|
|
|
|
return (h->seq2 != 0x01) ? 1:0;
|
|
|
|
case 5:
|
|
|
|
m("File size 1 %-8zu %u", size,
|
|
|
|
be32toh(h->filesize_be1));
|
|
|
|
return (be32toh(h->filesize_be1) != size) ? 2:0;
|
|
|
|
case 6:
|
|
|
|
ctrl = raw[0x13] + 0xb8;
|
|
|
|
m("Control 2 0x%02x 0x%02x", ctrl, h->control2);
|
|
|
|
return (h->control2 != ctrl) ? 2:0;
|
|
|
|
case 7:
|
2022-03-20 20:45:51 +01:00
|
|
|
sum = checksum_g1a(g1a, size);
|
2019-03-21 22:54:06 +01:00
|
|
|
m("Checksum 0x%02x 0x%02x", sum,
|
|
|
|
be16toh(h->checksum));
|
|
|
|
return (be16toh(h->checksum) != sum) ? 2:0;
|
|
|
|
case 8:
|
|
|
|
m("File size 2 %-8zu %u", size,
|
|
|
|
be32toh(h->filesize_be2));
|
|
|
|
return (be32toh(h->filesize_be2) != size) ? 2:0;
|
|
|
|
default:
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-20 20:45:51 +01:00
|
|
|
/* unknown(): Dump the contents of an unknown field */
|
2019-03-21 22:54:06 +01:00
|
|
|
static void unknown(uint8_t const *data, size_t offset, size_t size)
|
|
|
|
{
|
|
|
|
printf(" 0x%03zx %-4zd 0x", offset, size);
|
|
|
|
for(size_t i = 0; i < size; i++) printf("%02x", data[offset + i]);
|
|
|
|
printf("\n");
|
|
|
|
}
|
|
|
|
|
2022-03-20 20:45:51 +01:00
|
|
|
/* field(): Print a potentially not NUL-terminated text field */
|
2019-03-21 22:54:06 +01:00
|
|
|
static void field(const char *field, size_t size)
|
|
|
|
{
|
|
|
|
for(size_t i = 0; i < size && field[i]; i++) putchar(field[i]);
|
|
|
|
printf("\n");
|
|
|
|
}
|
|
|
|
|
2022-03-20 20:45:51 +01:00
|
|
|
void dump_g1a(struct g1a const *g1a, size_t size)
|
2019-03-21 22:54:06 +01:00
|
|
|
{
|
2022-03-20 20:45:51 +01:00
|
|
|
struct g1a_header const *header = &g1a->header;
|
2019-03-21 22:54:06 +01:00
|
|
|
uint8_t const *raw = (void *)header;
|
|
|
|
|
|
|
|
/* Checks for g1a files */
|
|
|
|
char status[81];
|
2022-03-20 20:45:51 +01:00
|
|
|
int ret=0, passed=0;
|
2019-03-21 22:54:06 +01:00
|
|
|
|
|
|
|
printf("G1A signature checks:\n\n");
|
|
|
|
printf(" Sta. Field Expected Value\n");
|
|
|
|
|
|
|
|
for(int test = 0; ret >= 0; test++)
|
|
|
|
{
|
2022-03-20 20:45:51 +01:00
|
|
|
ret = check_g1a(test, g1a, size, status);
|
2019-03-21 22:54:06 +01:00
|
|
|
passed += !ret;
|
|
|
|
if(ret < 0) break;
|
|
|
|
|
|
|
|
printf(" %s %s\n", ret ? "FAIL" : "OK ", status);
|
|
|
|
}
|
|
|
|
|
|
|
|
printf("\nFields with unknown meanings:\n\n");
|
|
|
|
printf(" Offset Size Value\n");
|
|
|
|
|
|
|
|
unknown(raw, 0x015, 1);
|
|
|
|
unknown(raw, 0x018, 6);
|
|
|
|
unknown(raw, 0x028, 3);
|
|
|
|
unknown(raw, 0x02c, 4);
|
|
|
|
unknown(raw, 0x03a, 2);
|
|
|
|
unknown(raw, 0x04a, 2);
|
|
|
|
unknown(raw, 0x1d0, 4);
|
|
|
|
unknown(raw, 0x1dc, 20);
|
|
|
|
unknown(raw, 0x1f4, 12);
|
|
|
|
|
|
|
|
printf("\nApplication metadata:\n\n");
|
|
|
|
|
|
|
|
printf(" Program name: ");
|
|
|
|
field(header->name, 8);
|
|
|
|
printf(" Internal name: ");
|
|
|
|
field(header->internal, 8);
|
|
|
|
printf(" Version: ");
|
|
|
|
field(header->version, 10);
|
|
|
|
printf(" Build date: ");
|
|
|
|
field(header->date, 14);
|
|
|
|
|
|
|
|
printf("\nProgram icon:\n\n");
|
2022-03-20 20:45:51 +01:00
|
|
|
icon_print_1(header->icon, 30, 17);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* See check_g3a() for a description */
|
|
|
|
static int check_g3a(int test, struct g3a const *g3a, size_t size,char *status)
|
|
|
|
{
|
|
|
|
struct g3a_header const *h = &g3a->header;
|
|
|
|
uint8_t const *raw = (void *)h;
|
|
|
|
|
|
|
|
uint16_t sum;
|
|
|
|
uint32_t sum2;
|
|
|
|
uint8_t ctrl;
|
|
|
|
|
|
|
|
switch(test)
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
m("Signature \"USBPower\" \"########\"");
|
|
|
|
strncpy(status + 28, h->magic, 8);
|
|
|
|
return strncmp(h->magic, "USBPower", 8) ? 2:0;
|
|
|
|
case 1:
|
|
|
|
m("MCS Type 0x2c 0x%02x", h->mcs_type);
|
|
|
|
return (h->mcs_type != 0x2c) ? 2:0;
|
|
|
|
case 2:
|
|
|
|
m("Sequence 1 0x0010001000 0x%02x%02x%02x%02x%02x",
|
|
|
|
h->seq1[0], h->seq1[1], h->seq1[2], h->seq1[3], h->seq1[4]);
|
|
|
|
return memcmp((const char *)h->seq1, "\x00\x01\x00\x01\x00",
|
|
|
|
5) ? 1:0;
|
|
|
|
case 3:
|
|
|
|
ctrl = raw[0x13] + 0x41;
|
|
|
|
m("Control 1 0x%02x 0x%02x", ctrl, h->control1);
|
|
|
|
return (h->control1 != ctrl) ? 2:0;
|
|
|
|
case 4:
|
|
|
|
m("Sequence 2 0x01 0x%02x", h->seq2);
|
|
|
|
return (h->seq2 != 0x01) ? 1:0;
|
|
|
|
case 5:
|
|
|
|
m("File size 1 %-8zu %u", size,
|
|
|
|
be32toh(h->filesize_be1));
|
|
|
|
return (be32toh(h->filesize_be1) != size) ? 2:0;
|
|
|
|
case 6:
|
|
|
|
ctrl = raw[0x13] + 0xb8;
|
|
|
|
m("Control 2 0x%02x 0x%02x", ctrl, h->control2);
|
|
|
|
return (h->control2 != ctrl) ? 2:0;
|
|
|
|
case 7:
|
|
|
|
sum = checksum_g3a(g3a, size);
|
|
|
|
m("Checksum 0x%02x 0x%02x", sum,
|
|
|
|
be16toh(h->checksum));
|
|
|
|
return (be16toh(h->checksum) != sum) ? 2:0;
|
|
|
|
case 8:
|
|
|
|
m("File size 2 %-8zu %u", size - 0x7004,
|
|
|
|
be32toh(h->filesize_be2));
|
|
|
|
return (be32toh(h->filesize_be2) != size - 0x7004) ? 2:0;
|
|
|
|
case 9:
|
|
|
|
sum2 = checksum_g3a_2(g3a, size);
|
|
|
|
m("Checksum 2 0x%08x 0x%08x", sum2,
|
|
|
|
be32toh(h->checksum_2));
|
|
|
|
return (be32toh(h->checksum_2) != sum2) ? 2:0;
|
|
|
|
case 10:
|
|
|
|
m("File size 1 %-8zu %u", size,
|
|
|
|
be32toh(h->filesize_be3));
|
|
|
|
return (be32toh(h->filesize_be3) != size) ? 2:0;
|
|
|
|
case 11:
|
|
|
|
sum2 = checksum_g3a_2(g3a, size);
|
|
|
|
uint32_t footer = be32toh(*(uint32_t *)((void *)g3a+size-4));
|
|
|
|
m("Footer 0x%08x 0x%08x", sum2, footer);
|
|
|
|
return (footer != sum2) ? 2:0;
|
|
|
|
default:
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void dump_g3a(struct g3a const *g3a, size_t size)
|
|
|
|
{
|
|
|
|
struct g3a_header const *header = &g3a->header;
|
|
|
|
uint8_t const *raw = (void *)header;
|
|
|
|
|
|
|
|
/* Checks for g3a files */
|
|
|
|
char status[81];
|
|
|
|
int ret=0, passed=0;
|
|
|
|
|
|
|
|
printf("G3A signature checks:\n\n");
|
|
|
|
printf(" Sta. Field Expected Value\n");
|
|
|
|
|
|
|
|
for(int test = 0; ret >= 0; test++)
|
|
|
|
{
|
|
|
|
ret = check_g3a(test, g3a, size, status);
|
|
|
|
passed += !ret;
|
|
|
|
if(ret < 0) break;
|
|
|
|
|
|
|
|
printf(" %s %s\n", ret ? "FAIL" : "OK ", status);
|
|
|
|
}
|
|
|
|
|
|
|
|
printf("\nFields with unknown meanings:\n\n");
|
|
|
|
printf(" Offset Size Value\n");
|
|
|
|
|
|
|
|
unknown(raw, 0x015, 1);
|
|
|
|
unknown(raw, 0x018, 6);
|
|
|
|
unknown(raw, 0x026, 8);
|
|
|
|
unknown(raw, 0x032, 14);
|
|
|
|
unknown(raw, 0x050, 12);
|
|
|
|
unknown(raw, 0x12c, 4);
|
|
|
|
unknown(raw, 0x13a, 2);
|
|
|
|
unknown(raw, 0x14a, 38);
|
|
|
|
|
|
|
|
printf(" 0x590 2348 ");
|
|
|
|
bool is_zeros = true;
|
|
|
|
for(int i = 0; i < 2348; i++)
|
|
|
|
if(raw[0x590+i] != 0) is_zeros =1;
|
|
|
|
printf(is_zeros ? "<All zeros>\n" : "<Not shown (non-zero)>\n");
|
|
|
|
|
|
|
|
printf("\nApplication metadata:\n\n");
|
|
|
|
|
|
|
|
printf(" Program name: ");
|
|
|
|
field(header->name, 16);
|
|
|
|
printf(" Internal name: ");
|
|
|
|
field(header->internal, 11);
|
|
|
|
printf(" Version: ");
|
|
|
|
field(header->version, 10);
|
|
|
|
printf(" Build date: ");
|
|
|
|
field(header->date, 14);
|
|
|
|
printf(" Filename: ");
|
|
|
|
field(header->filename, 324);
|
|
|
|
|
|
|
|
printf("\nUnselected program icon:\n\n");
|
|
|
|
icon_print_16(header->icon_uns, 92, 64);
|
|
|
|
|
|
|
|
printf("\nSelected program icon:\n\n");
|
|
|
|
icon_print_16(header->icon_sel, 92, 64);
|
|
|
|
|
|
|
|
printf("\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
void dump(void *gxa, size_t size)
|
|
|
|
{
|
|
|
|
if(is_g1a(gxa))
|
|
|
|
return dump_g1a(gxa, size);
|
|
|
|
if(is_g3a(gxa))
|
|
|
|
return dump_g3a(gxa, size);
|
2019-03-21 22:54:06 +01:00
|
|
|
}
|