Add title info display for NDS files

This commit is contained in:
d0k3 2017-03-31 03:14:07 +02:00
parent b9501318c5
commit c19bc1c464
9 changed files with 221 additions and 54 deletions

View File

@ -63,6 +63,8 @@ u32 IdentifyFileType(const char* path) {
return SYS_TICKDB; // ticket.db return SYS_TICKDB; // ticket.db
} else if (memcmp(header, smdh_magic, sizeof(smdh_magic)) == 0) { } else if (memcmp(header, smdh_magic, sizeof(smdh_magic)) == 0) {
return GAME_SMDH; // SMDH file return GAME_SMDH; // SMDH file
} else if (ValidateTwlHeader((TwlHeader*) data) == 0) {
return GAME_NDS; // NDS rom file
} }
} }
if ((fsize > sizeof(BossHeader)) && if ((fsize > sizeof(BossHeader)) &&

View File

@ -14,11 +14,12 @@
#define GAME_NUSCDN (1UL<<9) #define GAME_NUSCDN (1UL<<9)
#define GAME_TICKET (1UL<<10) #define GAME_TICKET (1UL<<10)
#define GAME_SMDH (1UL<<11) #define GAME_SMDH (1UL<<11)
#define SYS_FIRM (1UL<<12) #define GAME_NDS (1UL<<12)
#define SYS_TICKDB (1UL<<13) #define SYS_FIRM (1UL<<13)
#define BIN_NCCHNFO (1UL<<14) #define SYS_TICKDB (1UL<<14)
#define BIN_LAUNCH (1UL<<15) #define BIN_NCCHNFO (1UL<<15)
#define BIN_SUPPORT (1UL<<16) #define BIN_LAUNCH (1UL<<16)
#define BIN_SUPPORT (1UL<<17)
#define TYPE_BASE 0x00FFFFFF // 24 bit reserved for base types #define TYPE_BASE 0x00FFFFFF // 24 bit reserved for base types
#define FLAG_CTR (1UL<<29) #define FLAG_CTR (1UL<<29)
@ -31,7 +32,7 @@
#define FYTPE_ENCRYPTABLE(tp) (tp&(GAME_CIA|GAME_NCSD|GAME_NCCH|GAME_BOSS)) #define FYTPE_ENCRYPTABLE(tp) (tp&(GAME_CIA|GAME_NCSD|GAME_NCCH|GAME_BOSS))
#define FTYPE_BUILDABLE(tp) (tp&(GAME_NCSD|GAME_NCCH|GAME_TMD)) #define FTYPE_BUILDABLE(tp) (tp&(GAME_NCSD|GAME_NCCH|GAME_TMD))
#define FTYPE_BUILDABLE_L(tp) (FTYPE_BUILDABLE(tp) && (tp&(GAME_TMD))) #define FTYPE_BUILDABLE_L(tp) (FTYPE_BUILDABLE(tp) && (tp&(GAME_TMD)))
#define FTYPE_TITLEINFO(tp) (tp&(GAME_SMDH|GAME_NCCH|GAME_NCSD|GAME_CIA|GAME_TMD)) #define FTYPE_TITLEINFO(tp) (tp&(GAME_SMDH|GAME_NCCH|GAME_NCSD|GAME_CIA|GAME_TMD|GAME_NDS))
#define FTYPE_TRANSFERABLE(tp) ((u32) (tp&(IMG_FAT|FLAG_CTR)) == (u32) (IMG_FAT|FLAG_CTR)) #define FTYPE_TRANSFERABLE(tp) ((u32) (tp&(IMG_FAT|FLAG_CTR)) == (u32) (IMG_FAT|FLAG_CTR))
#define FTYPE_HSINJECTABLE(tp) ((u32) (tp&(GAME_NCCH|FLAG_CXI)) == (u32) (GAME_NCCH|FLAG_CXI)) #define FTYPE_HSINJECTABLE(tp) ((u32) (tp&(GAME_NCCH|FLAG_CXI)) == (u32) (GAME_NCCH|FLAG_CXI))
#define FTYPE_RESTORABLE(tp) (tp&(IMG_NAND)) #define FTYPE_RESTORABLE(tp) (tp&(IMG_NAND))

View File

@ -8,4 +8,5 @@
#include "firm.h" #include "firm.h"
#include "boss.h" #include "boss.h"
#include "smdh.h" #include "smdh.h"
#include "nds.h"
#include "ncchinfo.h" #include "ncchinfo.h"

View File

@ -1455,6 +1455,23 @@ u32 ShowTmdFileTitleInfo(const char* path) {
return ShowGameFileTitleInfo(path_content); return ShowGameFileTitleInfo(path_content);
} }
u32 ShowNdsFileTitleInfo(const char* path) {
const u32 lwrap = 24;
TwlIconData* twl_icon = (TwlIconData*) TEMP_BUFFER;
u8* icon = (u8*) (TEMP_BUFFER + sizeof(TwlIconData));
char* desc = (char*) icon + TWLICON_SIZE_ICON;
if ((LoadTwlIconData(path, twl_icon) != 0) ||
(GetTwlIcon(icon, twl_icon) != 0) ||
(GetTwlTitle(desc, twl_icon) != 0))
return 1;
WordWrapString(desc, lwrap);
ShowIconString(icon, TWLICON_DIM_ICON, TWLICON_DIM_ICON, "%s", desc);
InputWait();
ClearScreenF(true, false, COLOR_STD_BG);
return 0;
}
u32 ShowGameFileTitleInfo(const char* path) { u32 ShowGameFileTitleInfo(const char* path) {
u32 filetype = IdentifyFileType(path); u32 filetype = IdentifyFileType(path);
u32 ret = 1; u32 ret = 1;
@ -1470,6 +1487,8 @@ u32 ShowGameFileTitleInfo(const char* path) {
ret = ShowCiaFileTitleInfo(path); ret = ShowCiaFileTitleInfo(path);
} else if (filetype & GAME_TMD) { } else if (filetype & GAME_TMD) {
ret = ShowTmdFileTitleInfo(path); ret = ShowTmdFileTitleInfo(path);
} else if (filetype & GAME_NDS) {
ret = ShowNdsFileTitleInfo(path);
} }
return ret; return ret;

75
source/game/nds.c Normal file
View File

@ -0,0 +1,75 @@
#include "nds.h"
#include "vff.h"
#define CRC16_TABVAL 0x0000, 0xCC01, 0xD801, 0x1400, 0xF001, 0x3C00, 0x2800, 0xE401, 0xA001, 0x6C00, 0x7800, 0xB401, 0x5000, 0x9C01, 0x8801, 0x4400
// see: https://github.com/TASVideos/desmume/blob/master/desmume/src/bios.cpp#L1070tions
u16 crc16_quick(const void* src, u32 len) {
const u16 tabval[] = { CRC16_TABVAL };
u16* data = (u16*) src;
u16 crc = 0xFFFF;
for (len >>= 1; len; len--) {
u16 curr = *(data++);
for (u32 i = 0; i < 4; i++) {
u16 tval = tabval[crc&0xF];
crc >>= 4;
crc ^= tval;
tval = tabval[(curr >> (4*i))&0xF];
crc ^= tval;
}
}
return crc;
}
u32 ValidateTwlHeader(TwlHeader* twl) {
if (twl->logo_crc != NDS_LOGO_CRC16) return 1;
return (crc16_quick(twl->logo, sizeof(twl->logo)) == NDS_LOGO_CRC16) ? 0 : 1;
}
u32 LoadTwlIconData(const char* path, TwlIconData* icon) {
u8 ntr_header[0x200]; // we only need the NTR header (ignore TWL stuff)
TwlHeader* twl = (TwlHeader*) ntr_header;
UINT br;
if ((fvx_qread(path, ntr_header, 0, 0x200, &br) != FR_OK) || (br != 0x200) ||
(ValidateTwlHeader(twl) != 0))
return 1;
// we also don't need anything beyond the v0x0001 icon, so ignore this, too
if ((fvx_qread(path, icon, twl->icon_offset, TWLICON_SIZE_DATA(0x0001), &br) != FR_OK) || (br != TWLICON_SIZE_DATA(0x0001)) ||
(!TWLICON_SIZE_DATA(icon->version)) || (crc16_quick(((u8*) icon) + 0x20, TWLICON_SIZE_DATA(0x0001) - 0x20) != icon->crc_0x0020_0x0840))
return 1;
icon->version = 0x0001; // just to be safe
return 0;
}
// TWL title is max 128(+1) chars long
u32 GetTwlTitle(char* desc, const TwlIconData* twl_icon) {
const u16* title = twl_icon->title_eng; // english title
memset(desc, 0, TWLICON_SIZE_DESC + 1);
for (u32 i = 0; i < TWLICON_SIZE_DESC; i++) desc[i] = title[i];
return 0;
}
// TWL icon: 32x32 pixel, 8x8 tiles
u32 GetTwlIcon(u8* icon, const TwlIconData* twl_icon) {
const u32 h = TWLICON_DIM_ICON; // fixed size
const u32 w = TWLICON_DIM_ICON; // fixed size
u16* palette = twl_icon->palette;
u8* pix4 = (u8*) twl_icon->icon;
for (u32 y = 0; y < h; y += 8) {
for (u32 x = 0; x < w; x += 8) {
for (u32 i = 0; i < 8*8; i++) {
u32 ix = x + (i & 0x7);
u32 iy = y + (i >> 3);
u16 pix555 = palette[((i%2) ? (*pix4 >> 4) : *pix4) & 0xF];
u8* pix888 = icon + ((iy * w) + ix) * 3;
*(pix888++) = ((pix555 >> 10) & 0x1F) << 3; // B
*(pix888++) = ((pix555 >> 5) & 0x1F) << 3; // G
*(pix888++) = ((pix555 >> 0) & 0x1F) << 3; // R
if (i % 2) pix4++;
}
}
}
return 0;
}

115
source/game/nds.h Normal file
View File

@ -0,0 +1,115 @@
#pragma once
#include "common.h"
// size of the icon struct:
// see: http://problemkaputt.de/gbatek.htm#dscartridgeicontitle
// v0x0001 -> 0x0840 byte (contains JAP, USA, FRE, GER, ITA, ESP titles)
// v0x0002 -> 0x0940 byte (adds CHN title)
// v0x0003 -> 0x0A40 byte (adds KOR title)
// v0x0103 -> 0x23C0 byte (adds TWL animated icon data)
#define TWLICON_SIZE_DATA(v) ((v == 0x0001) ? 0x0840 : (v == 0x0002) ? 0x0940 : \
(v == 0x0003) ? 0x1240 : (v == 0x0103) ? 0x23C0 : 0x0000)
#define TWLICON_SIZE_DESC 128
#define TWLICON_DIM_ICON 32
#define TWLICON_SIZE_ICON (TWLICON_DIM_ICON * TWLICON_DIM_ICON * 3) // w * h * bpp (rgb888)
#define NDS_LOGO_CRC16 0xCF56
// see: http://problemkaputt.de/gbatek.htm#dscartridgeicontitle
typedef struct {
u16 version;
u16 crc_0x0020_0x0840;
u16 crc_0x0020_0x0940;
u16 crc_0x0020_0x0A40;
u16 crc_0x1240_0x23C0;
u8 reserved[0x16];
u8 icon[0x200]; // 32x32x4bpp / 4x4 tiles
u16 palette[0x10]; // palette[0] is transparent
u16 title_jap[0x80];
u16 title_eng[0x80];
u16 title_fre[0x80];
u16 title_ger[0x80];
u16 title_ita[0x80];
u16 title_spa[0x80];
u16 title_chn[0x80];
u16 title_kor[0x80];
u16 title_reserved[0x8 * 0x80];
u8 icon_anim[0x200 * 0x8]; // 32x32x4bpp / 8 frames
u16 palette_anim[0x10 * 0x8]; // 8 frames
u16 sequence_anim[0x40];
} __attribute__((packed)) TwlIconData;
// very limited, information taken from here:
// https://github.com/devkitPro/ndstool/blob/dsi-support/source/header.h
// http://problemkaputt.de/gbatek.htm#dscartridgeheader
// http://problemkaputt.de/gbatek.htm#dsicartridgeheader
typedef struct {
// common stuff (DS + DSi)
char game_title[12];
char game_code[4];
char maker_code[2];
u8 unit_code; // (0x00=NDS, 0x02=NDS+DSi, 0x03=DSi)
u8 seed_select;
u8 device_capacity; // cartridge size: (128 * 1024) << this
u8 reserved0[7];
u8 dsi_flags;
u8 nds_region;
u8 rom_version;
u8 autostart; // bit2: skip "press button" after Health & Safety
u32 arm9_rom_offset;
u32 arm9_entry_address;
u32 arm9_ram_address;
u32 arm9_size;
u32 arm7_rom_offset;
u32 arm7_entry_address;
u32 arm7_ram_address;
u32 arm7_size;
u32 fnt_offset;
u32 fnt_size;
u32 fat_offset;
u32 fat_size;
u32 arm9_overlay_offset;
u32 arm9_overlay_size;
u32 arm7_overlay_offset;
u32 arm7_overlay_size;
u32 rom_control_normal; // 0x00416657 for OneTimePROM
u32 rom_control_key1; // 0x081808F8 for OneTimePROM
u32 icon_offset;
u16 secure_area_crc;
u16 secure_area_delay;
u32 arm9_auto_load;
u32 arm7_auto_load;
u64 secure_area_disable;
u32 ntr_rom_size; // in byte
u32 header_size;
u8 reserved1[56];
u8 logo[156];
u16 logo_crc;
u16 header_crc;
u8 debugger_reserved[0x20];
// extended mode stuff (DSi only)
u8 ignored0[0x40]; // ignored
u32 arm9i_rom_offset;
u32 reserved2;
u32 arm9i_load_adress;
u32 arm9i_size;
u32 arm7i_rom_offset;
u32 unknown1;
u32 arm7i_load_adress;
u32 arm7i_size;
u8 ignored1[0x30]; // ignored
u32 ntr_twl_rom_size;
u8 unknown2[12];
u8 ignored2[0x10]; // ignored
u64 title_id;
u32 pubsav_size;
u32 prvsav_size;
u8 reserved3[176];
u8 unknown3[0x10];
u8 ignored3[0xD00]; // ignored
} __attribute__((packed)) TwlHeader;
u32 ValidateTwlHeader(TwlHeader* twl);
u32 LoadTwlIconData(const char* path, TwlIconData* icon);
u32 GetTwlTitle(char* desc, const TwlIconData* twl_icon);
u32 GetTwlIcon(u8* icon, const TwlIconData* twl_icon);

View File

@ -3,7 +3,7 @@
#include "command_ctr.h" #include "command_ctr.h"
#include "command_ntr.h" #include "command_ntr.h"
#include "card_eeprom.h" #include "card_eeprom.h"
#include "ndsheader.h" #include "nds.h"
#include "ncch.h" #include "ncch.h"
#include "ncsd.h" #include "ncsd.h"

View File

@ -1,47 +0,0 @@
#pragma once
#include "common.h"
// very limited, information taken from here:
// https://github.com/devkitPro/ndstool/blob/dsi-support/source/header.h
typedef struct {
// common stuff (DS + DSi)
char game_title[12];
char game_code[4];
char maker_code[2];
u8 unit_code; // (0x00=NDS, 0x02=NDS+DSi, 0x03=DSi)
u8 seed_select;
u8 device_capacity; // cartridge size: (128 * 1024) << this
u8 reserved0[7];
u8 unknown0[2];
u8 rom_version;
u8 flags;
u8 ignored0[0x60]; // ignored
u32 ntr_rom_size; // in byte
u32 header_size;
u8 reserved1[56];
u8 logo[156];
u16 logo_crc;
u16 header_crc;
u8 debugger_reserved[0x20];
// extended mode stuff (DSi only)
u8 ignored1[0x40]; // ignored
u32 arm9i_rom_offset;
u32 reserved2;
u32 arm9i_load_adress;
u32 arm9i_size;
u32 arm7i_rom_offset;
u32 unknown1;
u32 arm7i_load_adress;
u32 arm7i_size;
u8 ignored2[0x30]; // ignored
u32 ntr_twl_rom_size;
u8 unknown2[12];
u8 ignored3[0x10]; // ignored
u64 title_id;
u32 pubsav_size;
u32 prvsav_size;
u8 reserved3[176];
u8 unknown3[0x10];
u8 ignored4[0xD00]; // ignored
} __attribute__((packed)) TwlHeader;

View File

@ -697,6 +697,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur
(filetype & GAME_BOSS ) ? "BOSS file options..." : (filetype & GAME_BOSS ) ? "BOSS file options..." :
(filetype & GAME_NUSCDN)? "Decrypt NUS/CDN file" : (filetype & GAME_NUSCDN)? "Decrypt NUS/CDN file" :
(filetype & GAME_SMDH) ? "Show SMDH title info" : (filetype & GAME_SMDH) ? "Show SMDH title info" :
(filetype & GAME_NDS) ? "Show NDS title info" :
(filetype & SYS_FIRM ) ? "FIRM image options..." : (filetype & SYS_FIRM ) ? "FIRM image options..." :
(filetype & SYS_TICKDB) ? "Mount as ticket.db" : (filetype & SYS_TICKDB) ? "Mount as ticket.db" :
(filetype & BIN_NCCHNFO)? "NCCHinfo options..." : (filetype & BIN_NCCHNFO)? "NCCHinfo options..." :