Qt iNES header editor in work.

This commit is contained in:
Matthew Budd 2020-11-01 19:02:30 -05:00
parent b04938901f
commit 1055294895
2 changed files with 795 additions and 11 deletions

View File

@ -299,6 +299,8 @@ iNesHeaderEditor_t::iNesHeaderEditor_t(QWidget *parent)
trainerCBox = new QCheckBox( tr("Trainer") );
iNesUnOfBox = new QCheckBox( tr("iNES 1.0 Unofficial Properties") );
connect( iNesUnOfBox, SIGNAL(stateChanged(int)), this, SLOT(unofficialStateChange(int)) );
box = new QGroupBox( tr("Mirroring:") );
box->setLayout( vbox );
vbox5->addWidget( box );
@ -331,6 +333,9 @@ iNesHeaderEditor_t::iNesHeaderEditor_t(QWidget *parent)
iNesBusCfltBox = new QCheckBox( tr("Bus Conflict") );
iNesPrgRamBox = new QCheckBox( tr("PRG RAM Exists") );
connect( iNesPrgRamBox , SIGNAL(stateChanged(int)), this, SLOT(unofficialPrgRamStateChange(int)) );
connect( iNesDualRegBox, SIGNAL(stateChanged(int)), this, SLOT(unofficialDualRegionStateChange(int)) );
iNesUnOfGroupBox = new QGroupBox( tr("iNES 1.0 Unofficial Properties:") );
vbox = new QVBoxLayout();
@ -353,6 +358,11 @@ iNesHeaderEditor_t::iNesHeaderEditor_t(QWidget *parent)
plySysbtn = new QRadioButton( tr("PlayChoice-10") );
extSysbtn = new QRadioButton( tr("Extend") );
connect( normSysbtn, SIGNAL(clicked(bool)), this, SLOT(normSysClicked(bool)) );
connect( vsSysbtn , SIGNAL(clicked(bool)), this, SLOT(vsSysClicked(bool)) );
connect( plySysbtn , SIGNAL(clicked(bool)), this, SLOT(plySysClicked(bool)) );
connect( extSysbtn , SIGNAL(clicked(bool)), this, SLOT(extSysClicked(bool)) );
hbox->addWidget( normSysbtn );
hbox->addWidget( vsSysbtn );
hbox->addWidget( plySysbtn );
@ -407,7 +417,9 @@ iNesHeaderEditor_t::iNesHeaderEditor_t(QWidget *parent)
setLayout( mainLayout );
connect( closeBtn, SIGNAL(clicked(void)), this, SLOT(closeWindow(void)) );
connect( restoreBtn, SIGNAL(clicked(void)), this, SLOT(restoreHeader(void)) );
connect( saveAsBtn , SIGNAL(clicked(void)), this, SLOT(saveHeader(void)) );
connect( closeBtn , SIGNAL(clicked(void)), this, SLOT(closeWindow(void)) );
i=0;
while ( vsSysList[i] != NULL )
@ -470,6 +482,33 @@ void iNesHeaderEditor_t::closeWindow(void)
deleteLater();
}
//----------------------------------------------------------------------------
void iNesHeaderEditor_t::restoreHeader(void)
{
if ( iNesHdr != NULL )
{
setHeaderData( iNesHdr );
}
}
//----------------------------------------------------------------------------
void iNesHeaderEditor_t::saveHeader(void)
{
iNES_HEADER newHeader;
WriteHeaderData( &newHeader );
if ( iNesHdr != NULL )
{
if ( memcmp( iNesHdr, &newHeader, sizeof(iNES_HEADER) ) == 0 )
{
printf("Headers Match\n");
}
else
{
printf("Headers Changed\n");
}
}
}
//----------------------------------------------------------------------------
void iNesHeaderEditor_t::iNes1Clicked(bool checked)
{
//printf("INES 1.0 State: %i \n", checked);
@ -482,6 +521,48 @@ void iNesHeaderEditor_t::iNes2Clicked(bool checked)
ToggleINES20(true);
}
//----------------------------------------------------------------------------
void iNesHeaderEditor_t::normSysClicked(bool checked)
{
ToggleVSSystemGroup(false);
ToggleExtendSystemList(false);
}
//----------------------------------------------------------------------------
void iNesHeaderEditor_t::vsSysClicked(bool checked)
{
ToggleVSSystemGroup(true);
ToggleExtendSystemList(false);
}
//----------------------------------------------------------------------------
void iNesHeaderEditor_t::plySysClicked(bool checked)
{
ToggleVSSystemGroup(false);
ToggleExtendSystemList(false);
}
//----------------------------------------------------------------------------
void iNesHeaderEditor_t::extSysClicked(bool checked)
{
ToggleVSSystemGroup(false);
ToggleExtendSystemList(true);
}
//----------------------------------------------------------------------------
void iNesHeaderEditor_t::unofficialStateChange(int state)
{
ToggleUnofficialPropertiesEnabled( iNes2Btn->isChecked(), state != Qt::Unchecked );
}
//----------------------------------------------------------------------------
void iNesHeaderEditor_t::unofficialPrgRamStateChange(int state)
{
ToggleUnofficialPrgRamPresent( false, true, state != Qt::Unchecked );
}
//----------------------------------------------------------------------------
void iNesHeaderEditor_t::unofficialDualRegionStateChange(int state)
{
ToggleUnofficialExtraRegionCode( false, true, state != Qt::Unchecked );
}
//----------------------------------------------------------------------------
void iNesHeaderEditor_t::showErrorMsgWindow(const char *str)
{
QMessageBox msgBox(this);
@ -953,11 +1034,6 @@ void iNesHeaderEditor_t::ToggleINES20(bool ines20)
if ( ines20 )
{
if ( vsSysbtn->isChecked() )
{
vsGroupBox->setEnabled(true);
}
if ( dendyRegionBtn->isChecked() )
{
dendyRegionBtn->setChecked(false);
@ -971,12 +1047,14 @@ void iNesHeaderEditor_t::ToggleINES20(bool ines20)
}
}
else
{
vsGroupBox->setEnabled(false);
}
iNesUnOfBox->setEnabled(ines20);
// enable extend dialog only when ines 2.0 and extend button is checked
ToggleExtendSystemList( ines20 && extSysbtn->isChecked() );
// enable vs dialog only when ines 2.0 and vs button is checked
ToggleVSSystemGroup( ines20 && vsSysbtn->isChecked() );
iNesUnOfBox->setEnabled(!ines20);
ToggleUnofficialPropertiesEnabled( ines20, iNesUnOfBox->isChecked() );
}
@ -1025,3 +1103,696 @@ void iNesHeaderEditor_t::ToggleUnofficialPrgRamPresent(bool ines20, bool unoffic
prgRamBox->setEnabled( enable );
}
//----------------------------------------------------------------------------
void iNesHeaderEditor_t::ToggleExtendSystemList(bool enable)
{
// Extend combo box only enabled when in iNES 2.0 and Extend System was chosen
// Extend combo box
extGroupBox->setEnabled( enable );
//EnableWindow(GetDlgItem(hwnd, IDC_EXTEND_SYSTEM_GROUP), enable);
//EnableWindow(GetDlgItem(hwnd, IDC_SYSTEM_EXTEND_COMBO), enable);
//EnableWindow(GetDlgItem(hwnd, IDC_EXTEND_SYSTEM_TEXT), enable);
}
//----------------------------------------------------------------------------
void iNesHeaderEditor_t::ToggleVSSystemGroup(bool enable)
{
// VS System Groupbox only enabled when in iNES 2.0 and VS System in System groupbox is chosen
// VS System Groupbox
vsGroupBox->setEnabled(enable);
// System
//EnableWindow(GetDlgItem(hwnd, IDC_VS_SYSTEM_COMBO), enable);
//EnableWindow(GetDlgItem(hwnd, IDC_VS_SYSTEM_TEXT), enable);
// PPU
//EnableWindow(GetDlgItem(hwnd, IDC_VS_PPU_COMBO), enable);
//EnableWindow(GetDlgItem(hwnd, IDC_VS_PPU_TEXT), enable);
}
//----------------------------------------------------------------------------
bool iNesHeaderEditor_t::WriteHeaderData(iNES_HEADER* header)
{
// Temporary buffers
int idx;
char buf[256];
iNES_HEADER _header;
memset(&_header, 0, sizeof(iNES_HEADER));
// Check iNES 2.0
bool ines20 = iNes2Btn->isChecked();
// iNES 1.0 unofficial byte available
bool unofficial = !ines20 && iNesUnOfBox->isChecked();
// iNES 2.0 signature
if (ines20)
{
_header.ROM_type2 |= 8;
}
// Mapper
idx = mapperComboBox->currentIndex();
int mapper = mapperComboBox->itemData(idx).toInt();
if (mapper < 4096)
{
if (mapper < 256)
{
_header.ROM_type |= (mapper & 0xF) << 4;
_header.ROM_type2 |= (mapper & 0xF0);
}
else
{
if (ines20)
{
_header.ROM_type3 |= mapper >> 8;
}
else
{
sprintf(buf, "Error: Mapper# should be less than %d in iNES %d.0 format.", 256, 1);
showErrorMsgWindow(buf);
return false;
}
}
}
else
{
sprintf(buf, "Mapper# should be less than %d in iNES %d.0 format.", 4096, 2);
showErrorMsgWindow(buf);
return false;
}
// Sub mapper
if (ines20)
{
strcpy( buf, mapperSubEdit->text().toStdString().c_str() );
int submapper;
if (sscanf(buf, "%d", &submapper) > 0)
{
if (submapper < 16)
{
_header.ROM_type3 |= submapper << 4;
}
else
{
showErrorMsgWindow("Error: The sub mapper# should less than 16 in iNES 2.0 format.");
return false;
}
}
else
{
showErrorMsgWindow("Error: The sub mapper# you have entered is invalid. Please enter a decimal number.");
return false;
}
}
// PRG ROM
idx = prgRomBox->currentIndex();
int prg_rom = prgRomBox->itemData(idx).toInt();
if (prg_rom >= 0)
{
// max value which a iNES 2.0 header can hold
if (prg_rom < 16 * 1024 * 0xEFF)
{
// try to fit the irregular value with the alternative way
if (prg_rom % (16 * 1024) != 0)
{
if (ines20)
{
// try to calculate the nearest value
bool fit = false;
int result = 0x7FFFFFFF;
for (int multiplier = 0; multiplier < 4; ++multiplier)
{
for (int exponent = 0; exponent < 64; ++exponent)
{
int new_result = pow(2, exponent) * (multiplier * 2 + 1);
if (new_result == prg_rom)
{
_header.Upper_ROM_VROM_size |= 0xF;
_header.ROM_size |= exponent << 2;
_header.ROM_size |= multiplier & 3;
fit = true;
break;
}
if (new_result > prg_rom && result > new_result)
{
result = new_result;
}
}
if (fit) break;
}
if (!fit)
{
int result10 = (prg_rom / 16 / 1024 + 1) * 16 * 1024;
if (result10 < result)
{
result = result10;
}
char buf2[64];
if (result % 1024 != 0)
{
sprintf(buf2, "%dB", result);
}
else
{
sprintf(buf2, "%dKB", result / 1024);
}
sprintf(buf, "PRG ROM size you entered is invalid in iNES 2.0, do you want to set to its nearest value %s?", buf2);
showErrorMsgWindow(buf);
//if (MessageBox(hwnd, buf, "Error", MB_YESNO | MB_ICONERROR) == IDYES)
//{
// SetDlgItemText(hwnd, IDC_PRGROM_COMBO, buf2);
//}
//else
//{
// SetFocus(GetDlgItem(hwnd, IDC_PRGROM_COMBO));
// return false;
//}
return false;
}
}
else
{
// ines 1.0 can't handle this kind of value
showErrorMsgWindow("PRG ROM size must be multiple of 16KB in iNES 1.0");
return false;
}
}
// it's multiple size of 16KB
else {
// it can be fitted in iNES 1.0
if (prg_rom < 16 * 1024 * 0xFF)
{
_header.ROM_size |= prg_rom / 16 / 1024 & 0xFF;
}
else
{
if (ines20)
{
_header.Upper_ROM_VROM_size |= prg_rom / 16 / 1024 >> 8 & 0xF;
}
else
{
showErrorMsgWindow("PRG ROM size exceeded the limit of iNES 1.0 (4080KB).");
return false;
}
}
}
}
// A too large size
else
{
showErrorMsgWindow("PRG ROM size you entered is too large to fit into a cartridge, by the way this is an NES emulator, not for XBOX360 or PlayStation2.");
return false;
}
}
else
{
return false;
}
// PRG RAM
if (ines20 || !unofficial || iNesPrgRamBox->isChecked() )
{
idx = prgRamBox->currentIndex();
int prg_ram = prgRamBox->itemData(idx).toInt();
if (prg_ram >= 0)
{
if (ines20)
{
if (prg_ram < 64 << 0xF)
{
if (prg_ram % 64 == 0)
{
_header.RAM_size |= (int)log2(prg_ram / 64);
}
else
{
showErrorMsgWindow("Invalid PRG RAM size");
return false;
}
}
else
{
showErrorMsgWindow("PRG RAM size exceeded the limit (4096KB)");
return false;
}
}
else
{
if (prg_ram < 8 * 1024 * 255)
{
if (prg_ram % (8 * 1024) == 0)
{
_header.ROM_type3 |= prg_ram / 8 / 1024;
}
else
{
showErrorMsgWindow("PRG RAM size must be multiple of 8KB in iNES 1.0");
return false;
}
}
else {
showErrorMsgWindow("PRG RAM size exceeded the limit (2040KB)");
return false;
}
}
}
else
{
return false;
}
}
// PRG NVRAM
if (ines20)
{
// only iNES 2.0 has value for PRG VMRAM
idx = prgNvRamBox->currentIndex();
int prg_nvram = prgNvRamBox->itemData(idx).toInt();
if (prg_nvram >= 0)
{
if (prg_nvram < 64 << 0xF)
{
if (prg_nvram % 64 == 0)
{
_header.RAM_size |= (int)log2(prg_nvram / 64) << 4;
}
else
{
showErrorMsgWindow("Invalid PRG NVRAM size");
return false;
}
}
else
{
showErrorMsgWindow("PRG NVRAM size exceeded the limit (4096KB)");
return false;
}
if (prg_nvram != 0)
{
_header.ROM_type |= 2;
}
}
else
{
return false;
}
}
else
{
// iNES 1.0 is much simpler, it has only 1 bit for check
if ( battNvRamBox->isChecked() )
{
_header.ROM_type |= 2;
}
}
// CHR ROM
idx = chrRomBox->currentIndex();
int chr_rom = chrRomBox->itemData(idx).toInt();
if (chr_rom >= 0)
{
// max value which a iNES 2.0 header can hold
if (chr_rom < 8 * 1024 * 0xEFF)
{
// try to fit the irregular value with the alternative way
if (chr_rom % (8 * 1024) != 0)
{
if (ines20)
{
// try to calculate the nearest value
bool fit = false;
int result = 0;
for (int multiplier = 0; multiplier < 4; ++multiplier)
{
for (int exponent = 0; exponent < 64; ++exponent)
{
int new_result = pow(2, exponent) * (multiplier * 2 + 1);
if (new_result == chr_rom)
{
_header.Upper_ROM_VROM_size |= 0xF0;
_header.VROM_size |= exponent << 2;
_header.VROM_size |= multiplier & 3;
fit = true;
break;
}
if (result > new_result && new_result > chr_rom)
{
result = new_result;
}
}
if (fit) break;
}
if (!fit)
{
int result10 = (chr_rom / 1024 / 8 + 1) * 8 * 1024;
if (result10 < result)
{
result = result10;
}
char buf2[64];
if (result % 1024 != 0)
{
sprintf(buf2, "%dB", result);
}
else
{
sprintf(buf2, "%dKB", result / 1024);
}
sprintf(buf, "CHR ROM size you entered is invalid in iNES 2.0, do you want to set to its nearest value %s?", buf2);
showErrorMsgWindow(buf);
//if (MessageBox(hwnd, buf, "Error", MB_YESNO | MB_ICONERROR) == IDYES)
// SetDlgItemText(hwnd, IDC_CHRROM_COMBO, buf2);
//else
//{
// SetFocus(GetDlgItem(hwnd, IDC_CHRROM_COMBO));
// return false;
//}
return false;
}
}
else
{
// ines 1.0 can't handle this kind of value
showErrorMsgWindow("CHR ROM size must be multiple of 8KB in iNES 1.0");
return false;
}
}
// it's multiple size of 8KB
else {
// it can be fitted in iNES 1.0
if (chr_rom < 8 * 1024 * 0xFF)
{
_header.VROM_size |= chr_rom / 8 / 1024 & 0xFF;
}
else
{
if (ines20)
{
_header.Upper_ROM_VROM_size |= chr_rom / 8 / 1024 >> 4 & 0xF0;
}
else
{
showErrorMsgWindow("CHR ROM size exceeded the limit of iNES 1.0 (2040KB).");
return false;
}
}
}
}
// A too large size
else
{
showErrorMsgWindow("CHR ROM size you entered cannot be fitted in iNES 2.0.");
return false;
}
}
else
{
return false;
}
// CHR RAM
if (ines20)
{
// only iNES 2.0 has value for CHR RAM
idx = chrRamBox->currentIndex();
int chr_ram = chrRamBox->itemData(idx).toInt();
if (chr_ram >= 0)
{
if (chr_ram < 64 << 0xF)
{
if (chr_ram % 64 == 0)
{
_header.VRAM_size |= (int)log2(chr_ram / 64);
}
else
{
showErrorMsgWindow("Invalid CHR RAM size");
return false;
}
}
else
{
showErrorMsgWindow("CHR RAM size exceeded the limit (4096KB)");
return false;
}
}
else
{
return false;
}
}
// CHR NVRAM
if (ines20)
{
// only iNES 2.0 has value for CHR NVRAM
idx = chrNvRamBox->currentIndex();
int chr_nvram = chrNvRamBox->itemData(idx).toInt();
if (chr_nvram >= 0)
{
if (chr_nvram < 64 << 0xF)
{
if (chr_nvram % 64 == 0)
{
_header.VRAM_size |= (int)log2(chr_nvram / 64) << 4;
}
else
{
showErrorMsgWindow("Invalid CHR NVRAM size");
return false;
}
}
else
{
showErrorMsgWindow("CHR NVRAM size exceeded the limit (4096KB)");
return false;
}
if (chr_nvram != 0)
{
_header.ROM_type |= 2;
}
}
else
{
return false;
}
}
// Mirroring
if ( fourMirrorBtn->isChecked() )
{
_header.ROM_type |= 8;
}
else if ( vertMirrorBtn->isChecked() )
{
_header.ROM_type |= 1;
}
// Region
if ( palRegionBtn->isChecked() )
{
if (ines20)
{
_header.TV_system |= 1;
}
else
{
_header.Upper_ROM_VROM_size |= 1;
if ( unofficial && iNesDualRegBox->isChecked() )
{
_header.RAM_size |= 2;
}
}
}
else if ( dualRegionBtn->isChecked() )
{
if (ines20)
{
_header.TV_system |= 2;
}
else
{
_header.RAM_size |= 3;
}
}
else if ( dendyRegionBtn->isChecked() )
{
_header.TV_system |= 3;
}
// System
if ( vsSysbtn->isChecked() )
{
_header.ROM_type2 |= 1;
if (ines20) {
// VS System type
idx = vsHwBox->currentIndex();
int system = vsHwBox->itemData(idx).toInt();
if (system <= 0xF)
{
_header.VS_hardware |= (system & 0xF) << 4;
}
else
{
showErrorMsgWindow("Invalid VS System hardware type.");
return false;
}
// VS PPU type
idx = vsPpuBox->currentIndex();
int ppu = vsPpuBox->itemData(idx).toInt();
if ( (ppu >= 0) && (system <= 0xF) )
{
_header.VS_hardware |= ppu & 0xF;
}
else
{
showErrorMsgWindow("Invalid VS System PPU type.");
return false;
}
}
}
else if ( plySysbtn->isChecked() )
{
_header.ROM_type2 |= 2;
}
else if ( extSysbtn->isChecked() )
{
// Extend System
_header.ROM_type2 |= 3;
idx = extCslBox->currentIndex();
int extend = extCslBox->itemData(idx).toInt();
if (extend >= 0)
{
_header.VS_hardware |= extend & 0x3F;
}
else
{
showErrorMsgWindow("Invalid extend system type");
return false;
}
}
// Input device
if (ines20)
{
idx = inputDevBox->currentIndex();
int input = inputDevBox->itemData(idx).toInt();
if (input <= 0x3F)
{
_header.reserved[1] |= input & 0x3F;
}
else
{
showErrorMsgWindow("Invalid input device.");
return false;
}
}
// Miscellanous ROM(s)
if (ines20)
{
strcpy( buf, miscRomsEdit->text().toStdString().c_str() );
int misc_roms = 0;
if (sscanf(buf, "%d", &misc_roms) < 1)
{
showErrorMsgWindow("Invalid miscellanous ROM(s) count. If you don't know what value should be, we recommend to set it to 0.");
return false;
}
if (misc_roms > 3)
{
showErrorMsgWindow("Miscellanous ROM(s) count has exceeded the limit of iNES 2.0 (3)");
return false;
}
_header.reserved[0] |= misc_roms & 3;
}
// iNES 1.0 unofficial properties
if ( !ines20 && unofficial )
{
// bus conflict configure in unoffcial bit of iNES 1.0
if ( iNesBusCfltBox->isChecked() )
{
_header.RAM_size |= 0x20;
}
// PRG RAM non exist bit flag
if ( iNesPrgRamBox->isChecked() )
{
_header.RAM_size |= 0x10;
}
}
// Trainer
if ( trainerCBox->isChecked() )
{
_header.ROM_type |= 4;
}
bool fceux_support = false;
for (int i = 0; bmap[i].init; ++i)
{
if (mapper == bmap[i].number)
{
fceux_support = true;
break;
}
}
if (!fceux_support)
{
int ret;
QMessageBox msgBox(this);
sprintf(buf, "FCEUX doesn't support iNES Mapper# %d, this is not a serious problem, but the ROM will not be run in FCEUX properly.\nDo you want to continue?", mapper);
msgBox.setIcon( QMessageBox::Warning );
msgBox.setText( tr(buf) );
ret = msgBox.exec();
if (ret == 0)
{
return false;
}
}
memcpy(_header.ID, "NES\x1A", 4);
if (header)
{
memcpy(header, &_header, sizeof(iNES_HEADER));
}
return true;
}
//----------------------------------------------------------------------------
void iNesHeaderEditor_t::printHeader(iNES_HEADER* _header)
{
printf("header: ");
printf("%02X ", _header->ID[0]);
printf("%02X ", _header->ID[1]);
printf("%02X ", _header->ID[2]);
printf("%02X ", _header->ID[3]);
printf("%02X ", _header->ROM_size);
printf("%02X ", _header->VROM_size);
printf("%02X ", _header->ROM_type);
printf("%02X ", _header->ROM_type2);
printf("%02X ", _header->ROM_type3);
printf("%02X ", _header->Upper_ROM_VROM_size);
printf("%02X ", _header->RAM_size);
printf("%02X ", _header->VRAM_size);
printf("%02X ", _header->TV_system);
printf("%02X ", _header->VS_hardware);
printf("%02X ", _header->reserved[0]);
printf("%02X\n", _header->reserved[1]);
}
//----------------------------------------------------------------------------

View File

@ -80,18 +80,31 @@ class iNesHeaderEditor_t : public QDialog
iNES_HEADER *iNesHdr;
private:
void printHeader(iNES_HEADER* _header);
bool loadHeader(iNES_HEADER *header);
bool WriteHeaderData(iNES_HEADER* header);
void setHeaderData(iNES_HEADER *header);
void showErrorMsgWindow(const char *str);
void ToggleINES20(bool ines20);
void ToggleUnofficialPropertiesEnabled(bool ines20, bool check);
void ToggleUnofficialExtraRegionCode(bool ines20, bool unofficial_check, bool check);
void ToggleUnofficialPrgRamPresent(bool ines20, bool unofficial_check, bool check);
void ToggleVSSystemGroup(bool enable);
void ToggleExtendSystemList(bool enable);
public slots:
void closeWindow(void);
private slots:
void saveHeader(void);
void restoreHeader(void);
void iNes1Clicked(bool checked);
void iNes2Clicked(bool checked);
void normSysClicked(bool checked);
void vsSysClicked(bool checked);
void plySysClicked(bool checked);
void extSysClicked(bool checked);
void unofficialStateChange(int state);
void unofficialPrgRamStateChange(int state);
void unofficialDualRegionStateChange(int state);
};