#include <stdio.h>

/*
NAND Flash Information Dumper
===============================
Created on July 12, 2020 by Jason Gin (ripitapart.com).
Made to display a specific NAND Flash ID code and corresponding parameter table,
but the data can easily be replaced with other Flash chips' data if desired.
*/

int main(void) {
	printf("NAND Flash Info & Parameter Page Dump:\n");
	printf("SanDisk High Endurance 128GB microSDXC Card\n");
	printf("Blog post for more info: wp.me/p21X5Z-vp or visit ripitapart.com\n\n");
	
	// Raw NAND info and parameter page data from SanDisk High Endurance 128GB microSDXC card
	unsigned char nand_flash_id[6] = { 0x45, 0x48, 0x9A, 0xB3, 0x7E, 0x72 };
	// NOTE: Real parameter page contains 4128 bytes but is just the same repeating data
	unsigned char parameter_page_data[512] =
	{
		0x53,0x4E,0x44,0x4B,0x53,0x4E,0x44,0x4B,0x53,0x4E,0x44,0x4B,0x53,0x4E,0x44,0x4B,
		0x53,0x4E,0x44,0x4B,0x53,0x4E,0x44,0x4B,0x53,0x4E,0x44,0x4B,0x53,0x4E,0x44,0x4B,
		0x08,0x08,0x00,0x08,0x06,0x20,0x00,0x02,0x01,0x48,0x9A,0xB3,0x00,0x05,0x08,0x41,
		0x48,0x63,0x6A,0x08,0x08,0x00,0x08,0x06,0x20,0x00,0x02,0x01,0x48,0x9A,0xB3,0x00,
		0x05,0x08,0x41,0x48,0x63,0x6A,0x08,0x08,0x00,0x08,0x06,0x20,0x00,0x02,0x01,0x48,
		0x9A,0xB3,0x00,0x05,0x08,0x41,0x48,0x63,0x6A,0x08,0x08,0x00,0x08,0x06,0x20,0x00,
		0x02,0x01,0x48,0x9A,0xB3,0x00,0x05,0x08,0x41,0x48,0x63,0x6A,0x08,0x08,0x00,0x08,
		0x06,0x20,0x00,0x02,0x01,0x48,0x9A,0xB3,0x00,0x05,0x08,0x41,0x48,0x63,0x6A,0x08,
		0x08,0x00,0x08,0x06,0x20,0x00,0x02,0x01,0x48,0x9A,0xB3,0x00,0x05,0x08,0x41,0x48,
		0x63,0x6A,0x08,0x08,0x00,0x08,0x06,0x20,0x00,0x02,0x01,0x48,0x9A,0xB3,0x00,0x05,
		0x08,0x41,0x48,0x63,0x6A,0x08,0x08,0x00,0x08,0x06,0x20,0x00,0x02,0x01,0x48,0x9A,
		0xB3,0x00,0x05,0x08,0x41,0x48,0x63,0x6A,0x08,0x08,0x00,0x08,0x06,0x20,0x00,0x02,
		0x01,0x48,0x9A,0xB3,0x00,0x05,0x08,0x41,0x48,0x63,0x6A,0x08,0x08,0x00,0x08,0x06,
		0x20,0x00,0x02,0x01,0x48,0x9A,0xB3,0x00,0x05,0x08,0x41,0x48,0x63,0x6A,0x08,0x08,
		0x00,0x08,0x06,0x20,0x00,0x02,0x01,0x48,0x9A,0xB3,0x00,0x05,0x08,0x41,0x48,0x63,
		0x6A,0x08,0x08,0x00,0x08,0x06,0x20,0x00,0x02,0x01,0x48,0x9A,0xB3,0x00,0x05,0x08,
		0x41,0x48,0x63,0x6A,0x08,0x08,0x00,0x08,0x06,0x20,0x00,0x02,0x01,0x48,0x9A,0xB3,
		0x00,0x05,0x08,0x41,0x48,0x63,0x6A,0x08,0x08,0x00,0x08,0x06,0x20,0x00,0x02,0x01,
		0x48,0x9A,0xB3,0x00,0x05,0x08,0x41,0x48,0x63,0x6A,0x08,0x08,0x00,0x08,0x06,0x20,
		0x00,0x02,0x01,0x48,0x9A,0xB3,0x00,0x05,0x08,0x41,0x48,0x63,0x6A,0x08,0x08,0x00,
		0x08,0x06,0x20,0x00,0x02,0x01,0x48,0x9A,0xB3,0x00,0x05,0x08,0x41,0x48,0x63,0x6A,
		0x08,0x08,0x00,0x08,0x06,0x20,0x00,0x02,0x01,0x48,0x9A,0xB3,0x00,0x05,0x08,0x41,
		0x48,0x63,0x6A,0x08,0x08,0x00,0x08,0x06,0x20,0x00,0x02,0x01,0x48,0x9A,0xB3,0x00,
		0x05,0x08,0x41,0x48,0x63,0x6A,0x08,0x08,0x00,0x08,0x06,0x20,0x00,0x02,0x01,0x48,
		0x9A,0xB3,0x00,0x05,0x08,0x41,0x48,0x63,0x6A,0x08,0x08,0x00,0x08,0x06,0x20,0x00,
		0x02,0x01,0x48,0x9A,0xB3,0x00,0x05,0x08,0x41,0x48,0x63,0x6A,0x08,0x08,0x00,0x08,
		0x06,0x20,0x00,0x02,0x01,0x48,0x9A,0xB3,0x00,0x05,0x08,0x41,0x48,0x63,0x6A,0x08,
		0x08,0x00,0x08,0x06,0x20,0x00,0x02,0x01,0x48,0x9A,0xB3,0x00,0x05,0x08,0x41,0x48,
		0x63,0x6A,0x08,0x08,0x00,0x08,0x06,0x20,0x00,0x02,0x01,0x48,0x9A,0xB3,0x00,0x05,
		0x08,0x41,0x48,0x63,0x6A,0x08,0x08,0x00,0x08,0x06,0x20,0x00,0x02,0x01,0x48,0x9A,
		0xB3,0x00,0x05,0x08,0x41,0x48,0x63,0x6A,0x08,0x08,0x00,0x08,0x06,0x20,0x00,0x02,
		0x01,0x48,0x9A,0xB3,0x00,0x05,0x08,0x41,0x48,0x63,0x6A,0x08,0x08,0x00,0x08,0x06
		};
		
		// Create parameters for hex/ascii printout and print out Flash ID
		int bytes_per_row = 16;
		int total_rows = sizeof(parameter_page_data) / bytes_per_row;

		int nand_flash_id_index = 0;
		printf("NAND Flash ID: ");
		for (nand_flash_id_index = 0; nand_flash_id_index < sizeof(nand_flash_id); nand_flash_id_index++)
		{
			printf("%.2X ", nand_flash_id[nand_flash_id_index]);	
		}
		printf("\n\n");
		
		printf("Parameter page contents:\n");
		printf("==========================\n");
		printf("Data array size = %d bytes\n",sizeof(parameter_page_data));
		printf("Bytes per row = %d\n",bytes_per_row);
		printf("Total rows = %d\n\n",total_rows);
		
		// Print out parameter page in human-readable form
		
		// Desired format:
		// Offset 00:01:02:03:04:05:06:07:08:09:0A:0B:0C:0D:0E:0F 0123456789ABCDEF
		// ------ --+--+--+--+--+--+--+--+--+--+--+--+--+--+--+-- ----------------
		// 0xHHHH HH HH HH HH HH HH HH HH HH HH HH HH HH HH HH HH AAAAAAAAAAAAAAAA
		
		int row_index, column_index;
		int current_offset = 0;
		
		printf("Offset 00:01:02:03:04:05:06:07:08:09:0A:0B:0C:0D:0E:0F 0123456789ABCDEF\n");
		printf("------ --+--+--+--+--+--+--+--+--+--+--+--+--+--+--+-- ----------------\n");
		for (row_index = 0; row_index < total_rows; row_index++)
		{
			// Byte offset in hex with padding
			printf("0x%.4X ", row_index * bytes_per_row);
			
			// Hexadecimal representation
			current_offset = (row_index * column_index);
			for (column_index = 0; column_index < bytes_per_row; column_index++)
			{
				printf("%.2X ", parameter_page_data[current_offset]);
				current_offset++;
			}
			
			// ASCII representation
			current_offset = (row_index * column_index);
			for (column_index = 0; column_index < bytes_per_row; column_index++)
			{
				if (parameter_page_data[current_offset] < 0x20) // less than 0x20 are ASCII control codes
					printf(".");
				else if ((parameter_page_data[current_offset] >= 0x20) && (parameter_page_data[current_offset] < 0x80))
					printf("%c", parameter_page_data[current_offset]);
				else // 0x80 or greater is not valid for UTF-8
					printf(".");
				current_offset++;
			}
			printf("\n");
		}
	return 0;
}
