Tested and verified NES 2.0 header functionality

This commit is contained in:
Kreeblah 2021-01-26 00:16:08 -08:00
parent c18d26e09f
commit dcafe9b5a9
2 changed files with 216 additions and 256 deletions

View File

@ -12,16 +12,17 @@
//26 Supported Mappers //26 Supported Mappers
//101 Defines //101 Defines
//131 Variables //131 Variables
//189 Menu //191 Menu
//308 Setup //310 Setup
//337 Low Level Functions //339 Low Level Functions
//584 CRC Functions //586 CRC Functions
//639 File Functions //645 File Functions
//801 Config Functions //819 NES 2.0 Header Functions
//1397 ROM Functions //1095 Config Functions
//2490 RAM Functions //1691 ROM Functions
//2925 Eeprom Functions //2789 RAM Functions
//3115 NESmaker Flash Cart Functions //3224 Eeprom Functions
//3414 NESmaker Flash Cart Functions
/****************************************** /******************************************
Supported Mappers Supported Mappers
@ -169,6 +170,8 @@ boolean flashfound = false; // NESmaker 39SF040 Flash Cart
File sdFile; File sdFile;
char fileCount[3]; char fileCount[3];
File nesFile; File nesFile;
uint32_t prg_crc32;
uint32_t chr_crc32;
char filePRG[] = "PRG.bin"; char filePRG[] = "PRG.bin";
char fileCHR[] = "CHR.bin"; char fileCHR[] = "CHR.bin";
char fileNES[] = "CART.nes"; char fileNES[] = "CART.nes";
@ -621,7 +624,7 @@ uint32_t crc32EEP(File &file, uint32_t &charcnt) {
return ~oldcrc32; return ~oldcrc32;
} }
void calcCRC(char* checkFile, unsigned long filesize) { void calcCRC(char* checkFile, unsigned long filesize, uint32_t* crcCopy) {
uint32_t crc; uint32_t crc;
crcFile = sd.open(checkFile); crcFile = sd.open(checkFile);
if (filesize < 1024) if (filesize < 1024)
@ -631,6 +634,10 @@ void calcCRC(char* checkFile, unsigned long filesize) {
crcFile.close(); crcFile.close();
sprintf(tempCRC, "%08lX", crc); sprintf(tempCRC, "%08lX", crc);
if (crcCopy != NULL) {
*crcCopy = crc;
}
print_Msg(F("CRC: ")); print_Msg(F("CRC: "));
println_Msg(tempCRC); println_Msg(tempCRC);
display_Update(); display_Update();
@ -724,31 +731,8 @@ void CreateRAMFileInSD() {
void outputNES() { void outputNES() {
display_Clear(); display_Clear();
uint32_t prg_crc32 = 0;
uint32_t prg_size_bytes = 0;
File prg_crc32_file;
if(prg_crc32_file.open(filePRG, O_READ))
{
prg_size_bytes = 1024 * prg;
prg_crc32 = crc32(prg_crc32_file, prg_size_bytes);
prg_crc32_file.close();
}
uint32_t chr_crc32 = 0;
uint32_t chr_size_bytes = 0;
if(chr > 0)
{
File chr_crc32_file;
if(prg_crc32_file.open(fileCHR, O_READ))
{
chr_size_bytes = 1024 * chr;
chr_crc32 = crc32(chr_crc32_file, chr_size_bytes);
chr_crc32_file.close();
}
}
unsigned char* nes_header_bytes = getNESHeaderForFileInfo(1024 * prg, 1024 * chr, prg_crc32, chr_crc32); unsigned char* nes_header_bytes = getNESHeaderForFileInfo(1024 * prg, 1024 * chr, prg_crc32, chr_crc32);
LED_RED_ON; LED_RED_ON;
LED_GREEN_ON; LED_GREEN_ON;
LED_BLUE_ON; LED_BLUE_ON;
@ -775,10 +759,13 @@ void outputNES() {
print_Error(F("SD Error"), true); print_Error(F("SD Error"), true);
} }
if(nes_header_bytes != NULL) if (nes_header_bytes != NULL)
{ {
nesFile.write(nes_header_bytes, 16); nesFile.write(nes_header_bytes, 16);
free(nes_header_bytes); free(nes_header_bytes);
display_Clear();
println_Msg(F("SET HEADER"));
display_Update();
} }
size_t n; size_t n;
@ -805,11 +792,12 @@ void outputNES() {
nesFile.flush(); nesFile.flush();
nesFile.close(); nesFile.close();
display_Clear();
println_Msg(F("NES FILE OUTPUT!")); println_Msg(F("NES FILE OUTPUT!"));
println_Msg(F("")); println_Msg(F(""));
display_Update(); display_Update();
calcCRC(fileNES, (prg + chr) * 1024); calcCRC(fileNES, (prg + chr) * 1024, NULL);
LED_RED_OFF; LED_RED_OFF;
LED_GREEN_OFF; LED_GREEN_OFF;
LED_BLUE_OFF; LED_BLUE_OFF;
@ -833,303 +821,275 @@ void CartFinish() {
NES 2.0 Header Functions NES 2.0 Header Functions
*****************************************/ *****************************************/
unsigned char* getNESHeaderForFileInfo(size_t prg_size, size_t chr_size, uint32_t prg_crc32, uint32_t chr_crc32) unsigned char* getNESHeaderForFileInfo(size_t prg_size, size_t chr_size, uint32_t prg_crc32, uint32_t chr_crc32) {
{ if (prg_size == 0) {
if(prg_size == 0)
{
return NULL; return NULL;
} }
File nesDb;
char* temp_line; char* temp_line;
char* nes20_header;
int i; int i;
if(!nesDb.open("nes20db.txt", O_READ)) if (!sdFile.open("/nes20db.txt", FILE_READ)) {
{
return NULL; return NULL;
} else {
display_Clear();
println_Msg(F("SEARCHING DB"));
display_Update();
} }
temp_line = malloc(256 * sizeof(char)); temp_line = malloc(256 * sizeof(char));
while(nesDb.available()) while (sdFile.available()) {
{ // We're reading fixed-length lines
i = 0; // padded with null characters
char temp_char; sdFile.read(temp_line, 256);
do
{
temp_char = nesDb.read();
temp_line[i] = temp_char;
i++;
} while(nesDb.available() && temp_char != 0 && temp_char != '\n');
i--;
if(temp_line[i] == '\n')
{
temp_line[i] = 0;
}
size_t prg_size_db; size_t prg_size_db;
size_t chr_size_db; size_t chr_size_db;
uint32_t prg_crc32_db; uint32_t prg_crc32_db;
uint32_t chr_crc32_db; uint32_t chr_crc32_db;
// Match PRG and CHR sizes first, then
// match PRG CRC32 and, if the CHR size
// is greater than zero, the CHR CRC32
// as well.
prg_size_db = getPRGSizeFromDatabaseRow(temp_line); prg_size_db = getPRGSizeFromDatabaseRow(temp_line);
if(prg_size == prg_size_db) if (prg_size == prg_size_db) {
{
chr_size_db = getCHRSizeFromDatabaseRow(temp_line); chr_size_db = getCHRSizeFromDatabaseRow(temp_line);
if(chr_size == chr_size_db) if (chr_size == chr_size_db) {
{
prg_crc32_db = getPRGCRC32FromDatabaseRow(temp_line); prg_crc32_db = getPRGCRC32FromDatabaseRow(temp_line);
if(prg_crc32 == prg_crc32_db) if (prg_crc32 == prg_crc32_db) {
{ if (chr_size == 0) {
if(chr_size == 0) nes20_header = getNES20HeaderBytesFromDatabaseRow(temp_line);
{ free(temp_line);
nesDb.close(); sdFile.close();
return getNES20HeaderBytesFromDatabaseRow(temp_line); return nes20_header;
} } else {
else if(chr_crc32 == chr_crc32_db) chr_crc32_db = getCHRCRC32FromDatabaseRow(temp_line);
{ if (chr_crc32 == chr_crc32_db) {
nesDb.close(); nes20_header = getNES20HeaderBytesFromDatabaseRow(temp_line);
return getNES20HeaderBytesFromDatabaseRow(temp_line); free(temp_line);
sdFile.close();
return nes20_header;
}
} }
} }
} }
} }
} }
nesDb.close(); free(temp_line);
sdFile.close();
return NULL; return NULL;
} }
char* getDatabaseFieldFromRow(const char* dbstr, uint8_t fieldnum) // IMPORTANT: The string returned from this function MUST
{ // be passed to free() when ready to be disposed of, in
uint8_t field_start_pos = 0; // order to avoid a memory leak.
uint8_t field_end_pos = 1; char* getDatabaseFieldFromRow(const char* dbstr, uint8_t fieldnum) {
uint8_t current_field = 0; uint8_t field_start_pos = 0;
char* return_field; uint8_t field_end_pos = 1;
uint8_t current_field = 0;
char* return_field;
if(dbstr == NULL || fieldnum > 5) // Field order, beginning with field 0:
{ // PRG Size, CHR Size, PRG CRC32, CHR CRC32, Game Title, NES 2.0 Header (as ASCII)
return NULL; //
// Each entry is on its own line, with a field delimeter of ^^
// I'm assuming that nothing will ever use ^^ in a game title, but it's possible
// that could be wrong, in which case a different field delimeter would need
// to be used, and the logic here updated.
if (dbstr == NULL || fieldnum > 5) {
return NULL;
}
if (dbstr[0] == 0 || dbstr[0] == '\n') {
return NULL;
}
for (; field_end_pos < 255 && current_field < fieldnum; field_end_pos++) {
if (field_start_pos < 254 && dbstr[field_start_pos] == '^' && dbstr[field_start_pos + 1] == '^') {
current_field++;
field_start_pos = field_end_pos;
field_end_pos = field_start_pos + 1;
} }
if(dbstr[0] == 0 || dbstr[0] == '\n') if (current_field < fieldnum && dbstr[field_end_pos - 1] == '^' && dbstr[field_end_pos] == '^' || dbstr[field_end_pos] == 0 || dbstr[field_end_pos] == '\n') {
{ current_field++;
return NULL; field_start_pos = field_end_pos + 1;
field_end_pos = field_start_pos + 1;
} }
}
for(;field_end_pos < 255 && current_field < fieldnum; field_end_pos++) field_end_pos = field_start_pos;
{
if(field_start_pos < 254 && dbstr[field_start_pos] == '^' && dbstr[field_start_pos + 1] == '^')
{
current_field++;
field_start_pos = field_end_pos;
field_end_pos = field_start_pos + 1;
}
if(current_field < fieldnum && dbstr[field_end_pos - 1] == '^' && dbstr[field_end_pos] == '^' || dbstr[field_end_pos] == 0 || dbstr[field_end_pos] == '\n') while ((dbstr[field_end_pos - 1] != '^' || dbstr[field_end_pos] != '^') && dbstr[field_end_pos] != 0 && dbstr[field_end_pos] != '\n') {
{ field_end_pos++;
current_field++; }
field_start_pos = field_end_pos + 1;
field_end_pos = field_start_pos + 1;
}
}
field_end_pos = field_start_pos; if (dbstr[field_end_pos] == '^') {
field_end_pos = field_end_pos - 2;
} else {
field_end_pos = field_end_pos - 1;
}
while((dbstr[field_end_pos - 1] != '^' || dbstr[field_end_pos] != '^') && dbstr[field_end_pos] != 0 && dbstr[field_end_pos] != '\n') if ((field_end_pos - field_start_pos + 2) == 0) {
{ return NULL;
field_end_pos++; }
}
if(dbstr[field_end_pos] == '^') return_field = (char*)malloc((field_end_pos - field_start_pos + 2) * sizeof(char));
{
field_end_pos = field_end_pos - 2;
}
else
{
field_end_pos = field_end_pos - 1;
}
if((field_end_pos - field_start_pos + 2) == 0) memcpy(return_field, &dbstr[field_start_pos], field_end_pos - field_start_pos + 1);
{
return NULL;
}
return_field = (char*)malloc((field_end_pos - field_start_pos + 2) * sizeof(char)); return_field[(field_end_pos - field_start_pos) + 1] = 0;
memcpy(return_field, &dbstr[field_start_pos], field_end_pos - field_start_pos + 1); return return_field;
return_field[(field_end_pos - field_start_pos) + 1] = 0;
return return_field;
} }
unsigned char getNibbleFromChar(char num) unsigned char getNibbleFromChar(char num) {
{ char ret_char = num & 0x0F;
char ret_char = num & 0x0F; if (num > '9') {
if(num > '9') ret_char += 9;
{ }
ret_char += 9;
}
return ret_char; return ret_char;
} }
unsigned char getByteFromChars(char msn, char lsn) unsigned char getByteFromChars(char msn, char lsn) {
{ unsigned char return_char;
unsigned char return_char; return_char = (getNibbleFromChar(msn) << 4);
return_char = (getNibbleFromChar(msn) << 4); return_char |= getNibbleFromChar(lsn);
return_char |= getNibbleFromChar(lsn);
return return_char; return return_char;
} }
unsigned char* strToBytes(const char* bytestr) unsigned char* strToBytes(const char* bytestr) {
{ uint8_t str_length;
uint8_t str_length; uint8_t byte_length;
uint8_t byte_length; uint8_t str_idx;
uint8_t str_idx; uint8_t byte_idx = 0;
uint8_t byte_idx = 0; unsigned char* byte_arr;
unsigned char* byte_arr;
if(bytestr == NULL) if (bytestr == NULL) {
{ return NULL;
return NULL; }
str_length = (uint8_t)strlen(bytestr);
if (str_length % 2 != 0) {
return NULL;
}
byte_length = str_length / 2;
byte_arr = (unsigned char*)malloc(byte_length * sizeof(unsigned char));
for (str_idx = 0; str_idx < str_length && bytestr[str_idx] != 0; str_idx = str_idx + 2) {
if (!isxdigit(bytestr[str_idx]) || !isxdigit(bytestr[str_idx + 1])) {
free(byte_arr);
return NULL;
} }
str_length = (uint8_t)strlen(bytestr); byte_arr[byte_idx] = getByteFromChars(bytestr[str_idx], bytestr[str_idx + 1]);
byte_idx++;
}
if(str_length % 2 != 0) return byte_arr;
{
return NULL;
}
byte_length = str_length / 2;
byte_arr = (unsigned char*)malloc(byte_length * sizeof(unsigned char));
for(str_idx = 0; str_idx < str_length && bytestr[str_idx] != 0; str_idx = str_idx + 2)
{
if(!isxdigit(bytestr[str_idx]) || !isxdigit(bytestr[str_idx + 1]))
{
free(byte_arr);
return NULL;
}
byte_arr[byte_idx] = getByteFromChars(bytestr[str_idx], bytestr[str_idx + 1]);
byte_idx++;
}
return byte_arr;
} }
uint32_t crc32FromBytes(const unsigned char* bytearr) uint32_t crc32FromBytes(const unsigned char* bytearr) {
{ if (bytearr == NULL) {
if(bytearr == NULL) return 0;
{ }
return 0;
}
return (uint32_t)(((uint32_t)bytearr[0] << 24) | ((uint32_t)bytearr[1] << 16) | ((uint32_t)bytearr[2] << 8) | (uint32_t)bytearr[3]); return (uint32_t)(((uint32_t)bytearr[0] << 24) | ((uint32_t)bytearr[1] << 16) | ((uint32_t)bytearr[2] << 8) | (uint32_t)bytearr[3]);
} }
size_t getPRGSizeFromDatabaseRow(const char* crctest) size_t getPRGSizeFromDatabaseRow(const char* crctest) {
{ char* prg_size_str = getDatabaseFieldFromRow(crctest, 0);
char* prg_size_str = getDatabaseFieldFromRow(crctest, 0); if (prg_size_str == NULL) {
if(prg_size_str == NULL) return 0;
{ }
return 0;
}
size_t return_size = (size_t)atoi(prg_size_str); size_t return_size = (size_t)atoi(prg_size_str);
free(prg_size_str); free(prg_size_str);
return return_size; return return_size;
} }
size_t getCHRSizeFromDatabaseRow(const char* crctest) size_t getCHRSizeFromDatabaseRow(const char* crctest) {
{ char* chr_size_str = getDatabaseFieldFromRow(crctest, 1);
char* chr_size_str = getDatabaseFieldFromRow(crctest, 1); if (chr_size_str == NULL) {
if(chr_size_str == NULL) return 0;
{ }
return 0;
}
size_t return_size = (size_t)atoi(chr_size_str); size_t return_size = (size_t)atoi(chr_size_str);
free(chr_size_str); free(chr_size_str);
return return_size; return return_size;
} }
uint32_t getPRGCRC32FromDatabaseRow(const char* crctest) uint32_t getPRGCRC32FromDatabaseRow(const char* crctest) {
{ char* prg_crc32_str = getDatabaseFieldFromRow(crctest, 2);
char* prg_crc32_str = getDatabaseFieldFromRow(crctest, 2); if (prg_crc32_str == NULL) {
if(prg_crc32_str == NULL) return 0;
{ }
return 0;
}
unsigned char* prg_crc32_bytes = strToBytes(prg_crc32_str); unsigned char* prg_crc32_bytes = strToBytes(prg_crc32_str);
free(prg_crc32_str); free(prg_crc32_str);
if(prg_crc32_bytes == NULL) if (prg_crc32_bytes == NULL) {
{ return 0;
return 0; }
}
uint32_t prg_crc32 = crc32FromBytes(prg_crc32_bytes); uint32_t prg_crc32 = crc32FromBytes(prg_crc32_bytes);
free(prg_crc32_bytes); free(prg_crc32_bytes);
return prg_crc32; return prg_crc32;
} }
uint64_t getCHRCRC32FromDatabaseRow(const char* crctest) uint64_t getCHRCRC32FromDatabaseRow(const char* crctest) {
{ char* chr_crc32_str = getDatabaseFieldFromRow(crctest, 3);
char* chr_crc32_str = getDatabaseFieldFromRow(crctest, 3); if (chr_crc32_str == NULL) {
if(chr_crc32_str == NULL) return 0;
{ }
return 0;
}
unsigned char* chr_crc32_bytes = strToBytes(chr_crc32_str); unsigned char* chr_crc32_bytes = strToBytes(chr_crc32_str);
free(chr_crc32_str); free(chr_crc32_str);
if(chr_crc32_bytes == NULL) if (chr_crc32_bytes == NULL) {
{ return 0;
return 0; }
}
uint32_t chr_crc32 = crc32FromBytes(chr_crc32_bytes); uint32_t chr_crc32 = crc32FromBytes(chr_crc32_bytes);
free(chr_crc32_bytes); free(chr_crc32_bytes);
return chr_crc32; return chr_crc32;
} }
char* getGameTitleFromDatabaseRow(const char* crctest) // IMPORTANT: As with getDatabaseFieldFromRow(), the string
{ // returned from this function must be passed to free() after
char* game_title_str = getDatabaseFieldFromRow(crctest, 4); // it's no longer needed in order to avoid a memory leak.
char* getGameTitleFromDatabaseRow(const char* crctest) {
char* game_title_str = getDatabaseFieldFromRow(crctest, 4);
return game_title_str; return game_title_str;
} }
unsigned char* getNES20HeaderBytesFromDatabaseRow(const char* crctest) unsigned char* getNES20HeaderBytesFromDatabaseRow(const char* crctest) {
{ char* nes_header_str = getDatabaseFieldFromRow(crctest, 5);
char* nes_header_str = getDatabaseFieldFromRow(crctest, 5); if (nes_header_str == NULL) {
if(nes_header_str == NULL) return NULL;
{ }
return NULL;
}
unsigned char* nes_header_bytes = strToBytes(nes_header_str); unsigned char* nes_header_bytes = strToBytes(nes_header_str);
free(nes_header_str); free(nes_header_str);
if(nes_header_bytes == NULL) if (nes_header_bytes == NULL) {
{ return NULL;
return NULL; }
}
return nes_header_bytes; return nes_header_bytes;
} }
/****************************************** /******************************************
@ -2262,7 +2222,7 @@ void readPRG() {
println_Msg(F("")); println_Msg(F(""));
display_Update(); display_Update();
calcCRC(fileName, prg * 1024); calcCRC(fileName, prg * 1024, &prg_crc32);
} }
set_address(0); set_address(0);
PHI2_HI; PHI2_HI;
@ -2817,7 +2777,7 @@ void readCHR() {
println_Msg(F("")); println_Msg(F(""));
display_Update(); display_Update();
calcCRC(fileName, chr * 1024); calcCRC(fileName, chr * 1024, &chr_crc32);
} }
} }
set_address(0); set_address(0);
@ -3010,9 +2970,9 @@ void readRAM() {
display_Update(); display_Update();
if ((mapper == 16) || (mapper == 159)) if ((mapper == 16) || (mapper == 159))
calcCRC(fileName, eepsize); calcCRC(fileName, eepsize, NULL);
else else
calcCRC(fileName, ram * 1024); calcCRC(fileName, ram * 1024, NULL);
} }
} }
set_address(0); set_address(0);

Binary file not shown.