mirror of
https://github.com/wavemotion-dave/NINTV-DS.git
synced 2025-06-18 13:55:33 -04:00
844 lines
27 KiB
C++
844 lines
27 KiB
C++
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
|
|
#include "Rip.h"
|
|
#include "RAM.h"
|
|
#include "ROM.h"
|
|
#include "ROMBanker.h"
|
|
#include "CRC32.h"
|
|
|
|
#define MAX_MEMORY_UNITS 16
|
|
#define MAX_STRING_LENGTH 256
|
|
|
|
#define ROM_TAG_TITLE 0x01
|
|
#define ROM_TAG_PUBLISHER 0x02
|
|
#define ROM_TAG_RELEASE_DATE 0x05
|
|
#define ROM_TAG_COMPATIBILITY 0x06
|
|
|
|
Rip::Rip(UINT32 systemID)
|
|
: Peripheral("", ""),
|
|
peripheralCount(0),
|
|
targetSystemID(systemID),
|
|
crc(0)
|
|
{
|
|
producer = new CHAR[1];
|
|
strcpy(producer, "");
|
|
year = new CHAR[1];
|
|
strcpy(year, "");
|
|
memset(filename, 0, sizeof(filename));
|
|
}
|
|
|
|
Rip::~Rip()
|
|
{
|
|
for (UINT16 i = 0; i < peripheralCount; i++)
|
|
delete[] peripheralNames[i];
|
|
UINT16 count = GetROMCount();
|
|
for (UINT16 i = 0; i < count; i++)
|
|
delete GetROM(i);
|
|
delete[] producer;
|
|
delete[] year;
|
|
}
|
|
|
|
void Rip::SetName(const CHAR* n)
|
|
{
|
|
if (this->peripheralName)
|
|
delete[] peripheralName;
|
|
|
|
peripheralName = new CHAR[strlen(n)+1];
|
|
strcpy(peripheralName, n);
|
|
printf("RIP Peripheral Name [%s]\n", peripheralName);
|
|
}
|
|
|
|
void Rip::SetProducer(const CHAR* p)
|
|
{
|
|
if (this->producer)
|
|
delete[] producer;
|
|
|
|
producer = new CHAR[strlen(p)+1];
|
|
strcpy(producer, p);
|
|
}
|
|
|
|
void Rip::SetYear(const CHAR* y)
|
|
{
|
|
if (this->year)
|
|
delete[] year;
|
|
|
|
year = new CHAR[strlen(y)+1];
|
|
strcpy(year, y);
|
|
}
|
|
|
|
void Rip::AddPeripheralUsage(const CHAR* periphName, PeripheralCompatibility usage)
|
|
{
|
|
peripheralNames[peripheralCount] = new CHAR[strlen(periphName)+1];
|
|
strcpy(peripheralNames[peripheralCount], periphName);
|
|
peripheralUsages[peripheralCount] = usage;
|
|
peripheralCount++;
|
|
}
|
|
|
|
PeripheralCompatibility Rip::GetPeripheralUsage(const CHAR* periphName)
|
|
{
|
|
for (UINT16 i = 0; i < peripheralCount; i++) {
|
|
if (strcmpi(peripheralNames[i], periphName) == 0)
|
|
return peripheralUsages[i];
|
|
}
|
|
return PERIPH_INCOMPATIBLE;
|
|
}
|
|
|
|
Rip* Rip::LoadA52(const CHAR* filename)
|
|
{
|
|
Rip* rip = new Rip(ID_SYSTEM_ATARI5200);
|
|
if (rip == NULL)
|
|
return NULL;
|
|
|
|
//load the data into the rip
|
|
FILE* file = fopen(filename, "rb");
|
|
if (file == NULL) {
|
|
delete rip;
|
|
return NULL;
|
|
}
|
|
|
|
//obtain the file size
|
|
fseek(file, 0, SEEK_END);
|
|
size_t size = ftell(file);
|
|
rewind(file);
|
|
|
|
//add an appropriate ROM to the rip
|
|
ROM* rom = new ROM("Cartridge ROM", "", 0, 1, (UINT16)size, (UINT16)(0xC000-size));
|
|
rip->AddROM(rom);
|
|
|
|
//load in the file image
|
|
UINT8* image = new UINT8[size];
|
|
UINT32 count = 0;
|
|
while (count < size)
|
|
image[count++] = (UINT8)fgetc(file);
|
|
fclose(file);
|
|
|
|
//parse the file image into the rip
|
|
UINT32 offset = 0;
|
|
UINT16 romCount = rip->GetROMCount();
|
|
for (UINT16 i = 0; i < romCount; i++)
|
|
{
|
|
if (offset >= size) {
|
|
//something is wrong, the file we're trying to load isn't large enough
|
|
//getting here indicates a likely incorrect memory map in knowncarts.cfg
|
|
delete[] image;
|
|
delete rip;
|
|
return NULL;
|
|
}
|
|
ROM* nextRom = rip->GetROM(i);
|
|
nextRom->load(image+offset);
|
|
offset += nextRom->getByteWidth() * nextRom->getReadSize();
|
|
}
|
|
|
|
delete[] image;
|
|
|
|
rip->SetFileName(filename);
|
|
rip->crc = CRC32::getCrc(filename);
|
|
|
|
return rip;
|
|
}
|
|
|
|
Rip* Rip::LoadBin(const CHAR* filename, const CHAR* configFile)
|
|
{
|
|
//determine the crc of the designated file
|
|
UINT32 crc = CRC32::getCrc(filename);
|
|
Rip* rip = LoadCartridgeConfiguration(configFile, crc);
|
|
if (rip == NULL)
|
|
return NULL;
|
|
else printf("Configuration [%s] loaded successfully\n", configFile);
|
|
|
|
//load the data into the rip
|
|
FILE* file = fopen(filename, "rb");
|
|
if (file == NULL) {
|
|
delete rip;
|
|
return NULL;
|
|
} else printf("File [%s] read into memory successfully\n", filename);
|
|
|
|
//obtain the file size
|
|
fseek(file, 0, SEEK_END);
|
|
size_t size = ftell(file);
|
|
rewind(file);
|
|
printf("The file size is [%d] bytes\n", (int)size);
|
|
|
|
//load in the file image
|
|
UINT8* image = new UINT8[size];
|
|
UINT32 count = 0;
|
|
while (count < size)
|
|
image[count++] = (UINT8)fgetc(file);
|
|
fclose(file);
|
|
|
|
printf("Parsing file into RIP format\n");
|
|
//parse the file image into the rip
|
|
UINT32 offset = 0;
|
|
UINT16 romCount = rip->GetROMCount();
|
|
for (UINT16 i = 0; i < romCount; i++)
|
|
{
|
|
if (offset >= size)
|
|
{
|
|
//something is wrong, the file we're trying to load isn't large enough
|
|
//getting here indicates a likely incorrect memory map in knowncarts.cfg
|
|
delete[] image;
|
|
delete rip;
|
|
return NULL;
|
|
}
|
|
ROM* nextRom = rip->GetROM(i);
|
|
printf("Loading ROM segment [%s %d %d]\n", nextRom->getName(), nextRom->getByteWidth(), nextRom->getReadSize());
|
|
nextRom->load(image+offset);
|
|
offset += nextRom->getByteWidth() * nextRom->getReadSize();
|
|
}
|
|
|
|
printf("Done parsing...\n");
|
|
delete[] image;
|
|
|
|
rip->SetFileName(filename);
|
|
rip->crc = CRC32::getCrc(filename);
|
|
|
|
printf("RIP loaded!\n");
|
|
return rip;
|
|
}
|
|
|
|
Rip* Rip::LoadCartridgeConfiguration(const CHAR* configFile, UINT32 crc)
|
|
{
|
|
CHAR scrc[9];
|
|
sprintf(scrc, "%08X", crc);
|
|
|
|
//find the config that matches this crc
|
|
FILE* cfgFile = fopen(configFile, "r");
|
|
if (cfgFile == NULL)
|
|
return NULL;
|
|
|
|
Rip* rip = NULL;
|
|
BOOL parseSuccess = FALSE;
|
|
CHAR nextLine[256];
|
|
while (!feof(cfgFile)) {
|
|
fgets(nextLine, 256, cfgFile);
|
|
//size_t length = strlen(nextLine);
|
|
if (strstr(nextLine, scrc) != nextLine) {
|
|
if (parseSuccess)
|
|
break;
|
|
else
|
|
continue;
|
|
}
|
|
|
|
CHAR* nextToken;
|
|
if ((nextToken = strstr(nextLine, "Info")) != NULL) {
|
|
if ((nextToken = strrchr(nextLine, ':')) == NULL)
|
|
continue;
|
|
|
|
if (rip == NULL)
|
|
rip = new Rip(ID_SYSTEM_INTELLIVISION);
|
|
rip->SetYear(nextToken+1);
|
|
|
|
*nextToken = NULL;
|
|
if ((nextToken = strrchr(nextLine, ':')) == NULL)
|
|
continue;
|
|
rip->SetProducer(nextToken+1);
|
|
|
|
*nextToken = NULL;
|
|
if ((nextToken = strrchr(nextLine, ':')) == NULL)
|
|
continue;
|
|
rip->SetName(nextToken+1);
|
|
|
|
parseSuccess = TRUE;
|
|
}
|
|
else if ((nextToken = strstr(nextLine, "ROM")) != NULL) {
|
|
//TODO: check to make sure we don't exceed the maximum number of roms for a Rip
|
|
|
|
//parse rom bit width
|
|
if ((nextToken = strrchr(nextLine, ':')) == NULL)
|
|
continue;
|
|
|
|
//first check to see if this is a banked rom; if there is an equals (=) sign in
|
|
//the last field, then we assume we should parse banking
|
|
UINT16 triggerAddress, triggerMask, triggerValue, onMask, onValue;
|
|
triggerAddress = triggerMask = triggerValue = onMask = onValue = 0;
|
|
CHAR* nextEqual = strrchr(nextLine, '=');
|
|
if (nextEqual != NULL && nextEqual > nextToken) {
|
|
onValue = (UINT16)strtol(nextEqual+1, NULL, 16);
|
|
*nextEqual = NULL;
|
|
onMask = (UINT16)strtol(nextToken+1, NULL, 16);
|
|
*nextToken = NULL;
|
|
if ((nextToken = strrchr(nextLine, ':')) == NULL)
|
|
continue;
|
|
|
|
nextEqual = strrchr(nextLine, '=');
|
|
if (nextEqual == NULL || nextEqual < nextToken)
|
|
continue;
|
|
|
|
CHAR* nextComma = strrchr(nextLine, ',');
|
|
if (nextComma == NULL || nextComma < nextToken || nextComma > nextEqual)
|
|
continue;
|
|
|
|
triggerValue = (UINT16)strtol(nextEqual+1, NULL, 16);
|
|
*nextEqual = NULL;
|
|
triggerMask = (UINT16)strtol(nextComma+1, NULL, 16);
|
|
*nextComma = NULL;
|
|
triggerAddress = (UINT16)strtol(nextToken+1, NULL, 16);
|
|
*nextToken = NULL;
|
|
|
|
if ((nextToken = strrchr(nextLine, ':')) == NULL)
|
|
continue;
|
|
}
|
|
|
|
UINT8 romBitWidth = (UINT8)atoi(nextToken+1);
|
|
|
|
//parse rom size
|
|
*nextToken = NULL;
|
|
if ((nextToken = strrchr(nextLine, ':')) == NULL)
|
|
continue;
|
|
UINT16 romSize = (UINT16)strtol(nextToken+1, NULL, 16);
|
|
|
|
*nextToken = NULL;
|
|
if ((nextToken = strrchr(nextLine, ':')) == NULL)
|
|
continue;
|
|
UINT16 romAddress = (UINT16)strtol(nextToken+1, NULL, 16);
|
|
|
|
if (rip == NULL)
|
|
rip = new Rip(ID_SYSTEM_INTELLIVISION);
|
|
ROM* nextRom = new ROM("Cartridge ROM", "", 0, (romBitWidth+7)/8, romSize, romAddress);
|
|
rip->AddROM(nextRom);
|
|
if (triggerAddress != 0)
|
|
rip->AddRAM(new ROMBanker(nextRom, triggerAddress, triggerMask, triggerValue, onMask, onValue));
|
|
|
|
parseSuccess = TRUE;
|
|
}
|
|
else if ((nextToken = strstr(nextLine, "RAM")) != NULL) {
|
|
//TODO: check to make sure we don't exceed the maximum number of rams for a Rip
|
|
|
|
//parse ram width
|
|
if ((nextToken = strrchr(nextLine, ':')) == NULL)
|
|
continue;
|
|
UINT8 ramBitWidth = (UINT8)atoi(nextToken+1);
|
|
|
|
//parse ram size
|
|
*nextToken = NULL;
|
|
if ((nextToken = strrchr(nextLine, ':')) == NULL)
|
|
continue;
|
|
UINT16 ramSize = (UINT16)strtol(nextToken+1, NULL, 16);
|
|
|
|
//parse ram address
|
|
*nextToken = NULL;
|
|
if ((nextToken = strrchr(nextLine, ':')) == NULL)
|
|
continue;
|
|
UINT16 ramAddress = (UINT16)strtol(nextToken+1, NULL, 16);
|
|
|
|
if (rip == NULL)
|
|
rip = new Rip(ID_SYSTEM_INTELLIVISION);
|
|
rip->AddRAM(new RAM(ramSize, ramAddress, ramBitWidth));
|
|
parseSuccess = TRUE;
|
|
}
|
|
else if ((nextToken = strstr(nextLine, "Peripheral")) != NULL) {
|
|
//TODO: check to make sure we don't exceed the maximum number of peripherals for a Rip
|
|
|
|
if ((nextToken = strrchr(nextLine, ':')) == NULL)
|
|
continue;
|
|
|
|
PeripheralCompatibility pc;
|
|
if (strcmpi(nextToken+1, "required") != 0)
|
|
pc = PERIPH_REQUIRED;
|
|
else if (strcmpi(nextToken+1, "optional") != 0)
|
|
pc = PERIPH_OPTIONAL;
|
|
else
|
|
continue;
|
|
|
|
*nextToken = NULL;
|
|
if ((nextToken = strrchr(nextLine, ':')) == NULL)
|
|
continue;
|
|
|
|
if (rip == NULL)
|
|
rip = new Rip(ID_SYSTEM_INTELLIVISION);
|
|
rip->AddPeripheralUsage(nextToken+1, pc);
|
|
parseSuccess = TRUE;
|
|
}
|
|
}
|
|
fclose(cfgFile);
|
|
|
|
if (rip != NULL && !parseSuccess) {
|
|
delete rip;
|
|
rip = NULL;
|
|
}
|
|
|
|
return rip;
|
|
}
|
|
|
|
Rip* Rip::LoadRom(const CHAR* filename)
|
|
{
|
|
FILE* infile = fopen(filename, "rb");
|
|
if (infile == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
//read the magic byte (should always be $A8)
|
|
int read = fgetc(infile);
|
|
if (read != 0xA8) {
|
|
fclose(infile);
|
|
return NULL;
|
|
}
|
|
|
|
//read the number of ROM segments
|
|
int romSegmentCount = fgetc(infile);
|
|
read = fgetc(infile);
|
|
if ((read ^ 0xFF) != romSegmentCount) {
|
|
fclose(infile);
|
|
return NULL;
|
|
}
|
|
|
|
Rip* rip = new Rip(ID_SYSTEM_INTELLIVISION);
|
|
|
|
int i;
|
|
for (i = 0; i < romSegmentCount; i++) {
|
|
UINT16 start = (UINT16)(fgetc(infile) << 8);
|
|
UINT16 end = (UINT16)((fgetc(infile) << 8) | 0xFF);
|
|
UINT16 size = (UINT16)((end-start)+1);
|
|
|
|
//finally, transfer the ROM image
|
|
UINT16* romImage = new UINT16[size];
|
|
int j;
|
|
for (j = 0; j < size; j++) {
|
|
int nextbyte = fgetc(infile) << 8;
|
|
nextbyte |= fgetc(infile);
|
|
romImage[j] = (UINT16)nextbyte;
|
|
}
|
|
rip->AddROM(new ROM("Cartridge ROM", romImage, 2, size, start, 0xFFFF));
|
|
delete[] romImage;
|
|
|
|
//read the CRC
|
|
fgetc(infile); fgetc(infile);
|
|
//TODO: validate the CRC16 instead of just skipping it
|
|
//int crc16 = (fgetc(infile) << 8) | fgetc(infile);
|
|
//...
|
|
}
|
|
|
|
//no support currently for the access tables, so skip them
|
|
for (i = 0; i < 16; i++)
|
|
fgetc(infile);
|
|
|
|
//no support currently for the fine address restriction
|
|
//tables, so skip them, too
|
|
for (i = 0; i < 32; i++)
|
|
fgetc(infile);
|
|
|
|
while ((read = fgetc(infile)) != -1) {
|
|
int length = (read & 0x3F);
|
|
read = (read & 0xC) >> 6;
|
|
for (i = 0; i < read; i++)
|
|
length = (length | (fgetc(infile) << ((8*i)+6)));
|
|
|
|
int type = fgetc(infile);
|
|
int crc16;
|
|
switch (type) {
|
|
case ROM_TAG_TITLE:
|
|
{
|
|
CHAR* title = new char[length*sizeof(char)];
|
|
for (i = 0; i < length; i++) {
|
|
read = fgetc(infile);
|
|
title[i] = (char)read;
|
|
}
|
|
crc16 = (fgetc(infile) << 8) | fgetc(infile);
|
|
rip->SetName(title);
|
|
delete[] title;
|
|
}
|
|
break;
|
|
case ROM_TAG_PUBLISHER:
|
|
{
|
|
CHAR* publisher = new char[length*sizeof(char)];
|
|
for (i = 0; i < length; i++) {
|
|
read = fgetc(infile);
|
|
publisher[i] = (char)read;
|
|
}
|
|
crc16 = (fgetc(infile) << 8) | fgetc(infile);
|
|
rip->SetProducer(publisher);
|
|
|
|
delete[] publisher;
|
|
}
|
|
break;
|
|
case ROM_TAG_COMPATIBILITY:
|
|
{
|
|
read = fgetc(infile);
|
|
BOOL requiresECS = ((read & 0xC0) == 0x80);
|
|
if (requiresECS)
|
|
rip->AddPeripheralUsage("ECS", PERIPH_REQUIRED);
|
|
for (i = 0; i < length-1; i++) {
|
|
fgetc(infile);
|
|
fgetc(infile);
|
|
}
|
|
fgetc(infile);
|
|
fgetc(infile);
|
|
}
|
|
break;
|
|
case ROM_TAG_RELEASE_DATE:
|
|
default:
|
|
{
|
|
for (i = 0; i < length; i++) {
|
|
fgetc(infile);
|
|
fgetc(infile);
|
|
}
|
|
fgetc(infile);
|
|
fgetc(infile);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
fclose(infile);
|
|
|
|
rip->SetFileName(filename);
|
|
rip->crc = CRC32::getCrc(filename);
|
|
|
|
return rip;
|
|
}
|
|
|
|
Rip* Rip::LoadRip(const CHAR* filename)
|
|
{
|
|
FILE* file = fopen(filename, "rb");
|
|
if (file == NULL)
|
|
return NULL;
|
|
|
|
//first check the magic number
|
|
unsigned int nextUINT32 = freadUINT32(file);
|
|
if (nextUINT32 != 0x3F383A34) {
|
|
fclose(file);
|
|
return NULL;
|
|
}
|
|
|
|
nextUINT32 = freadUINT32(file);
|
|
if (nextUINT32 != 0x7651B5DA) {
|
|
fclose(file);
|
|
return NULL;
|
|
}
|
|
|
|
//the magic number is good. this is definitely a RIP file
|
|
//start reading the records
|
|
Rip* rip = NULL;
|
|
unsigned int nextRecordID = freadUINT32(file);
|
|
while (!feof(file)) {
|
|
unsigned int nextRecordSize = freadUINT32(file);
|
|
switch (nextRecordID) {
|
|
case ID_HEADER_RECORD:
|
|
{
|
|
freadUINT16(file); //major file format version
|
|
freadUINT16(file); //minor file format version
|
|
UINT32 targetSystemID = freadUINT32(file); //target system
|
|
rip = new Rip(targetSystemID);
|
|
break;
|
|
}
|
|
/*
|
|
case ID_BIOS_COMPAT_RECORD:
|
|
tmp1 = ripFile->biosCount;
|
|
ripFile->biosCompat[tmp1] =
|
|
malloc(sizeof(BiosCompatRecord));
|
|
ripFile->biosCompat[tmp1]->periphNum = fgetc(file);
|
|
ripFile->biosCompat[tmp1]->biosTypeNum = fgetc(file);
|
|
ripFile->biosCompat[tmp1]->biosNum = fgetc(file);
|
|
ripFile->biosCompat[tmp1]->compatibility = fgetc(file);
|
|
if (ripFile->biosCompat[tmp1]->compatibility > 2)
|
|
ripFile->biosCompat[tmp1]->compatibility = 2;
|
|
ripFile->biosCount++;
|
|
break;
|
|
*/
|
|
case ID_PERIPH_COMPAT_RECORD:
|
|
{
|
|
if (!rip)
|
|
return NULL;
|
|
UINT32 periphID = freadUINT32(file);
|
|
PeripheralCompatibility usage = (PeripheralCompatibility)(fgetc(file) & 0x03);
|
|
rip->AddPeripheralUsage((periphID == ID_PERIPH_ECS ? "ECS" : "Intellivoice"), usage);
|
|
break;
|
|
}
|
|
case ID_NAME_RECORD:
|
|
{
|
|
if (!rip)
|
|
return NULL;
|
|
CHAR name[MAX_STRING_LENGTH];
|
|
freadString(file, name, MAX_STRING_LENGTH);
|
|
rip->SetName(name);
|
|
break;
|
|
}
|
|
case ID_PRODUCER_RECORD:
|
|
{
|
|
if (!rip)
|
|
return NULL;
|
|
CHAR producer[MAX_STRING_LENGTH];
|
|
freadString(file, producer, MAX_STRING_LENGTH);
|
|
rip->SetProducer(producer);
|
|
break;
|
|
}
|
|
case ID_YEAR_RECORD:
|
|
{
|
|
if (!rip)
|
|
return NULL;
|
|
CHAR year[MAX_STRING_LENGTH];
|
|
freadString(file, year, MAX_STRING_LENGTH);
|
|
rip->SetProducer(year);
|
|
break;
|
|
}
|
|
case ID_RAM_RECORD:
|
|
{
|
|
if (!rip)
|
|
return NULL;
|
|
|
|
//TODO: handle when we exceed maximum memory units
|
|
|
|
UINT8 flags = (UINT8)fgetc(file);
|
|
BOOL partialReads = !!(flags & 0x80);
|
|
BOOL partialWrites = !!(flags & 0x40);
|
|
BOOL banked = !!(flags & 0x20);
|
|
UINT8 addressByteWidth = (flags & 0x0F)+1;
|
|
|
|
flags = (UINT8)fgetc(file);
|
|
UINT8 dataBitWidth = (flags & 0x7F)+1;
|
|
UINT16 address = (UINT16)freadInt(file, addressByteWidth);
|
|
UINT16 size = (UINT16)freadInt(file, addressByteWidth);
|
|
//RAM reset value unused at this point, so skip it
|
|
freadInt(file, (dataBitWidth+7)/8);
|
|
|
|
UINT16 readMask = 0xFFFF;
|
|
if (partialReads)
|
|
readMask = (UINT16)freadInt(file, addressByteWidth);
|
|
|
|
UINT16 writeMask = 0xFFFF;
|
|
if (partialWrites)
|
|
writeMask = (UINT16)freadInt(file, addressByteWidth);
|
|
|
|
if (banked) {
|
|
//banking descriptors not yet supported, so just need to skip 'em
|
|
UINT32 bankingDescriptorCount = freadUINT32(file);
|
|
for (UINT32 k = 0; k < bankingDescriptorCount; k++) {
|
|
freadInt(file, addressByteWidth); //target address width
|
|
freadInt(file, addressByteWidth); //write decoding mask
|
|
freadInt(file, (dataBitWidth+7)/8); //reset value
|
|
UINT32 dataCount = freadUINT32(file);
|
|
for (UINT32 l = 0; l < dataCount; l++)
|
|
freadInt(file, (dataBitWidth+7)/8); //data banking match values
|
|
}
|
|
}
|
|
|
|
rip->AddRAM(new RAM(size, address, readMask, writeMask, dataBitWidth));
|
|
}
|
|
case ID_ROM_RECORD:
|
|
{
|
|
if (!rip)
|
|
return NULL;
|
|
|
|
//TODO: handle when we exceed maximum memory units
|
|
|
|
UINT8 flags = (UINT8)fgetc(file);
|
|
BOOL partialReads = !!(flags & 0x80);
|
|
BOOL compressed = !!(flags & 0x40);
|
|
BOOL banked = !!(flags & 0x20);
|
|
UINT8 addressByteWidth = (flags & 0x0F)+1;
|
|
|
|
flags = (UINT8)fgetc(file);
|
|
UINT8 dataBitWidth = (flags & 0x7F)+1;
|
|
UINT16 address = (UINT16)freadInt(file, addressByteWidth);
|
|
|
|
UINT32 arraySize = freadUINT32(file);
|
|
UINT8* romImage = NULL;
|
|
if (compressed) {
|
|
//TODO: support zlib compressed rom images
|
|
for (UINT32 k = 0; k < arraySize; k++)
|
|
fgetc(file);
|
|
arraySize = 0;
|
|
romImage = new UINT8[0];
|
|
}
|
|
else {
|
|
UINT32 dataByteWidth = (dataBitWidth+7)/8;
|
|
romImage = new UINT8[arraySize*dataByteWidth];
|
|
for (UINT32 k = 0; k < arraySize; k++) {
|
|
for (UINT8 l = 0; l < dataByteWidth; l++)
|
|
romImage[(k*dataByteWidth)+(dataByteWidth-l-1)] = (UINT8)fgetc(file);
|
|
}
|
|
}
|
|
|
|
UINT16 readMask = 0xFFFF;
|
|
if (partialReads)
|
|
readMask = (UINT16)freadInt(file, addressByteWidth);
|
|
|
|
if (banked) {
|
|
//banking descriptors not yet supported, so just need to skip 'em
|
|
UINT32 bankingDescriptorCount = freadUINT32(file);
|
|
for (UINT32 k = 0; k < bankingDescriptorCount; k++) {
|
|
freadInt(file, addressByteWidth); //target address width
|
|
freadInt(file, addressByteWidth); //write decoding mask
|
|
freadInt(file, (dataBitWidth+7)/8); //reset value
|
|
UINT32 dataCount = freadUINT32(file);
|
|
for (UINT32 l = 0; l < dataCount; l++)
|
|
freadInt(file, (dataBitWidth+7)/8); //data banking match values
|
|
}
|
|
}
|
|
|
|
rip->AddROM(new ROM("Cartridge ROM", (void*)romImage, (dataBitWidth+7)/8, (UINT16)arraySize, address, readMask));
|
|
delete[] romImage;
|
|
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
//unknown record; just skip it
|
|
for (UINT32 i = 0; i < nextRecordSize; i++)
|
|
fgetc(file);
|
|
}
|
|
}
|
|
nextRecordID = freadUINT32(file);
|
|
}
|
|
fclose(file);
|
|
|
|
rip->SetFileName(filename);
|
|
rip->crc = CRC32::getCrc(filename);
|
|
|
|
return rip;
|
|
}
|
|
|
|
|
|
BOOL Rip::SaveRip(const CHAR* filename)
|
|
{
|
|
FILE* file = fopen(filename, "wb");
|
|
if (file == NULL) {
|
|
printf("Error: Unable to create file %s\n", filename);
|
|
return 1;
|
|
}
|
|
|
|
//write the 64-bit magic RIP XBF number
|
|
fwriteUINT64(file, RIP_MAGIC_NUMBER);
|
|
|
|
//write the header record
|
|
fwriteUINT32(file, ID_HEADER_RECORD);
|
|
//write the header record size in bytes
|
|
fwriteUINT32(file, 9);
|
|
//write the major RIP revision number
|
|
fwriteUINT16(file, RIP_MAJOR_REVISION);
|
|
//write the minor RIP revision number
|
|
fwriteUINT16(file, RIP_MINOR_REVISION);
|
|
//write the system id
|
|
fwriteUINT32(file, targetSystemID);
|
|
|
|
//write the name record
|
|
fwriteUINT32(file, ID_NAME_RECORD);
|
|
fwriteUINT32(file, (UINT32)(4+strlen(GetName())));
|
|
fwriteString(file, GetName());
|
|
|
|
//write the year record
|
|
fwriteUINT32(file, ID_YEAR_RECORD);
|
|
fwriteUINT32(file, (UINT32)(4+strlen(year)));
|
|
fwriteString(file, year);
|
|
|
|
//write the producer record
|
|
fwriteUINT32(file, ID_PRODUCER_RECORD);
|
|
fwriteUINT32(file, (UINT32)(4+strlen(producer)));
|
|
fwriteString(file, producer);
|
|
|
|
//write the peripheral compatibility records
|
|
for (UINT16 i = 0; i < peripheralCount; i++) {
|
|
fwriteUINT32(file, ID_PERIPH_COMPAT_RECORD);
|
|
fwriteUINT32(file, 5);
|
|
//these records are hard-coded for Intellivision now, will have to work out something more
|
|
//flexible when Atari 5200 support is re-added in the future
|
|
fwriteUINT32(file, (strcmpi(peripheralNames[i], "Intellivoice") == 0) ? ID_PERIPH_INTELLIVOICE : ID_PERIPH_ECS);
|
|
fputc(peripheralUsages[i], file);
|
|
}
|
|
|
|
//write the RAM records
|
|
UINT16 ramCount = GetRAMCount();
|
|
for (UINT16 i = 0; i < ramCount; i++) {
|
|
RAM* nextMem = GetRAM(i);
|
|
fwriteUINT32(file, ID_RAM_RECORD);
|
|
UINT16 readMask = nextMem->getReadAddressMask();
|
|
UINT16 writeMask = nextMem->getWriteAddressMask();
|
|
UINT16 byteWidth = (nextMem->getBitWidth()+7)/8;
|
|
|
|
//calculate the size of the RAM record
|
|
int recordSize = 1 + //flags
|
|
1 + //more flags
|
|
2 + //address
|
|
2 + //size
|
|
byteWidth + //reset value
|
|
(readMask != 0xFFFF ? 2 : 0) + //read mask
|
|
(writeMask != 0xFFFF ? 2 : 0) + //write mask
|
|
0; //banking not supported just yet
|
|
fwriteUINT32(file, recordSize);
|
|
|
|
//flags #1
|
|
fputc(((readMask != 0xFFFF) ? 0x80 : 0) |
|
|
((writeMask != 0xFFFF) ? 0x40 : 0) |
|
|
1, file);
|
|
|
|
//flags #2
|
|
fputc(nextMem->getBitWidth()-1, file);
|
|
|
|
//address
|
|
fwriteUINT16(file, nextMem->getReadAddress());
|
|
|
|
//size
|
|
fwriteUINT16(file, nextMem->getReadSize());
|
|
|
|
//reset value, always zero
|
|
for (UINT16 k = 0; k < byteWidth; k++)
|
|
fputc(0, file);
|
|
|
|
//read mask
|
|
if (readMask != 0xFFFF)
|
|
fwriteUINT16(file, readMask);
|
|
|
|
//write mask
|
|
if (writeMask != 0xFFFF)
|
|
fwriteUINT16(file, writeMask);
|
|
}
|
|
|
|
//write the ROM records
|
|
UINT16 romCount = GetROMCount();
|
|
for (UINT16 i = 0; i < romCount; i++) {
|
|
ROM* nextMem = GetROM(i);
|
|
fwriteUINT32(file, ID_ROM_RECORD);
|
|
|
|
//calculate the size of the ROM record
|
|
UINT16 readMask = nextMem->getReadAddressMask();
|
|
UINT16 byteWidth = nextMem->getByteWidth();
|
|
UINT16 memSize = nextMem->getReadSize();
|
|
int recordSize = 1 + //flags
|
|
1 + //more flags
|
|
2 + //address
|
|
4 + (byteWidth*memSize) + //ROM image
|
|
(readMask != 0xFFFF ? 2 : 0) + //read mask
|
|
0; //banking not supported just yet
|
|
fwriteUINT32(file, recordSize);
|
|
|
|
//flags #1
|
|
fputc(((readMask != 0xFFFF) ? 0x80 : 0) |
|
|
1, file);
|
|
|
|
//flags #2
|
|
fputc((byteWidth*8)-1, file);
|
|
|
|
//address
|
|
UINT16 address = nextMem->getReadAddress();
|
|
fwriteUINT16(file, address);
|
|
|
|
//ROM image
|
|
fwriteUINT32(file, memSize);
|
|
for (UINT16 j = 0; j < memSize; j++) {
|
|
switch (byteWidth) {
|
|
case 1:
|
|
fputc(nextMem->peek(address+j), file);
|
|
break;
|
|
case 2:
|
|
fwriteUINT16(file, nextMem->peek(address+j));
|
|
break;
|
|
case 4:
|
|
fwriteUINT32(file, nextMem->peek(address+j));
|
|
break;
|
|
default:
|
|
for (UINT8 k = 0; k < byteWidth; k++)
|
|
fputc(0, file);
|
|
}
|
|
}
|
|
|
|
//read mask
|
|
if (readMask != 0xFFFF)
|
|
fwriteUINT16(file, readMask);
|
|
}
|
|
|
|
fclose(file);
|
|
|
|
return 0;
|
|
}
|