2019-07-13 23:00:10 -04:00
# include "bdri.h"
2019-09-21 10:30:18 -04:00
# include "vff.h"
2019-07-13 23:00:10 -04:00
# define FAT_ENTRY_SIZE 2 * sizeof(u32)
2019-09-21 10:30:18 -04:00
# define getfatflag(uv) (((uv) & 0x80000000UL) != 0)
# define getfatindex(uv) ((uv) & 0x7FFFFFFFUL)
# define buildfatuv(index, flag) ((index) | ((flag) ? 0x80000000UL : 0))
2019-07-13 23:00:10 -04:00
2020-01-11 12:59:12 -05:00
typedef struct {
char magic [ 4 ] ; // "BDRI"
u32 version ; // == 0x30000
u64 info_offset ; // == 0x20
u64 image_size ; // in blocks; and including the pre-header
u32 image_block_size ;
u8 padding1 [ 4 ] ;
u8 unknown [ 4 ] ;
u32 data_block_size ;
u64 dht_offset ;
u32 dht_bucket_count ;
u8 padding2 [ 4 ] ;
u64 fht_offset ;
u32 fht_bucket_count ;
u8 padding3 [ 4 ] ;
u64 fat_offset ;
u32 fat_entry_count ; // exculdes 0th entry
u8 padding4 [ 4 ] ;
u64 data_offset ;
u32 data_block_count ; // == fat_entry_count
u8 padding5 [ 4 ] ;
u32 det_start_block ;
u32 det_block_count ;
u32 max_dir_count ;
u8 padding6 [ 4 ] ;
u32 fet_start_block ;
u32 fet_block_count ;
u32 max_file_count ;
u8 padding7 [ 4 ] ;
} __attribute__ ( ( packed ) ) BDRIFsHeader ;
typedef struct {
char magic [ 8 ] ; // varies based on media type and importdb vs titledb
u8 reserved [ 0x78 ] ;
BDRIFsHeader fs_header ;
} __attribute__ ( ( packed ) ) TitleDBPreHeader ;
typedef struct {
char magic [ 4 ] ; // "TICK"
u32 unknown1 ; // usually (assuming always) == 1
u32 unknown2 ;
u32 unknown3 ;
BDRIFsHeader fs_header ;
} __attribute__ ( ( packed ) ) TickDBPreHeader ;
typedef struct {
u32 parent_index ;
u8 title_id [ 8 ] ;
u32 next_sibling_index ;
u8 padding1 [ 4 ] ;
u32 start_block_index ;
u64 size ; // in bytes
u8 padding2 [ 8 ] ;
u32 hash_bucket_next_index ;
} __attribute__ ( ( packed ) ) TdbFileEntry ;
typedef struct {
u32 total_entry_count ;
u32 max_entry_count ; // == max_file_count + 1
u8 padding [ 32 ] ;
u32 next_dummy_index ;
} __attribute__ ( ( packed ) ) DummyFileEntry ;
typedef struct {
u32 unknown ; // usually (assuming always) == 1
u32 ticket_size ; // == 0x350 == sizeof(Ticket)
Ticket ticket ;
} __attribute__ ( ( packed , aligned ( 4 ) ) ) TicketEntry ;
2019-09-21 10:30:18 -04:00
static FIL * bdrifp ;
static FRESULT BDRIRead ( UINT ofs , UINT btr , void * buf ) {
if ( bdrifp ) {
FRESULT res ;
UINT br ;
if ( ( fvx_tell ( bdrifp ) ! = ofs ) & &
( fvx_lseek ( bdrifp , ofs ) ! = FR_OK ) ) return FR_DENIED ;
res = fvx_read ( bdrifp , buf , btr , & br ) ;
if ( ( res = = FR_OK ) & & ( br ! = btr ) ) res = FR_DENIED ;
return res ;
} else return FR_DENIED ;
}
static FRESULT BDRIWrite ( UINT ofs , UINT btw , const void * buf ) {
if ( bdrifp ) {
FRESULT res ;
UINT bw ;
if ( ( fvx_tell ( bdrifp ) ! = ofs ) & &
( fvx_lseek ( bdrifp , ofs ) ! = FR_OK ) ) return FR_DENIED ;
res = fvx_write ( bdrifp , buf , btw , & bw ) ;
if ( ( res = = FR_OK ) & & ( bw ! = btw ) ) res = FR_DENIED ;
return res ;
} else return FR_DENIED ;
}
bool CheckDBMagic ( const u8 * pre_header , bool tickdb ) {
2019-07-13 23:00:10 -04:00
const TitleDBPreHeader * title = ( TitleDBPreHeader * ) pre_header ;
const TickDBPreHeader * tick = ( TickDBPreHeader * ) pre_header ;
2020-08-24 21:27:19 -07:00
return ( tickdb ? ( ( strncmp ( tick - > magic , " TICK " , 4 ) = = 0 ) & & ( tick - > unknown1 = = 1 ) ) :
2019-07-13 23:00:10 -04:00
( ( strcmp ( title - > magic , " NANDIDB " ) = = 0 ) | | ( strcmp ( title - > magic , " NANDTDB " ) = = 0 ) | |
( strcmp ( title - > magic , " TEMPIDB " ) = = 0 ) | | ( strcmp ( title - > magic , " TEMPTDB " ) = = 0 ) ) ) & &
( strcmp ( ( tickdb ? tick - > fs_header : title - > fs_header ) . magic , " BDRI " ) = = 0 ) & &
( ( tickdb ? tick - > fs_header : title - > fs_header ) . version = = 0x30000 ) ;
}
// This function was taken, with slight modification, from https://3dbrew.org/wiki/Inner_FAT
2019-09-21 10:30:18 -04:00
static u32 GetHashBucket ( const u8 * tid , u32 parent_dir_index , u32 bucket_count ) {
2019-07-13 23:00:10 -04:00
u32 hash = parent_dir_index ^ 0x091A2B3C ;
for ( u8 i = 0 ; i < 2 ; + + i ) {
hash = ( hash > > 1 ) | ( hash < < 31 ) ;
hash ^ = ( u32 ) tid [ i * 4 ] ;
hash ^ = ( u32 ) tid [ i * 4 + 1 ] < < 8 ;
hash ^ = ( u32 ) tid [ i * 4 + 2 ] < < 16 ;
hash ^ = ( u32 ) tid [ i * 4 + 3 ] < < 24 ;
}
return hash % bucket_count ;
}
2020-01-23 00:15:01 +00:00
static u32 GetBDRIEntrySize ( const BDRIFsHeader * fs_header , const u32 fs_header_offset , const u8 * title_id , u32 * size ) {
if ( ( fs_header - > info_offset ! = 0x20 ) | | ( fs_header - > fat_entry_count ! = fs_header - > data_block_count ) ) // Could be more thorough
return 1 ;
const u32 data_offset = fs_header_offset + fs_header - > data_offset ;
const u32 fet_offset = data_offset + fs_header - > fet_start_block * fs_header - > data_block_size ;
2020-05-23 19:57:47 -04:00
const u32 fht_offset = fs_header_offset + fs_header - > fht_offset ;
2020-08-24 21:27:19 -07:00
2020-01-23 00:15:01 +00:00
u32 index = 0 ;
TdbFileEntry file_entry ;
u64 tid_be = getbe64 ( title_id ) ;
u8 * title_id_be = ( u8 * ) & tid_be ;
2020-05-23 19:57:47 -04:00
const u32 hash_bucket = GetHashBucket ( title_id_be , 1 , fs_header - > fht_bucket_count ) ;
2020-08-24 21:27:19 -07:00
2020-05-23 19:57:47 -04:00
if ( BDRIRead ( fht_offset + hash_bucket * sizeof ( u32 ) , sizeof ( u32 ) , & ( file_entry . hash_bucket_next_index ) ) ! = FR_OK )
2020-01-23 00:15:01 +00:00
return 1 ;
2020-05-23 19:57:47 -04:00
2020-01-23 00:15:01 +00:00
// Find the file entry for the tid specified, fail if it doesn't exist
do {
2020-05-23 19:57:47 -04:00
if ( file_entry . hash_bucket_next_index = = 0 )
2020-01-23 00:15:01 +00:00
return 1 ;
2020-08-24 21:27:19 -07:00
2020-05-23 19:57:47 -04:00
index = file_entry . hash_bucket_next_index ;
2020-08-24 21:27:19 -07:00
2020-01-23 00:15:01 +00:00
if ( BDRIRead ( fet_offset + index * sizeof ( TdbFileEntry ) , sizeof ( TdbFileEntry ) , & file_entry ) ! = FR_OK )
return 1 ;
} while ( memcmp ( title_id_be , file_entry . title_id , 8 ) ! = 0 ) ;
2020-08-24 21:27:19 -07:00
2020-01-23 00:15:01 +00:00
* size = file_entry . size ;
return 0 ;
}
2019-09-21 10:30:18 -04:00
static u32 ReadBDRIEntry ( const BDRIFsHeader * fs_header , const u32 fs_header_offset , const u8 * title_id , u8 * entry , const u32 expected_size ) {
if ( ( fs_header - > info_offset ! = 0x20 ) | | ( fs_header - > fat_entry_count ! = fs_header - > data_block_count ) ) // Could be more thorough
2019-07-13 23:00:10 -04:00
return 1 ;
2020-08-24 21:27:19 -07:00
2019-07-13 23:00:10 -04:00
const u32 data_offset = fs_header_offset + fs_header - > data_offset ;
const u32 fet_offset = data_offset + fs_header - > fet_start_block * fs_header - > data_block_size ;
const u32 fht_offset = fs_header_offset + fs_header - > fht_offset ;
const u32 fat_offset = fs_header_offset + fs_header - > fat_offset ;
2020-08-24 21:27:19 -07:00
2019-07-13 23:00:10 -04:00
u32 index = 0 ;
TdbFileEntry file_entry ;
u64 tid_be = getbe64 ( title_id ) ;
u8 * title_id_be = ( u8 * ) & tid_be ;
2020-05-23 19:57:47 -04:00
const u32 hash_bucket = GetHashBucket ( title_id_be , 1 , fs_header - > fht_bucket_count ) ;
2020-08-24 21:27:19 -07:00
2020-05-23 19:57:47 -04:00
if ( BDRIRead ( fht_offset + hash_bucket * sizeof ( u32 ) , sizeof ( u32 ) , & ( file_entry . hash_bucket_next_index ) ) ! = FR_OK )
2019-07-13 23:00:10 -04:00
return 1 ;
2020-05-23 19:57:47 -04:00
2019-07-13 23:00:10 -04:00
// Find the file entry for the tid specified, fail if it doesn't exist
do {
2020-05-23 19:57:47 -04:00
if ( file_entry . hash_bucket_next_index = = 0 )
2019-07-13 23:00:10 -04:00
return 1 ;
2020-08-24 21:27:19 -07:00
2020-05-23 19:57:47 -04:00
index = file_entry . hash_bucket_next_index ;
2020-08-24 21:27:19 -07:00
2019-09-21 10:30:18 -04:00
if ( BDRIRead ( fet_offset + index * sizeof ( TdbFileEntry ) , sizeof ( TdbFileEntry ) , & file_entry ) ! = FR_OK )
2019-07-13 23:00:10 -04:00
return 1 ;
} while ( memcmp ( title_id_be , file_entry . title_id , 8 ) ! = 0 ) ;
2020-08-24 21:27:19 -07:00
2019-07-13 23:00:10 -04:00
if ( expected_size & & ( file_entry . size ! = expected_size ) )
return 1 ;
2020-08-24 21:27:19 -07:00
2019-07-13 23:00:10 -04:00
index = file_entry . start_block_index + 1 ; // FAT entry index
2020-08-24 21:27:19 -07:00
2019-07-13 23:00:10 -04:00
u32 bytes_read = 0 ;
u32 fat_entry [ 2 ] ;
2020-08-24 21:27:19 -07:00
2019-09-21 10:30:18 -04:00
while ( bytes_read < file_entry . size ) { // Read the full entry, walking the FAT node chain
2019-07-13 23:00:10 -04:00
u32 read_start = index - 1 ; // Data region block index
u32 read_count = 0 ;
2020-08-24 21:27:19 -07:00
2019-09-21 10:30:18 -04:00
if ( BDRIRead ( fat_offset + index * FAT_ENTRY_SIZE , FAT_ENTRY_SIZE , fat_entry ) ! = FR_OK )
2019-07-13 23:00:10 -04:00
return 1 ;
2020-08-24 21:27:19 -07:00
2019-09-21 10:30:18 -04:00
if ( ( bytes_read = = 0 ) & & ! getfatflag ( fat_entry [ 0 ] ) )
2019-07-13 23:00:10 -04:00
return 1 ;
2020-08-24 21:27:19 -07:00
2019-09-21 10:30:18 -04:00
u32 next_index = getfatindex ( fat_entry [ 1 ] ) ;
2020-08-24 21:27:19 -07:00
2019-09-21 10:30:18 -04:00
if ( getfatflag ( fat_entry [ 1 ] ) ) { // Multi-entry node
if ( BDRIRead ( fat_offset + ( index + 1 ) * FAT_ENTRY_SIZE , FAT_ENTRY_SIZE , fat_entry ) ! = FR_OK )
2019-07-13 23:00:10 -04:00
return 1 ;
2020-08-24 21:27:19 -07:00
2019-09-21 10:30:18 -04:00
if ( ! getfatflag ( fat_entry [ 0 ] ) | | getfatflag ( fat_entry [ 1 ] ) | | ( getfatindex ( fat_entry [ 0 ] ) ! = index ) | | ( getfatindex ( fat_entry [ 0 ] ) > = getfatindex ( fat_entry [ 1 ] ) ) )
2019-07-13 23:00:10 -04:00
return 1 ;
2020-08-24 21:27:19 -07:00
2019-09-21 10:30:18 -04:00
read_count = getfatindex ( fat_entry [ 1 ] ) + 1 - index ;
2019-07-13 23:00:10 -04:00
} else { // Single-entry node
read_count = 1 ;
}
2020-08-24 21:27:19 -07:00
2019-07-13 23:00:10 -04:00
index = next_index ;
2020-08-24 21:27:19 -07:00
2019-07-13 23:00:10 -04:00
u32 btr = min ( file_entry . size - bytes_read , read_count * fs_header - > data_block_size ) ;
2019-09-21 10:30:18 -04:00
if ( entry & & ( BDRIRead ( data_offset + read_start * fs_header - > data_block_size , btr , entry + bytes_read ) ! = FR_OK ) )
2019-07-13 23:00:10 -04:00
return 1 ;
2020-08-24 21:27:19 -07:00
2019-07-13 23:00:10 -04:00
bytes_read + = btr ;
}
2020-08-24 21:27:19 -07:00
2019-07-13 23:00:10 -04:00
return 0 ;
}
2019-09-21 10:30:18 -04:00
static u32 RemoveBDRIEntry ( const BDRIFsHeader * fs_header , const u32 fs_header_offset , const u8 * title_id ) {
if ( ( fs_header - > info_offset ! = 0x20 ) | | ( fs_header - > fat_entry_count ! = fs_header - > data_block_count ) ) // Could be more thorough
2019-07-13 23:00:10 -04:00
return 1 ;
2020-08-24 21:27:19 -07:00
2019-07-13 23:00:10 -04:00
const u32 data_offset = fs_header_offset + fs_header - > data_offset ;
const u32 det_offset = data_offset + fs_header - > det_start_block * fs_header - > data_block_size ;
const u32 fet_offset = data_offset + fs_header - > fet_start_block * fs_header - > data_block_size ;
const u32 fht_offset = fs_header_offset + fs_header - > fht_offset ;
const u32 fat_offset = fs_header_offset + fs_header - > fat_offset ;
2020-08-24 21:27:19 -07:00
2019-07-13 23:00:10 -04:00
u32 index = 0 , previous_index = 0 ;
TdbFileEntry file_entry ;
u64 tid_be = getbe64 ( title_id ) ;
u8 * title_id_be = ( u8 * ) & tid_be ;
2020-08-24 21:27:19 -07:00
2019-07-13 23:00:10 -04:00
// Read the index of the first file entry from the directory entry table
2019-09-21 10:30:18 -04:00
if ( BDRIRead ( det_offset + 0x2C , sizeof ( u32 ) , & ( file_entry . next_sibling_index ) ) ! = FR_OK )
2019-07-13 23:00:10 -04:00
return 1 ;
2020-08-24 21:27:19 -07:00
2019-07-13 23:00:10 -04:00
// Find the file entry for the tid specified, fail if it doesn't exist
do {
previous_index = index ;
index = file_entry . next_sibling_index ;
2020-08-24 21:27:19 -07:00
2019-07-13 23:00:10 -04:00
if ( index = = 0 )
return 1 ;
2020-08-24 21:27:19 -07:00
2019-09-21 10:30:18 -04:00
if ( BDRIRead ( fet_offset + index * sizeof ( TdbFileEntry ) , sizeof ( TdbFileEntry ) , & file_entry ) ! = FR_OK )
2019-07-13 23:00:10 -04:00
return 1 ;
} while ( memcmp ( title_id_be , file_entry . title_id , 8 ) ! = 0 ) ;
2020-08-24 21:27:19 -07:00
2019-07-13 23:00:10 -04:00
DummyFileEntry dummy_entry ;
2020-08-24 21:27:19 -07:00
2019-07-13 23:00:10 -04:00
// Read the 0th entry in the FET, which is always a dummy entry
2019-09-21 10:30:18 -04:00
if ( BDRIRead ( fet_offset , sizeof ( DummyFileEntry ) , & dummy_entry ) ! = FR_OK )
2019-07-13 23:00:10 -04:00
return 1 ;
2020-08-24 21:27:19 -07:00
2019-07-13 23:00:10 -04:00
if ( dummy_entry . max_entry_count ! = fs_header - > max_file_count + 1 )
return 1 ;
2020-08-24 21:27:19 -07:00
2019-09-21 10:30:18 -04:00
if ( ( BDRIWrite ( fet_offset + index * sizeof ( TdbFileEntry ) , sizeof ( TdbFileEntry ) , & dummy_entry ) ! = FR_OK ) | |
( BDRIWrite ( fet_offset + 0x28 , sizeof ( u32 ) , & index ) ! = FR_OK ) | |
( BDRIWrite ( ( previous_index = = 0 ) ? det_offset + 0x2C : fet_offset + previous_index * sizeof ( TdbFileEntry ) + 0xC , sizeof ( u32 ) , & ( file_entry . next_sibling_index ) ) ! = FR_OK ) )
2019-07-13 23:00:10 -04:00
return 1 ;
2020-08-24 21:27:19 -07:00
2019-07-13 23:00:10 -04:00
const u32 hash_bucket = GetHashBucket ( file_entry . title_id , file_entry . parent_index , fs_header - > fht_bucket_count ) ;
u32 index_hash = 0 ;
2020-08-24 21:27:19 -07:00
2019-09-21 10:30:18 -04:00
if ( BDRIRead ( fht_offset + hash_bucket * sizeof ( u32 ) , sizeof ( u32 ) , & index_hash ) ! = FR_OK )
2019-07-13 23:00:10 -04:00
return 1 ;
2020-08-24 21:27:19 -07:00
2019-07-13 23:00:10 -04:00
if ( index_hash = = index ) {
2019-09-21 10:30:18 -04:00
if ( BDRIWrite ( fht_offset + hash_bucket * sizeof ( u32 ) , sizeof ( u32 ) , & ( file_entry . hash_bucket_next_index ) ) ! = FR_OK )
2019-07-13 23:00:10 -04:00
return 1 ;
} else {
do {
if ( index_hash = = 0 ) // This shouldn't happen if the entry was properly added
break ;
2020-08-24 21:27:19 -07:00
2019-09-21 10:30:18 -04:00
if ( BDRIRead ( fet_offset + index_hash * sizeof ( TdbFileEntry ) + 0x28 , sizeof ( u32 ) , & index_hash ) ! = FR_OK )
2019-07-13 23:00:10 -04:00
return 1 ;
} while ( index_hash ! = index ) ;
2020-08-24 21:27:19 -07:00
2019-09-21 10:30:18 -04:00
if ( ( index_hash ! = 0 ) & & BDRIWrite ( fet_offset + index_hash * sizeof ( TdbFileEntry ) + 0x28 , sizeof ( u32 ) , & ( file_entry . hash_bucket_next_index ) ) ! = FR_OK )
2019-07-13 23:00:10 -04:00
return 1 ;
}
2020-08-24 21:27:19 -07:00
2019-07-13 23:00:10 -04:00
u32 fat_entry [ 2 ] ;
2020-08-24 21:27:19 -07:00
2019-09-21 10:30:18 -04:00
if ( BDRIRead ( fat_offset , FAT_ENTRY_SIZE , fat_entry ) ! = FR_OK )
2019-07-13 23:00:10 -04:00
return 1 ;
2020-08-24 21:27:19 -07:00
2019-09-21 10:30:18 -04:00
if ( getfatflag ( fat_entry [ 1 ] ) | | ( fat_entry [ 0 ] ! = 0 ) )
2019-07-13 23:00:10 -04:00
return 1 ;
2020-08-24 21:27:19 -07:00
2019-09-21 10:30:18 -04:00
u32 next_free_index = getfatindex ( fat_entry [ 1 ] ) , fat_index = file_entry . start_block_index + 1 ;
2020-08-24 21:27:19 -07:00
2019-09-21 10:30:18 -04:00
if ( BDRIWrite ( fat_offset + sizeof ( u32 ) , sizeof ( u32 ) , & fat_index ) ! = FR_OK )
2019-07-13 23:00:10 -04:00
return 1 ;
2020-08-24 21:27:19 -07:00
2019-07-13 23:00:10 -04:00
fat_entry [ 1 ] = fat_index ;
2020-08-24 21:27:19 -07:00
2019-07-13 23:00:10 -04:00
do {
2019-09-21 10:30:18 -04:00
fat_index = getfatindex ( fat_entry [ 1 ] ) ;
2020-08-24 21:27:19 -07:00
2019-09-21 10:30:18 -04:00
if ( BDRIRead ( fat_offset + FAT_ENTRY_SIZE * fat_index , FAT_ENTRY_SIZE , fat_entry ) ! = FR_OK )
2019-07-13 23:00:10 -04:00
return 1 ;
2019-09-21 10:30:18 -04:00
} while ( getfatindex ( fat_entry [ 1 ] ) ! = 0 ) ;
2020-08-24 21:27:19 -07:00
2019-07-13 23:00:10 -04:00
fat_entry [ 1 ] | = next_free_index ;
2020-08-24 21:27:19 -07:00
2019-09-21 10:30:18 -04:00
if ( ( BDRIWrite ( fat_offset + fat_index * FAT_ENTRY_SIZE , FAT_ENTRY_SIZE , fat_entry ) ! = FR_OK ) | |
( BDRIRead ( fat_offset + next_free_index * FAT_ENTRY_SIZE , FAT_ENTRY_SIZE , fat_entry ) ! = FR_OK ) )
2019-07-13 23:00:10 -04:00
return 1 ;
2020-08-24 21:27:19 -07:00
2019-09-21 10:30:18 -04:00
fat_entry [ 0 ] = buildfatuv ( fat_index , false ) ;
2020-08-24 21:27:19 -07:00
2019-09-21 10:30:18 -04:00
if ( BDRIWrite ( fat_offset + next_free_index * FAT_ENTRY_SIZE , FAT_ENTRY_SIZE , fat_entry ) ! = FR_OK )
2019-07-13 23:00:10 -04:00
return 1 ;
2020-08-24 21:27:19 -07:00
2019-07-13 23:00:10 -04:00
return 0 ;
}
2019-09-21 10:30:18 -04:00
static u32 AddBDRIEntry ( const BDRIFsHeader * fs_header , const u32 fs_header_offset , const u8 * title_id , const u8 * entry , const u32 size , bool replace ) {
if ( ( fs_header - > info_offset ! = 0x20 ) | | ( fs_header - > fat_entry_count ! = fs_header - > data_block_count ) ) // Could be more thorough
2019-07-13 23:00:10 -04:00
return 1 ;
2020-08-24 21:27:19 -07:00
2019-07-13 23:00:10 -04:00
if ( ! entry | | ! size )
return 1 ;
2020-08-24 21:27:19 -07:00
2019-07-13 23:00:10 -04:00
const u32 data_offset = fs_header_offset + fs_header - > data_offset ;
const u32 det_offset = data_offset + fs_header - > det_start_block * fs_header - > data_block_size ;
const u32 fet_offset = data_offset + fs_header - > fet_start_block * fs_header - > data_block_size ;
const u32 fht_offset = fs_header_offset + fs_header - > fht_offset ;
const u32 fat_offset = fs_header_offset + fs_header - > fat_offset ;
const u32 size_blocks = ( size / fs_header - > data_block_size ) + ( ( ( size % fs_header - > data_block_size ) = = 0 ) ? 0 : 1 ) ;
2020-08-24 21:27:19 -07:00
2019-07-13 23:00:10 -04:00
u32 index = 0 , max_index = 0 ;
TdbFileEntry file_entry ;
u64 tid_be = getbe64 ( title_id ) ;
u8 * title_id_be = ( u8 * ) & tid_be ;
2019-09-21 10:30:18 -04:00
bool do_replace = false ;
2020-08-24 21:27:19 -07:00
2019-07-13 23:00:10 -04:00
// Read the index of the first file entry from the directory entry table
2020-08-24 21:27:19 -07:00
if ( BDRIRead ( det_offset + 0x2C , sizeof ( u32 ) , & ( file_entry . next_sibling_index ) ) ! = FR_OK )
2019-07-13 23:00:10 -04:00
return 1 ;
2020-08-24 21:27:19 -07:00
2019-09-21 10:30:18 -04:00
// Try to find the file entry for the tid specified
2019-07-13 23:00:10 -04:00
while ( file_entry . next_sibling_index ! = 0 ) {
index = file_entry . next_sibling_index ;
2020-08-24 21:27:19 -07:00
max_index = max ( index , max_index ) ;
2019-09-21 10:30:18 -04:00
if ( BDRIRead ( fet_offset + index * sizeof ( TdbFileEntry ) , sizeof ( TdbFileEntry ) , & file_entry ) ! = FR_OK )
2019-07-13 23:00:10 -04:00
return 1 ;
2020-08-24 21:27:19 -07:00
2019-09-21 10:30:18 -04:00
// If an entry for the tid already existed that is already the specified size and replace was specified, just replace the existing entry
if ( memcmp ( title_id_be , file_entry . title_id , 8 ) = = 0 ) {
if ( ! replace | | ( file_entry . size ! = size ) ) return 1 ;
else {
do_replace = true ;
break ;
}
}
2019-07-13 23:00:10 -04:00
}
2020-08-24 21:27:19 -07:00
2019-07-13 23:00:10 -04:00
u32 fat_entry [ 2 ] ;
2019-09-21 10:30:18 -04:00
u32 fat_index = 0 ;
2020-08-24 21:27:19 -07:00
2019-09-21 10:30:18 -04:00
if ( ! do_replace ) {
if ( BDRIRead ( fat_offset , FAT_ENTRY_SIZE , fat_entry ) ! = FR_OK )
return 1 ;
if ( getfatflag ( fat_entry [ 1 ] ) | | ( fat_entry [ 0 ] ! = 0 ) )
return 1 ;
u32 next_fat_index = getfatindex ( fat_entry [ 1 ] ) , node_size = 0 ;
// Find contiguous free space in the FAT for the entry. Technically there could be a case of enough space existing, but not in a contiguous fasion, but this would never realistically happen
do {
if ( next_fat_index = = 0 )
return 1 ; // Reached the end of the free node chain without finding enough contiguous free space - this should never realistically happen
fat_index = next_fat_index ;
if ( BDRIRead ( fat_offset + fat_index * FAT_ENTRY_SIZE , FAT_ENTRY_SIZE , fat_entry ) ! = FR_OK )
return 1 ;
next_fat_index = getfatindex ( fat_entry [ 1 ] ) ;
if ( getfatflag ( fat_entry [ 1 ] ) ) { // Multi-entry node
if ( BDRIRead ( fat_offset + ( fat_index + 1 ) * FAT_ENTRY_SIZE , FAT_ENTRY_SIZE , fat_entry ) ! = FR_OK )
return 1 ;
if ( ! getfatflag ( fat_entry [ 0 ] ) | | getfatflag ( fat_entry [ 1 ] ) | | ( getfatindex ( fat_entry [ 0 ] ) ! = fat_index ) | | ( getfatindex ( fat_entry [ 0 ] ) > = getfatindex ( fat_entry [ 1 ] ) ) )
return 1 ;
node_size = getfatindex ( fat_entry [ 1 ] ) + 1 - fat_index ;
} else { // Single-entry node
node_size = 1 ;
}
} while ( node_size < size_blocks ) ;
const bool shrink_free_node = node_size > size_blocks ;
if ( shrink_free_node ) {
if ( BDRIRead ( fat_offset + fat_index * FAT_ENTRY_SIZE , FAT_ENTRY_SIZE , fat_entry ) ! = FR_OK )
return 1 ;
if ( node_size - size_blocks = = 1 )
fat_entry [ 1 ] = buildfatuv ( getfatindex ( fat_entry [ 1 ] ) , false ) ;
if ( BDRIWrite ( fat_offset + ( fat_index + size_blocks ) * FAT_ENTRY_SIZE , FAT_ENTRY_SIZE , fat_entry ) ! = FR_OK )
return 1 ;
if ( node_size - size_blocks > 1 ) {
if ( BDRIRead ( fat_offset + ( fat_index + node_size - 1 ) * FAT_ENTRY_SIZE , FAT_ENTRY_SIZE , fat_entry ) ! = FR_OK )
return 1 ;
fat_entry [ 0 ] = buildfatuv ( fat_index + size_blocks , true ) ;
if ( ( BDRIWrite ( fat_offset + ( fat_index + node_size - 1 ) * FAT_ENTRY_SIZE , FAT_ENTRY_SIZE , fat_entry ) ! = FR_OK ) | |
( BDRIWrite ( fat_offset + ( fat_index + size_blocks + 1 ) * FAT_ENTRY_SIZE , FAT_ENTRY_SIZE , fat_entry ) ! = FR_OK ) )
return 1 ;
}
}
if ( BDRIRead ( fat_offset + fat_index * FAT_ENTRY_SIZE , FAT_ENTRY_SIZE , fat_entry ) ! = FR_OK )
return 1 ;
const u32 previous_free_index = getfatindex ( fat_entry [ 0 ] ) , next_free_index = getfatindex ( fat_entry [ 1 ] ) ;
fat_entry [ 0 ] = buildfatuv ( 0 , true ) ;
fat_entry [ 1 ] = buildfatuv ( 0 , size_blocks > 1 ) ;
if ( BDRIWrite ( fat_offset + fat_index * FAT_ENTRY_SIZE , FAT_ENTRY_SIZE , fat_entry ) ! = FR_OK )
return 1 ;
if ( size_blocks > 1 ) {
fat_entry [ 0 ] = buildfatuv ( fat_index , true ) ;
fat_entry [ 1 ] = buildfatuv ( fat_index + size_blocks - 1 , false ) ;
if ( ( BDRIWrite ( fat_offset + ( fat_index + size_blocks - 1 ) * FAT_ENTRY_SIZE , FAT_ENTRY_SIZE , fat_entry ) ! = FR_OK ) | |
( BDRIWrite ( fat_offset + ( fat_index + 1 ) * FAT_ENTRY_SIZE , FAT_ENTRY_SIZE , fat_entry ) ! = FR_OK ) )
return 1 ;
}
if ( next_free_index ! = 0 ) {
if ( BDRIRead ( fat_offset + next_free_index * FAT_ENTRY_SIZE , FAT_ENTRY_SIZE , fat_entry ) ! = FR_OK )
return 1 ;
fat_entry [ 0 ] = buildfatuv ( shrink_free_node ? fat_index + size_blocks : previous_free_index , ( ! shrink_free_node & & ( previous_free_index = = 0 ) ) ) ;
if ( BDRIWrite ( fat_offset + next_free_index * FAT_ENTRY_SIZE , FAT_ENTRY_SIZE , fat_entry ) ! = FR_OK )
return 1 ;
}
if ( BDRIRead ( fat_offset + previous_free_index * FAT_ENTRY_SIZE , FAT_ENTRY_SIZE , fat_entry ) ! = FR_OK )
return 1 ;
fat_entry [ 1 ] = buildfatuv ( shrink_free_node ? fat_index + size_blocks : next_free_index , getfatflag ( fat_entry [ 1 ] ) ) ;
if ( BDRIWrite ( fat_offset + previous_free_index * FAT_ENTRY_SIZE , FAT_ENTRY_SIZE , fat_entry ) ! = FR_OK )
return 1 ;
} else fat_index = file_entry . start_block_index + 1 ;
2020-08-24 21:27:19 -07:00
2019-09-21 10:30:18 -04:00
u32 bytes_written = 0 , fat_index_write = fat_index ;
2020-08-24 21:27:19 -07:00
2020-06-10 03:53:24 -04:00
while ( bytes_written < size ) { // Write the full entry, walking the FAT node chain
// Can't assume contiguity here, because we might be replacing an existing entry
2019-09-21 10:30:18 -04:00
u32 write_start = fat_index_write - 1 ; // Data region block index
u32 write_count = 0 ;
2020-08-24 21:27:19 -07:00
2019-09-21 10:30:18 -04:00
if ( BDRIRead ( fat_offset + fat_index_write * FAT_ENTRY_SIZE , FAT_ENTRY_SIZE , fat_entry ) ! = FR_OK )
return 1 ;
2020-08-24 21:27:19 -07:00
2019-09-21 10:30:18 -04:00
if ( ( bytes_written = = 0 ) & & ! getfatflag ( fat_entry [ 0 ] ) )
2019-07-13 23:00:10 -04:00
return 1 ;
2020-08-24 21:27:19 -07:00
2019-09-21 10:30:18 -04:00
u32 next_index = getfatindex ( fat_entry [ 1 ] ) ;
2020-08-24 21:27:19 -07:00
2019-09-21 10:30:18 -04:00
if ( getfatflag ( fat_entry [ 1 ] ) ) { // Multi-entry node
if ( BDRIRead ( fat_offset + ( fat_index_write + 1 ) * FAT_ENTRY_SIZE , FAT_ENTRY_SIZE , fat_entry ) ! = FR_OK )
2019-07-13 23:00:10 -04:00
return 1 ;
2020-08-24 21:27:19 -07:00
2019-09-21 10:30:18 -04:00
if ( ! getfatflag ( fat_entry [ 0 ] ) | | getfatflag ( fat_entry [ 1 ] ) | | ( getfatindex ( fat_entry [ 0 ] ) ! = fat_index_write ) | | ( getfatindex ( fat_entry [ 0 ] ) > = getfatindex ( fat_entry [ 1 ] ) ) )
2019-07-13 23:00:10 -04:00
return 1 ;
2020-08-24 21:27:19 -07:00
2019-09-21 10:30:18 -04:00
write_count = getfatindex ( fat_entry [ 1 ] ) + 1 - fat_index_write ;
2019-07-13 23:00:10 -04:00
} else { // Single-entry node
2019-09-21 10:30:18 -04:00
write_count = 1 ;
2019-07-13 23:00:10 -04:00
}
2020-08-24 21:27:19 -07:00
2019-09-21 10:30:18 -04:00
fat_index_write = next_index ;
2020-08-24 21:27:19 -07:00
2020-06-10 03:53:24 -04:00
u32 btw = min ( size - bytes_written , write_count * fs_header - > data_block_size ) ;
2019-09-21 10:30:18 -04:00
if ( BDRIWrite ( data_offset + write_start * fs_header - > data_block_size , btw , entry + bytes_written ) ! = FR_OK )
2019-07-13 23:00:10 -04:00
return 1 ;
2020-08-24 21:27:19 -07:00
2019-09-21 10:30:18 -04:00
bytes_written + = btw ;
2019-07-13 23:00:10 -04:00
}
2020-08-24 21:27:19 -07:00
2019-09-21 10:30:18 -04:00
if ( ! do_replace ) {
DummyFileEntry dummy_entry ;
// Read the 0th entry in the FET, which is always a dummy entry
2020-08-24 21:27:19 -07:00
if ( BDRIRead ( fet_offset , sizeof ( DummyFileEntry ) , & dummy_entry ) ! = FR_OK )
2019-07-13 23:00:10 -04:00
return 1 ;
2019-09-21 10:30:18 -04:00
if ( dummy_entry . max_entry_count ! = fs_header - > max_file_count + 1 )
2019-07-13 23:00:10 -04:00
return 1 ;
2019-09-21 10:30:18 -04:00
if ( dummy_entry . next_dummy_index = = 0 ) { // If the 0th entry is the only dummy entry, make a new entry
file_entry . next_sibling_index = max_index + 1 ;
dummy_entry . total_entry_count + + ;
if ( BDRIWrite ( fet_offset , sizeof ( u32 ) , & ( dummy_entry . total_entry_count ) ) ! = FR_OK )
return 1 ;
} else { // If there's at least one extraneous dummy entry, replace it
file_entry . next_sibling_index = dummy_entry . next_dummy_index ;
if ( ( BDRIRead ( fet_offset + dummy_entry . next_dummy_index * sizeof ( DummyFileEntry ) , sizeof ( DummyFileEntry ) , & dummy_entry ) ! = FR_OK ) | |
( BDRIWrite ( fet_offset , sizeof ( DummyFileEntry ) , & dummy_entry ) ! = FR_OK ) )
return 1 ;
}
if ( BDRIWrite ( ( index = = 0 ) ? det_offset + 0x2C : fet_offset + index * sizeof ( TdbFileEntry ) + 0xC , sizeof ( u32 ) , & ( file_entry . next_sibling_index ) ) ! = FR_OK )
return 1 ;
index = file_entry . next_sibling_index ;
const u32 hash_bucket = GetHashBucket ( title_id_be , 1 , fs_header - > fht_bucket_count ) ;
u32 index_hash = 0 ;
if ( ( BDRIRead ( fht_offset + hash_bucket * sizeof ( u32 ) , sizeof ( u32 ) , & index_hash ) ! = FR_OK ) | |
( BDRIWrite ( fht_offset + hash_bucket * sizeof ( u32 ) , sizeof ( u32 ) , & index ) ! = FR_OK ) )
return 1 ;
memset ( & file_entry , 0 , sizeof ( TdbFileEntry ) ) ;
file_entry . parent_index = 1 ;
memcpy ( file_entry . title_id , title_id_be , 8 ) ;
file_entry . start_block_index = fat_index - 1 ;
file_entry . size = ( u64 ) size ;
file_entry . hash_bucket_next_index = index_hash ;
if ( BDRIWrite ( fet_offset + index * sizeof ( TdbFileEntry ) , sizeof ( TdbFileEntry ) , & file_entry ) ! = FR_OK )
2019-07-13 23:00:10 -04:00
return 1 ;
}
2020-08-24 21:27:19 -07:00
2019-09-21 10:30:18 -04:00
return 0 ;
}
static u32 GetNumBDRIEntries ( const BDRIFsHeader * fs_header , const u32 fs_header_offset ) {
if ( ( fs_header - > info_offset ! = 0x20 ) | | ( fs_header - > fat_entry_count ! = fs_header - > data_block_count ) ) // Could be more thorough
2020-08-24 21:27:19 -07:00
return 0 ;
2019-09-21 10:30:18 -04:00
const u32 data_offset = fs_header_offset + fs_header - > data_offset ;
const u32 det_offset = data_offset + fs_header - > det_start_block * fs_header - > data_block_size ;
const u32 fet_offset = data_offset + fs_header - > fet_start_block * fs_header - > data_block_size ;
2020-08-24 21:27:19 -07:00
2019-09-21 10:30:18 -04:00
u32 num_entries = 0 ;
TdbFileEntry file_entry ;
2020-08-24 21:27:19 -07:00
2019-09-21 10:30:18 -04:00
// Read the index of the first file entry from the directory entry table
if ( BDRIRead ( det_offset + 0x2C , sizeof ( u32 ) , & ( file_entry . next_sibling_index ) ) ! = FR_OK )
return 0 ;
2020-08-24 21:27:19 -07:00
2019-09-21 10:30:18 -04:00
while ( file_entry . next_sibling_index ! = 0 ) {
num_entries + + ;
if ( BDRIRead ( fet_offset + file_entry . next_sibling_index * sizeof ( TdbFileEntry ) , sizeof ( TdbFileEntry ) , & file_entry ) ! = FR_OK )
return 0 ;
}
2020-08-24 21:27:19 -07:00
2019-09-21 10:30:18 -04:00
return num_entries ;
}
static u32 ListBDRIEntryTitleIDs ( const BDRIFsHeader * fs_header , const u32 fs_header_offset , u8 * title_ids , u32 max_title_ids ) {
if ( ( fs_header - > info_offset ! = 0x20 ) | | ( fs_header - > fat_entry_count ! = fs_header - > data_block_count ) )
return 0 ;
2020-08-24 21:27:19 -07:00
2019-09-21 10:30:18 -04:00
const u32 data_offset = fs_header_offset + fs_header - > data_offset ;
const u32 det_offset = data_offset + fs_header - > det_start_block * fs_header - > data_block_size ;
const u32 fet_offset = data_offset + fs_header - > fet_start_block * fs_header - > data_block_size ;
2020-08-24 21:27:19 -07:00
2019-09-21 10:30:18 -04:00
u32 num_entries = 0 ;
TdbFileEntry file_entry ;
2020-08-24 21:27:19 -07:00
2019-09-21 10:30:18 -04:00
for ( u32 i = 0 ; i < max_title_ids ; i + + )
memset ( title_ids , 0 , max_title_ids * 8 ) ;
2020-08-24 21:27:19 -07:00
2019-09-21 10:30:18 -04:00
// Read the index of the first file entry from the directory entry table
if ( BDRIRead ( det_offset + 0x2C , sizeof ( u32 ) , & ( file_entry . next_sibling_index ) ) ! = FR_OK )
return 1 ;
2020-08-24 21:27:19 -07:00
2019-09-21 10:30:18 -04:00
while ( ( file_entry . next_sibling_index ! = 0 ) & & ( num_entries < max_title_ids ) ) {
if ( BDRIRead ( fet_offset + file_entry . next_sibling_index * sizeof ( TdbFileEntry ) , sizeof ( TdbFileEntry ) , & file_entry ) ! = FR_OK )
2019-07-13 23:00:10 -04:00
return 1 ;
2020-08-24 21:27:19 -07:00
2020-05-23 19:57:47 -04:00
u64 tid_be = getbe64 ( file_entry . title_id ) ;
memcpy ( title_ids + num_entries * 8 , ( u8 * ) & tid_be , 8 ) ;
2020-08-24 21:27:19 -07:00
2019-09-21 10:30:18 -04:00
num_entries + + ;
2019-07-13 23:00:10 -04:00
}
2020-08-24 21:27:19 -07:00
2019-09-21 10:30:18 -04:00
return 0 ;
}
u32 GetNumTitleInfoEntries ( const char * path ) {
FIL file ;
TitleDBPreHeader pre_header ;
2020-08-24 21:27:19 -07:00
2019-09-21 10:30:18 -04:00
if ( fvx_open ( & file , path , FA_READ | FA_OPEN_EXISTING ) ! = FR_OK )
return 0 ;
2020-08-24 21:27:19 -07:00
2019-09-21 10:30:18 -04:00
bdrifp = & file ;
2020-08-24 21:27:19 -07:00
2019-09-21 10:30:18 -04:00
if ( ( BDRIRead ( 0 , sizeof ( TitleDBPreHeader ) , & pre_header ) ! = FR_OK ) | |
! CheckDBMagic ( ( u8 * ) & pre_header , false ) ) {
fvx_close ( bdrifp ) ;
bdrifp = NULL ;
return 0 ;
}
2020-08-24 21:27:19 -07:00
2019-09-21 10:30:18 -04:00
u32 num = GetNumBDRIEntries ( & ( pre_header . fs_header ) , sizeof ( TitleDBPreHeader ) - sizeof ( BDRIFsHeader ) ) ;
2020-08-24 21:27:19 -07:00
2019-09-21 10:30:18 -04:00
fvx_close ( bdrifp ) ;
bdrifp = NULL ;
return num ;
2019-07-13 23:00:10 -04:00
}
2019-09-21 10:30:18 -04:00
u32 GetNumTickets ( const char * path ) {
2019-07-13 23:00:10 -04:00
FIL file ;
2019-09-21 10:30:18 -04:00
TickDBPreHeader pre_header ;
2020-08-24 21:27:19 -07:00
2019-07-13 23:00:10 -04:00
if ( fvx_open ( & file , path , FA_READ | FA_OPEN_EXISTING ) ! = FR_OK )
2019-09-21 10:30:18 -04:00
return 0 ;
2020-08-24 21:27:19 -07:00
2019-09-21 10:30:18 -04:00
bdrifp = & file ;
2020-08-24 21:27:19 -07:00
2019-09-21 10:30:18 -04:00
if ( ( BDRIRead ( 0 , sizeof ( TickDBPreHeader ) , & pre_header ) ! = FR_OK ) | |
! CheckDBMagic ( ( u8 * ) & pre_header , true ) ) {
fvx_close ( bdrifp ) ;
bdrifp = NULL ;
return 0 ;
2019-07-13 23:00:10 -04:00
}
2019-09-21 10:30:18 -04:00
u32 num = GetNumBDRIEntries ( & ( pre_header . fs_header ) , sizeof ( TickDBPreHeader ) - sizeof ( BDRIFsHeader ) ) ;
2020-08-24 21:27:19 -07:00
2019-09-21 10:30:18 -04:00
fvx_close ( bdrifp ) ;
bdrifp = NULL ;
return num ;
}
u32 ListTitleInfoEntryTitleIDs ( const char * path , u8 * title_ids , u32 max_title_ids ) {
FIL file ;
2019-07-13 23:00:10 -04:00
TitleDBPreHeader pre_header ;
2020-08-24 21:27:19 -07:00
2019-09-21 10:30:18 -04:00
if ( fvx_open ( & file , path , FA_READ | FA_OPEN_EXISTING ) ! = FR_OK )
2019-07-13 23:00:10 -04:00
return 1 ;
2020-08-24 21:27:19 -07:00
2019-09-21 10:30:18 -04:00
bdrifp = & file ;
2020-08-24 21:27:19 -07:00
2019-09-21 10:30:18 -04:00
if ( ( BDRIRead ( 0 , sizeof ( TitleDBPreHeader ) , & pre_header ) ! = FR_OK ) | |
! CheckDBMagic ( ( u8 * ) & pre_header , false ) | |
( ListBDRIEntryTitleIDs ( & ( pre_header . fs_header ) , sizeof ( TitleDBPreHeader ) - sizeof ( BDRIFsHeader ) , title_ids , max_title_ids ) ! = 0 ) ) {
fvx_close ( bdrifp ) ;
bdrifp = NULL ;
2019-07-13 23:00:10 -04:00
return 1 ;
}
2020-08-24 21:27:19 -07:00
2019-09-21 10:30:18 -04:00
fvx_close ( bdrifp ) ;
bdrifp = NULL ;
2019-07-13 23:00:10 -04:00
return 0 ;
}
2019-09-21 10:30:18 -04:00
u32 ListTicketTitleIDs ( const char * path , u8 * title_ids , u32 max_title_ids ) {
2019-07-13 23:00:10 -04:00
FIL file ;
2019-09-21 10:30:18 -04:00
TickDBPreHeader pre_header ;
2020-08-24 21:27:19 -07:00
2019-07-13 23:00:10 -04:00
if ( fvx_open ( & file , path , FA_READ | FA_OPEN_EXISTING ) ! = FR_OK )
return 1 ;
2020-08-24 21:27:19 -07:00
2019-09-21 10:30:18 -04:00
bdrifp = & file ;
2020-08-24 21:27:19 -07:00
2019-09-21 10:30:18 -04:00
if ( ( BDRIRead ( 0 , sizeof ( TickDBPreHeader ) , & pre_header ) ! = FR_OK ) | |
! CheckDBMagic ( ( u8 * ) & pre_header , true ) | |
( ListBDRIEntryTitleIDs ( & ( pre_header . fs_header ) , sizeof ( TickDBPreHeader ) - sizeof ( BDRIFsHeader ) , title_ids , max_title_ids ) ! = 0 ) ) {
fvx_close ( bdrifp ) ;
bdrifp = NULL ;
2019-07-13 23:00:10 -04:00
return 1 ;
}
2020-08-24 21:27:19 -07:00
2019-09-21 10:30:18 -04:00
fvx_close ( bdrifp ) ;
bdrifp = NULL ;
return 0 ;
}
u32 ReadTitleInfoEntryFromDB ( const char * path , const u8 * title_id , TitleInfoEntry * tie ) {
FIL file ;
TitleDBPreHeader pre_header ;
2020-08-24 21:27:19 -07:00
2019-09-21 10:30:18 -04:00
if ( fvx_open ( & file , path , FA_READ | FA_OPEN_EXISTING ) ! = FR_OK )
2019-07-13 23:00:10 -04:00
return 1 ;
2020-08-24 21:27:19 -07:00
2019-09-21 10:30:18 -04:00
bdrifp = & file ;
2020-08-24 21:27:19 -07:00
2019-09-21 10:30:18 -04:00
if ( ( BDRIRead ( 0 , sizeof ( TitleDBPreHeader ) , & pre_header ) ! = FR_OK ) | |
! CheckDBMagic ( ( u8 * ) & pre_header , false ) | |
( ReadBDRIEntry ( & ( pre_header . fs_header ) , sizeof ( TitleDBPreHeader ) - sizeof ( BDRIFsHeader ) , title_id , ( u8 * ) tie ,
sizeof ( TitleInfoEntry ) ) ! = 0 ) ) {
fvx_close ( bdrifp ) ;
bdrifp = NULL ;
2019-07-13 23:00:10 -04:00
return 1 ;
}
2020-08-24 21:27:19 -07:00
2019-09-21 10:30:18 -04:00
fvx_close ( bdrifp ) ;
bdrifp = NULL ;
2019-07-13 23:00:10 -04:00
return 0 ;
}
2020-05-23 19:57:47 -04:00
u32 ReadTicketFromDB ( const char * path , const u8 * title_id , Ticket * * ticket ) {
2019-07-13 23:00:10 -04:00
FIL file ;
2019-09-21 10:30:18 -04:00
TickDBPreHeader pre_header ;
2020-01-23 00:29:11 +00:00
TicketEntry * te = NULL ;
u32 entry_size ;
2020-10-28 00:00:18 +01:00
* ticket = NULL ;
2019-09-21 10:30:18 -04:00
if ( fvx_open ( & file , path , FA_READ | FA_OPEN_EXISTING ) ! = FR_OK )
2019-07-13 23:00:10 -04:00
return 1 ;
2020-08-24 21:27:19 -07:00
2019-09-21 10:30:18 -04:00
bdrifp = & file ;
2020-08-24 21:27:19 -07:00
2019-09-21 10:30:18 -04:00
if ( ( BDRIRead ( 0 , sizeof ( TickDBPreHeader ) , & pre_header ) ! = FR_OK ) | |
! CheckDBMagic ( ( u8 * ) & pre_header , true ) | |
2020-01-23 00:29:11 +00:00
( GetBDRIEntrySize ( & ( pre_header . fs_header ) , sizeof ( TickDBPreHeader ) - sizeof ( BDRIFsHeader ) , title_id , & entry_size ) ! = 0 ) | |
2020-08-24 21:27:19 -07:00
entry_size < sizeof ( TicketEntry ) + 0x14 | |
2020-01-23 00:29:11 +00:00
( te = ( TicketEntry * ) malloc ( entry_size ) , te = = NULL ) | |
( ReadBDRIEntry ( & ( pre_header . fs_header ) , sizeof ( TickDBPreHeader ) - sizeof ( BDRIFsHeader ) , title_id , ( u8 * ) te ,
2020-05-23 19:57:47 -04:00
entry_size ) ! = 0 ) ) {
2020-01-23 00:29:11 +00:00
free ( te ) ; // if allocated
2019-09-21 10:30:18 -04:00
fvx_close ( bdrifp ) ;
bdrifp = NULL ;
2019-07-13 23:00:10 -04:00
return 1 ;
}
2020-08-24 21:27:19 -07:00
2019-09-21 10:30:18 -04:00
fvx_close ( bdrifp ) ;
bdrifp = NULL ;
2020-08-24 21:27:19 -07:00
2020-01-23 00:29:11 +00:00
if ( te - > ticket_size ! = GetTicketSize ( & te - > ticket ) ) {
free ( te ) ;
2019-07-13 23:00:10 -04:00
return 1 ;
2020-01-23 00:29:11 +00:00
}
2020-08-24 21:27:19 -07:00
2020-01-23 00:29:11 +00:00
if ( ticket ) {
u32 size = te - > ticket_size ;
memmove ( te , & te - > ticket , size ) ; // recycle this memory, instead of allocating another
Ticket * tik = realloc ( te , size ) ;
if ( ! tik ) tik = ( Ticket * ) te ;
* ticket = tik ;
return 0 ;
}
2020-08-24 21:27:19 -07:00
2020-01-23 00:29:11 +00:00
free ( te ) ;
2019-07-13 23:00:10 -04:00
return 0 ;
}
2019-09-21 10:30:18 -04:00
u32 RemoveTitleInfoEntryFromDB ( const char * path , const u8 * title_id ) {
2019-07-13 23:00:10 -04:00
FIL file ;
2019-09-21 10:30:18 -04:00
TitleDBPreHeader pre_header ;
2020-08-24 21:27:19 -07:00
2019-07-13 23:00:10 -04:00
if ( fvx_open ( & file , path , FA_READ | FA_WRITE | FA_OPEN_EXISTING ) ! = FR_OK )
return 1 ;
2020-08-24 21:27:19 -07:00
2019-09-21 10:30:18 -04:00
bdrifp = & file ;
2020-08-24 21:27:19 -07:00
2019-09-21 10:30:18 -04:00
if ( ( BDRIRead ( 0 , sizeof ( TitleDBPreHeader ) , & pre_header ) ! = FR_OK ) | |
! CheckDBMagic ( ( u8 * ) & pre_header , false ) | |
( RemoveBDRIEntry ( & ( pre_header . fs_header ) , sizeof ( TitleDBPreHeader ) - sizeof ( BDRIFsHeader ) , title_id ) ! = 0 ) ) {
fvx_close ( bdrifp ) ;
bdrifp = NULL ;
2019-07-13 23:00:10 -04:00
return 1 ;
}
2020-08-24 21:27:19 -07:00
2019-09-21 10:30:18 -04:00
fvx_close ( bdrifp ) ;
bdrifp = NULL ;
return 0 ;
}
u32 RemoveTicketFromDB ( const char * path , const u8 * title_id ) {
FIL file ;
2019-07-13 23:00:10 -04:00
TickDBPreHeader pre_header ;
2020-08-24 21:27:19 -07:00
2019-09-21 10:30:18 -04:00
if ( fvx_open ( & file , path , FA_READ | FA_WRITE | FA_OPEN_EXISTING ) ! = FR_OK )
2019-07-13 23:00:10 -04:00
return 1 ;
2020-08-24 21:27:19 -07:00
2019-09-21 10:30:18 -04:00
bdrifp = & file ;
2020-08-24 21:27:19 -07:00
2019-09-21 10:30:18 -04:00
if ( ( BDRIRead ( 0 , sizeof ( TickDBPreHeader ) , & pre_header ) ! = FR_OK ) | |
! CheckDBMagic ( ( u8 * ) & pre_header , true ) | |
( RemoveBDRIEntry ( & ( pre_header . fs_header ) , sizeof ( TickDBPreHeader ) - sizeof ( BDRIFsHeader ) , title_id ) ! = 0 ) ) {
fvx_close ( bdrifp ) ;
bdrifp = NULL ;
2019-07-13 23:00:10 -04:00
return 1 ;
}
2020-08-24 21:27:19 -07:00
2019-07-13 23:00:10 -04:00
fvx_close ( & file ) ;
2019-09-21 10:30:18 -04:00
bdrifp = NULL ;
2019-07-13 23:00:10 -04:00
return 0 ;
}
2019-09-21 10:30:18 -04:00
u32 AddTitleInfoEntryToDB ( const char * path , const u8 * title_id , const TitleInfoEntry * tie , bool replace ) {
2019-07-13 23:00:10 -04:00
FIL file ;
TitleDBPreHeader pre_header ;
2020-08-24 21:27:19 -07:00
2019-09-21 10:30:18 -04:00
if ( fvx_open ( & file , path , FA_READ | FA_WRITE | FA_OPEN_EXISTING ) ! = FR_OK )
2019-07-13 23:00:10 -04:00
return 1 ;
2020-08-24 21:27:19 -07:00
2019-09-21 10:30:18 -04:00
bdrifp = & file ;
2020-08-24 21:27:19 -07:00
if ( ( BDRIRead ( 0 , sizeof ( TitleDBPreHeader ) , & pre_header ) ! = FR_OK ) | |
2019-09-21 10:30:18 -04:00
! CheckDBMagic ( ( u8 * ) & pre_header , false ) | |
( AddBDRIEntry ( & ( pre_header . fs_header ) , sizeof ( TitleDBPreHeader ) - sizeof ( BDRIFsHeader ) , title_id ,
( const u8 * ) tie , sizeof ( TitleInfoEntry ) , replace ) ! = 0 ) ) {
fvx_close ( bdrifp ) ;
bdrifp = NULL ;
2019-07-13 23:00:10 -04:00
return 1 ;
}
2020-08-24 21:27:19 -07:00
2019-09-21 10:30:18 -04:00
fvx_close ( bdrifp ) ;
bdrifp = NULL ;
2019-07-13 23:00:10 -04:00
return 0 ;
}
2019-09-21 10:30:18 -04:00
u32 AddTicketToDB ( const char * path , const u8 * title_id , const Ticket * ticket , bool replace ) {
2019-07-13 23:00:10 -04:00
FIL file ;
2019-09-21 10:30:18 -04:00
TickDBPreHeader pre_header ;
2020-01-23 00:29:11 +00:00
u32 entry_size = sizeof ( TicketEntry ) + GetTicketContentIndexSize ( ticket ) ;
2020-08-24 21:27:19 -07:00
2020-01-23 00:29:11 +00:00
TicketEntry * te = ( TicketEntry * ) malloc ( entry_size ) ;
if ( ! te ) {
return 1 ;
}
te - > unknown = 1 ;
te - > ticket_size = GetTicketSize ( ticket ) ;
memcpy ( & te - > ticket , ticket , te - > ticket_size ) ;
if ( fvx_open ( & file , path , FA_READ | FA_WRITE | FA_OPEN_EXISTING ) ! = FR_OK ) {
free ( te ) ;
2019-07-13 23:00:10 -04:00
return 1 ;
2020-01-23 00:29:11 +00:00
}
2020-08-24 21:27:19 -07:00
2019-09-21 10:30:18 -04:00
bdrifp = & file ;
2020-08-24 21:27:19 -07:00
2019-09-21 10:30:18 -04:00
if ( ( BDRIRead ( 0 , sizeof ( TickDBPreHeader ) , & pre_header ) ! = FR_OK ) | |
! CheckDBMagic ( ( u8 * ) & pre_header , true ) | |
( AddBDRIEntry ( & ( pre_header . fs_header ) , sizeof ( TickDBPreHeader ) - sizeof ( BDRIFsHeader ) , title_id ,
2020-01-23 00:29:11 +00:00
( const u8 * ) te , entry_size , replace ) ! = 0 ) ) {
free ( te ) ;
2019-09-21 10:30:18 -04:00
fvx_close ( bdrifp ) ;
bdrifp = NULL ;
2019-07-13 23:00:10 -04:00
return 1 ;
}
2020-05-23 19:57:47 -04:00
2020-01-23 00:29:11 +00:00
free ( te ) ;
2019-09-21 10:30:18 -04:00
fvx_close ( bdrifp ) ;
bdrifp = NULL ;
2019-07-13 23:00:10 -04:00
return 0 ;
2020-05-23 19:57:47 -04:00
}