mirror of
https://github.com/ApacheThunder/NTR_Launcher.git
synced 2025-06-19 03:25:38 -04:00

* Stage2 launcher UI added. This is a heavily modified HBMenu with ability to launch carts! This menu can be used to boot a bootloader NDS file for flascharts that the direct cart launcher fails to boot. * New audio files added for new menu. * HBMenu UI loaded by default if booted with no cart inserted. * INI file folder now located in NTR_Launcher folder instead. A default ini file from NitroFS (if mount of nitrofs succeeds) will be copied to SD if one is not present. * Stage2 launchers are expected to be in NTR Launcehr folder though you can navigate to any folder you want in the UI so stage2 launchers aren't the only thing this can be used for. You can also use this as a means of booting homebrew off SD in NTR mode.
593 lines
15 KiB
C
593 lines
15 KiB
C
/*-----------------------------------------------------------------
|
|
fat.c
|
|
|
|
NDS MP
|
|
GBAMP NDS Firmware Hack Version 2.12
|
|
An NDS aware firmware patch for the GBA Movie Player.
|
|
By Michael Chisholm (Chishm)
|
|
|
|
Filesystem code based on GBAMP_CF.c by Chishm (me).
|
|
|
|
License:
|
|
Copyright (C) 2005 Michael "Chishm" Chisholm
|
|
|
|
This program 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 2
|
|
of the License, or (at your option) any later version.
|
|
|
|
This program 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 this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
If you use this code, please give due credit and email me about your
|
|
project at chishm@hotmail.com
|
|
------------------------------------------------------------------*/
|
|
|
|
#include "fat.h"
|
|
#include "card.h"
|
|
|
|
|
|
//---------------------------------------------------------------
|
|
// FAT constants
|
|
|
|
#define FILE_LAST 0x00
|
|
#define FILE_FREE 0xE5
|
|
|
|
#define ATTRIB_ARCH 0x20
|
|
#define ATTRIB_DIR 0x10
|
|
#define ATTRIB_LFN 0x0F
|
|
#define ATTRIB_VOL 0x08
|
|
#define ATTRIB_HID 0x02
|
|
#define ATTRIB_SYS 0x04
|
|
#define ATTRIB_RO 0x01
|
|
|
|
#define FAT16_ROOT_DIR_CLUSTER 0x00
|
|
|
|
// File Constants
|
|
#ifndef EOF
|
|
#define EOF -1
|
|
#define SEEK_SET 0
|
|
#define SEEK_CUR 1
|
|
#define SEEK_END 2
|
|
#endif
|
|
|
|
|
|
//-----------------------------------------------------------------
|
|
// FAT constants
|
|
#define CLUSTER_EOF_16 0xFFFF
|
|
|
|
#define ATTRIB_ARCH 0x20
|
|
#define ATTRIB_DIR 0x10
|
|
#define ATTRIB_LFN 0x0F
|
|
#define ATTRIB_VOL 0x08
|
|
#define ATTRIB_HID 0x02
|
|
#define ATTRIB_SYS 0x04
|
|
#define ATTRIB_RO 0x01
|
|
|
|
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
// Data Structures
|
|
|
|
#define __PACKED __attribute__ ((__packed__))
|
|
|
|
// BIOS Parameter Block
|
|
typedef struct {
|
|
|
|
u16 bytesPerSector;
|
|
u8 sectorsPerCluster;
|
|
u16 reservedSectors;
|
|
u8 numFATs;
|
|
u16 rootEntries;
|
|
u16 numSectorsSmall;
|
|
u8 mediaDesc;
|
|
u16 sectorsPerFAT;
|
|
u16 sectorsPerTrk;
|
|
u16 numHeads;
|
|
u32 numHiddenSectors;
|
|
u32 numSectors;
|
|
} __PACKED BIOS_BPB;
|
|
|
|
// Boot Sector - must be packed
|
|
typedef struct
|
|
{
|
|
u8 jmpBoot[3];
|
|
u8 OEMName[8];
|
|
BIOS_BPB bpb;
|
|
union // Different types of extended BIOS Parameter Block for FAT16 and FAT32
|
|
{
|
|
struct
|
|
{
|
|
// Ext BIOS Parameter Block for FAT16
|
|
u8 driveNumber;
|
|
u8 reserved1;
|
|
u8 extBootSig;
|
|
u32 volumeID;
|
|
u8 volumeLabel[11];
|
|
u8 fileSysType[8];
|
|
// Bootcode
|
|
u8 bootCode[448];
|
|
} __PACKED fat16;
|
|
struct
|
|
{
|
|
// FAT32 extended block
|
|
u32 sectorsPerFAT32;
|
|
u16 extFlags;
|
|
u16 fsVer;
|
|
u32 rootClus;
|
|
u16 fsInfo;
|
|
u16 bkBootSec;
|
|
u8 reserved[12];
|
|
// Ext BIOS Parameter Block for FAT16
|
|
u8 driveNumber;
|
|
u8 reserved1;
|
|
u8 extBootSig;
|
|
u32 volumeID;
|
|
u8 volumeLabel[11];
|
|
u8 fileSysType[8];
|
|
// Bootcode
|
|
u8 bootCode[420];
|
|
} __PACKED fat32;
|
|
} __PACKED extBlock;
|
|
|
|
__PACKED u16 bootSig;
|
|
|
|
} __PACKED BOOT_SEC;
|
|
|
|
// _Static_assert(sizeof(BOOT_SEC) == 512);
|
|
|
|
// Directory entry - must be packed
|
|
typedef struct
|
|
{
|
|
u8 name[8];
|
|
u8 ext[3];
|
|
u8 attrib;
|
|
u8 reserved;
|
|
u8 cTime_ms;
|
|
u16 cTime;
|
|
u16 cDate;
|
|
u16 aDate;
|
|
u16 startClusterHigh;
|
|
u16 mTime;
|
|
u16 mDate;
|
|
u16 startCluster;
|
|
u32 fileSize;
|
|
} __PACKED DIR_ENT;
|
|
|
|
// File information - no need to pack
|
|
typedef struct
|
|
{
|
|
u32 firstCluster;
|
|
u32 length;
|
|
u32 curPos;
|
|
u32 curClus; // Current cluster to read from
|
|
int curSect; // Current sector within cluster
|
|
int curByte; // Current byte within sector
|
|
char readBuffer[512]; // Buffer used for unaligned reads
|
|
u32 appClus; // Cluster to append to
|
|
int appSect; // Sector within cluster for appending
|
|
int appByte; // Byte within sector for appending
|
|
bool read; // Can read from file
|
|
bool write; // Can write to file
|
|
bool append;// Can append to file
|
|
bool inUse; // This file is open
|
|
u32 dirEntSector; // The sector where the directory entry is stored
|
|
int dirEntOffset; // The offset within the directory sector
|
|
} FAT_FILE;
|
|
|
|
|
|
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
// Global Variables
|
|
|
|
// _VARS_IN_RAM variables are stored in the largest section of WRAM
|
|
// available: IWRAM on NDS ARM7, EWRAM on NDS ARM9 and GBA
|
|
|
|
// Locations on card
|
|
int discRootDir;
|
|
int discRootDirClus;
|
|
int discFAT;
|
|
int discSecPerFAT;
|
|
int discNumSec;
|
|
int discData;
|
|
int discBytePerSec;
|
|
int discSecPerClus;
|
|
int discBytePerClus;
|
|
|
|
enum {FS_UNKNOWN, FS_FAT12, FS_FAT16, FS_FAT32} discFileSystem;
|
|
|
|
// Global sector buffer to save on stack space
|
|
unsigned char globalBuffer[BYTES_PER_SECTOR];
|
|
|
|
|
|
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
//FAT routines
|
|
|
|
u32 FAT_ClustToSect (u32 cluster) {
|
|
return (((cluster-2) * discSecPerClus) + discData);
|
|
}
|
|
|
|
/*-----------------------------------------------------------------
|
|
FAT_NextCluster
|
|
Internal function - gets the cluster linked from input cluster
|
|
-----------------------------------------------------------------*/
|
|
u32 FAT_NextCluster(u32 cluster)
|
|
{
|
|
u32 nextCluster = CLUSTER_FREE;
|
|
u32 sector;
|
|
int offset;
|
|
|
|
|
|
switch (discFileSystem)
|
|
{
|
|
case FS_UNKNOWN:
|
|
nextCluster = CLUSTER_FREE;
|
|
break;
|
|
|
|
case FS_FAT12:
|
|
sector = discFAT + (((cluster * 3) / 2) / BYTES_PER_SECTOR);
|
|
offset = ((cluster * 3) / 2) % BYTES_PER_SECTOR;
|
|
CARD_ReadSector(sector, globalBuffer);
|
|
nextCluster = ((u8*) globalBuffer)[offset];
|
|
offset++;
|
|
|
|
if (offset >= BYTES_PER_SECTOR) {
|
|
offset = 0;
|
|
sector++;
|
|
}
|
|
|
|
CARD_ReadSector(sector, globalBuffer);
|
|
nextCluster |= (((u8*) globalBuffer)[offset]) << 8;
|
|
|
|
if (cluster & 0x01) {
|
|
nextCluster = nextCluster >> 4;
|
|
} else {
|
|
nextCluster &= 0x0FFF;
|
|
}
|
|
|
|
break;
|
|
|
|
case FS_FAT16:
|
|
sector = discFAT + ((cluster << 1) / BYTES_PER_SECTOR);
|
|
offset = cluster % (BYTES_PER_SECTOR >> 1);
|
|
|
|
CARD_ReadSector(sector, globalBuffer);
|
|
// read the nextCluster value
|
|
nextCluster = ((u16*)globalBuffer)[offset];
|
|
|
|
if (nextCluster >= 0xFFF7)
|
|
{
|
|
nextCluster = CLUSTER_EOF;
|
|
}
|
|
break;
|
|
|
|
case FS_FAT32:
|
|
sector = discFAT + ((cluster << 2) / BYTES_PER_SECTOR);
|
|
offset = cluster % (BYTES_PER_SECTOR >> 2);
|
|
|
|
CARD_ReadSector(sector, globalBuffer);
|
|
// read the nextCluster value
|
|
nextCluster = (((u32*)globalBuffer)[offset]) & 0x0FFFFFFF;
|
|
|
|
if (nextCluster >= 0x0FFFFFF7)
|
|
{
|
|
nextCluster = CLUSTER_EOF;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
nextCluster = CLUSTER_FREE;
|
|
break;
|
|
}
|
|
|
|
return nextCluster;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------
|
|
ucase
|
|
Returns the uppercase version of the given char
|
|
char IN: a character
|
|
char return OUT: uppercase version of character
|
|
-----------------------------------------------------------------*/
|
|
char ucase (char character)
|
|
{
|
|
if ((character > 0x60) && (character < 0x7B))
|
|
character = character - 0x20;
|
|
return (character);
|
|
}
|
|
|
|
/*-----------------------------------------------------------------
|
|
FAT_InitFiles
|
|
Reads the FAT information from the CF card.
|
|
You need to call this before reading any files.
|
|
bool return OUT: true if successful.
|
|
-----------------------------------------------------------------*/
|
|
bool FAT_InitFiles (bool initCard)
|
|
{
|
|
int i;
|
|
int bootSector;
|
|
BOOT_SEC* bootSec;
|
|
|
|
if (initCard && !CARD_StartUp())
|
|
{
|
|
return (false);
|
|
}
|
|
|
|
// Read first sector of card
|
|
if (!CARD_ReadSector (0, globalBuffer))
|
|
{
|
|
return false;
|
|
}
|
|
// Check if there is a FAT string, which indicates this is a boot sector
|
|
if ((globalBuffer[0x36] == 'F') && (globalBuffer[0x37] == 'A') && (globalBuffer[0x38] == 'T'))
|
|
{
|
|
bootSector = 0;
|
|
}
|
|
// Check for FAT32
|
|
else if ((globalBuffer[0x52] == 'F') && (globalBuffer[0x53] == 'A') && (globalBuffer[0x54] == 'T'))
|
|
{
|
|
bootSector = 0;
|
|
}
|
|
else // This is an MBR
|
|
{
|
|
// Find first valid partition from MBR
|
|
// First check for an active partition
|
|
for (i=0x1BE; (i < 0x1FE) && (globalBuffer[i] != 0x80); i+= 0x10);
|
|
// If it didn't find an active partition, search for any valid partition
|
|
if (i == 0x1FE)
|
|
for (i=0x1BE; (i < 0x1FE) && (globalBuffer[i+0x04] == 0x00); i+= 0x10);
|
|
|
|
// Go to first valid partition
|
|
if ( i != 0x1FE) // Make sure it found a partition
|
|
{
|
|
bootSector = globalBuffer[0x8 + i] + (globalBuffer[0x9 + i] << 8) + (globalBuffer[0xA + i] << 16) + ((globalBuffer[0xB + i] << 24) & 0x0F);
|
|
} else {
|
|
bootSector = 0; // No partition found, assume this is a MBR free disk
|
|
}
|
|
}
|
|
|
|
// Read in boot sector
|
|
bootSec = (BOOT_SEC*) globalBuffer;
|
|
CARD_ReadSector (bootSector, bootSec);
|
|
|
|
// Store required information about the file system
|
|
if (bootSec->bpb.sectorsPerFAT != 0)
|
|
{
|
|
discSecPerFAT = bootSec->bpb.sectorsPerFAT;
|
|
}
|
|
else
|
|
{
|
|
discSecPerFAT = bootSec->extBlock.fat32.sectorsPerFAT32;
|
|
}
|
|
|
|
if (bootSec->bpb.numSectorsSmall != 0)
|
|
{
|
|
discNumSec = bootSec->bpb.numSectorsSmall;
|
|
}
|
|
else
|
|
{
|
|
discNumSec = bootSec->bpb.numSectors;
|
|
}
|
|
|
|
discBytePerSec = BYTES_PER_SECTOR; // Sector size is redefined to be 512 bytes
|
|
discSecPerClus = bootSec->bpb.sectorsPerCluster * bootSec->bpb.bytesPerSector / BYTES_PER_SECTOR;
|
|
discBytePerClus = discBytePerSec * discSecPerClus;
|
|
discFAT = bootSector + bootSec->bpb.reservedSectors;
|
|
|
|
discRootDir = discFAT + (bootSec->bpb.numFATs * discSecPerFAT);
|
|
discData = discRootDir + ((bootSec->bpb.rootEntries * sizeof(DIR_ENT)) / BYTES_PER_SECTOR);
|
|
|
|
if ((discNumSec - discData) / bootSec->bpb.sectorsPerCluster < 4085)
|
|
{
|
|
discFileSystem = FS_FAT12;
|
|
}
|
|
else if ((discNumSec - discData) / bootSec->bpb.sectorsPerCluster < 65525)
|
|
{
|
|
discFileSystem = FS_FAT16;
|
|
}
|
|
else
|
|
{
|
|
discFileSystem = FS_FAT32;
|
|
}
|
|
|
|
if (discFileSystem != FS_FAT32)
|
|
{
|
|
discRootDirClus = FAT16_ROOT_DIR_CLUSTER;
|
|
}
|
|
else // Set up for the FAT32 way
|
|
{
|
|
discRootDirClus = bootSec->extBlock.fat32.rootClus;
|
|
// Check if FAT mirroring is enabled
|
|
if (!(bootSec->extBlock.fat32.extFlags & 0x80))
|
|
{
|
|
// Use the active FAT
|
|
discFAT = discFAT + ( discSecPerFAT * (bootSec->extBlock.fat32.extFlags & 0x0F));
|
|
}
|
|
}
|
|
|
|
return (true);
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------
|
|
getBootFileCluster
|
|
-----------------------------------------------------------------*/
|
|
u32 getBootFileCluster (const char* bootName)
|
|
{
|
|
DIR_ENT dir;
|
|
int firstSector = 0;
|
|
bool notFound = false;
|
|
bool found = false;
|
|
// int maxSectors;
|
|
u32 wrkDirCluster = discRootDirClus;
|
|
u32 wrkDirSector = 0;
|
|
int wrkDirOffset = 0;
|
|
int nameOffset;
|
|
|
|
dir.startCluster = CLUSTER_FREE; // default to no file found
|
|
dir.startClusterHigh = CLUSTER_FREE;
|
|
|
|
|
|
// Check if fat has been initialised
|
|
if (discBytePerSec == 0)
|
|
{
|
|
return (CLUSTER_FREE);
|
|
}
|
|
|
|
char *ptr = (char*)bootName;
|
|
while (*ptr != '.') ptr++;
|
|
int namelen = ptr - bootName;
|
|
|
|
// maxSectors = (wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? (discData - discRootDir) : discSecPerClus);
|
|
// Scan Dir for correct entry
|
|
firstSector = discRootDir;
|
|
CARD_ReadSector (firstSector + wrkDirSector, globalBuffer);
|
|
found = false;
|
|
notFound = false;
|
|
wrkDirOffset = -1; // Start at entry zero, Compensating for increment
|
|
while (!found && !notFound) {
|
|
wrkDirOffset++;
|
|
if (wrkDirOffset == BYTES_PER_SECTOR / sizeof (DIR_ENT))
|
|
{
|
|
wrkDirOffset = 0;
|
|
wrkDirSector++;
|
|
if ((wrkDirSector == discSecPerClus) && (wrkDirCluster != FAT16_ROOT_DIR_CLUSTER))
|
|
{
|
|
wrkDirSector = 0;
|
|
wrkDirCluster = FAT_NextCluster(wrkDirCluster);
|
|
if (wrkDirCluster == CLUSTER_EOF)
|
|
{
|
|
notFound = true;
|
|
}
|
|
firstSector = FAT_ClustToSect(wrkDirCluster);
|
|
}
|
|
else if ((wrkDirCluster == FAT16_ROOT_DIR_CLUSTER) && (wrkDirSector == (discData - discRootDir)))
|
|
{
|
|
notFound = true; // Got to end of root dir
|
|
}
|
|
CARD_ReadSector (firstSector + wrkDirSector, globalBuffer);
|
|
}
|
|
dir = ((DIR_ENT*) globalBuffer)[wrkDirOffset];
|
|
found = true;
|
|
if ((dir.attrib & ATTRIB_DIR) || (dir.attrib & ATTRIB_VOL))
|
|
{
|
|
found = false;
|
|
}
|
|
if(namelen<8 && dir.name[namelen]!=0x20) found = false;
|
|
for (nameOffset = 0; nameOffset < namelen && found; nameOffset++)
|
|
{
|
|
if (ucase(dir.name[nameOffset]) != bootName[nameOffset])
|
|
found = false;
|
|
}
|
|
for (nameOffset = 0; nameOffset < 3 && found; nameOffset++)
|
|
{
|
|
if (ucase(dir.ext[nameOffset]) != bootName[nameOffset+namelen+1])
|
|
found = false;
|
|
}
|
|
if (dir.name[0] == FILE_LAST)
|
|
{
|
|
notFound = true;
|
|
}
|
|
}
|
|
|
|
// If no file is found, return CLUSTER_FREE
|
|
if (notFound)
|
|
{
|
|
return CLUSTER_FREE;
|
|
}
|
|
|
|
return (dir.startCluster | (dir.startClusterHigh << 16));
|
|
}
|
|
|
|
/*-----------------------------------------------------------------
|
|
fileRead(buffer, cluster, startOffset, length)
|
|
-----------------------------------------------------------------*/
|
|
u32 fileRead (char* buffer, u32 cluster, u32 startOffset, u32 length)
|
|
{
|
|
int curByte;
|
|
int curSect;
|
|
|
|
int dataPos = 0;
|
|
int chunks;
|
|
int beginBytes;
|
|
|
|
if (cluster == CLUSTER_FREE || cluster == CLUSTER_EOF)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
// Follow cluster list until desired one is found
|
|
for (chunks = startOffset / discBytePerClus; chunks > 0; chunks--)
|
|
{
|
|
cluster = FAT_NextCluster (cluster);
|
|
}
|
|
|
|
// Calculate the sector and byte of the current position,
|
|
// and store them
|
|
curSect = (startOffset % discBytePerClus) / BYTES_PER_SECTOR;
|
|
curByte = startOffset % BYTES_PER_SECTOR;
|
|
|
|
// Load sector buffer for new position in file
|
|
CARD_ReadSector( curSect + FAT_ClustToSect(cluster), globalBuffer);
|
|
curSect++;
|
|
|
|
// Number of bytes needed to read to align with a sector
|
|
beginBytes = (BYTES_PER_SECTOR < length + curByte ? (BYTES_PER_SECTOR - curByte) : length);
|
|
|
|
// Read first part from buffer, to align with sector boundary
|
|
for (dataPos = 0 ; dataPos < beginBytes; dataPos++)
|
|
{
|
|
buffer[dataPos] = globalBuffer[curByte++];
|
|
}
|
|
|
|
// Read in all the 512 byte chunks of the file directly, saving time
|
|
for ( chunks = ((int)length - beginBytes) / BYTES_PER_SECTOR; chunks > 0;)
|
|
{
|
|
int sectorsToRead;
|
|
|
|
// Move to the next cluster if necessary
|
|
if (curSect >= discSecPerClus)
|
|
{
|
|
curSect = 0;
|
|
cluster = FAT_NextCluster (cluster);
|
|
}
|
|
|
|
// Calculate how many sectors to read (read a maximum of discSecPerClus at a time)
|
|
sectorsToRead = discSecPerClus - curSect;
|
|
if(chunks < sectorsToRead)
|
|
sectorsToRead = chunks;
|
|
|
|
// Read the sectors
|
|
CARD_ReadSectors(curSect + FAT_ClustToSect(cluster), sectorsToRead, buffer + dataPos);
|
|
chunks -= sectorsToRead;
|
|
curSect += sectorsToRead;
|
|
dataPos += BYTES_PER_SECTOR * sectorsToRead;
|
|
}
|
|
|
|
// Take care of any bytes left over before end of read
|
|
if (dataPos < length)
|
|
{
|
|
|
|
// Update the read buffer
|
|
curByte = 0;
|
|
if (curSect >= discSecPerClus)
|
|
{
|
|
curSect = 0;
|
|
cluster = FAT_NextCluster (cluster);
|
|
}
|
|
CARD_ReadSector( curSect + FAT_ClustToSect( cluster), globalBuffer);
|
|
|
|
// Read in last partial chunk
|
|
for (; dataPos < length; dataPos++)
|
|
{
|
|
buffer[dataPos] = globalBuffer[curByte];
|
|
curByte++;
|
|
}
|
|
}
|
|
|
|
return dataPos;
|
|
}
|