+#define MFEMUL_NOFIELD 0
+#define MFEMUL_IDLE 1
+#define MFEMUL_SELECT1 2
+#define MFEMUL_SELECT2 3
+#define MFEMUL_SELECT3 4
+#define MFEMUL_AUTH1 5
+#define MFEMUL_AUTH2 6
+#define MFEMUL_WORK 7
+#define MFEMUL_WRITEBL2 8
+#define MFEMUL_INTREG_INC 9
+#define MFEMUL_INTREG_DEC 10
+#define MFEMUL_INTREG_REST 11
+#define MFEMUL_HALTED 12
+
+#define AC_DATA_READ 0
+#define AC_DATA_WRITE 1
+#define AC_DATA_INC 2
+#define AC_DATA_DEC_TRANS_REST 3
+#define AC_KEYA_READ 0
+#define AC_KEYA_WRITE 1
+#define AC_KEYB_READ 2
+#define AC_KEYB_WRITE 3
+#define AC_AC_READ 4
+#define AC_AC_WRITE 5
+
+#define AUTHKEYA 0
+#define AUTHKEYB 1
+#define AUTHKEYNONE 0xff
+
+
+static int ParamCardSizeBlocks(const char c) {
+ int numBlocks = 16 * 4;
+ switch (c) {
+ case '0' : numBlocks = 5 * 4; break;
+ case '2' : numBlocks = 32 * 4; break;
+ case '4' : numBlocks = 32 * 4 + 8 * 16; break;
+ default: numBlocks = 16 * 4;
+ }
+ return numBlocks;
+}
+
+static uint8_t BlockToSector(int block_num) {
+ if (block_num < 32 * 4) { // 4 blocks per sector
+ return (block_num / 4);
+ } else { // 16 blocks per sector
+ return 32 + (block_num - 32 * 4) / 16;
+ }
+}
+
+static bool IsTrailerAccessAllowed(uint8_t blockNo, uint8_t keytype, uint8_t action) {
+ uint8_t sector_trailer[16];
+ emlGetMem(sector_trailer, blockNo, 1);
+ uint8_t AC = ((sector_trailer[7] >> 5) & 0x04)
+ | ((sector_trailer[8] >> 2) & 0x02)
+ | ((sector_trailer[8] >> 7) & 0x01);
+ switch (action) {
+ case AC_KEYA_READ: {
+ return false;
+ break;
+ }
+ case AC_KEYA_WRITE: {
+ return ((keytype == AUTHKEYA && (AC == 0x00 || AC == 0x01))
+ || (keytype == AUTHKEYB && (AC == 0x04 || AC == 0x03)));
+ break;
+ }
+ case AC_KEYB_READ: {
+ return (keytype == AUTHKEYA && (AC == 0x00 || AC == 0x02 || AC == 0x01));
+ break;
+ }
+ case AC_KEYB_WRITE: {
+ return ((keytype == AUTHKEYA && (AC == 0x00 || AC == 0x04))
+ || (keytype == AUTHKEYB && (AC == 0x04 || AC == 0x03)));
+ break;
+ }
+ case AC_AC_READ: {
+ return ((keytype == AUTHKEYA)
+ || (keytype == AUTHKEYB && !(AC == 0x00 || AC == 0x02 || AC == 0x01)));
+ break;
+ }
+ case AC_AC_WRITE: {
+ return ((keytype == AUTHKEYA && (AC == 0x01))
+ || (keytype == AUTHKEYB && (AC == 0x03 || AC == 0x05)));
+ break;
+ }
+ default: return false;
+ }
+}
+
+
+static bool IsDataAccessAllowed(uint8_t blockNo, uint8_t keytype, uint8_t action)
+{
+ uint8_t sector_trailer[16];
+ emlGetMem(sector_trailer, SectorTrailer(blockNo), 1);
+
+ uint8_t sector_block;
+ if (blockNo < 32*4) {
+ sector_block = blockNo & 0x03;
+ } else {
+ sector_block = (blockNo & 0x0f) / 5;
+ }
+
+ uint8_t AC;
+ switch (sector_block) {
+ case 0x00: {
+ AC = ((sector_trailer[7] >> 2) & 0x04)
+ | ((sector_trailer[8] << 1) & 0x02)
+ | ((sector_trailer[8] >> 4) & 0x01);
+ break;
+ }
+ case 0x01: {
+ AC = ((sector_trailer[7] >> 3) & 0x04)
+ | ((sector_trailer[8] >> 0) & 0x02)
+ | ((sector_trailer[8] >> 5) & 0x01);
+ break;
+ }
+ case 0x02: {
+ AC = ((sector_trailer[7] >> 4) & 0x04)
+ | ((sector_trailer[8] >> 1) & 0x02)
+ | ((sector_trailer[8] >> 6) & 0x01);
+ break;
+ }
+ default:
+ return false;
+ }