diff --git a/src/drivers/Qt/iNesHeaderEditor.cpp b/src/drivers/Qt/iNesHeaderEditor.cpp index 6cee71a9..ca17f7c0 100644 --- a/src/drivers/Qt/iNesHeaderEditor.cpp +++ b/src/drivers/Qt/iNesHeaderEditor.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include "../../types.h" #include "../../fceu.h" @@ -24,14 +25,121 @@ extern BMAPPINGLocal bmap[]; +// VS System type +static const char* vsSysList[] = { + "Normal", + "RBI Baseball", + "TKO Boxing", + "Super Xevious", + "Ice Climber", + "Dual Normal", + "Dual Raid on Bungeling Bay", + 0 +}; + +// VS PPU type +static const char* vsPPUList[] = { + "RP2C03B", + "RP2C03G", + "RP2C04-0001", + "RP2C04-0002", + "RP2C04-0003", + "RP2C04-0004", + "RC2C03B", + "RC2C03C", + "RC2C05-01 ($2002 AND $\?\?=$1B)", + "RC2C05-02 ($2002 AND $3F=$3D)", + "RC2C05-03 ($2002 AND $1F=$1C)", + "RC2C05-04 ($2002 AND $1F=$1B)", + "RC2C05-05 ($2002 AND $1F=$\?\?)", + "Reserved", + "Reserved", + "Reserved", + 0 +}; + +// Extend console type +static const char* extConsoleList[] = { + "Normal", + "VS. System", + "Playchoice 10", + "Bit Corp. Creator", + "V.R. Tech. VT01 monochrome", + "V.R. Tech. VT01 red / cyan", + "V.R. Tech. VT02", + "V.R. Tech. VT03", + "V.R. Tech. VT09", + "V.R. Tech. VT32", + "V.R. Tech. VT369", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + 0 +}; + +// Default input device list +static const char* inputDevList[] = { + "Unspecified", + "Standard NES / Famicom Controllers", + "Four-score (NES)", + "Four-score (Famicom)", + "VS. System", + "VS. System (controllers swapped)", + "VS. Pinball (J)", + "VS. Zapper", + "Zapper", + "Double Zappers", + "Bandai Hyper Shot", + "Power Pad Side A", + "Power Pad Side B", + "Family Trainer Side A", + "Family Trainer Side B", + "Arkanoid Paddle (NES)", + "Arkanoid Paddle (Famicom)", + "Double Arkanoid Paddle", + "Konami Hyper Shot", + "Pachinko", + "Exciting Boxing Punching Bag", + "Jissen Mahjong", + "Party Tap", + "Oeka Kids Tablet", + "Barcode Reader", + "Miracle Piano", + "Pokkun Moguraa", + "Top Rider", + "Double-Fisted", + "Famicom 3D", + "Doremikko Keyboard", + "R.O.B. Gyro Set", + "Famicom Data Recorder", + "ASCII Turbo File", + "IGS Storage Battle Box", + "Family BASIC Keyboard", + "PEC-586 Keyboard", + "Bit Corp. Keyboard", + "Subor Keyboard", + "Subor Keyboard + Mouse A", + "Subor Keyboard + Mouse B", + "SNES Mouse", + "Multicart", + "Double SNES controllers", + "RacerMate Bicycle", + "U-Force", + "R.O.B. Stack-Up", + 0 +}; //---------------------------------------------------------------------------- iNesHeaderEditor_t::iNesHeaderEditor_t(QWidget *parent) : QDialog( parent ) { + int i; QVBoxLayout *mainLayout, *hdrLayout; QVBoxLayout *vbox, *vbox1, *vbox2, *vbox3, *vbox4, *vbox5; QHBoxLayout *hbox, *hbox1, *hbox2, *hbox3; QGroupBox *box, *hdrBox; + QGridLayout *grid; char stmp[128]; setWindowTitle("iNES Header Editor"); @@ -70,7 +178,7 @@ iNesHeaderEditor_t::iNesHeaderEditor_t(QWidget *parent) hbox->addWidget( new QLabel( tr("Sub #:") ) ); hbox->addWidget( mapperSubEdit ); - for (int i = 0; bmap[i].init; ++i) + for (i = 0; bmap[i].init; ++i) { sprintf(stmp, "%d %s", bmap[i].number, bmap[i].name); @@ -79,38 +187,52 @@ iNesHeaderEditor_t::iNesHeaderEditor_t(QWidget *parent) hdrLayout->addLayout( hbox1 ); box = new QGroupBox( tr("PRG") ); - hbox = new QHBoxLayout(); + grid = new QGridLayout(); - box->setLayout( hbox ); + box->setLayout( grid ); hdrLayout->addWidget( box ); prgRomBox = new QComboBox(); prgRamBox = new QComboBox(); prgNvRamBox = new QComboBox(); + battNvRamBox= new QCheckBox( tr("Battery-backed NVRAM") ); + hbox = new QHBoxLayout(); + grid->addLayout( hbox, 0, 0 ); hbox->addWidget( new QLabel( tr("PRG ROM:") ), 0, Qt::AlignRight ); - hbox->addWidget( prgRomBox ); + hbox->addWidget( prgRomBox, 0, Qt::AlignLeft ); + hbox = new QHBoxLayout(); + grid->addLayout( hbox, 0, 1 ); hbox->addWidget( new QLabel( tr("PRG RAM:") ), 0, Qt::AlignRight ); - hbox->addWidget( prgRamBox ); - hbox->addWidget( new QLabel( tr("PRG NVRAM:") ), 0, Qt::AlignRight ); - hbox->addWidget( prgNvRamBox ); + hbox->addWidget( prgRamBox, 0, Qt::AlignLeft ); + hbox = new QHBoxLayout(); + grid->addLayout( hbox, 0, 2 ); + hbox->addWidget( prgNvRamLbl = new QLabel( tr("PRG NVRAM:") ), 0, Qt::AlignRight ); + hbox->addWidget( prgNvRamBox, 0, Qt::AlignLeft ); + hbox->addWidget( battNvRamBox, 0, Qt::AlignRight ); box = new QGroupBox( tr("CHR") ); - hbox = new QHBoxLayout(); + grid = new QGridLayout(); - box->setLayout( hbox ); + box->setLayout( grid ); hdrLayout->addWidget( box ); chrRomBox = new QComboBox(); chrRamBox = new QComboBox(); chrNvRamBox = new QComboBox(); + hbox = new QHBoxLayout(); + grid->addLayout( hbox, 0, 0 ); hbox->addWidget( new QLabel( tr("CHR ROM:") ), 0, Qt::AlignRight ); - hbox->addWidget( chrRomBox ); + hbox->addWidget( chrRomBox, 0, Qt::AlignLeft ); + hbox = new QHBoxLayout(); + grid->addLayout( hbox, 0, 1 ); hbox->addWidget( new QLabel( tr("CHR RAM:") ), 0, Qt::AlignRight ); - hbox->addWidget( chrRamBox ); + hbox->addWidget( chrRamBox, 0, Qt::AlignLeft ); + hbox = new QHBoxLayout(); + grid->addLayout( hbox, 0, 2 ); hbox->addWidget( new QLabel( tr("CHR NVRAM:") ), 0, Qt::AlignRight ); - hbox->addWidget( chrNvRamBox ); + hbox->addWidget( chrNvRamBox, 0, Qt::AlignLeft ); // add usually used size strings strcpy(stmp, "0 B"); @@ -206,21 +328,21 @@ iNesHeaderEditor_t::iNesHeaderEditor_t(QWidget *parent) iNesBusCfltBox = new QCheckBox( tr("Bus Conflict") ); iNesPrgRamBox = new QCheckBox( tr("PRG RAM Exists") ); - box = new QGroupBox( tr("iNES 1.0 Unofficial Properties:") ); + iNesUnOfGroupBox = new QGroupBox( tr("iNES 1.0 Unofficial Properties:") ); vbox = new QVBoxLayout(); vbox->addWidget( iNesDualRegBox ); vbox->addWidget( iNesBusCfltBox ); vbox->addWidget( iNesPrgRamBox ); - box->setLayout( vbox ); + iNesUnOfGroupBox->setLayout( vbox ); vbox1->addWidget( iNesUnOfBox ); - vbox1->addWidget( box ); + vbox1->addWidget( iNesUnOfGroupBox ); hbox = new QHBoxLayout(); - box = new QGroupBox( tr("System") ); - hbox2->addWidget( box ); - box->setLayout( vbox3 ); + sysGroupBox = new QGroupBox( tr("System") ); + hbox2->addWidget( sysGroupBox ); + sysGroupBox->setLayout( vbox3 ); vbox3->addLayout( hbox ); normSysbtn = new QRadioButton( tr("Normal") ); @@ -234,9 +356,9 @@ iNesHeaderEditor_t::iNesHeaderEditor_t(QWidget *parent) hbox->addWidget( extSysbtn ); hbox = new QHBoxLayout(); - box = new QGroupBox( tr("VS. System") ); - box->setLayout( vbox4 ); - vbox3->addWidget( box ); + vsGroupBox = new QGroupBox( tr("VS. System") ); + vsGroupBox->setLayout( vbox4 ); + vbox3->addWidget( vsGroupBox ); vbox4->addLayout( hbox ); vsHwBox = new QComboBox(); @@ -253,9 +375,9 @@ iNesHeaderEditor_t::iNesHeaderEditor_t(QWidget *parent) hbox->addWidget( vsPpuBox ); hbox = new QHBoxLayout(); - box = new QGroupBox( tr("Extend System") ); - vbox3->addWidget( box ); - box->setLayout( hbox ); + extGroupBox = new QGroupBox( tr("Extend System") ); + vbox3->addWidget( extGroupBox ); + extGroupBox->setLayout( hbox ); hbox->addWidget( new QLabel( tr("Console:") ) ); hbox->addWidget( extCslBox ); @@ -269,12 +391,63 @@ iNesHeaderEditor_t::iNesHeaderEditor_t(QWidget *parent) hbox->addWidget( new QLabel( tr("Input Device:") ) ); hbox->addWidget( inputDevBox ); + grid = new QGridLayout(); + restoreBtn = new QPushButton( tr("Restore") ); + saveAsBtn = new QPushButton( tr("Save As") ); + closeBtn = new QPushButton( tr("Close") ); + + grid->addWidget( restoreBtn, 0, 0, Qt::AlignLeft ); + grid->addWidget( saveAsBtn , 0, 1, Qt::AlignRight ); + grid->addWidget( closeBtn , 0, 2, Qt::AlignRight ); + + mainLayout->addLayout( grid ); + setLayout( mainLayout ); + + i=0; + while ( vsSysList[i] != NULL ) + { + vsHwBox->addItem( vsSysList[i], i ); i++; + } + + i=0; + while ( vsPPUList[i] != NULL ) + { + vsPpuBox->addItem( vsPPUList[i], i ); i++; + } + + i=0; + while ( extConsoleList[i] != NULL ) + { + extCslBox->addItem( extConsoleList[i], i ); i++; + } + + i=0; + while ( inputDevList[i] != NULL ) + { + inputDevBox->addItem( inputDevList[i], i ); i++; + } + + iNesHdr = (iNES_HEADER*)::malloc( sizeof(iNES_HEADER) ); + + ::memset( iNesHdr, 0, sizeof(iNES_HEADER) ); + + if (GameInfo) + { + loadHeader( iNesHdr ); + } + + setHeaderData( iNesHdr ); } //---------------------------------------------------------------------------- iNesHeaderEditor_t::~iNesHeaderEditor_t(void) { printf("Destroy Header Editor Config Window\n"); + + if ( iNesHdr ) + { + free( iNesHdr ); iNesHdr = NULL; + } } //---------------------------------------------------------------------------- void iNesHeaderEditor_t::closeEvent(QCloseEvent *event) @@ -292,3 +465,378 @@ void iNesHeaderEditor_t::closeWindow(void) deleteLater(); } //---------------------------------------------------------------------------- +void iNesHeaderEditor_t::showErrorMsgWindow(const char *str) +{ + QMessageBox msgBox(this); + + msgBox.setIcon( QMessageBox::Critical ); + msgBox.setText( tr(str) ); + msgBox.show(); + msgBox.exec(); +} +//---------------------------------------------------------------------------- +bool iNesHeaderEditor_t::loadHeader(iNES_HEADER* header) +{ + int error = 0; + enum errors { + OK, + OPEN_FAILED, + INVALID_HEADER, + FDS_HEADER, + UNIF_HEADER, + NSF_HEADER//, +// NSFE_HEADER, +// NSF2_HEADER + }; + + FCEUFILE* fp = FCEU_fopen(LoadedRomFName, NULL, "rb", NULL); + if (!GameInfo) + { + strcpy(LoadedRomFName, fp->fullFilename.c_str()); + } + + if (fp) + { + if (FCEU_fread(header, 1, sizeof(iNES_HEADER), fp) == sizeof(iNES_HEADER) && !memcmp(header, "NES\x1A", 4)) + { + header->cleanup(); + } + else if (!memcmp(header, "FDS\x1A", 4)) + { + error = errors::FDS_HEADER; + } + else if (!memcmp(header, "UNIF", 4)) + { + error = errors::UNIF_HEADER; + } + else if (!memcmp(header, "NESM", 4)) + { + error = errors::NSF_HEADER; + } +/* else if (!memcmp(header, "NSFE", 4)) + error = errors::NSFE_HEADER; + else if (!memcmp(header, "NESM\2", 4)) + error = errors::NSF2_HEADER; +*/ else + { + error = errors::INVALID_HEADER; + } + FCEU_fclose(fp); + } + else + { + error = errors::OPEN_FAILED; + } + + if (error) + { + switch (error) + { + case errors::OPEN_FAILED: + { + char buf[2200]; + sprintf(buf, "Error opening %s!", LoadedRomFName); + showErrorMsgWindow( buf ); + break; + } + case errors::INVALID_HEADER: + //MessageBox(parent, "Invalid iNES header.", "iNES Header Editor", MB_OK | MB_ICONERROR); + showErrorMsgWindow( "Invalid iNES header." ); + break; + case errors::FDS_HEADER: + //MessageBox(parent, "Editing header of an FDS file is not supported.", "iNES Header Editor", MB_OK | MB_ICONERROR); + showErrorMsgWindow("Editing header of an FDS file is not supported."); + break; + case errors::UNIF_HEADER: + //MessageBox(parent, "Editing header of a UNIF file is not supported.", "iNES Header Editor", MB_OK | MB_ICONERROR); + showErrorMsgWindow("Editing header of a UNIF file is not supported."); + break; + case errors::NSF_HEADER: +// case errors::NSF2_HEADER: +// case errors::NSFE_HEADER: + //MessageBox(parent, "Editing header of an NSF file is not supported.", "iNES Header Editor", MB_OK | MB_ICONERROR); + showErrorMsgWindow("Editing header of an NSF file is not supported."); + break; + } + return false; + } + + printf("Opened File: '%s'\n", LoadedRomFName ); + + + return true; +} +//---------------------------------------------------------------------------- +void iNesHeaderEditor_t::setHeaderData(iNES_HEADER* header) +{ + int i; + // Temporary buffers + char buf[256]; + + bool ines20 = (header->ROM_type2 & 0xC) == 8; + bool unofficial = false; + + ToggleINES20( ines20 ); + + iNes1Btn->setChecked( !ines20 ); + iNes2Btn->setChecked( ines20 ); + + // Mapper# + int mapper = header->ROM_type >> 4 | header->ROM_type2 & 0xF0; + if (ines20) + { + mapper |= (header->ROM_type3 & 0xF0) << 4; + } + mapperComboBox->setCurrentIndex( mapper ); + + // Sub Mapper + sprintf(buf, "%d", ines20 ? header->ROM_type3 >> 4 : 0); + + mapperSubEdit->setText( buf ); + + // PRG ROM + int prg_rom = header->ROM_size; + if (ines20) + { + if ((header->Upper_ROM_VROM_size & 0xF) == 0xF) + prg_rom = pow(2, header->ROM_size >> 2) * ((header->ROM_size & 3) * 2 + 1); + else { + prg_rom |= (header->Upper_ROM_VROM_size & 0xF) << 8; + prg_rom *= 1024 * 16; + } + } + else + { + prg_rom *= 1024 * 16; + } + + for (i=0; icount(); i++) + { + if ( prgRomBox->itemData(i).toInt() == prg_rom ) + { + prgRomBox->setCurrentIndex(i); break; + } + } + + // PRG RAM + int prg_ram = 0; + if (ines20) + { + int shift = header->RAM_size & 0xF; + if (shift) + { + prg_ram = 64 << shift; + } + + } + else + { + if (!(header->RAM_size & 0x10) && header->ROM_type3) + { + prg_ram = (header->ROM_type3 ? 1 : header->ROM_type3 * 8) * 1024; + } + } + + for (i=0; icount(); i++) + { + if ( prgRamBox->itemData(i).toInt() == prg_ram ) + { + prgRamBox->setCurrentIndex(i); break; + } + } + + // PRG NVRAM + int prg_nvram = 0; + if (ines20) + { + int shift = header->RAM_size >> 4; + if (shift) + { + prg_nvram = 64 << shift; + } + battNvRamBox->hide(); + prgNvRamLbl->show(); + prgNvRamBox->show(); + } + else + { + prgNvRamLbl->hide(); + prgNvRamBox->hide(); + battNvRamBox->show(); + battNvRamBox->setChecked( header->ROM_type & 0x2 ? true : false ); + } + + for (i=0; icount(); i++) + { + if ( prgNvRamBox->itemData(i).toInt() == prg_nvram ) + { + prgNvRamBox->setCurrentIndex(i); break; + } + } + + // CHR ROM + int chr_rom = header->VROM_size; + if (ines20) + { + if ((header->Upper_ROM_VROM_size & 0xF0) == 0xF0) + { + chr_rom = pow(2, header->VROM_size >> 2) * (((header->VROM_size & 3) * 2) + 1); + } + else + { + chr_rom |= (header->Upper_ROM_VROM_size & 0xF0) << 4; + chr_rom *= 8 * 1024; + } + } + else + { + chr_rom *= 8 * 1024; + } + + for (i=0; icount(); i++) + { + if ( chrRomBox->itemData(i).toInt() == chr_rom ) + { + chrRomBox->setCurrentIndex(i); break; + } + } + + // CHR RAM + int chr_ram = 0; + if (ines20) + { + int shift = header->VRAM_size & 0xF; + if (shift) + { + chr_ram = 64 << shift; + } + } + + for (i=0; icount(); i++) + { + if ( chrRamBox->itemData(i).toInt() == chr_ram ) + { + chrRamBox->setCurrentIndex(i); break; + } + } + + // CHR NVRAM + int chr_nvram = 0; + if (ines20) + { + int shift = header->VRAM_size >> 4; + if (shift) + { + chr_nvram = 64 << shift; + } + } + + for (i=0; icount(); i++) + { + if ( chrNvRamBox->itemData(i).toInt() == chr_nvram ) + { + chrNvRamBox->setCurrentIndex(i); break; + } + } + + // Mirroring + if ( header->ROM_type & 8 ) + { + horzMirrorBtn->setChecked(false); + vertMirrorBtn->setChecked(false); + fourMirrorBtn->setChecked(true); + } + else if ( header->ROM_type & 1 ) + { + horzMirrorBtn->setChecked(false); + vertMirrorBtn->setChecked(true); + fourMirrorBtn->setChecked(false); + } + else + { + horzMirrorBtn->setChecked(true); + vertMirrorBtn->setChecked(false); + fourMirrorBtn->setChecked(false); + } + + // Region + if (ines20) + { + int region = header->TV_system & 3; + switch (region) + { + case 0: + // NTSC + ntscRegionBtn->setChecked(true); + break; + case 1: + // PAL + palRegionBtn->setChecked(true); + break; + case 2: + // Dual + dualRegionBtn->setChecked(true); + break; + case 3: + // Dendy + dendyRegionBtn->setChecked(true); + break; + } + } + else + { + // Only the unofficial 10th byte has a dual region, we must check it first. + int region = header->RAM_size & 3; + if (region == 3 || region == 1) + { + // Check the unofficial checkmark and the region code + dualRegionBtn->setChecked(true); + unofficial = true; + } + else + { + // When the region in unofficial byte is inconsistent with the official byte, based on the official byte + if ( header->Upper_ROM_VROM_size & 1 ) + { + palRegionBtn->setChecked(true); + } + else + { + ntscRegionBtn->setChecked(true); + } + } + } + +} +//---------------------------------------------------------------------------- +void iNesHeaderEditor_t::ToggleINES20(bool ines20) +{ + // only ines 2.0 has these values + // these are always have when in 2.0 + // Submapper# + //EnableWindow(GetDlgItem(hwnd, IDC_SUBMAPPER_TEXT), ines20); + mapperSubEdit->setEnabled(ines20); + // PRG NVRAM + prgNvRamBox->setEnabled(ines20); + //EnableWindow(GetDlgItem(hwnd, IDC_PRGNVRAM_TEXT), ines20); + // CHR RAM + chrRamBox->setEnabled(ines20); + //EnableWindow(GetDlgItem(hwnd, IDC_CHRRAM_TEXT), ines20); + // CHR NVRAM + chrNvRamBox->setEnabled(ines20); + //EnableWindow(GetDlgItem(hwnd, IDC_CHRNVRAM_TEXT), ines20); + // Dendy in Region Groupbox + dendyRegionBtn->setEnabled(ines20); + // Multiple in Regtion Groupbox + dualRegionBtn->setEnabled(ines20); + // Extend in System Groupbox + extGroupBox->setEnabled(ines20); + // Input Device + inputDevBox->setEnabled(ines20); + //EnableWindow(GetDlgItem(hwnd, IDC_INPUT_DEVICE_TEXT), ines20); + // Miscellaneous ROMs + miscRomsEdit->setEnabled(ines20); + //EnableWindow(GetDlgItem(hwnd, IDC_MISCELLANEOUS_ROMS_TEXT), ines20); + // +} +//---------------------------------------------------------------------------- diff --git a/src/drivers/Qt/iNesHeaderEditor.h b/src/drivers/Qt/iNesHeaderEditor.h index 83febd95..cdea8a7e 100644 --- a/src/drivers/Qt/iNesHeaderEditor.h +++ b/src/drivers/Qt/iNesHeaderEditor.h @@ -18,6 +18,8 @@ #include "Qt/main.h" +class iNES_HEADER; + class iNesHeaderEditor_t : public QDialog { Q_OBJECT @@ -49,6 +51,7 @@ class iNesHeaderEditor_t : public QDialog QCheckBox *iNesDualRegBox; QCheckBox *iNesBusCfltBox; QCheckBox *iNesPrgRamBox; + QCheckBox *battNvRamBox; QRadioButton *horzMirrorBtn; QRadioButton *vertMirrorBtn; QRadioButton *fourMirrorBtn; @@ -60,8 +63,22 @@ class iNesHeaderEditor_t : public QDialog QRadioButton *vsSysbtn; QRadioButton *plySysbtn; QRadioButton *extSysbtn; + QPushButton *restoreBtn; + QPushButton *saveAsBtn; + QPushButton *closeBtn; + QGroupBox *iNesUnOfGroupBox; + QGroupBox *sysGroupBox; + QGroupBox *vsGroupBox; + QGroupBox *extGroupBox; + QLabel *prgNvRamLbl; + iNES_HEADER *iNesHdr; private: + bool loadHeader(iNES_HEADER *header); + void setHeaderData(iNES_HEADER *header); + void showErrorMsgWindow(const char *str); + void ToggleINES20(bool ines20); + public slots: void closeWindow(void); private slots: