Unverified Commit e5240a68 authored by WaluigiWare64's avatar WaluigiWare64 Committed by GitHub
Browse files

Add ROM Header struct and ROM info dialog (#1095)

parent 5a071c4c
......@@ -58,6 +58,9 @@ u32 CartID;
bool CartIsHomebrew;
bool CartIsDSi;
NDSHeader Header;
NDSBanner Banner;
CartCommon* Cart;
u32 Key1_KeyBuf[0x412];
......@@ -1498,8 +1501,11 @@ void DecryptSecureArea(u8* out)
// * .srl ROMs (VC dumps) have encrypted secure areas but have precomputed
// decryption data at 0x1000 (and at the beginning of the DSi region if any)
u32 gamecode = *(u32*)&CartROM[0x0C];
u32 arm9base = *(u32*)&CartROM[0x20];
u32 gamecode = (u32)Header.GameCode[3] << 24 |
(u32)Header.GameCode[2] << 16 |
(u32)Header.GameCode[1] << 8 |
(u32)Header.GameCode[0];
u32 arm9base = Header.ARM9ROMOffset;
memcpy(out, &CartROM[arm9base], 0x800);
......@@ -1526,11 +1532,17 @@ void DecryptSecureArea(u8* out)
bool LoadROMCommon(u32 filelength, const char *sram, bool direct)
{
u32 gamecode;
memcpy(&gamecode, CartROM + 0x0C, 4);
printf("Game code: %c%c%c%c\n", gamecode&0xFF, (gamecode>>8)&0xFF, (gamecode>>16)&0xFF, gamecode>>24);
memcpy(&Header, CartROM, sizeof(Header));
memcpy(&Banner, CartROM + Header.BannerOffset, sizeof(Banner));
printf("Game code: %.4s\n", Header.GameCode);
u32 gamecode = (u32)Header.GameCode[3] << 24 |
(u32)Header.GameCode[2] << 16 |
(u32)Header.GameCode[1] << 8 |
(u32)Header.GameCode[0];
u8 unitcode = CartROM[0x12];
u8 unitcode = Header.UnitCode;
CartIsDSi = (unitcode & 0x02) != 0;
ROMListEntry romparams;
......
......@@ -20,6 +20,7 @@
#define NDSCART_H
#include "types.h"
#include "NDS_Header.h"
namespace NDSCart
{
......@@ -191,6 +192,9 @@ extern u32 CartROMSize;
extern u32 CartID;
extern NDSHeader Header;
extern NDSBanner Banner;
bool Init();
void DeInit();
void Reset();
......
/*
Copyright 2016-2021 Arisotura, WaluigiWare64
This file is part of melonDS.
melonDS 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 3 of the License, or (at your option)
any later version.
melonDS 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 melonDS. If not, see http://www.gnu.org/licenses/.
*/
#ifndef NDS_HEADER_H
#define NDS_HEADER_H
#include "types.h"
// Consult GBATEK for info on what these are
struct NDSHeader
{
char GameTitle[12];
char GameCode[4];
char MakerCode[2];
u8 UnitCode;
u8 EncryptionSeedSelect;
u8 CardSize;
u8 Reserved1[8];
u8 NDSRegion;
u8 ROMVersion;
u8 Autostart;
u32 ARM9ROMOffset;
u32 ARM9EntryAddress;
u32 ARM9RAMAddress;
u32 ARM9Size;
u32 ARM7ROMOffset;
u32 ARM7EntryAddress;
u32 ARM7RAMAddress;
u32 ARM7Size;
u32 FNTOffset;
u32 FNTSize;
u32 FATOffset;
u32 FATSize;
u32 ARM9OverlayOffset;
u32 ARM9OverlaySize;
u32 ARM7OverlayOffset;
u32 ARM7OverlaySize;
u32 NormalCommandSettings;
u32 Key1CommandSettings;
u32 BannerOffset;
u16 SecureAreaCRC16;
u16 SecureAreaDelay;
// GBATEK lists the following two with a question mark
u32 ARM9AutoLoadListAddress;
u32 ARM7AutoLoadListAddress;
u64 SecureAreaDisable;
u32 ROMSize;
u32 HeaderSize;
u32 Unknown1;
u8 Reserved2[52];
u8 NintendoLogo[156];
u16 NintendoLogoCRC16;
u16 HeaderCRC16;
u32 DebugROMOffset;
u32 DebugSize;
u32 DebugRAMAddress;
u32 Reserved4;
u8 Reserved5[144];
};
static_assert(sizeof(NDSHeader) == 512, "NDSHeader is not 512 bytes!");
struct NDSBanner
{
u16 Version;
u16 CRC16[4];
u8 Reserved1[22];
u8 Icon[512];
u16 Palette[16];
char16_t JapaneseTitle[128];
char16_t EnglishTitle[128];
char16_t FrenchTitle[128];
char16_t GermanTitle[128];
char16_t ItalianTitle[128];
char16_t SpanishTitle[128];
char16_t ChineseTitle[128];
char16_t KoreanTitle[128];
u8 Reserved2[2048];
u8 DSiIcon[8][512];
u16 DSiPalette[8][16];
u16 DSiSequence[64];
};
static_assert(sizeof(NDSBanner) == 9152, "NDSBanner is not 9152 bytes!");
#endif //NDS_HEADER_H
......@@ -21,6 +21,8 @@
#include "types.h"
#include <vector>
namespace Frontend
{
......@@ -84,6 +86,9 @@ int LoadROM(const u8 *romdata, u32 romlength, const char *archivefilename, const
// simulating ejection of the cartridge
void UnloadROM(int slot);
void ROMIcon(u8 (&data)[512], u16 (&palette)[16], u32* iconRef);
void AnimatedROMIcon(u8 (&data)[8][512], u16 (&palette)[8][16], u16 (&sequence)[64], u32 (&animatedTexRef)[32 * 32 * 64], std::vector<int> &animatedSequenceRef);
// reset execution of the current ROM
int Reset();
......
......@@ -19,6 +19,8 @@
#include <stdio.h>
#include <string.h>
#include <utility>
#ifdef ARCHIVE_SUPPORT_ENABLED
#include "ArchiveUtil.h"
#endif
......@@ -461,6 +463,72 @@ int LoadROM(const char* file, int slot)
}
}
void ROMIcon(u8 (&data)[512], u16 (&palette)[16], u32* iconRef)
{
int index = 0;
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
for (int k = 0; k < 8; k++)
{
for (int l = 0; l < 8; l++)
{
u8 pal_index = index % 2 ? data[index/2] >> 4 : data[index/2] & 0x0F;
u8 r = ((palette[pal_index] >> 0) & 0x1F) * 255 / 31;
u8 g = ((palette[pal_index] >> 5) & 0x1F) * 255 / 31;
u8 b = ((palette[pal_index] >> 10) & 0x1F) * 255 / 31;
u8 a = pal_index ? 255: 0;
u32* row = &iconRef[256 * i + 32 * k + 8 * j];
row[l] = (a << 24) | (r << 16) | (g << 8) | b;
index++;
}
}
}
}
}
#define SEQ_FLIPV(i) ((i & 0b1000000000000000) >> 15)
#define SEQ_FLIPH(i) ((i & 0b0100000000000000) >> 14)
#define SEQ_PAL(i) ((i & 0b0011100000000000) >> 11)
#define SEQ_BMP(i) ((i & 0b0000011100000000) >> 8)
#define SEQ_DUR(i) ((i & 0b0000000011111111) >> 0)
void AnimatedROMIcon(u8 (&data)[8][512], u16 (&palette)[8][16], u16 (&sequence)[64], u32 (&animatedTexRef)[32 * 32 * 64], std::vector<int> &animatedSequenceRef)
{
for (int i = 0; i < 64; i++)
{
if (!sequence[i])
break;
u32* frame = &animatedTexRef[32 * 32 * i];
ROMIcon(data[SEQ_BMP(sequence[i])], palette[SEQ_PAL(sequence[i])], frame);
if (SEQ_FLIPH(sequence[i]))
{
for (int x = 0; x < 32; x++)
{
for (int y = 0; y < 32/2; y++)
{
std::swap(frame[x * 32 + y], frame[x * 32 + (32 - 1 - y)]);
}
}
}
if (SEQ_FLIPV(sequence[i]))
{
for (int x = 0; x < 32/2; x++)
{
for (int y = 0; y < 32; y++)
{
std::swap(frame[x * 32 + y], frame[(32 - 1 - x) * 32 + y]);
}
}
}
for (int j = 0; j < SEQ_DUR(sequence[i]); j++)
animatedSequenceRef.push_back(i);
}
}
void UnloadROM(int slot)
{
if (slot == ROMSlot_NDS)
......
......@@ -10,6 +10,7 @@ SET(SOURCES_QT_SDL
AudioSettingsDialog.cpp
WifiSettingsDialog.cpp
InterfaceSettingsDialog.cpp
ROMInfoDialog.cpp
Input.cpp
LAN_PCap.cpp
LAN_Socket.cpp
......
/*
Copyright 2016-2021 Arisotura, WaluigiWare64
This file is part of melonDS.
melonDS 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 3 of the License, or (at your option)
any later version.
melonDS 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 melonDS. If not, see http://www.gnu.org/licenses/.
*/
#include "ROMInfoDialog.h"
#include "ui_ROMInfoDialog.h"
#include <QFileDialog>
#include "NDS.h"
#include "NDSCart.h"
#include "Platform.h"
#include "Config.h"
#include "PlatformConfig.h"
QString IntToHex(u64 num)
{
return ("0x" + QString::number(num, 16).toUpper());
}
QString QStringBytes(u64 num)
{
return (QString::number(num) + " Bytes");
}
ROMInfoDialog* ROMInfoDialog::currentDlg = nullptr;
ROMInfoDialog::ROMInfoDialog(QWidget* parent) : QDialog(parent), ui(new Ui::ROMInfoDialog)
{
ui->setupUi(this);
setAttribute(Qt::WA_DeleteOnClose);
u32 iconData[32 * 32];
Frontend::ROMIcon(NDSCart::Banner.Icon, NDSCart::Banner.Palette, iconData);
iconImage = QImage(reinterpret_cast<unsigned char*>(iconData), 32, 32, QImage::Format_ARGB32).copy();
ui->iconImage->setPixmap(QPixmap::fromImage(iconImage));
if (NDSCart::Banner.Version == 0x103)
{
u32 animatedIconData[32 * 32 * 64] = {0};
Frontend::AnimatedROMIcon(NDSCart::Banner.DSiIcon, NDSCart::Banner.DSiPalette, NDSCart::Banner.DSiSequence, animatedIconData, animatedSequence);
for (int i = 0; i < 64; i++)
{
if (animatedIconData[32 * 32 * i] == 0)
break;
animatedIconImages.push_back(QPixmap::fromImage(QImage(reinterpret_cast<unsigned char*>(&animatedIconData[32 * 32 * i]), 32, 32, QImage::Format_ARGB32).copy()));
}
iconTimeline = new QTimeLine(animatedSequence.size() / 60 * 1000, this);
iconTimeline->setFrameRange(0, animatedSequence.size() - 1);
iconTimeline->setLoopCount(0);
iconTimeline->setEasingCurve(QEasingCurve::Linear);
connect(iconTimeline, &QTimeLine::frameChanged, this, &ROMInfoDialog::iconSetFrame);
iconTimeline->start();
}
else
{
ui->dsiIconImage->setPixmap(QPixmap::fromImage(iconImage));
}
ui->iconTitle->setText(QString::fromUtf16(NDSCart::Banner.EnglishTitle));
ui->japaneseTitle->setText(QString::fromUtf16(NDSCart::Banner.JapaneseTitle));
ui->englishTitle->setText(QString::fromUtf16(NDSCart::Banner.EnglishTitle));
ui->frenchTitle->setText(QString::fromUtf16(NDSCart::Banner.FrenchTitle));
ui->germanTitle->setText(QString::fromUtf16(NDSCart::Banner.GermanTitle));
ui->italianTitle->setText(QString::fromUtf16(NDSCart::Banner.ItalianTitle));
ui->spanishTitle->setText(QString::fromUtf16(NDSCart::Banner.SpanishTitle));
if (NDSCart::Banner.Version > 1)
ui->chineseTitle->setText(QString::fromUtf16(NDSCart::Banner.ChineseTitle));
else
ui->chineseTitle->setText("None");
if (NDSCart::Banner.Version > 2)
ui->koreanTitle->setText(QString::fromUtf16(NDSCart::Banner.KoreanTitle));
else
ui->koreanTitle->setText("None");
ui->gameTitle->setText(QString::fromLatin1(NDSCart::Header.GameTitle, 12));
ui->gameCode->setText(QString::fromLatin1(NDSCart::Header.GameCode, 4));
ui->makerCode->setText(QString::fromLatin1(NDSCart::Header.MakerCode, 2));
ui->cardSize->setText(QString::number(128 << NDSCart::Header.CardSize) + " KB");
ui->arm9RomOffset->setText(IntToHex(NDSCart::Header.ARM9ROMOffset));
ui->arm9EntryAddress->setText(IntToHex(NDSCart::Header.ARM9EntryAddress));
ui->arm9RamAddress->setText(IntToHex(NDSCart::Header.ARM9RAMAddress));
ui->arm9Size->setText(QStringBytes(NDSCart::Header.ARM9Size));
ui->arm7RomOffset->setText(IntToHex(NDSCart::Header.ARM7ROMOffset));
ui->arm7EntryAddress->setText(IntToHex(NDSCart::Header.ARM7EntryAddress));
ui->arm7RamAddress->setText(IntToHex(NDSCart::Header.ARM7RAMAddress));
ui->arm7Size->setText(QStringBytes(NDSCart::Header.ARM7Size));
ui->fntOffset->setText(IntToHex(NDSCart::Header.FNTOffset));
ui->fntSize->setText(QStringBytes(NDSCart::Header.FNTSize));
ui->fatOffset->setText(IntToHex(NDSCart::Header.FATOffset));
ui->fatSize->setText(QStringBytes(NDSCart::Header.FATSize));
}
ROMInfoDialog::~ROMInfoDialog()
{
delete ui;
}
void ROMInfoDialog::done(int r)
{
QDialog::done(r);
closeDlg();
}
void ROMInfoDialog::on_saveIconButton_clicked()
{
QString filename = QFileDialog::getSaveFileName(this,
"Save Icon",
Config::LastROMFolder,
"PNG Images (*.png)");
if (filename.isEmpty())
return;
iconImage.save(filename, "PNG");
}
void ROMInfoDialog::iconSetFrame(int frame)
{
ui->dsiIconImage->setPixmap(animatedIconImages[animatedSequence[frame]]);
}
/*
Copyright 2016-2021 Arisotura, WaluigiWare64
This file is part of melonDS.
melonDS 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 3 of the License, or (at your option)
any later version.
melonDS 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 melonDS. If not, see http://www.gnu.org/licenses/.
*/
#ifndef ROMINFODIALOG_H
#define ROMINFODIALOG_H
#include <QDialog>
#include <QTimeLine>
#include <QPixmap>
#include <QImage>
#include "types.h"
#include "FrontendUtil.h"
namespace Ui { class ROMInfoDialog; }
class ROMInfoDialog;
class ROMInfoDialog : public QDialog
{
Q_OBJECT
public:
explicit ROMInfoDialog(QWidget* parent);
~ROMInfoDialog();
static ROMInfoDialog* currentDlg;
static ROMInfoDialog* openDlg(QWidget* parent)
{
if (currentDlg)
{
currentDlg->activateWindow();
return currentDlg;
}
currentDlg = new ROMInfoDialog(parent);
currentDlg->open();
return currentDlg;
}
static void closeDlg()
{
currentDlg = nullptr;
}
private slots:
void done(int r);
void on_saveIconButton_clicked();
void iconSetFrame(int frame);
private:
Ui::ROMInfoDialog* ui;
QImage iconImage;
QTimeLine* iconTimeline;
std::vector<QPixmap> animatedIconImages;
std::vector<int> animatedSequence;
};
#endif // ROMINFODIALOG_H
This diff is collapsed.
......@@ -57,6 +57,7 @@
#include "AudioSettingsDialog.h"
#include "WifiSettingsDialog.h"
#include "InterfaceSettingsDialog.h"
#include "ROMInfoDialog.h"
#include "types.h"
#include "version.h"
......@@ -65,6 +66,7 @@
#include "OSD.h"
#include "NDS.h"
#include "NDSCart.h"
#include "GBACart.h"
#include "GPU.h"
#include "SPU.h"
......@@ -1331,6 +1333,10 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent)
actSetupCheats = menu->addAction("Setup cheat codes");
actSetupCheats->setMenuRole(QAction::NoRole);
connect(actSetupCheats, &QAction::triggered, this, &MainWindow::onSetupCheats);
menu->addSeparator();
actROMInfo = menu->addAction("ROM Info");
connect(actROMInfo, &QAction::triggered, this, &MainWindow::onROMInfo);
}
{
QMenu* menu = menubar->addMenu("Config");
......@@ -1533,9 +1539,10 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent)
actSetupCheats->setEnabled(false);
actEnableCheats->setChecked(Config::EnableCheats != 0);
actROMInfo->setEnabled(false);
actSavestateSRAMReloc->setChecked(Config::SavestateRelocSRAM != 0);
actScreenRotation[Config::ScreenRotation]->setChecked(true);
......@@ -2335,6 +2342,10 @@ void MainWindow::onCheatsDialogFinished(int res)
emuThread->emuUnpause();
}
void MainWindow::onROMInfo()
{
ROMInfoDialog* dlg = ROMInfoDialog::openDlg(this);
}
void MainWindow::onOpenEmuSettings()
{
......@@ -2589,6 +2600,8 @@ void MainWindow::onEmuStart()
actImportSavefile->setEnabled(true);
actSetupCheats->setEnabled(true);
actROMInfo->setEnabled(true);
}
void MainWindow::onEmuStop()
......@@ -2609,6 +2622,8 @@ void MainWindow::onEmuStop()
actFrameStep->setEnabled(false);
actSetupCheats->setEnabled(false);
actROMInfo->setEnabled(false);
}
void MainWindow::onUpdateVideoSettings(bool glchange)
......
......@@ -235,6 +235,7 @@ private slots:
void onEnableCheats(bool checked);
void onSetupCheats();
void onCheatsDialogFinished(int res);
void onROMInfo();
void onOpenEmuSettings();
void onEmuSettingsDialogFinished(int res);
......@@ -310,6 +311,7 @@ public:
QAction* actFrameStep;
QAction* actEnableCheats;
QAction* actSetupCheats;
QAction* actROMInfo;
QAction* actEmuSettings;
QAction* actInputConfig;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment