mirror of
https://github.com/dborth/fceugx.git
synced 2024-12-04 22:34:14 +01:00
5800f2e688
* Ability to load PocketNES ROMs that contain one NES ROM * Ability to read/write PocketNES SRAM data if it already exists
118 lines
3.7 KiB
C++
118 lines
3.7 KiB
C++
/* pocketnesrom.c - functions to find uncompressed NES ROM images
|
|
stored within PocketNES ROMs
|
|
|
|
Copyright (C) 2016 libertyernie
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
When compiling in Visual Studio, set the project to compile
|
|
as C++ code (Properties -> C/C++ -> Advanced -> Compile As.)
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "pocketnesrom.h"
|
|
|
|
const char NES_WORD[4] = { 'N','E','S',0x1A };
|
|
|
|
/* Finds the first PocketNES ROM header in the given data block by looking for
|
|
the segment 4E45531A (N,E,S,^Z) in the ROM itself. If no valid data is found,
|
|
this method will return NULL. */
|
|
const pocketnes_romheader* pocketnes_first_rom(const void* data, size_t length) {
|
|
const char* ptr = (const char*)data;
|
|
const char* end = ptr + length;
|
|
int logo_pos = 0;
|
|
while (ptr < end) {
|
|
if (*ptr == NES_WORD[logo_pos]) {
|
|
// match
|
|
logo_pos++;
|
|
if (logo_pos == 4) { // matched all of GB logo - on last byte (0x133)
|
|
// check if length fits
|
|
const pocketnes_romheader* candidate = (pocketnes_romheader*)(ptr - 3 - sizeof(pocketnes_romheader));
|
|
size_t filesize = candidate->filesize;
|
|
if (*(uint16_t *)"\0\xff" < 0x100) {
|
|
uint32_t buffer;
|
|
((char*)&buffer)[0] = ((char*)&filesize)[3];
|
|
((char*)&buffer)[1] = ((char*)&filesize)[2];
|
|
((char*)&buffer)[2] = ((char*)&filesize)[1];
|
|
((char*)&buffer)[3] = ((char*)&filesize)[0];
|
|
filesize = buffer;
|
|
}
|
|
const char* candidate_end_ptr = (ptr - 3) + filesize;
|
|
if (candidate_end_ptr <= end) {
|
|
return candidate;
|
|
} else {
|
|
// no match, try again
|
|
logo_pos = 0;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
// no match, try again
|
|
if (logo_pos > 0) {
|
|
ptr -= logo_pos;
|
|
logo_pos = 0;
|
|
}
|
|
}
|
|
ptr++;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/* Returns a pointer to the next PocketNES ROM header in the data. If the
|
|
location where the next ROM header would be does not contain a 'N,E,S,^Z'
|
|
segment at the start of the ROM, this method will return NULL. */
|
|
const pocketnes_romheader* pocketnes_next_rom(const void* data, size_t length, const pocketnes_romheader* first_rom) {
|
|
size_t diff = (const char*)first_rom - (const char*)data;
|
|
if (diff > length) {
|
|
return NULL;
|
|
}
|
|
size_t effective_length = length - diff;
|
|
if (effective_length <= 0x200) {
|
|
// Assume there will never be a ROM this small
|
|
return NULL;
|
|
}
|
|
return pocketnes_first_rom(
|
|
(const char*)first_rom + 4 + sizeof(pocketnes_romheader),
|
|
effective_length - 4 - sizeof(pocketnes_romheader));
|
|
}
|
|
|
|
/* Returns true if the given data region looks like a PocketNES ROM header
|
|
(based on the 'N,E,S,^Z' segment), or false otherwise. */
|
|
int pocketnes_is_romheader(const void* data) {
|
|
const char* rom_ptr = (const char*)data + sizeof(pocketnes_romheader);
|
|
return memcmp(rom_ptr, NES_WORD, 4) == 0;
|
|
}
|
|
|
|
/* Returns the checksum that PocketNES would use for this ROM.
|
|
You can pass the PocketNES ROM header or the NES ROM itself. */
|
|
uint32_t pocketnes_get_checksum(const void* rom) {
|
|
const uint8_t* p = (const uint8_t*)rom;
|
|
|
|
// Checksum should not include NES ROM format header
|
|
if (memcmp(p, NES_WORD, 4) == 0) {
|
|
p += 16;
|
|
}
|
|
|
|
// TODO: add support for compressed roms
|
|
|
|
uint32_t sum = 0;
|
|
int i;
|
|
for (i = 0; i<128; i++) {
|
|
sum += *p | (*(p + 1) << 8) | (*(p + 2) << 16) | (*(p + 3) << 24);
|
|
p += 128;
|
|
}
|
|
return sum;
|
|
}
|